From 1f5c3a74d8e0236e81a884f76e9c3fe8c78c6f67 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 26 Jun 2025 01:04:03 +0000 Subject: [PATCH 1/3] Track assignment to property backing field --- .../DataFlow/LocalDataFlowVisitor.cs | 33 ++++++++---- .../TrimAnalysis/FieldValue.cs | 16 ++++-- .../TrimAnalysis/FlowAnnotations.cs | 8 +++ .../TrimAnalysis/GenericArgumentDataFlow.cs | 10 ++++ .../TrimAnalysis/TrimAnalysisPatternStore.cs | 16 ++++++ .../TrimAnalysisPropertyAccessPattern.cs | 52 +++++++++++++++++++ .../TrimAnalysis/TrimAnalysisVisitor.cs | 39 ++++++++++++-- .../DataFlow/ConstructorDataFlow.cs | 4 +- .../DataFlow/PropertyDataFlow.cs | 8 ++- 9 files changed, 161 insertions(+), 25 deletions(-) create mode 100644 src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPropertyAccessPattern.cs diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs index 1fe0177a18020e..fb091448e1ac98 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs @@ -129,7 +129,9 @@ public abstract TConditionValue GetConditionValue( IOperation branchValueOperation, LocalDataFlowState state); - public abstract TValue GetFieldTargetValue(IFieldSymbol field, IFieldReferenceOperation fieldReferenceOperation, in TContext context); + public abstract TValue GetFieldTargetValue(IFieldReferenceOperation fieldReference, in TContext context); + + public abstract TValue GetPropertyTargetValue(IPropertyReferenceOperation propertyReference, in TContext context); public abstract TValue GetParameterTargetValue(IParameterSymbol parameter); @@ -247,7 +249,7 @@ private TValue ProcessSingleTargetAssignment(IOperation targetOperation, IAssign var current = state.Current; TValue targetValue = targetOperation switch { - IFieldReferenceOperation fieldRef => GetFieldTargetValue(fieldRef.Field, fieldRef, in current.Context), + IFieldReferenceOperation fieldRef => GetFieldTargetValue(fieldRef, in current.Context), IParameterReferenceOperation parameterRef => GetParameterTargetValue(parameterRef.Parameter), _ => throw new InvalidOperationException() }; @@ -266,19 +268,28 @@ private TValue ProcessSingleTargetAssignment(IOperation targetOperation, IAssign TValue instanceValue = Visit(propertyRef.Instance, state); TValue value = Visit(operation.Value, state); IMethodSymbol? setMethod = propertyRef.Property.GetSetMethod(); - if (setMethod == null) + + if (setMethod == null || + (OwningSymbol is IPropertySymbol && (ControlFlowGraph.OriginalOperation is not IAttributeOperation))) { + // This can be a write to a byref return of a property getter. + // Skip for now: https://github.com/dotnet/linker/issues/2158 + if (propertyRef.Property.RefKind is not RefKind.None) + break; + // 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. - // To match the linker, this should warn about the compiler-generated backing field. - // For now, just don't warn. https://github.com/dotnet/runtime/issues/93277 - break; + // Handle this similarly to field assignments. + + // Even if the property has a set method, if the assignment takes place in a property initializer, + // the write becomes a direct write to the underlying field. This should be treated the same as + // the case where there is no set method. + + var current = state.Current; + TValue targetValue = GetPropertyTargetValue(propertyRef, in current.Context); + HandleAssignment(value, targetValue, operation, in current.Context); + return value; } - // Even if the property has a set method, if the assignment takes place in a property initializer, - // the write becomes a direct write to the underlying field. This should be treated the same as - // the case where there is no set method. - if (OwningSymbol is IPropertySymbol && (ControlFlowGraph.OriginalOperation is not IAttributeOperation)) - break; // Property may be an indexer, in which case there will be one or more index arguments followed by a value argument ImmutableArray.Builder arguments = ImmutableArray.CreateBuilder(); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FieldValue.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FieldValue.cs index dce9fb248936c7..2f238754c20631 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FieldValue.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FieldValue.cs @@ -12,13 +12,23 @@ namespace ILLink.Shared.TrimAnalysis internal partial record FieldValue { public FieldValue(IFieldSymbol fieldSymbol) + : this(fieldSymbol, fieldSymbol.Type, FlowAnnotations.GetFieldAnnotation(fieldSymbol)) + { + } + + public FieldValue(IPropertySymbol propertySymbol) + : this(propertySymbol, propertySymbol.Type, FlowAnnotations.GetFieldAnnotation(propertySymbol)) + { + } + + private FieldValue(ISymbol fieldSymbol, ITypeSymbol fieldType, DynamicallyAccessedMemberTypes annotations) { FieldSymbol = fieldSymbol; - StaticType = new(fieldSymbol.Type); - DynamicallyAccessedMemberTypes = FlowAnnotations.GetFieldAnnotation(fieldSymbol); + StaticType = new(fieldType); + DynamicallyAccessedMemberTypes = annotations; } - public readonly IFieldSymbol FieldSymbol; + public readonly ISymbol FieldSymbol; public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs index 32d604217f5ad7..23b3f095879b59 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs @@ -120,6 +120,14 @@ internal static DynamicallyAccessedMemberTypes GetFieldAnnotation(IFieldSymbol f return field.GetDynamicallyAccessedMemberTypes(); } + internal static DynamicallyAccessedMemberTypes GetFieldAnnotation(IPropertySymbol property) + { + if (!property.OriginalDefinition.Type.IsTypeInterestingForDataflow(isByRef: false)) + return DynamicallyAccessedMemberTypes.None; + + return property.GetDynamicallyAccessedMemberTypes(); + } + internal static DynamicallyAccessedMemberTypes GetTypeAnnotations(INamedTypeSymbol type) { DynamicallyAccessedMemberTypes typeAnnotation = type.GetDynamicallyAccessedMemberTypes(); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericArgumentDataFlow.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericArgumentDataFlow.cs index 679760c8528470..280d81b876e08f 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericArgumentDataFlow.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericArgumentDataFlow.cs @@ -33,6 +33,11 @@ public static void ProcessGenericArgumentDataFlow(Location location, IFieldSymbo ProcessGenericArgumentDataFlow(location, field.ContainingType, reportDiagnostic); } + public static void ProcessGenericArgumentDataFlow(Location location, IPropertySymbol property, Action reportDiagnostic) + { + ProcessGenericArgumentDataFlow(location, property.ContainingType, reportDiagnostic); + } + private static void ProcessGenericArgumentDataFlow( Location location, ImmutableArray typeArguments, @@ -102,6 +107,11 @@ public static bool RequiresGenericArgumentDataFlow(IFieldSymbol field) return RequiresGenericArgumentDataFlow(field.ContainingType); } + public static bool RequiresGenericArgumentDataFlow(IPropertySymbol property) + { + return RequiresGenericArgumentDataFlow(property.ContainingType); + } + private static bool RequiresGenericArgumentDataFlow(ImmutableArray typeParameters) { foreach (var typeParameter in typeParameters) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPatternStore.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPatternStore.cs index 34e5226b31d949..9a7f57e72620a4 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPatternStore.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPatternStore.cs @@ -14,6 +14,7 @@ internal readonly struct TrimAnalysisPatternStore { private readonly Dictionary AssignmentPatterns; private readonly Dictionary FieldAccessPatterns; + private readonly Dictionary PropertyAccessPatterns; private readonly Dictionary GenericInstantiationPatterns; private readonly Dictionary MethodCallPatterns; private readonly Dictionary ReflectionAccessPatterns; @@ -27,6 +28,7 @@ public TrimAnalysisPatternStore( { AssignmentPatterns = new Dictionary(); FieldAccessPatterns = new Dictionary(); + PropertyAccessPatterns = new Dictionary(); GenericInstantiationPatterns = new Dictionary(); MethodCallPatterns = new Dictionary(); ReflectionAccessPatterns = new Dictionary(); @@ -35,6 +37,17 @@ public TrimAnalysisPatternStore( FeatureContextLattice = featureContextLattice; } + public void Add(TrimAnalysisPropertyAccessPattern pattern) + { + if (!PropertyAccessPatterns.TryGetValue(pattern.Operation, out var existingPattern)) + { + PropertyAccessPatterns.Add(pattern.Operation, pattern); + return; + } + + PropertyAccessPatterns[pattern.Operation] = pattern.Merge(FeatureContextLattice, existingPattern); + } + public void Add(TrimAnalysisAssignmentPattern trimAnalysisPattern) { // Finally blocks will be analyzed multiple times, once for normal control flow and once @@ -117,6 +130,9 @@ public void ReportDiagnostics(DataFlowAnalyzerContext context, Action reportDiagnostic) + { + DiagnosticContext diagnosticContext = new(Operation.Syntax.GetLocation(), reportDiagnostic); + foreach (var requiresAnalyzer in context.EnabledRequiresAnalyzers) + requiresAnalyzer.CheckAndCreateRequiresDiagnostic(Operation, Property, OwningSymbol, context, FeatureContext, in diagnosticContext); + } + } +} diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs index 69b41f5929f447..09901e2b0ea658 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs @@ -176,7 +176,7 @@ public override MultiValue VisitFieldReference(IFieldReferenceOperation fieldRef return constValue; var current = state.Current; - return GetFieldTargetValue(fieldRef.Field, fieldRef, in current.Context); + return GetFieldTargetValue(fieldRef, in current.Context); } public override MultiValue VisitTypeOf(ITypeOfOperation typeOfOperation, StateValue state) @@ -224,17 +224,33 @@ operation.OperatorMethod is null && // - method calls // - value returned from a method - public override MultiValue GetFieldTargetValue(IFieldSymbol field, IFieldReferenceOperation fieldReferenceOperation, in FeatureContext featureContext) + public override MultiValue GetFieldTargetValue(IFieldReferenceOperation fieldReference, in FeatureContext featureContext) { + var field = fieldReference.Field; + TrimAnalysisPatterns.Add( - new TrimAnalysisFieldAccessPattern(field, fieldReferenceOperation, OwningSymbol, featureContext) + new TrimAnalysisFieldAccessPattern(field, fieldReference, OwningSymbol, featureContext) ); - ProcessGenericArgumentDataFlow(field, fieldReferenceOperation, featureContext); + ProcessGenericArgumentDataFlow(field, fieldReference, featureContext); return new FieldValue(field); } + public override MultiValue GetPropertyTargetValue(IPropertyReferenceOperation propertyReference, in FeatureContext featureContext) + { + var property = propertyReference.Property; + + TrimAnalysisPatterns.Add( + new TrimAnalysisPropertyAccessPattern(propertyReference.Property, propertyReference, OwningSymbol, featureContext) + ); + + ProcessGenericArgumentDataFlow(property, propertyReference, featureContext); + + return new FieldValue(property); + } + + public override MultiValue GetParameterTargetValue(IParameterSymbol parameter) => new MethodParameterValue(parameter); @@ -453,6 +469,21 @@ private void ProcessGenericArgumentDataFlow(IFieldSymbol field, IOperation opera } } + private void ProcessGenericArgumentDataFlow(IPropertySymbol property, IOperation operation, in FeatureContext featureContext) + { + if (!property.IsStatic) + return; + + if (GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(property)) + { + TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationPattern( + property, + operation, + OwningSymbol, + featureContext)); + } + } + private static bool TryGetConstantValue(IOperation operation, out MultiValue constValue) { if (operation.ConstantValue.HasValue) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ConstructorDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ConstructorDataFlow.cs index 462c8579605557..60309e3acdc96f 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ConstructorDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ConstructorDataFlow.cs @@ -39,11 +39,11 @@ public DataFlowInConstructor() [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type annotatedField = GetUnknown(); - [ExpectedWarning("IL2074", nameof(GetUnknown), nameof(AnnotatedProperty), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/93277", CompilerGeneratedCode = true)] + [ExpectedWarning("IL2074", nameof(GetUnknown), nameof(AnnotatedProperty), CompilerGeneratedCode = true)] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type AnnotatedProperty { get; } = GetUnknown(); - [ExpectedWarning("IL2074", nameof(GetUnknown), nameof(AnnotatedPropertyWithSetter), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/93277", CompilerGeneratedCode = true)] + [ExpectedWarning("IL2074", nameof(GetUnknown), nameof(AnnotatedPropertyWithSetter), CompilerGeneratedCode = true)] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type AnnotatedPropertyWithSetter { get; set; } = GetUnknown(); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/PropertyDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/PropertyDataFlow.cs index 76ead1e85bfafc..8adf581c6c5031 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/PropertyDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/PropertyDataFlow.cs @@ -489,8 +489,7 @@ class WriteToGetOnlyProperty [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public Type GetOnlyProperty { get; } - // Analyzer doesn't warn about compiler-generated backing field of property: https://github.com/dotnet/runtime/issues/93277 - [ExpectedWarning("IL2074", nameof(WriteToGetOnlyProperty), nameof(GetUnknownType), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning("IL2074", nameof(WriteToGetOnlyProperty), nameof(GetUnknownType))] public WriteToGetOnlyProperty() { GetOnlyProperty = GetUnknownType(); @@ -568,9 +567,8 @@ class WriteCapturedGetOnlyProperty [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type GetOnlyProperty { get; } - // Analyzer doesn't warn about compiler-generated backing field of property: https://github.com/dotnet/runtime/issues/93277 - [ExpectedWarning("IL2074", nameof(WriteCapturedGetOnlyProperty), nameof(GetUnknownType), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning("IL2074", nameof(WriteCapturedGetOnlyProperty), nameof(GetTypeWithPublicConstructors), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning("IL2074", nameof(WriteCapturedGetOnlyProperty), nameof(GetUnknownType))] + [ExpectedWarning("IL2074", nameof(WriteCapturedGetOnlyProperty), nameof(GetTypeWithPublicConstructors))] public WriteCapturedGetOnlyProperty() { GetOnlyProperty = GetUnknownType() ?? GetTypeWithPublicConstructors(); From 0c380367e632dfa5f4c9cab4f62182dc2ddf97bc Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 26 Jun 2025 17:01:05 +0000 Subject: [PATCH 2/3] PR feedback - GetBackingFieldAnnotation instead of GetFieldAnnotation - Rename other "Property" types/methods to "BackingField" for consistency --- .../DataFlow/LocalDataFlowVisitor.cs | 4 ++-- .../TrimAnalysis/FieldValue.cs | 2 +- .../TrimAnalysis/FlowAnnotations.cs | 2 +- ... => TrimAnalysisBackingFieldAccessPattern.cs} | 10 +++++----- .../TrimAnalysis/TrimAnalysisPatternStore.cs | 16 ++++++++-------- .../TrimAnalysis/TrimAnalysisVisitor.cs | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) rename src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/{TrimAnalysisPropertyAccessPattern.cs => TrimAnalysisBackingFieldAccessPattern.cs} (85%) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs index fb091448e1ac98..e1f8eacbf7957e 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs @@ -131,7 +131,7 @@ public abstract TConditionValue GetConditionValue( public abstract TValue GetFieldTargetValue(IFieldReferenceOperation fieldReference, in TContext context); - public abstract TValue GetPropertyTargetValue(IPropertyReferenceOperation propertyReference, in TContext context); + public abstract TValue GetBackingFieldTargetValue(IPropertyReferenceOperation propertyReference, in TContext context); public abstract TValue GetParameterTargetValue(IParameterSymbol parameter); @@ -286,7 +286,7 @@ private TValue ProcessSingleTargetAssignment(IOperation targetOperation, IAssign // the case where there is no set method. var current = state.Current; - TValue targetValue = GetPropertyTargetValue(propertyRef, in current.Context); + TValue targetValue = GetBackingFieldTargetValue(propertyRef, in current.Context); HandleAssignment(value, targetValue, operation, in current.Context); return value; } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FieldValue.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FieldValue.cs index 2f238754c20631..35bf5214c0fce5 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FieldValue.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FieldValue.cs @@ -17,7 +17,7 @@ public FieldValue(IFieldSymbol fieldSymbol) } public FieldValue(IPropertySymbol propertySymbol) - : this(propertySymbol, propertySymbol.Type, FlowAnnotations.GetFieldAnnotation(propertySymbol)) + : this(propertySymbol, propertySymbol.Type, FlowAnnotations.GetBackingFieldAnnotation(propertySymbol)) { } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs index 23b3f095879b59..78368423563f27 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs @@ -120,7 +120,7 @@ internal static DynamicallyAccessedMemberTypes GetFieldAnnotation(IFieldSymbol f return field.GetDynamicallyAccessedMemberTypes(); } - internal static DynamicallyAccessedMemberTypes GetFieldAnnotation(IPropertySymbol property) + internal static DynamicallyAccessedMemberTypes GetBackingFieldAnnotation(IPropertySymbol property) { if (!property.OriginalDefinition.Type.IsTypeInterestingForDataflow(isByRef: false)) return DynamicallyAccessedMemberTypes.None; diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPropertyAccessPattern.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs similarity index 85% rename from src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPropertyAccessPattern.cs rename to src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs index 2788a45c2de261..30f73ae1c9dfb4 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPropertyAccessPattern.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs @@ -8,14 +8,14 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis { - internal readonly record struct TrimAnalysisPropertyAccessPattern + internal readonly record struct TrimAnalysisBackingFieldAccessPattern { public IPropertySymbol Property { get; init; } public IPropertyReferenceOperation Operation { get; init; } public ISymbol OwningSymbol { get; init; } public FeatureContext FeatureContext { get; init; } - public TrimAnalysisPropertyAccessPattern( + public TrimAnalysisBackingFieldAccessPattern( IPropertySymbol property, IPropertyReferenceOperation operation, ISymbol owningSymbol, @@ -27,15 +27,15 @@ public TrimAnalysisPropertyAccessPattern( FeatureContext = featureContext; } - public TrimAnalysisPropertyAccessPattern Merge( + public TrimAnalysisBackingFieldAccessPattern Merge( FeatureContextLattice featureContextLattice, - TrimAnalysisPropertyAccessPattern other) + TrimAnalysisBackingFieldAccessPattern other) { Debug.Assert(SymbolEqualityComparer.Default.Equals(Property, other.Property)); Debug.Assert(Operation == other.Operation); Debug.Assert(SymbolEqualityComparer.Default.Equals(OwningSymbol, other.OwningSymbol)); - return new TrimAnalysisPropertyAccessPattern( + return new TrimAnalysisBackingFieldAccessPattern( Property, Operation, OwningSymbol, diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPatternStore.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPatternStore.cs index 9a7f57e72620a4..96c1505a6cf9c9 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPatternStore.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisPatternStore.cs @@ -14,7 +14,7 @@ internal readonly struct TrimAnalysisPatternStore { private readonly Dictionary AssignmentPatterns; private readonly Dictionary FieldAccessPatterns; - private readonly Dictionary PropertyAccessPatterns; + private readonly Dictionary BackingFieldAccessPatterns; private readonly Dictionary GenericInstantiationPatterns; private readonly Dictionary MethodCallPatterns; private readonly Dictionary ReflectionAccessPatterns; @@ -28,7 +28,7 @@ public TrimAnalysisPatternStore( { AssignmentPatterns = new Dictionary(); FieldAccessPatterns = new Dictionary(); - PropertyAccessPatterns = new Dictionary(); + BackingFieldAccessPatterns = new Dictionary(); GenericInstantiationPatterns = new Dictionary(); MethodCallPatterns = new Dictionary(); ReflectionAccessPatterns = new Dictionary(); @@ -37,15 +37,15 @@ public TrimAnalysisPatternStore( FeatureContextLattice = featureContextLattice; } - public void Add(TrimAnalysisPropertyAccessPattern pattern) + public void Add(TrimAnalysisBackingFieldAccessPattern pattern) { - if (!PropertyAccessPatterns.TryGetValue(pattern.Operation, out var existingPattern)) + if (!BackingFieldAccessPatterns.TryGetValue(pattern.Operation, out var existingPattern)) { - PropertyAccessPatterns.Add(pattern.Operation, pattern); + BackingFieldAccessPatterns.Add(pattern.Operation, pattern); return; } - PropertyAccessPatterns[pattern.Operation] = pattern.Merge(FeatureContextLattice, existingPattern); + BackingFieldAccessPatterns[pattern.Operation] = pattern.Merge(FeatureContextLattice, existingPattern); } public void Add(TrimAnalysisAssignmentPattern trimAnalysisPattern) @@ -130,8 +130,8 @@ public void ReportDiagnostics(DataFlowAnalyzerContext context, Action Date: Thu, 26 Jun 2025 16:13:58 -0700 Subject: [PATCH 3/3] Update src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs Co-authored-by: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> --- .../TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs index 30f73ae1c9dfb4..7ad0d048f50125 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + using System; using System.Diagnostics; using ILLink.Shared.TrimAnalysis;