Skip to content

Commit 1644ea2

Browse files
committed
Analyzer and source generator optimizations
1 parent 53aecd7 commit 1644ea2

File tree

10 files changed

+165
-129
lines changed

10 files changed

+165
-129
lines changed
20 KB
Binary file not shown.

Medicine.SourceGenerator~/Analyzers/DisallowReadOnlyAnalyzer.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ static void AnalyzeField(SyntaxNodeAnalysisContext context)
4040
if (!fieldDeclaration.Modifiers.Any(SyntaxKind.ReadOnlyKeyword))
4141
return;
4242

43-
if (!fieldDeclaration.HasAttribute(x => x.MatchesQualifiedNamePattern("Medicine.DisallowReadonlyAttribute", namespaceSegments: 1)))
44-
return;
45-
4643
var typeInfo = context.SemanticModel.GetTypeInfo(fieldDeclaration.Declaration.Type);
4744

4845
if (typeInfo is not { Type: INamedTypeSymbol { TypeKind: TypeKind.Struct } type })

Medicine.SourceGenerator~/Analyzers/FindObjectsAnalyzer.cs

Lines changed: 54 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -36,87 +36,75 @@ public override void Initialize(AnalysisContext context)
3636
{
3737
context.EnableConcurrentExecution();
3838
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
39-
context.RegisterSyntaxNodeAction(
40-
action: AnalyzeInvocation,
41-
syntaxKinds: SyntaxKind.InvocationExpression
42-
);
43-
44-
return;
45-
46-
static void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
39+
context.RegisterCompilationStartAction(startContext =>
4740
{
48-
if (context.Node is not InvocationExpressionSyntax invocation)
49-
return;
50-
51-
var nameSyntax = invocation.Expression switch
52-
{
53-
GenericNameSyntax x => x,
54-
MemberAccessExpressionSyntax { Name: GenericNameSyntax x } => x,
55-
_ => null,
56-
};
57-
58-
if (nameSyntax is null)
59-
return;
60-
61-
if (context.SemanticModel.GetSymbolInfo(invocation).Symbol is not IMethodSymbol methodSymbol)
62-
return;
41+
var knownSymbols = new KnownSymbols(startContext.Compilation);
42+
startContext.RegisterSyntaxNodeAction(
43+
action: syntaxContext => AnalyzeInvocation(syntaxContext, knownSymbols),
44+
syntaxKinds: SyntaxKind.InvocationExpression
45+
);
46+
});
47+
}
6348

