Skip to content

Commit e911894

Browse files
authored
Add trim/aot analyzer warnings for transitive references (#118180)
Adds new analyzer warnings for references to assemblies not marked as trim or aot compatible. The new warnings are opt-in under the following settings: - `<VerifyReferenceTrimCompatibility>true</VerifyReferenceTrimCompatibility>` - `<VerifyReferenceAotCompatibility>true</VerifyReferenceAotCompatibility>` The analyzer uses AssemblyMetadataAttribute in the referenced assemblies to detect whether they are marked as trim/aot compatible. To mark a project as trim/aot compatible, it should be built with the usual recommended property settings: - `<IsTrimmable>true</IsTrimmable>` adds `[assembly: AssemblyMetadata("IsTrimmable", "True")]` - `<IsAotCompatible>true</IsAotCompatible>` adds `[assembly: AssemblyMetadata("IsAotCompatible", "True")`
1 parent cb44822 commit e911894

File tree

13 files changed

+305
-19
lines changed

13 files changed

+305
-19
lines changed

eng/pipelines/runtime-linker-tests.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ extends:
8282
or(
8383
eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_illink.containsChange'], true),
8484
eq(variables['isRollingBuild'], true))
85-
buildArgs: -s tools.illinktests -test -c $(_BuildConfig)
85+
# Build libs.sfx subset first so that Roslyn analyzer tests can use live ref pack
86+
buildArgs: -s libs.sfx -c $(_BuildConfig)
87+
postBuildSteps:
88+
- script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) -s tools.illinktests -test -c $(_BuildConfig) $(crossArg) $(_officialBuildParameter)
89+
displayName: Run ILLink Tests
8690

8791
#
8892
# Build Release config vertical for Windows, Linux, and OSX

src/libraries/System.Runtime.InteropServices.JavaScript/Directory.Build.props

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@
33
<PropertyGroup>
44
<StrongNameKeyId>Microsoft</StrongNameKeyId>
55
<IncludePlatformAttributes>true</IncludePlatformAttributes>
6-
<IsAotCompatible>false</IsAotCompatible>
76
</PropertyGroup>
87
</Project>

src/tools/illink/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,7 @@ public static class MSBuildPropertyOptionNames
99
public const string IncludeAllContentForSelfExtract = nameof(IncludeAllContentForSelfExtract);
1010
public const string EnableTrimAnalyzer = nameof(EnableTrimAnalyzer);
1111
public const string EnableAotAnalyzer = nameof(EnableAotAnalyzer);
12+
public const string VerifyReferenceAotCompatibility = nameof(VerifyReferenceAotCompatibility);
13+
public const string VerifyReferenceTrimCompatibility = nameof(VerifyReferenceTrimCompatibility);
1214
}
1315
}

src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public abstract class RequiresAnalyzerBase : DiagnosticAnalyzer
3636

3737
private protected virtual ImmutableArray<(Action<SyntaxNodeAnalysisContext> Action, SyntaxKind[] SyntaxKind)> ExtraSyntaxNodeActions { get; } = ImmutableArray<(Action<SyntaxNodeAnalysisContext> Action, SyntaxKind[] SyntaxKind)>.Empty;
3838
private protected virtual ImmutableArray<(Action<SymbolAnalysisContext> Action, SymbolKind[] SymbolKind)> ExtraSymbolActions { get; } = ImmutableArray<(Action<SymbolAnalysisContext> Action, SymbolKind[] SymbolKind)>.Empty;
39+
private protected virtual ImmutableArray<Action<CompilationAnalysisContext>> ExtraCompilationActions { get; } = ImmutableArray<Action<CompilationAnalysisContext>>.Empty;
3940

4041
public override void Initialize(AnalysisContext context)
4142
{
@@ -165,6 +166,9 @@ void CheckMatchingAttributesInInterfaces(
165166
}
166167
}
167168
});
169+
170+
foreach (var extraCompilationAction in ExtraCompilationActions)
171+
context.RegisterCompilationAction(extraCompilationAction);
168172
}
169173

170174
internal void CheckAndCreateRequiresDiagnostic(
@@ -386,5 +390,38 @@ ImmutableArray<MultiValue> arguments
386390
{
387391
return false;
388392
}
393+
394+
protected void CheckReferencedAssemblies(
395+
CompilationAnalysisContext context,
396+
string msbuildPropertyName,
397+
string assemblyMetadataName,
398+
DiagnosticDescriptor diagnosticDescriptor)
399+
{
400+
var options = context.Options;
401+
if (!IsAnalyzerEnabled(options))
402+
return;
403+
404+
if (!options.IsMSBuildPropertyValueTrue(msbuildPropertyName))
405+
return;
406+
407+
foreach (var reference in context.Compilation.References)
408+
{
409+
var refAssembly = context.Compilation.GetAssemblyOrModuleSymbol(reference) as IAssemblySymbol;
410+
if (refAssembly is null)
411+
continue;
412+
413+
var assemblyMetadata = refAssembly.GetAttributes().FirstOrDefault(attr =>
414+
attr.AttributeClass?.Name == "AssemblyMetadataAttribute" &&
415+
attr.ConstructorArguments.Length == 2 &&
416+
attr.ConstructorArguments[0].Value?.ToString() == assemblyMetadataName &&
417+
string.Equals(attr.ConstructorArguments[1].Value?.ToString(), "True", StringComparison.OrdinalIgnoreCase));
418+
419+
if (assemblyMetadata is null)
420+
{
421+
var diag = Diagnostic.Create(diagnosticDescriptor, Location.None, refAssembly.Name);
422+
context.ReportDiagnostic(diag);
423+
}
424+
}
425+
}
389426
}
390427
}

