diff --git a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs index e066d24de29e..b17114687048 100644 --- a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs @@ -49,14 +49,6 @@ public static string GetDisplayName (this ISymbol symbol) { var sb = new StringBuilder (); switch (symbol) { - case IFieldSymbol fieldSymbol: - sb.Append (fieldSymbol.Type); - sb.Append (" "); - sb.Append (fieldSymbol.ContainingSymbol.ToDisplayString ()); - sb.Append ("::"); - sb.Append (fieldSymbol.MetadataName); - break; - case IParameterSymbol parameterSymbol: sb.Append (parameterSymbol.Name); break; @@ -93,5 +85,11 @@ public static bool IsSubclassOf (this ISymbol symbol, string ns, string type) return false; } + + public static bool IsConstructor ([NotNullWhen (returnValue: true)] this ISymbol? symbol) + => (symbol as IMethodSymbol)?.MethodKind == MethodKind.Constructor; + + public static bool IsStaticConstructor ([NotNullWhen (returnValue: true)] this ISymbol? symbol) + => (symbol as IMethodSymbol)?.MethodKind == MethodKind.StaticConstructor; } } diff --git a/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs b/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs index 52e56217c468..05e97d464d88 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs @@ -94,6 +94,11 @@ public override void Initialize (AnalysisContext context) } }, OperationKind.ObjectCreation); + context.RegisterOperationAction (operationContext => { + var fieldReference = (IFieldReferenceOperation) operationContext.Operation; + CheckCalledMember (operationContext, fieldReference.Field, incompatibleMembers); + }, OperationKind.FieldReference); + context.RegisterOperationAction (operationContext => { var propAccess = (IPropertyReferenceOperation) operationContext.Operation; var prop = propAccess.Property; @@ -111,13 +116,19 @@ public override void Initialize (AnalysisContext context) context.RegisterOperationAction (operationContext => { var eventRef = (IEventReferenceOperation) operationContext.Operation; var eventSymbol = (IEventSymbol) eventRef.Member; - CheckCalledMember (operationContext, eventSymbol, incompatibleMembers); + var assignmentOperation = eventRef.Parent as IEventAssignmentOperation; - if (eventSymbol.AddMethod is IMethodSymbol eventAddMethod) + if (assignmentOperation != null && assignmentOperation.Adds && eventSymbol.AddMethod is IMethodSymbol eventAddMethod) CheckCalledMember (operationContext, eventAddMethod, incompatibleMembers); - if (eventSymbol.RemoveMethod is IMethodSymbol eventRemoveMethod) + if (assignmentOperation != null && !assignmentOperation.Adds && eventSymbol.RemoveMethod is IMethodSymbol eventRemoveMethod) CheckCalledMember (operationContext, eventRemoveMethod, incompatibleMembers); + + if (eventSymbol.RaiseMethod is IMethodSymbol eventRaiseMethod) + CheckCalledMember (operationContext, eventRaiseMethod, incompatibleMembers); + + if (AnalyzerDiagnosticTargets.HasFlag (DiagnosticTargets.Event)) + CheckCalledMember (operationContext, eventSymbol, incompatibleMembers); }, OperationKind.EventReference); context.RegisterOperationAction (operationContext => { @@ -163,34 +174,20 @@ void CheckCalledMember ( ISymbol containingSymbol = FindContainingSymbol (operationContext, AnalyzerDiagnosticTargets); // Do not emit any diagnostic if caller is annotated with the attribute too. - if (containingSymbol.HasAttribute (RequiresAttributeName)) - return; - - // Check also for RequiresAttribute in the associated symbol - if (containingSymbol is IMethodSymbol methodSymbol && methodSymbol.AssociatedSymbol is not null && methodSymbol.AssociatedSymbol!.HasAttribute (RequiresAttributeName)) + if (IsMemberInRequiresScope (containingSymbol)) return; if (ReportSpecialIncompatibleMembersDiagnostic (operationContext, incompatibleMembers, member)) return; - if (!member.HasAttribute (RequiresAttributeName)) - return; - // Warn on the most derived base method taking into account covariant returns while (member is IMethodSymbol method && method.OverriddenMethod != null && SymbolEqualityComparer.Default.Equals (method.ReturnType, method.OverriddenMethod.ReturnType)) member = method.OverriddenMethod; - if (TryGetRequiresAttribute (member, out var requiresAttribute)) { - if (member is IMethodSymbol eventAccessorMethod && eventAccessorMethod.AssociatedSymbol is IEventSymbol eventSymbol) { - // If the annotated member is an event accessor, we warn on the event to match the linker behavior. - member = eventAccessorMethod.ContainingSymbol; - operationContext.ReportDiagnostic (Diagnostic.Create (RequiresDiagnosticRule, - eventSymbol.Locations[0], member.Name, GetMessageFromAttribute (requiresAttribute), GetUrlFromAttribute (requiresAttribute))); - return; - } + if (!TargetHasRequiresAttribute (member, out var requiresAttribute)) + return; - ReportRequiresDiagnostic (operationContext, member, requiresAttribute); - } + ReportRequiresDiagnostic (operationContext, member, requiresAttribute); } void CheckMatchingAttributesInOverrides ( @@ -228,7 +225,8 @@ protected enum DiagnosticTargets Property = 0x0002, Field = 0x0004, Event = 0x0008, - All = MethodOrConstructor | Property | Field | Event + Class = 0x0010, + All = MethodOrConstructor | Property | Field | Event | Class } /// @@ -293,6 +291,46 @@ private void ReportMismatchInAttributesDiagnostic (SymbolAnalysisContext symbolA private bool HasMismatchingAttributes (ISymbol member1, ISymbol member2) => member1.HasAttribute (RequiresAttributeName) ^ member2.HasAttribute (RequiresAttributeName); + // TODO: Consider sharing with linker IsMethodInRequiresUnreferencedCodeScope method + /// + /// True if the source of a call is considered to be annotated with the Requires... attribute + /// + protected bool IsMemberInRequiresScope (ISymbol containingSymbol) + { + if (containingSymbol.HasAttribute (RequiresAttributeName) || + containingSymbol.ContainingType.HasAttribute (RequiresAttributeName)) { + return true; + } + + // Check also for RequiresAttribute in the associated symbol + if (containingSymbol is IMethodSymbol { AssociatedSymbol: { } associated } && associated.HasAttribute (RequiresAttributeName)) + return true; + + return false; + } + + // TODO: Consider sharing with linker DoesMethodRequireUnreferencedCode method + /// + /// True if the target of a call is considered to be annotated with the Requires... attribute + /// + protected bool TargetHasRequiresAttribute (ISymbol member, [NotNullWhen (returnValue: true)] out AttributeData? requiresAttribute) + { + requiresAttribute = null; + if (member.IsStaticConstructor ()) { + return false; + } + + if (TryGetRequiresAttribute (member, out requiresAttribute)) { + return true; + } + + // Also check the containing type + if (member.IsStatic || member.IsConstructor ()) { + return TryGetRequiresAttribute (member.ContainingType, out requiresAttribute); + } + return false; + } + protected abstract string GetMessageFromAttribute (AttributeData requiresAttribute); public static string GetUrlFromAttribute (AttributeData? requiresAttribute) diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index b9a3764589a8..ba060ccf08ac 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -92,7 +92,7 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase private protected override string RequiresAttributeFullyQualifiedName => FullyQualifiedRequiresUnreferencedCodeAttribute; - private protected override DiagnosticTargets AnalyzerDiagnosticTargets => DiagnosticTargets.MethodOrConstructor; + private protected override DiagnosticTargets AnalyzerDiagnosticTargets => DiagnosticTargets.MethodOrConstructor | DiagnosticTargets.Class; private protected override DiagnosticDescriptor RequiresDiagnosticRule => s_requiresUnreferencedCodeRule; diff --git a/src/linker/Linker/MethodReferenceExtensions.cs b/src/linker/Linker/MethodReferenceExtensions.cs index e9272fdb7b6d..569a9df5b3c5 100644 --- a/src/linker/Linker/MethodReferenceExtensions.cs +++ b/src/linker/Linker/MethodReferenceExtensions.cs @@ -23,6 +23,20 @@ public static string GetDisplayName (this MethodReference method) return sb.ToString (); } + if (methodDefinition != null && methodDefinition.IsEventMethod ()) { + // Append event name + string name = methodDefinition.SemanticsAttributes switch { + MethodSemanticsAttributes.AddOn => string.Concat (methodDefinition.Name.AsSpan (4), ".add"), + MethodSemanticsAttributes.RemoveOn => string.Concat (methodDefinition.Name.AsSpan (7), ".remove"), + MethodSemanticsAttributes.Fire => string.Concat (methodDefinition.Name.AsSpan (6), ".raise"), + _ => throw new NotSupportedException (), + }; + sb.Append (name); + // Insert declaring type name and namespace + sb.Insert (0, '.').Insert (0, method.DeclaringType.GetDisplayName ()); + return sb.ToString (); + } + // Append parameters sb.Append ("("); if (method.HasParameters) { diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresCapability.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresCapability.cs index f6b21bca68db..f7155e5fec1a 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresCapability.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresCapability.cs @@ -744,24 +744,26 @@ public static void Test () class OnEventMethod { - [ExpectedWarning ("IL2026", "--EventToTestRemove.remove--")] + [ExpectedWarning ("IL2026", "--EventToTestRemove.remove--", ProducedBy = ProducedBy.Trimmer)] static event EventHandler EventToTestRemove { add { } [RequiresUnreferencedCode ("Message for --EventToTestRemove.remove--")] remove { } } - [ExpectedWarning ("IL2026", "--EventToTestAdd.add--")] + [ExpectedWarning ("IL2026", "--EventToTestAdd.add--", ProducedBy = ProducedBy.Trimmer)] static event EventHandler EventToTestAdd { [RequiresUnreferencedCode ("Message for --EventToTestAdd.add--")] add { } remove { } } + [ExpectedWarning ("IL2026", "--EventToTestRemove.remove--")] + [ExpectedWarning ("IL2026", "--EventToTestAdd.add--")] public static void Test () { - EventToTestRemove += (sender, e) => { }; - EventToTestAdd -= (sender, e) => { }; + EventToTestRemove -= (sender, e) => { }; + EventToTestAdd += (sender, e) => { }; } } @@ -924,7 +926,7 @@ static StaticCtor () } } - [ExpectedWarning ("IL2026", "RequiresOnClass.StaticCtor.StaticCtor()", "Message for --StaticCtor--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "RequiresOnClass.StaticCtor.StaticCtor()", "Message for --StaticCtor--")] static void TestStaticCctorRequires () { _ = new StaticCtor (); @@ -941,13 +943,13 @@ static StaticCtorTriggeredByFieldAccess () public static int field; } - [ExpectedWarning ("IL2026", "StaticCtorTriggeredByFieldAccess.field", "Message for --StaticCtorTriggeredByFieldAccess--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "StaticCtorTriggeredByFieldAccess.field", "Message for --StaticCtorTriggeredByFieldAccess--")] static void TestStaticCtorMarkingIsTriggeredByFieldAccessWrite () { StaticCtorTriggeredByFieldAccess.field = 1; } - [ExpectedWarning ("IL2026", "StaticCtorTriggeredByFieldAccess.field", "Message for --StaticCtorTriggeredByFieldAccess--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "StaticCtorTriggeredByFieldAccess.field", "Message for --StaticCtorTriggeredByFieldAccess--")] static void TestStaticCtorMarkingTriggeredOnSecondAccessWrite () { StaticCtorTriggeredByFieldAccess.field = 2; @@ -971,7 +973,7 @@ class StaticCCtorTriggeredByFieldAccessRead public static int field = 42; } - [ExpectedWarning ("IL2026", "StaticCCtorTriggeredByFieldAccessRead.field", "Message for --StaticCCtorTriggeredByFieldAccessRead--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "StaticCCtorTriggeredByFieldAccessRead.field", "Message for --StaticCCtorTriggeredByFieldAccessRead--")] static void TestStaticCtorMarkingIsTriggeredByFieldAccessRead () { var _ = StaticCCtorTriggeredByFieldAccessRead.field; @@ -989,7 +991,7 @@ public void TriggerStaticCtorMarking () } } - [ExpectedWarning ("IL2026", "StaticCtorTriggeredByCtorCalls.StaticCtorTriggeredByCtorCalls()", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "StaticCtorTriggeredByCtorCalls.StaticCtorTriggeredByCtorCalls()")] static void TestStaticCtorTriggeredByCtorCall () { new StaticCtorTriggeredByCtorCalls (); @@ -1001,7 +1003,7 @@ class ClassWithInstanceField public int field = 42; } - [ExpectedWarning ("IL2026", "ClassWithInstanceField.ClassWithInstanceField()", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "ClassWithInstanceField.ClassWithInstanceField()")] static void TestInstanceFieldCallDontWarn () { ClassWithInstanceField instance = new ClassWithInstanceField (); @@ -1101,13 +1103,13 @@ public int Method (int a) } } - [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.StaticMethod()", "--ClassWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.StaticMethod()", "--ClassWithRequires--")] static void TestRequiresInClassAccessedByStaticMethod () { ClassWithRequires.StaticMethod (); } - [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires", "--ClassWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires", "--ClassWithRequires--")] static void TestRequiresInClassAccessedByCctor () { var classObject = new ClassWithRequires (); @@ -1118,10 +1120,10 @@ static void TestRequiresInParentClassAccesedByStaticMethod () ClassWithRequires.NestedClass.NestedStaticMethod (); } - [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.StaticMethod()", "--ClassWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.StaticMethod()", "--ClassWithRequires--")] // Although we suppress the warning from RequiresOnMethod.MethodWithRequires () we still get a warning because we call CallRequiresMethod() which is an static method on a type with RUC - [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.CallMethodWithRequires()", "--ClassWithRequires--", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "ClassWithRequires.Instance", "--ClassWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.CallMethodWithRequires()", "--ClassWithRequires--")] + [ExpectedWarning ("IL2026", "ClassWithRequires.Instance", "--ClassWithRequires--")] static void TestRequiresOnBaseButNotOnDerived () { DerivedWithoutRequires.StaticMethodInInheritedClass (); @@ -1135,7 +1137,7 @@ static void TestRequiresOnBaseButNotOnDerived () DerivedWithoutRequires2.StaticMethod (); } - [ExpectedWarning ("IL2026", "RequiresOnClass.DerivedWithRequires.StaticMethodInInheritedClass()", "--DerivedWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "RequiresOnClass.DerivedWithRequires.StaticMethodInInheritedClass()", "--DerivedWithRequires--")] static void TestRequiresOnDerivedButNotOnBase () { DerivedWithRequires.StaticMethodInInheritedClass (); @@ -1144,8 +1146,8 @@ static void TestRequiresOnDerivedButNotOnBase () DerivedWithRequires.NestedClass.NestedStaticMethod (); } - [ExpectedWarning ("IL2026", "RequiresOnClass.DerivedWithRequires2.StaticMethodInInheritedClass()", "--DerivedWithRequires2--", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.StaticMethod()", "--ClassWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "RequiresOnClass.DerivedWithRequires2.StaticMethodInInheritedClass()", "--DerivedWithRequires2--")] + [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.StaticMethod()", "--ClassWithRequires--")] static void TestRequiresOnBaseAndDerived () { DerivedWithRequires2.StaticMethodInInheritedClass (); @@ -1154,7 +1156,8 @@ static void TestRequiresOnBaseAndDerived () DerivedWithRequires2.NestedClass.NestedStaticMethod (); } - [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.TestSuppressions(Type[])", ProducedBy = ProducedBy.Trimmer)] + // TODO: Parameter signature differs between linker and analyzer + [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequires.TestSuppressions(", "Type[])")] static void TestSuppressionsOnClass () { ClassWithRequires.TestSuppressions (new[] { typeof (ClassWithRequires) }); @@ -1192,14 +1195,14 @@ class MemberTypesWithRequires public static int Property { get; set; } // These should not be reported https://github.com/mono/linker/issues/2218 - [ExpectedWarning ("IL2026", "add_Event", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "remove_Event", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "MemberTypesWithRequires.Event.add", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "MemberTypesWithRequires.Event.remove", ProducedBy = ProducedBy.Trimmer)] public static event EventHandler Event; } - [ExpectedWarning ("IL2026", "MemberTypesWithRequires.field", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "MemberTypesWithRequires.Property.set", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "MemberTypesWithRequires.remove_Event", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "MemberTypesWithRequires.field")] + [ExpectedWarning ("IL2026", "MemberTypesWithRequires.Property.set")] + [ExpectedWarning ("IL2026", "MemberTypesWithRequires.Event.remove")] static void TestOtherMemberTypesWithRequires () { MemberTypesWithRequires.field = 1; @@ -1340,7 +1343,7 @@ class WithRequires [RequiresUnreferencedCode ("--WithRequiresOnlyInstanceFields--")] class WithRequiresOnlyInstanceFields { - public int InstnaceField; + public int InstanceField; } [ExpectedWarning ("IL2109", "ReflectionAccessOnField/DerivedWithoutRequires", "ReflectionAccessOnField.WithRequires", ProducedBy = ProducedBy.Trimmer)] @@ -1367,20 +1370,22 @@ static void TestDAMAccess () typeof (DerivedWithRequires).RequiresPublicFields (); } - [ExpectedWarning ("IL2026", "WithRequires.StaticField", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "WithRequires.StaticField")] + // Analyzer does not recognize the binding flags [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticField", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticField", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticField")] + [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticField", ProducedBy = ProducedBy.Analyzer)] static void TestDirectReflectionAccess () { typeof (WithRequires).GetField (nameof (WithRequires.StaticField)); typeof (WithRequires).GetField (nameof (WithRequires.InstanceField)); // Doesn't warn typeof (WithRequires).GetField ("PrivateStaticField", BindingFlags.NonPublic); - typeof (WithRequiresOnlyInstanceFields).GetField (nameof (WithRequiresOnlyInstanceFields.InstnaceField)); // Doesn't warn + typeof (WithRequiresOnlyInstanceFields).GetField (nameof (WithRequiresOnlyInstanceFields.InstanceField)); // Doesn't warn typeof (DerivedWithoutRequires).GetField (nameof (DerivedWithRequires.DerivedStaticField)); // Doesn't warn typeof (DerivedWithRequires).GetField (nameof (DerivedWithRequires.DerivedStaticField)); } - [ExpectedWarning ("IL2026", "WithRequires.StaticField", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "WithRequires.StaticField")] [DynamicDependency (nameof (WithRequires.StaticField), typeof (WithRequires))] [DynamicDependency (nameof (WithRequires.InstanceField), typeof (WithRequires))] // Doesn't warn [DynamicDependency (DynamicallyAccessedMemberTypes.PublicFields, typeof (DerivedWithoutRequires))] // Doesn't warn @@ -1432,12 +1437,12 @@ class WithRequires { // These should be reported only in TestDirectReflectionAccess // https://github.com/mono/linker/issues/2218 - [ExpectedWarning ("IL2026", "add_StaticEvent", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "remove_StaticEvent", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "StaticEvent.add", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "StaticEvent.remove", ProducedBy = ProducedBy.Trimmer)] public static event EventHandler StaticEvent; } - [ExpectedWarning ("IL2026", "add_StaticEvent", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "StaticEvent.add", ProducedBy = ProducedBy.Trimmer)] static void TestDirectReflectionAccess () { typeof (WithRequires).GetEvent (nameof (WithRequires.StaticEvent)); diff --git a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index fe2b6bf8e367..bab3a4f591b5 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -679,7 +679,7 @@ void VerifyLoggedMessages (AssemblyDefinition original, LinkerTestLogger logger, bool foundReflectionAccessPatternAttributesToVerify = false; foreach (var attr in attrProvider.CustomAttributes) { if (!IsProducedByLinker (attr)) - break; + continue; switch (attr.AttributeType.Name) {