示例#1
0
        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());
        }
示例#3
0
            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);
            }
示例#4
0
 /// <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);
     }
 }
示例#5
0
 /// <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));
     }
 }
示例#6
0
        /// <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());
        }
示例#8
0
            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);
                }
            }
示例#9
0
        /// <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&lt;EventHandler&gt;
        ///     (new Func&lt;EventHandler, EventRegistrationToken&gt;(@object.add),
        ///      new Action&lt;EventRegistrationToken&gt;(@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));
        }
示例#10
0
            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;
            }
示例#12
0
        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());
        }
示例#13
0
        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));
        }
示例#14
0
            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));
            }
示例#15
0
        /// <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));
示例#16
0
        /// <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);
        }
示例#17
0
        /// <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));
 }
示例#19
0
        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()));
        }
示例#20
0
        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;
            }
        }