64-
var methodName = nameSyntax.Identifier.ValueText;
49+
static void AnalyzeInvocation(SyntaxNodeAnalysisContext context, KnownSymbols knownSymbols)
50+
{
51+
if (context.Node is not InvocationExpressionSyntax invocation)
52+
return;
6553

66-
if (methodName is "FindObjectOfType" or "FindFirstObjectByType" or "FindAnyObjectByType")
67-
{
68-
if (methodSymbol.ContainingType is not { } containingType)
69-
return;
54+
var nameSyntax = invocation.Expression switch
55+
{
56+
GenericNameSyntax x => x,
57+
MemberAccessExpressionSyntax { Name: GenericNameSyntax x } => x,
58+
_ => null,
59+
};
7060

71-
if (containingType.ContainingNamespace.ToDisplayString() != "UnityEngine")
72-
return;
61+
if (nameSyntax is null)
62+
return;
7363

74-
if (containingType.Name != "Object")
75-
return;
64+
if (context.SemanticModel.GetSymbolInfo(invocation).Symbol is not IMethodSymbol methodSymbol)
65+
return;
7666

77-
if (methodSymbol.TypeArguments.FirstOrDefault()?.HasAttribute("global::Medicine.SingletonAttribute") == true)
78-
context.ReportDiagnostic(Diagnostic.Create(MED013, invocation.GetLocation(), invocation.ToString()));
67+
var methodName = nameSyntax.Identifier.ValueText;
7968

69+
if (methodName is "FindObjectOfType" or "FindFirstObjectByType" or "FindAnyObjectByType")
70+
{
71+
if (!methodSymbol.ContainingType.Is(knownSymbols.UnityObject))
8072
return;
81-
}
8273

83-
if (methodName is "FindObjectsOfType" or "FindObjectsByType" or "ObjectsByType")
84-
if (IsTarget(methodSymbol, invocation, context.SemanticModel))
85-
context.ReportDiagnostic(Diagnostic.Create(MED011, invocation.GetLocation()));
86-
}
74+
if (methodSymbol.TypeArguments.FirstOrDefault()?.HasAttribute(knownSymbols.SingletonAttribute) == true)
75+
context.ReportDiagnostic(Diagnostic.Create(MED013, invocation.GetLocation(), invocation.ToString()));
8776

88-
static bool IsTarget(IMethodSymbol methodSymbol, InvocationExpressionSyntax invocation, SemanticModel semanticModel)
89-
{
90-
if (!methodSymbol.IsGenericMethod)
91-
return false;
77+
return;
78+
}
9279

93-
var containingType = methodSymbol.ContainingType;
94-
if (containingType is null)
95-
return false;
80+
if (methodName is "FindObjectsOfType" or "FindObjectsByType" or "ObjectsByType")
81+
if (IsTarget(methodSymbol, invocation, knownSymbols))
82+
context.ReportDiagnostic(Diagnostic.Create(MED011, invocation.GetLocation()));
83+
}
9684

97-
var nameSpace = containingType.ContainingNamespace.ToDisplayString();
85+
static bool IsTarget(IMethodSymbol methodSymbol, InvocationExpressionSyntax invocation, KnownSymbols knownSymbols)
86+
{
87+
if (!methodSymbol.IsGenericMethod)
88+
return false;
9889

99-
bool IsTracked()
100-
=> methodSymbol.TypeArguments.FirstOrDefault()?.HasAttribute("global::Medicine.TrackAttribute") is true;
90+
bool IsTracked()
91+
=> methodSymbol.TypeArguments.FirstOrDefault()?.HasAttribute(knownSymbols.TrackAttribute) is true;
10192

102-
if (nameSpace is "UnityEngine")
103-
if (containingType.Name is "Object")
104-
if (methodSymbol.Name is "FindObjectsOfType" or "FindObjectsByType")
105-
return true;
93+
if (methodSymbol.ContainingType.Is(knownSymbols.UnityObject))
94+
if (methodSymbol.Name is "FindObjectsOfType" or "FindObjectsByType")
95+
return true;
10696

107-
bool IsIncludeInactive()
108-
=> invocation.ArgumentList.Arguments.FirstOrDefault()?.Expression
109-
is LiteralExpressionSyntax { Token.ValueText: "true" }
110-
or MemberAccessExpressionSyntax { Name.Identifier.Text: "Include" };
97+
bool IsIncludeInactive()
98+
=> invocation.ArgumentList.Arguments.FirstOrDefault()?.Expression
99+
is LiteralExpressionSyntax { Token.ValueText: "true" }
100+
or MemberAccessExpressionSyntax { Name.Identifier.Text: "Include" };
111101

112-
if (nameSpace is "Medicine")
113-
if (containingType.Name is "Find")
114-
if (methodSymbol.Name is "ObjectsByType")
115-
if (IsTracked())
116-
if (!IsIncludeInactive())
117-
return true;
102+
if (methodSymbol.ContainingType.Is(knownSymbols.MedicineFind))
103+
if (methodSymbol.Name is "ObjectsByType")
104+
if (IsTracked())
105+
if (!IsIncludeInactive())
106+
return true;
118107

119-
return false;
120-
}
108+
return false;
121109
}
122-
}
110+
}

