Skip to content

Commit

Permalink
Check Locations length before accessing (#87659)
Browse files Browse the repository at this point in the history
Fixes an `IndexOutOfRangeException` thrown by the analyzer when
analyzing method parameters for the implicitly generated Main
method when using top-level statements. These method parameters
don't have location info because they are not in user code.

This also adds checks in the other places this analyzer accesses
`Locations[0]`, to be safe.
  • Loading branch information
sbomer authored Jun 17, 2023
1 parent 61ce817 commit fe88013
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ void AddRange (DiagnosticId first, DiagnosticId last)

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => GetSupportedDiagnostics ();

static Location GetPrimaryLocation (ImmutableArray<Location> locations) => locations.Length > 0 ? locations[0] : Location.None;

public override void Initialize (AnalysisContext context)
{
if (!System.Diagnostics.Debugger.IsAttached)
Expand All @@ -87,19 +89,20 @@ public override void Initialize (AnalysisContext context)
// Examine generic instantiations in base types and interface list
context.RegisterSymbolAction (context => {
var type = (INamedTypeSymbol) context.Symbol;
var location = GetPrimaryLocation (type.Locations);
// RUC on type doesn't silence DAM warnings about generic base/interface types.
// This knowledge lives in IsInRequiresUnreferencedCodeAttributeScope,
// which we still call for consistency here, but it is expected to return false.
if (type.IsInRequiresUnreferencedCodeAttributeScope (out _))
return;
if (type.BaseType is INamedTypeSymbol baseType) {
foreach (var diagnostic in ProcessGenericParameters (baseType, type.Locations[0]))
foreach (var diagnostic in ProcessGenericParameters (baseType, location))
context.ReportDiagnostic (diagnostic);
}
foreach (var interfaceType in type.Interfaces) {
foreach (var diagnostic in ProcessGenericParameters (interfaceType, type.Locations[0]))
foreach (var diagnostic in ProcessGenericParameters (interfaceType, location))
context.ReportDiagnostic (diagnostic);
}
}, SymbolKind.NamedType);
Expand All @@ -111,11 +114,11 @@ public override void Initialize (AnalysisContext context)
return;
var returnType = method.ReturnType;
foreach (var diagnostic in ProcessGenericParameters (returnType, method.Locations[0]))
foreach (var diagnostic in ProcessGenericParameters (returnType, GetPrimaryLocation (method.Locations)))
context.ReportDiagnostic (diagnostic);
foreach (var parameter in method.Parameters) {
foreach (var diagnostic in ProcessGenericParameters (parameter.Type, parameter.Locations[0]))
foreach (var diagnostic in ProcessGenericParameters (parameter.Type, GetPrimaryLocation (parameter.Locations)))
context.ReportDiagnostic (diagnostic);
}
}, SymbolKind.Method);
Expand All @@ -125,7 +128,7 @@ public override void Initialize (AnalysisContext context)
if (field.IsInRequiresUnreferencedCodeAttributeScope (out _))
return;
foreach (var diagnostic in ProcessGenericParameters (field.Type, field.Locations[0]))
foreach (var diagnostic in ProcessGenericParameters (field.Type, GetPrimaryLocation (field.Locations)))
context.ReportDiagnostic (diagnostic);
}, SymbolKind.Field);
// Examine generic instantiations in invocations of generically instantiated methods,
Expand Down Expand Up @@ -244,19 +247,20 @@ static List<Diagnostic> GetDynamicallyAccessedMembersDiagnostics (SingleValue so

static void VerifyMemberOnlyApplyToTypesOrStrings (SymbolAnalysisContext context, ISymbol member)
{
var location = GetPrimaryLocation (member.Locations);
if (member is IFieldSymbol field && field.GetDynamicallyAccessedMemberTypes () != DynamicallyAccessedMemberTypes.None && !field.Type.IsTypeInterestingForDataflow ())
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersOnFieldCanOnlyApplyToTypesOrStrings), member.Locations[0], member.GetDisplayName ()));
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersOnFieldCanOnlyApplyToTypesOrStrings), location, member.GetDisplayName ()));
else if (member is IMethodSymbol method) {
if (method.GetDynamicallyAccessedMemberTypesOnReturnType () != DynamicallyAccessedMemberTypes.None && !method.ReturnType.IsTypeInterestingForDataflow ())
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersOnMethodReturnValueCanOnlyApplyToTypesOrStrings), member.Locations[0], member.GetDisplayName ()));
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersOnMethodReturnValueCanOnlyApplyToTypesOrStrings), location, member.GetDisplayName ()));
if (method.GetDynamicallyAccessedMemberTypes () != DynamicallyAccessedMemberTypes.None && !method.ContainingType.IsTypeInterestingForDataflow ())
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods), member.Locations[0]));
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods), location));
foreach (var parameter in method.Parameters) {
if (parameter.GetDynamicallyAccessedMemberTypes () != DynamicallyAccessedMemberTypes.None && !parameter.Type.IsTypeInterestingForDataflow ())
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings), member.Locations[0], parameter.GetDisplayName (), member.GetDisplayName ()));
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings), location, parameter.GetDisplayName (), member.GetDisplayName ()));
}
} else if (member is IPropertySymbol property && property.GetDynamicallyAccessedMemberTypes () != DynamicallyAccessedMemberTypes.None && !property.Type.IsTypeInterestingForDataflow ()) {
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersOnPropertyCanOnlyApplyToTypesOrStrings), member.Locations[0], member.GetDisplayName ()));
context.ReportDiagnostic (Diagnostic.Create (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersOnPropertyCanOnlyApplyToTypesOrStrings), location, member.GetDisplayName ()));
}
}

