TODO: Review whether the C# 13 test verifier should use real reference assemblies (e.g., a future ReferenceAssemblies.Net.NetX_Y that includes System.Threading.Lock) instead of the current minimal stub in CSharp13CodeFixVerifier<TAnalyzer, TCodeFix>.
-
Partial properties/indexers refactoring: indexers excluded
- Limitation:
PartialPropertiesIndexersRefactoringAnalyzercurrently returnsfalsefor indexers and only suggests auto-properties. - Why: Roslyn preview support in this repo makes it hard to safely test “auto-indexers without bodies”, so indexers were intentionally excluded (see analyzer comment).
- Long-term fix: Add indexer support end-to-end (analyzer + safety checker + code fix + tests) once the test infrastructure can compile/verify partial indexers reliably.
- Pointers:
PartialPropertiesIndexersRefactoringAnalyzer.IsCandidate(),PartialPropertiesIndexersRefactoringCodeFixProvider.CreateImplementingDeclaration(),PartialPropertiesIndexersRefactoringCodeFixTests.
- Limitation:
-
C# 13 test infrastructure:
CSharp13CodeFixVerifierpinsReferenceAssemblies.Net.Net90- Limitation: Tests using
CSharp13CodeFixVerifier<TAnalyzer, TCodeFix>compile against .NET 9 reference assemblies regardless of the repo’s baseline. - Why: Needed a “modern target framework” so preview C# 13 features that depend on runtime support can compile in fixed-state (see comment in verifier).
- Long-term fix: Revisit whether .NET 9 is the right baseline for tests (or whether it should be conditional / centralized), and switch to the smallest reference set that still supports the required C# 13 features.
- Pointers:
CSharp13CodeFixVerifier.CreateTest().
- Limitation: Tests using
-
System.Threading.Lockis injected as a test stub- Limitation: The verifier injects a minimal
System.Threading.Lockimplementation into both test and fixed states, which can mask real API shape differences. - Why: Current reference assemblies used by tests don’t provide
System.Threading.Lock, but safety checkers/code fixes need the symbol to resolve. - Long-term fix: Remove the stub and rely on real reference assemblies once they include
System.Threading.Lock(or use a dedicated reference assembly package that contains it). - Pointers:
CSharp13CodeFixVerifier.LockStubSource,UseSystemThreadingLockSafetyChecker.
- Limitation: The verifier injects a minimal
You asked for an inventory of all analyzers and fix providers in the old project ./original-sharpen and a comparison with what is implemented in the new project ./sharpen.analyzer.
Important terminology mismatch:
- In
original-sharpen, “analyzers” are engine analyzers implementingISingleSyntaxTreeAnalyzerand returningAnalysisResultobjects. “Fixes” are suggestions (ISharpenSuggestion) surfaced by the VS extension UI. - In
sharpen.analyzer, analyzers are RoslynDiagnosticAnalyzerimplementations and fixes are RoslynCodeFixProviderimplementations.
This report compares capabilities by:
- enumerating the old engine analyzers from
SharpenAnalyzersHolder.Analyzers - verifying the corresponding new analyzer/fix exists, and
- spot-checking implementation content (not only file names) for a few representative rules.
The old implementation is split into:
-
VS integration layer:
original-sharpen/src/Sharpen.VisualStudioExtension- Collects scope (solution/projects/documents) and runs analysis via scope analyzers like
SolutionScopeAnalyzerandMultipleDocumentsScopeAnalyzer.
- Collects scope (solution/projects/documents) and runs analysis via scope analyzers like
-
Analysis + suggestion engine:
original-sharpen/src/Sharpen.Engine- Central registry of analyzers:
SharpenAnalyzersHolder.Analyzers - Execution model:
BaseScopeAnalyzeriterates documents, builds aSingleSyntaxTreeAnalysisContext, and runs all analyzers in parallel (Parallel.Invoke) against the sameSyntaxTree+SemanticModel.
- Central registry of analyzers:
From SharpenAnalyzersHolder.Analyzers, the old project contains the following capabilities:
UseVarKeywordInVariableDeclarationWithObjectCreation
ConsiderAwaitingEquivalentAsynchronousMethodAndMakingTheCallerAsynchronousAwaitEquivalentAsynchronousMethodAwaitTaskDelayInsteadOfCallingThreadSleepAwaitTaskInsteadOfCallingTaskWaitAwaitTaskInsteadOfCallingTaskResultAwaitTaskWhenAllInsteadOfCallingTaskWaitAllAwaitTaskWhenAnyInsteadOfCallingTaskWaitAny
UseExpressionBodyForGetOnlyPropertiesUseExpressionBodyForGetOnlyIndexersUseNameofExpressionForThrowingArgumentExceptionsUseNameofExpressionInDependencyPropertyDeclarations
UseExpressionBodyForConstructorsUseExpressionBodyForDestructorsUseExpressionBodyForGetAccessorsInPropertiesUseExpressionBodyForGetAccessorsInIndexersUseExpressionBodyForSetAccessorsInPropertiesUseExpressionBodyForSetAccessorsInIndexersUseExpressionBodyForLocalFunctionsUseOutVariablesInMethodInvocationsUseOutVariablesInObjectCreationsDiscardOutVariablesInMethodInvocationsDiscardOutVariablesInObjectCreations
UseDefaultExpressionInReturnStatementsUseDefaultExpressionInOptionalMethodParametersUseDefaultExpressionInOptionalConstructorParameters
EnableNullableContextAndDeclareIdentifierAsNullableAnalyzer- Code fix provider exists:
EnableNullableContextAndDeclareIdentifierAsNullableCodeFixProvider - Code fix test: not implemented (Roslyn test harness rejects it as "non-local analyzer diagnostic" because the analyzer reports the diagnostic on the declaration location while being triggered by a different usage site)
- Code fix provider exists:
ConsiderAwaitingEquivalentAsynchronousMethodAndYieldingIAsyncEnumerableAnalyzerReplaceUsingStatementWithUsingDeclarationAnalyzerReplaceSwitchStatementWithSwitchExpressionAnalyzerUseNullCoalescingAssignmentOperatorInsteadOfAssigningResultOfTheNullCoalescingOperatorAnalyzer
The new project contains Roslyn analyzers and code fix providers under:
- Analyzers:
Sharpen.Analyzer/Sharpen.Analyzer/Sharpen.Analyzer/Analyzers/ - Fix providers:
Sharpen.Analyzer/Sharpen.Analyzer/Sharpen.Analyzer/FixProvider/
Additionally, the new project includes analyzers beyond the old project (C# 9-12), e.g. UseTopLevelStatementsAnalyzer, UsePrimaryConstructorAnalyzer.
Old engine:
UseOutVariablesInObjectCreationsis a thin wrapper overBaseUseOutVariables<T>withbase(false)meaning “use out var” (not discard).
New Roslyn analyzer:
UseOutVariablesInObjectCreationsAnalyzerregisters onSyntaxKind.ObjectCreationExpressionand:- iterates arguments
- filters
outkeyword + identifier expression - calls
OutVariableCandidateHelper.IsCandidate(...)withoutArgumentCanBeDiscarded: false - reports diagnostic on the
outkeyword location
Conclusion: capability exists in new project and the core detection logic is present.
Old engine:
DiscardOutVariablesInMethodInvocationsis also a thin wrapper overBaseUseOutVariables<T>but withbase(true)meaning “discard allowed/desired”.
New Roslyn analyzer:
DiscardOutVariablesInMethodInvocationsAnalyzerregisters onSyntaxKind.InvocationExpressionand:- iterates arguments
- filters
outkeyword + identifier expression - calls
OutVariableCandidateHelper.IsCandidate(..., outArgumentCanBeDiscarded: true) - reports diagnostic on the
outkeyword location
Conclusion: capability exists in new project and the “discard vs out var” distinction is preserved.
New Roslyn code fix:
UseExpressionBodyForSetAccessorsInIndexersCodeFixProviderperforms a concrete syntax transformation:- finds the
AccessorDeclarationSyntaxat diagnostic span - validates it is a
setaccessor inside anIndexerDeclarationSyntax - requires a block body with exactly one
ExpressionStatementSyntax - replaces
{ expr; }with=> expr;while attempting to preserve trivia
- finds the
Conclusion: new project not only detects but also provides an automated fix for this rule.
Old engine:
- The rule is implemented by
UseExpressionBodyForSetAccessorsInProperties, which inheritsBaseUseExpressionBodyForSetAccessors<TBasePropertyDeclarationSyntax>. - The detection logic in the base class is:
- scan all
AccessorDeclarationSyntax - keep only
setaccessors (accessor.Keyword.IsKind(SyntaxKind.SetKeyword)) - require a block body with exactly one statement
- require that statement is an expression statement
- require the accessor is inside a
PropertyDeclarationSyntax(for this specialization)
- scan all
New project:
- There is a matching analyzer+rule+codefix for indexers only:
- analyzer:
UseExpressionBodyForSetAccessorsInIndexersAnalyzer - rule descriptor:
UseExpressionBodyForSetAccessorsInIndexersRule - code fix:
UseExpressionBodyForSetAccessorsInIndexersCodeFixProvider
- analyzer:
- A repository-wide search for
UseExpressionBodyForSetAccessorsInPropertiesreturns no results, and there is noDiagnosticDescriptorfor “set accessor in property” inRules.cs. - A search for analyzers registering on
SyntaxKind.SetAccessorDeclarationshows only the indexer rule (plus unrelated analyzers like init-only setter) (seeUseExpressionBodyForSetAccessorsInIndexersAnalyzer).
Conclusion: UseExpressionBodyForSetAccessorsInProperties appears to be a real missing capability in the new project (not just a naming mismatch).
After re-checking the new project’s analyzer/fix inventory (and not relying only on file names), the earlier “missing” list was incorrect.
The following capabilities from the old engine are present in the new project (examples):
UseExpressionBodyForGetAccessorsInIndexersexists asUseExpressionBodyForGetAccessorsInIndexersAnalyzer+UseExpressionBodyForGetAccessorsInIndexersCodeFixProviderUseExpressionBodyForSetAccessorsInIndexersexists asUseExpressionBodyForSetAccessorsInIndexersAnalyzer+UseExpressionBodyForSetAccessorsInIndexersCodeFixProviderDiscardOutVariablesInMethodInvocationsexists asDiscardOutVariablesInMethodInvocationsAnalyzerDiscardOutVariablesInObjectCreationsexists asDiscardOutVariablesInObjectCreationsAnalyzerUseOutVariablesInObjectCreationsexists asUseOutVariablesInObjectCreationsAnalyzer
Confirmed missing capability:
UseExpressionBodyForSetAccessorsInPropertiesexists in the old engine list (seeUseExpressionBodyForSetAccessorsInProperties) but does not appear to exist in the new project (no analyzer, no rule descriptor, no code fix provider).
- The old project’s “fixes” are not Roslyn
CodeFixProviders; they are suggestions surfaced by the VS extension UI. So this report compares capabilities, not 1:1 type equivalence. - I only did content spot-checks for a subset of rules (out vars + expression-bodied rule(s)). A full content-level diff for every rule would require reading each old suggestion implementation and its new analyzer + code fix pair.