Skip to content

Commit bced7d5

Browse files
committed
Add trimming test project and fix warnings
1 parent 2128408 commit bced7d5

File tree

8 files changed

+358
-10
lines changed

8 files changed

+358
-10
lines changed

src/Microsoft.OpenApi/Any/OpenApiAnyCloneHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class OpenApiAnyCloneHelper
1717
/// <param name="obj">The object instance.</param>
1818
/// <returns>A clone copy or the object itself.</returns>
1919
[Obsolete("Use native AoT-friendly generic overload of CloneFromCopyConstructor instead.")]
20+
[RequiresUnreferencedCode("CloneFromCopyConstructor is not trim-compatible. Recommended to use native AoT-friendly type-specific overloads of CloneFromCopyConstructor instead.")]
2021
public static IOpenApiAny CloneFromCopyConstructor(IOpenApiAny obj)
2122
{
2223
if (obj != null)

src/Microsoft.OpenApi/Attributes/TrimmingAttributes.cs

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,289 @@
33

44
#nullable enable
55

6+
// This collection of attribute definitions are helpers for accessing trim-related attributes in
7+
// projects targeting .NET 6 or lower. Since the trimmer queries for these attributes by name, having
8+
// these attributes source included is sufficient for the trimmer to recognize them. For more information
9+
// on this approach, see https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#approach-2-define-the-attributes-internally.
610
namespace System.Diagnostics.CodeAnalysis
711
{
812
#if !NET7_0_OR_GREATER
13+
/// <summary>
14+
/// Indicates that the specified method requires the ability to generate new code at runtime,
15+
/// for example through <see cref="System.Reflection"/>.
16+
/// </summary>
17+
/// <remarks>
18+
/// This allows tools to understand which methods are unsafe to call when compiling ahead of time.
19+
/// </remarks>
20+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)]
21+
internal sealed class RequiresDynamicCodeAttribute : Attribute
22+
{
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref="RequiresDynamicCodeAttribute"/> class
25+
/// with the specified message.
26+
/// </summary>
27+
/// <param name="message">
28+
/// A message that contains information about the usage of dynamic code.
29+
/// </param>
30+
public RequiresDynamicCodeAttribute(string message)
31+
{
32+
Message = message;
33+
}
34+
35+
/// <summary>
36+
/// Gets a message that contains information about the usage of dynamic code.
37+
/// </summary>
38+
public string Message { get; }
39+
40+
/// <summary>
41+
/// Gets or sets an optional URL that contains more information about the method,
42+
/// why it requires dynamic code, and what options a consumer has to deal with it.
43+
/// </summary>
44+
public string? Url { get; set; }
45+
}
46+
#endif
47+
48+
#if !NET5_0_OR_GREATER
49+
/// <summary>
50+
/// Indicates that the specified method requires dynamic access to code that is not referenced
51+
/// statically, for example through <see cref="System.Reflection"/>.
52+
/// </summary>
53+
/// <remarks>
54+
/// This allows tools to understand which methods are unsafe to call when removing unreferenced
55+
/// code from an application.
56+
/// </remarks>
57+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)]
58+
internal sealed class RequiresUnreferencedCodeAttribute : Attribute
59+
{
60+
/// <summary>
61+
/// Initializes a new instance of the <see cref="RequiresUnreferencedCodeAttribute"/> class
62+
/// with the specified message.
63+
/// </summary>
64+
/// <param name="message">
65+
/// A message that contains information about the usage of unreferenced code.
66+
/// </param>
67+
public RequiresUnreferencedCodeAttribute(string message)
68+
{
69+
Message = message;
70+
}
71+
72+
/// <summary>
73+
/// Gets a message that contains information about the usage of unreferenced code.
74+
/// </summary>
75+
public string Message { get; }
76+
77+
/// <summary>
78+
/// Gets or sets an optional URL that contains more information about the method,
79+
/// why it requires unreferenced code, and what options a consumer has to deal with it.
80+
/// </summary>
81+
public string? Url { get; set; }
82+
}
83+
84+
/// <summary>
85+
/// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a
86+
/// single code artifact.
87+
/// </summary>
88+
/// <remarks>
89+
/// <see cref="UnconditionalSuppressMessageAttribute"/> is different than
90+
/// <see cref="SuppressMessageAttribute"/> in that it doesn't have a
91+
/// <see cref="ConditionalAttribute"/>. So it is always preserved in the compiled assembly.
92+
/// </remarks>
93+
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
94+
internal sealed class UnconditionalSuppressMessageAttribute : Attribute
95+
{
96+
/// <summary>
97+
/// Initializes a new instance of the <see cref="UnconditionalSuppressMessageAttribute"/>
98+
/// class, specifying the category of the tool and the identifier for an analysis rule.
99+
/// </summary>
100+
/// <param name="category">The category for the attribute.</param>
101+
/// <param name="checkId">The identifier of the analysis rule the attribute applies to.</param>
102+
public UnconditionalSuppressMessageAttribute(string category, string checkId)
103+
{
104+
Category = category;
105+
CheckId = checkId;
106+
}
107+
108+
/// <summary>
109+
/// Gets the category identifying the classification of the attribute.
110+
/// </summary>
111+
/// <remarks>
112+
/// The <see cref="Category"/> property describes the tool or tool analysis category
113+
/// for which a message suppression attribute applies.
114+
/// </remarks>
115+
public string Category { get; }
116+
117+
/// <summary>
118+
/// Gets the identifier of the analysis tool rule to be suppressed.
119+
/// </summary>
120+
/// <remarks>
121+
/// Concatenated together, the <see cref="Category"/> and <see cref="CheckId"/>
122+
/// properties form a unique check identifier.
123+
/// </remarks>
124+
public string CheckId { get; }
125+
126+
/// <summary>
127+
/// Gets or sets the scope of the code that is relevant for the attribute.
128+
/// </summary>
129+
/// <remarks>
130+
/// The Scope property is an optional argument that specifies the metadata scope for which
131+
/// the attribute is relevant.
132+
/// </remarks>
133+
public string? Scope { get; set; }
134+
135+
/// <summary>
136+
/// Gets or sets a fully qualified path that represents the target of the attribute.
137+
/// </summary>
138+
/// <remarks>
139+
/// The <see cref="Target"/> property is an optional argument identifying the analysis target
140+
/// of the attribute. An example value is "System.IO.Stream.ctor():System.Void".
141+
/// Because it is fully qualified, it can be long, particularly for targets such as parameters.
142+
/// The analysis tool user interface should be capable of automatically formatting the parameter.
143+
/// </remarks>
144+
public string? Target { get; set; }
145+
146+
/// <summary>
147+
/// Gets or sets an optional argument expanding on exclusion criteria.
148+
/// </summary>
149+
/// <remarks>
150+
/// The <see cref="MessageId "/> property is an optional argument that specifies additional
151+
/// exclusion where the literal metadata target is not sufficiently precise. For example,
152+
/// the <see cref="UnconditionalSuppressMessageAttribute"/> cannot be applied within a method,
153+
/// and it may be desirable to suppress a violation against a statement in the method that will
154+
/// give a rule violation, but not against all statements in the method.
155+
/// </remarks>
156+
public string? MessageId { get; set; }
157+
158+
/// <summary>
159+
/// Gets or sets the justification for suppressing the code analysis message.
160+
/// </summary>
161+
public string? Justification { get; set; }
162+
}
163+
164+
/// <summary>
165+
/// States a dependency that one member has on another.
166+
/// </summary>
167+
/// <remarks>
168+
/// This can be used to inform tooling of a dependency that is otherwise not evident purely from
169+
/// metadata and IL, for example a member relied on via reflection.
170+
/// </remarks>
171+
[AttributeUsage(
172+
AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method,
173+
AllowMultiple = true, Inherited = false)]
174+
internal sealed class DynamicDependencyAttribute : Attribute
175+
{
176+
/// <summary>
177+
/// Initializes a new instance of the <see cref="DynamicDependencyAttribute"/> class
178+
/// with the specified signature of a member on the same type as the consumer.
179+
/// </summary>
180+
/// <param name="memberSignature">The signature of the member depended on.</param>
181+
public DynamicDependencyAttribute(string memberSignature)
182+
{
183+
MemberSignature = memberSignature;
184+
}
185+
186+
/// <summary>
187+
/// Initializes a new instance of the <see cref="DynamicDependencyAttribute"/> class
188+
/// with the specified signature of a member on a <see cref="System.Type"/>.
189+
/// </summary>
190+
/// <param name="memberSignature">The signature of the member depended on.</param>
191+
/// <param name="type">The <see cref="System.Type"/> containing <paramref name="memberSignature"/>.</param>
192+
public DynamicDependencyAttribute(string memberSignature, Type type)
193+
{
194+
MemberSignature = memberSignature;
195+
Type = type;
196+
}
197+
198+
/// <summary>
199+
/// Initializes a new instance of the <see cref="DynamicDependencyAttribute"/> class
200+
/// with the specified signature of a member on a type in an assembly.
201+
/// </summary>
202+
/// <param name="memberSignature">The signature of the member depended on.</param>
203+
/// <param name="typeName">The full name of the type containing the specified member.</param>
204+
/// <param name="assemblyName">The assembly name of the type containing the specified member.</param>
205+
public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName)
206+
{
207+
MemberSignature = memberSignature;
208+
TypeName = typeName;
209+
AssemblyName = assemblyName;
210+
}
211+
212+
/// <summary>
213+
/// Initializes a new instance of the <see cref="DynamicDependencyAttribute"/> class
214+
/// with the specified types of members on a <see cref="System.Type"/>.
215+
/// </summary>
216+
/// <param name="memberTypes">The types of members depended on.</param>
217+
/// <param name="type">The <see cref="System.Type"/> containing the specified members.</param>
218+
public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type)
219+
{
220+
MemberTypes = memberTypes;
221+
Type = type;
222+
}
223+
224+
/// <summary>
225+
/// Initializes a new instance of the <see cref="DynamicDependencyAttribute"/> class
226+
/// with the specified types of members on a type in an assembly.
227+
/// </summary>
228+
/// <param name="memberTypes">The types of members depended on.</param>
229+
/// <param name="typeName">The full name of the type containing the specified members.</param>
230+
/// <param name="assemblyName">The assembly name of the type containing the specified members.</param>
231+
public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName)
232+
{
233+
MemberTypes = memberTypes;
234+
TypeName = typeName;
235+
AssemblyName = assemblyName;
236+
}
237+
238+
/// <summary>
239+
/// Gets the signature of the member depended on.
240+
/// </summary>
241+
/// <remarks>
242+
/// Either <see cref="MemberSignature"/> must be a valid string or <see cref="MemberTypes"/>
243+
/// must not equal <see cref="DynamicallyAccessedMemberTypes.None"/>, but not both.
244+
/// </remarks>
245+
public string? MemberSignature { get; }
246+
247+
/// <summary>
248+
/// Gets the <see cref="DynamicallyAccessedMemberTypes"/> which specifies the type
249+
/// of members depended on.
250+
/// </summary>
251+
/// <remarks>
252+
/// Either <see cref="MemberSignature"/> must be a valid string or <see cref="MemberTypes"/>
253+
/// must not equal <see cref="DynamicallyAccessedMemberTypes.None"/>, but not both.
254+
/// </remarks>
255+
public DynamicallyAccessedMemberTypes MemberTypes { get; }
256+
257+
/// <summary>
258+
/// Gets the <see cref="System.Type"/> containing the specified member.
259+
/// </summary>
260+
/// <remarks>
261+
/// If neither <see cref="Type"/> nor <see cref="TypeName"/> are specified,
262+
/// the type of the consumer is assumed.
263+
/// </remarks>
264+
public Type? Type { get; }
265+
266+
/// <summary>
267+
/// Gets the full name of the type containing the specified member.
268+
/// </summary>
269+
/// <remarks>
270+
/// If neither <see cref="Type"/> nor <see cref="TypeName"/> are specified,
271+
/// the type of the consumer is assumed.
272+
/// </remarks>
273+
public string? TypeName { get; }
274+
275+
/// <summary>
276+
/// Gets the assembly name of the specified type.
277+
/// </summary>
278+
/// <remarks>
279+
/// <see cref="AssemblyName"/> is only valid when <see cref="TypeName"/> is specified.
280+
/// </remarks>
281+
public string? AssemblyName { get; }
282+
283+
/// <summary>
284+
/// Gets or sets the condition in which the dependency is applicable, e.g. "DEBUG".
285+
/// </summary>
286+
public string? Condition { get; set; }
287+
}
288+
9289
/// <summary>
10290
/// Indicates that certain members on a specified <see cref="Type"/> are accessed dynamically,
11291
/// for example through <see cref="System.Reflection"/>.

