static Attribute ProcessRequiresUnreferencedCodeAttribute(LinkContext context, ICustomAttributeProvider provider, CustomAttribute customAttribute) { if (!(provider is MethodDefinition method)) { return(null); } if (customAttribute.HasConstructorArguments && customAttribute.ConstructorArguments[0].Value is string message) { var ruca = new RequiresUnreferencedCodeAttribute(message); if (customAttribute.HasProperties) { foreach (var prop in customAttribute.Properties) { if (prop.Name == "Url") { ruca.Url = prop.Argument.Value as string; break; } } } return(ruca); } context.LogWarning( $"Attribute '{typeof (RequiresUnreferencedCodeAttribute).FullName}' doesn't have the required number of parameters specified", 2028, method); return(null); }
public static DynamicDependency?ProcessAttribute(LinkContext context, ICustomAttributeProvider provider, CustomAttribute customAttribute) { if (!(provider is IMemberDefinition member)) { return(null); } if (!(member is MethodDefinition || member is FieldDefinition)) { return(null); } // Don't honor the Condition until we have figured out the behavior for DynamicDependencyAttribute: // https://github.com/dotnet/linker/issues/1231 // if (!ShouldProcess (context, customAttribute)) // return null; var dynamicDependency = GetDynamicDependency(customAttribute); if (dynamicDependency != null) { return(dynamicDependency); } context.LogWarning($"The 'DynamicDependencyAttribute' could not be analyzed.", 2034, member); return(null); }
static Attribute ProcessRequiresUnreferencedCodeAttribute(LinkContext context, ICustomAttributeProvider provider, CustomAttribute customAttribute) { if (!(provider is MethodDefinition method)) { return(null); } if (customAttribute.HasConstructorArguments) { string message = (string)customAttribute.ConstructorArguments[0].Value; string url = null; foreach (var prop in customAttribute.Properties) { if (prop.Name == "Url") { url = (string)prop.Argument.Value; } } return(new RequiresUnreferencedCodeAttribute(message) { Url = url }); } context.LogWarning($"Attribute '{typeof (RequiresUnreferencedCodeAttribute).FullName}' on '{method.GetDisplayName ()}' doesn't have a required constructor argument.", 2028, method); return(null); }
IEnumerable <(SuppressMessageInfo Info, ICustomAttributeProvider Target)> DecodeGlobalSuppressions(ModuleDefinition module, ICustomAttributeProvider provider) { var attributes = _context.CustomAttributes.GetCustomAttributes(provider). Where(a => TypeRefHasUnconditionalSuppressions(a.AttributeType)); foreach (var instance in attributes) { SuppressMessageInfo info; if (!TryDecodeSuppressMessageAttributeData(instance, out info)) { continue; } var scope = info.Scope?.ToLower(); if (info.Target == null && (scope == "module" || scope == null)) { yield return(info, provider); continue; } switch (scope) { case "module": yield return(info, provider); break; case "type": case "member": if (info.Target == null) { break; } foreach (var result in DocumentationSignatureParser.GetMembersForDocumentationSignature(info.Target, module, _context)) { yield return(info, result); } break; default: _context.LogWarning($"Invalid scope '{info.Scope}' used in 'UnconditionalSuppressMessageAttribute' on module '{module.Name}' " + $"with target '{info.Target}'.", 2108, _context.GetAssemblyLocation(module.Assembly)); break; } } }
void PopulateCacheForType(TypeDefinition type) { // Avoid repeat scans of the same type if (!_typesWithPopulatedCache.Add(type)) { return; } foreach (MethodDefinition method in type.Methods) { if (!method.HasCustomAttributes) { continue; } foreach (var attribute in method.CustomAttributes) { if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices") { continue; } switch (attribute.AttributeType.Name) { case "AsyncIteratorStateMachineAttribute": case "AsyncStateMachineAttribute": case "IteratorStateMachineAttribute": TypeDefinition stateMachineType = GetFirstConstructorArgumentAsType(attribute); if (stateMachineType != null) { if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd(stateMachineType, method)) { var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType]; _context.LogWarning( $"Methods '{method.GetDisplayName ()}' and '{alreadyAssociatedMethod.GetDisplayName ()}' are both associated with state machine type '{stateMachineType.GetDisplayName ()}'. This is currently unsupported and may lead to incorrectly reported warnings.", 2107, new MessageOrigin(method), MessageSubCategory.TrimAnalysis); } } break; } } } }
static RemoveAttributeInstancesAttribute BuildRemoveAttributeInstancesAttribute(LinkContext context, TypeDefinition attributeContext, CustomAttribute ca) { switch (ca.ConstructorArguments.Count) { case 0: return(new RemoveAttributeInstancesAttribute()); case 1: // Argument is always boxed return(new RemoveAttributeInstancesAttribute((CustomAttributeArgument)ca.ConstructorArguments[0].Value)); default: context.LogWarning( $"Attribute '{ca.AttributeType.GetDisplayName ()}' doesn't have the required number of arguments specified", 2028, attributeContext); return(null); } ; }
static bool TryLogSingleWarning(LinkContext context, int code, MessageOrigin origin, string subcategory) { if (subcategory != MessageSubCategory.TrimAnalysis) { return(false); } // There are valid cases where we can't map the message to an assembly // For example if it's caused by something in an xml file passed on the command line // In that case, give up on single-warn collapse and just print out the warning on its own. var assembly = origin.Provider switch { AssemblyDefinition asm => asm, TypeDefinition type => type.Module.Assembly, IMemberDefinition member => member.DeclaringType.Module.Assembly, _ => null }; if (assembly == null) { return(false); } // Any IL2026 warnings left in an assembly with an IsTrimmable attribute are considered intentional // and should not be collapsed, so that the user-visible RUC message gets printed. if (code == 2026 && context.IsTrimmable(assembly)) { return(false); } var assemblyName = assembly.Name.Name; if (!context.IsSingleWarn(assemblyName)) { return(false); } if (context.AssembliesWithGeneratedSingleWarning.Add(assemblyName)) { context.LogWarning(context.GetAssemblyLocation(assembly), DiagnosticId.AssemblyProducedTrimWarnings, assemblyName); } return(true); }
static bool TryLogSingleWarning(LinkContext context, int code, MessageOrigin origin, string subcategory) { if (subcategory != MessageSubCategory.TrimAnalysis) { return(false); } Debug.Assert(origin.Provider != null); var assembly = origin.Provider switch { AssemblyDefinition asm => asm, TypeDefinition type => type.Module.Assembly, IMemberDefinition member => member.DeclaringType.Module.Assembly, _ => throw new NotSupportedException() }; Debug.Assert(assembly != null); if (assembly == null) { return(false); } // Any IL2026 warnings left in an assembly with an IsTrimmable attribute are considered intentional // and should not be collapsed, so that the user-visible RUC message gets printed. if (code == 2026 && context.IsTrimmable(assembly)) { return(false); } var assemblyName = assembly.Name.Name; if (!context.IsSingleWarn(assemblyName)) { return(false); } if (context.AssembliesWithGeneratedSingleWarning.Add(assemblyName)) { context.LogWarning($"Assembly '{assemblyName}' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries", 2104, context.GetAssemblyLocation(assembly)); } return(true); }
void PopulateCacheForType(TypeDefinition type) { // Avoid repeat scans of the same type if (!_typesWithPopulatedCache.Add(type)) { return; } foreach (MethodDefinition method in type.Methods) { if (!method.HasCustomAttributes) { continue; } foreach (var attribute in method.CustomAttributes) { if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices") { continue; } switch (attribute.AttributeType.Name) { case "AsyncIteratorStateMachineAttribute": case "AsyncStateMachineAttribute": case "IteratorStateMachineAttribute": TypeDefinition?stateMachineType = GetFirstConstructorArgumentAsType(attribute); if (stateMachineType != null) { if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd(stateMachineType, method)) { var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType]; _context.LogWarning(new MessageOrigin(method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), stateMachineType.GetDisplayName()); } } break; } } } }
private static MessageContainer CreateWarningMessageContainer(LinkContext context, string text, int code, MessageOrigin origin, WarnVersion version, string subcategory = MessageSubCategory.None) { if (!(version >= WarnVersion.ILLink0 && version <= WarnVersion.Latest)) { throw new ArgumentException($"The provided warning version '{version}' is invalid."); } if (context.IsWarningSuppressed(code, origin)) { return(Empty); } if (version > context.WarnVersion) { return(Empty); } if (subcategory == MessageSubCategory.TrimAnalysis) { Debug.Assert(origin.MemberDefinition != null); var declaringType = origin.MemberDefinition?.DeclaringType ?? (origin.MemberDefinition as TypeDefinition); var assembly = declaringType.Module.Assembly; var assemblyName = assembly?.Name.Name; if (assemblyName != null && context.IsSingleWarn(assemblyName)) { if (context.AssembliesWithGeneratedSingleWarning.Add(assemblyName)) { context.LogWarning($"Assembly '{assemblyName}' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries", 2104, context.GetAssemblyLocation(assembly)); } return(Empty); } } if (context.IsWarningAsError(code)) { return(new MessageContainer(MessageCategory.WarningAsError, text, code, subcategory, origin)); } return(new MessageContainer(MessageCategory.Warning, text, code, subcategory, origin)); }
public bool TryResolveTypeName(string typeNameString, ICustomAttributeProvider?origin, [NotNullWhen(true)] out TypeReference?typeReference, [NotNullWhen(true)] out AssemblyDefinition?typeAssembly, bool needsAssemblyName = true) { typeReference = null; typeAssembly = null; if (string.IsNullOrEmpty(typeNameString)) { return(false); } TypeName parsedTypeName; try { parsedTypeName = TypeParser.ParseTypeName(typeNameString); } catch (ArgumentException) { return(false); } catch (System.IO.FileLoadException) { return(false); } if (parsedTypeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { typeAssembly = _context.TryResolve(assemblyQualifiedTypeName.AssemblyName.Name); if (typeAssembly == null) { return(false); } typeReference = ResolveTypeName(typeAssembly, assemblyQualifiedTypeName.TypeName); return(typeReference != null); } // If parsedTypeName doesn't have an assembly name in it but it does have a namespace, // search for the type in the calling object's assembly. If not found, look in the core // assembly. typeAssembly = origin switch { AssemblyDefinition asm => asm, TypeDefinition type => type.Module?.Assembly, IMemberDefinition member => member.DeclaringType.Module.Assembly, null => null, _ => throw new NotSupportedException() }; if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out typeReference)) { return(true); } // If type is not found in the caller's assembly, try in core assembly. typeAssembly = _context.TryResolve(PlatformAssemblies.CoreLib); if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out typeReference)) { return(true); } // It is common to use Type.GetType for looking if a type is available. // If no type was found only warn and return null. if (needsAssemblyName && origin != null) { _context.LogWarning(new MessageOrigin(origin), DiagnosticId.TypeWasNotFoundInAssemblyNorBaseLibrary, typeNameString); } typeAssembly = null; return(false); bool TryResolveTypeName(AssemblyDefinition assemblyDefinition, TypeName typeName, [NotNullWhen(true)] out TypeReference?typeReference) { typeReference = null; if (assemblyDefinition == null) { return(false); } typeReference = ResolveTypeName(assemblyDefinition, typeName); return(typeReference != null); } }
public void UnrecognizedReflectionAccessPattern(IMemberDefinition source, Instruction sourceInstruction, IMetadataTokenProvider accessedItem, string message, int messageCode) { var origin = new MessageOrigin(source, sourceInstruction?.Offset); _context.LogWarning(message, messageCode, origin, MessageSubCategory.TrimAnalysis); }
public TypeReference ResolveTypeName(string typeNameString, ICustomAttributeProvider origin, out AssemblyDefinition typeAssembly, bool needsAssemblyName = true) { typeAssembly = null; if (string.IsNullOrEmpty(typeNameString)) { return(null); } TypeName parsedTypeName; try { parsedTypeName = TypeParser.ParseTypeName(typeNameString); } catch (ArgumentException) { return(null); } catch (System.IO.FileLoadException) { return(null); } if (parsedTypeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { typeAssembly = _context.TryResolve(assemblyQualifiedTypeName.AssemblyName.Name); if (typeAssembly == null) { return(null); } return(ResolveTypeName(typeAssembly, assemblyQualifiedTypeName.TypeName)); } // If parsedTypeName doesn't have an assembly name in it but it does have a namespace, // search for the type in the calling object's assembly. If not found, look in the core // assembly. typeAssembly = origin switch { AssemblyDefinition asm => asm, TypeDefinition type => type.Module?.Assembly, IMemberDefinition member => member.DeclaringType.Module.Assembly, null => null, _ => throw new NotSupportedException() }; if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out var typeRef)) { return(typeRef); } // If type is not found in the caller's assembly, try in core assembly. typeAssembly = _context.TryResolve(PlatformAssemblies.CoreLib); if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out var typeRefFromSPCL)) { return(typeRefFromSPCL); } // It is common to use Type.GetType for looking if a type is available. // If no type was found only warn and return null. if (needsAssemblyName && origin != null) { _context.LogWarning($"Type '{typeNameString}' was not found in the caller assembly nor in the base library. " + $"Type name strings used for dynamically accessing a type should be assembly qualified.", 2105, new MessageOrigin(origin)); } typeAssembly = null; return(null); bool TryResolveTypeName(AssemblyDefinition assemblyDefinition, TypeName typeName, out TypeReference typeReference) { typeReference = null; if (assemblyDefinition == null) { return(false); } typeReference = ResolveTypeName(assemblyDefinition, typeName); return(typeReference != null); } }
void PopulateCacheForType(TypeDefinition type) { // Avoid repeat scans of the same type if (!_typesWithPopulatedCache.Add(type)) { return; } var callGraph = new CompilerGeneratedCallGraph(); var callingMethods = new HashSet <MethodDefinition> (); void ProcessMethod(MethodDefinition method) { bool isStateMachineMember = CompilerGeneratedNames.IsStateMachineType(method.DeclaringType.Name); if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(method.Name)) { if (!isStateMachineMember) { // If it's not a nested function, track as an entry point to the call graph. var added = callingMethods.Add(method); Debug.Assert(added); } } else { // We don't expect lambdas or local functions to be emitted directly into // state machine types. Debug.Assert(!isStateMachineMember); } // Discover calls or references to lambdas or local functions. This includes // calls to local functions, and lambda assignments (which use ldftn). if (method.Body != null) { foreach (var instruction in method.Body.Instructions) { if (instruction.OpCode.OperandType != OperandType.InlineMethod) { continue; } MethodDefinition?lambdaOrLocalFunction = _context.TryResolve((MethodReference)instruction.Operand); if (lambdaOrLocalFunction == null) { continue; } if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(lambdaOrLocalFunction.Name)) { continue; } if (isStateMachineMember) { callGraph.TrackCall(method.DeclaringType, lambdaOrLocalFunction); } else { callGraph.TrackCall(method, lambdaOrLocalFunction); } } } // Discover state machine methods. if (!method.HasCustomAttributes) { return; } foreach (var attribute in method.CustomAttributes) { if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices") { continue; } switch (attribute.AttributeType.Name) { case "AsyncIteratorStateMachineAttribute": case "AsyncStateMachineAttribute": case "IteratorStateMachineAttribute": TypeDefinition?stateMachineType = GetFirstConstructorArgumentAsType(attribute); if (stateMachineType == null) { break; } Debug.Assert(stateMachineType.DeclaringType == type || (CompilerGeneratedNames.IsGeneratedMemberName(stateMachineType.DeclaringType.Name) && stateMachineType.DeclaringType.DeclaringType == type)); callGraph.TrackCall(method, stateMachineType); if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd(stateMachineType, method)) { var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType]; _context.LogWarning(new MessageOrigin(method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), stateMachineType.GetDisplayName()); } break; } } } // Look for state machine methods, and methods which call local functions. foreach (MethodDefinition method in type.Methods) { ProcessMethod(method); } // Also scan compiler-generated state machine methods (in case they have calls to nested functions), // and nested functions inside compiler-generated closures (in case they call other nested functions). // State machines can be emitted into lambda display classes, so we need to go down at least two // levels to find calls from iterator nested functions to other nested functions. We just recurse into // all compiler-generated nested types to avoid depending on implementation details. foreach (var nestedType in GetCompilerGeneratedNestedTypes(type)) { foreach (var method in nestedType.Methods) { ProcessMethod(method); } } // Now we've discovered the call graphs for calls to nested functions. // Use this to map back from nested functions to the declaring user methods. // Note: This maps all nested functions back to the user code, not to the immediately // declaring local function. The IL doesn't contain enough information in general for // us to determine the nesting of local functions and lambdas. // Note: this only discovers nested functions which are referenced from the user // code or its referenced nested functions. There is no reliable way to determine from // IL which user code an unused nested function belongs to. foreach (var userDefinedMethod in callingMethods) { foreach (var compilerGeneratedMember in callGraph.GetReachableMembers(userDefinedMethod)) { switch (compilerGeneratedMember) { case MethodDefinition nestedFunction: Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(nestedFunction.Name)); // Nested functions get suppressions from the user method only. if (!_compilerGeneratedMethodToUserCodeMethod.TryAdd(nestedFunction, userDefinedMethod)) { var alreadyAssociatedMethod = _compilerGeneratedMethodToUserCodeMethod[nestedFunction]; _context.LogWarning(new MessageOrigin(userDefinedMethod), DiagnosticId.MethodsAreAssociatedWithUserMethod, userDefinedMethod.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), nestedFunction.GetDisplayName()); } break; case TypeDefinition stateMachineType: // Types in the call graph are always state machine types // For those all their methods are not tracked explicitly in the call graph; instead, they // are represented by the state machine type itself. // We are already tracking the association of the state machine type to the user code method // above, so no need to track it here. Debug.Assert(CompilerGeneratedNames.IsStateMachineType(stateMachineType.Name)); break; default: throw new InvalidOperationException(); } } } }
IEnumerable <CustomAttribute> ProcessAttributes(XPathNavigator nav) { XPathNodeIterator iterator = nav.SelectChildren("attribute", string.Empty); var attributes = new List <CustomAttribute> (); while (iterator.MoveNext()) { AssemblyDefinition assembly; TypeDefinition attributeType; string attributeFullName = GetFullName(iterator.Current); if (attributeFullName == String.Empty) { _context.LogWarning($"Attribute element does not contain attribute 'fullname'", 2029, _xmlDocumentLocation); continue; } string assemblyName = GetAttribute(iterator.Current, "assembly"); if (assemblyName == String.Empty) { attributeType = _context.GetType(attributeFullName); } else { try { assembly = GetAssembly(_context, AssemblyNameReference.Parse(assemblyName)); } catch (Exception) { _context.LogWarning($"Could not resolve assembly '{assemblyName}' in attribute '{attributeFullName}' specified in the '{_xmlDocumentLocation}'", 2030, _xmlDocumentLocation); continue; } attributeType = assembly.FindType(attributeFullName); } if (attributeType == null) { _context.LogWarning($"Attribute type '{attributeFullName}' could not be found", 2031, _xmlDocumentLocation); continue; } ArrayBuilder <string> arguments = GetAttributeChildren(iterator.Current.SelectChildren("argument", string.Empty)); MethodDefinition constructor = attributeType.Methods.Where(method => method.IsInstanceConstructor()).FirstOrDefault(c => c.Parameters.Count == arguments.Count); if (constructor == null) { _context.LogWarning($"Could not find a constructor for type '{attributeType}' that receives '{arguments.Count}' arguments as parameter", 2022, _xmlDocumentLocation); continue; } string[] xmlArguments = arguments.ToArray(); bool recognizedArgument = true; CustomAttribute attribute = new CustomAttribute(constructor); for (int i = 0; i < xmlArguments.Length; i++) { object argumentValue = null; if (constructor.Parameters[i].ParameterType.Resolve().IsEnum) { foreach (var field in constructor.Parameters[i].ParameterType.Resolve().Fields) { if (field.IsStatic && field.Name == xmlArguments[i]) { argumentValue = Convert.ToInt32(field.Constant); break; } } if (argumentValue == null) { _context.LogWarning($"Could not parse argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' as a {constructor.Parameters[i].ParameterType.FullName}", 2021, _xmlDocumentLocation); recognizedArgument = false; } } else { switch (constructor.Parameters[i].ParameterType.MetadataType) { case MetadataType.String: argumentValue = xmlArguments[i]; break; case MetadataType.Int32: int result; if (int.TryParse(xmlArguments[i], out result)) { argumentValue = result; } else { _context.LogWarning($"Argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' could not be transformed to the constructor parameter type", 2032, _xmlDocumentLocation); } break; default: _context.LogWarning($"Argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' is of unsupported type '{constructor.Parameters[i].ParameterType}'", 2020, _xmlDocumentLocation); recognizedArgument = false; break; } } attribute.ConstructorArguments.Add(new CustomAttributeArgument(constructor.Parameters[i].ParameterType, argumentValue)); } if (recognizedArgument) { attributes.Add(attribute); } } return(attributes); }