diff --git a/.gitmodules b/.gitmodules index 380a6f5827ca..86e2b0d1131a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "cecil"] path = external/cecil - url = https://github.com/mono/cecil.git + url = https://github.com/dotnet/cecil.git diff --git a/Directory.Build.props b/Directory.Build.props index 62047707d44c..fd92a0f7f464 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,7 @@ + + false true true @@ -9,8 +11,6 @@ net7.0 - - false true $(MSBuildProjectName)/ref diff --git a/docs/design/feature-attribute-semantics.md b/docs/design/feature-attribute-semantics.md new file mode 100644 index 000000000000..946e62ab128f --- /dev/null +++ b/docs/design/feature-attribute-semantics.md @@ -0,0 +1,206 @@ +# Feature attribute semantics + +This specification defines the semantics of "feature attributes", in terms of a hypothetical `RequiresFeature` attribute type. The rules described here are designed to be applicable to any attribute that describes a feature or capability of the code or the platform. + +## Terminology + +There's a history in .NET of using the terms "features" and "capabilities" to describe closely related concepts. + +In the linker and runtime we often refer to "feature switches", which are toggles that can tell illink and NativeAot to treat specific predefined properties as constants. There are also "runtime features" such as support for new language features like byref fields. + +We also often refer to "platform capabilities" such as the ability to run dynamic code, or the ability to create new threads. Sometimes the deployment model may restrict the set of available APIs; in that case we also may refer to "capabilities" (for example the ability to access files on disk, which is not available in single-file apps). + +Capabilities will often have associated attributes and/or feature switches. For example, `RequiresDynamicCodeAttribute` is used to annotate code which requires support for running dynamic code, and `RuntimeFeature.IsDynamicCodeSupported` can be used to check for this support and guard access to annotated code. We also have many feature switches for features which depend on the availablility of so-called unreferenced code (that is, code which may be removed by trimming), which is attributed with `RequiresUnreferencedCodeAttribute`. + +Feature switch settings may also be determined by explicit configuration (in addition to the choice of target platform or deployment model). For example, the `System.Globalization.Invariant` feature switch can be used to disable support for culture-specific globalization behavior. + +This design takes the view that "features" and "capabilities" are essentially the same concept. We use the terminology "features" for these attribute semantics, because it seems to have slightly more general usage. "Features" as used today often includes runtime features which don't have feature switches or attributes, whereas "capabilities" is most often used to refer to capabilities of the platform or deployment model and do usually come with feature switches. + +The intention is for this specification to be generally applicable to any feature which would benefit from an attribute used to annotate code that should only be run when a given feature is available. The decision whether to introduce such a feature attribute is of course determined case by case by the feature owners. + +## Motivation + +Existing attributes like `RequiresUnreferencedCodeAttribute`, `RequiresDynamicCodeAttribute`, and `RequiresAssemblyFilesAttribute` have behavior close to what is described below. The behavior differs slightly between illink and NativeAot in the details, so this is an attempt to specify the semantics as clearly as possible, so that both tools can converge to match this. + +The ILLink Roslyn analyzer also produces warnings for these attributes, but doesn't have insight into the compilation strategy used for compiler-generated code. These rules are designed so that the warnings produced by a Roslyn analyzer are matched by the IL analysis, but IL analysis may include additional warnings (specifically for reflection access to compiler-generated code). + +There is also the possibility that we will create an attribute-based model which allows users to define their own feature attributes; see this draft for example: https://github.com/dotnet/designs/pull/261. The semantics outlined here could be extended to those attributes if we determine that they are appropriate there. + +We would also like to share as much code as possible for this logic between the linker, NativeAot, the corresponding analyzers, and possibly future analyzers. + +## Goals + +- Define the semantics of feature attributes +- Define the access patterns which are allowed and disallowed by these semantics + +## Non-goals + +- Specify the warning codes or wording of the specific warnings for disallowed access +- Define a model for defining new feature attributes +- Define an attribute-based model for feature switches +- Specify the relationship between feature switches and feature attributes +- Define the interactions between `RequiresUnreferencedCodeAttribute` and `DynamicallyAccessedMembersAttribute` + +## RequiresFeatureAttribute + +`RequiresFeatureAttribute` may be used on methods, constructors, or classes only. + +The use of this attribute establishes a [_feature requirement_](#feature-requirement) for the attributed type or member, which restricts access to the attributed type or member (and in some cases to other related IL) in certain ways. It also establishes a [_feature available_](#feature-available-scope) scope (which includes the attributed member but may also include other related IL) wherein access to members with a _feature requirement_ is allowed. + +Access to members with a _feature requirement_ is always allowed from a _feature available_ scope, and never produces feature warnings. The restrictions created by _feature requirement_ only limit access from scopes outside of _feature available_, where certain access patterns produce warnings. + +## Feature available scope + +The following constructs with a _feature requirement_ are also in a _feature available_ scope: +- Methods +- Constructors (including static constructors) + +When a class or struct has a _feature requirement_, the following members are in a _feature available_ scope: +- Methods +- Constructors (including static constructors) +- Fields +- Properties +- Events + +Note that the _feature available_ scope for a type does not extend to nested types or to members of base classes or interfaces implemented by the type. + +## Feature requirement + +### Methods + +When `RequiresFeature` is used on a method or constructor (except static constructors), this declares a _feature requirement_ for the method. + +`RequiresFeature` on a static constructor is not supported. Note however that static constructors may have a _feature requirement_ due to the declaring type having a _feature requirement_. + +### Classes + +When `RequiresFeature` is used on a class, this declares a _feature requirement_ for the class. + +When a class has a _feature requirement_, this creates a _feature requirement_ for the following members of the class: + - static methods + - all constructors (static and instance) + - static fields + - static properties + - static events + +Note that this does not create a _feature requirement_ for nested types or for members of base classes or interfaces implemented by the type. +Note also that this may create a _feature requirement_ for fields, properties, and events, which cannot have `RequiresFeature` used on them directly. + +### Structs + +When a struct has a _feature requirement_, this creates a _feature requirement_ for the following members of the struct: + - all methods + - all constructors (static and instance) + - all fields + - all properties + - all events + +Note that structs may have _feature requirement_ due to compiler-generated code, even though they can not have `RequiresFeature`. +Note also that this does not create a _feature requirement_ for members of interfaces implemented by the type. + +### State machine types + +When an iterator or async method is in a _feature available_ scope, the compiler-generated state machine class or type has a _feature requirement_. + +### Nested functions + +When a method is in a _feature available_ scope, lambdas and local functions declared in the method have a _feature requirement_. + +When a lambda or local function is in a _feature available_ scope, lambdas and local functions declared in it have a _feature requirement_. + +When a lambda or local function is declared in a method or nested function which is in a _feature available_ scope, then the following compiler-generated type or members have a _feature requirement_: + +- The generated closure environment type, if it is unique to the lambda or local function, OR + +- The generated method for the lambda or local function, if the compiler does not generate a type for the closure environment, OR + +- The generated method and delegate cache field for the lambda or local function, if these are generated into a static closure environment type. + +Note that IL analysis tools currently deviate from this specification because the IL does not always contain enough information to reconstruct the original nesting of lambdas and local functions. (For ILLink, lambdas and local functions inherit _feature requirement_ from the enclosing user method, not from an enclosing lambda or local function if one is present.) + +## Validation behavior + +### RequiresFeature attribute + +`RequiresFeature` on a static constructor is disallowed. + +`RequiresFeature` on a method that already has a _feature requirement_ due to another attribute is allowed. + +`RequiresFeature` on a method that is in a _feature available_ scope is allowed. This establishes a _feature requirement_ for the method even if there was not one previously. (Note: this could be made stricter by warning about redundant `RequiresFeature` on methods that are already in a _feature available_ scope.) + +### Virtual methods + +- Overriding a method that has a _feature requirement_ with a method outside of a _feature available_ scope is disallowed. +- Overriding a method outside of a _feature available_ scope with a method that has a _feature requirement_ is disallowed. + +### Member access + +Access to a _feature requirement_ method, constructor, field, property, or event outside of a _feature available_ scope is disallowed. + +## Feature checks + +Some feature attributes also come with corresponding feature checks that can be evaluated as constant at the time of trimming, with the guarded code removed when a feature is disabled. This effectivtely places the guarded code in a _feature available_ scope for the purposes of this analysis. However, the definition of such feature checks is left unspecified for now. + +## Trimming + +These semantics have been designed with trimming in mind. When a feature is disabled (by user configuration, or based on limitations of the target platform), trimming an app that will remove most or all of the feature-related code. Specifically, when a feature is disabled and an app has no trim warnings (including suppressed warnings): + +- Methods, fields, properties, and events which have a _feature requirement_ for the disabled feature may be removed. + +- Methods which are in a _feature available_ scope for the disabled feature, but aren't entirely removed, may have the method body replaced with a throwing instruction sequence. + +Thie latter can happen for methods in a type with _feature requirement_ (but that do not themselves have _feature requirement_) that are referenced outside of a _feature available_ scope. The reference to such a method may remain even though the type is never constructed. The callsite would produce a `NullReferenceException` and the method body is unreachable. + +## Alternatives + +One simplification would be to unify the concepts of _feature requirement_ with _feature available_, and treat both as similar to preprocessor symbols, where _any_ reference to a guarded type or member from an unguarded context is disallowed. + +The advantage of the specified model is that it allows some references without warning, giving some extra flexibility and making it easier to migrate existing code. The downside is that it might lead to preserving more code, whereas a simplified model could guarantee that all code related to a disabled feature is removed. + +Here is an example of a pattern which does not warn in the current model, but would warn with a simplified model. Assume that the code under `SomeFeatureIsSupported` is removed when the feature is unavailable. + +```csharp +class FeatureConsumer { + static void Run() { + SomeFeatureProvider? some; + if (Features.SomeFeatureIsSupported) + some = new SomeFeatureProvider(); + OtherFeatureProvider other = new(); + Helper(some, other); + } + + static void Helper(SomeFeatureProvider? some, OtherFeatureProvider other) { + some?.Use(); // This callsite would warn with the simplified model. + other.Use(); + } +} + +[RequiresSomeFeature] +class SomeFeatureProvider { + public void Use() {} +} + +class OtherFeatureProvider { + public void Use() {} +} +``` + +Note that the `SomeFeatureProvider` type and its `Use` method are kept, but the `Use` method will be rewritten to throw. + +The simplified model would encourage the above to be rewritten as follows, resulting in the entire type `SomeFeatureProvider` being removed: + +```csharp + +class FeatureConsumer { + static void Run() { + if (Features.SomeFeatureIsSupported) { + var some = new SomeFeatureProvider(); + some.Use(); + } + OtherFeatureProvider other = new(); + other.Use(); + } +} +``` + +Perhaps we could introduce the simplified model as an optional strict mode for people who are interested in rewriting their code for maximal size savings. \ No newline at end of file diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 6fa044a762cf..faf8d5675415 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -3,14 +3,14 @@ - + https://github.com/dotnet/arcade - 80b6be47e1425ea90c5febffac119250043a0c92 + 6b3bad6673f3ebe89ebe12ea7c4eff1705b893e6 - + https://github.com/dotnet/arcade - 80b6be47e1425ea90c5febffac119250043a0c92 + 6b3bad6673f3ebe89ebe12ea7c4eff1705b893e6 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 511c6dc29607..0d0c1d760ca4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -2,7 +2,7 @@ - 7 + 8 0 100 @@ -18,7 +18,7 @@ 5.0.0 17.0.0-preview-21267-01 17.0.0-preview-21267-01 - 8.0.0-beta.22554.2 + 8.0.0-beta.22579.2 6.0.0-beta.21271.1 3.10.0-2.final 4.5.0-1.22517.9 diff --git a/eng/common/cross/toolchain.cmake b/eng/common/cross/toolchain.cmake index 561576be97c2..964610524760 100644 --- a/eng/common/cross/toolchain.cmake +++ b/eng/common/cross/toolchain.cmake @@ -1,5 +1,12 @@ set(CROSS_ROOTFS $ENV{ROOTFS_DIR}) +# reset platform variables (e.g. cmake 3.25 sets LINUX=1) +unset(LINUX) +unset(FREEBSD) +unset(ILLUMOS) +unset(ANDROID) +unset(TIZEN) + set(TARGET_ARCH_NAME $ENV{TARGET_BUILD_ARCH}) if(EXISTS ${CROSS_ROOTFS}/bin/freebsd-version) set(CMAKE_SYSTEM_NAME FreeBSD) diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh index 41a26d802a93..292ebb653586 100644 --- a/eng/common/native/init-compiler.sh +++ b/eng/common/native/init-compiler.sh @@ -132,8 +132,8 @@ if [[ -z "$CC" ]]; then exit 1 fi -# Only lld version >= 9 can be considered stable -if [[ "$compiler" == "clang" && "$majorVersion" -ge 9 ]]; then +# Only lld version >= 9 can be considered stable. lld doesn't support s390x. +if [[ "$compiler" == "clang" && "$majorVersion" -ge 9 && "$build_arch" != "s390x" ]]; then if "$CC" -fuse-ld=lld -Wl,--version >/dev/null 2>&1; then LDFLAGS="-fuse-ld=lld" fi diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index 39be08d4b16c..e10a59687974 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -64,7 +64,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.3.1" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.4.1" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index 1f1b78f2d459..42017109f374 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -34,6 +34,7 @@ jobs: - job: Asset_Registry_Publish dependsOn: ${{ parameters.dependsOn }} + timeoutInMinutes: 150 ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: displayName: Publish Assets @@ -73,12 +74,6 @@ jobs: - task: NuGetAuthenticate@0 - - task: PowerShell@2 - displayName: Enable cross-org NuGet feed authentication - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/enable-cross-org-publishing.ps1 - arguments: -token $(dn-bot-all-orgs-artifact-feeds-rw) - - task: PowerShell@2 displayName: Publish Build Assets inputs: diff --git a/eng/common/templates/post-build/common-variables.yml b/eng/common/templates/post-build/common-variables.yml index 1ac7f49a43ca..c24193acfc98 100644 --- a/eng/common/templates/post-build/common-variables.yml +++ b/eng/common/templates/post-build/common-variables.yml @@ -1,8 +1,4 @@ variables: - - group: AzureDevOps-Artifact-Feeds-Pats - - group: DotNet-Blob-Feed - - group: DotNet-DotNetCli-Storage - - group: DotNet-MSRC-Storage - group: Publish-Build-Assets # Whether the build is internal or not diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index 91251d08973c..ef720f9d7819 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -172,12 +172,6 @@ stages: - task: NuGetAuthenticate@0 displayName: 'Authenticate to AzDO Feeds' - - task: PowerShell@2 - displayName: Enable cross-org publishing - inputs: - filePath: eng\common\enable-cross-org-publishing.ps1 - arguments: -token $(dn-bot-dnceng-artifact-feeds-rw) - # Signing validation will optionally work with the buildmanifest file which is downloaded from # Azure DevOps above. - task: PowerShell@2 diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 449126943299..fc56f63f6f25 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -365,8 +365,8 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_packaging?_a=package&feed=dotnet-eng&package=RoslynTools.MSBuild&protocolType=NuGet&version=17.3.1view=overview - $defaultXCopyMSBuildVersion = '17.3.1' + # https://dev.azure.com/dnceng/public/_packaging?_a=package&feed=dotnet-eng&package=RoslynTools.MSBuild&protocolType=NuGet&version=17.4.1&view=overview + $defaultXCopyMSBuildVersion = '17.4.1' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { @@ -413,6 +413,7 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = if($vsMinVersion -lt $vsMinVersionReqd){ Write-Host "Using xcopy-msbuild version of $defaultXCopyMSBuildVersion since VS version $vsMinVersionStr provided in global.json is not compatible" $xcopyMSBuildVersion = $defaultXCopyMSBuildVersion + $vsMajorVersion = $xcopyMSBuildVersion.Split('.')[0] } else{ # If the VS version IS compatible, look for an xcopy msbuild package diff --git a/eng/common/tools.sh b/eng/common/tools.sh index 6586eab458e5..e7d1e3897e03 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -521,7 +521,7 @@ global_json_file="${repo_root}global.json" # determine if global.json contains a "runtimes" entry global_json_has_runtimes=false if command -v jq &> /dev/null; then - if jq -er '. | select(has("runtimes"))' "$global_json_file" &> /dev/null; then + if jq -e '.tools | has("runtimes")' "$global_json_file" &> /dev/null; then global_json_has_runtimes=true fi elif [[ "$(cat "$global_json_file")" =~ \"runtimes\"[[:space:]\:]*\{ ]]; then diff --git a/global.json b/global.json index 6134944de06a..29b15ae7adb5 100644 --- a/global.json +++ b/global.json @@ -1,15 +1,14 @@ { "sdk": { - "version": "7.0.100-rc.1.22431.12", + "version": "7.0.100", "allowPrerelease": true, "rollForward": "major" }, "tools": { - "dotnet": "7.0.100-rc.1.22431.12", - "xcopy-msbuild": "17.2.1" + "dotnet": "7.0.100" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.22554.2", + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.22579.2", "Microsoft.FIX-85B6-MERGE-9C38-CONFLICT": "1.0.0", "Microsoft.NET.Sdk.IL": "7.0.0-rtm.22507.1" } diff --git a/src/ILLink.RoslynAnalyzer/COMAnalyzer.cs b/src/ILLink.RoslynAnalyzer/COMAnalyzer.cs index 8fe4c7caf098..2ffba6701ac3 100644 --- a/src/ILLink.RoslynAnalyzer/COMAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/COMAnalyzer.cs @@ -40,7 +40,7 @@ public override void Initialize (AnalysisContext context) if (!targetMethod.HasAttribute (DllImportAttribute)) return; - if (operationContext.ContainingSymbol.IsInRequiresUnreferencedCodeAttributeScope ()) + if (operationContext.ContainingSymbol.IsInRequiresUnreferencedCodeAttributeScope (out _)) return; bool comDangerousMethod = IsComInterop (targetMethod.ReturnType); diff --git a/src/ILLink.RoslynAnalyzer/DataFlow/DynamicallyAccessedMembersBinder.cs b/src/ILLink.RoslynAnalyzer/DataFlow/DynamicallyAccessedMembersBinder.cs index a3b1898ee752..c0018482e13b 100644 --- a/src/ILLink.RoslynAnalyzer/DataFlow/DynamicallyAccessedMembersBinder.cs +++ b/src/ILLink.RoslynAnalyzer/DataFlow/DynamicallyAccessedMembersBinder.cs @@ -113,7 +113,7 @@ public static IEnumerable GetConstructorsOnType (this ITypeSymbol { foreach (var method in type.GetMembers ().OfType ()) { - if (method.MethodKind != MethodKind.Constructor) + if (!method.IsConstructor ()) continue; if (filter != null && !filter (method)) @@ -142,7 +142,7 @@ public static IEnumerable GetMethodsOnTypeHierarchy (this ITypeSy while (type != null) { foreach (var method in type.GetMembers ().OfType ()) { // Ignore constructors as those are not considered methods from a reflection's point of view - if (method.MethodKind == MethodKind.Constructor) + if (method.IsConstructor ()) continue; // Ignore private methods on a base type - those are completely ignored by reflection diff --git a/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowAnalysis.cs b/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowAnalysis.cs index f06b005da2d0..6b9c8bb1781c 100644 --- a/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowAnalysis.cs +++ b/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowAnalysis.cs @@ -63,7 +63,7 @@ public void InterproceduralAnalyze () oldInterproceduralState = interproceduralState.Clone (); foreach (var method in oldInterproceduralState.Methods) { - if (method.Method.IsInRequiresUnreferencedCodeAttributeScope ()) + if (method.Method.IsInRequiresUnreferencedCodeAttributeScope (out _)) continue; AnalyzeMethod (method, ref interproceduralState); diff --git a/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs b/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs index d78468022033..1c8ee148389c 100644 --- a/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs @@ -73,7 +73,7 @@ public override void Initialize (AnalysisContext context) return; context.RegisterOperationBlockAction (context => { - if (context.OwningSymbol.IsInRequiresUnreferencedCodeAttributeScope ()) + if (context.OwningSymbol.IsInRequiresUnreferencedCodeAttributeScope (out _)) return; @@ -111,7 +111,7 @@ static void ProcessGenericParameters (SyntaxNodeAnalysisContext context) // warnings about base type arguments. if (context.ContainingSymbol is not null && context.ContainingSymbol is not INamedTypeSymbol - && context.ContainingSymbol.IsInRequiresUnreferencedCodeAttributeScope ()) + && context.ContainingSymbol.IsInRequiresUnreferencedCodeAttributeScope (out _)) return; var symbol = context.SemanticModel.GetSymbolInfo (context.Node).Symbol; diff --git a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs index 0d39282d389e..c3aff3cbe239 100644 --- a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs @@ -170,7 +170,7 @@ public static bool IsSubclassOf (this ISymbol symbol, string ns, string type) } public static bool IsConstructor ([NotNullWhen (returnValue: true)] this ISymbol? symbol) - => (symbol as IMethodSymbol)?.MethodKind == MethodKind.Constructor; + => (symbol as IMethodSymbol)?.MethodKind is MethodKind.Constructor or MethodKind.StaticConstructor; 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 2922c51ca2d7..af531091e535 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs @@ -134,7 +134,7 @@ public override void Initialize (AnalysisContext context) context.RegisterSyntaxNodeAction (syntaxNodeAnalysisContext => { var model = syntaxNodeAnalysisContext.SemanticModel; - if (syntaxNodeAnalysisContext.ContainingSymbol is not ISymbol containingSymbol || containingSymbol.IsInRequiresScope (RequiresAttributeName)) + if (syntaxNodeAnalysisContext.ContainingSymbol is not ISymbol containingSymbol || containingSymbol.IsInRequiresScope (RequiresAttributeName, out _)) return; GenericNameSyntax genericNameSyntaxNode = (GenericNameSyntax) syntaxNodeAnalysisContext.Node; @@ -200,7 +200,7 @@ void CheckCalledMember ( ISymbol containingSymbol = FindContainingSymbol (operationContext, AnalyzerDiagnosticTargets); // Do not emit any diagnostic if caller is annotated with the attribute too. - if (containingSymbol.IsInRequiresScope (RequiresAttributeName)) + if (containingSymbol.IsInRequiresScope (RequiresAttributeName, out _)) return; if (ReportSpecialIncompatibleMembersDiagnostic (operationContext, incompatibleMembers, member)) diff --git a/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs index c4504332e9b9..a6e8bb809a2c 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs @@ -15,10 +15,7 @@ public static class RequiresISymbolExtensions public static bool DoesMemberRequire (this ISymbol member, string requiresAttribute, [NotNullWhen (returnValue: true)] out AttributeData? requiresAttributeData) { requiresAttributeData = null; - if (member.IsStaticConstructor ()) - return false; - - if (member.TryGetAttribute (requiresAttribute, out requiresAttributeData)) + if (!member.IsStaticConstructor () && member.TryGetAttribute (requiresAttribute, out requiresAttributeData)) return true; // Also check the containing type @@ -32,9 +29,9 @@ public static bool DoesMemberRequire (this ISymbol member, string requiresAttrib /// /// True if the source of a call is considered to be annotated with the Requires... attribute /// - public static bool IsInRequiresScope (this ISymbol member, string requiresAttribute) + public static bool IsInRequiresScope (this ISymbol member, string attributeName, [NotNullWhen (true)] out AttributeData? requiresAttribute) { - return member.IsInRequiresScope (requiresAttribute, true); + return member.IsInRequiresScope (attributeName, true, out requiresAttribute); } /// @@ -46,41 +43,46 @@ public static bool IsInRequiresScope (this ISymbol member, string requiresAttrib /// public static bool IsOverrideInRequiresScope (this ISymbol member, string requiresAttribute) { - return member.IsInRequiresScope (requiresAttribute, false); + return member.IsInRequiresScope (requiresAttribute, false, out _); } - private static bool IsInRequiresScope (this ISymbol member, string requiresAttribute, bool checkAssociatedSymbol) + private static bool IsInRequiresScope (this ISymbol member, string attributeName, bool checkAssociatedSymbol, [NotNullWhen (true)] out AttributeData? requiresAttribute) { // Requires attribute on a type does not silence warnings that originate // from the type directly. We also only check the containing type for members // below, not of nested types. - if (member is ITypeSymbol) + if (member is ITypeSymbol) { + requiresAttribute = null; return false; + } while (true) { - if (member.HasAttribute (requiresAttribute) && !member.IsStaticConstructor ()) + if (member.TryGetAttribute (attributeName, out requiresAttribute) && !member.IsStaticConstructor ()) return true; if (member.ContainingSymbol is not IMethodSymbol method) break; member = method; } - if (member.ContainingType is ITypeSymbol containingType && containingType.HasAttribute (requiresAttribute)) + if (member.ContainingType is ITypeSymbol containingType && containingType.TryGetAttribute (attributeName, out requiresAttribute)) return true; // Only check associated symbol if not override or virtual method - if (checkAssociatedSymbol && member is IMethodSymbol { AssociatedSymbol: { } associated } && associated.HasAttribute (requiresAttribute)) + if (checkAssociatedSymbol && member is IMethodSymbol { AssociatedSymbol: { } associated } && associated.TryGetAttribute (attributeName, out requiresAttribute)) return true; // When using instance fields suppress the warning if the constructor has already the Requires annotation if (member is IFieldSymbol field && !field.IsStatic) { foreach (var constructor in field.ContainingType.InstanceConstructors) { - if (!constructor.HasAttribute (requiresAttribute)) + if (!constructor.TryGetAttribute (attributeName, out requiresAttribute)) { + requiresAttribute = null; return false; + } } - return true; + return requiresAttribute != null; } + requiresAttribute = null; return false; } } diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index f2d2878791d5..c4ebfb516a30 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -30,7 +30,7 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase static readonly Action s_dynamicTypeInvocation = operationContext => { if (FindContainingSymbol (operationContext, DiagnosticTargets.All) is ISymbol containingSymbol && - containingSymbol.IsInRequiresScope (RequiresUnreferencedCodeAttribute)) + containingSymbol.IsInRequiresScope (RequiresUnreferencedCodeAttribute, out _)) return; operationContext.ReportDiagnostic (Diagnostic.Create (s_dynamicTypeInvocationRule, diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeUtils.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeUtils.cs index f24b866dea7d..d6b17d008c4a 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeUtils.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeUtils.cs @@ -25,8 +25,8 @@ public static bool DoesMemberRequireUnreferencedCodeAttribute (this ISymbol memb /// /// True if the source of a call is considered to be annotated with the RequiresUnreferencedCode attribute /// - public static bool IsInRequiresUnreferencedCodeAttributeScope (this ISymbol member) - => member.IsInRequiresScope (RequiresUnreferencedCodeAttribute); + public static bool IsInRequiresUnreferencedCodeAttributeScope (this ISymbol member, [NotNullWhen (true)] out AttributeData? requiresAttribute) + => member.IsInRequiresScope (RequiresUnreferencedCodeAttribute, out requiresAttribute); /// /// This method verifies that the arguments in an attribute have certain structure. diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index 78ea087f1987..318e9a0101fe 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -77,8 +77,8 @@ private partial bool TryResolveTypeNameForCreateInstanceAndMark (in MethodProxy return false; } - // TODO: Does the analyzer need to do something here? - private partial void MarkStaticConstructor (TypeProxy type) { } + private partial void MarkStaticConstructor (TypeProxy type) + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForConstructorsOnType (_diagnosticContext, type.Type, BindingFlags.Static, parameterCount: 0); private partial void MarkEventsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForEventsOnTypeHierarchy (_diagnosticContext, type.Type, name, bindingFlags); diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs index ec5f170c8703..09abc1332083 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs @@ -80,7 +80,7 @@ static void ReportRequiresUnreferencedCodeDiagnostic (in DiagnosticContext diagn internal static void GetReflectionAccessDiagnosticsForMethod (in DiagnosticContext diagnosticContext, IMethodSymbol methodSymbol) { - if (methodSymbol.TryGetRequiresUnreferencedCodeAttribute (out var requiresUnreferencedCodeAttributeData)) + if (methodSymbol.IsInRequiresUnreferencedCodeAttributeScope (out var requiresUnreferencedCodeAttributeData)) ReportRequiresUnreferencedCodeDiagnostic (diagnosticContext, requiresUnreferencedCodeAttributeData, methodSymbol); else if (methodSymbol.IsVirtual && FlowAnnotations.GetMethodReturnValueAnnotation (methodSymbol) != DynamicallyAccessedMemberTypes.None) diagnosticContext.AddDiagnostic (DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection, methodSymbol.GetDisplayName ()); diff --git a/src/ILLink.Tasks/LinkTask.cs b/src/ILLink.Tasks/LinkTask.cs index 170ef8f7bf81..b89b893be681 100644 --- a/src/ILLink.Tasks/LinkTask.cs +++ b/src/ILLink.Tasks/LinkTask.cs @@ -43,11 +43,8 @@ public class ILLink : ToolTask /// /// The names of the assemblies to root. This should contain /// assembly names without an extension, not file names or - /// paths. Exactly which parts of the assemblies get rooted - /// is subject to change. Currently these get passed to - /// illink with "-a", which roots the entry point for - /// executables, and everything for libraries. To control - /// the behaviour explicitly, set RootMode metadata. + /// paths. The default is to root everything in these assemblies. + // For more fine-grained control, set RootMode metadata. /// [Required] public ITaskItem[] RootAssemblyNames { get; set; } diff --git a/src/ILLink.Tasks/build/Microsoft.NET.ILLink.targets b/src/ILLink.Tasks/build/Microsoft.NET.ILLink.targets index 84430b7686bb..8330d0718133 100644 --- a/src/ILLink.Tasks/build/Microsoft.NET.ILLink.targets +++ b/src/ILLink.Tasks/build/Microsoft.NET.ILLink.targets @@ -64,7 +64,7 @@ Copyright (c) .NET Foundation. All rights reserved. - + $(NoWarn);IL2121 @@ -279,9 +279,9 @@ Copyright (c) .NET Foundation. All rights reserved. - + - + diff --git a/src/linker/Linker.Dataflow/CompilerGeneratedState.cs b/src/linker/Linker.Dataflow/CompilerGeneratedState.cs index 36950fb9c03f..cc7d205b9fb9 100644 --- a/src/linker/Linker.Dataflow/CompilerGeneratedState.cs +++ b/src/linker/Linker.Dataflow/CompilerGeneratedState.cs @@ -126,6 +126,7 @@ public static bool TryGetStateMachineType (MethodDefinition method, [NotNullWhen var callGraph = new CompilerGeneratedCallGraph (); var userDefinedMethods = new HashSet (); + var generatedTypeToTypeArgs = new Dictionary (); void ProcessMethod (MethodDefinition method) { @@ -152,14 +153,15 @@ void ProcessMethod (MethodDefinition method) if (referencedMethod == null) continue; + // Find calls to state machine constructors that occur outside the type if (referencedMethod.IsConstructor && referencedMethod.DeclaringType is var generatedType && // Don't consider calls in the same type, like inside a static constructor method.DeclaringType != generatedType && CompilerGeneratedNames.IsLambdaDisplayClass (generatedType.Name)) { // fill in null for now, attribute providers will be filled in later - if (!_generatedTypeToTypeArgumentInfo.TryAdd (generatedType, new TypeArgumentInfo (method, null))) { - var alreadyAssociatedMethod = _generatedTypeToTypeArgumentInfo[generatedType].CreatingMethod; + if (!generatedTypeToTypeArgs.TryAdd (generatedType, new TypeArgumentInfo (method, null))) { + var alreadyAssociatedMethod = generatedTypeToTypeArgs[generatedType].CreatingMethod; _context.LogWarning (new MessageOrigin (method), DiagnosticId.MethodsAreAssociatedWithUserMethod, method.GetDisplayName (), alreadyAssociatedMethod.GetDisplayName (), generatedType.GetDisplayName ()); } continue; @@ -189,7 +191,7 @@ referencedMethod.DeclaringType is var generatedType && // Don't consider field accesses in the same type, like inside a static constructor method.DeclaringType != generatedType && CompilerGeneratedNames.IsLambdaDisplayClass (generatedType.Name)) { - if (!_generatedTypeToTypeArgumentInfo.TryAdd (generatedType, new TypeArgumentInfo (method, null))) { + if (!generatedTypeToTypeArgs.TryAdd (generatedType, new TypeArgumentInfo (method, null))) { // It's expected that there may be multiple methods associated with the same static closure environment. // All of these methods will substitute the same type arguments into the closure environment // (if it is generic). Don't warn. @@ -214,7 +216,7 @@ referencedMethod.DeclaringType is var generatedType && } // Already warned above if multiple methods map to the same type // Fill in null for argument providers now, the real providers will be filled in later - _generatedTypeToTypeArgumentInfo[stateMachineType] = new TypeArgumentInfo (method, null); + generatedTypeToTypeArgs[stateMachineType] = new TypeArgumentInfo (method, null); } } @@ -280,9 +282,17 @@ referencedMethod.DeclaringType is var generatedType && // Now that we have instantiating methods fully filled out, walk the generated types and fill in the attribute // providers - foreach (var generatedType in _generatedTypeToTypeArgumentInfo.Keys) { - if (HasGenericParameters (generatedType)) - MapGeneratedTypeTypeParameters (generatedType); + foreach (var generatedType in generatedTypeToTypeArgs.Keys) { + if (HasGenericParameters (generatedType)) { + MapGeneratedTypeTypeParameters (generatedType, generatedTypeToTypeArgs, _context); + // Finally, add resolved type arguments to the cache + var info = generatedTypeToTypeArgs[generatedType]; + if (!_generatedTypeToTypeArgumentInfo.TryAdd (generatedType, info)) { + var method = info.CreatingMethod; + var alreadyAssociatedMethod = _generatedTypeToTypeArgumentInfo[generatedType].CreatingMethod; + _context.LogWarning (new MessageOrigin (method), DiagnosticId.MethodsAreAssociatedWithUserMethod, method.GetDisplayName (), alreadyAssociatedMethod.GetDisplayName (), generatedType.GetDisplayName ()); + } + } } _cachedTypeToCompilerGeneratedMembers.Add (type, compilerGeneratedCallees); @@ -301,18 +311,42 @@ static bool HasGenericParameters (TypeDefinition typeDef) return typeDef.GenericParameters.Count > typeDef.DeclaringType.GenericParameters.Count; } - void MapGeneratedTypeTypeParameters (TypeDefinition generatedType) + /// + /// Attempts to reverse the process of the compiler's alpha renaming. So if the original code was + /// something like this: + /// + /// void M<T> () { + /// Action a = () => { Console.WriteLine (typeof (T)); }; + /// } + /// + /// The compiler will generate a nested class like this: + /// + /// class <>c__DisplayClass0<T> { + /// public void <M>b__0 () { + /// Console.WriteLine (typeof (T)); + /// } + /// } + /// + /// The task of this method is to figure out that the type parameter T in the nested class is the same + /// as the type parameter T in the parent method M. + /// acts as a memoization table to avoid recalculating the + /// mapping multiple times. + /// + static void MapGeneratedTypeTypeParameters ( + TypeDefinition generatedType, + Dictionary generatedTypeToTypeArgs, + LinkContext context) { Debug.Assert (CompilerGeneratedNames.IsGeneratedType (generatedType.Name)); - var typeInfo = _generatedTypeToTypeArgumentInfo[generatedType]; + var typeInfo = generatedTypeToTypeArgs[generatedType]; if (typeInfo.OriginalAttributes is not null) { return; } var method = typeInfo.CreatingMethod; if (method.Body is { } body) { var typeArgs = new ICustomAttributeProvider[generatedType.GenericParameters.Count]; - var typeRef = ScanForInit (generatedType, body); + var typeRef = ScanForInit (generatedType, body, context); if (typeRef is null) { return; } @@ -334,9 +368,9 @@ void MapGeneratedTypeTypeParameters (TypeDefinition generatedType) var owningRef = (TypeReference) owner; if (!CompilerGeneratedNames.IsGeneratedType (owningRef.Name)) { userAttrs = param; - } else if (_context.TryResolve ((TypeReference) param.Owner) is { } owningType) { - MapGeneratedTypeTypeParameters (owningType); - if (_generatedTypeToTypeArgumentInfo[owningType].OriginalAttributes is { } owningAttrs) { + } else if (context.TryResolve ((TypeReference) param.Owner) is { } owningType) { + MapGeneratedTypeTypeParameters (owningType, generatedTypeToTypeArgs, context); + if (generatedTypeToTypeArgs[owningType].OriginalAttributes is { } owningAttrs) { userAttrs = owningAttrs[param.Position]; } else { Debug.Assert (false, "This should be impossible in valid code"); @@ -348,19 +382,22 @@ void MapGeneratedTypeTypeParameters (TypeDefinition generatedType) typeArgs[i] = userAttrs; } - _generatedTypeToTypeArgumentInfo[generatedType] = typeInfo with { OriginalAttributes = typeArgs }; + generatedTypeToTypeArgs[generatedType] = typeInfo with { OriginalAttributes = typeArgs }; } } - GenericInstanceType? ScanForInit (TypeDefinition compilerGeneratedType, MethodBody body) + static GenericInstanceType? ScanForInit ( + TypeDefinition compilerGeneratedType, + MethodBody body, + LinkContext context) { - foreach (var instr in _context.GetMethodIL (body).Instructions) { + foreach (var instr in context.GetMethodIL (body).Instructions) { bool handled = false; switch (instr.OpCode.Code) { case Code.Initobj: case Code.Newobj: { if (instr.Operand is MethodReference { DeclaringType: GenericInstanceType typeRef } - && compilerGeneratedType == _context.TryResolve (typeRef)) { + && compilerGeneratedType == context.TryResolve (typeRef)) { return typeRef; } handled = true; @@ -368,7 +405,7 @@ void MapGeneratedTypeTypeParameters (TypeDefinition generatedType) break; case Code.Stsfld: { if (instr.Operand is FieldReference { DeclaringType: GenericInstanceType typeRef } - && compilerGeneratedType == _context.TryResolve (typeRef)) { + && compilerGeneratedType == context.TryResolve (typeRef)) { return typeRef; } handled = true; @@ -381,7 +418,7 @@ void MapGeneratedTypeTypeParameters (TypeDefinition generatedType) if (!handled && instr.OpCode.OperandType is OperandType.InlineMethod) { if (instr.Operand is GenericInstanceMethod gim) { foreach (var tr in gim.GenericArguments) { - if (tr is GenericInstanceType git && compilerGeneratedType == _context.TryResolve (git)) { + if (tr is GenericInstanceType git && compilerGeneratedType == context.TryResolve (git)) { return git; } } diff --git a/src/linker/Linker.Dataflow/GenericArgumentDataFlow.cs b/src/linker/Linker.Dataflow/GenericArgumentDataFlow.cs index a4092c4a9f80..5b43e7799c67 100644 --- a/src/linker/Linker.Dataflow/GenericArgumentDataFlow.cs +++ b/src/linker/Linker.Dataflow/GenericArgumentDataFlow.cs @@ -30,7 +30,7 @@ public void ProcessGenericArgumentDataFlow (GenericParameter genericParameter, T MultiValue genericArgumentValue = _context.Annotations.FlowAnnotations.GetTypeValueFromGenericArgument (genericArgument); - var diagnosticContext = new DiagnosticContext (_origin, !_context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (_origin.Provider), _context); + var diagnosticContext = new DiagnosticContext (_origin, !_context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (_origin.Provider, out _), _context); RequireDynamicallyAccessedMembers (diagnosticContext, genericArgumentValue, genericParameterValue); } diff --git a/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs b/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs index 81010a3a6257..5ab5ab3488f3 100644 --- a/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs +++ b/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs @@ -34,7 +34,7 @@ public TrimAnalysisAssignmentPattern Merge (ValueSetLattice lattice public void MarkAndProduceDiagnostics (ReflectionMarker reflectionMarker, LinkContext context) { - bool diagnosticsEnabled = !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (Origin.Provider); + bool diagnosticsEnabled = !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (Origin.Provider, out _); var diagnosticContext = new DiagnosticContext (Origin, diagnosticsEnabled, context); foreach (var sourceValue in Source) { diff --git a/src/linker/Linker.Dataflow/TrimAnalysisMethodCallPattern.cs b/src/linker/Linker.Dataflow/TrimAnalysisMethodCallPattern.cs index be4219ae6d69..a9d9cdf8e303 100644 --- a/src/linker/Linker.Dataflow/TrimAnalysisMethodCallPattern.cs +++ b/src/linker/Linker.Dataflow/TrimAnalysisMethodCallPattern.cs @@ -63,7 +63,7 @@ public TrimAnalysisMethodCallPattern Merge (ValueSetLattice lattice public void MarkAndProduceDiagnostics (ReflectionMarker reflectionMarker, MarkStep markStep, LinkContext context) { - bool diagnosticsEnabled = !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (Origin.Provider); + bool diagnosticsEnabled = !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (Origin.Provider, out _); var diagnosticContext = new DiagnosticContext (Origin, diagnosticsEnabled, context); ReflectionMethodBodyScanner.HandleCall (Operation, CalledMethod, Instance, Arguments, diagnosticContext, diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index 67a3c42b36bf..1c5f10340ae4 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -710,11 +710,12 @@ void ProcessVirtualMethod (MethodDefinition method) /// /// Returns true if the Override in should be marked because it is needed by the base method. /// Does not take into account if the base method is in a preserved scope. - /// Assumes the base method is marked. + /// Assumes the base method is marked or comes from a preserved scope. /// // TODO: Move interface method marking logic here https://github.com/dotnet/linker/issues/3090 bool ShouldMarkOverrideForBase (OverrideInformation overrideInformation) { + Debug.Assert (Annotations.IsMarked (overrideInformation.Base) || IgnoreScope (overrideInformation.Base.DeclaringType.Scope)); if (!Annotations.IsMarked (overrideInformation.Override.DeclaringType)) return false; if (overrideInformation.IsOverrideOfInterfaceMember) { @@ -725,8 +726,9 @@ bool ShouldMarkOverrideForBase (OverrideInformation overrideInformation) if (!Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, overrideInformation.Override)) return true; - // Methods on instantiated types that override a ov.Override from a base type (not an interface) should be marked - // Interface ov.Overrides should only be marked if the interfaceImplementation is marked, which is handled below + // In this context, an override needs to be kept if + // a) it's an override on an instantiated type (of a marked base) or + // b) it's an override of an abstract base (required for valid IL) if (Annotations.IsInstantiated (overrideInformation.Override.DeclaringType)) return true; @@ -744,6 +746,7 @@ bool ShouldMarkOverrideForBase (OverrideInformation overrideInformation) // TODO: Take into account a base method in preserved scope void MarkOverrideForBaseMethod (OverrideInformation overrideInformation) { + Debug.Assert (ShouldMarkOverrideForBase (overrideInformation)); if (Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, overrideInformation.Override) && Annotations.IsInstantiated (overrideInformation.Override.DeclaringType)) { MarkMethod (overrideInformation.Override, new DependencyInfo (DependencyKind.OverrideOnInstantiatedType, overrideInformation.Override.DeclaringType), ScopeStack.CurrentScope.Origin); } else { @@ -1653,7 +1656,7 @@ static bool IsDeclaredWithinType (IMemberDefinition member, TypeDefinition type) // When marking override methods via DynamicallyAccessedMembers, we should only issue a warning for the base method. bool skipWarningsForOverride = member is MethodDefinition m && m.IsVirtual && Annotations.GetBaseMethods (m) != null; - bool isReflectionAccessCoveredByRUC = Annotations.DoesMemberRequireUnreferencedCode (member, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCodeAttribute); + bool isReflectionAccessCoveredByRUC = Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (member, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCodeAttribute); if (isReflectionAccessCoveredByRUC && !skipWarningsForOverride) { var id = reportOnMember ? DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberWithRequiresUnreferencedCode : DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithRequiresUnreferencedCode; Context.LogWarning (origin, id, type.GetDisplayName (), @@ -1777,11 +1780,11 @@ void ProcessAnalysisAnnotationsForField (FieldDefinition field, DependencyKind d return; } - if (Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (origin.Provider)) + if (Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (origin.Provider, out _)) return; bool isReflectionAccessCoveredByRUC; - if (isReflectionAccessCoveredByRUC = Annotations.DoesFieldRequireUnreferencedCode (field, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCodeAttribute)) + if (isReflectionAccessCoveredByRUC = Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (field, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCodeAttribute)) ReportRequiresUnreferencedCode (field.GetDisplayName (), requiresUnreferencedCodeAttribute!, new DiagnosticContext (origin, diagnosticsEnabled: true, Context)); bool isReflectionAccessCoveredByDAM = false; @@ -2034,12 +2037,14 @@ internal void MarkStaticConstructorVisibleToReflection (TypeDefinition type, in _typesWithInterfaces.Add ((type, ScopeStack.CurrentScope)); if (type.HasMethods) { - // TODO: MarkMethodIfNeededByBaseMethod should include logic for IsMethodNeededBytTypeDueToPreservedScope + // TODO: MarkMethodIfNeededByBaseMethod should include logic for IsMethodNeededByTypeDueToPreservedScope: https://github.com/dotnet/linker/issues/3090 foreach (var method in type.Methods) { MarkMethodIfNeededByBaseMethod (method); + if (IsMethodNeededByTypeDueToPreservedScope (method)) { + // For methods that must be preserved, blame the declaring type. + MarkMethod (method, new DependencyInfo (DependencyKind.VirtualNeededDueToPreservedScope, type), ScopeStack.CurrentScope.Origin); + } } - // For methods that must be preserved, blame the declaring type. - MarkMethodsIf (type.Methods, IsMethodNeededByTypeDueToPreservedScope, new DependencyInfo (DependencyKind.VirtualNeededDueToPreservedScope, type), ScopeStack.CurrentScope.Origin); if (ShouldMarkTypeStaticConstructor (type) && reason.Kind != DependencyKind.TriggersCctorForCalledMethod) { using (ScopeStack.PopToParent ()) MarkStaticConstructor (type, new DependencyInfo (DependencyKind.CctorForType, type), ScopeStack.CurrentScope.Origin); @@ -2929,12 +2934,38 @@ protected internal void MarkIndirectlyCalledMethod (MethodDefinition method, in if (Annotations.GetAction (method) == MethodAction.Nothing) Annotations.SetAction (method, MethodAction.Parse); - EnqueueMethod (method, reason, origin); // Use the original reason as it's important to correctly generate warnings // the updated reason is only useful for better tracking of dependencies. ProcessAnalysisAnnotationsForMethod (method, originalReasonKind, origin); + // Record the reason for marking a method on each call. + switch (reason.Kind) { + case DependencyKind.AlreadyMarked: + Debug.Assert (Annotations.IsMarked (method)); + break; + default: + Annotations.Mark (method, reason, origin); + break; + } + + bool markedForCall = + reason.Kind == DependencyKind.DirectCall || + reason.Kind == DependencyKind.VirtualCall || + reason.Kind == DependencyKind.Newobj; + if (markedForCall) { + // Record declaring type of a called method up-front as a special case so that we may + // track at least some method calls that trigger a cctor. + // Temporarily switch to the original source for marking this method + // this is for the same reason as for tracking, but this time so that we report potential + // warnings from a better place. + MarkType (method.DeclaringType, new DependencyInfo (DependencyKind.DeclaringTypeOfCalledMethod, method), new MessageOrigin (reason.Source as IMemberDefinition ?? method)); + } + + // We will only enqueue a method to be processed if it hasn't been processed yet. + if (!CheckProcessed (method)) + EnqueueMethod (method, reason, origin); + return method; } @@ -3009,6 +3040,13 @@ void ProcessAnalysisAnnotationsForMethod (MethodDefinition method, DependencyKin // since in those cases the warnings are desirable (potential access through reflection). case DependencyKind.MemberOfType: + // Used when marking a cctor because a type or field is kept. This should not warn because we already warn + // on access to members of the type which could trigger the cctor. + case DependencyKind.CctorForType: + case DependencyKind.CctorForField: + case DependencyKind.TriggersCctorThroughFieldAccess: + case DependencyKind.TriggersCctorForCalledMethod: + // We should not be generating code which would produce warnings case DependencyKind.UnreachableBodyRequirement: @@ -3032,15 +3070,30 @@ void ProcessAnalysisAnnotationsForMethod (MethodDefinition method, DependencyKin break; }; - if (Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (origin.Provider)) + if (Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (origin.Provider, out _)) return; - // All override methods should have the same annotations as their base methods - // (else we will produce warning IL2046 or IL2092 or some other warning). - // When marking override methods via DynamicallyAccessedMembers, we should only issue a warning for the base method. - bool skipWarningsForOverride = dependencyKind == DependencyKind.DynamicallyAccessedMember && method.IsVirtual && Annotations.GetBaseMethods (method) != null; + bool skipWarningsForOverride; + bool isReflectionAccessCoveredByRUC; + RequiresUnreferencedCodeAttribute? requiresUnreferencedCode; + if (dependencyKind == DependencyKind.AttributeProperty) { + // Property assignment in an attribute instance. + // This case is more like a direct method call than reflection, and should + // be logically similar to what is done in ReflectionMethodBodyScanner for method calls. + skipWarningsForOverride = false; + isReflectionAccessCoveredByRUC = Annotations.DoesMethodRequireUnreferencedCode (method, out requiresUnreferencedCode); + } else { + // All override methods should have the same annotations as their base methods + // (else we will produce warning IL2046 or IL2092 or some other warning). + // When marking override methods via DynamicallyAccessedMembers, we should only issue a warning for the base method. + skipWarningsForOverride = dependencyKind == DependencyKind.DynamicallyAccessedMember && method.IsVirtual && Annotations.GetBaseMethods (method) != null; + // If the method being accessed has warnings suppressed due to Requires attributes, + // we need to issue a warning for the reflection access. This is true even for instance + // methods, which can be reflection-invoked without ever calling a constructor of the + // accessed type. + isReflectionAccessCoveredByRUC = Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (method, out requiresUnreferencedCode); + } - bool isReflectionAccessCoveredByRUC = Annotations.DoesMethodRequireUnreferencedCode (method, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCode); if (isReflectionAccessCoveredByRUC && !skipWarningsForOverride) ReportRequiresUnreferencedCode (method.GetDisplayName (), requiresUnreferencedCode!, new DiagnosticContext (origin, diagnosticsEnabled: true, Context)); @@ -3106,32 +3159,10 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo using var parentScope = ScopeStack.PushScope (new MarkScopeStack.Scope (origin)); using var methodScope = ScopeStack.PushScope (new MessageOrigin (method)); - // Record the reason for marking a method on each call. The logic under CheckProcessed happens - // only once per method. - switch (reason.Kind) { - case DependencyKind.AlreadyMarked: - Debug.Assert (Annotations.IsMarked (method)); - break; - default: - Annotations.Mark (method, reason, ScopeStack.CurrentScope.Origin); - break; - } - bool markedForCall = reason.Kind == DependencyKind.DirectCall || reason.Kind == DependencyKind.VirtualCall || reason.Kind == DependencyKind.Newobj; - if (markedForCall) { - // Record declaring type of a called method up-front as a special case so that we may - // track at least some method calls that trigger a cctor. - // Temporarily switch to the original source for marking this method - // this is for the same reason as for tracking, but this time so that we report potential - // warnings from a better place. - MarkType (method.DeclaringType, new DependencyInfo (DependencyKind.DeclaringTypeOfCalledMethod, method), new MessageOrigin (reason.Source as IMemberDefinition ?? method)); - } - - if (CheckProcessed (method)) - return; foreach (Action handleMarkMethod in MarkContext.MarkMethodActions) handleMarkMethod (method); @@ -3190,7 +3221,7 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo MarkBaseMethods (method); if (Annotations.GetOverrides (method) is IEnumerable overrides) { - foreach (var @override in overrides) { + foreach (var @override in overrides.Where (ov => Annotations.IsMarked (ov.Base) || IgnoreScope (ov.Base.DeclaringType.Scope))) { if (ShouldMarkOverrideForBase (@override)) MarkOverrideForBaseMethod (@override); } diff --git a/src/linker/Linker.Steps/RootAssemblyInputStep.cs b/src/linker/Linker.Steps/RootAssemblyInputStep.cs index 7e4d47b2cc4a..2bc77920e401 100644 --- a/src/linker/Linker.Steps/RootAssemblyInputStep.cs +++ b/src/linker/Linker.Steps/RootAssemblyInputStep.cs @@ -42,11 +42,6 @@ protected override void Process () } switch (rootMode) { - case AssemblyRootMode.Default: - if (assembly.MainModule.Kind == ModuleKind.Dll) - goto case AssemblyRootMode.AllMembers; - else - goto case AssemblyRootMode.EntryPoint; case AssemblyRootMode.EntryPoint: var ep = assembly.MainModule.EntryPoint; if (ep == null) { diff --git a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs index 28eb66f1513b..2d9097f975df 100644 --- a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs +++ b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs @@ -617,7 +617,7 @@ void RewriteCondition (int index, Instruction instr, int operand) case Code.Switch: var targets = (Instruction[]) instr.Operand; - if (operand <= targets.Length) { + if (operand < targets.Length) { // It does not need to be conditional but existing logic in BodySweeper would // need to be updated to deal with 1->2 instruction replacement RewriteConditionTo (index, Instruction.Create (operand == 0 ? OpCodes.Brfalse : OpCodes.Brtrue, targets[operand])); diff --git a/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs b/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs index a13876880118..807a9b03b8a8 100644 --- a/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs +++ b/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs @@ -40,8 +40,8 @@ protected override void Process () void ValidateMethodRequiresUnreferencedCodeAreSame (MethodDefinition method, MethodDefinition baseMethod) { var annotations = Context.Annotations; - bool methodHasAttribute = annotations.IsInRequiresUnreferencedCodeScope (method); - if (methodHasAttribute != annotations.IsInRequiresUnreferencedCodeScope (baseMethod)) { + bool methodHasAttribute = annotations.IsInRequiresUnreferencedCodeScope (method, out _); + if (methodHasAttribute != annotations.IsInRequiresUnreferencedCodeScope (baseMethod, out _)) { string message = MessageFormat.FormatRequiresAttributeMismatch (methodHasAttribute, baseMethod.DeclaringType.IsInterface, nameof (RequiresUnreferencedCodeAttribute), method.GetDisplayName (), baseMethod.GetDisplayName ()); Context.LogWarning (method, DiagnosticId.RequiresUnreferencedCodeAttributeMismatch, message); diff --git a/src/linker/Linker/Annotations.cs b/src/linker/Linker/Annotations.cs index 4729ec773de8..6576436b963c 100644 --- a/src/linker/Linker/Annotations.cs +++ b/src/linker/Linker/Annotations.cs @@ -601,52 +601,42 @@ public bool TryGetLinkerAttribute (IMemberDefinition member, [NotNullWhen (re /// Determines if method is within a declared RUC scope - this typically means that trim analysis /// warnings should be suppressed in such a method. /// - /// Unlike + /// Unlike /// if a declaring type has RUC, all methods in that type are considered "in scope" of that RUC. So this includes also /// instance methods (not just statics and .ctors). - internal bool IsInRequiresUnreferencedCodeScope (MethodDefinition method) + internal bool IsInRequiresUnreferencedCodeScope (MethodDefinition method, [NotNullWhen (true)] out RequiresUnreferencedCodeAttribute? attribute) { - if (HasLinkerAttribute (method) && !method.IsStaticConstructor ()) + if (TryGetLinkerAttribute (method, out attribute) && !method.IsStaticConstructor ()) return true; - if (method.DeclaringType is not null && HasLinkerAttribute (method.DeclaringType)) + if (method.DeclaringType is not null && TryGetLinkerAttribute (method.DeclaringType, out attribute)) return true; + attribute = null; return false; } - /// - /// Determines if a member requires unreferenced code (and thus any usage of such method should be warned about). - /// - /// Unlike only static methods - /// and .ctors are reported as requiring unreferenced code when the declaring type has RUC on it. - internal bool DoesMemberRequireUnreferencedCode (IMemberDefinition member, [NotNullWhen (returnValue: true)] out RequiresUnreferencedCodeAttribute? attribute) + internal bool ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (ICustomAttributeProvider? originMember, [NotNullWhen (true)] out RequiresUnreferencedCodeAttribute? attribute) { attribute = null; - return member switch { - MethodDefinition method => DoesMethodRequireUnreferencedCode (method, out attribute), - FieldDefinition field => DoesFieldRequireUnreferencedCode (field, out attribute), - _ => false - }; - } - - internal bool ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (ICustomAttributeProvider? originMember) - { // Check if the current scope method has RequiresUnreferencedCode on it // since that attribute automatically suppresses all trim analysis warnings. // Check both the immediate origin method as well as suppression context method // since that will be different for compiler generated code. if (originMember is MethodDefinition && - IsInRequiresUnreferencedCodeScope ((MethodDefinition) originMember)) + IsInRequiresUnreferencedCodeScope ((MethodDefinition) originMember, out attribute)) return true; + if (originMember is FieldDefinition field) + return DoesFieldRequireUnreferencedCode (field, out attribute); + if (originMember is not IMemberDefinition member) return false; MethodDefinition? owningMethod; while (context.CompilerGeneratedState.TryGetOwningMethodForCompilerGeneratedMember (member, out owningMethod)) { Debug.Assert (owningMethod != member); - if (IsInRequiresUnreferencedCodeScope (owningMethod)) + if (IsInRequiresUnreferencedCodeScope (owningMethod, out attribute)) return true; member = owningMethod; } @@ -654,15 +644,16 @@ internal bool ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (ICustom return false; } + /// + /// Determines if a method requires unreferenced code (and thus any usage of such method should be warned about). + /// + /// Unlike only static methods + /// and .ctors are reported as requiring unreferenced code when the declaring type has RUC on it. internal bool DoesMethodRequireUnreferencedCode (MethodDefinition originalMethod, [NotNullWhen (returnValue: true)] out RequiresUnreferencedCodeAttribute? attribute) { MethodDefinition? method = originalMethod; do { - if (method.IsStaticConstructor ()) { - attribute = null; - return false; - } - if (TryGetLinkerAttribute (method, out attribute)) + if (!method.IsStaticConstructor () && TryGetLinkerAttribute (method, out attribute)) return true; if ((method.IsStatic || method.IsConstructor) && method.DeclaringType is not null && @@ -670,6 +661,7 @@ internal bool DoesMethodRequireUnreferencedCode (MethodDefinition originalMethod return true; } while (context.CompilerGeneratedState.TryGetOwningMethodForCompilerGeneratedMember (method, out method)); + attribute = null; return false; } diff --git a/src/linker/Linker/AssemblyRootMode.cs b/src/linker/Linker/AssemblyRootMode.cs index 07ceac1625a6..c9a3d98a604c 100644 --- a/src/linker/Linker/AssemblyRootMode.cs +++ b/src/linker/Linker/AssemblyRootMode.cs @@ -5,9 +5,8 @@ namespace Mono.Linker { public enum AssemblyRootMode { - Default = 0, + AllMembers = 0, EntryPoint, - AllMembers, VisibleMembers, Library } diff --git a/src/linker/Linker/Driver.cs b/src/linker/Linker/Driver.cs index f92457544ef5..7b9e10bdda64 100644 --- a/src/linker/Linker/Driver.cs +++ b/src/linker/Linker/Driver.cs @@ -639,10 +639,10 @@ protected int SetupContext (ILogger? customLogger = null) return -1; } - AssemblyRootMode rmode = AssemblyRootMode.Default; + AssemblyRootMode rmode = AssemblyRootMode.AllMembers; var rootMode = GetNextStringValue (); if (rootMode != null) { - var parsed_rmode = ParseAssemblyRootsMode (rootMode); + var parsed_rmode = ParseAssemblyRootMode (rootMode); if (parsed_rmode is null) return -1; @@ -1115,11 +1115,9 @@ static string[] ReadLines (string file) return null; } - AssemblyRootMode? ParseAssemblyRootsMode (string s) + AssemblyRootMode? ParseAssemblyRootMode (string s) { switch (s.ToLowerInvariant ()) { - case "default": - return AssemblyRootMode.Default; case "all": return AssemblyRootMode.AllMembers; case "visible": diff --git a/src/linker/Linker/TypeReferenceExtensions.cs b/src/linker/Linker/TypeReferenceExtensions.cs index d93a7578d528..f7f69995ba5a 100644 --- a/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/linker/Linker/TypeReferenceExtensions.cs @@ -66,6 +66,7 @@ public static StringBuilder GetDisplayNameWithoutNamespace (this TypeReference t break; } + type = type.GetElementType (); if (type.DeclaringType is not TypeReference declaringType) break; diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs index ad36b4884366..12e0844b5593 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs @@ -52,6 +52,12 @@ public Task RequiresInCompilerGeneratedCode () return RunTest (nameof (RequiresInCompilerGeneratedCode)); } + [Fact] + public Task RequiresInCompilerGeneratedCodeRelease () + { + return RunTest (); + } + [Fact] public Task RequiresOnAttribute () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/UnreachableBlockTests.cs b/test/ILLink.RoslynAnalyzer.Tests/UnreachableBlockTests.cs index 5b3b8d5f993f..f2edd0c93e09 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/UnreachableBlockTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/UnreachableBlockTests.cs @@ -15,5 +15,11 @@ public Task TryFilterBlocks () { return RunTest (allowMissingWarnings: true); } + + [Fact (Skip = "ILLink analyzers don't support constant propagation https://github.com/dotnet/linker/issues/2715")] + public Task CompilerGeneratedCodeSubstitutions () + { + return RunTest (allowMissingWarnings: true); + } } } \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.NoSecurityTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.NoSecurityTests.g.cs index 0b1237f30a62..4b8032519376 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.NoSecurityTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.NoSecurityTests.g.cs @@ -15,5 +15,17 @@ public Task CoreLibrarySecurityAttributeTypesAreRemoved () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task SecurityAttributesOnUsedMethodAreRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task SecurityAttributesOnUsedTypeAreRemoved () + { + return RunTest (allowMissingWarnings: true); + } + } } \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.OnlyKeepUsedTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.OnlyKeepUsedTests.g.cs index 12033311190e..be6d427d35c0 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.OnlyKeepUsedTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.OnlyKeepUsedTests.g.cs @@ -9,6 +9,24 @@ public sealed partial class OnlyKeepUsedTests : LinkerTestBase protected override string TestSuiteName => "Attributes.OnlyKeepUsed"; + [Fact] + public Task AttributeDefinedAndUsedInOtherAssemblyIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AttributeUsedByAttributeIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task CanLinkCoreLibrariesWithOnlyKeepUsedAttributes () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task ComAttributesArePreserved () { @@ -63,5 +81,119 @@ public Task ThreadStaticIsPreservedOnField () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task UnusedAttributeOnGenericParameterIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributeOnReturnTypeIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributePreservedViaLinkXmlIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributeTypeOnAssemblyIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributeTypeOnEventIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributeTypeOnMethodIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributeTypeOnModuleIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributeTypeOnParameterIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributeTypeOnPropertyIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributeTypeOnTypeIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedAttributeWithTypeForwarderIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedDerivedAttributeType () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedAttributeTypeOnAssemblyIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedAttributeTypeOnEventIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedAttributeTypeOnMethodIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedAttributeTypeOnModuleIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedAttributeTypeOnParameterIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedAttributeTypeOnPropertyIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedAttributeTypeOnTypeIsKept () + { + return RunTest (allowMissingWarnings: true); + } + } } \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.StructLayoutTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.StructLayoutTests.g.cs index 93c54e738608..82d0662e3f6a 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.StructLayoutTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.StructLayoutTests.g.cs @@ -27,5 +27,11 @@ public Task SequentialClass () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task UnusedTypeWithSequentialLayoutIsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + } } \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/AttributesTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/AttributesTests.g.cs index 672db426f8fd..5eb17e8a4856 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/AttributesTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/AttributesTests.g.cs @@ -9,6 +9,36 @@ public sealed partial class AttributesTests : LinkerTestBase protected override string TestSuiteName => "Attributes"; + [Fact] + public Task AssemblyAttributeIsRemovedIfOnlyTypesUsedInAssembly () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AssemblyAttributeKeptInComplexCase () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AttributeOnAssemblyIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AttributeOnAssemblyIsKeptIfDeclarationIsSkipped () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AttributeOnParameterInUsedMethodIsKept () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task AttributeOnPreservedTypeIsKept () { @@ -63,6 +93,12 @@ public Task AttributeOnUsedPropertyIsKept () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task BoxedValues () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task CoreLibraryAssemblyAttributesAreKept () { @@ -75,12 +111,48 @@ public Task FixedLengthArrayAttributesArePreserved () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task GenericAttributes () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task IVTUnused () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task IVTUsed () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task MarshalAsCustomMarshaler () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task MarshalAsCustomMarshalerInterface () { return RunTest (allowMissingWarnings: true); } + [Fact] + public Task SecurityAttributesOnUsedMethodAreKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task SecurityAttributesOnUsedTypeAreKept () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task TypeUsedInObjectArrayConstructorArgumentOnAttributeIsKept () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/BasicTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/BasicTests.g.cs index c5834582d31f..d512aed60e39 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/BasicTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/BasicTests.g.cs @@ -51,6 +51,12 @@ public Task MultiLevelNestedClassesAllRemovedWhenNonUsed () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task NestedDelegateInvokeMethodsPreserved () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task NeverInstantiatedTypeWithOverridesFromObject () { @@ -69,6 +75,18 @@ public Task UnusedClassGetsRemoved () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task UnusedDelegateGetsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedEnumGetsRemoved () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task UnusedEventGetsRemoved () { @@ -111,12 +129,30 @@ public Task UnusedPropertySetterRemoved () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task UsedEnumIsKept () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task UsedEventIsKept () { return RunTest (allowMissingWarnings: true); } + [Fact] + public Task UsedEventOnInterfaceIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedEventOnInterfaceIsRemovedWhenUsedFromClass () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task UsedGenericInterfaceIsKept () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DataFlowTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DataFlowTests.g.cs index 77154abde851..2dad733fa65c 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DataFlowTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DataFlowTests.g.cs @@ -7,6 +7,12 @@ namespace ILLink.RoslynAnalyzer.Tests public sealed partial class DataFlowTests : LinkerTestBase { + [Fact] + public Task GenericParameterWarningLocation () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task MethodByRefParameterDataFlow () { @@ -25,6 +31,12 @@ public Task StaticInterfaceMethodDataflow () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task TypeInfoIntrinsics () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task UnsafeDataFlow () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DynamicDependenciesTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DynamicDependenciesTests.g.cs index d512c99c98b5..f515de3ef024 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DynamicDependenciesTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/DynamicDependenciesTests.g.cs @@ -93,6 +93,12 @@ public Task DynamicDependencyMethodInNonReferencedAssemblyWithSweptReferences () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task DynamicDependencyOnForwardedType () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task DynamicDependencyOnUnusedMethodInNonReferencedAssembly () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.BaseProvidesInterfaceEdgeCaseTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.BaseProvidesInterfaceEdgeCaseTests.g.cs new file mode 100644 index 000000000000..5aaf8776a261 --- /dev/null +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.BaseProvidesInterfaceEdgeCaseTests.g.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace ILLink.RoslynAnalyzer.Tests.Inheritance.Interfaces +{ + public sealed partial class BaseProvidesInterfaceEdgeCaseTests : LinkerTestBase + { + + protected override string TestSuiteName => "Inheritance.Interfaces.BaseProvidesInterfaceEdgeCase"; + + [Fact] + public Task BaseProvidesInterfaceMethodCircularReference () + { + return RunTest (allowMissingWarnings: true); + } + + } +} \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs index 47d8e64e29e0..4d0079b0661e 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs @@ -21,6 +21,12 @@ public Task GenericDefaultInterfaceMethods () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceWithAttributeOnImplementation () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task SimpleDefaultInterfaceMethod () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.OnReferenceTypeTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.OnReferenceTypeTests.g.cs index b61435b86b72..89b18fde5162 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.OnReferenceTypeTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.OnReferenceTypeTests.g.cs @@ -93,6 +93,12 @@ public Task InterfaceMarkOrderingDoesNotMatter3 () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceNeededOnUnrelatedInterfaceList () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task InterfaceTypeInOtherUsedOnlyByCopiedAssembly () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.StaticInterfaceMethodsTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.StaticInterfaceMethodsTests.g.cs index 472da5e89a78..77157ac14983 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.StaticInterfaceMethodsTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.StaticInterfaceMethodsTests.g.cs @@ -8,7 +8,7 @@ public sealed partial class StaticInterfaceMethodsTests : LinkerTestBase { [Fact] - public Task BaseProvidesInterfaceMethod () + public Task VarianceBasic () { return RunTest (allowMissingWarnings: true); } diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.VirtualMethodsTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.VirtualMethodsTests.g.cs index 5ddfcf4f3b67..94801f87f106 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.VirtualMethodsTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.VirtualMethodsTests.g.cs @@ -28,7 +28,7 @@ public Task NeverInstantiatedTypeWithBaseInCopiedAssembly () } [Fact] - public Task OverrideInUnmarkedClassIsRemoved () + public Task OverrideOfAbstractInUnmarkedClassIsRemoved () { return RunTest (allowMissingWarnings: true); } diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/References.IndividualTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/References.IndividualTests.g.cs new file mode 100644 index 000000000000..419677634696 --- /dev/null +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/References.IndividualTests.g.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace ILLink.RoslynAnalyzer.Tests.References +{ + public sealed partial class IndividualTests : LinkerTestBase + { + + protected override string TestSuiteName => "References.Individual"; + + [Fact] + public Task CanSkipUnresolved () + { + return RunTest (allowMissingWarnings: true); + } + + } +} \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs index 23c3173c0e9a..b729ec4a892f 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs @@ -37,6 +37,12 @@ public Task CoreLibMessages () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task IsAssignableFrom () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task ObjectGetTypeLibraryMode () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/SymbolsTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/SymbolsTests.g.cs new file mode 100644 index 000000000000..333d8a0945e6 --- /dev/null +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/SymbolsTests.g.cs @@ -0,0 +1,235 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace ILLink.RoslynAnalyzer.Tests +{ + public sealed partial class SymbolsTests : LinkerTestBase + { + + protected override string TestSuiteName => "Symbols"; + + [Fact] + public Task AssemblyWithDefaultSymbols () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AssemblyWithDefaultSymbolsAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferencesWithMixedSymbolTypes () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferencesWithMixedSymbolTypesAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferencesWithMixedSymbolTypesWithMdbAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithEmbeddedPdb () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithEmbeddedPdbAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithEmbeddedPdbAndSymbolLinkingEnabledAndDeterministicMvid () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithEmbeddedPdbAndSymbolLinkingEnabledAndNewMvid () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithEmbeddedPdbCopyAction () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithEmbeddedPdbCopyActionAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithEmbeddedPdbDeleteAction () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithEmbeddedPdbDeleteActionAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithMdb () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithMdbAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithMdbAndSymbolLinkingEnabledAndDeterministicMvid () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithMdbAndSymbolLinkingEnabledAndNewMvid () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithMdbCopyAction () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithMdbCopyActionAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithMdbDeleteAction () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithMdbDeleteActionAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPdb () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPdbAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPdbAndSymbolLinkingEnabledAndDeterministicMvid () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPdbAndSymbolLinkingEnabledAndNewMvid () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPdbCopyAction () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPdbCopyActionAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPdbDeleteAction () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPdbDeleteActionAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPortablePdb () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPortablePdbAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPortablePdbAndSymbolLinkingEnabledAndDeterministicMvid () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPortablePdbAndSymbolLinkingEnabledAndNewMvid () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPortablePdbCopyAction () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPortablePdbCopyActionAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPortablePdbDeleteAction () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ReferenceWithPortablePdbDeleteActionAndSymbolLinkingEnabled () + { + return RunTest (allowMissingWarnings: true); + } + + } +} \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TestFrameworkTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TestFrameworkTests.g.cs index 76279e066643..8b935e4a1c67 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TestFrameworkTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TestFrameworkTests.g.cs @@ -15,6 +15,12 @@ public Task CanCheckInitializersByIndex () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task CanCompileILAssembly () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task CanCompileReferencesUsingTypes () { @@ -39,6 +45,24 @@ public Task CanCompileReferencesWithResourcesWithMcs () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task CanCompileTestCaseWithCsc () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task CanCompileTestCaseWithDebugPdbs () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task CanCompileTestCaseWithMcs () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task CanSandboxDependenciesUsingType () { @@ -63,6 +87,12 @@ public Task VerifyAttributesInAssemblyWorksWithStrings () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task VerifyDefineAttributeBehavior () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task VerifyExpectModifiedAttributesWork () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TypeForwardingTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TypeForwardingTests.g.cs index 1b2bb4b15981..cca59d56aedf 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TypeForwardingTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TypeForwardingTests.g.cs @@ -9,12 +9,54 @@ public sealed partial class TypeForwardingTests : LinkerTestBase protected override string TestSuiteName => "TypeForwarding"; + [Fact] + public Task AttributeArgumentForwarded () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AttributeArgumentForwardedWithCopyAction () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AttributeEnumArgumentForwarded () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AttributeEnumArgumentForwardedCopyUsedWithSweptForwarder () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task AttributesScopeUpdated () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task MissingTargetReference () { return RunTest (allowMissingWarnings: true); } + [Fact] + public Task MultiForwardedTypesWithCopyUsed () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task MultiForwardedTypesWithLink () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task SecurityAttributeScope () { @@ -33,6 +75,66 @@ public Task TypeForwarderOnlyAssembliesRemoved () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task TypeForwarderOnlyAssemblyCanBePreservedViaLinkXml () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task TypeForwardersModifiers () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task TypeForwardersRewrite () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedForwarderWithAssemblyCopyIsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedForwarderWithAssemblyCopyUsed () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedForwarderWithAssemblyLinked () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UnusedForwarderWithAssemblyLinkedAndFacadeCopy () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedAndUnusedForwarderReferencedFromCopyUsedAssembly () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedAndUnusedForwarderWithAssemblyCopy () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedForwarderAndUnusedReference () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task UsedForwarderInCopyAssemblyKeptByPreserveDependency () { @@ -81,5 +183,77 @@ public Task UsedForwarderInCopyAssemblyKeptByUsedTypeAsGenericArg () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task UsedForwarderInGenericIsDynamicallyAccessedWithAssemblyCopyUsed () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedForwarderIsDynamicallyAccessedWithAssemblyCopyUsed () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedForwarderIsRemovedWhenLink () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedForwarderWithAssemblyCopy () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedForwarderWithAssemblyCopyUsed () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedForwarderWithAssemblyCopyUsedAndForwarderLibraryKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedForwarderWithAssemblyCopyUsedAndUnusedReference () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedTransitiveForwarderInCopyAssemblyIsDynamicallyAccessed () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedTransitiveForwarderInCopyUsedAssemblyIsDynamicallyAccessed () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedTransitiveForwarderIsDynamicallyAccessed () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedTransitiveForwarderIsResolvedAndFacadeRemoved () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task UsedTransitiveForwarderIsResolvedAndFacadeRemovedInCopyAssembly () + { + return RunTest (allowMissingWarnings: true); + } + } } \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Warnings.IndividualTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Warnings.IndividualTests.g.cs index 8bcd66357ba7..745b5ccb5746 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Warnings.IndividualTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Warnings.IndividualTests.g.cs @@ -21,6 +21,12 @@ public Task CanGenerateWarningSuppressionFileXml () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task CustomStepWithWarnings () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task WarningsAreSorted () { diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Warnings.WarningSuppressionTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Warnings.WarningSuppressionTests.g.cs index 116fd82395eb..4185931ffaae 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Warnings.WarningSuppressionTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Warnings.WarningSuppressionTests.g.cs @@ -61,6 +61,30 @@ public Task DetectRedundantSuppressionsSingleWarn () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task DetectRedundantSuppressionsTrimmedMembersTarget () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ModuleSuppressionWithMemberScopeNullTarget () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ModuleSuppressionWithModuleScopeNullTarget () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task ModuleSuppressionWithNullScopeNullTarget () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task SuppressWarningsInAssembly () { @@ -97,5 +121,11 @@ public Task SuppressWarningsUsingTargetViaXmlNetCore () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task TargettedModuleSuppressionWithUnmatchedScope () + { + return RunTest (allowMissingWarnings: true); + } + } } \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/System.Text.RegularExpressions.Generator/System.Text.RegularExpressions.Generator.RegexGenerator/RegexGenerator.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/System.Text.RegularExpressions.Generator/System.Text.RegularExpressions.Generator.RegexGenerator/RegexGenerator.g.cs deleted file mode 100644 index b61e47c934f4..000000000000 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/System.Text.RegularExpressions.Generator/System.Text.RegularExpressions.Generator.RegexGenerator/RegexGenerator.g.cs +++ /dev/null @@ -1,5 +0,0 @@ -// -#nullable enable -#pragma warning disable CS0162 // Unreachable code -#pragma warning disable CS0164 // Unreferenced label -#pragma warning disable CS0219 // Variable assigned but never used diff --git a/test/ILLink.Tasks.Tests/Mock.cs b/test/ILLink.Tasks.Tests/Mock.cs index e4fba2d23064..979d62b1448a 100644 --- a/test/ILLink.Tasks.Tests/Mock.cs +++ b/test/ILLink.Tasks.Tests/Mock.cs @@ -178,7 +178,7 @@ public Dictionary GetCustomData () protected override List CreateDefaultResolvers () { return new List () { - new RootAssemblyInput (null, AssemblyRootMode.Default) + new RootAssemblyInput (null, AssemblyRootMode.EntryPoint) }; } } diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs b/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs index 64c31a813f5a..317e3b31310c 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs @@ -105,10 +105,10 @@ public static IEnumerable IteratorWithProblematicDataflow () [RequiresUnreferencedCode ("--RUCTypeWithIterators--")] class RUCTypeWithIterators { - [ExpectedWarning ("IL2112", nameof (StaticIteratorCallsMethodWithRequires), "--RUCTypeWithIterators--", - ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2112", "<" + nameof (StaticIteratorCallsMethodWithRequires) + ">", "--RUCTypeWithIterators--", CompilerGeneratedCode = true, + [ExpectedWarning ("IL2112", "<" + nameof (StaticIteratorCallsMethodWithRequires) + ">", "(Int32)", "--RUCTypeWithIterators--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer)] // state machine ctor + [ExpectedWarning ("IL2112", nameof (StaticIteratorCallsMethodWithRequires) + "()", "--RUCTypeWithIterators--", + ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] public static IEnumerable StaticIteratorCallsMethodWithRequires () @@ -117,11 +117,9 @@ public static IEnumerable StaticIteratorCallsMethodWithRequires () MethodWithRequires (); } - // BUG: this should also give IL2112 for the InstanceIteratorCallsMethodWithRequires state machine constructor. - // https://github.com/dotnet/linker/issues/2806 - // [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithIterators.InstanceIteratorCallsMethodWithRequires) + ">")] - // With that, the IL2119 warning should also go away. - [ExpectedWarning ("IL2119", "<" + nameof (InstanceIteratorCallsMethodWithRequires) + ">", "MoveNext", CompilerGeneratedCode = true, + [ExpectedWarning ("IL2112", "<" + nameof (InstanceIteratorCallsMethodWithRequires) + ">", "(Int32)", "--RUCTypeWithIterators--", CompilerGeneratedCode = true, + ProducedBy = ProducedBy.Trimmer)] // state machine ctor + [ExpectedWarning ("IL2112", nameof (InstanceIteratorCallsMethodWithRequires) + "()", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] @@ -147,12 +145,9 @@ public IEnumerable InstanceIteratorCallsMethodWithRequires () [ExpectedWarning ("IL2026", nameof (RUCTypeWithIterators.StaticIteratorCallsMethodWithRequires) + "()", "--RUCTypeWithIterators--")] [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithIterators.StaticIteratorCallsMethodWithRequires) + ">", ProducedBy = ProducedBy.Trimmer)] - // BUG: this should also give IL2026 for the InstanceIteratorCallsMethodWithRequires state machine constructor. - // https://github.com/dotnet/linker/issues/2806 - // [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithIterators.InstanceIteratorCallsMethodWithRequires) + ">")] - // With that, the IL2118 warning should also go away. - [ExpectedWarning ("IL2118", "<" + nameof (RUCTypeWithIterators.InstanceIteratorCallsMethodWithRequires) + ">", "MoveNext", - ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", nameof (RUCTypeWithIterators.InstanceIteratorCallsMethodWithRequires) + "()")] + [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithIterators.InstanceIteratorCallsMethodWithRequires) + ">", "(Int32)", + ProducedBy = ProducedBy.Trimmer)] // state machine ctor [ExpectedWarning ("IL2118", "<" + nameof (IteratorWithCorrectDataflow) + ">", "", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2118", "<" + nameof (IteratorWithProblematicDataflow) + ">", "", @@ -365,10 +360,11 @@ static void LambdaCallsPInvokeTakingObject () [RequiresUnreferencedCode ("--RUCTypeWithLambdas--")] class RUCTypeWithLambdas { + [ExpectedWarning ("IL2112", nameof (MethodWithLambdas), "--RUCTypeWithLambdas--", ProducedBy = ProducedBy.Trimmer)] public void MethodWithLambdas () { var lambda = - [ExpectedWarning ("IL2119", "<" + nameof (MethodWithLambdas) + ">", + [ExpectedWarning ("IL2112", "<" + nameof (MethodWithLambdas) + ">", "--RUCTypeWithLambdas--", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] @@ -376,7 +372,7 @@ public void MethodWithLambdas () int i = 0; var lambdaWithCapturedState = - [ExpectedWarning ("IL2119", "<" + nameof (MethodWithLambdas) + ">", + [ExpectedWarning ("IL2112", "<" + nameof (MethodWithLambdas) + ">", "--RUCTypeWithLambdas--", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] @@ -402,11 +398,11 @@ public void MethodWithLambdas () ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2118", "<" + nameof (LambdaWithCapturedTypeToDAM) + ">", ProducedBy = ProducedBy.Trimmer)] - // Expect RUC warnings for static, compiler-generated code warnings for instance. [ExpectedWarning ("IL2026", nameof (RUCTypeWithLambdas) + "()", "--RUCTypeWithLambdas--")] - [ExpectedWarning ("IL2118", "<" + nameof (RUCTypeWithLambdas.MethodWithLambdas) + ">", + [ExpectedWarning ("IL2026", nameof (RUCTypeWithLambdas.MethodWithLambdas) + "()", "--RUCTypeWithLambdas--")] + [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithLambdas.MethodWithLambdas) + ">", "--RUCTypeWithLambdas--", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2118", "<" + nameof (RUCTypeWithLambdas.MethodWithLambdas) + ">", + [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithLambdas.MethodWithLambdas) + ">", "--RUCTypeWithLambdas--", ProducedBy = ProducedBy.Trimmer)] public static void Test (Lambdas test = null) { @@ -501,6 +497,8 @@ static void LocalFunctionCallsPInvokeTakingObject () [RequiresUnreferencedCode ("--RUCTypeWithLocalFunctions--")] class RUCTypeWithLocalFunctions { + [ExpectedWarning ("IL2112", nameof (MethodWithLocalFunctions), "--RUCTypeWithLocalFunctions--", + ProducedBy = ProducedBy.Trimmer)] public void MethodWithLocalFunctions () { [ExpectedWarning ("IL2112", "<" + nameof (MethodWithLocalFunctions) + ">", @@ -550,6 +548,7 @@ void LocalFunctionWithCapturedState () ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithLocalFunctions.MethodWithLocalFunctions) + ">", "LocalFunction", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", nameof (RUCTypeWithLocalFunctions.MethodWithLocalFunctions) + "()")] public static void Test (LocalFunctions test = null) { typeof (LocalFunctions).RequiresAll (); diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/Dependencies/TypeInfoCalls.il b/test/Mono.Linker.Tests.Cases/DataFlow/Dependencies/TypeInfoCalls.il new file mode 100644 index 000000000000..af2e7eaaf19e --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DataFlow/Dependencies/TypeInfoCalls.il @@ -0,0 +1,111 @@ +// Metadata version: v4.0.30319 +.assembly extern System.Runtime +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: +} +.assembly 'library' +{ + .hash algorithm 0x00008004 + .ver 1:0:0:0 +} +.module library.dll + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed beforefieldinit Library.TypeInfoCalls + extends [System.Runtime]System.Object +{ + .method public hidebysig static void TestGetConstructors(class [System.Runtime]System.Reflection.TypeInfo 'type') cil managed + { + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance class [System.Runtime]System.Reflection.ConstructorInfo[] [System.Runtime]System.Reflection.TypeInfo::GetConstructors() + IL_0007: pop + IL_0008: ret + } + + .method public hidebysig static void TestGetMethods(class [System.Runtime]System.Reflection.TypeInfo 'type') cil managed + { + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance class [System.Runtime]System.Reflection.MethodInfo[] [System.Runtime]System.Reflection.TypeInfo::GetMethods() + IL_0007: pop + IL_0008: ret + } + + .method public hidebysig static void TestGetFields(class [System.Runtime]System.Reflection.TypeInfo 'type') cil managed + { + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance class [System.Runtime]System.Reflection.FieldInfo[] [System.Runtime]System.Reflection.TypeInfo::GetFields() + IL_0007: pop + IL_0008: ret + } + + .method public hidebysig static void TestGetProperties(class [System.Runtime]System.Reflection.TypeInfo 'type') cil managed + { + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance class [System.Runtime]System.Reflection.PropertyInfo[] [System.Runtime]System.Reflection.TypeInfo::GetProperties() + IL_0007: pop + IL_0008: ret + } + + .method public hidebysig static void TestGetEvents(class [System.Runtime]System.Reflection.TypeInfo 'type') cil managed + { + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance class [System.Runtime]System.Reflection.EventInfo[] [System.Runtime]System.Reflection.TypeInfo::GetEvents() + IL_0007: pop + IL_0008: ret + } + + .method public hidebysig static void TestGetNestedTypes(class [System.Runtime]System.Reflection.TypeInfo 'type') cil managed + { + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance class [System.Runtime]System.Type[] [System.Runtime]System.Reflection.TypeInfo::GetNestedTypes() + IL_0007: pop + IL_0008: ret + } + + .method public hidebysig static void TestGetField(class [System.Runtime]System.Reflection.TypeInfo 'type') cil managed + { + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldstr "unknown" + IL_0007: callvirt instance class [System.Runtime]System.Reflection.FieldInfo [System.Runtime]System.Reflection.TypeInfo::GetField(string) + IL_000c: pop + IL_000d: ret + } + + .method public hidebysig static void TestGetProperty(class [System.Runtime]System.Reflection.TypeInfo 'type') cil managed + { + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldstr "unknown" + IL_0007: callvirt instance class [System.Runtime]System.Reflection.PropertyInfo [System.Runtime]System.Reflection.TypeInfo::GetProperty(string) + IL_000c: pop + IL_000d: ret + } + + .method public hidebysig static void TestGetEvent(class [System.Runtime]System.Reflection.TypeInfo 'type') cil managed + { + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldstr "unknown" + IL_0007: callvirt instance class [System.Runtime]System.Reflection.EventInfo [System.Runtime]System.Reflection.TypeInfo::GetEvent(string) + IL_000c: pop + IL_000d: ret + } + +} // end of class Library.TypeInfoCalls \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs index e30b6f1d2c98..d860a1abb268 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; using Mono.Linker.Tests.Cases.Expectations.Assertions; diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs new file mode 100644 index 000000000000..1b82d33abbf8 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs @@ -0,0 +1,889 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Security.Policy; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class GenericParameterWarningLocation + { + public static void Main () + { + TypeInheritance.Test (); + TypeImplementingInterface.Test (); + MethodParametersAndReturn.Test (); + FieldDefinition.Test (); + PropertyDefinition.Test (); + MethodBody.Test (); + } + + class TypeInheritance + { + class BaseWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + class BaseWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + // No warning - annotations applied + class DerivedWithSpecificType : BaseWithPublicMethods { } + + // No warning - annotations match + class DerivedWithMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] TAllMethods> + : BaseWithPublicMethods + { } + + [ExpectedWarning ("IL2091")] + class DerivedWithNoAnnotations + : BaseWithPublicMethods + { } + + [ExpectedWarning ("IL2091")] + class DerivedWithMismatchAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : BaseWithPublicMethods + { } + + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicMethods))] + class DerivedWithOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : BaseWithTwo + { } + + class DerivedWithTwoMatching< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + : BaseWithTwo + { } + + public static void Test () + { + Type t; + t = typeof (DerivedWithSpecificType); + t = typeof (DerivedWithMatchingAnnotation<>); + t = typeof (DerivedWithNoAnnotations<>); + t = typeof (DerivedWithMismatchAnnotation<>); + t = typeof (DerivedWithOneMismatch<>); + t = typeof (DerivedWithTwoMatching<,>); + } + } + + class TypeImplementingInterface + { + interface IWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> { } + + interface IWithPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> { } + + // No warning - annotations applied + class ImplementsWithSpecificType : IWithPublicMethods, IWithPublicFields { } + + // No warning - matching annotations + class ImplementsWithMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] TAll> + : IWithPublicMethods, IWithPublicFields + { } + + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + class ImplementsWithOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + : IWithPublicMethods, IWithPublicFields + { } + + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicMethods))] + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + class ImplementsWithTwoMismatches< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : IWithPublicMethods, IWithPublicFields + { } + + public static void Test () + { + // Instantiate the types + new ImplementsWithSpecificType (); + new ImplementsWithMatchingAnnotation (); + new ImplementsWithOneMismatch (); + new ImplementsWithTwoMismatches (); + + // Also reference the interfaces, otherwise they could be trimmed + Type t; + t = typeof (IWithPublicMethods<>); + t = typeof (IWithPublicFields<>); + } + } + + class MethodParametersAndReturn + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + static void MethodWithSpecificType (TypeWithPublicMethods one, IWithTwo two) { } + + [ExpectedWarning ("IL2091")] + static void MethodWithOneMismatch (TypeWithPublicMethods one) { } + + [ExpectedWarning ("IL2091", nameof (IWithTwo))] + [ExpectedWarning ("IL2091", nameof (TypeWithPublicMethods))] + static void MethodWithTwoMismatches< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + (IWithTwo two, TypeWithPublicMethods one) + { } + + static TypeWithPublicMethods MethodWithSpecificReturnType () => null; + + static TypeWithPublicMethods MethodWithMatchingReturn<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () => null; + + [ExpectedWarning ("IL2091")] + static TypeWithPublicMethods MethodWithOneMismatchReturn () => null; + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static IWithTwo MethodWithTwoMismatchesInReturn< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () => null; + + public static void Test () + { + MethodWithSpecificType (null, null); + MethodWithOneMismatch (null); + MethodWithTwoMismatches (null, null); + + MethodWithSpecificReturnType (); + MethodWithMatchingReturn (); + MethodWithOneMismatchReturn (); + MethodWithTwoMismatchesInReturn (); + } + } + + class FieldDefinition + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + class SpecificType + { + static TypeWithPublicMethods _field; + + public static void Test () + { + _field = null; + } + } + + class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + static TypeWithPublicMethods _field; + + public static void Test () + { + _field = null; + } + } + + class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown> + { + [ExpectedWarning ("IL2091")] + static TypeWithPublicMethods _field1; + static TypeWithPublicMethods _field2; + [ExpectedWarning ("IL2091")] + static TypeWithPublicMethods _field3; + + public static void Test () + { + _field1 = null; + _field2 = null; + _field3 = null; + } + } + + class TwoMismatchesInOne< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static IWithTwo _field; + + public static void Test () + { + _field = null; + } + } + + public static void Test () + { + SpecificType.Test (); + OneMatchingAnnotation.Test (); + MultipleReferencesToTheSameType.Test (); + TwoMismatchesInOne.Test (); + } + } + + class PropertyDefinition + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + class SpecificType + { + static TypeWithPublicMethods Property { get; set; } + + public static void Test () + { + Property = null; + } + } + + class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + static TypeWithPublicMethods Property { get; set; } + + public static void Test () + { + Property = null; + } + } + + class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown> + { + // The warning is generated on the backing field + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true)] + static TypeWithPublicMethods Property1 { + [ExpectedWarning ("IL2091")] + get; + + [ExpectedWarning ("IL2091")] + set; + } + + static TypeWithPublicMethods Property2 { + get; + set; + } + + // The warning is generated on the backing field + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true)] + static TypeWithPublicMethods Property3 { + [ExpectedWarning ("IL2091")] + get; + + [ExpectedWarning ("IL2091")] + set; + } + + public static void Test () + { + Property1 = Property1; + Property2 = Property2; + Property3 = Property3; + } + } + + class TwoMismatchesInOne< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + // The warnings are generated on the backing field + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true)] + static IWithTwo Property { + // Getter is trimmed and doesn't produce any warning + get; + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + set; + } + + public static void Test () + { + Property = null; + } + } + + public static void Test () + { + SpecificType.Test (); + OneMatchingAnnotation.Test (); + MultipleReferencesToTheSameType.Test (); + TwoMismatchesInOne.Test (); + } + } + + class MethodBody + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> : Exception + { + public static void Method () { } + } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + public static void Method () { } + } + + class TypeWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> : Exception + { } + + static void MethodWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () { } + + static void MethodWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> () + { } + + class TypeOf + { + static void SpecificType () + { + Type t = typeof (TypeWithPublicMethods); + t = typeof (IWithTwo); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Type t = typeof (TypeWithPublicMethods); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Type t = typeof (TypeWithPublicMethods); // Warn + t = typeof (TypeWithPublicMethods); // No warn + t = typeof (TypeWithPublicMethods); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Type t = typeof (IWithTwo); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + class MethodCallOnGenericMethod + { + static void SpecificType () + { + MethodWithPublicMethods (); + MethodWithTwo (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + MethodWithPublicMethods (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + MethodWithPublicMethods (); // Warn + MethodWithPublicMethods (); // No warn + MethodWithPublicMethods (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + MethodWithTwo (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class MethodCallOnGenericType + { + static void SpecificType () + { + TypeWithPublicMethods.Method (); + IWithTwo.Method (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + TypeWithPublicMethods.Method (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + TypeWithPublicMethods.Method (); // Warn + TypeWithPublicMethods.Method (); // No warn + TypeWithPublicMethods.Method (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + IWithTwo.Method (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class LocalVariable + { + static void SpecificType () + { + TypeWithPublicMethods t = null; + IWithTwo i = null; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + TypeWithPublicMethods t = null; + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + TypeWithPublicMethods t1 = null; // Warn + TypeWithPublicMethods t2 = null; // No warn + TypeWithPublicMethods t3 = null; // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + IWithTwo i = null; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + class DelegateUsageOnGenericMethod + { + static void SpecificType () + { + var a = new Action (MethodWithPublicMethods); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + var a = new Action (MethodWithPublicMethods); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + var a1 = new Action (MethodWithPublicMethods); // Warn + var a2 = new Action (MethodWithPublicMethods); // No warn + var a3 = new Action (MethodWithPublicMethods); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + var a = new Action (MethodWithTwo); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class DelegateUsageOnGenericType + { + static void SpecificType () + { + var a = new Action (TypeWithPublicMethods.Method); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + var a = new Action (TypeWithPublicMethods.Method); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + var a1 = new Action (TypeWithPublicMethods.Method); // Warn + var a2 = new Action (TypeWithPublicMethods.Method); // No warn + var a3 = new Action (TypeWithPublicMethods.Method); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + var a = new Action (IWithTwo.Method); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class CreateInstance + { + static void SpecificType () + { + object a = new TypeWithPublicMethods (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + object a = new TypeWithPublicMethods (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + object a1 = new TypeWithPublicMethods (); // Warn + object a2 = new TypeWithPublicMethods (); // No warn + object a3 = new TypeWithPublicMethods (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + object a = new TypeWithTwo (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class IsInstance + { + static object _value = null; + + static void SpecificType () + { + bool a = _value is TypeWithPublicMethods; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + bool a = _value is TypeWithPublicMethods; + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + bool a1 = _value is TypeWithPublicMethods; // Warn + bool a2 = _value is TypeWithPublicMethods; // No warn + bool a3 = _value is TypeWithPublicMethods; // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + bool a = _value is TypeWithTwo; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class AsType + { + static object _value = null; + + static void SpecificType () + { + object a = _value as TypeWithPublicMethods; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + object a = _value as TypeWithPublicMethods; + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + object a1 = _value as TypeWithPublicMethods; // Warn + object a2 = _value as TypeWithPublicMethods; // No warn + object a3 = _value as TypeWithPublicMethods; // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + object a = _value as TypeWithTwo; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class ExceptionCatch + { + static void SpecificType () + { + try { + DoNothing (); + } catch (TypeWithPublicMethods) { + } + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + try { + DoNothing (); + } catch (TypeWithPublicMethods) { + } + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + try { + DoNothing (); + } catch (TypeWithPublicMethods) { // Warn + } + + try { + DoNothing (); + } catch (TypeWithPublicMethods) { // No warn + } + + try { + DoNothing (); + } catch (TypeWithPublicMethods) { // Warn + } + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + try { + DoNothing (); + } catch (TypeWithTwo) { // Warn x2 + } + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + class ExceptionFilter + { + static void SpecificType () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { + } + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { + } + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { // Warn + } + + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { // No warn + } + + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { // Warn + } + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithTwo) { // Warn x2 + } + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + public static void Test () + { + TypeOf.Test (); + MethodCallOnGenericMethod.Test (); + MethodCallOnGenericType.Test (); + LocalVariable.Test (); + DelegateUsageOnGenericMethod.Test (); + DelegateUsageOnGenericType.Test (); + CreateInstance.Test (); + IsInstance.Test (); + AsType.Test (); + ExceptionCatch.Test (); + ExceptionFilter.Test (); + } + } + + class TestType { } + + static void DoNothing () { } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/TypeInfoIntrinsics.cs b/test/Mono.Linker.Tests.Cases/DataFlow/TypeInfoIntrinsics.cs new file mode 100644 index 000000000000..ecbe71849031 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DataFlow/TypeInfoIntrinsics.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + // Note: this test's goal is to validate that the product correctly reports unrecognized patterns + // - so the main validation is done by the ExpectedWarning attributes. + [SkipKeptItemsValidation] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/TypeInfoCalls.il" })] + + [LogContains ("IL2070: Library.TypeInfoCalls.TestGetConstructors(TypeInfo)")] + [LogContains ("IL2070: Library.TypeInfoCalls.TestGetMethods(TypeInfo)")] + [LogContains ("IL2070: Library.TypeInfoCalls.TestGetFields(TypeInfo)")] + [LogContains ("IL2070: Library.TypeInfoCalls.TestGetProperties(TypeInfo)")] + [LogContains ("IL2070: Library.TypeInfoCalls.TestGetEvents(TypeInfo)")] + [LogContains ("IL2070: Library.TypeInfoCalls.TestGetNestedTypes(TypeInfo)")] + [LogContains ("IL2070: Library.TypeInfoCalls.TestGetField(TypeInfo)")] + [LogContains ("IL2070: Library.TypeInfoCalls.TestGetProperty(TypeInfo)")] + [LogContains ("IL2070: Library.TypeInfoCalls.TestGetEvent(TypeInfo)")] + public class TypeInfoIntrinsics + { + public static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Library.TypeInfoCalls.TestGetConstructors(typeof(string).GetTypeInfo()); + Library.TypeInfoCalls.TestGetMethods(typeof(string).GetTypeInfo()); + Library.TypeInfoCalls.TestGetFields(typeof(string).GetTypeInfo()); + Library.TypeInfoCalls.TestGetProperties(typeof(string).GetTypeInfo()); + Library.TypeInfoCalls.TestGetEvents(typeof(string).GetTypeInfo()); + Library.TypeInfoCalls.TestGetNestedTypes(typeof(string).GetTypeInfo()); + Library.TypeInfoCalls.TestGetField(typeof(string).GetTypeInfo()); + Library.TypeInfoCalls.TestGetProperty(typeof(string).GetTypeInfo()); + Library.TypeInfoCalls.TestGetEvent(typeof(string).GetTypeInfo()); +#endif + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs b/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs index ced70e08f120..1be306593c0b 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs @@ -49,6 +49,7 @@ public static void Main () RequirePublicMethods (typeof (ImplementationOfTwoInterfacesWithOneMethod)); StaticInterfaceMethods.Test (); BaseInPreservedScope.Test (); + DirectCall.Test (); } static void RequirePublicMethods ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) @@ -562,7 +563,6 @@ public void RequiresUnreferencedCodeInterfaceBaseWithImplementationWith_ () { } public void RequiresUnreferencedCodeInterfaceBaseWithoutImplementationWith_ () { } } - interface IBaseImplementedInterface { Type ReturnValueBaseWithInterfaceWithout (); @@ -629,22 +629,27 @@ static virtual Type VirtualMethod class ImplIDamOnAllMissing : IDamOnAll { - [ExpectedWarning ("IL2092")] - [ExpectedWarning ("IL2093")] - [ExpectedWarning ("IL2095")] + // NativeAOT doesn't validate overrides when accessed through reflection because it's a direct call (non-virtual) + // So it doesn't matter that the annotations are not in-sync since the access will validate + // the annotations on the implementation method - it doesn't even see the base method in this case. + [ExpectedWarning ("IL2092", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2093", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2095", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] public static Type AbstractMethod (Type type) => null; - [ExpectedWarning ("IL2092")] - [ExpectedWarning ("IL2093")] - [ExpectedWarning ("IL2095")] + // NativeAOT doesn't validate overrides when accessed through reflection because it's a direct call (non-virtual) + [ExpectedWarning ("IL2092", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2093", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2095", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] public static Type VirtualMethod (Type type) => null; } class ImplIDamOnAllMismatch : IDamOnAll { - [ExpectedWarning ("IL2092")] - [ExpectedWarning ("IL2093")] - [ExpectedWarning ("IL2095")] + // NativeAOT doesn't validate overrides when accessed through reflection because it's a direct call (non-virtual) + [ExpectedWarning ("IL2092", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2093", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2095", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] public static Type AbstractMethod <[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] @@ -653,9 +658,10 @@ public static Type AbstractMethod Type type) { return null; } - [ExpectedWarning ("IL2092")] - [ExpectedWarning ("IL2093")] - [ExpectedWarning ("IL2095")] + // NativeAOT doesn't validate overrides when accessed through reflection because it's a direct call (non-virtual) + [ExpectedWarning ("IL2092", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2093", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2095", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] public static Type VirtualMethod <[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] @@ -698,9 +704,10 @@ class ImplIDamOnNoneMatch : IDamOnNone class ImplIDamOnNoneMismatch : IDamOnNone { - [ExpectedWarning ("IL2092")] - [ExpectedWarning ("IL2093")] - [ExpectedWarning ("IL2095")] + // NativeAOT doesn't validate overrides when accessed through reflection because it's a direct call (non-virtual) + [ExpectedWarning ("IL2092", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2093", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2095", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] public static Type AbstractMethod <[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] @@ -709,9 +716,10 @@ public static Type AbstractMethod Type type) { return null; } - [ExpectedWarning ("IL2092")] - [ExpectedWarning ("IL2093")] - [ExpectedWarning ("IL2095")] + // NativeAOT doesn't validate overrides when accessed through reflection because it's a direct call (non-virtual) + [ExpectedWarning ("IL2092", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2093", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2095", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] public static Type VirtualMethod <[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] @@ -739,7 +747,8 @@ class BaseInPreservedScope { class ImplIAnnotatedMethodsMismatch : Library.IAnnotatedMethods { - [ExpectedWarning ("IL2095")] + // NativeAOT doesn't always validate static overrides when accessed through reflection because it's a direct call (non-virtual) + [ExpectedWarning ("IL2095", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] public static void GenericWithMethodsStatic () { } [ExpectedWarning ("IL2092")] @@ -760,7 +769,8 @@ public void ParamWithMethods (Type t) { } class ImplIUnannotatedMethodsMismatch : Library.IUnannotatedMethods { - [ExpectedWarning ("IL2095")] + // NativeAOT doesn't always validate static overrides when accessed through reflection because it's a direct call (non-virtual) + [ExpectedWarning ("IL2095", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] public static void GenericStatic<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () { } [ExpectedWarning ("IL2092")] @@ -859,6 +869,16 @@ public override void Param (Type t) { } public static void Test () { + // https://github.com/dotnet/linker/issues/3133 + // Access the interfaces as well - otherwise NativeAOT can decide + // to not look for overrides since it knows it's making a direct access + // to a method and it doesn't need to know about the base method + // which leads to some warnings not being generated. + // The goal of this test is to validate the generated diagnostics + // so we're forcing the checks to happen with this. + typeof (Library.IAnnotatedMethods).RequiresAll (); + typeof (Library.IUnannotatedMethods).RequiresAll (); + typeof (ImplIUnannotatedMethodsMismatch).RequiresPublicMethods (); typeof (ImplIAnnotatedMethodsMismatch).RequiresPublicMethods (); typeof (DerivedFromAnnotatedMismatch).RequiresPublicMethods (); @@ -869,6 +889,136 @@ public static void Test () typeof (DerivedFromUnannotatedMatch).RequiresPublicMethods (); } } + + // This is mostly for Native AOT - in that compiler it matters how a method + // is referenced as it will take a different code path to do some of these validations + // The above tests all rely on reflection marking so this test also uses direct calls + class DirectCall + { + abstract class Base + { + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public abstract Type NonGenericAbstract ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type); + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public virtual Type NonGenericVirtual ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) => type; + + public abstract void GenericAbstract<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> (); + + public virtual void GenericVirtual<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () { } + + public abstract Type UnannotatedAbstract (Type type); + + public abstract void UnannotatedGenericAbstract (); + } + + class Derived : Base + { + [ExpectedWarning ("IL2092")] + [ExpectedWarning ("IL2093")] + public override Type NonGenericAbstract ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] Type type) => null; + + [ExpectedWarning ("IL2092")] + [ExpectedWarning ("IL2093")] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] + public override Type NonGenericVirtual (Type type) => null; + + [ExpectedWarning ("IL2095")] + public override void GenericAbstract () { } + + [ExpectedWarning ("IL2095")] + public override void GenericVirtual<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T> () { } + + [ExpectedWarning ("IL2092")] + [ExpectedWarning ("IL2093")] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public override Type UnannotatedAbstract ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] Type type) => null; + + [ExpectedWarning ("IL2095")] + public override void UnannotatedGenericAbstract<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T> () { } + } + + interface IBaseWithDefault + { + void DefaultMethod (Type type); + } + + interface IDerivedWithDefault : IBaseWithDefault + { + [ExpectedWarning ("IL2092")] + [ExpectedWarning ("IL2092", ProducedBy = ProducedBy.Analyzer)] // https://github.com/dotnet/linker/issues/3121 + void IBaseWithDefault.DefaultMethod ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) { } + } + + class ImplDerivedWithDefault : IDerivedWithDefault + { + } + + interface IGvmBase + { + Type UnannotatedGvm (Type type); + Type UnannotatedGvmCalledThroughBase (Type type); + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static abstract Type AnnotatedStaticGvm<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T> ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] Type type); + + static virtual Type UnannotatedStaticGvm (Type type) => null; + } + + class ImplIGvmBase : IGvmBase + { + // NativeAOT doesn't validate overrides when it can resolve them as direct calls + [ExpectedWarning ("IL2092", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2093", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2095", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type UnannotatedGvm<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) => null; + + [ExpectedWarning ("IL2092")] + [ExpectedWarning ("IL2093")] + [ExpectedWarning ("IL2095")] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type UnannotatedGvmCalledThroughBase<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) => null; + + [ExpectedWarning ("IL2092")] + [ExpectedWarning ("IL2093")] + [ExpectedWarning ("IL2095")] + public static Type AnnotatedStaticGvm (Type type) => null; + + [ExpectedWarning ("IL2092")] + [ExpectedWarning ("IL2093")] + [ExpectedWarning ("IL2095")] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type UnannotatedStaticGvm<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T> ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] Type type) => null; + } + + static void CallStaticGvm () where TGvmBase : IGvmBase + { + TGvmBase.AnnotatedStaticGvm (typeof (string)); + TGvmBase.UnannotatedStaticGvm (typeof (string)); + } + + public static void Test () + { + Base instance = new Derived (); + instance.NonGenericAbstract (typeof (string)); + instance.NonGenericVirtual (typeof (string)); + instance.GenericAbstract (); + instance.GenericVirtual (); + instance.UnannotatedAbstract (typeof (string)); + instance.UnannotatedGenericAbstract (); + + ((IBaseWithDefault) (new ImplDerivedWithDefault ())).DefaultMethod (typeof (string)); + + ImplIGvmBase impl = new ImplIGvmBase (); + impl.UnannotatedGvm (typeof (string)); + + IGvmBase ibase = (IGvmBase) impl; + ibase.UnannotatedGvmCalledThroughBase (typeof (string)); + + CallStaticGvm (); + } + } } } diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/BaseProvidesInterfaceMethodCircularReference.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/BaseProvidesInterfaceMethodCircularReference.cs new file mode 100644 index 000000000000..fd37de182df6 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/BaseProvidesInterfaceMethodCircularReference.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.Inheritance.Interfaces.BaseProvidesInterfaceEdgeCase.Dependencies; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.BaseProvidesInterfaceEdgeCase +{ + /// + /// Reproduces the issue found in https://github.com/dotnet/linker/issues/3112. + /// derives from and uses 's method to implement , + /// creating a psuedo-circular assembly reference (but not quite since doesn't implement IFoo itself). + /// In the linker, IsMethodNeededByInstantiatedTypeDueToPreservedScope would iterate through 's method's base methods, + /// and in the process would trigger the assembly of to be processed. Since that assembly also has that + /// inherits from and implements using 's methods, the linker adds + /// 's method as a base to 's method, which modifies the collection as it's being iterated, causing an exception. + /// + [SetupCompileBefore ("base.dll", new[] { "Dependencies/Base.cs" })] // Base Implements IFoo.Method (psuedo-reference to ifoo.dll) + [SetupCompileBefore ("ifoo.dll", new[] { "Dependencies/IFoo.cs" }, references: new[] { "base.dll" })] // Derived2 references Base from base.dll (circular reference) + [SetupCompileBefore ("derived1.dll", new[] { "Dependencies/Derived1.cs" }, references: new[] { "ifoo.dll", "base.dll" })] + [KeptMemberInAssembly ("base.dll", typeof (Base), "Method()")] + [RemovedMemberInAssembly ("ifoo", "Derived2")] + public class BaseProvidesInterfaceMethodCircularReference + { + [Kept] + public static void Main () + { + _ = new Derived1 (); + Foo (); + } + + [Kept] + public static void Foo () + { + ((IFoo) null).Method (); + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/Dependencies/Base.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/Dependencies/Base.cs new file mode 100644 index 000000000000..14528a26502f --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/Dependencies/Base.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.BaseProvidesInterfaceEdgeCase.Dependencies +{ + public class Base + { + public virtual void Method () + { + throw new NotImplementedException (); + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/Dependencies/Derived1.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/Dependencies/Derived1.cs new file mode 100644 index 000000000000..5cac5a8953d8 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/Dependencies/Derived1.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.BaseProvidesInterfaceEdgeCase.Dependencies +{ + public class Derived1 : Base, IFoo + { + } +} diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/Dependencies/IFoo.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/Dependencies/IFoo.cs new file mode 100644 index 000000000000..e095df38aa4b --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/BaseProvidesInterfaceEdgeCase/Dependencies/IFoo.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.BaseProvidesInterfaceEdgeCase.Dependencies +{ + public interface IFoo + { + void Method (); + } + public interface IBar + { + void Method (); + } + public class Derived2 : Base, IBar + { + } +} diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticInterfaceMethodsInPreservedScope.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticInterfaceMethodsInPreservedScope.cs index b57d58c395f3..362413c3f971 100644 --- a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticInterfaceMethodsInPreservedScope.cs +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticInterfaceMethodsInPreservedScope.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; @@ -15,7 +15,6 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods { [SetupCompileBefore ("library.dll", new[] { "Dependencies/Library.cs" })] [SetupLinkerAction ("skip", "library")] - [SetupLinkerArgument ("-a", "test.exe")] public static class StaticInterfaceMethodsInPreservedScope { [Kept] diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticVirtualInterfaceMethodsInPreservedScope.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticVirtualInterfaceMethodsInPreservedScope.cs index 3dac2feb789f..26a5a2cc0661 100644 --- a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticVirtualInterfaceMethodsInPreservedScope.cs +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticVirtualInterfaceMethodsInPreservedScope.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; @@ -15,7 +15,6 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods { [SetupCompileBefore ("library.dll", new[] { "Dependencies/Library.cs" })] [SetupLinkerAction ("skip", "library")] - [SetupLinkerArgument ("-a", "test.exe")] public static class StaticVirtualInterfaceMethodsInPreservedScope { [Kept] diff --git a/test/Mono.Linker.Tests.Cases/Libraries/DefaultLibraryLinkBehavior.cs b/test/Mono.Linker.Tests.Cases/Libraries/DefaultLibraryLinkBehavior.cs index 19ae1ea1cf80..fd0c55781622 100644 --- a/test/Mono.Linker.Tests.Cases/Libraries/DefaultLibraryLinkBehavior.cs +++ b/test/Mono.Linker.Tests.Cases/Libraries/DefaultLibraryLinkBehavior.cs @@ -1,9 +1,10 @@ -using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.Cases.Libraries { [SetupCompileAsLibrary] + [SetupLinkerArgument ("-a", "test.dll")] [Kept] [KeptMember (".ctor()")] public class DefaultLibraryLinkBehavior diff --git a/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs b/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs index d3f7cdf333c7..43d19473d136 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs @@ -534,6 +534,7 @@ public NestedRUCType () { } void RUCMethod () { } [Kept] + [ExpectedWarning ("IL2112", "--RUC on NestedRUCType--")] void Method () { } [Kept] @@ -590,6 +591,7 @@ public AnnotatedRUCPublicMethods () { } public void RUCMethod () { } [Kept] + [ExpectedWarning ("IL2112", "--AnnotatedRUCPublicMethods--")] public void Method () { } [Kept] diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/BasicRequires.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/BasicRequires.cs index b89dfc61e8cf..25275f426c56 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/BasicRequires.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/BasicRequires.cs @@ -188,12 +188,9 @@ class GenericWithStaticMethod public static void GenericTypeWithStaticMethodWhichRequires () { } } - // NativeAOT doesnt produce Requires warnings in Generics https://github.com/dotnet/runtime/issues/68688 - // [ExpectedWarning("IL2026", "--GenericTypeWithStaticMethodWhichRequires--"] - [ExpectedWarning ("IL2026", "--GenericTypeWithStaticMethodWhichRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.Trimmer)] - // [ExpectedWarning("IL3002", "--GenericTypeWithStaticMethodWhichRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--GenericTypeWithStaticMethodWhichRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--GenericTypeWithStaticMethodWhichRequires--", ProducedBy = ProducedBy.Analyzer)] - // [ExpectedWarning("IL3050", "--GenericTypeWithStaticMethodWhichRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] [ExpectedWarning ("IL3050", "--GenericTypeWithStaticMethodWhichRequires--", ProducedBy = ProducedBy.Analyzer)] public static void GenericTypeWithStaticMethodViaLdftn () { diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs index bd46a6a664e5..f77d37382551 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.Cases.RequiresCapability { @@ -48,8 +49,8 @@ public static void Main () class WarnInIteratorBody { [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] static IEnumerable TestCallBeforeYieldReturn () { MethodWithRequires (); @@ -57,8 +58,8 @@ static IEnumerable TestCallBeforeYieldReturn () } [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] static IEnumerable TestCallAfterYieldReturn () { yield return 0; @@ -75,7 +76,13 @@ static IEnumerable TestReflectionAccess () yield return 1; } - [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] +#if !RELEASE + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer)] +#else + // In release mode, the compiler optimizes away the unused Action (and reference to MethodWithRequires) +#endif + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] static IEnumerable TestLdftn () @@ -86,7 +93,8 @@ static IEnumerable TestLdftn () } // Cannot annotate fields either with RUC nor RAF therefore the warning persists - [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -153,7 +161,8 @@ static IEnumerable TestLdftn () } // Cannot annotate fields either with RUC nor RAF therefore the warning persists - [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -184,6 +193,8 @@ static IEnumerable TestMethodParameterWithRequirements (Type unknownType = yield return 0; } + // https://github.com/dotnet/runtime/issues/68688 + // This test passes on NativeAot even without the Requires* attributes. [RequiresUnreferencedCode ("Suppress in body")] [RequiresAssemblyFiles ("Suppress in body")] [RequiresDynamicCode ("Suppress in body")] @@ -193,6 +204,8 @@ static IEnumerable TestGenericMethodParameterRequirement () yield return 0; } + // https://github.com/dotnet/runtime/issues/68688 + // This test passes on NativeAot even without the Requires* attributes. [RequiresUnreferencedCode ("Suppress in body")] [RequiresAssemblyFiles ("Suppress in body")] [RequiresDynamicCode ("Suppress in body")] @@ -221,8 +234,8 @@ public static void Test () class WarnInAsyncBody { [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] static async void TestCallBeforeYieldReturn () { MethodWithRequires (); @@ -230,8 +243,8 @@ static async void TestCallBeforeYieldReturn () } [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] static async void TestCallAfterYieldReturn () { await MethodAsync (); @@ -248,7 +261,11 @@ static async void TestReflectionAccess () await MethodAsync (); } - [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] +#if !RELEASE + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer)] +#endif + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] static async void TestLdftn () @@ -257,7 +274,8 @@ static async void TestLdftn () var action = new Action (MethodWithRequires); } - [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -321,7 +339,8 @@ static async void TestLdftn () } // Cannot annotate fields either with RUC nor RAF therefore the warning persists - [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -350,6 +369,8 @@ static async void TestMethodParameterWithRequirements (Type unknownType = null) await MethodAsync (); } + // https://github.com/dotnet/runtime/issues/68688 + // This test passes on NativeAot even without the Requires* attributes. [RequiresUnreferencedCode ("Suppress in body")] [RequiresAssemblyFiles ("Suppress in body")] [RequiresDynamicCode ("Suppress in body")] @@ -359,6 +380,8 @@ static async void TestGenericMethodParameterRequirement () await MethodAsync (); } + // https://github.com/dotnet/runtime/issues/68688 + // This test passes on NativeAot even without the Requires* attributes. [RequiresUnreferencedCode ("Suppress in body")] [RequiresAssemblyFiles ("Suppress in body")] [RequiresDynamicCode ("Suppress in body")] @@ -387,8 +410,8 @@ public static void Test () class WarnInAsyncIteratorBody { [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] static async IAsyncEnumerable TestCallBeforeYieldReturn () { await MethodAsync (); @@ -397,8 +420,8 @@ static async IAsyncEnumerable TestCallBeforeYieldReturn () } [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] static async IAsyncEnumerable TestCallAfterYieldReturn () { yield return 0; @@ -418,7 +441,11 @@ static async IAsyncEnumerable TestReflectionAccess () yield return 1; } - [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] +#if !RELEASE + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Trimmer, CompilerGeneratedCode = true)] +#endif + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] static async IAsyncEnumerable TestLdftn () @@ -428,7 +455,8 @@ static async IAsyncEnumerable TestLdftn () var action = new Action (MethodWithRequires); } - [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -498,7 +526,8 @@ static async IAsyncEnumerable TestLdftn () } // Cannot annotate fields either with RUC nor RAF therefore the warning persists - [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -530,6 +559,8 @@ static async IAsyncEnumerable TestMethodParameterWithRequirements (Type unk yield return 0; } + // https://github.com/dotnet/runtime/issues/68688 + // This test passes on NativeAot even without the Requires* attributes. [RequiresUnreferencedCode ("Suppress in body")] [RequiresAssemblyFiles ("Suppress in body")] [RequiresDynamicCode ("Suppress in body")] @@ -540,6 +571,8 @@ static async IAsyncEnumerable TestGenericMethodParameterRequirement MethodWithRequires (); + } + + [ExpectedWarning ("IL2026", "--LocalFunctionWithRequires--")] + [ExpectedWarning ("IL3002", "--LocalFunctionWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--LocalFunctionWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + static void TestLocalFunctionWithRequires () + { + LocalFunction (); + + [RequiresUnreferencedCode ("--LocalFunctionWithRequires--")] + [RequiresAssemblyFiles ("--LocalFunctionWithRequires--")] + [RequiresDynamicCode ("--LocalFunctionWithRequires--")] void LocalFunction () => MethodWithRequires (); } @@ -592,8 +638,8 @@ static void TestCallWithClosure (int p = 0) LocalFunction (); [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] void LocalFunction () { p++; @@ -628,7 +674,11 @@ static void TestLdftn () { LocalFunction (); - [ExpectedWarning ("IL2026", "--MethodWithRequires--")] +#if !RELEASE + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Trimmer)] +#endif + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] void LocalFunction () @@ -637,7 +687,8 @@ void LocalFunction () } } - [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -663,6 +714,7 @@ static void TestDynamicallyAccessedMethod () public static void Test () { TestCall (); + TestLocalFunctionWithRequires (); TestCallUnused (); TestCallWithClosure (); TestCallWithClosureUnused (); @@ -739,7 +791,8 @@ void LocalFunction () } } - [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "Message from --MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "Message from --MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -871,6 +924,7 @@ public static void TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction class DynamicallyAccessedLocalFunctionUnusedShouldWarn { + // https://github.com/dotnet/runtime/issues/68786 [ExpectedWarning ("IL2118", nameof (TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction), "LocalFunction", ProducedBy = ProducedBy.Trimmer)] public static void TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction () { @@ -906,8 +960,8 @@ public static void TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction } [ExpectedWarning ("IL2026")] - [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] static void TestSuppressionOnLocalFunction () { LocalFunction (); // This will produce a warning since the local function has Requires on it @@ -923,8 +977,8 @@ void LocalFunction (Type unknownType = null) } [ExpectedWarning ("IL2026")] - [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] static void TestSuppressionOnLocalFunctionWithAssignment () { LocalFunction (); // This will produce a warning since the local function has Requires on it @@ -943,8 +997,8 @@ void LocalFunction (Type unknownType = null) static Type typeWithNonPublicMethods; [ExpectedWarning ("IL2026")] - [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] static void TestSuppressionOnLocalFunctionWithNestedLocalFunction () { LocalFunction (); // This will produce a warning since the local function has Requires on it @@ -958,7 +1012,9 @@ void LocalFunction () // The linker doesn't have enough information to associate the Requires on LocalFunction // with this nested local function. - [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.NativeAot)] void NestedLocalFunction () => MethodWithRequires (); } } @@ -983,8 +1039,8 @@ void LocalFunction (Type unknownType = null) class TestSuppressionOnOuterWithSameName { [ExpectedWarning ("IL2026", nameof (Outer) + "()")] - [ExpectedWarning ("IL3002", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] public static void Test () { Outer (); @@ -1006,8 +1062,8 @@ static void Outer (int i) LocalFunction (); [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] void LocalFunction () => MethodWithRequires (); } } @@ -1048,14 +1104,14 @@ static void WarnInNonNestedLocalFunctionTest () LocalFunction (); [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] static void LocalFunction () => MethodWithRequires (); } [ExpectedWarning ("IL2026", "--MethodWithNonNestedLocalFunction--")] - [ExpectedWarning ("IL3002", "--MethodWithNonNestedLocalFunction--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithNonNestedLocalFunction--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithNonNestedLocalFunction--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithNonNestedLocalFunction--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] static void SuppressInNonNestedLocalFunctionTest () { MethodWithNonNestedLocalFunction (); @@ -1077,14 +1133,15 @@ static void TestCall () { Action lambda = [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] () => MethodWithRequires (); lambda (); } - [ExpectedWarning ("IL2026", "--LambdaWithRequires--")] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--LambdaWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--LambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--LambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)] static void TestLambdaWithRequires () @@ -1102,12 +1159,13 @@ static void TestCallUnused () { Action _ = [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] () => MethodWithRequires (); } - [ExpectedWarning ("IL2026", "--LambdaWithRequires--")] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--LambdaWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--LambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--LambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)] static void TestLambdaWithRequiresUnused () @@ -1123,8 +1181,8 @@ static void TestCallWithClosure (int p = 0) { Action lambda = [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] () => { p++; MethodWithRequires (); @@ -1136,7 +1194,12 @@ static void TestCallWithClosure (int p = 0) static void TestCallWithClosureUnused (int p = 0) { Action _ = - [ExpectedWarning ("IL2026", "--MethodWithRequires--")] +#if !RELEASE + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.NativeAot)] +#endif + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] () => { @@ -1159,7 +1222,11 @@ static void TestReflectionAccess () static void TestLdftn () { Action _ = - [ExpectedWarning ("IL2026", "--MethodWithRequires--")] +#if !RELEASE + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Trimmer)] +#endif + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] () => { @@ -1167,7 +1234,8 @@ static void TestLdftn () }; } - [ExpectedWarning ("IL2026", "--MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -1270,7 +1338,8 @@ static void TestLdftn () }; } - [ExpectedWarning ("IL2026", "--MethodWithRequiresAndReturns--", CompilerGeneratedCode = true)] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--MethodWithRequiresAndReturns--", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--MethodWithRequiresAndReturns--", ProducedBy = ProducedBy.Analyzer)] public static Lazy _default = new Lazy (MethodWithRequiresAndReturns); @@ -1302,6 +1371,8 @@ static async void TestMethodParameterWithRequirements (Type unknownType = null) () => unknownType.RequiresNonPublicMethods (); } + // https://github.com/dotnet/runtime/issues/68688 + // This test passes on NativeAot even without the Requires* attributes. [RequiresUnreferencedCode ("Suppress in body")] [RequiresAssemblyFiles ("Suppress in body")] [RequiresDynamicCode ("Suppress in body")] @@ -1313,6 +1384,8 @@ static void TestGenericMethodParameterRequirement () }; } + // https://github.com/dotnet/runtime/issues/68688 + // This test passes on NativeAot even without the Requires* attributes. [RequiresUnreferencedCode ("Suppress in body")] [RequiresAssemblyFiles ("Suppress in body")] [RequiresDynamicCode ("Suppress in body")] @@ -1323,7 +1396,8 @@ static void TestGenericTypeParameterRequirement () }; } - [ExpectedWarning ("IL2026")] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)] static void TestSuppressionOnLambda () @@ -1337,7 +1411,8 @@ static void TestSuppressionOnLambda () lambda (); // This will produce a warning since the lambda has Requires on it } - [ExpectedWarning ("IL2026")] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)] static void TestSuppressionOnLambdaWithNestedLambda () @@ -1352,7 +1427,9 @@ static void TestSuppressionOnLambdaWithNestedLambda () // an IL reference to the generated lambda method, unlike local functions. // However, we don't make this association, for consistency with local functions. var nestedLambda = - [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.NativeAot)] () => MethodWithRequires (); }; @@ -1379,8 +1456,8 @@ static void TestSuppressionOnOuterAndLambda () class TestSuppressionOnOuterWithSameName { [ExpectedWarning ("IL2026", nameof (Outer) + "()")] - [ExpectedWarning ("IL3002", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] public static void Test () { Outer (); @@ -1401,8 +1478,8 @@ static void Outer (int i) { var lambda = [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] () => MethodWithRequires (); lambda (); @@ -1442,8 +1519,8 @@ static async void TestIteratorLocalFunctionInAsync () await MethodAsync (); [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] IEnumerable LocalFunction () { yield return 0; @@ -1452,6 +1529,10 @@ IEnumerable LocalFunction () } } +#if !NATIVEAOT + // BUG: https://github.com/dotnet/runtime/issues/77455 + // NativeAot produces an incorrect warning pointing to the InstantiatedMethod, which isn't associated + // with the calling context. [ExpectedWarning ("IL2026", "--TypeWithMethodWithRequires.MethodWithRequires--", CompilerGeneratedCode = true)] static IEnumerable TestDynamicallyAccessedMethodViaGenericMethodParameterInIterator () { @@ -1465,6 +1546,7 @@ static IEnumerable TestDynamicallyAccessedMethodViaGenericTypeParameterInIt yield return 1; new TypeWithGenericWhichRequiresMethods (); } +#endif static void TestLocalFunctionInIteratorLocalFunction () { @@ -1477,8 +1559,8 @@ IEnumerable IteratorLocalFunction () yield return 1; [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] void LocalFunction () => MethodWithRequires (); } } @@ -1495,16 +1577,18 @@ IEnumerable IteratorLocalFunction () } [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] void LocalFunction () => MethodWithRequires (); } public static void Test () { TestIteratorLocalFunctionInAsync (); +#if !NATIVEAOT TestDynamicallyAccessedMethodViaGenericMethodParameterInIterator (); TestDynamicallyAccessedMethodViaGenericTypeParameterInIterator (); +#endif TestLocalFunctionInIteratorLocalFunction (); TestLocalFunctionCalledFromIteratorLocalFunctionAndMethod (); } @@ -1671,6 +1755,10 @@ IEnumerable LocalFunction () } } +#if !NATIVEAOT + // BUG: https://github.com/dotnet/runtime/issues/77455 + // NativeAot produces an incorrect warning pointing to the InstantiatedMethod, which isn't associated + // with the calling context. [RequiresUnreferencedCode ("Suppress in body")] [RequiresAssemblyFiles ("Suppress in body")] [RequiresDynamicCode ("Suppress in body")] @@ -1679,10 +1767,11 @@ static IEnumerable TestDynamicallyAccessedMethodViaGenericMethodParameterIn MethodWithGenericWhichRequiresMethods (); yield return 0; } +#endif [ExpectedWarning ("IL2026", "--IteratorLocalFunction--")] - [ExpectedWarning ("IL3002", "--IteratorLocalFunction--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--IteratorLocalFunction--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--IteratorLocalFunction--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--IteratorLocalFunction--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] static void TestLocalFunctionInIteratorLocalFunction () { IteratorLocalFunction (); @@ -1698,14 +1787,16 @@ IEnumerable IteratorLocalFunction () yield return 1; // Trimmer doesn't try to associate LocalFunction with IteratorLocalFunction - [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.NativeAot)] void LocalFunction () => MethodWithRequires (); } } [ExpectedWarning ("IL2026", "--IteratorLocalFunction--")] - [ExpectedWarning ("IL3002", "--IteratorLocalFunction--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--IteratorLocalFunction--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--IteratorLocalFunction--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--IteratorLocalFunction--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] static void TestLocalFunctionCalledFromIteratorLocalFunctionAndMethod () { IteratorLocalFunction (); @@ -1723,8 +1814,8 @@ IEnumerable IteratorLocalFunction () } [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] void LocalFunction () => MethodWithRequires (); } @@ -1743,7 +1834,9 @@ public static void Test () TestLambdaInAsyncLambdaWithClosure (); TestIteratorLocalFunctionInAsync (); TestIteratorLocalFunctionInAsyncWithoutInner (); +#if !NATIVEAOT TestDynamicallyAccessedMethodViaGenericMethodParameterInIterator (); +#endif TestLocalFunctionInIteratorLocalFunction (); TestLocalFunctionCalledFromIteratorLocalFunctionAndMethod (); } @@ -1751,18 +1844,18 @@ public static void Test () class StateMachinesOnlyReferencedViaReflection { - [RequiresUnreferencedCode ("Requires to suppress")] - [RequiresAssemblyFiles ("Requires to suppress")] - [RequiresDynamicCode ("Requires to suppress")] + [RequiresUnreferencedCode ("--TestIteratorOnlyReferencedViaReflectionWhichShouldSuppress--")] + [RequiresAssemblyFiles ("--TestIteratorOnlyReferencedViaReflectionWhichShouldSuppress--")] + [RequiresDynamicCode ("--TestIteratorOnlyReferencedViaReflectionWhichShouldSuppress--")] static IEnumerable TestIteratorOnlyReferencedViaReflectionWhichShouldSuppress () { yield return 0; MethodWithRequires (); } - [RequiresUnreferencedCode ("Requires to suppress")] - [RequiresAssemblyFiles ("Requires to suppress")] - [RequiresDynamicCode ("Requires to suppress")] + [RequiresUnreferencedCode ("--TestAsyncOnlyReferencedViaReflectionWhichShouldSuppress--")] + [RequiresAssemblyFiles ("--TestAsyncOnlyReferencedViaReflectionWhichShouldSuppress--")] + [RequiresDynamicCode ("--TestAsyncOnlyReferencedViaReflectionWhichShouldSuppress--")] static async void TestAsyncOnlyReferencedViaReflectionWhichShouldSuppress () { await MethodAsync (); @@ -1770,8 +1863,8 @@ static async void TestAsyncOnlyReferencedViaReflectionWhichShouldSuppress () } [ExpectedWarning ("IL2026", CompilerGeneratedCode = true)] - [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] + [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] static IEnumerable TestIteratorOnlyReferencedViaReflectionWhichShouldWarn () { yield return 0; @@ -1779,21 +1872,28 @@ static IEnumerable TestIteratorOnlyReferencedViaReflectionWhichShouldWarn ( } [ExpectedWarning ("IL2026", CompilerGeneratedCode = true)] - [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] + [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot, CompilerGeneratedCode = true)] static async void TestAsyncOnlyReferencedViaReflectionWhichShouldWarn () { await MethodAsync (); MethodWithRequires (); } - [ExpectedWarning ("IL2026", "Requires to suppress")] - [ExpectedWarning ("IL2026", "Requires to suppress")] + [ExpectedWarning ("IL2026", "--TestIteratorOnlyReferencedViaReflectionWhichShouldSuppress--")] + [ExpectedWarning ("IL2026", "--TestAsyncOnlyReferencedViaReflectionWhichShouldSuppress--")] // Analyzer doesn't emit additional warnings about reflection access to the compiler-generated // state machine members. - [ExpectedWarning ("IL2026", "Requires to suppress", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "Requires to suppress", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "--TestIteratorOnlyReferencedViaReflectionWhichShouldSuppress--", ProducedBy = ProducedBy.Trimmer)] +#if !RELEASE + [ExpectedWarning ("IL2026", "--TestAsyncOnlyReferencedViaReflectionWhichShouldSuppress--", ProducedBy = ProducedBy.Trimmer)] + // In debug mode, the async state machine is a class with a constructor, so a warning is emitted for the constructor. + // The MoveNext method is virtual, so doesn't warn either way. +#else + // In release mode, the async state machine is a struct which doesn't have a constructor, so no warning is emitted. +#endif // Linker warns about reflection access to compiler-generated state machine members. + // https://github.com/dotnet/runtime/issues/68786 [ExpectedWarning ("IL2118", nameof (StateMachinesOnlyReferencedViaReflection), "<" + nameof (TestAsyncOnlyReferencedViaReflectionWhichShouldWarn) + ">", "MoveNext()", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2118", nameof (StateMachinesOnlyReferencedViaReflection), "<" + nameof (TestIteratorOnlyReferencedViaReflectionWhichShouldWarn) + ">", "MoveNext()", @@ -1803,8 +1903,8 @@ static void TestAll () typeof (StateMachinesOnlyReferencedViaReflection).RequiresAll (); } - [ExpectedWarning ("IL2026", "Requires to suppress")] - [ExpectedWarning ("IL2026", "Requires to suppress")] + [ExpectedWarning ("IL2026", "--TestIteratorOnlyReferencedViaReflectionWhichShouldSuppress--")] + [ExpectedWarning ("IL2026", "--TestAsyncOnlyReferencedViaReflectionWhichShouldSuppress--")] // NonPublicMethods doesn't warn for members emitted into compiler-generated state machine types. static void TestNonPublicMethods () { @@ -1821,8 +1921,8 @@ public static void Test () class LocalFunctionsReferencedViaReflection { [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequires--")] - [ExpectedWarning ("IL3002", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] static void TestLocalFunctionWithRequires () { LocalFunction (); @@ -1842,8 +1942,8 @@ static void TestLocalFunctionWithRequiresOnlyAccessedViaReflection () } [ExpectedWarning ("IL2026", "LocalFunction")] - [ExpectedWarning ("IL3002", "LocalFunction", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "LocalFunction", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "LocalFunction", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "LocalFunction", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] static void TestLocalFunctionWithClosureWithRequires (int p = 0) { LocalFunction (); @@ -1899,12 +1999,15 @@ void LocalFunction () // The linker correctly emits warnings about reflection access to local functions with Requires // or which inherit Requires from the containing method. The analyzer doesn't bind to local functions // so does not warn here. - [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequiresOnlyAccessedViaReflection--", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequiresOnlyAccessedViaReflection--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // BUG: https://github.com/dotnet/runtime/issues/68786 + // NativeAot doesn't associate Requires on method with the local functions it contains. [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)] // Linker warns about reflection access to compiler-generated code + // https://github.com/dotnet/runtime/issues/68786 [ExpectedWarning ("IL2118", nameof (LocalFunctionsReferencedViaReflection), nameof (TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection), ProducedBy = ProducedBy.Trimmer)] static void TestAll () @@ -1917,12 +2020,15 @@ static void TestAll () [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--")] [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection--")] // NonPublicMethods warns for local functions not emitted into display classes. - [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequiresOnlyAccessedViaReflection--", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequiresOnlyAccessedViaReflection--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // BUG: https://github.com/dotnet/runtime/issues/68786 + // NativeAot doesn't associate Requires on method with the local functions it contains. [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)] // Linker warns about reflection access to compiler-generated code + // https://github.com/dotnet/runtime/issues/68786 [ExpectedWarning ("IL2118", nameof (LocalFunctionsReferencedViaReflection), "<" + nameof (TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection) + ">", ProducedBy = ProducedBy.Trimmer)] static void TestNonPublicMethods () @@ -1939,7 +2045,8 @@ public static void Test () class LambdasReferencedViaReflection { - [ExpectedWarning ("IL2026", "--TestLambdaWithRequires--")] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "--TestLambdaWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "--TestLambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "--TestLambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)] static void TestLambdaWithRequires () @@ -1953,7 +2060,8 @@ static void TestLambdaWithRequires () lambda (); } - [ExpectedWarning ("IL2026", "Lambda")] + // NativeAot is missing ldftn detection: https://github.com/dotnet/runtime/issues/68786 + [ExpectedWarning ("IL2026", "Lambda", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL3002", "Lambda", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL3050", "Lambda", ProducedBy = ProducedBy.Analyzer)] static void TestLambdaWithClosureWithRequires (int p = 0) @@ -1999,8 +2107,10 @@ static void TestLambdaWithClosureInMethodWithRequires (int p = 0) // The linker correctly emits warnings about reflection access to lambdas with Requires // or which inherit Requires from the containing method. The analyzer doesn't bind to lambdas // so does not warn here. - [ExpectedWarning ("IL2026", "--TestLambdaWithRequires--", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "--TestLambdaWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "--TestLambdaWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2026", "--TestLambdaWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // BUG: https://github.com/dotnet/runtime/issues/68786 + // NativeAot doesn't associate Requires on method with the lambdas it contains. [ExpectedWarning ("IL2026", "--TestLambdaInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2026", "--TestLambdaWithClosureInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)] static void TestAll () @@ -2047,8 +2157,8 @@ static async Task AsyncMethodCallingRequires (Type type) } [ExpectedWarning ("IL2026", "ParentSuppression")] - [ExpectedWarning ("IL3002", "ParentSuppression", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "ParentSuppression", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "ParentSuppression", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "ParentSuppression", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] public static void Test () { AsyncMethodCallingRequires (typeof (object)); @@ -2076,8 +2186,8 @@ static async Task AsyncMethodCallingRequires () } [ExpectedWarning ("IL2026", "ParentSuppression")] - [ExpectedWarning ("IL3002", "ParentSuppression", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "ParentSuppression", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "ParentSuppression", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "ParentSuppression", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] public static void Test () { AsyncMethodCallingRequires (); @@ -2116,8 +2226,8 @@ static async IAsyncEnumerable CreateAsync () } [ExpectedWarning ("IL2026", "ParentSuppression")] - [ExpectedWarning ("IL3002", "ParentSuppression", ProducedBy = ProducedBy.Analyzer)] - [ExpectedWarning ("IL3050", "ParentSuppression", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3002", "ParentSuppression", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL3050", "ParentSuppression", ProducedBy = ProducedBy.Analyzer | ProducedBy.NativeAot)] public static void Test () { AsyncEnumMethodCallingRequires (); diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCodeRelease.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCodeRelease.cs new file mode 100644 index 000000000000..a5f79c8c08f2 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCodeRelease.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.RequiresCapability +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + [SetupCompileArgument ("/optimize+")] + [Define ("RELEASE")] + [SetupCompileArgument ("/main:Mono.Linker.Tests.Cases.RequiresCapability.RequiresInCompilerGeneratedCodeRelease")] + [SandboxDependency ("RequiresInCompilerGeneratedCode.cs")] + class RequiresInCompilerGeneratedCodeRelease + { + // This test just links the RequiresIncompilerGeneratedCode test in the Release configuration, to test + // with optimizations enabled for closures and state machine types. + // Sometimes the compiler optimizes away unused references to lambdas. + public static void Main () + { + RequiresInCompilerGeneratedCode.Main (); + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs index 44de82e7f7c9..b2867740f70e 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs @@ -38,6 +38,7 @@ public static void Main () ReflectionAccessOnProperties.Test (); KeepFieldOnAttribute (); AttributeParametersAndProperties.Test (); + MembersOnClassWithRequires.Test (); } [RequiresUnreferencedCode ("Message for --ClassWithRequires--")] @@ -330,8 +331,7 @@ class BaseWithRequiresOnType public virtual void Method () { } } - [RequiresUnreferencedCode ("RUC")] - [RequiresDynamicCode ("RDC")] + [ExpectedWarning ("IL2109", nameof (BaseWithRequiresOnType))] class DerivedWithoutRequiresOnType : BaseWithRequiresOnType { public override void Method () { } @@ -515,11 +515,23 @@ static void TestNameOfDoesntWarn () class ReflectionAccessOnMethod { + [ExpectedWarning ("IL2026", "BaseWithRequiresOnType.Method()")] + [ExpectedWarning ("IL2026", "BaseWithRequiresOnType.Method()")] [ExpectedWarning ("IL2026", "BaseWithoutRequiresOnType.Method()")] [ExpectedWarning ("IL2026", "BaseWithoutRequiresOnType.Method()")] + // Linker skips warnings for base method overrides, assuming it is covered by RUC on the base method. + [ExpectedWarning ("IL2026", "DerivedWithRequiresOnType.Method()", ProducedBy = ProducedBy.Analyzer)] [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method(Int32)")] [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method()")] [ExpectedWarning ("IL2026", "ImplementationWithRequiresOnType.Method()")] + // Linker skips warnings for interface overrides, assuming it is covered by RUC on the interface method. + [ExpectedWarning ("IL2026", "ImplementationWithRequiresOnType.Method(Int32)", ProducedBy = ProducedBy.Analyzer)] + // Linker incorrectly skips warnings for derived method, under the assumption that + // it will be covered by the base method. But in this case the base method + // is unannotated (and the mismatch produces no warning because the derived + // type has RUC). + // https://github.com/dotnet/linker/issues/2533 + [ExpectedWarning ("IL2026", "DerivedWithRequiresOnTypeOverBaseWithNoRequires.Method()", ProducedBy = ProducedBy.Analyzer)] static void TestDAMAccess () { // Warns because BaseWithoutRequiresOnType.Method has Requires on the method @@ -534,20 +546,22 @@ static void TestDAMAccess () // Warns because ImplementationWithRequiresOnType.Method is a static public method on a RUC type typeof (ImplementationWithRequiresOnType).RequiresPublicMethods (); - // Doesn't warn since BaseWithRequiresOnType has no static methods + // Warns for instance method on BaseWithRequiresOnType typeof (BaseWithRequiresOnType).RequiresPublicMethods (); - // Doesn't warn since DerivedWithoutRequiresOnType has no static methods + // Warns for instance method on base type typeof (DerivedWithoutRequiresOnType).RequiresPublicMethods (); // Doesn't warn since the type has no statics typeof (DerivedWithRequiresOnTypeOverBaseWithNoRequires).RequiresPublicMethods (); } + [ExpectedWarning ("IL2026", "BaseWithRequiresOnType.Method()")] [ExpectedWarning ("IL2026", "BaseWithoutRequiresOnType.Method()")] [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method(Int32)")] [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method()")] [ExpectedWarning ("IL2026", "ImplementationWithRequiresOnType.Method()")] + [ExpectedWarning ("IL2026", "ImplementationWithRequiresOnType.Method(Int32)")] static void TestDirectReflectionAccess () { // Requires on the method itself @@ -556,10 +570,10 @@ static void TestDirectReflectionAccess () // Requires on the method itself typeof (InterfaceWithoutRequires).GetMethod (nameof (InterfaceWithoutRequires.Method)); - // Warns because ImplementationWithRequiresOnType.Method is a static public method on a RUC type + // Warns for static and instance methods on ImplementationWithRequiresOnType typeof (ImplementationWithRequiresOnType).GetMethod (nameof (ImplementationWithRequiresOnType.Method)); - // Doesn't warn since Method is not static (so it doesn't matter that the type has RUC) + // Warns for instance Method on RUC type typeof (BaseWithRequiresOnType).GetMethod (nameof (BaseWithRequiresOnType.Method)); } @@ -812,7 +826,7 @@ class WithRequires [RequiresDynamicCode ("--WithRequiresOnlyInstanceProperties--")] class WithRequiresOnlyInstanceProperties { - public int InstnaceProperty { get; set; } + public int InstanceProperty { get; set; } } [ExpectedWarning ("IL2109", "ReflectionAccessOnProperties.DerivedWithoutRequires", "ReflectionAccessOnProperties.WithRequires")] @@ -828,6 +842,12 @@ class DerivedWithRequires : WithRequires public static int DerivedStaticProperty { get; set; } } + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.get")] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.get")] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.get")] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.set")] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.set")] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.set")] [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.get")] [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.get")] [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.get")] @@ -836,6 +856,8 @@ class DerivedWithRequires : WithRequires [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.set")] [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticProperty.get")] [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticProperty.set")] + [ExpectedWarning ("IL2026", "WithRequiresOnlyInstanceProperties.InstanceProperty.get")] + [ExpectedWarning ("IL2026", "WithRequiresOnlyInstanceProperties.InstanceProperty.set")] [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticProperty.get")] [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticProperty.set")] static void TestDAMAccess () @@ -847,22 +869,32 @@ static void TestDAMAccess () typeof (DerivedWithRequires).RequiresPublicProperties (); } + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.get")] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.set")] [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.get")] [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.set")] [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticProperty.get")] [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticProperty.set")] + [ExpectedWarning ("IL2026", "WithRequiresOnlyInstanceProperties.InstanceProperty.get")] + [ExpectedWarning ("IL2026", "WithRequiresOnlyInstanceProperties.InstanceProperty.set")] [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticProperty.get")] [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticProperty.set")] static void TestDirectReflectionAccess () { typeof (WithRequires).GetProperty (nameof (WithRequires.StaticProperty)); - typeof (WithRequires).GetProperty (nameof (WithRequires.InstanceProperty)); // Doesn't warn + typeof (WithRequires).GetProperty (nameof (WithRequires.InstanceProperty)); typeof (WithRequires).GetProperty ("PrivateStaticProperty", BindingFlags.NonPublic); - typeof (WithRequiresOnlyInstanceProperties).GetProperty (nameof (WithRequiresOnlyInstanceProperties.InstnaceProperty)); // Doesn't warn + typeof (WithRequiresOnlyInstanceProperties).GetProperty (nameof (WithRequiresOnlyInstanceProperties.InstanceProperty)); typeof (DerivedWithoutRequires).GetProperty (nameof (DerivedWithRequires.DerivedStaticProperty)); // Doesn't warn typeof (DerivedWithRequires).GetProperty (nameof (DerivedWithRequires.DerivedStaticProperty)); } + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.get", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.get", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.get", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.set", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.set", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "WithRequires.InstanceProperty.set", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.get", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.get", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.get", ProducedBy = ProducedBy.Trimmer)] @@ -987,5 +1019,115 @@ public static void Test () TestClass.Test (); } } + + class RequiresOnCtorAttribute : Attribute + { + [RequiresUnreferencedCode ("--RequiresOnCtorAttribute--")] + public RequiresOnCtorAttribute () + { + } + } + + class MembersOnClassWithRequires + { + public class RequiresAll<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] U> + { + } + + [RequiresUnreferencedCode ("--ClassWithRequires--")] + public class ClassWithRequires + { + public static RequiresAll field; + + // https://github.com/dotnet/linker/issues/3142 + // Instance fields get generic warnings but static fields don't. + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer)] + public RequiresAll instanceField; + + [RequiresOnCtor] + public static int fieldWithAttribute; + + // https://github.com/dotnet/linker/issues/3140 + // Instance fields get attribute warnings but static fields don't. + [ExpectedWarning ("IL2026", "--RequiresOnCtorAttribute--", ProducedBy = ProducedBy.Trimmer)] + [RequiresOnCtor] + public int instanceFieldWithAttribute; + + public static void GenericMethod (RequiresAll r) { } + + public void GenericInstanceMethod (RequiresAll r) { } + + [RequiresOnCtor] + public static void MethodWithAttribute () { } + + [RequiresOnCtor] + public void InstanceMethodWithAttribute () { } + + // NOTE: The enclosing RUC does not apply to nested types. + [ExpectedWarning ("IL2091")] + public class ClassWithWarning : RequiresAll + { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer)] + public ClassWithWarning () + { + } + } + + // NOTE: The enclosing RUC does not apply to nested types. + [ExpectedWarning ("IL2026", "--RequiresOnCtorAttribute--")] + [RequiresOnCtor] + public class ClassWithAttribute + { + } + } + + // This warning should ideally be suppressed by the RUC on the type: + // https://github.com/dotnet/linker/issues/3142 + [ExpectedWarning ("IL2091")] + [RequiresUnreferencedCode ("--GenericClassWithWarningWithRequires--")] + public class GenericClassWithWarningWithRequires : RequiresAll + { + } + + // This warning should ideally be suppressed by the RUC on the type: + // https://github.com/dotnet/linker/issues/3142 + [ExpectedWarning ("IL2091")] + [RequiresUnreferencedCode ("--ClassWithWarningWithRequires--")] + public class ClassWithWarningWithRequires : RequiresAll + { + } + + // https://github.com/dotnet/linker/issues/3142 + [ExpectedWarning ("IL2091")] + [RequiresUnreferencedCode ("--GenericAnnotatedWithWarningWithRequires--")] + public class GenericAnnotatedWithWarningWithRequires<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields> : RequiresAll + { + } + + [ExpectedWarning ("IL2026", "--ClassWithRequires--", "ClassWithRequires.field")] + [ExpectedWarning ("IL2026", "--ClassWithRequires--", "ClassWithRequires.fieldWithAttribute")] + [ExpectedWarning ("IL2026", "--ClassWithRequires--", "ClassWithRequires.GenericMethod")] + [ExpectedWarning ("IL2026", "--ClassWithRequires--", "ClassWithRequires.MethodWithAttribute")] + [ExpectedWarning ("IL2026", "--GenericClassWithWarningWithRequires--")] + [ExpectedWarning ("IL2026", "--ClassWithWarningWithRequires--")] + [ExpectedWarning ("IL2026", "--GenericAnnotatedWithWarningWithRequires--")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer)] + public static void Test (ClassWithRequires inst = null) + { + var f = ClassWithRequires.field; + f = inst.instanceField; + int i = ClassWithRequires.fieldWithAttribute; + i = inst.instanceFieldWithAttribute; + ClassWithRequires.GenericMethod (new ()); + inst.GenericInstanceMethod (new ()); + ClassWithRequires.MethodWithAttribute (); + inst.InstanceMethodWithAttribute (); + var c = new ClassWithRequires.ClassWithWarning (); + var d = new ClassWithRequires.ClassWithAttribute (); + var g = new GenericClassWithWarningWithRequires (); + var h = new ClassWithWarningWithRequires (); + var j = new GenericAnnotatedWithWarningWithRequires (); + } + } } } diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs index db3af83d29c9..f24324292635 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using Mono.Linker.Tests.Cases.Expectations.Assertions; @@ -23,6 +25,8 @@ public static void Main () TestStaticCtorMarkingIsTriggeredByFieldAccessOnExplicitLayout (); TestStaticCtorTriggeredByMethodCall (); TestTypeIsBeforeFieldInit (); + TestStaticCtorOnTypeWithRequires (); + TestRunClassConstructorOnTypeWithRequires (); typeof (StaticCtor).RequiresNonPublicConstructors (); } @@ -44,6 +48,28 @@ static void TestStaticCctorRequires () _ = new StaticCtor (); } + [RequiresUnreferencedCode ("Message for --StaticCtorOnTypeWithRequires--")] + class StaticCtorOnTypeWithRequires + { + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)] + static StaticCtorOnTypeWithRequires () => MethodWithRequires (); + } + + [ExpectedWarning ("IL2026", "Message for --StaticCtorOnTypeWithRequires--")] + static void TestStaticCtorOnTypeWithRequires () + { + var cctor = typeof (StaticCtorOnTypeWithRequires).GetConstructor (BindingFlags.Static | BindingFlags.NonPublic, new Type[0]); + cctor.Invoke (null, null); + } + + [ExpectedWarning ("IL2026", "Message for --StaticCtorOnTypeWithRequires--")] + static void TestRunClassConstructorOnTypeWithRequires () + { + var typeHandle = typeof (StaticCtorOnTypeWithRequires).TypeHandle; + RuntimeHelpers.RunClassConstructor (typeHandle); + } + class StaticCtorTriggeredByFieldAccess { [ExpectedWarning ("IL2116", "StaticCtorTriggeredByFieldAccess..cctor()")] diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBlock/ComplexConditionsOptimized.cs b/test/Mono.Linker.Tests.Cases/UnreachableBlock/ComplexConditionsOptimized.cs index 1161e757644b..951243ab458d 100644 --- a/test/Mono.Linker.Tests.Cases/UnreachableBlock/ComplexConditionsOptimized.cs +++ b/test/Mono.Linker.Tests.Cases/UnreachableBlock/ComplexConditionsOptimized.cs @@ -1,5 +1,6 @@ using System; using System.Reflection.Emit; +using System.Runtime.InteropServices; using System.Runtime.Serialization; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; @@ -17,6 +18,7 @@ public static void Main () TestSwitch.TestOffset (); TestSwitch2.TestFallThrough (); TestSwitchZero.Test (); + TestSwitch3.TestFallThrough (true); } [Kept] @@ -125,6 +127,54 @@ public static void TestFallThrough () static void Unreached () { } } + [Kept] + class TestSwitch3 + { + [Kept] + [ExpectedInstructionSequence (new[] { + "ldc.i4.4", + "stloc.0", + "ldloc.0", + "pop", + "br.s il_6", + "newobj System.Void System.NotSupportedException::.ctor()", + "throw", + })] + public static object TestFallThrough (bool createReader) + { + object instance; + + switch (ProcessArchitecture, createReader) { + case (Architecture.X86, true): + Unreached (); + break; + case (Architecture.X86, false): + Unreached (); + break; + case (Architecture.X64, true): + Unreached (); + break; + case (Architecture.X64, false): + Unreached (); + break; + case (Architecture.Arm64, true): + Unreached (); + break; + case (Architecture.Arm64, false): + Unreached (); + break; + default: + throw new NotSupportedException (); + } + + return null; + } + + static Architecture ProcessArchitecture => Architecture.Wasm; + + static void Unreached () { } + } + [Kept] class TestSwitchZero { diff --git a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 26ad1309b8b1..f5d4ad15a458 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -57,6 +57,10 @@ public void Verify () linkedMembers.Remove ("System.Int32 System.Runtime.CompilerServices.RefSafetyRulesAttribute::Version"); linkedMembers.Remove ("System.Void System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(System.Int32)"); + // Workaround for compiler injected attribute to describe the language version + verifiedGeneratedTypes.Add ("Microsoft.CodeAnalysis.EmbeddedAttribute"); + verifiedGeneratedTypes.Add ("System.Runtime.CompilerServices.RefSafetyRulesAttribute"); + var membersToAssert = originalAssembly.MainModule.Types; foreach (var originalMember in membersToAssert) { if (originalMember is TypeDefinition td) { @@ -99,10 +103,6 @@ protected virtual void VerifyModule (ModuleDefinition original, ModuleDefinition protected virtual void VerifyTypeDefinition (TypeDefinition original, TypeDefinition linked) { - // Workaround for compiler injected attribute to describe the language version - verifiedGeneratedTypes.Add ("Microsoft.CodeAnalysis.EmbeddedAttribute"); - verifiedGeneratedTypes.Add ("System.Runtime.CompilerServices.RefSafetyRulesAttribute"); - if (linked != null && verifiedGeneratedTypes.Contains (linked.FullName)) return; diff --git a/test/Mono.Linker.Tests/TestCasesRunner/LinkerArgumentBuilder.cs b/test/Mono.Linker.Tests/TestCasesRunner/LinkerArgumentBuilder.cs index 43dbcdd21c10..ae0673e9903a 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/LinkerArgumentBuilder.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/LinkerArgumentBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; @@ -61,6 +61,7 @@ public virtual void LinkFromAssembly (string fileName) { Append ("-a"); Append (fileName); + Append ("entrypoint"); } public virtual void LinkFromPublicAndFamily (string fileName) diff --git a/test/Mono.Linker.Tests/Tests/PerfTestGeneratorForCompilerGeneratedCode.cs b/test/Mono.Linker.Tests/Tests/PerfTestGeneratorForCompilerGeneratedCode.cs new file mode 100644 index 000000000000..41f1aa0a798b --- /dev/null +++ b/test/Mono.Linker.Tests/Tests/PerfTestGeneratorForCompilerGeneratedCode.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; +using System.Linq; + +/// +/// This class generates a test that can be used to test perf of analyzing +/// compiler-generated code. Run it by copying this file into a console app and +/// calling . A file +/// will be generated in the current directory named GeneratedLinkerTests.cs. +/// Copy this file into another Console app and trim the app to measure the +/// perf. +/// +static class PerfTestGeneratorForCompilerGeneratedCode +{ + const int FuncNumber = 10000; + public static void Run () + { + using var fstream = File.Create ("GeneratedLinkerTests.cs"); + using var writer = new StreamWriter (fstream); + writer.WriteLine ($$""" +class C { + public static async void Main() + { + int x = 0; + {{string.Join (@" + ", Enumerable.Range (0, FuncNumber).Select (i => $"x += await N{i}.M();"))}} + Console.WriteLine(x); + } +} +"""); + for (int i = 0; i < FuncNumber; i++) { + writer.WriteLine ($$""" +public static class N{{i}} +{ + public static async ValueTask M() + { + Func a = () => 1; + await Task.Delay(0); + return a(); + } +} +"""); + } + } +} \ No newline at end of file