src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresDynamicCodeAnalyzer.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ public sealed class RequiresDynamicCodeAnalyzer : RequiresAnalyzerBase
2323
private static readonly DiagnosticDescriptor s_requiresDynamicCodeOnEntryPoint = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresDynamicCodeOnEntryPoint);
2424
private static readonly DiagnosticDescriptor s_requiresDynamicCodeRule = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresDynamicCode);
2525
private static readonly DiagnosticDescriptor s_requiresDynamicCodeAttributeMismatch = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresDynamicCodeAttributeMismatch);
26+
private static readonly DiagnosticDescriptor s_referenceNotMarkedIsAotCompatibleRule = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.ReferenceNotMarkedIsAotCompatible);
2627

2728
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
28-
ImmutableArray.Create(s_requiresDynamicCodeRule, s_requiresDynamicCodeAttributeMismatch, s_requiresDynamicCodeOnStaticCtor, s_requiresDynamicCodeOnEntryPoint);
29+
ImmutableArray.Create(s_requiresDynamicCodeRule, s_requiresDynamicCodeAttributeMismatch, s_requiresDynamicCodeOnStaticCtor, s_requiresDynamicCodeOnEntryPoint, s_referenceNotMarkedIsAotCompatibleRule);
2930

3031
private protected override string RequiresAttributeName => RequiresDynamicCodeAttribute;
3132

@@ -163,6 +164,16 @@ private protected override bool IsRequiresCheck(IPropertySymbol propertySymbol,
163164
return SymbolEqualityComparer.Default.Equals(propertySymbol, isDynamicCodeSupportedProperty);
164165
}
165166

167+
private protected override ImmutableArray<Action<CompilationAnalysisContext>> ExtraCompilationActions =>
168+
ImmutableArray.Create<Action<CompilationAnalysisContext>>((context) =>
169+
{
170+
CheckReferencedAssemblies(
171+
context,
172+
MSBuildPropertyOptionNames.VerifyReferenceAotCompatibility,
173+
"IsAotCompatible",
174+
s_referenceNotMarkedIsAotCompatibleRule);
175+
});
176+
166177
protected override bool VerifyAttributeArguments(AttributeData attribute) =>
167178
attribute.ConstructorArguments.Length >= 1 && attribute.ConstructorArguments is [{ Type.SpecialType: SpecialType.System_String }, ..];
168179

src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase
2525
private static readonly DiagnosticDescriptor s_requiresUnreferencedCodeOnStaticCtor = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor);
2626
private static readonly DiagnosticDescriptor s_requiresUnreferencedCodeOnEntryPoint = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresUnreferencedCodeOnEntryPoint);
2727

28+
private static readonly DiagnosticDescriptor s_referenceNotMarkedIsTrimmableRule = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.ReferenceNotMarkedIsTrimmable);
29+
2830
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
29-
ImmutableArray.Create(s_makeGenericMethodRule, s_makeGenericTypeRule, s_requiresUnreferencedCodeRule, s_requiresUnreferencedCodeAttributeMismatch, s_requiresUnreferencedCodeOnStaticCtor, s_requiresUnreferencedCodeOnEntryPoint);
31+
ImmutableArray.Create(s_makeGenericMethodRule, s_makeGenericTypeRule, s_requiresUnreferencedCodeRule, s_requiresUnreferencedCodeAttributeMismatch, s_requiresUnreferencedCodeOnStaticCtor, s_requiresUnreferencedCodeOnEntryPoint, s_referenceNotMarkedIsTrimmableRule);
3032

3133
private protected override string RequiresAttributeName => RequiresUnreferencedCodeAttribute;
3234

@@ -76,6 +78,16 @@ protected override bool CreateSpecialIncompatibleMembersDiagnostic(
7678
return false;
7779
}
7880

