Skip to content

Honor #[mutants::skip] on const and static items#622

Open
sandersaares wants to merge 1 commit into
sourcefrog:mainfrom
sandersaares:sandersaares/skip-attr-on-const-items
Open

Honor #[mutants::skip] on const and static items#622
sandersaares wants to merge 1 commit into
sourcefrog:mainfrom
sandersaares:sandersaares/skip-attr-on-const-items

Conversation

@sandersaares

@sandersaares sandersaares commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

Fixes #508.

Problem

#[mutants::skip] (and #[cfg_attr(..., mutants::skip)]) placed on a const or static item was silently ignored. The DiscoveryVisitor in src/visit.rs had no visit_item_const / visit_item_static / visit_impl_item_const / visit_trait_item_const, so syn's default visitor walked straight into the initializer expression and produced the usual operator mutants without consulting the item's attributes.

Reproduced before the fix:

src/lib.rs:1:31: replace | with &     # plain const
src/lib.rs:1:31: replace | with ^
src/lib.rs:4:39: replace | with &     # const with #[mutants::skip] - still mutated
src/lib.rs:4:39: replace | with ^

Fix

Added four visitor methods to DiscoveryVisitor that bail out via attrs_excluded(&i.attrs) when the item carries a skip attribute, and otherwise delegate to the default syn::visit::visit_* recursion. Behavior for unskipped consts/statics is unchanged.

The attribute is now honoured on:

  • Top-level const FOO: T = ...;
  • Top-level static FOO: T = ...;
  • Associated const inside impl blocks
  • Associated const defaults inside trait blocks

Both #[mutants::skip] and #[cfg_attr(..., mutants::skip)] work, via the existing attrs_excluded helper.

Tests

Five new tests under src/visit/test/ following the existing skip_attr_impl.rs pattern:

  • skip_attr_item_const.rs — covers #[mutants::skip] and #[cfg_attr(test, mutants::skip)] on a top-level const
  • skip_attr_item_static.rs — covers static
  • skip_attr_impl_item_const.rs — covers associated const in impl
  • skip_attr_trait_item_const.rs — covers associated const default in trait

Each test puts a distinct operator on the skipped item (^) and on the unskipped sibling (|), then asserts on Mutant::original_text() rather than line numbers, so reformatting the indoc block cannot turn the assertions into silent passes.

Documentation

  • book/src/attrs.md: added a Scope bullet for const / static items.
  • NEWS.md: added an Unreleased entry describing the fix.

Out of scope

Generating fresh "replace value" mutants for const initializers (per @kpreid's follow-up on the issue) would need a new mutation genre and is a separate, larger feature.

Validation

  • cargo fmt --check passes
  • cargo clippy --all-targets --all-features -- -D warnings passes
  • cargo nextest run --all-features --no-fail-fast: 401/402 pass (the one failure, symlink_in_source_tree_is_copied, is a pre-existing Windows symlink-privilege issue unrelated to this change)
  • End-to-end repro confirms each item kind now honors both #[mutants::skip] and #[cfg_attr(..., mutants::skip)] while unskipped items continue to produce mutants as before.

Previously, #[mutants::skip] (and #[cfg_attr(..., mutants::skip)]) placed
on a const or static item was silently ignored: the visitor walked into
the initializer expression and generated the usual operator mutants.

Add visit_item_const, visit_item_static, visit_impl_item_const, and
visit_trait_item_const to DiscoveryVisitor. Each bails out when the
item's attrs match attrs_excluded, suppressing all mutants inside the
initializer; otherwise it delegates to the default syn::visit recursion
so behavior for unskipped items is unchanged.

This addresses sourcefrog#508. Generating fresh "replace value" mutants for the
constant itself (kpreid's follow-up suggestion) is a separate, larger
feature and is not included here.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sandersaares sandersaares force-pushed the sandersaares/skip-attr-on-const-items branch from e1c096d to 7a2cb01 Compare June 7, 2026 08:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request fixes an attribute-handling gap in the mutation discovery visitor so that #[mutants::skip] (including #[cfg_attr(..., mutants::skip)]) is honored on const and static items, preventing mutants from being generated inside their initializer expressions.

Changes:

  • Added Visit overrides in DiscoveryVisitor for ItemConst, ItemStatic, ImplItemConst, and TraitItemConst to early-return when attrs_excluded(&i.attrs) matches.
  • Added targeted tests ensuring skipped const/static initializers don’t produce operator mutants while unskipped siblings still do.
  • Updated documentation and changelog to reflect the newly supported skip scope.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/visit.rs Adds visitor hooks for const/static items (including associated consts) to honor #[mutants::skip] before descending into initializer/default expressions.
src/visit/test/skip_attr_item_const.rs Tests skipping top-level const initializers, including cfg_attr(..., mutants::skip).
src/visit/test/skip_attr_item_static.rs Tests skipping top-level static initializers.
src/visit/test/skip_attr_impl_item_const.rs Tests skipping associated const initializers inside impl blocks.
src/visit/test/skip_attr_trait_item_const.rs Tests skipping associated const defaults inside trait blocks.
book/src/attrs.md Documents that skip applies to const/static initializer expressions (including associated consts).
NEWS.md Adds an Unreleased changelog entry describing the fix and linking to #508.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support skipping mutation of constants

2 participants