Skip to content

Commit 511d266

Browse files
authored
Warn on method access in Requires analyzers (#110086)
The issue with calling Methods with requires is the the method itself, not the parameters. Warning on the entire invocation including the parameters can be confusing. Instead, we should warn just on the method access for Requires. DynamicallyAccessedMembers should still warn on the invocation. Also updates the Microsoft.Extensions.Configuration diagnostic suppressor to work suppress warnings on the method access, not only on method invocation warnings.
1 parent 6045e29 commit 511d266

File tree

6 files changed

+67
-57
lines changed

6 files changed

+67
-57
lines changed

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,16 @@ public override void ReportSuppressions(SuppressionAnalysisContext context)
4848
? diagnostic.AdditionalLocations[0]
4949
: diagnostic.Location;
5050

51+
// The trim analyzer changed from warning on the InvocationExpression to the MemberAccessExpression in https://github.com/dotnet/runtime/pull/110086
52+
// In other words, the warning location went from from `{|Method1(arg1, arg2)|}` to `{|Method1|}(arg1, arg2)`
53+
// To account for this, we need to check if the location is an InvocationExpression or a child of an InvocationExpression.
5154
bool shouldSuppressDiagnostic =
5255
location.SourceTree is SyntaxTree sourceTree &&
5356
sourceTree.GetRoot().FindNode(location.SourceSpan) is SyntaxNode syntaxNode &&
54-
BinderInvocation.IsCandidateSyntaxNode(syntaxNode) &&
57+
(syntaxNode as InvocationExpressionSyntax ?? syntaxNode.Parent as InvocationExpressionSyntax) is InvocationExpressionSyntax invocation &&
58+
BinderInvocation.IsCandidateSyntaxNode(invocation) &&
5559
context.GetSemanticModel(sourceTree)
56-
.GetOperation((InvocationExpressionSyntax)syntaxNode, context.CancellationToken) is IInvocationOperation operation &&
60+
.GetOperation(invocation, context.CancellationToken) is IInvocationOperation operation &&
5761
BinderInvocation.IsBindingOperation(operation);
5862

5963
if (shouldSuppressDiagnostic)

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisMethodCallPattern.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using ILLink.Shared.DataFlow;
99
using ILLink.Shared.TrimAnalysis;
1010
using Microsoft.CodeAnalysis;
11+
using Microsoft.CodeAnalysis.CSharp.Syntax;
1112
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;
1213

1314
namespace ILLink.RoslynAnalyzer.TrimAnalysis
@@ -71,14 +72,19 @@ public TrimAnalysisMethodCallPattern Merge (
7172

7273
public void ReportDiagnostics (DataFlowAnalyzerContext context, Action<Diagnostic> reportDiagnostic)
7374
{
74-
var location = Operation.Syntax.GetLocation ();
75+
Location location = Operation.Syntax.GetLocation ();
7576
if (context.EnableTrimAnalyzer &&
7677
!OwningSymbol.IsInRequiresUnreferencedCodeAttributeScope(out _) &&
7778
!FeatureContext.IsEnabled (RequiresUnreferencedCodeAnalyzer.FullyQualifiedRequiresUnreferencedCodeAttribute))
7879
{
7980
TrimAnalysisVisitor.HandleCall(Operation, OwningSymbol, CalledMethod, Instance, Arguments, location, reportDiagnostic, default, out var _);
8081
}
81-
82+
// For Requires, make the location the reference to the method, not the entire invocation.
83+
// The parameters are not part of the issue, and including them in the location can be misleading.
84+
location = Operation.Syntax switch {
85+
InvocationExpressionSyntax invocationSyntax => invocationSyntax.Expression.GetLocation (),
86+
_ => location
87+
};
8288
var diagnosticContext = new DiagnosticContext (location, reportDiagnostic);
8389
foreach (var requiresAnalyzer in context.EnabledRequiresAnalyzers)
8490
{

src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ void M2()
230230
""";
231231
return VerifyRequiresAssemblyFilesAnalyzer (TestRequiresAssemblyFilesWithMessageAndUrl,
232232
// (12,3): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. https://helpurl
233-
VerifyCS.Diagnostic (DiagnosticId.RequiresAssemblyFiles).WithSpan (12, 3, 12, 7).WithArguments ("C.M1()", "", " https://helpurl"));
233+
VerifyCS.Diagnostic (DiagnosticId.RequiresAssemblyFiles).WithSpan (12, 3, 12, 5).WithArguments ("C.M1()", "", " https://helpurl"));
234234
}
235235

236236
[Fact]
@@ -275,7 +275,7 @@ void M3()
275275
""";
276276
return VerifyRequiresAssemblyFilesAnalyzer (TestNoDiagnosticIsProducedIfCallerIsAnnotated,
277277
// (7,3): warning IL3002: Using member 'C.M2()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. Warn from M2.
278-
VerifyCS.Diagnostic (DiagnosticId.RequiresAssemblyFiles).WithSpan (7, 3, 7, 7).WithArguments ("C.M2()", " Warn from M2.", ""));
278+
VerifyCS.Diagnostic (DiagnosticId.RequiresAssemblyFiles).WithSpan (7, 3, 7, 5).WithArguments ("C.M2()", " Warn from M2.", ""));
279279
}
280280

281281
[Fact]
@@ -334,9 +334,9 @@ public void M()
334334
""";
335335
return VerifyRequiresAssemblyFilesAnalyzer (src,
336336
// (7,7): warning IL3001: Assemblies embedded in a single-file app cannot have additional files in the manifest.
337-
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyGetFilesInSingleFile).WithSpan (7, 7, 7, 35).WithArguments ("System.Reflection.Assembly.GetFile(String)"),
337+
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyGetFilesInSingleFile).WithSpan (7, 7, 7, 16).WithArguments ("System.Reflection.Assembly.GetFile(String)"),
338338
// (8,7): warning IL3001: Assemblies embedded in a single-file app cannot have additional files in the manifest.
339-
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyGetFilesInSingleFile).WithSpan (8, 7, 8, 19).WithArguments ("System.Reflection.Assembly.GetFiles()")
339+
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyGetFilesInSingleFile).WithSpan (8, 7, 8, 17).WithArguments ("System.Reflection.Assembly.GetFiles()")
340340
);
341341
}
342342

@@ -388,7 +388,7 @@ public void M()
388388
// (7,7): warning IL3000: 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.
389389
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyLocationInSingleFile).WithSpan (7, 7, 7, 17).WithArguments ("System.Reflection.Assembly.Location.get"),
390390
// (8,7): warning IL3001: Assemblies embedded in a single-file app cannot have additional files in the manifest.
391-
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyGetFilesInSingleFile).WithSpan (8, 7, 8, 19).WithArguments ("System.Reflection.Assembly.GetFiles()")
391+
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyGetFilesInSingleFile).WithSpan (8, 7, 8, 17).WithArguments ("System.Reflection.Assembly.GetFiles()")
392392
);
393393
}
394394

@@ -493,13 +493,13 @@ public class F
493493
fixedSource: fixtest,
494494
baselineExpected: new[] {
495495
// /0/Test0.cs(6,14): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. message.
496-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(6, 14, 6, 18).WithArguments("C.M1()", " message.", ""),
496+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(6, 14, 6, 16).WithArguments("C.M1()", " message.", ""),
497497
// /0/Test0.cs(10,24): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. message.
498-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(10, 24, 10, 30).WithArguments("C.M1()", " message.", ""),
498+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(10, 24, 10, 28).WithArguments("C.M1()", " message.", ""),
499499
// /0/Test0.cs(13,25): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. message.
500-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(13, 25, 13, 31).WithArguments("C.M1()", " message.", ""),
500+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(13, 25, 13, 29).WithArguments("C.M1()", " message.", ""),
501501
// /0/Test0.cs(20,25): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. message.
502-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(20, 25, 20, 31).WithArguments("C.M1()", " message.", "")
502+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(20, 25, 20, 29).WithArguments("C.M1()", " message.", "")
503503
},
504504
fixedExpected: Array.Empty<DiagnosticResult> ());
505505
}
@@ -542,7 +542,7 @@ public void M2() {
542542
// /0/Test0.cs(6,24): warning IL3000: 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.
543543
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyLocationInSingleFile).WithSpan (6, 24, 6, 41).WithArguments ("System.Reflection.Assembly.Location.get", "", ""),
544544
// /0/Test0.cs(8,7): warning IL3001: 'System.Reflection.Assembly.GetFiles()' will throw for assemblies embedded in a single-file app
545-
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyGetFilesInSingleFile).WithSpan (8, 7, 8, 26).WithArguments("System.Reflection.Assembly.GetFiles()", "", ""),
545+
VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyGetFilesInSingleFile).WithSpan (8, 7, 8, 24).WithArguments("System.Reflection.Assembly.GetFiles()", "", ""),
546546
},
547547
fixedExpected: Array.Empty<DiagnosticResult> ());
548548
}
@@ -580,7 +580,7 @@ public class C
580580
fixedSource: fix,
581581
baselineExpected: new[] {
582582
// /0/Test0.cs(9,12): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. message.
583-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(9, 12, 9, 16).WithArguments("C.M1()", " message.", "")
583+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(9, 12, 9, 14).WithArguments("C.M1()", " message.", "")
584584
},
585585
fixedExpected: Array.Empty<DiagnosticResult> ());
586586
}
@@ -627,9 +627,9 @@ private int M2 {
627627
""";
628628
var diag = new[] {
629629
// /0/Test0.cs(12,16): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. message.
630-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(12, 16, 12, 20).WithArguments("C.M1()", " message.", ""),
630+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(12, 16, 12, 18).WithArguments("C.M1()", " message.", ""),
631631
// /0/Test0.cs(13,17): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. message.
632-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(13, 17, 13, 21).WithArguments("C.M1()", " message.", "")
632+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(13, 17, 13, 19).WithArguments("C.M1()", " message.", "")
633633
};
634634
return VerifyRequiresAssemblyFilesCodeFix (src, fix, diag, Array.Empty<DiagnosticResult> ());
635635
}
@@ -702,7 +702,7 @@ Action M2()
702702
fixedSource: fix,
703703
baselineExpected: new[] {
704704
// /0/Test0.cs(11,22): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. message.
705-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(11, 22, 11, 26).WithArguments("C.M1()", " message.", "")
705+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(11, 22, 11, 24).WithArguments("C.M1()", " message.", "")
706706
},
707707
fixedExpected: Array.Empty<DiagnosticResult> (),
708708
numberOfIterations: 2);
@@ -741,7 +741,7 @@ public class C
741741
fixedSource: fix,
742742
baselineExpected: new[] {
743743
// /0/Test0.cs(9,17): warning IL3002: Using member 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. message.
744-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(9, 17, 9, 21).WithArguments("C.M1()", " message.", "")
744+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(9, 17, 9, 19).WithArguments("C.M1()", " message.", "")
745745
},
746746
fixedExpected: Array.Empty<DiagnosticResult> ());
747747
}
@@ -793,7 +793,7 @@ public event EventHandler E1
793793
fixedSource: fix,
794794
baselineExpected: new[] {
795795
// /0/Test0.cs(13,12): warning IL3002: Using method 'C.M1()' which has 'RequiresAssemblyFilesAttribute' can break functionality when trimming application code. message.
796-
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(13, 12, 13, 16).WithArguments("C.M1()", " message.", "")
796+
VerifyCS.Diagnostic(DiagnosticId.RequiresAssemblyFiles).WithSpan(13, 12, 13, 14).WithArguments("C.M1()", " message.", "")
797797
},
798798
fixedExpected: Array.Empty<DiagnosticResult> ());
799799
}

0 commit comments

Comments
 (0)