src/Microsoft.OpenApi/Extensions/EnumExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.Linq;
67
using System.Reflection;
78
using Microsoft.OpenApi.Attributes;
@@ -22,6 +23,7 @@ public static class EnumExtensions
2223
/// <returns>
2324
/// The attribute of the specified type or null.
2425
/// </returns>
26+
[RequiresUnreferencedCode("GetAttributeOfType is not trim-compatible. Recommended to use native AoT-friendly type-specific overloads of GetDisplayName instead.")]
2527
public static T GetAttributeOfType<T>(this Enum enumValue) where T : Attribute
2628
{
2729
var type = enumValue.GetType();
@@ -39,6 +41,7 @@ public static T GetAttributeOfType<T>(this Enum enumValue) where T : Attribute
3941
/// Otherwise, use the standard string representation.
4042
/// </returns>
4143
[Obsolete("Use native AoT-friendly type-specific overloads GetDisplayName methods instead.")]
44+
[RequiresUnreferencedCode("GetAttributeOfType is not trim-compatible. Recommended to use native AoT-friendly type-specific overloads of GetDisplayName instead.")]
4245
public static string GetDisplayName(this Enum enumValue)
4346
{
4447
var attribute = enumValue.GetAttributeOfType<DisplayAttribute>();

src/Microsoft.OpenApi/Extensions/StringExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.Reflection;
67
using Microsoft.OpenApi.Attributes;
78

@@ -16,7 +17,7 @@ public static class StringExtensions
1617
/// Gets the enum value based on the given enum type and display name.
1718
/// </summary>
1819
/// <param name="displayName">The display name.</param>
19-
public static T GetEnumFromDisplayName<T>(this string displayName)
20+
public static T GetEnumFromDisplayName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>(this string displayName)
2021
{
2122
var type = typeof(T);
2223
if (!type.IsEnum)

src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,17 +185,15 @@ IEnumerator IEnumerable.GetEnumerator()
185185
private static ValidationRuleSet BuildDefaultRuleSet()
186186
{
187187
var ruleSet = new ValidationRuleSet();
188-
var validationRuleType = typeof(ValidationRule);
188+
189+
var ruleTypeProperties = GetValidationRuleTypes();
189190

190-
var rules = typeof(ValidationRuleSet).Assembly.GetTypes()
191-
.Where(t => t.IsClass
192-
&& t != typeof(object)
193-
&& t.GetCustomAttributes(typeof(OpenApiRuleAttribute), false).Any())
194-
.SelectMany(t2 => t2.GetProperties(BindingFlags.Static | BindingFlags.Public)
195-
.Where(p => validationRuleType.IsAssignableFrom(p.PropertyType)));
196-
197-
foreach (var property in rules)
191+
foreach (var property in ruleTypeProperties)
198192
{
193+
if (!typeof(ValidationRule).IsAssignableFrom(property.PropertyType))
194+
{
195+
continue;
196+
}
199197
var propertyValue = property.GetValue(null); // static property
200198
if (propertyValue is ValidationRule rule)
201199
{
@@ -205,5 +203,28 @@ private static ValidationRuleSet BuildDefaultRuleSet()
205203

206204
return ruleSet;
207205
}
206+
207+
internal static PropertyInfo[] GetValidationRuleTypes()
208+
{
209+
return [
210+
..typeof(OpenApiComponentsRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
211+
..typeof(OpenApiContactRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
212+
..typeof(OpenApiDocumentRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
213+
..typeof(OpenApiExtensibleRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
214+
..typeof(OpenApiExternalDocsRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
215+
..typeof(OpenApiInfoRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
216+
..typeof(OpenApiLicenseRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
217+
..typeof(OpenApiMediaTypeRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
218+
..typeof(OpenApiOAuthFlowRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
219+
..typeof(OpenApiServerRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
220+
..typeof(OpenApiResponseRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
221+
..typeof(OpenApiResponsesRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
222+
..typeof(OpenApiSchemaRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
223+
..typeof(OpenApiHeaderRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
224+
..typeof(OpenApiTagRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
225+
..typeof(OpenApiPathsRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
226+
..typeof(OpenApiParameterRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
227+
];
228+
}
208229
}
209230
}

0 commit comments

Comments
 (0)