Skip to content

Refactor source-export annotation removal to use Roslyn #179

Description

@feO2x

Rationale

The source exporter removes annotation usages with exact text replacement and whole-line classification. These approaches miss attributes in combined lists or with alternative argument syntax, and multiline handling currently depends on formatting performed earlier in the export pipeline.

Replace annotation cleanup with a Roslyn syntax transformation so each configured attribute can be removed without disturbing neighboring attributes or nullable-flow metadata. Provide equivalent cleanup support for NoEnumeration so exports that omit bundled JetBrains annotations can remain self-contained and compilable.

Acceptance Criteria

  • Configured annotation removal works for standalone, combined, adjacent, and multiline attribute syntax while preserving unrelated attributes and surrounding comments.
  • All existing annotation-removal options use the syntax-aware cleanup path, including caller-argument expressions that use nameof.
  • RemoveNoEnumeration is available as an opt-in source-export setting with a default value of false.
  • The canonical exporter settings and source-inclusion documentation describe RemoveNoEnumeration.
  • Removing ValidatedNotNull or NoEnumeration preserves adjacent nullable-flow annotations such as NotNull and NotNullWhen.
  • Automated tests cover the supported attribute layouts and protect the behavior of every annotation-removal option.
  • Generated output configured without bundled JetBrains annotation definitions or their using directive compiles successfully when JetBrains-only usages are configured for removal.

Technical Details

Perform annotation removal on the merged CompilationUnitSyntax already maintained by the exporter rather than serializing and reparsing the source. Derive the set of targeted attribute names from RemoveContractAnnotations, RemoveValidatedNotNull, RemoveDoesNotReturn, RemoveNotNullWhen, RemoveCallerArgumentExpressions, and the new RemoveNoEnumeration option.

Inspect AttributeListSyntax nodes and remove only matching AttributeSyntax children from mixed lists. Remove the complete attribute list when every child is targeted. Preserve exterior trivia when removing nodes, then normalize whitespace after the syntax transformation so comments and unaffected adjacent lists remain well-formed. Attribute matching should use the rightmost name component and treat the optional Attribute suffix equivalently; semantic binding is intentionally unnecessary because excluded attribute definitions may be unresolved during export.

Keep non-syntactic formatting cleanup, such as eliminating unwanted blank lines after XML documentation, separate from annotation removal. RemoveNoEnumeration must remain independent from IncludeJetBrainsAnnotations and IncludeJetBrainsAnnotationsUsing because consumers may exclude the bundled definitions while supplying JetBrains annotations externally.

Include focused cleanup tests for partial and complete list removal, multiple adjacent lists, multiline arguments, preservation of trivia, and CallerArgumentExpression(nameof(parameter)). Add an end-to-end generated-file build validation using the configuration from issue #178 plus RemoveNoEnumeration=true, and assert that targeted names are absent while NotNull and NotNullWhen remain.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions