Skip to content

Commit d5af5bd

Browse files
authored
Make ReflectionAccessAnalyzer logic more similar to ILLink/ILC (#105956)
ReflectionAccessAnalyzer is analogous to ILLink/ILC's ReflectionMarker, and will be responsible for producing warnings about DynamicallyAccessedMembers on types when we fix #102002. These warnings will not all have the same location since they can be reported on different members of the type. To support this, this is changing ReflectionAccessAnalyzer to create a DiagnosticContext as needed when reporting a warning, instead of taking it as an argument, which would require all warnings to share the same location (since DiagnosticContext holds a Location). This is similar to how ReflectionMarker takes a MessageOrigin instead of DiagnosticContext.
1 parent fdb7415 commit d5af5bd

15 files changed

+134
-154
lines changed

src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ public override void Initialize (AnalysisContext context)
9898
foreach (var operationBlock in context.OperationBlocks) {
9999
TrimDataFlowAnalysis trimDataFlowAnalysis = new (context, dataFlowAnalyzerContext, operationBlock);
100100
trimDataFlowAnalysis.InterproceduralAnalyze ();
101-
foreach (var diagnostic in trimDataFlowAnalysis.CollectDiagnostics ())
102-
context.ReportDiagnostic (diagnostic);
101+
trimDataFlowAnalysis.ReportDiagnostics (context.ReportDiagnostic);
103102
}
104103
});
105104

@@ -117,16 +116,12 @@ public override void Initialize (AnalysisContext context)
117116
return;
118117

119118
var location = GetPrimaryLocation (type.Locations);
120-
DiagnosticContext diagnosticContext = new (location);
121119

122120
if (type.BaseType is INamedTypeSymbol baseType)
123-
GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (diagnosticContext, baseType);
121+
GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (location, baseType, context.ReportDiagnostic);
124122

125123
foreach (var interfaceType in type.Interfaces)
126-
GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (diagnosticContext, interfaceType);
127-
128-
foreach (var diagnostic in diagnosticContext.Diagnostics)
129-
context.ReportDiagnostic (diagnostic);
124+
GenericArgumentDataFlow.ProcessGenericArgumentDataFlow (location, interfaceType, context.ReportDiagnostic);
130125
}, SymbolKind.NamedType);
131126
context.RegisterSymbolAction (context => {
132127
VerifyMemberOnlyApplyToTypesOrStrings (context, context.Symbol);

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/DiagnosticContext.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,35 @@ namespace ILLink.Shared.TrimAnalysis
1212
{
1313
public readonly partial struct DiagnosticContext
1414
{
15-
public List<Diagnostic> Diagnostics { get; } = new ();
15+
public readonly Location Location { get; }
1616

17-
readonly Location? Location { get; init; }
17+
readonly Action<Diagnostic>? _reportDiagnostic;
1818

19-
public DiagnosticContext (Location location)
19+
public DiagnosticContext (Location location, Action<Diagnostic>? reportDiagnostic)
2020
{
2121
Location = location;
22+
_reportDiagnostic = reportDiagnostic;
2223
}
2324

24-
public static DiagnosticContext CreateDisabled () => new () { Location = null };
25-
26-
public Diagnostic CreateDiagnostic (DiagnosticId id, params string[] args)
25+
private Diagnostic CreateDiagnostic (DiagnosticId id, params string[] args)
2726
{
2827
return Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (id), Location, args);
2928
}
3029

3130
public partial void AddDiagnostic (DiagnosticId id, params string[] args)
3231
{
33-
if (Location == null)
32+
if (_reportDiagnostic is null)
3433
return;
3534

36-
Diagnostics.Add (CreateDiagnostic (id, args));
35+
_reportDiagnostic (CreateDiagnostic (id, args));
3736
}
3837

3938
public partial void AddDiagnostic (DiagnosticId id, ValueWithDynamicallyAccessedMembers actualValue, ValueWithDynamicallyAccessedMembers expectedAnnotationsValue, params string[] args)
4039
{
41-
if (Location == null)
40+
if (_reportDiagnostic is null)
4241
return;
4342

44-
Diagnostics.Add (CreateDiagnostic (id, actualValue, expectedAnnotationsValue, args));
43+
_reportDiagnostic (CreateDiagnostic (id, actualValue, expectedAnnotationsValue, args));
4544
}
4645

4746
private Diagnostic CreateDiagnostic (DiagnosticId id, ValueWithDynamicallyAccessedMembers actualValue, ValueWithDynamicallyAccessedMembers expectedAnnotationsValue, params string[] args)

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FeatureCheckReturnValuePattern.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using ILLink.Shared;
67
using ILLink.Shared.DataFlow;
@@ -29,22 +30,22 @@ public FeatureCheckReturnValuePattern (
2930
OwningSymbol = owningSymbol;
3031
}
3132

32-
public IEnumerable<Diagnostic> CollectDiagnostics (DataFlowAnalyzerContext context)
33+
public void ReportDiagnostics (DataFlowAnalyzerContext context, Action<Diagnostic> reportDiagnostic)
3334
{
34-
var diagnosticContext = new DiagnosticContext (Operation.Syntax.GetLocation ());
35+
var diagnosticContext = new DiagnosticContext (Operation.Syntax.GetLocation (), reportDiagnostic);
3536
// For now, feature check validation is enabled only when trim analysis is enabled.
3637
if (!context.EnableTrimAnalyzer)
37-
return diagnosticContext.Diagnostics;
38+
return;
3839

3940
if (!OwningSymbol.IsStatic || OwningSymbol.Type.SpecialType != SpecialType.System_Boolean || OwningSymbol.SetMethod != null) {
4041
// Warn about invalid feature checks (non-static or non-bool properties or properties with setter)
4142
diagnosticContext.AddDiagnostic (
4243
DiagnosticId.InvalidFeatureGuard);
43-
return diagnosticContext.Diagnostics;
44+
return;
4445
}
4546

4647
if (ReturnValue == FeatureChecksValue.All)
47-
return diagnosticContext.Diagnostics;
48+
return;
4849

4950
ValueSet<string> returnValueFeatures = ReturnValue.EnabledFeatures;
5051
// For any analyzer-supported feature that this property is declared to guard,
@@ -63,8 +64,6 @@ public IEnumerable<Diagnostic> CollectDiagnostics (DataFlowAnalyzerContext conte
6364
}
6465
}
6566
}
66-
67-
return diagnosticContext.Diagnostics;
6867
}
6968
}
7069
}

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericArgumentDataFlow.cs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,54 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.Collections.Generic;
65
using System.Collections.Immutable;
7-
using System.Diagnostics;
86
using System.Diagnostics.CodeAnalysis;
97
using Microsoft.CodeAnalysis;
108
using ILLink.Shared.DataFlow;
119
using ILLink.Shared.TrimAnalysis;
12-
using ILLink.Shared.TypeSystemProxy;
1310