Expand All @@ -277,7 +281,7 @@ static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbo
(IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements (overrideMethod,
baseMethod, overrideMethodReturnAnnotation, baseMethodReturnAnnotation);

Location attributableSymbolLocation = attributableMethod.Locations[0];
Location attributableSymbolLocation = GetPrimaryLocation (attributableMethod.Locations);

// code fix does not support merging multiple attributes. If an attribute is present or the method is not in source, do not provide args for code fix.
(Location[]? sourceLocation, Dictionary<string, string?>? DAMArgs) = (!attributableSymbolLocation.IsInSource
Expand All @@ -287,7 +291,7 @@ static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbo

context.ReportDiagnostic (Diagnostic.Create (
DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodReturnValueBetweenOverrides),
overrideMethod.Locations[0], sourceLocation, DAMArgs?.ToImmutableDictionary (), overrideMethod.GetDisplayName (), baseMethod.GetDisplayName ()));
GetPrimaryLocation (overrideMethod.Locations), sourceLocation, DAMArgs?.ToImmutableDictionary (), overrideMethod.GetDisplayName (), baseMethod.GetDisplayName ()));
}

foreach (var overrideParam in overrideMethod.GetMetadataParameters ()) {
Expand Down Expand Up @@ -320,7 +324,8 @@ static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbo

(IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements (overrideMethod, baseMethod, methodTypeParameterAnnotation, overriddenMethodTypeParameterAnnotation);

Location attributableSymbolLocation = attributableMethod.TypeParameters[i].Locations[0];
var attributableSymbol = attributableMethod.TypeParameters[i];
Location attributableSymbolLocation = GetPrimaryLocation (attributableSymbol.Locations);

// code fix does not support merging multiple attributes. If an attribute is present or the method is not in source, do not provide args for code fix.
(Location[]? sourceLocation, Dictionary<string, string?>? DAMArgs) = (!attributableSymbolLocation.IsInSource
Expand All @@ -330,7 +335,7 @@ static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbo

context.ReportDiagnostic (Diagnostic.Create (
DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnGenericParameterBetweenOverrides),
overrideMethod.TypeParameters[i].Locations[0], sourceLocation, DAMArgs?.ToImmutableDictionary (),
GetPrimaryLocation (overrideMethod.TypeParameters[i].Locations), sourceLocation, DAMArgs?.ToImmutableDictionary (),
overrideMethod.TypeParameters[i].GetDisplayName (), overrideMethod.GetDisplayName (),
baseMethod.TypeParameters[i].GetDisplayName (), baseMethod.GetDisplayName ()));
}
Expand All @@ -339,7 +344,7 @@ static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbo
if (!overrideMethod.IsStatic && overrideMethod.GetDynamicallyAccessedMemberTypes () != baseMethod.GetDynamicallyAccessedMemberTypes ())
context.ReportDiagnostic (Diagnostic.Create (
DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnImplicitThisBetweenOverrides),
overrideMethod.Locations[0],
GetPrimaryLocation (overrideMethod.Locations),
overrideMethod.GetDisplayName (), baseMethod.GetDisplayName ()));
}

Expand All @@ -364,10 +369,11 @@ static void VerifyDamOnPropertyAndAccessorMatch (SymbolAnalysisContext context,
// None on parameter of 'set' matches unannotated
|| methodSymbol.MethodKind == MethodKind.PropertySet
&& methodSymbol.Parameters[methodSymbol.Parameters.Length - 1].GetDynamicallyAccessedMemberTypes () != DynamicallyAccessedMemberTypes.None) {
var associatedSymbol = methodSymbol.AssociatedSymbol!;
context.ReportDiagnostic (Diagnostic.Create (
DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor),
methodSymbol.AssociatedSymbol!.Locations[0],
methodSymbol.AssociatedSymbol!.GetDisplayName (),
GetPrimaryLocation (associatedSymbol.Locations),
associatedSymbol.GetDisplayName (),
methodSymbol.GetDisplayName ()
));
return;
Expand Down
Loading

0 comments on commit fe88013

Please sign in to comment.