Skip to content

Commit 49ecd9b

Browse files
committed
Improve record analyzer performance
Switch to symbol action rather than syntax, to speed up analysis. Partially fixes #60
1 parent ff93ea8 commit 49ecd9b

File tree

1 file changed

+26
-16
lines changed

1 file changed

+26
-16
lines changed

src/StructId.Analyzer/RecordAnalyzer.cs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,42 @@ public override void Initialize(AnalysisContext context)
2222
if (!Debugger.IsAttached)
2323
context.EnableConcurrentExecution();
2424

25-
context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.ClassDeclaration);
26-
context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.StructDeclaration);
27-
context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.RecordDeclaration);
28-
context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.RecordStructDeclaration);
25+
context.RegisterCompilationStartAction(start =>
26+
{
27+
var known = new KnownTypes(start.Compilation);
28+
if (known.IStructId is null || known.IStructIdT is null)
29+
return;
30+
31+
start.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
32+
});
2933
}
3034

31-
static void Analyze(SyntaxNodeAnalysisContext context)
35+
static void AnalyzeSymbol(SymbolAnalysisContext context)
3236
{
37+
if (context.Symbol is not INamedTypeSymbol symbol)
38+
return;
39+
3340
var known = new KnownTypes(context.Compilation);
3441

35-
if (context.Node is not TypeDeclarationSyntax typeDeclaration ||
36-
known.IStructIdT is not { } structIdTypeOfT ||
37-
known.IStructId is not { } structIdType)
42+
// We only care about IStructId and IStructId<T>
43+
if (!symbol.Is(known.IStructId) && !symbol.Is(known.IStructIdT))
3844
return;
3945

40-
var symbol = context.SemanticModel.GetDeclaredSymbol(typeDeclaration);
41-
if (symbol is null)
46+
// We can only analyze if there's a declaration in source.
47+
if (symbol.DeclaringSyntaxReferences.Length == 0 ||
48+
symbol.DeclaringSyntaxReferences
49+
.Select(x => x.GetSyntax())
50+
.OfType<TypeDeclarationSyntax>()
51+
.FirstOrDefault() is not { } typeDeclaration)
4252
return;
4353

44-
if (!symbol.Is(structIdType) && !symbol.Is(structIdTypeOfT))
45-
return;
54+
// TODO: report or ignore if more than one declaration?
4655

4756
// If there's only one declaration and it's not partial
48-
var report = symbol.DeclaringSyntaxReferences.Length == 1 && !typeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword);
49-
report |= !typeDeclaration.IsKind(SyntaxKind.RecordStructDeclaration) || !symbol.IsReadOnly;
57+
var report = symbol.DeclaringSyntaxReferences.Length == 1 &&
58+
!typeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword);
59+
60+
report |= !symbol.IsRecord || symbol.TypeKind != TypeKind.Struct || !symbol.IsReadOnly;
5061

5162
if (report)
5263
{
@@ -55,7 +66,7 @@ known.IStructIdT is not { } structIdTypeOfT ||
5566
else if (typeDeclaration.BaseList?.Types.FirstOrDefault(t => t.Type is IdentifierNameSyntax { Identifier.Text: "IStructId" }) is { } implementation)
5667
context.ReportDiagnostic(Diagnostic.Create(MustBeRecordStruct, implementation.GetLocation(), symbol.Name));
5768
else
58-
context.ReportDiagnostic(Diagnostic.Create(MustBeRecordStruct, typeDeclaration.Identifier.GetLocation(), symbol.Name));
69+
context.ReportDiagnostic(Diagnostic.Create(MustBeRecordStruct, symbol.Locations.FirstOrDefault(), symbol.Name));
5970
}
6071

6172
if (typeDeclaration.ParameterList is null)
@@ -72,6 +83,5 @@ known.IStructIdT is not { } structIdTypeOfT ||
7283
var parameter = typeDeclaration.ParameterList.Parameters[0];
7384
if (parameter.Identifier.Text != "Value")
7485
context.ReportDiagnostic(Diagnostic.Create(MustHaveValueConstructor, parameter.Identifier.GetLocation(), symbol.Name));
75-
7686
}
7787
}

0 commit comments

Comments
 (0)