1411
namespace ILLink.RoslynAnalyzer.TrimAnalysis
1512
{
1613
internal static class GenericArgumentDataFlow
1714
{
18-
public static void ProcessGenericArgumentDataFlow (DiagnosticContext diagnosticContext, INamedTypeSymbol type)
15+
public static void ProcessGenericArgumentDataFlow (Location location, INamedTypeSymbol type, Action<Diagnostic> reportDiagnostic)
1916
{
20-
ProcessGenericArgumentDataFlow (diagnosticContext, type.TypeArguments, type.TypeParameters);
17+
ProcessGenericArgumentDataFlow (location, type.TypeArguments, type.TypeParameters, reportDiagnostic);
2118
}
2219

23-
public static void ProcessGenericArgumentDataFlow (DiagnosticContext diagnosticContext, IMethodSymbol method)
20+
public static void ProcessGenericArgumentDataFlow (Location location, IMethodSymbol method, Action<Diagnostic> reportDiagnostic)
2421
{
25-
ProcessGenericArgumentDataFlow (diagnosticContext, method.TypeArguments, method.TypeParameters);
22+
ProcessGenericArgumentDataFlow (location, method.TypeArguments, method.TypeParameters, reportDiagnostic);
2623

27-
ProcessGenericArgumentDataFlow (diagnosticContext, method.ContainingType);
24+
ProcessGenericArgumentDataFlow (location, method.ContainingType, reportDiagnostic);
2825
}
2926

30-
public static void ProcessGenericArgumentDataFlow (DiagnosticContext diagnosticContext, IFieldSymbol field)
27+
public static void ProcessGenericArgumentDataFlow (Location location, IFieldSymbol field, Action<Diagnostic> reportDiagnostic)
3128
{
32-
ProcessGenericArgumentDataFlow (diagnosticContext, field.ContainingType);
29+
ProcessGenericArgumentDataFlow (location, field.ContainingType, reportDiagnostic);
3330
}
3431

3532
static void ProcessGenericArgumentDataFlow (
36-
DiagnosticContext diagnosticContext,
33+
Location location,
3734
ImmutableArray<ITypeSymbol> typeArguments,
38-
ImmutableArray<ITypeParameterSymbol> typeParameters)
35+
ImmutableArray<ITypeParameterSymbol> typeParameters,
36+
Action<Diagnostic> reportDiagnostic)
3937
{
38+
var diagnosticContext = new DiagnosticContext (location, reportDiagnostic);
4039
for (int i = 0; i < typeArguments.Length; i++) {
4140
var typeArgument = typeArguments[i];
4241
// Apply annotations to the generic argument
4342
var genericParameterValue = new GenericParameterValue (typeParameters[i]);
4443
if (genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None) {
4544
SingleValue genericArgumentValue = SingleValueExtensions.FromTypeSymbol (typeArgument)!;
46-
var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (diagnosticContext, default (ReflectionAccessAnalyzer));
45+
var reflectionAccessAnalyzer = new ReflectionAccessAnalyzer (reportDiagnostic);
46+
var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (diagnosticContext, reflectionAccessAnalyzer);
4747
requireDynamicallyAccessedMembersAction.Invoke (genericArgumentValue, genericParameterValue);
4848
}
4949

5050
// Recursively process generic argument data flow on the generic argument if it itself is generic
51-
if (typeArgument is INamedTypeSymbol namedTypeArgument && namedTypeArgument.IsGenericType) {
52-
ProcessGenericArgumentDataFlow (diagnosticContext, namedTypeArgument);
53-
}
51+
if (typeArgument is INamedTypeSymbol namedTypeArgument && namedTypeArgument.IsGenericType)
52+
ProcessGenericArgumentDataFlow (location, namedTypeArgument, reportDiagnostic);
5453
}
5554
}
5655

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Diagnostics;
67
using System.Diagnostics.CodeAnalysis;
@@ -27,18 +28,19 @@ internal partial struct HandleCallAction
2728
ValueSetLattice<SingleValue> _multiValueLattice;
2829

2930
public HandleCallAction (
30-
in DiagnosticContext diagnosticContext,
31+
Location location,
3132
ISymbol owningSymbol,
3233
IOperation operation,
33-
ValueSetLattice<SingleValue> multiValueLattice)
34+
ValueSetLattice<SingleValue> multiValueLattice,
35+
Action<Diagnostic>? reportDiagnostic)
3436
{
3537
_owningSymbol = owningSymbol;
3638
_operation = operation;
3739
_isNewObj = operation.Kind == OperationKind.ObjectCreation;
38-
_diagnosticContext = diagnosticContext;
40+
_diagnosticContext = new DiagnosticContext (location, reportDiagnostic);
3941
_annotations = FlowAnnotations.Instance;
40-
_reflectionAccessAnalyzer = default;
41-
_requireDynamicallyAccessedMembersAction = new (diagnosticContext, _reflectionAccessAnalyzer);
42+
_reflectionAccessAnalyzer = new (reportDiagnostic);
43+
_requireDynamicallyAccessedMembersAction = new (_diagnosticContext, _reflectionAccessAnalyzer);
4244
_multiValueLattice = multiValueLattice;
4345
}
4446

