diff options
Diffstat (limited to 'src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs')
-rw-r--r-- | src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs | 71 |
1 files changed, 47 insertions, 24 deletions
diff --git a/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs b/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs index 7ad261191..819653eeb 100644 --- a/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs +++ b/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs @@ -136,12 +136,9 @@ namespace ILLink.RoslynAnalyzer.DataFlow // Avoid visiting the property reference because for captured properties, we can't // correctly detect whether it is used for reading or writing inside of VisitPropertyReference. // https://github.com/dotnet/roslyn/issues/25057 - IPropertySymbol? property = propertyRef.Property; - IMethodSymbol? setMethod; - while ((setMethod = property.SetMethod) == null) { - if ((property = property.OverriddenProperty) == null) - break; - } + TValue instanceValue = Visit (propertyRef.Instance, state); + TValue value = Visit (operation.Value, state); + IMethodSymbol? setMethod = propertyRef.Property.GetSetMethod (); if (setMethod == null) { // This can happen in a constructor - there it is possible to assign to a property // without a setter. This turns into an assignment to the compiler-generated backing field. @@ -149,17 +146,34 @@ namespace ILLink.RoslynAnalyzer.DataFlow // For now, just don't warn. https://github.com/dotnet/linker/issues/2731 break; } - TValue instanceValue = Visit (propertyRef.Instance, state); - TValue value = Visit (operation.Value, state); - HandleMethodCall ( - setMethod, - instanceValue, - ImmutableArray.Create (value), - operation); + HandleMethodCall (setMethod, instanceValue, ImmutableArray.Create (value), operation); // The return value of a property set expression is the value, // even though a property setter has no return value. return value; } + case IImplicitIndexerReferenceOperation indexerRef: { + // An implicit reference to an indexer where the argument is a System.Index + TValue instanceValue = Visit (indexerRef.Instance, state); + TValue indexArgumentValue = Visit (indexerRef.Argument, state); + TValue value = Visit (operation.Value, state); + + var property = (IPropertySymbol) indexerRef.IndexerSymbol; + + var argumentsBuilder = ImmutableArray.CreateBuilder<TValue> (); + argumentsBuilder.Add (indexArgumentValue); + argumentsBuilder.Add (value); + + IMethodSymbol? setMethod = property.GetSetMethod (); + if (setMethod == null) { + // It might actually be a call to a ref-returning get method, + // like Span<T>.this[int].get. We don't handle ref returns yet. + break; + } + + HandleMethodCall (setMethod, instanceValue, argumentsBuilder.ToImmutableArray (), operation); + return value; + } + // TODO: when setting a property in an attribute, target is an IPropertyReference. case ILocalReferenceOperation localRef: { TValue value = Visit (operation.Value, state); @@ -208,7 +222,7 @@ namespace ILLink.RoslynAnalyzer.DataFlow // (don't have specific I*Operation types), such as pointer dereferences. if (targetOperation.Kind is OperationKind.None) break; - throw new NotImplementedException (targetOperation.GetType ().ToString ()); + throw new NotImplementedException ($"{targetOperation.GetType ().ToString ()}: {targetOperation.Syntax.GetLocation ().GetLineSpan ()}"); } return Visit (operation.Value, state); } @@ -272,17 +286,26 @@ namespace ILLink.RoslynAnalyzer.DataFlow // Accessing property for reading is really a call to the getter // The setter case is handled in assignment operation since here we don't have access to the value to pass to the setter TValue instanceValue = Visit (operation.Instance, state); - IPropertySymbol? property = operation.Property; - IMethodSymbol? getMethod; - while ((getMethod = property.GetMethod) == null) { - if ((property = property.OverriddenProperty) == null) - break; + IMethodSymbol? getMethod = operation.Property.GetGetMethod (); + return HandleMethodCall (getMethod!, instanceValue, ImmutableArray<TValue>.Empty, operation); + } + + public override TValue VisitImplicitIndexerReference (IImplicitIndexerReferenceOperation operation, LocalDataFlowState<TValue, TValueLattice> state) + { + if (!operation.GetValueUsageInfo (Context.OwningSymbol).HasFlag (ValueUsageInfo.Read)) + return TopValue; + + TValue instanceValue = Visit (operation.Instance, state); + TValue indexArgumentValue = Visit (operation.Argument, state); + + if (operation.IndexerSymbol is not IPropertySymbol indexerProperty) { + // For example, System.Span<T>.Slice(int, int). + // Don't try to handle it for now. + return TopValue; } - return HandleMethodCall ( - getMethod!, - instanceValue, - ImmutableArray<TValue>.Empty, - operation); + + IMethodSymbol getMethod = indexerProperty.GetGetMethod ()!; + return HandleMethodCall (getMethod, instanceValue, ImmutableArray.Create (indexArgumentValue), operation); } public override TValue VisitArrayElementReference (IArrayElementReferenceOperation operation, LocalDataFlowState<TValue, TValueLattice> state) |