diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs index 5aea7858179e71..5aef4ca5adff64 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs @@ -223,7 +223,7 @@ private static void VerifyDamOnMethodsMatch(SymbolAnalysisContext context, IMeth 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? DAMArgs) = (!attributableSymbolLocation.IsInSource + (Location[]? sourceLocation, Dictionary? DAMArgs) = (!CanOfferCodeFixAt(attributableSymbolLocation, context.Compilation) || (overrideMethod.TryGetReturnAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _) && baseMethod.TryGetReturnAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) ) ? (null, null) : CreateArguments(attributableSymbolLocation, missingAttribute); @@ -247,7 +247,7 @@ private static void VerifyDamOnMethodsMatch(SymbolAnalysisContext context, IMeth Location attributableSymbolLocation = attributableMethod.GetParameter(overrideParam.Index).Location!; // 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? DAMArgs) = (!attributableSymbolLocation.IsInSource + (Location[]? sourceLocation, Dictionary? DAMArgs) = (!CanOfferCodeFixAt(attributableSymbolLocation, context.Compilation) || (overrideParam.ParameterSymbol!.TryGetAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _) && baseParam.ParameterSymbol!.TryGetAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) ) ? (null, null) : CreateArguments(attributableSymbolLocation, missingAttribute); @@ -273,7 +273,7 @@ private static void VerifyDamOnMethodsMatch(SymbolAnalysisContext context, IMeth 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? DAMArgs) = (!attributableSymbolLocation.IsInSource + (Location[]? sourceLocation, Dictionary? DAMArgs) = (!CanOfferCodeFixAt(attributableSymbolLocation, context.Compilation) || (overrideMethod.TypeParameters[i].TryGetAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _) && baseMethod.TypeParameters[i].TryGetAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) ) ? (null, null) : CreateArguments(attributableSymbolLocation, missingAttribute); @@ -351,8 +351,16 @@ private static void VerifyDamOnPropertyAndAccessorMatch(SymbolAnalysisContext co } } - private static (IMethodSymbol Method, DynamicallyAccessedMemberTypes Requirements) GetTargetAndRequirements(IMethodSymbol method, IMethodSymbol overriddenMethod, DynamicallyAccessedMemberTypes methodAnnotation, DynamicallyAccessedMemberTypes overriddenMethodAnnotation) - { + /// + /// Determines whether a code fix location can be attached to a diagnostic. The location must point into the + /// compilation being analyzed, otherwise Roslyn rejects the reported diagnostic. Note that a location can be in + /// source and still belong to another project: the IDE models project-to-project references as compilation + /// references, which expose source symbols whose syntax trees belong to a different compilation. + /// + private static bool CanOfferCodeFixAt(Location location, Compilation compilation) + => location.SourceTree is { } sourceTree && compilation.ContainsSyntaxTree(sourceTree); + + private static (IMethodSymbol Method, DynamicallyAccessedMemberTypes Requirements) GetTargetAndRequirements(IMethodSymbol method, IMethodSymbol overriddenMethod, DynamicallyAccessedMemberTypes methodAnnotation, DynamicallyAccessedMemberTypes overriddenMethodAnnotation) { DynamicallyAccessedMemberTypes mismatchedArgument; IMethodSymbol paramNeedsAttributes; if (methodAnnotation == DynamicallyAccessedMemberTypes.None) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs index d811bb8de6afe0..44f41973a01746 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs @@ -53,7 +53,7 @@ public virtual void ProcessGenericInstantiation( if (publicParameterlessConstructor != null) { - var diagnosticContext = new DiagnosticContext(location, reportDiagnostic); + var diagnosticContext = new DiagnosticContext(location, typeNameResolver.Compilation, reportDiagnostic); CheckAndCreateRequiresDiagnostic( publicParameterlessConstructor, owningSymbol, @@ -201,6 +201,7 @@ private void AnalyzeImplicitBaseCtor(SymbolAnalysisContext context) var diagnosticContext = new DiagnosticContext( typeSymbol.Locations[0], + context.Compilation, context.ReportDiagnostic); CheckAndCreateRequiresDiagnostic( diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/DiagnosticContext.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/DiagnosticContext.cs index 335f845d9e4800..7794b4eefeccff 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/DiagnosticContext.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/DiagnosticContext.cs @@ -14,11 +14,14 @@ public readonly partial struct DiagnosticContext { public readonly Location Location { get; } + private readonly Compilation _compilation; + private readonly Action? _reportDiagnostic; - public DiagnosticContext(Location location, Action? reportDiagnostic) + public DiagnosticContext(Location location, Compilation compilation, Action? reportDiagnostic) { Location = location; + _compilation = compilation; _reportDiagnostic = reportDiagnostic; } @@ -68,7 +71,7 @@ private Diagnostic CreateDiagnostic(DiagnosticId id, ValueWithDynamicallyAccesse Dictionary? DAMArgument = new Dictionary(); // not supporting merging differing attributes, check to make sure symbol has no other attributes - if (symbol.DeclaringSyntaxReferences.Length == 0 + if (!CanOfferCodeFixOn(symbol) || (actualValue is not MethodReturnValue && symbol.TryGetAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) || (actualValue is MethodReturnValue @@ -88,5 +91,20 @@ private Diagnostic CreateDiagnostic(DiagnosticId id, ValueWithDynamicallyAccesse return Diagnostic.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(id), Location, sourceLocation, DAMArgument?.ToImmutableDictionary(), args); } + + /// + /// Determines whether a code fix location can be attached to a diagnostic for . + /// The symbol must be declared in source that is part of the compilation being analyzed, otherwise Roslyn + /// rejects the reported diagnostic. Note that a symbol from another project can still have declaring syntax + /// references: the IDE models project-to-project references as compilation references, which expose source + /// symbols whose syntax trees belong to a different compilation. + /// + private bool CanOfferCodeFixOn(ISymbol symbol) + { + if (symbol.DeclaringSyntaxReferences.Length == 0) + return false; + + return _compilation.ContainsSyntaxTree(symbol.DeclaringSyntaxReferences[0].SyntaxTree); + } } } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FeatureCheckReturnValuePattern.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FeatureCheckReturnValuePattern.cs index ed69b463b7a67f..ac0b9a4b39b282 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FeatureCheckReturnValuePattern.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FeatureCheckReturnValuePattern.cs @@ -32,7 +32,7 @@ public FeatureCheckReturnValuePattern( public void ReportDiagnostics(DataFlowAnalyzerContext context, Action reportDiagnostic) { - var diagnosticContext = new DiagnosticContext(Operation.Syntax.GetLocation(), reportDiagnostic); + var diagnosticContext = new DiagnosticContext(Operation.Syntax.GetLocation(), context.Compilation, reportDiagnostic); // For now, feature check validation is enabled only when trim analysis is enabled. if (context.TrimAnalyzer is null) return; diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index 3c691e00b3af0a..bf56b41b07867e 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -41,7 +41,7 @@ public HandleCallAction( _owningSymbol = owningSymbol; _operation = operation; _isNewObj = operation.Kind == OperationKind.ObjectCreation; - _diagnosticContext = new DiagnosticContext(location, reportDiagnostic); + _diagnosticContext = new DiagnosticContext(location, typeNameResolver.Compilation, reportDiagnostic); _annotations = FlowAnnotations.Instance; _reflectionAccessAnalyzer = new(reportDiagnostic, typeNameResolver, typeHierarchyType: null); _typeNameResolver = typeNameResolver; diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs index e27c79d03506cd..58a32de36966b6 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs @@ -96,7 +96,7 @@ private void ReportRequiresUnreferencedCodeDiagnostic(Location location, Attribu { var message = RequiresUnreferencedCodeUtils.GetMessageFromAttribute(requiresAttributeData); var url = RequiresAnalyzerBase.GetUrlFromAttribute(requiresAttributeData); - var diagnosticContext = new DiagnosticContext(location, _reportDiagnostic); + var diagnosticContext = new DiagnosticContext(location, _typeNameResolver.Compilation, _reportDiagnostic); diagnosticContext.AddDiagnostic(DiagnosticId.RequiresUnreferencedCode, member.GetDisplayName(), message, url); } @@ -146,7 +146,7 @@ static bool IsDeclaredWithinType(ISymbol member, INamedTypeSymbol type) if (reportOnMember) location = DynamicallyAccessedMembersAnalyzer.GetPrimaryLocation(member.Locations); - var diagnosticContext = new DiagnosticContext(location, _reportDiagnostic); + var diagnosticContext = new DiagnosticContext(location, _typeNameResolver.Compilation, _reportDiagnostic); if (member.IsInRequiresUnreferencedCodeAttributeScope(out AttributeData? requiresUnreferencedCodeAttribute)) { @@ -166,7 +166,7 @@ static bool IsDeclaredWithinType(ISymbol member, INamedTypeSymbol type) internal void GetDiagnosticsForReflectionAccessToDAMOnMethod(Location location, IMethodSymbol methodSymbol) { - var diagnosticContext = new DiagnosticContext(location, _reportDiagnostic); + var diagnosticContext = new DiagnosticContext(location, _typeNameResolver.Compilation, _reportDiagnostic); if (methodSymbol.IsVirtual && FlowAnnotations.GetMethodReturnValueAnnotation(methodSymbol) != DynamicallyAccessedMemberTypes.None) { diagnosticContext.AddDiagnostic(DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection, methodSymbol.GetDisplayName()); @@ -215,7 +215,7 @@ private void GetDiagnosticsForField(Location location, IFieldSymbol fieldSymbol) if (FlowAnnotations.GetFieldAnnotation(fieldSymbol) != DynamicallyAccessedMemberTypes.None) { - var diagnosticContext = new DiagnosticContext(location, _reportDiagnostic); + var diagnosticContext = new DiagnosticContext(location, _typeNameResolver.Compilation, _reportDiagnostic); diagnosticContext.AddDiagnostic(DiagnosticId.DynamicallyAccessedMembersFieldAccessedViaReflection, fieldSymbol.GetDisplayName()); } } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs index 3a7ff369c0df8f..1ca7cd11c1bdf6 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs @@ -42,12 +42,12 @@ public RequireDynamicallyAccessedMembersAction( _reportDiagnostic = reportDiagnostic; _reflectionAccessAnalyzer = reflectionAccessAnalyzer; _owningSymbol = owningSymbol; - _diagnosticContext = new(location, reportDiagnostic); + _diagnosticContext = new(location, typeNameResolver.Compilation, reportDiagnostic); } public partial bool TryResolveTypeNameAndMark(string typeName, bool needsAssemblyName, out TypeProxy type) { - var diagnosticContext = new DiagnosticContext(_location, _reportDiagnostic); + var diagnosticContext = new DiagnosticContext(_location, _typeNameResolver.Compilation, _reportDiagnostic); if (_reflectionAccessAnalyzer.TryResolveTypeNameAndMark(typeName, diagnosticContext, needsAssemblyName, out ITypeSymbol? foundType)) { if (foundType is INamedTypeSymbol namedType && namedType.IsGenericType) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs index 7ad0d048f50125..8c8bdbba02e457 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisBackingFieldAccessPattern.cs @@ -47,7 +47,7 @@ public TrimAnalysisBackingFieldAccessPattern Merge( public void ReportDiagnostics(DataFlowAnalyzerContext context, Action reportDiagnostic) { - DiagnosticContext diagnosticContext = new(Operation.Syntax.GetLocation(), reportDiagnostic); + DiagnosticContext diagnosticContext = new(Operation.Syntax.GetLocation(), context.Compilation, 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/TrimAnalysisFieldAccessPattern.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisFieldAccessPattern.cs index 35ad06cef57f4c..f1a2894dff066b 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisFieldAccessPattern.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisFieldAccessPattern.cs @@ -46,7 +46,7 @@ public TrimAnalysisFieldAccessPattern Merge( public void ReportDiagnostics(DataFlowAnalyzerContext context, Action reportDiagnostic) { - DiagnosticContext diagnosticContext = new(Operation.Syntax.GetLocation(), reportDiagnostic); + DiagnosticContext diagnosticContext = new(Operation.Syntax.GetLocation(), context.Compilation, reportDiagnostic); foreach (var requiresAnalyzer in context.EnabledRequiresAnalyzers) requiresAnalyzer.CheckAndCreateRequiresDiagnostic(Operation, Field, OwningSymbol, context, FeatureContext, in diagnosticContext); } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisMethodCallPattern.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisMethodCallPattern.cs index 69d70a1ef8c74e..5df274bdb59a71 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisMethodCallPattern.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisMethodCallPattern.cs @@ -92,7 +92,7 @@ public void ReportDiagnostics(DataFlowAnalyzerContext context, Action invocationSyntax.Expression.GetLocation(), _ => location }; - var diagnosticContext = new DiagnosticContext(location, reportDiagnostic); + var diagnosticContext = new DiagnosticContext(location, context.Compilation, reportDiagnostic); foreach (var requiresAnalyzer in context.EnabledRequiresAnalyzers) { if (!requiresAnalyzer.IsIntrinsicallyHandled(CalledMethod, Instance, Arguments)) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisReflectionAccessPattern.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisReflectionAccessPattern.cs index 281e93e06f542f..1719014ff6b5f6 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisReflectionAccessPattern.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisReflectionAccessPattern.cs @@ -55,7 +55,7 @@ public void ReportDiagnostics(DataFlowAnalyzerContext context, Action _compilation; + static readonly TypeNameParseOptions s_typeNameParseOptions = new() { MaxNodes = int.MaxValue }; public TypeNameResolver(Compilation compilation) diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersAnalyzerTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersAnalyzerTests.cs index 2db6ae9f47800f..daebf070a29f9e 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersAnalyzerTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersAnalyzerTests.cs @@ -3,7 +3,10 @@ using System.Threading.Tasks; using ILLink.Shared; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; +using SourceGenerators.Tests; using Xunit; using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpCodeFixVerifier< ILLink.RoslynAnalyzer.DynamicallyAccessedMembersAnalyzer, @@ -369,6 +372,96 @@ private static Type GetFoo() .WithArguments("System.Type.GetMethod(String)", "C.GetFoo()", "'DynamicallyAccessedMemberTypes.PublicMethods'")); } + [Fact] + public Task SourceMethodInReferencedProjectReturnTypeDoesNotMatchTargetMethod() + { + var referencedSource = """ + using System; + + public class Referenced + { + public static Type GetFoo() + { + return typeof(Referenced); + } + } + """; + + var source = """ + class C + { + public static void Main() + { + Referenced.GetFoo().GetMethod("Bar"); + } + } + """; + + // (5,9): warning IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. The return value of method 'Referenced.GetFoo()' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. + // No code fix location is expected, because 'Referenced.GetFoo()' is declared outside of the compilation being analyzed. + return VerifyDynamicallyAccessedMembersAnalyzerWithProjectReference(source, referencedSource, + VerifyCS.Diagnostic(DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter) + .WithSpan(5, 9, 5, 45) + .WithArguments("System.Type.GetMethod(String)", "Referenced.GetFoo()", "'DynamicallyAccessedMemberTypes.PublicMethods'")); + } + + [Fact] + public Task SourceMethodInReferencedProjectParameterDoesNotMatchOverride() + { + // Here the method that would need the attribute applied is the base method, which lives in + // the referenced project. The code fix location must be omitted in that case as well. + var referencedSource = """ + using System; + + public class Base + { + public virtual void M(Type t) {} + } + """; + + var source = """ + using System; + using System.Diagnostics.CodeAnalysis; + + class Derived : Base + { + public override void M([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type t) {} + } + """; + + // (6,108): warning IL2092: 'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the parameter 't' of method 'Derived.M(Type)' don't match overridden parameter 't' of method 'Base.M(Type)'. All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage. + return VerifyDynamicallyAccessedMembersAnalyzerWithProjectReference(source, referencedSource, + VerifyCS.Diagnostic(DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodParameterBetweenOverrides) + .WithSpan(6, 108, 6, 109) + .WithArguments("t", "Derived.M(Type)", "t", "Base.M(Type)")); + } + + /// + /// Runs the analyzer against , with compiled + /// separately and referenced via a , which is how the + /// IDE models a project-to-project reference. Symbols exposed that way are source symbols whose declaring + /// syntax lives in a syntax tree that is not part of the compilation being analyzed, so no code fix location + /// may be attached to diagnostics about them - otherwise Roslyn rejects the diagnostic and reports AD0001. + /// + static Task VerifyDynamicallyAccessedMembersAnalyzerWithProjectReference( + string source, + string referencedSource, + params DiagnosticResult[] expected) + { + var referencedCompilation = CSharpCompilation.Create( + assemblyName: "ReferencedProject", + syntaxTrees: new[] { CSharpSyntaxTree.ParseText(referencedSource, new CSharpParseOptions(LanguageVersion.Preview)) }, + references: LiveReferencePack.GetMetadataReferences(), + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + + return VerifyCS.VerifyAnalyzerAsync( + source, + consoleApplication: false, + TestCaseUtils.UseMSBuildProperties(MSBuildPropertyOptionNames.EnableTrimAnalyzer), + additionalReferences: new[] { referencedCompilation.ToMetadataReference() }, + expected: expected); + } + #endregion #region SourceField