@@ -201,25 +203,25 @@ private partial bool TryResolveTypeNameForCreateInstanceAndMark (in MethodProxy
201203
}
202204

203205
private partial void MarkStaticConstructor (TypeProxy type)
204-
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForConstructorsOnType (_diagnosticContext, type.Type, BindingFlags.Static, parameterCount: 0);
206+
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForConstructorsOnType (_diagnosticContext.Location, type.Type, BindingFlags.Static, parameterCount: 0);
205207

206208
private partial void MarkEventsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags)
207-
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForEventsOnTypeHierarchy (_diagnosticContext, type.Type, name, bindingFlags);
209+
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForEventsOnTypeHierarchy (_diagnosticContext.Location, type.Type, name, bindingFlags);
208210

209211
private partial void MarkFieldsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags)
210-
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchy (_diagnosticContext, type.Type, name, bindingFlags);
212+
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchy (_diagnosticContext.Location, type.Type, name, bindingFlags);
211213

212214
private partial void MarkPropertiesOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags)
213-
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchy (_diagnosticContext, type.Type, name, bindingFlags);
215+
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchy (_diagnosticContext.Location, type.Type, name, bindingFlags);
214216

215217
private partial void MarkPublicParameterlessConstructorOnType (TypeProxy type)
216-
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForPublicParameterlessConstructor (_diagnosticContext, type.Type);
218+
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForPublicParameterlessConstructor (_diagnosticContext.Location, type.Type);
217219

218220
private partial void MarkConstructorsOnType (TypeProxy type, BindingFlags? bindingFlags, int? parameterCount)
219-
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForConstructorsOnType (_diagnosticContext, type.Type, bindingFlags, parameterCount);
221+
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForConstructorsOnType (_diagnosticContext.Location, type.Type, bindingFlags, parameterCount);
220222

221223
private partial void MarkMethod (MethodProxy method)
222-
=> ReflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForMethod (_diagnosticContext, method.Method);
224+
=> _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForMethod (_diagnosticContext.Location, method.Method);
223225

224226
// TODO: Does the analyzer need to do something here?
225227
private partial void MarkType (TypeProxy type) { }
@@ -229,7 +231,7 @@ private partial bool MarkAssociatedProperty (MethodProxy method)
229231
if (method.Method.MethodKind == MethodKind.PropertyGet || method.Method.MethodKind == MethodKind.PropertySet) {
230232
var property = (IPropertySymbol) method.Method.AssociatedSymbol!;
231233
Debug.Assert (property != null);
232-
ReflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForProperty (_diagnosticContext, property!);
234+
_reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForProperty (_diagnosticContext.Location, property!);
233235
return true;
234236
}
235237

0 commit comments

Comments
 (0)