Medicine.SourceGenerator~/Analyzers/NullComparisonAnalyzer.cs

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1010
public sealed class NullComparisonAnalyzer : DiagnosticAnalyzer
1111
{
12-
const string ExtensionsDefine = "MEDICINE_EXTENSIONS_LIB";
13-
const string UnityObjectFqn = "global::UnityEngine.Object";
14-
1512
public static readonly DiagnosticDescriptor MED026 = new(
1613
id: nameof(MED026),
1714
title: "Use faster IsNull extension method",
@@ -39,43 +36,49 @@ public override void Initialize(AnalysisContext context)
3936
context.EnableConcurrentExecution();
4037

4138
context.RegisterCompilationStartAction(startContext =>
42-
{
43-
if (!HasExtensionsSymbol(startContext.Compilation))
44-
return;
45-
46-
startContext.RegisterSyntaxNodeAction(AnalyzeBinary, SyntaxKind.EqualsExpression, SyntaxKind.NotEqualsExpression);
47-
startContext.RegisterSyntaxNodeAction(AnalyzeIsPattern, SyntaxKind.IsPatternExpression);
48-
});
39+
{
40+
if (!HasExtensionsSymbol(startContext.Compilation))
41+
return;
42+
43+
var knownSymbols = new KnownSymbols(startContext.Compilation);
44+
startContext.RegisterSyntaxNodeAction(
45+
syntaxContext => AnalyzeBinary(syntaxContext, knownSymbols.UnityObject),
46+
SyntaxKind.EqualsExpression,
47+
SyntaxKind.NotEqualsExpression
48+
);
49+
50+
startContext.RegisterSyntaxNodeAction(
51+
syntaxContext => AnalyzeIsPattern(syntaxContext, knownSymbols.UnityObject),
52+
SyntaxKind.IsPatternExpression
53+
);
54+
}
55+
);
4956
}
5057

5158
static bool HasExtensionsSymbol(Compilation compilation)
5259
{
5360
foreach (var tree in compilation.SyntaxTrees)
54-
{
55-
if (tree.Options is not CSharpParseOptions options)
56-
continue;
57-
58-
foreach (var name in options.PreprocessorSymbolNames)
59-
if (name == ExtensionsDefine)
60-
return true;
61-
}
61+
if (tree.Options is CSharpParseOptions options)
62+
foreach (var name in options.PreprocessorSymbolNames)
63+
if (name is Constants.MedicineExtensionsDefine)
64+
return true;
6265

6366
return false;
6467
}
6568

66-
static void AnalyzeBinary(SyntaxNodeAnalysisContext context)
69+
static void AnalyzeBinary(SyntaxNodeAnalysisContext context, INamedTypeSymbol unityObjectSymbol)
6770
{
6871
if (context.Node is not BinaryExpressionSyntax binary)
6972
return;
7073

71-
if (!TryGetUnityOperand(binary.Left, binary.Right, context.SemanticModel, context.CancellationToken, out _))
74+
if (!TryGetUnityOperand(binary.Left, binary.Right, context.SemanticModel, context.CancellationToken, unityObjectSymbol, out _))
7275
return;
7376

7477
var descriptor = binary.IsKind(SyntaxKind.EqualsExpression) ? MED026 : MED027;
7578
context.ReportDiagnostic(Diagnostic.Create(descriptor, binary.GetLocation()));
7679
}
7780

