Commit 36841b1
Implement LuceneDev4000-4002 NoInlining Analyzers & CodeFix (#1097) (apache#30)
* Implement LuceneDev4000-4002 NoInlining Analyzers & CodeFix (#1097)
Adds three Roslyn analyzers under the new Performance category covering
[MethodImpl(MethodImplOptions.NoInlining)] usage:
- LuceneDev4000: Reports when NoInlining is applied to an interface or
abstract method. The MethodImpl attribute is not inherited, so the
attribute has no effect on the implementation.
- LuceneDev4001: Reports when NoInlining is applied to an empty-bodied
method. An empty body cannot appear above any frame in a stack trace,
so preventing inlining provides no benefit.
- LuceneDev4002: Reports when a method referenced by the 2-argument
StackTraceHelper.DoesStackTraceContainMethod(className, methodName)
overload is missing NoInlining. Without it, the JIT may inline the
method out of the stack trace, silently breaking the check.
A code fix is provided for 4000 and 4001 (remove the attribute). 4002
has no automated fix because the diagnostic is reported on a method
declaration triggered by an invocation in another scope, which Roslyn
treats as a non-local diagnostic and refuses to fix automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Split LuceneDev4002 into its own analyzer class
LuceneDev4002 (StackTraceHelper-driven NoInlining requirement) is a
distinct rule from 4000/4001 (NoInlining-as-no-op detection): it has a
different trigger (invocation vs. method declaration) and no code fix.
Extract it into its own analyzer + test class. Shared logic for
recognising the [MethodImpl(MethodImplOptions.NoInlining)] attribute,
empty bodies, and interface/abstract methods moves into
NoInliningAttributeHelper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add Sample project entries for LuceneDev4000-4002
Demonstrates each NoInlining diagnostic firing in the sample project:
4000 on an interface and an abstract method, 4001 on an empty-bodied
method, and 4002 on a method referenced by the 2-arg
StackTraceHelper.DoesStackTraceContainMethod overload (with a local
stub mirroring the real type so the sample compiles standalone).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Preserve leading comments and trim attribute indent in 4000/4001 fix
The previous remove-attribute logic used SyntaxRemoveOptions.KeepNoTrivia
which discarded any comments or blank lines that preceded the attribute,
or KeepLeadingTrivia which left a stray indent on the next line. Replace
with an approach that operates on the parent member declaration directly:
strip the attribute list while moving its leading comments (minus the
final whitespace block, which was just the attribute's own indent) onto
the next member's leading trivia.
Adds tests covering: leading comment preservation, removing one of
several attributes inside a single attribute list, and removing one
attribute list among multiple sibling lists.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Report LuceneDev4002 at the call site instead of the target method
Reporting on the target method declaration produced a non-local
diagnostic — the analyzer was visiting an InvocationExpressionSyntax
but raising the diagnostic on a syntax tree it had not visited. MSBuild
ran a full compilation and surfaced the warning, but the IDE's per-file
live analysis filtered it out, so the warning never appeared in the
editor.
Move the report to the DoesStackTraceContainMethod invocation. The
diagnostic is now local, shows up in the IDE, and opens the door to a
code fix at the call site. The message names the qualified target
(e.g. 'Target.Merge') so it remains clear which method needs the
attribute. Test span updated accordingly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add code fix for LuceneDev4002
Adds [MethodImpl(MethodImplOptions.NoInlining)] to the target method
referenced by a DoesStackTraceContainMethod call. The fix resolves the
target from the call's nameof() (or string-literal) arguments, locates
its declaration in source, and edits that document — even when it lives
in a different file from the call. Adds
'using System.Runtime.CompilerServices;' to the target's compilation
unit if missing, and prepends the new attribute list ahead of any
existing attributes on the method.
Promote NoInliningAttributeHelper from internal to public so the code
fix project can reuse it. Adds tests covering: target with using
already present, target without the using, and target with an existing
attribute.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Match source line-ending convention in 4002 code fix
The fix previously emitted '\n' line terminators unconditionally, which
matched local Mac checkouts (LF) but failed on Linux CI where the
checked-out source uses CRLF. Detect the existing line ending from the
target tree's trivia and reuse it so the fixed output stays consistent
with the surrounding file.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Simplify 4002 code fix and rely on Formatter for layout
Replaces hand-rolled trivia construction with a single Formatter pass.
The new attribute list and using directive are built without trivia and
formatted via Formatter.FormatAsync, with the workspace NewLine option
explicitly set from the source file's existing line endings so the
output doesn't mix CRLF and LF on platforms where Environment.NewLine
disagrees with the file (e.g. CRLF source on Linux CI).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Address Copilot review on LuceneDev4002
Use a symbol-based attribute check (IMethodSymbol.GetAttributes) instead
of inspecting AttributeSyntax with a SemanticModel from the wrong tree.
The previous code threw "Syntax node is not within syntax tree" whenever
the target method lived in a different file from the call site and had
any attribute. Symbol-based lookup also avoids Compilation.GetSemanticModel
in the analyzer (which trips RS1030).
Also:
- Drop the Contains("NoInlining") string fallback in the syntax-based
helper; the constant-value path already resolves MethodImplOptions.NoInlining
and the fallback false-positives on identifiers like "NotNoInlining".
- Replace the namespace walk in FindSourceTypeByName with
Compilation.GetSymbolsWithName to leverage Roslyn's symbol index.
- Update the analyzer's XML doc — the PR adds a code fix; the previous
comment claimed there was none.
- Add cross-document tests for both analyzer and code fix; the analyzer
test reproduces the SemanticModel bug above.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 66f539c commit 36841b1
14 files changed
Lines changed: 2067 additions & 6 deletions
File tree
- src
- Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev4xxx
- Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev4xxx
- Lucene.Net.CodeAnalysis.Dev
- LuceneDev4xxx
- Utility
- tests
- Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev4xxx
- Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev4xxx
Lines changed: 132 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
0 commit comments