Skip to content

Commit e09fba1

Browse files
jtschustermichaelgsharp
authored andcommitted
Create separate attribute for warning behavior differences (dotnet#101220)
To help track differences in the warning behavior of the trimming related tools, this modifies how adds UnexpectedWarning, and requires an issue link to be provided when there is a ProducedBy argument to the constructor. To enforce that either both a ProducedBy and IssueLink is provided or neither, the ProducedBy property is removed and is provided as the second to last argument, and IssueLink is provided as the last argument. ExpectedWarning means that the correct behavior is to warn. Any attributes that expect it only from a subset of the tools must provide an issue link. (These are mostly blank strings now, though) UnexpectedWarning means that this warning is not the correct behavior. These attributes always include a ProducedBy anrdshould link to an issue. Changes Look for a Tool attribute argument in the second to last position of ExpectedWarning and Unexpected warning when a ProducedBy property is not found. Find a replace existing ExpectedWarnings to use the new constructors. Adds issue links within AttributedMembersAccessedViaReflection.cs and in some places in ArrayDataFlow.cs
1 parent 8e41c3b commit e09fba1

File tree

79 files changed

+1642
-1754
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1642
-1754
lines changed

src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ResultChecker.cs

+22-3
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ protected virtual void AdditionalChecking (TrimmedTestCaseResult linkResult, Ass
159159

160160
private static bool IsProducedByNativeAOT (CustomAttribute attr)
161161
{
162+
if (attr.ConstructorArguments.Count > 2 && attr.ConstructorArguments[^2].Type.Name == "Tool")
163+
return ((Tool)attr.ConstructorArguments[^2].Value).HasFlag(Tool.NativeAot);
162164
var producedBy = attr.GetPropertyValue ("ProducedBy");
163165
return producedBy is null ? true : ((Tool) producedBy).HasFlag (Tool.NativeAot);
164166
}
@@ -227,12 +229,29 @@ private void VerifyLoggedMessages (AssemblyDefinition original, TrimmingTestLogg
227229
}
228230
break;
229231

230-
case nameof (ExpectedWarningAttribute): {
232+
case nameof (ExpectedWarningAttribute) or nameof(UnexpectedWarningAttribute): {
231233
var expectedWarningCode = (string) attr.GetConstructorArgumentValue (0);
232234
if (!expectedWarningCode.StartsWith ("IL")) {
233-
Assert.Fail ($"The warning code specified in {nameof (ExpectedWarningAttribute)} must start with the 'IL' prefix. Specified value: '{expectedWarningCode}'.");
235+
Assert.Fail ($"The warning code specified in {attr.AttributeType.Name} must start with the 'IL' prefix. Specified value: '{expectedWarningCode}'.");
234236
}
235-
var expectedMessageContains = ((CustomAttributeArgument[]) attr.GetConstructorArgumentValue (1)).Select (a => (string) a.Value).ToArray ();
237+
IEnumerable<string> expectedMessageContains = attr.Constructor.Parameters switch
238+
{
239+
// ExpectedWarningAttribute(string warningCode, params string[] expectedMessages)
240+
// ExpectedWarningAttribute(string warningCode, string[] expectedMessages, Tool producedBy, string issueLink)
241+
[_, { ParameterType.IsArray: true }, ..]
242+
=> ((CustomAttributeArgument[])attr.ConstructorArguments[1].Value)
243+
.Select(caa => (string)caa.Value),
244+
// ExpectedWarningAttribute(string warningCode, string expectedMessage1, string expectedMessage2, Tool producedBy, string issueLink)
245+
[_, { ParameterType.Name: "String" }, { ParameterType.Name: "String" }, { ParameterType.Name: "Tool" }, _]
246+
=> [(string)attr.GetConstructorArgumentValue(1), (string)attr.GetConstructorArgumentValue(2)],
247+
// ExpectedWarningAttribute(string warningCode, string expectedMessage, Tool producedBy, string issueLink)
248+
[_, { ParameterType.Name: "String" }, { ParameterType.Name: "Tool" }, _]
249+
=> [(string)attr.GetConstructorArgumentValue(1)],
250+
// ExpectedWarningAttribute(string warningCode, Tool producedBy, string issueLink)
251+
[_, { ParameterType.Name: "Tool" }, _]
252+
=> [],
253+
_ => throw new UnreachableException(),
254+
};
236255
string fileName = (string) attr.GetPropertyValue ("FileName")!;
237256
int? sourceLine = (int?) attr.GetPropertyValue ("SourceLine");
238257
int? sourceColumn = (int?) attr.GetPropertyValue ("SourceColumn");

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

+50-19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Collections.Immutable;
7+
using System.Diagnostics;
78
using System.Diagnostics.CodeAnalysis;
89
using System.Linq;
910
using System.Text.RegularExpressions;
@@ -66,7 +67,7 @@ public void Check (bool allowMissingWarnings)
6667
}
6768

6869
if (message.Length > 0) {
69-
Assert.Fail(message);
70+
Assert.Fail (message);
7071
}
7172
}
7273

@@ -207,51 +208,62 @@ private void ValidateDiagnostics (CSharpSyntaxNode memberSyntax, SyntaxList<Attr
207208

208209
static bool IsExpectedDiagnostic (AttributeSyntax attribute)
209210
{
210-
switch (attribute.Name.ToString ()) {
211-
case "ExpectedWarning":
212-
case "LogContains":
211+
switch (attribute.Name.ToString () + "Attribute") {
212+
case nameof (ExpectedWarningAttribute):
213+
case nameof (UnexpectedWarningAttribute):
214+
case nameof (LogContainsAttribute):
213215
var args = LinkerTestBase.GetAttributeArguments (attribute);
214216
if (args.TryGetValue ("ProducedBy", out var producedBy)) {
215217
// Skip if this warning is not expected to be produced by any of the analyzers that we are currently testing.
216218
return GetProducedBy (producedBy).HasFlag (Tool.Analyzer);
217219
}
220+
var toolArg = args.Where (arg => arg.Key.StartsWith ('#')).Count () - 2;
221+
if (args.TryGetValue ($"#{toolArg}", out var maybeProducedBy) && TryGetProducedBy (maybeProducedBy, out Tool producedByTool)) {
222+
return producedByTool.HasFlag (Tool.Analyzer);
223+
}
218224

219225
return true;
220226
default:
221227
return false;
222228
}
223229

224-
static Tool GetProducedBy (ExpressionSyntax expression)
230+
static bool TryGetProducedBy (ExpressionSyntax expression, out Tool producedBy)
225231
{
226-
var producedBy = (Tool) 0x0;
232+
producedBy = (Tool) 0x0;
227233
switch (expression) {
228-
case BinaryExpressionSyntax binaryExpressionSyntax:
234+
case BinaryExpressionSyntax binaryExpressionSyntax when binaryExpressionSyntax.Kind () == SyntaxKind.BitwiseOrExpression:
229235
if (!Enum.TryParse<Tool> ((binaryExpressionSyntax.Left as MemberAccessExpressionSyntax)!.Name.Identifier.ValueText, out var besProducedBy))
230-
throw new ArgumentException ("Expression must be a ProducedBy value", nameof (expression));
236+
return false;
231237
producedBy |= besProducedBy;
232238
producedBy |= GetProducedBy (binaryExpressionSyntax.Right);
233239
break;
234240

235241
case MemberAccessExpressionSyntax memberAccessExpressionSyntax:
236242
if (!Enum.TryParse<Tool> (memberAccessExpressionSyntax.Name.Identifier.ValueText, out var maeProducedBy))
237-
throw new ArgumentException ("Expression must be a ProducedBy value", nameof (expression));
243+
return false;
238244
producedBy |= maeProducedBy;
239245
break;
240246

241247
default:
242-
break;
248+
return false;
243249
}
244250

245-
return producedBy;
251+
return true;
252+
}
253+
254+
static Tool GetProducedBy (ExpressionSyntax expression)
255+
{
256+
return TryGetProducedBy (expression, out var tool) ? tool : throw new ArgumentException ("Expression must be a ProducedBy value", nameof (expression));
246257
}
247258
}
248259

249260
bool TryValidateExpectedDiagnostic (AttributeSyntax attribute, List<Diagnostic> diagnostics, [NotNullWhen (true)] out int? matchIndex, [NotNullWhen (false)] out string? missingDiagnosticMessage)
250261
{
251-
switch (attribute.Name.ToString ()) {
252-
case "ExpectedWarning":
262+
switch (attribute.Name.ToString () + "Attribute") {
263+
case nameof (ExpectedWarningAttribute):
264+
case nameof (UnexpectedWarningAttribute):
253265
return TryValidateExpectedWarningAttribute (attribute!, diagnostics, out matchIndex, out missingDiagnosticMessage);
254-
case "LogContains":
266+
case nameof (LogContainsAttribute):
255267
return TryValidateLogContainsAttribute (attribute!, diagnostics, out matchIndex, out missingDiagnosticMessage);
256268
default:
257269
throw new InvalidOperationException ($"Unsupported attribute type {attribute.Name}");
@@ -268,10 +280,29 @@ private bool TryValidateExpectedWarningAttribute (AttributeSyntax attribute, Lis
268280
if (!expectedWarningCode.StartsWith ("IL"))
269281
throw new InvalidOperationException ($"Expected warning code should start with \"IL\" prefix.");
270282

271-
List<string> expectedMessages = args
272-
.Where (arg => arg.Key.StartsWith ("#") && arg.Key != "#0")
273-
.Select (arg => LinkerTestBase.GetStringFromExpression (arg.Value, _semanticModel))
274-
.ToList ();
283+
List<string> expectedMessages = ((IMethodSymbol) (_semanticModel.GetSymbolInfo (attribute).Symbol!)).Parameters switch {
284+
// ExpectedWarningAttribute(string warningCode, params string[] expectedMessages)
285+
[_, { IsParams: true }]
286+
=> args
287+
.Where (arg => arg.Key.StartsWith ('#') && arg.Key != "#0")
288+
.Select (arg => LinkerTestBase.GetStringFromExpression (arg.Value, _semanticModel))
289+
.ToList (),
290+
// ExpectedWarningAttribute(string warningCode, string[] expectedMessages, Tool producedBy, string issueLink)
291+
[_, { Type.TypeKind: TypeKind.Array }, _, _]
292+
=> ((CollectionExpressionSyntax) args["#1"]).Elements
293+
.Select (arg => LinkerTestBase.GetStringFromExpression (((ExpressionElementSyntax) arg).Expression, _semanticModel))
294+
.ToList (),
295+
// ExpectedWarningAttribute(string warningCode, string expectedMessage, Tool producedBy, string issueLink)
296+
[_, { Type.SpecialType: SpecialType.System_String }, _, _]
297+
=> [LinkerTestBase.GetStringFromExpression (args["#1"], _semanticModel)],
298+
// ExpectedWarningAttribute(string warningCode, string expectedMessage1, string expectedMessage2, Tool producedBy, string issueLink)
299+
[_, { Type.SpecialType: SpecialType.System_String }, { Type.SpecialType: SpecialType.System_String }, _, _]
300+
=> [LinkerTestBase.GetStringFromExpression (args["#1"], _semanticModel), LinkerTestBase.GetStringFromExpression (args["#2"], _semanticModel)],
301+
// ExpectedWarningAttribute(string warningCode, Tool producedBy, string issueLink)
302+
[_, _, _]
303+
=> [],
304+
_ => throw new UnreachableException (),
305+
};
275306

276307
for (int i = 0; i < diagnostics.Count; i++) {
277308
if (Matches (diagnostics[i])) {
@@ -318,7 +349,7 @@ private void ValidateLogDoesNotContainAttribute (AttributeSyntax attribute, IRea
318349
Assert.False (args.ContainsKey ("#1"));
319350
_ = LinkerTestBase.GetStringFromExpression (arg, _semanticModel);
320351
if (LogContains (attribute, diagnosticMessages, out var matchIndex, out var findText)) {
321-
Assert.Fail($"LogDoesNotContain failure: Text\n\"{findText}\"\nfound in diagnostic:\n {diagnosticMessages[(int) matchIndex]}");
352+
Assert.Fail ($"LogDoesNotContain failure: Text\n\"{findText}\"\nfound in diagnostic:\n {diagnosticMessages[(int) matchIndex]}");
322353
}
323354
}
324355

src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DataFlowTests.g.cs

-6
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ public Task GenericParameterDataFlowMarking ()
1919
return RunTest (allowMissingWarnings: true);
2020
}
2121

22-
[Fact]
23-
public Task MakeGenericDataflowIntrinsics ()
24-
{
25-
return RunTest (allowMissingWarnings: true);
26-
}
27-
2822
[Fact]
2923
public Task MethodByRefParameterDataFlow ()
3024
{

src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedWarningAttribute.cs

+19-6
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,27 @@ namespace Mono.Linker.Tests.Cases.Expectations.Assertions
99
AttributeTargets.Assembly | AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Event,
1010
AllowMultiple = true,
1111
Inherited = false)]
12+
/// <summary>
13+
/// An attribute applied to a member to indicate that a warning is expected in ideal behavior, and is present in all tools
14+
/// </summary>
1215
public class ExpectedWarningAttribute : EnableLoggerAttribute
1316
{
17+
public ExpectedWarningAttribute (string warningCode, string[] messageContains, Tool producedBy, string issueLinkOrReason)
18+
{
19+
}
20+
21+
public ExpectedWarningAttribute (string warningCode, string messageContains, Tool producedBy, string issueLinkOrReason)
22+
{
23+
}
24+
25+
public ExpectedWarningAttribute (string warningCode, string messageContains, string messageContains2, Tool producedBy, string issueLinkOrReason)
26+
{
27+
}
28+
29+
public ExpectedWarningAttribute (string warningCode, Tool producedBy, string issueLinkOrReason)
30+
{
31+
}
32+
1433
public ExpectedWarningAttribute (string warningCode, params string[] messageContains)
1534
{
1635
}
@@ -19,12 +38,6 @@ public ExpectedWarningAttribute (string warningCode, params string[] messageCont
1938
public int SourceLine { get; set; }
2039
public int SourceColumn { get; set; }
2140

22-
/// <summary>
23-
/// Property used by the result checkers of trimmer and analyzers to determine whether
24-
/// the tool should have produced the specified warning on the annotated member.
25-
/// </summary>
26-
public Tool ProducedBy { get; set; } = Tool.TrimmerAnalyzerAndNativeAot;
27-
2841
public bool CompilerGeneratedCode { get; set; }
2942
}
3043
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
6+
namespace Mono.Linker.Tests.Cases.Expectations.Assertions
7+
{
8+
[AttributeUsage (
9+
AttributeTargets.Assembly | AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Event,
10+
AllowMultiple = true,
11+
Inherited = false)]
12+
/// <summary>
13+
/// An attribute applied to a member to indicate that a warning is raised in tests, but should not be present in ideal behavior
14+
/// </summary>
15+
public class UnexpectedWarningAttribute : ExpectedWarningAttribute
16+
{
17+
public UnexpectedWarningAttribute (string warningCode, string[] messageContains, Tool producedBy, string issueLinkOrReason)
18+
: base (warningCode, messageContains, producedBy, issueLinkOrReason) { }
19+
public UnexpectedWarningAttribute (string warningCode, string messageContains, Tool producedBy, string issueLinkOrReason)
20+
: base (warningCode, messageContains, producedBy, issueLinkOrReason) { }
21+
public UnexpectedWarningAttribute (string warningCode, string messageContains, string messageContains2, Tool producedBy, string issueLinkOrReason)
22+
: base (warningCode, messageContains, messageContains2, producedBy, issueLinkOrReason) { }
23+
public UnexpectedWarningAttribute (string warningCode, Tool producedBy, string issueLinkOrReason)
24+
: base (warningCode, producedBy, issueLinkOrReason) { }
25+
}
26+
}

src/tools/illink/test/Mono.Linker.Tests.Cases/CoreLink/InvalidIsTrimmableAttribute.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ public static void Unused ()
2929
{
3030
}
3131
}
32-
}
32+
}

0 commit comments

Comments
 (0)