78-
static void AnalyzeIsPattern(SyntaxNodeAnalysisContext context)
81+
static void AnalyzeIsPattern(SyntaxNodeAnalysisContext context, INamedTypeSymbol unityObjectSymbol)
7982
{
8083
if (context.Node is not IsPatternExpressionSyntax isPattern)
8184
return;
@@ -84,15 +87,12 @@ static void AnalyzeIsPattern(SyntaxNodeAnalysisContext context)
8487

8588
if (IsNullPattern(pattern))
8689
{
87-
if (IsUnityObjectExpression(isPattern.Expression, context.SemanticModel, context.CancellationToken))
90+
if (IsUnityObjectExpression(isPattern.Expression, context.SemanticModel, context.CancellationToken, unityObjectSymbol))
8891
context.ReportDiagnostic(Diagnostic.Create(MED026, isPattern.GetLocation()));
89-
90-
return;
9192
}
92-
93-
if (IsNotNullPattern(pattern))
93+
else if (IsNotNullPattern(pattern))
9494
{
95-
if (IsUnityObjectExpression(isPattern.Expression, context.SemanticModel, context.CancellationToken))
95+
if (IsUnityObjectExpression(isPattern.Expression, context.SemanticModel, context.CancellationToken, unityObjectSymbol))
9696
context.ReportDiagnostic(Diagnostic.Create(MED027, isPattern.GetLocation()));
9797
}
9898
}
@@ -102,14 +102,15 @@ static bool TryGetUnityOperand(
102102
ExpressionSyntax right,
103103
SemanticModel model,
104104
CancellationToken ct,
105+
INamedTypeSymbol unityObjectSymbol,
105106
out ExpressionSyntax unityOperand
106107
)
107108
{
108109
unityOperand = null!;
109110

110111
if (IsNullLiteral(left))
111112
{
112-
if (IsUnityObjectExpression(right, model, ct))
113+
if (IsUnityObjectExpression(right, model, ct, unityObjectSymbol))
113114
{
114115
unityOperand = right;
115116
return true;
@@ -120,7 +121,7 @@ out ExpressionSyntax unityOperand
120121

121122
if (IsNullLiteral(right))
122123
{
123-
if (!IsUnityObjectExpression(left, model, ct))
124+
if (!IsUnityObjectExpression(left, model, ct, unityObjectSymbol))
124125
return false;
125126

126127
unityOperand = left;
@@ -159,27 +160,38 @@ static PatternSyntax UnwrapParenthesizedPattern(PatternSyntax pattern)
159160
return pattern;
160161
}
161162

162-
static bool IsUnityObjectExpression(ExpressionSyntax expression, SemanticModel model, CancellationToken ct)
163+
static bool IsUnityObjectExpression(
164+
ExpressionSyntax expression,
165+
SemanticModel model,
166+
CancellationToken ct,
167+
INamedTypeSymbol unityObjectSymbol
168+
)
163169
{
164170
var typeInfo = model.GetTypeInfo(expression, ct);
165171
var type = typeInfo.Type ?? typeInfo.ConvertedType;
166-
return IsUnityObjectType(type);
172+
return IsUnityObjectType(type, unityObjectSymbol);
167173
}
168174

169-
static bool IsUnityObjectType(ITypeSymbol? type)
175+
static bool IsUnityObjectType(ITypeSymbol? type, INamedTypeSymbol unityObjectSymbol)
170176
{
171-
if (type is null or IErrorTypeSymbol)
172-
return false;
173-
174-
if (type is ITypeParameterSymbol typeParameter)
177+
switch (type)
175178
{
176-
foreach (var constraint in typeParameter.ConstraintTypes)
177-
if (IsUnityObjectType(constraint))
178-
return true;
179+
case null or IErrorTypeSymbol:
180+
{
181+
return false;
182+
}
183+
case ITypeParameterSymbol typeParameter:
184+
{
185+
foreach (var constraint in typeParameter.ConstraintTypes)
186+
if (IsUnityObjectType(constraint, unityObjectSymbol))
187+
return true;
179188

180-
return false;
189+
return false;
190+
}
191+
default:
192+
{
193+
return type.Is(unityObjectSymbol) || type.InheritsFrom(unityObjectSymbol);
194+
}
181195
}
182-
183-
return type.Is(UnityObjectFqn) || type.InheritsFrom(UnityObjectFqn);
184196
}
185-
}
197+
}

0 commit comments

Comments
 (0)