81+
private protected override ImmutableArray<Action<CompilationAnalysisContext>> ExtraCompilationActions =>
82+
ImmutableArray.Create<Action<CompilationAnalysisContext>>((context) =>
83+
{
84+
CheckReferencedAssemblies(
85+
context,
86+
MSBuildPropertyOptionNames.VerifyReferenceTrimCompatibility,
87+
"IsTrimmable",
88+
s_referenceNotMarkedIsTrimmableRule);
89+
});
90+
7991
protected override bool VerifyAttributeArguments(AttributeData attribute) =>
8092
RequiresUnreferencedCodeUtils.VerifyRequiresUnreferencedCodeAttributeArguments(attribute);
8193

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<Project>
22
<ItemGroup>
3-
<CompilerVisibleProperty Include="EnableAotAnalyzer"/>
4-
<CompilerVisibleProperty Include="EnableSingleFileAnalyzer"/>
5-
<CompilerVisibleProperty Include="EnableTrimAnalyzer"/>
6-
<CompilerVisibleProperty Include="IncludeAllContentForSelfExtract"/>
3+
<CompilerVisibleProperty Include="EnableAotAnalyzer" />
4+
<CompilerVisibleProperty Include="EnableSingleFileAnalyzer" />
5+
<CompilerVisibleProperty Include="EnableTrimAnalyzer" />
6+
<CompilerVisibleProperty Include="IncludeAllContentForSelfExtract" />
7+
<CompilerVisibleProperty Include="VerifyReferenceTrimCompatibility" />
8+
<CompilerVisibleProperty Include="VerifyReferenceAotCompatibility" />
79
</ItemGroup>
810
</Project>

src/tools/illink/src/ILLink.Shared/DiagnosticId.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ public enum DiagnosticId
189189
TypeNameIsNotAssemblyQualified = 2122,
190190
RequiresUnreferencedCodeOnEntryPoint = 2123,
191191
TypeMapGroupTypeCannotBeStaticallyDetermined = 2124,
192+
ReferenceNotMarkedIsTrimmable = 2125,
192193
_EndTrimAnalysisWarningsSentinel,
193194

194195
// Single-file diagnostic ids.
@@ -208,6 +209,7 @@ public enum DiagnosticId
208209
CorrectnessOfAbstractDelegatesCannotBeGuaranteed = 3055,
209210
RequiresDynamicCodeOnStaticConstructor = 3056,
210211
RequiresDynamicCodeOnEntryPoint = 3057,
212+
ReferenceNotMarkedIsAotCompatible = 3058,
211213
_EndAotAnalysisWarningsSentinel,
212214

213215
// Feature guard diagnostic ids.

src/tools/illink/src/ILLink.Shared/SharedStrings.resx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,4 +1239,16 @@
12391239
<data name="TypeNameIsNotAssemblyQualifiedTitle" xml:space="preserve">
12401240
<value>Type name is not assembly qualified.</value>
12411241
</data>
1242+
<data name="ReferenceNotMarkedIsTrimmableTitle" xml:space="preserve">
1243+
<value>Referenced assembly is not marked as trimmable.</value>
1244+
</data>
1245+
<data name="ReferenceNotMarkedIsTrimmableMessage" xml:space="preserve">
1246+
<value>Referenced assembly '{0}' is not built with `<IsTrimmable>true</IsTrimmable>` and may not be compatible with trimming.</value>
1247+
</data>
1248+
<data name="ReferenceNotMarkedIsAotCompatibleTitle" xml:space="preserve">
1249+
<value>Referenced assembly is not marked as AOT-compatible.</value>
1250+
</data>
1251+
<data name="ReferenceNotMarkedIsAotCompatibleMessage" xml:space="preserve">
1252+
<value>Referenced assembly '{0}' is not built with `<IsAotCompatible>true</IsAotCompatible>` and may not be compatible with AOT.</value>
1253+
</data>
12421254
</root>

src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,12 @@
3737
<!-- The analyzer tests require the reference assemblies that are compiled against.
3838
Copy them to the output/publish directory so that tests still work on a different
3939
machine (i.e. when testing with Helix). -->
40-
<Target Name="CopyReferenceAssembliesToOutputDir"
41-
DependsOnTargets="FindReferenceAssembliesForReferences"
42-
BeforeTargets="AssignTargetPaths;GetCopyToPublishDirectoryItems">
43-
<ItemGroup>
44-
<None Include="@(ReferencePathWithRefAssemblies->WithMetadataValue('FrameworkReferenceName', 'Microsoft.NETCore.App'))"
45-
CopyToOutputDirectory="PreserveNewest"
46-
CopyToPublishDirectory="PreserveNewest"
47-
Link="live-ref-pack\%(RecursiveDir)%(Filename)%(Extension)"
48-
Visible="false" />
49-
</ItemGroup>
50-
</Target>
40+
<ItemGroup>
41+
<None Include="$(MicrosoftNetCoreAppRefPackRefDir)**/*.dll"
42+
CopyToOutputDirectory="PreserveNewest"
43+
CopyToPublishDirectory="PreserveNewest"
44+
LinkBase="live-ref-pack/"
45+
Visible="false" />
46+
</ItemGroup>
5147

5248
</Project>

0 commit comments

Comments
 (0)