private static NavigateToMatchKind GetNavigateToMatchKind(ArrayBuilder <PatternMatch> nameMatches) { if (nameMatches.Any(r => r.Kind == PatternMatchKind.Exact)) { return(NavigateToMatchKind.Exact); } if (nameMatches.Any(r => r.Kind == PatternMatchKind.Prefix)) { return(NavigateToMatchKind.Prefix); } if (nameMatches.Any(r => r.Kind == PatternMatchKind.Substring)) { return(NavigateToMatchKind.Substring); } return(NavigateToMatchKind.Regular); }
protected static SyntaxTokenList GetUpdatedDeclarationAccessibilityModifiers( ArrayBuilder <SyntaxToken> newModifierTokens, SyntaxTokenList modifiersList, Func <SyntaxToken, bool> isAccessibilityModifier) { using var _ = ArrayBuilder <SyntaxToken> .GetInstance(out var updatedModifiersList); var anyAccessModifierSeen = false; foreach (var modifier in modifiersList) { SyntaxToken newModifier; if (isAccessibilityModifier(modifier)) { if (newModifierTokens.Count == 0) { continue; } newModifier = newModifierTokens[0] .WithLeadingTrivia(modifier.LeadingTrivia) .WithTrailingTrivia(modifier.TrailingTrivia); newModifierTokens.RemoveAt(0); anyAccessModifierSeen = true; } else { if (anyAccessModifierSeen && newModifierTokens.Any()) { updatedModifiersList.AddRange(newModifierTokens); newModifierTokens.Clear(); } newModifier = modifier; } updatedModifiersList.Add(newModifier); } if (!anyAccessModifierSeen) { for (var i = newModifierTokens.Count - 1; i >= 0; i--) { updatedModifiersList.Insert(0, newModifierTokens[i]); } } else { updatedModifiersList.AddRange(newModifierTokens); } return(updatedModifiersList.ToSyntaxTokenList()); }
internal static void EncodeInternal(TypeSymbol type, int customModifiersCount, RefKind refKind, ArrayBuilder <bool> transformFlagsBuilder) { Debug.Assert(!transformFlagsBuilder.Any()); if (refKind != RefKind.None) { // Native compiler encodes an extra transform flag, always false, for ref/out parameters. transformFlagsBuilder.Add(false); } // Native compiler encodes an extra transform flag, always false, for each custom modifier. HandleCustomModifiers(customModifiersCount, transformFlagsBuilder); type.VisitType(s_encodeDynamicTransform, transformFlagsBuilder); }
/// <summary> /// The set of synthesized delegates created by /// this AnonymousTypeManager. /// </summary> private void GetCreatedSynthesizedDelegates(ArrayBuilder<SynthesizedDelegateSymbol> builder) { Debug.Assert(!builder.Any()); var delegates = _lazySynthesizedDelegates; if (delegates != null) { foreach (var template in delegates.Values) { if (ReferenceEquals(template.Manager, this)) { builder.Add(template.Delegate); } } builder.Sort(SynthesizedDelegateSymbolComparer.Instance); } }
/// <summary> /// The set of anonymous type templates created by /// this AnonymousTypeManager, in fixed order. /// </summary> private void GetCreatedAnonymousTypeTemplates(ArrayBuilder<AnonymousTypeTemplateSymbol> builder) { Debug.Assert(!builder.Any()); var anonymousTypes = _lazyAnonymousTypeTemplates; if (anonymousTypes != null) { foreach (var template in anonymousTypes.Values) { if (ReferenceEquals(template.Manager, this)) { builder.Add(template); } } // Sort type templates using smallest location builder.Sort(new AnonymousTypeComparer(this.Compilation)); } }
/// <summary> /// The set of synthesized delegates created by /// this AnonymousTypeManager. /// </summary> private void GetCreatedSynthesizedDelegates(ArrayBuilder <SynthesizedDelegateSymbol> builder) { Debug.Assert(!builder.Any()); var delegates = _lazySynthesizedDelegates; if (delegates != null) { foreach (var template in delegates.Values) { if (ReferenceEquals(template.Manager, this)) { builder.Add(template.Delegate); } } // Should be sorted, same as AnonymousTypeTemplates. See VB. } }
private void EmitMultidimensionalElementInitializers(ArrayTypeSymbol arrayType, ImmutableArray<BoundExpression> inits, bool includeConstants) { // Using a List for the stack instead of the framework Stack because IEnumerable from Stack is top to bottom. // This algorithm requires the IEnumerable to be from bottom to top. See extensions for List in CollectionExtensions.vb. var indices = new ArrayBuilder<IndexDesc>(); // emit initializers for all values of the leftmost index. for (int i = 0; i < inits.Length; i++) { indices.Push(new IndexDesc(i, ((BoundArrayInitialization)inits[i]).Initializers)); EmitAllElementInitializersRecursive(arrayType, indices, includeConstants); } Debug.Assert(!indices.Any()); }
private static void EncodeInternal(TypeSymbol type, int customModifiersCount, RefKind refKind, ArrayBuilder <bool> transformFlagsBuilder, bool addCustomModifierFlags) { Debug.Assert(!transformFlagsBuilder.Any()); if (refKind != RefKind.None) { // Native compiler encodes an extra transform flag, always false, for ref/out parameters. transformFlagsBuilder.Add(false); } if (addCustomModifierFlags) { // Native compiler encodes an extra transform flag, always false, for each custom modifier. HandleCustomModifiers(customModifiersCount, transformFlagsBuilder); type.VisitType((typeSymbol, builder, isNested) => AddFlags(typeSymbol, builder, isNested, addCustomModifierFlags: true), transformFlagsBuilder); } else { type.VisitType((typeSymbol, builder, isNested) => AddFlags(typeSymbol, builder, isNested, addCustomModifierFlags: false), transformFlagsBuilder); } }
/// <summary> /// If we have a WinRT type event, we need to encapsulate the adder call /// (which returns an EventRegistrationToken) with a call to /// WindowsRuntimeMarshal.AddEventHandler or RemoveEventHandler, but these /// require us to create a new Func representing the adder and another /// Action representing the Remover. /// /// The rewritten call looks something like: /// /// WindowsRuntimeMarshal.AddEventHandler<EventHandler> /// (new Func<EventHandler, EventRegistrationToken>(@object.add), /// new Action<EventRegistrationToken>(@object.remove), handler); /// /// Where @object is a compiler-generated local temp if needed. /// </summary> /// <remarks> /// TODO: use or delete isDynamic. /// </remarks> private BoundExpression RewriteWindowsRuntimeEventAssignmentOperator(SyntaxNode syntax, EventSymbol eventSymbol, EventAssignmentKind kind, bool isDynamic, BoundExpression rewrittenReceiverOpt, BoundExpression rewrittenArgument) { BoundAssignmentOperator tempAssignment = null; BoundLocal boundTemp = null; if (!eventSymbol.IsStatic && CanChangeValueBetweenReads(rewrittenReceiverOpt)) { boundTemp = _factory.StoreToTemp(rewrittenReceiverOpt, out tempAssignment); } NamedTypeSymbol tokenType = _factory.WellKnownType(WellKnownType.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationToken); NamedTypeSymbol marshalType = _factory.WellKnownType(WellKnownType.System_Runtime_InteropServices_WindowsRuntime_WindowsRuntimeMarshal); NamedTypeSymbol actionType = _factory.WellKnownType(WellKnownType.System_Action_T).Construct(tokenType); TypeSymbol eventType = eventSymbol.Type; BoundExpression delegateCreationArgument = boundTemp ?? rewrittenReceiverOpt ?? _factory.Type(eventType); BoundDelegateCreationExpression removeDelegate = new BoundDelegateCreationExpression( syntax: syntax, argument: delegateCreationArgument, methodOpt: eventSymbol.RemoveMethod, isExtensionMethod: false, type: actionType); BoundExpression clearCall = null; if (kind == EventAssignmentKind.Assignment) { MethodSymbol clearMethod; if (TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_WindowsRuntimeMarshal__RemoveAllEventHandlers, out clearMethod)) { clearCall = MakeCall( syntax: syntax, rewrittenReceiver: null, method: clearMethod, rewrittenArguments: ImmutableArray.Create <BoundExpression>(removeDelegate), type: clearMethod.ReturnType); } else { clearCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundExpression>(removeDelegate), ErrorTypeSymbol.UnknownResultType); } } ImmutableArray <BoundExpression> marshalArguments; WellKnownMember helper; if (kind == EventAssignmentKind.Subtraction) { helper = WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_WindowsRuntimeMarshal__RemoveEventHandler_T; marshalArguments = ImmutableArray.Create <BoundExpression>(removeDelegate, rewrittenArgument); } else { NamedTypeSymbol func2Type = _factory.WellKnownType(WellKnownType.System_Func_T2).Construct(eventType, tokenType); BoundDelegateCreationExpression addDelegate = new BoundDelegateCreationExpression( syntax: syntax, argument: delegateCreationArgument, methodOpt: eventSymbol.AddMethod, isExtensionMethod: false, type: func2Type); helper = WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_WindowsRuntimeMarshal__AddEventHandler_T; marshalArguments = ImmutableArray.Create <BoundExpression>(addDelegate, removeDelegate, rewrittenArgument); } BoundExpression marshalCall; MethodSymbol marshalMethod; if (TryGetWellKnownTypeMember(syntax, helper, out marshalMethod)) { marshalMethod = marshalMethod.Construct(eventType); marshalCall = MakeCall( syntax: syntax, rewrittenReceiver: null, method: marshalMethod, rewrittenArguments: marshalArguments, type: marshalMethod.ReturnType); } else { marshalCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, marshalArguments, ErrorTypeSymbol.UnknownResultType); } // In this case, we don't need a sequence. if (boundTemp == null && clearCall == null) { return(marshalCall); } ImmutableArray <LocalSymbol> tempSymbols = boundTemp == null ? ImmutableArray <LocalSymbol> .Empty : ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol); ArrayBuilder <BoundExpression> sideEffects = ArrayBuilder <BoundExpression> .GetInstance(2); //max size if (clearCall != null) { sideEffects.Add(clearCall); } if (tempAssignment != null) { sideEffects.Add(tempAssignment); } Debug.Assert(sideEffects.Any(), "Otherwise, we shouldn't be building a sequence"); return(new BoundSequence(syntax, tempSymbols, sideEffects.ToImmutableAndFree(), marshalCall, marshalCall.Type)); }
private void OnSymbolEnd(SymbolAnalysisContext symbolEndContext, bool hasInvalidOrDynamicOperation) { // We bail out reporting diagnostics for named types if it contains following kind of operations: // 1. Invalid operations, i.e. erroneous code: // We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user // is still editing code and fixing unresolved references to symbols, such as overload resolution errors. // 2. Dynamic operations, where we do not know the exact member being referenced at compile time. if (hasInvalidOrDynamicOperation) { return; } if (symbolEndContext.Symbol.GetAttributes().Any(a => a.AttributeClass == _structLayoutAttributeType)) { // Bail out for types with 'StructLayoutAttribute' as the ordering of the members is critical, // and removal of unused members might break semantics. return; } // Report diagnostics for unused candidate members. var first = true; PooledHashSet <ISymbol> symbolsReferencedInDocComments = null; ArrayBuilder <string> debuggerDisplayAttributeArguments = null; try { var namedType = (INamedTypeSymbol)symbolEndContext.Symbol; foreach (var member in namedType.GetMembers()) { // Check if the underlying member is neither read nor a readable reference to the member is taken. // If so, we flag the member as either unused (never written) or unread (written but not read). if (TryRemove(member, out var valueUsageInfo) && !valueUsageInfo.IsReadFrom()) { Debug.Assert(IsCandidateSymbol(member)); Debug.Assert(!member.IsImplicitlyDeclared); if (first) { // Bail out if there are syntax errors in any of the declarations of the containing type. // Note that we check this only for the first time that we report an unused or unread member for the containing type. if (HasSyntaxErrors(namedType, symbolEndContext.CancellationToken)) { return; } // Compute the set of candidate symbols referenced in all the documentation comments within the named type declarations. // This set is computed once and used for all the iterations of the loop. symbolsReferencedInDocComments = GetCandidateSymbolsReferencedInDocComments(namedType, symbolEndContext.Compilation, symbolEndContext.CancellationToken); // Compute the set of string arguments to DebuggerDisplay attributes applied to any symbol within the named type declaration. // These strings may have an embedded reference to the symbol. // This set is computed once and used for all the iterations of the loop. debuggerDisplayAttributeArguments = GetDebuggerDisplayAttributeArguments(namedType); first = false; } // Simple heuristic for members referenced in DebuggerDisplayAttribute's string argument: // bail out if any of the DebuggerDisplay string arguments contains the member name. // In future, we can consider improving this heuristic to parse the embedded expression // and resolve symbol references. if (debuggerDisplayAttributeArguments.Any(arg => arg.Contains(member.Name))) { continue; } // Report IDE0051 or IDE0052 based on whether the underlying member has any Write/WritableRef/NonReadWriteRef references or not. var rule = !valueUsageInfo.IsWrittenTo() && !valueUsageInfo.IsNameOnly() && !symbolsReferencedInDocComments.Contains(member) ? s_removeUnusedMembersRule : s_removeUnreadMembersRule; // Do not flag write-only properties that are not read. // Write-only properties are assumed to have side effects // visible through other means than a property getter. if (rule == s_removeUnreadMembersRule && member is IPropertySymbol property && property.IsWriteOnly) { continue; } // Most of the members should have a single location, except for partial methods. // We report the diagnostic on the first location of the member. var diagnostic = DiagnosticHelper.CreateWithMessage( rule, member.Locations[0], rule.GetEffectiveSeverity(symbolEndContext.Compilation.Options), additionalLocations: null, properties: null, GetMessage(rule, member)); symbolEndContext.ReportDiagnostic(diagnostic); } } } finally { symbolsReferencedInDocComments?.Free(); debuggerDisplayAttributeArguments?.Free(); } return; }
private void OnSymbolEnd(SymbolAnalysisContext symbolEndContext, bool hasInvalidOperation) { // We bail out reporting diagnostics for named types which have any invalid operations, i.e. erroneous code. // We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user // is still editing code and fixing unresolved references to symbols, such as overload resolution errors. if (hasInvalidOperation) { return; } // Report diagnostics for unused candidate members. var first = true; PooledHashSet <ISymbol> symbolsReferencedInDocComments = null; ArrayBuilder <string> debuggerDisplayAttributeArguments = null; try { var namedType = (INamedTypeSymbol)symbolEndContext.Symbol; foreach (var member in namedType.GetMembers()) { // Check if the underlying member is neither read nor a readable reference to the member is taken. // If so, we flag the member as either unused (never written) or unread (written but not read). if (TryRemove(member, out var valueUsageInfo) && !valueUsageInfo.ContainsReadOrReadableRef()) { Debug.Assert(IsCandidateSymbol(member)); Debug.Assert(!member.IsImplicitlyDeclared); if (first) { // Bail out if there are syntax errors in any of the declarations of the containing type. // Note that we check this only for the first time that we report an unused or unread member for the containing type. if (HasSyntaxErrors(namedType, symbolEndContext.CancellationToken)) { return; } // Compute the set of candidate symbols referenced in all the documentation comments within the named type declarations. // This set is computed once and used for all the iterations of the loop. symbolsReferencedInDocComments = GetCandidateSymbolsReferencedInDocComments(namedType, symbolEndContext.Compilation, symbolEndContext.CancellationToken); // Compute the set of string arguments to DebuggerDisplay attributes applied to any symbol within the named type declaration. // These strings may have an embedded reference to the symbol. // This set is computed once and used for all the iterations of the loop. debuggerDisplayAttributeArguments = GetDebuggerDisplayAttributeArguments(namedType); first = false; } // Simple heuristic for members referenced in DebuggerDisplayAttribute's string argument: // bail out if any of the DebuggerDisplay string arguments contains the member name. // In future, we can consider improving this heuristic to parse the embedded expression // and resolve symbol references. if (debuggerDisplayAttributeArguments.Any(arg => arg.Contains(member.Name))) { continue; } // Report IDE0051 or IDE0052 based on whether the underlying member has any Write/WritableRef/NonReadWriteRef references or not. var rule = !valueUsageInfo.ContainsWriteOrWritableRef() && !valueUsageInfo.ContainsNonReadWriteRef() && !symbolsReferencedInDocComments.Contains(member) ? s_removeUnusedMembersRule : s_removeUnreadMembersRule; // Most of the members should have a single location, except for partial methods. // We report the diagnostic on the first location of the member. var diagnostic = Diagnostic.Create( rule, member.Locations[0], additionalLocations: null, properties: null, $"{member.ContainingType.Name}.{member.Name}"); symbolEndContext.ReportDiagnostic(diagnostic); } } } finally { symbolsReferencedInDocComments?.Free(); debuggerDisplayAttributeArguments?.Free(); } return; }
TypeAnnotations BuildTypeAnnotations(TypeDefinition type) { var annotatedFields = new ArrayBuilder <FieldAnnotation> (); // First go over all fields with an explicit annotation if (type.HasFields) { foreach (FieldDefinition field in type.Fields) { DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(field); if (annotation == DynamicallyAccessedMemberTypes.None) { continue; } if (!IsTypeInterestingForDataflow(field.FieldType)) { // Already know that there's a non-empty annotation on a field which is not System.Type/String and we're about to ignore it _context.LogWarning( $"Field '{field.GetDisplayName ()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to fields of type 'System.Type' or 'System.String'", 2097, field, subcategory: MessageSubCategory.TrimAnalysis); continue; } annotatedFields.Add(new FieldAnnotation(field, annotation)); } } var annotatedMethods = new ArrayBuilder <MethodAnnotations> (); // Next go over all methods with an explicit annotation if (type.HasMethods) { foreach (MethodDefinition method in type.Methods) { DynamicallyAccessedMemberTypes[] paramAnnotations = null; // We convert indices from metadata space to IL space here. // IL space assigns index 0 to the `this` parameter on instance methods. DynamicallyAccessedMemberTypes methodMemberTypes = GetMemberTypesForDynamicallyAccessedMembersAttribute(method); int offset; if (method.HasImplicitThis()) { offset = 1; if (IsTypeInterestingForDataflow(method.DeclaringType)) { // If there's an annotation on the method itself and it's one of the special types (System.Type for example) // treat that annotation as annotating the "this" parameter. if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { paramAnnotations = new DynamicallyAccessedMemberTypes[method.Parameters.Count + offset]; paramAnnotations[0] = methodMemberTypes; } } else if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { _context.LogWarning( $"The 'DynamicallyAccessedMembersAttribute' is not allowed on methods. It is allowed on method return value or method parameters though.", 2041, method, subcategory: MessageSubCategory.TrimAnalysis); } } else { offset = 0; if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { _context.LogWarning( $"The 'DynamicallyAccessedMembersAttribute' is not allowed on methods. It is allowed on method return value or method parameters though.", 2041, method, subcategory: MessageSubCategory.TrimAnalysis); } } for (int i = 0; i < method.Parameters.Count; i++) { var methodParameter = method.Parameters[i]; DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMembersAttribute(methodParameter, method); if (pa == DynamicallyAccessedMemberTypes.None) { continue; } if (!IsTypeInterestingForDataflow(methodParameter.ParameterType)) { _context.LogWarning( $"Parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage (methodParameter)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (methodParameter.Method)}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to parameters of type 'System.Type' or 'System.String'", 2098, method, subcategory: MessageSubCategory.TrimAnalysis); continue; } if (paramAnnotations == null) { paramAnnotations = new DynamicallyAccessedMemberTypes[method.Parameters.Count + offset]; } paramAnnotations[i + offset] = pa; } DynamicallyAccessedMemberTypes returnAnnotation = IsTypeInterestingForDataflow(method.ReturnType) ? GetMemberTypesForDynamicallyAccessedMembersAttribute(method.MethodReturnType, method) : DynamicallyAccessedMemberTypes.None; DynamicallyAccessedMemberTypes[] genericParameterAnnotations = null; if (method.HasGenericParameters) { for (int genericParameterIndex = 0; genericParameterIndex < method.GenericParameters.Count; genericParameterIndex++) { var genericParameter = method.GenericParameters[genericParameterIndex]; var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(genericParameter, method); if (annotation != DynamicallyAccessedMemberTypes.None) { if (genericParameterAnnotations == null) { genericParameterAnnotations = new DynamicallyAccessedMemberTypes[method.GenericParameters.Count]; } genericParameterAnnotations[genericParameterIndex] = annotation; } } } if (returnAnnotation != DynamicallyAccessedMemberTypes.None || paramAnnotations != null || genericParameterAnnotations != null) { annotatedMethods.Add(new MethodAnnotations(method, paramAnnotations, returnAnnotation, genericParameterAnnotations)); } } } // Next up are properties. Annotations on properties are kind of meta because we need to // map them to annotations on methods/fields. They're syntactic sugar - what they do is expressible // by placing attribute on the accessor/backing field. For complex properties, that's what people // will need to do anyway. Like so: // // [field: Attribute] // Type MyProperty { // [return: Attribute] // get; // [value: Attribute] // set; // } // if (type.HasProperties) { foreach (PropertyDefinition property in type.Properties) { DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(property); if (annotation == DynamicallyAccessedMemberTypes.None) { continue; } if (!IsTypeInterestingForDataflow(property.PropertyType)) { _context.LogWarning( $"Property '{property.GetDisplayName ()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to properties of type 'System.Type' or 'System.String'", 2099, property, subcategory: MessageSubCategory.TrimAnalysis); continue; } FieldDefinition backingFieldFromSetter = null; // Propagate the annotation to the setter method MethodDefinition setMethod = property.SetMethod; if (setMethod != null) { // Abstract property backing field propagation doesn't make sense, and any derived property will be validated // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. if (setMethod.HasBody) { // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn // that the field (which ever it is) must be annotated as well. ScanMethodBodyForFieldAccess(setMethod.Body, write: true, out backingFieldFromSetter); } if (annotatedMethods.Any(a => a.Method == setMethod)) { _context.LogWarning( $"'DynamicallyAccessedMembersAttribute' on property '{property.GetDisplayName ()}' conflicts with the same attribute on its accessor '{setMethod.GetDisplayName ()}'.", 2043, setMethod, subcategory: MessageSubCategory.TrimAnalysis); } else { int offset = setMethod.HasImplicitThis() ? 1 : 0; if (setMethod.Parameters.Count > 0) { DynamicallyAccessedMemberTypes[] paramAnnotations = new DynamicallyAccessedMemberTypes[setMethod.Parameters.Count + offset]; paramAnnotations[offset] = annotation; annotatedMethods.Add(new MethodAnnotations(setMethod, paramAnnotations, DynamicallyAccessedMemberTypes.None, null)); } } } FieldDefinition backingFieldFromGetter = null; // Propagate the annotation to the getter method MethodDefinition getMethod = property.GetMethod; if (getMethod != null) { // Abstract property backing field propagation doesn't make sense, and any derived property will be validated // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. if (getMethod.HasBody) { // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn // that the field (which ever it is) must be annotated as well. ScanMethodBodyForFieldAccess(getMethod.Body, write: false, out backingFieldFromGetter); } if (annotatedMethods.Any(a => a.Method == getMethod)) { _context.LogWarning( $"'DynamicallyAccessedMembersAttribute' on property '{property.GetDisplayName ()}' conflicts with the same attribute on its accessor '{getMethod.GetDisplayName ()}'.", 2043, getMethod, subcategory: MessageSubCategory.TrimAnalysis); } else { annotatedMethods.Add(new MethodAnnotations(getMethod, null, annotation, null)); } } FieldDefinition backingField; if (backingFieldFromGetter != null && backingFieldFromSetter != null && backingFieldFromGetter != backingFieldFromSetter) { _context.LogWarning( $"Could not find a unique backing field for property '{property.GetDisplayName ()}' to propagate 'DynamicallyAccessedMembersAttribute'.", 2042, property, subcategory: MessageSubCategory.TrimAnalysis); backingField = null; } else { backingField = backingFieldFromGetter ?? backingFieldFromSetter; } if (backingField != null) { if (annotatedFields.Any(a => a.Field == backingField)) { _context.LogWarning( $"'DynamicallyAccessedMemberAttribute' on property '{property.GetDisplayName ()}' conflicts with the same attribute on its backing field '{backingField.GetDisplayName ()}'.", 2056, backingField, subcategory: MessageSubCategory.TrimAnalysis); } else { annotatedFields.Add(new FieldAnnotation(backingField, annotation)); } } } } DynamicallyAccessedMemberTypes[] typeGenericParameterAnnotations = null; if (type.HasGenericParameters) { for (int genericParameterIndex = 0; genericParameterIndex < type.GenericParameters.Count; genericParameterIndex++) { var genericParameter = type.GenericParameters[genericParameterIndex]; var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(genericParameter, type); if (annotation != DynamicallyAccessedMemberTypes.None) { if (typeGenericParameterAnnotations == null) { typeGenericParameterAnnotations = new DynamicallyAccessedMemberTypes[type.GenericParameters.Count]; } typeGenericParameterAnnotations[genericParameterIndex] = annotation; } } } return(new TypeAnnotations(type, annotatedMethods.ToArray(), annotatedFields.ToArray(), typeGenericParameterAnnotations)); }
protected override TypeAnnotations CreateValueFromKey(TypeDesc key) { // We scan the entire type at this point; the reason for doing that is properties. // // We allow annotating properties, but those annotations need to flow onto individual get/set methods // and backing fields. Without scanning all properties, we can't answer questions about fields/methods. // And if we're going over all properties, we might as well go over everything else to keep things simple. Debug.Assert(key.IsTypeDefinition); if (key is not EcmaType ecmaType) { return(new TypeAnnotations(key, DynamicallyAccessedMemberTypes.None, null, null, null)); } MetadataReader reader = ecmaType.MetadataReader; // class, interface, struct can have annotations TypeDefinition typeDef = reader.GetTypeDefinition(ecmaType.Handle); DynamicallyAccessedMemberTypes typeAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, typeDef.GetCustomAttributes()); try { // Also inherit annotation from bases TypeDesc baseType = key.BaseType; while (baseType != null) { TypeDefinition baseTypeDef = reader.GetTypeDefinition(((EcmaType)baseType.GetTypeDefinition()).Handle); typeAnnotation |= GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, baseTypeDef.GetCustomAttributes()); baseType = baseType.BaseType; } // And inherit them from interfaces foreach (DefType runtimeInterface in key.RuntimeInterfaces) { TypeDefinition interfaceTypeDef = reader.GetTypeDefinition(((EcmaType)runtimeInterface.GetTypeDefinition()).Handle); typeAnnotation |= GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, interfaceTypeDef.GetCustomAttributes()); } } catch (TypeSystemException) { // If the class hierarchy is not walkable, just stop collecting the annotations. } var annotatedFields = new ArrayBuilder <FieldAnnotation>(); // First go over all fields with an explicit annotation foreach (EcmaField field in ecmaType.GetFields()) { FieldDefinition fieldDef = reader.GetFieldDefinition(field.Handle); DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, fieldDef.GetCustomAttributes()); if (annotation == DynamicallyAccessedMemberTypes.None) { continue; } if (!IsTypeInterestingForDataflow(field.FieldType)) { // Already know that there's a non-empty annotation on a field which is not System.Type/String and we're about to ignore it _logger.LogWarning(field, DiagnosticId.DynamicallyAccessedMembersOnFieldCanOnlyApplyToTypesOrStrings, field.GetDisplayName()); continue; } annotatedFields.Add(new FieldAnnotation(field, annotation)); } var annotatedMethods = new ArrayBuilder <MethodAnnotations>(); // Next go over all methods with an explicit annotation foreach (EcmaMethod method in ecmaType.GetMethods()) { DynamicallyAccessedMemberTypes[]? paramAnnotations = null; // We convert indices from metadata space to IL space here. // IL space assigns index 0 to the `this` parameter on instance methods. DynamicallyAccessedMemberTypes methodMemberTypes = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, reader.GetMethodDefinition(method.Handle).GetCustomAttributes()); MethodSignature signature; try { signature = method.Signature; } catch (TypeSystemException) { // If we cannot resolve things in the signature, just move along. continue; } int offset; if (!signature.IsStatic) { offset = 1; } else { offset = 0; } // If there's an annotation on the method itself and it's one of the special types (System.Type for example) // treat that annotation as annotating the "this" parameter. if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { if (IsTypeInterestingForDataflow(method.OwningType) && !signature.IsStatic) { paramAnnotations = new DynamicallyAccessedMemberTypes[signature.Length + offset]; paramAnnotations[0] = methodMemberTypes; } else { _logger.LogWarning(method, DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods); } } MethodDefinition methodDef = reader.GetMethodDefinition(method.Handle); ParameterHandleCollection parameterHandles = methodDef.GetParameters(); DynamicallyAccessedMemberTypes returnAnnotation = DynamicallyAccessedMemberTypes.None; foreach (ParameterHandle parameterHandle in parameterHandles) { Parameter parameter = reader.GetParameter(parameterHandle); if (parameter.SequenceNumber == 0) { // this is the return parameter returnAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, parameter.GetCustomAttributes()); if (returnAnnotation != DynamicallyAccessedMemberTypes.None && !IsTypeInterestingForDataflow(signature.ReturnType)) { _logger.LogWarning(method, DiagnosticId.DynamicallyAccessedMembersOnMethodReturnValueCanOnlyApplyToTypesOrStrings, method.GetDisplayName()); } } else { DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, parameter.GetCustomAttributes()); if (pa == DynamicallyAccessedMemberTypes.None) { continue; } if (!IsTypeInterestingForDataflow(signature[parameter.SequenceNumber - 1])) { _logger.LogWarning(method, DiagnosticId.DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings, DiagnosticUtilities.GetParameterNameForErrorMessage(method, parameter.SequenceNumber - 1), method.GetDisplayName()); continue; } if (paramAnnotations == null) { paramAnnotations = new DynamicallyAccessedMemberTypes[signature.Length + offset]; } paramAnnotations[parameter.SequenceNumber - 1 + offset] = pa; } } DynamicallyAccessedMemberTypes[]? genericParameterAnnotations = null; foreach (EcmaGenericParameter genericParameter in method.Instantiation) { GenericParameter genericParameterDef = reader.GetGenericParameter(genericParameter.Handle); var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, genericParameterDef.GetCustomAttributes()); if (annotation != DynamicallyAccessedMemberTypes.None) { if (genericParameterAnnotations == null) { genericParameterAnnotations = new DynamicallyAccessedMemberTypes[method.Instantiation.Length]; } genericParameterAnnotations[genericParameter.Index] = annotation; } } if (returnAnnotation != DynamicallyAccessedMemberTypes.None || paramAnnotations != null || genericParameterAnnotations != null) { annotatedMethods.Add(new MethodAnnotations(method, paramAnnotations, returnAnnotation, genericParameterAnnotations)); } } // Next up are properties. Annotations on properties are kind of meta because we need to // map them to annotations on methods/fields. They're syntactic sugar - what they do is expressible // by placing attribute on the accessor/backing field. For complex properties, that's what people // will need to do anyway. Like so: // // [field: Attribute] // Type MyProperty // { // [return: Attribute] // get; // [value: Attribute] // set; // } foreach (PropertyDefinitionHandle propertyHandle in reader.GetTypeDefinition(ecmaType.Handle).GetProperties()) { DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute( reader, reader.GetPropertyDefinition(propertyHandle).GetCustomAttributes()); if (annotation == DynamicallyAccessedMemberTypes.None) { continue; } PropertyPseudoDesc property = new PropertyPseudoDesc(ecmaType, propertyHandle); if (!IsTypeInterestingForDataflow(property.Signature.ReturnType)) { _logger.LogWarning(property, DiagnosticId.DynamicallyAccessedMembersOnPropertyCanOnlyApplyToTypesOrStrings, property.GetDisplayName()); continue; } FieldDesc?backingFieldFromSetter = null; // Propagate the annotation to the setter method MethodDesc setMethod = property.SetMethod; if (setMethod != null) { // Abstract property backing field propagation doesn't make sense, and any derived property will be validated // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. MethodIL methodBody = _ilProvider.GetMethodIL(setMethod); if (methodBody != null) { // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn // that the field (which ever it is) must be annotated as well. ScanMethodBodyForFieldAccess(methodBody, write: true, out backingFieldFromSetter); } if (annotatedMethods.Any(a => a.Method == setMethod)) { _logger.LogWarning(setMethod, DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor, property.GetDisplayName(), setMethod.GetDisplayName()); } else { int offset = setMethod.Signature.IsStatic ? 0 : 1; if (setMethod.Signature.Length > 0) { DynamicallyAccessedMemberTypes[] paramAnnotations = new DynamicallyAccessedMemberTypes[setMethod.Signature.Length + offset]; paramAnnotations[paramAnnotations.Length - 1] = annotation; annotatedMethods.Add(new MethodAnnotations(setMethod, paramAnnotations, DynamicallyAccessedMemberTypes.None, null)); } } } FieldDesc?backingFieldFromGetter = null; // Propagate the annotation to the getter method MethodDesc getMethod = property.GetMethod; if (getMethod != null) { // Abstract property backing field propagation doesn't make sense, and any derived property will be validated // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. MethodIL methodBody = _ilProvider.GetMethodIL(getMethod); if (methodBody != null) { // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn // that the field (which ever it is) must be annotated as well. ScanMethodBodyForFieldAccess(methodBody, write: false, out backingFieldFromGetter); } if (annotatedMethods.Any(a => a.Method == getMethod)) { _logger.LogWarning(getMethod, DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor, property.GetDisplayName(), getMethod.GetDisplayName()); } else { annotatedMethods.Add(new MethodAnnotations(getMethod, null, annotation, null)); } } FieldDesc?backingField; if (backingFieldFromGetter != null && backingFieldFromSetter != null && backingFieldFromGetter != backingFieldFromSetter) { _logger.LogWarning(property, DiagnosticId.DynamicallyAccessedMembersCouldNotFindBackingField, property.GetDisplayName()); backingField = null; } else { backingField = backingFieldFromGetter ?? backingFieldFromSetter; } if (backingField != null) { if (annotatedFields.Any(a => a.Field == backingField)) { _logger.LogWarning(backingField, DiagnosticId.DynamicallyAccessedMembersOnPropertyConflictsWithBackingField, property.GetDisplayName(), backingField.GetDisplayName()); } else { annotatedFields.Add(new FieldAnnotation(backingField, annotation)); } } } DynamicallyAccessedMemberTypes[]? typeGenericParameterAnnotations = null; foreach (EcmaGenericParameter genericParameter in ecmaType.Instantiation) { GenericParameter genericParameterDef = reader.GetGenericParameter(genericParameter.Handle); var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, genericParameterDef.GetCustomAttributes()); if (annotation != DynamicallyAccessedMemberTypes.None) { if (typeGenericParameterAnnotations == null) { typeGenericParameterAnnotations = new DynamicallyAccessedMemberTypes[ecmaType.Instantiation.Length]; } typeGenericParameterAnnotations[genericParameter.Index] = annotation; } } return(new TypeAnnotations(ecmaType, typeAnnotation, annotatedMethods.ToArray(), annotatedFields.ToArray(), typeGenericParameterAnnotations)); }
/// <summary> /// Bind and return a single type parameter constraint clause. /// </summary> private TypeParameterConstraintClause BindTypeParameterConstraints(TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, bool isForOverride, DiagnosticBag diagnostics) { var constraints = TypeParameterConstraintKind.None; ArrayBuilder <TypeWithAnnotations> constraintTypes = null; ArrayBuilder <TypeConstraintSyntax> syntaxBuilder = null; SeparatedSyntaxList <TypeParameterConstraintSyntax> constraintsSyntax = constraintClauseSyntax.Constraints; Debug.Assert(!InExecutableBinder); // Cannot eagerly report diagnostics handled by LazyMissingNonNullTypesContextDiagnosticInfo bool hasTypeLikeConstraint = false; bool reportedOverrideWithConstraints = false; if (!isForOverride) { constraintTypes = ArrayBuilder <TypeWithAnnotations> .GetInstance(); syntaxBuilder = ArrayBuilder <TypeConstraintSyntax> .GetInstance(); } for (int i = 0, n = constraintsSyntax.Count; i < n; i++) { var syntax = constraintsSyntax[i]; switch (syntax.Kind()) { case SyntaxKind.ClassConstraint: hasTypeLikeConstraint = true; if (i != 0) { diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation()); if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0) { continue; } } var constraintSyntax = (ClassOrStructConstraintSyntax)syntax; SyntaxToken questionToken = constraintSyntax.QuestionToken; if (questionToken.IsKind(SyntaxKind.QuestionToken)) { constraints |= TypeParameterConstraintKind.NullableReferenceType; if (isForOverride) { reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics); } else { LazyMissingNonNullTypesContextDiagnosticInfo.ReportNullableReferenceTypesIfNeeded(IsNullableEnabled(questionToken), questionToken.GetLocation(), diagnostics); } } else if (isForOverride || IsNullableEnabled(constraintSyntax.ClassOrStructKeyword)) { constraints |= TypeParameterConstraintKind.NotNullableReferenceType; } else { constraints |= TypeParameterConstraintKind.ReferenceType; } continue; case SyntaxKind.StructConstraint: hasTypeLikeConstraint = true; if (i != 0) { diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation()); if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0) { continue; } } constraints |= TypeParameterConstraintKind.ValueType; continue; case SyntaxKind.ConstructorConstraint: if (isForOverride) { reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics); continue; } if ((constraints & TypeParameterConstraintKind.ValueType) != 0) { diagnostics.Add(ErrorCode.ERR_NewBoundWithVal, syntax.GetFirstToken().GetLocation()); } if ((constraints & TypeParameterConstraintKind.Unmanaged) != 0) { diagnostics.Add(ErrorCode.ERR_NewBoundWithUnmanaged, syntax.GetFirstToken().GetLocation()); } if (i != n - 1) { diagnostics.Add(ErrorCode.ERR_NewBoundMustBeLast, syntax.GetFirstToken().GetLocation()); } constraints |= TypeParameterConstraintKind.Constructor; continue; case SyntaxKind.TypeConstraint: if (isForOverride) { reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics); } else { hasTypeLikeConstraint = true; var typeConstraintSyntax = (TypeConstraintSyntax)syntax; var typeSyntax = typeConstraintSyntax.Type; var typeSyntaxKind = typeSyntax.Kind(); // For pointer types, don't report this error. It is already reported during binding typeSyntax below. switch (typeSyntaxKind) { case SyntaxKind.PredefinedType: case SyntaxKind.PointerType: case SyntaxKind.NullableType: break; default: if (!SyntaxFacts.IsName(typeSyntax.Kind())) { diagnostics.Add(ErrorCode.ERR_BadConstraintType, typeSyntax.GetLocation()); } break; } var type = BindTypeOrUnmanagedKeyword(typeSyntax, diagnostics, out var isUnmanaged); if (isUnmanaged) { if (constraints != 0 || constraintTypes.Any()) { diagnostics.Add(ErrorCode.ERR_UnmanagedConstraintMustBeFirst, typeSyntax.GetLocation()); continue; } // This should produce diagnostics if the types are missing GetWellKnownType(WellKnownType.System_Runtime_InteropServices_UnmanagedType, diagnostics, typeSyntax); GetSpecialType(SpecialType.System_ValueType, diagnostics, typeSyntax); constraints |= TypeParameterConstraintKind.Unmanaged; continue; } constraintTypes.Add(type); syntaxBuilder.Add(typeConstraintSyntax); } continue; default: throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); } } if (!isForOverride && !hasTypeLikeConstraint && !IsNullableEnabled(typeParameterSyntax.Identifier)) { constraints |= TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType; } Debug.Assert(!isForOverride || (constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) != (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)); return(TypeParameterConstraintClause.Create(constraints, constraintTypes?.ToImmutableAndFree() ?? ImmutableArray <TypeWithAnnotations> .Empty, syntaxBuilder?.ToImmutableAndFree() ?? default));
/// <summary> /// The strategy of this rewrite is to do rewrite "locally". /// We analyze arguments of the concat in a shallow fashion assuming that /// lowering and optimizations (including this one) is already done for the arguments. /// Based on the arguments we select the most appropriate pattern for the current node. /// /// NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only /// local information - i.e. we look at the arguments, but we do not know about siblings. /// When we move to the parent, the node may be rewritten by this or some another optimization. /// /// Example: /// result = ( "abc" + "def" + null ?? expr1 + "moo" + "baz" ) + expr2 /// /// Will rewrite into: /// result = Concat("abcdef", expr2) /// /// However there will be transient nodes like Concat(expr1 + "moo") that will not be present in the /// resulting tree. /// /// </summary> private BoundExpression RewriteStringConcatenation(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type) { Debug.Assert( operatorKind == BinaryOperatorKind.StringConcatenation || operatorKind == BinaryOperatorKind.StringAndObjectConcatenation || operatorKind == BinaryOperatorKind.ObjectAndStringConcatenation); if (_inExpressionLambda) { return(RewriteStringConcatInExpressionLambda(syntax, operatorKind, loweredLeft, loweredRight, type)); } // Convert both sides to a string (calling ToString if necessary) loweredLeft = ConvertConcatExprToString(syntax, loweredLeft); loweredRight = ConvertConcatExprToString(syntax, loweredRight); Debug.Assert(loweredLeft.Type.IsStringType() || loweredLeft.ConstantValue?.IsNull == true || loweredLeft.Type.IsErrorType()); Debug.Assert(loweredRight.Type.IsStringType() || loweredRight.ConstantValue?.IsNull == true || loweredRight.Type.IsErrorType()); // try fold two args without flattening. var folded = TryFoldTwoConcatOperands(syntax, loweredLeft, loweredRight); if (folded != null) { return(folded); } // flatten and merge - ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2) ArrayBuilder <BoundExpression> leftFlattened = ArrayBuilder <BoundExpression> .GetInstance(); ArrayBuilder <BoundExpression> rightFlattened = ArrayBuilder <BoundExpression> .GetInstance(); FlattenConcatArg(loweredLeft, leftFlattened); FlattenConcatArg(loweredRight, rightFlattened); if (leftFlattened.Any() && rightFlattened.Any()) { folded = TryFoldTwoConcatOperands(syntax, leftFlattened.Last(), rightFlattened.First()); if (folded != null) { rightFlattened[0] = folded; leftFlattened.RemoveLast(); } } leftFlattened.AddRange(rightFlattened); rightFlattened.Free(); BoundExpression result; switch (leftFlattened.Count) { case 0: result = _factory.StringLiteral(string.Empty); break; case 1: // All code paths which reach here (through TryFoldTwoConcatOperands) have already called // RewriteStringConcatenationOneExpr if necessary result = leftFlattened[0]; break; case 2: var left = leftFlattened[0]; var right = leftFlattened[1]; result = RewriteStringConcatenationTwoExprs(syntax, left, right); break; case 3: { var first = leftFlattened[0]; var second = leftFlattened[1]; var third = leftFlattened[2]; result = RewriteStringConcatenationThreeExprs(syntax, first, second, third); } break; case 4: { var first = leftFlattened[0]; var second = leftFlattened[1]; var third = leftFlattened[2]; var fourth = leftFlattened[3]; result = RewriteStringConcatenationFourExprs(syntax, first, second, third, fourth); } break; default: result = RewriteStringConcatenationManyExprs(syntax, leftFlattened.ToImmutable()); break; } leftFlattened.Free(); return(result); }
/// <summary> /// The strategy of this rewrite is to do rewrite "locally". /// We analyze arguments of the concat in a shallow fashion assuming that /// lowering and optimizations (including this one) is already done for the arguments. /// Based on the arguments we select the most appropriate pattern for the current node. /// /// NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only /// local information - i.e. we look at the arguments, but we do not know about siblings. /// When we move to the parent, the node may be rewritten by this or some another optimization. /// /// Example: /// result = ( "abc" + "def" + null ?? expr1 + "moo" + "baz" ) + expr2 /// /// Will rewrite into: /// result = Concat("abcdef", expr2) /// /// However there will be transient nodes like Concat(expr1 + "moo") that will not be present in the /// resulting tree. /// /// </summary> private BoundExpression RewriteStringConcatenation(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type) { Debug.Assert( operatorKind == BinaryOperatorKind.StringConcatenation || operatorKind == BinaryOperatorKind.StringAndObjectConcatenation || operatorKind == BinaryOperatorKind.ObjectAndStringConcatenation); if (_inExpressionLambda) { return(RewriteStringConcatInExpressionLambda(syntax, operatorKind, loweredLeft, loweredRight, type)); } // avoid run time boxing and ToString operations if we can reasonably convert to a string at compile time loweredLeft = ConvertConcatExprToStringIfPossible(syntax, loweredLeft); loweredRight = ConvertConcatExprToStringIfPossible(syntax, loweredRight); // try fold two args without flattening. var folded = TryFoldTwoConcatOperands(syntax, loweredLeft, loweredRight); if (folded != null) { return(folded); } // flatten and merge - ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2) ArrayBuilder <BoundExpression> leftFlattened = ArrayBuilder <BoundExpression> .GetInstance(); ArrayBuilder <BoundExpression> rightFlattened = ArrayBuilder <BoundExpression> .GetInstance(); FlattenConcatArg(loweredLeft, leftFlattened); FlattenConcatArg(loweredRight, rightFlattened); if (leftFlattened.Any() && rightFlattened.Any()) { folded = TryFoldTwoConcatOperands(syntax, leftFlattened.Last(), rightFlattened.First()); if (folded != null) { rightFlattened[0] = folded; leftFlattened.RemoveLast(); } } leftFlattened.AddRange(rightFlattened); rightFlattened.Free(); BoundExpression result; switch (leftFlattened.Count) { case 0: result = _factory.StringLiteral(string.Empty); break; case 1: result = leftFlattened[0]; break; case 2: var left = leftFlattened[0]; var right = leftFlattened[1]; result = RewriteStringConcatenationTwoExprs(syntax, left, right); break; case 3: var first = leftFlattened[0]; var second = leftFlattened[1]; var third = leftFlattened[2]; result = RewriteStringConcatenationThreeExprs(syntax, first, second, third); break; default: result = RewriteStringConcatenationManyExprs(syntax, leftFlattened.ToImmutable()); break; } leftFlattened.Free(); return(result); }
internal static bool TryGetNames(TypeSymbol type, ArrayBuilder <string> namesBuilder) { type.VisitType((t, builder, _ignore) => AddNames(t, builder), namesBuilder); return(namesBuilder.Any(name => name != null)); }
private TypeAnnotations BuildTypeAnnotations(TypeDefinition type) { var annotatedFields = new ArrayBuilder <FieldAnnotation> (); // First go over all fields with an explicit annotation if (type.HasFields) { foreach (FieldDefinition field in type.Fields) { if (!IsTypeInterestingForDataflow(field.FieldType)) { continue; } DynamicallyAccessedMemberKinds annotation = _source.GetFieldAnnotation(field); if (annotation == 0) { continue; } annotatedFields.Add(new FieldAnnotation(field, annotation)); } } var annotatedMethods = new ArrayBuilder <MethodAnnotations> (); // Next go over all methods with an explicit annotation if (type.HasMethods) { foreach (MethodDefinition method in type.Methods) { DynamicallyAccessedMemberKinds [] paramAnnotations = null; // We convert indices from metadata space to IL space here. // IL space assigns index 0 to the `this` parameter on instance methods. int offset; if (method.HasImplicitThis()) { offset = 1; if (IsTypeInterestingForDataflow(method.DeclaringType)) { DynamicallyAccessedMemberKinds ta = _source.GetThisParameterAnnotation(method); if (ta != 0) { paramAnnotations = new DynamicallyAccessedMemberKinds [method.Parameters.Count + offset]; paramAnnotations [0] = ta; } } } else { offset = 0; } for (int i = 0; i < method.Parameters.Count; i++) { if (!IsTypeInterestingForDataflow(method.Parameters [i].ParameterType)) { continue; } DynamicallyAccessedMemberKinds pa = _source.GetParameterAnnotation(method, i); if (pa == 0) { continue; } if (paramAnnotations == null) { paramAnnotations = new DynamicallyAccessedMemberKinds [method.Parameters.Count + offset]; } paramAnnotations [i + offset] = pa; } DynamicallyAccessedMemberKinds returnAnnotation = IsTypeInterestingForDataflow(method.ReturnType) ? _source.GetReturnParameterAnnotation(method) : 0; if (returnAnnotation != 0 || paramAnnotations != null) { annotatedMethods.Add(new MethodAnnotations(method, paramAnnotations, returnAnnotation)); } } } // Next up are properties. Annotations on properties are kind of meta because we need to // map them to annotations on methods/fields. They're syntactic sugar - what they do is expressible // by placing attribute on the accessor/backing field. For complex properties, that's what people // will need to do anyway. Like so: // // [field: Attribute] // Type MyProperty { // [return: Attribute] // get; // [value: Attribute] // set; // } // if (type.HasProperties) { foreach (PropertyDefinition property in type.Properties) { if (!IsTypeInterestingForDataflow(property.PropertyType)) { continue; } DynamicallyAccessedMemberKinds annotation = _source.GetPropertyAnnotation(property); if (annotation == 0) { continue; } FieldDefinition backingFieldFromSetter = null; // Propagate the annotation to the setter method MethodDefinition setMethod = property.SetMethod; if (setMethod != null) { // TODO: Handle abstract properties - no way to propagate the annotation to the field if (!setMethod.HasBody || !ScanMethodBodyForFieldAccess(setMethod.Body, write: true, out backingFieldFromSetter)) { // TODO: warn we couldn't find a unique backing field } if (annotatedMethods.Any(a => a.Method == setMethod)) { // TODO: warn: duplicate annotation. not propagating. } else { int offset = setMethod.HasImplicitThis() ? 1 : 0; if (setMethod.Parameters.Count > 0) { DynamicallyAccessedMemberKinds [] paramAnnotations = new DynamicallyAccessedMemberKinds [setMethod.Parameters.Count + offset]; paramAnnotations [offset] = annotation; annotatedMethods.Add(new MethodAnnotations(setMethod, paramAnnotations, 0)); } } } FieldDefinition backingFieldFromGetter = null; // Propagate the annotation to the getter method MethodDefinition getMethod = property.GetMethod; if (getMethod != null) { // TODO: Handle abstract properties - no way to propagate the annotation to the field if (!getMethod.HasBody || !ScanMethodBodyForFieldAccess(getMethod.Body, write: false, out backingFieldFromGetter)) { // TODO: warn we couldn't find a unique backing field } if (annotatedMethods.Any(a => a.Method == getMethod)) { // TODO: warn: duplicate annotation. not propagating. } else { annotatedMethods.Add(new MethodAnnotations(getMethod, null, annotation)); } } FieldDefinition backingField; if (backingFieldFromGetter != null && backingFieldFromSetter != null && backingFieldFromGetter != backingFieldFromSetter) { // TODO: warn we couldn't find a unique backing field backingField = null; } else { backingField = backingFieldFromGetter ?? backingFieldFromSetter; } if (backingField != null) { if (annotatedFields.Any(a => a.Field == backingField)) { // TODO: warn about duplicate annotations } else { annotatedFields.Add(new FieldAnnotation(backingField, annotation)); } } } } return(new TypeAnnotations(annotatedMethods.ToArray(), annotatedFields.ToArray())); }
private async Task AppendFixesAsync( Document document, TextSpan span, IEnumerable<DiagnosticData> diagnostics, bool fixAllForInSpan, bool isBlocking, ArrayBuilder<CodeFixCollection> result, Func<string, IDisposable?> addOperationScope, CancellationToken cancellationToken) { var hasAnySharedFixer = _workspaceFixersMap.TryGetValue(document.Project.Language, out var fixerMap); var projectFixersMap = GetProjectFixers(document.Project); var hasAnyProjectFixer = projectFixersMap.Any(); if (!hasAnySharedFixer && !hasAnyProjectFixer) { return; } var allFixers = new List<CodeFixProvider>(); // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive var isInteractive = document.Project.Solution.Workspace.Kind == WorkspaceKind.Interactive; // gather CodeFixProviders for all distinct diagnostics found for current span foreach (var diagnosticId in diagnostics.Select(d => d.Id).Distinct()) { cancellationToken.ThrowIfCancellationRequested(); if (hasAnySharedFixer && fixerMap.Value.TryGetValue(diagnosticId, out var workspaceFixers)) { if (isInteractive) { allFixers.AddRange(workspaceFixers.Where(IsInteractiveCodeFixProvider)); } else { allFixers.AddRange(workspaceFixers); } } if (hasAnyProjectFixer && projectFixersMap.TryGetValue(diagnosticId, out var projectFixers)) { Debug.Assert(!isInteractive); allFixers.AddRange(projectFixers); } } var extensionManager = document.Project.Solution.Workspace.Services.GetService<IExtensionManager>(); // run each CodeFixProvider to gather individual CodeFixes for reported diagnostics foreach (var fixer in allFixers.Distinct()) { cancellationToken.ThrowIfCancellationRequested(); await AppendFixesOrConfigurationsAsync( document, span, diagnostics, fixAllForInSpan, result, fixer, hasFix: d => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id), getFixes: dxs => { var fixerName = fixer.GetType().Name; using (addOperationScope(fixerName)) using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixerName, cancellationToken)) { if (fixAllForInSpan) { var primaryDiagnostic = dxs.First(); return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, isBlocking, ImmutableArray.Create(primaryDiagnostic), cancellationToken); } else { return GetCodeFixesAsync(document, span, fixer, isBlocking, dxs, cancellationToken); } } }, cancellationToken: cancellationToken).ConfigureAwait(false); // Just need the first result if we are doing fix all in span if (fixAllForInSpan && result.Any()) return; } }