public static void AnalyzeXmlCrefAttribute(SyntaxNodeAnalysisContext context)
        {
            var xmlCrefAttribute = (XmlCrefAttributeSyntax)context.Node;

            CrefSyntax cref = xmlCrefAttribute.Cref;

            switch (cref?.Kind())
            {
            case SyntaxKind.NameMemberCref:
            {
                Analyze(context, cref, (NameMemberCrefSyntax)cref);
                break;
            }

            case SyntaxKind.QualifiedCref:
            {
                var qualifiedCref = (QualifiedCrefSyntax)cref;

                MemberCrefSyntax memberCref = qualifiedCref.Member;

                if (memberCref?.IsKind(SyntaxKind.NameMemberCref) != true)
                {
                    break;
                }

                Analyze(context, cref, (NameMemberCrefSyntax)memberCref);
                break;
            }
            }
        }
Ejemplo n.º 2
0
 private void AddTypeParameters(MemberCrefSyntax memberSyntax, MultiDictionary <string, TypeParameterSymbol> map)
 {
     // Other members have arity 0.
     if (memberSyntax.Kind() == SyntaxKind.NameMemberCref)
     {
         AddTypeParameters(((NameMemberCrefSyntax)memberSyntax).Name, map);
     }
 }
Ejemplo n.º 3
0
        private static CrefSyntax GetRootCrefSyntax(MemberCrefSyntax syntax)
        {
            SyntaxNode parentSyntax = syntax.Parent; // Could be null when speculating.

            return(parentSyntax == null || parentSyntax.IsKind(SyntaxKind.XmlCrefAttribute)
                ? syntax
                : (CrefSyntax)parentSyntax);
        }
 public new void AddChildren()
 {
     base.AddChildren();
     Kind                = Node.Kind();
     _container          = ((QualifiedCrefSyntax)Node).Container;
     _containerIsChanged = false;
     _dotToken           = ((QualifiedCrefSyntax)Node).DotToken;
     _dotTokenIsChanged  = false;
     _member             = ((QualifiedCrefSyntax)Node).Member;
     _memberIsChanged    = false;
 }
Ejemplo n.º 5
0
        private ImmutableArray <Symbol> BindMemberCref(MemberCrefSyntax syntax, NamespaceOrTypeSymbol containerOpt, out Symbol ambiguityWinner, DiagnosticBag diagnostics)
        {
            if ((object)containerOpt != null && containerOpt.Kind == SymbolKind.TypeParameter)
            {
                // As in normal lookup (see CreateErrorIfLookupOnTypeParameter), you can't dot into a type parameter
                // (though you can dot into an expression of type parameter type).
                CrefSyntax crefSyntax = GetRootCrefSyntax(syntax);
                var        noTrivia   = syntax.WithLeadingTrivia(null).WithTrailingTrivia(null);
                diagnostics.Add(ErrorCode.WRN_BadXMLRef, crefSyntax.Location, noTrivia.ToFullString());

                ambiguityWinner = null;
                return(ImmutableArray <Symbol> .Empty);
            }

            ImmutableArray <Symbol> result;

            switch (syntax.Kind)
            {
            case SyntaxKind.NameMemberCref:
                result = BindNameMemberCref((NameMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics);
                break;

            case SyntaxKind.IndexerMemberCref:
                result = BindIndexerMemberCref((IndexerMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics);
                break;

            case SyntaxKind.OperatorMemberCref:
                result = BindOperatorMemberCref((OperatorMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics);
                break;

            case SyntaxKind.ConversionOperatorMemberCref:
                result = BindConversionOperatorMemberCref((ConversionOperatorMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics);
                break;

            default:
                Debug.Assert(false, "Unexpected member cref kind " + syntax.Kind);
                ambiguityWinner = null;
                result          = ImmutableArray <Symbol> .Empty;
                break;
            }

            if (!result.Any())
            {
                CrefSyntax crefSyntax = GetRootCrefSyntax(syntax);
                var        noTrivia   = syntax.WithLeadingTrivia(null).WithTrailingTrivia(null);
                diagnostics.Add(ErrorCode.WRN_BadXMLRef, crefSyntax.Location, noTrivia.ToFullString());
            }

            return(result);
        }
Ejemplo n.º 6
0
        internal static void AnalyzeXmlCrefAttribute(SyntaxNodeAnalysisContext context)
        {
            var xmlCrefAttribute = (XmlCrefAttributeSyntax)context.Node;

            CrefSyntax cref = xmlCrefAttribute.Cref;

            switch (cref?.Kind())
            {
            case SyntaxKind.NameMemberCref:
            {
                var nameMemberCref = (NameMemberCrefSyntax)cref;

                TypeSyntax name = nameMemberCref.Name;

                if (name?.IsKind(SyntaxKind.PredefinedType) == false &&
                    IsFixable(context, name))
                {
                    ReportDiagnostic(context, cref);
                }

                break;
            }

            case SyntaxKind.QualifiedCref:
            {
                var qualifiedCref = (QualifiedCrefSyntax)cref;

                MemberCrefSyntax memberCref = qualifiedCref.Member;

                if (memberCref?.IsKind(SyntaxKind.NameMemberCref) == true)
                {
                    var nameMemberCref = (NameMemberCrefSyntax)memberCref;

                    TypeSyntax name = nameMemberCref.Name;

                    if (name != null &&
                        IsFixable(context, name))
                    {
                        ReportDiagnostic(context, cref);
                    }
                }

                break;
            }
            }
        }
Ejemplo n.º 7
0
        /// <remarks>
        /// Keep in sync with <see cref="M:SemanticModel.GetSpeculativelyBoundExpression()"/>.
        /// </remarks>
        private TypeSymbol BindCrefParameterOrReturnType(TypeSyntax typeSyntax, MemberCrefSyntax memberCrefSyntax, DiagnosticBag diagnostics)
        {
            DiagnosticBag unusedDiagnostics = DiagnosticBag.GetInstance(); // Examined, but not reported.

            // After much deliberation, we eventually decided to suppress lookup of inherited members within
            // crefs, in order to match dev11's behavior (Changeset #829014).  Unfortunately, it turns out
            // that dev11 does not suppress these members when performing lookup within parameter and return
            // types, within crefs (DevDiv #586815, #598371).
            Debug.Assert(InCrefButNotParameterOrReturnType);
            Binder parameterOrReturnTypeBinder = this.WithAdditionalFlags(BinderFlags.CrefParameterOrReturnType);

            // It would be nice to pull this binder out of the factory so we wouldn't have to worry about them getting out
            // of sync, but this code is also used for included crefs, which don't have BinderFactories.
            // As a compromise, we'll assert that the binding locations match in scenarios where we can go through the factory.
            Debug.Assert(!this.Compilation.ContainsSyntaxTree(typeSyntax.SyntaxTree) ||
                         this.Compilation.GetBinderFactory(typeSyntax.SyntaxTree).GetBinder(typeSyntax).Flags ==
                         (parameterOrReturnTypeBinder.Flags & ~BinderFlags.SemanticModel));

            TypeSymbol type = parameterOrReturnTypeBinder.BindType(typeSyntax, unusedDiagnostics);

            if (unusedDiagnostics.HasAnyErrors())
            {
                if (HasNonObsoleteError(unusedDiagnostics))
                {
                    ErrorCode code = typeSyntax.Parent.Kind == SyntaxKind.ConversionOperatorMemberCref
                        ? ErrorCode.WRN_BadXMLRefReturnType
                        : ErrorCode.WRN_BadXMLRefParamType;
                    CrefSyntax crefSyntax = GetRootCrefSyntax(memberCrefSyntax);
                    diagnostics.Add(code, crefSyntax.Location, typeSyntax.ToString(), crefSyntax.ToString());
                }
            }
            else
            {
                Debug.Assert(type.TypeKind != TypeKind.Error || typeSyntax.ContainsDiagnostics || !typeSyntax.SyntaxTree.ReportDocumentationCommentDiagnostics(), "Why wasn't there a diagnostic?");
            }

            unusedDiagnostics.Free();

            return(type);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Given a list of method and/or property candidates, choose the first one (if any) with a signature
        /// that matches the parameter list in the cref.  Return null if there isn't one.
        /// </summary>
        /// <remarks>
        /// Produces a diagnostic for ambiguous matches, but not for unresolved members - WRN_BadXMLRef is
        /// handled in BindMemberCref.
        /// </remarks>
        private static ImmutableArray <Symbol> PerformCrefOverloadResolution(ArrayBuilder <Symbol> candidates, ImmutableArray <ParameterSymbol> parameterSymbols, int arity, MemberCrefSyntax memberSyntax, out Symbol ambiguityWinner, DiagnosticBag diagnostics)
        {
            ArrayBuilder <Symbol> viable = null;

            foreach (Symbol candidate in candidates)
            {
                // BREAK: In dev11, any candidate with the type "dynamic" anywhere in its parameter list would be skipped
                // (see XmlDocCommentBinder::bindXmlReference).  Apparently, this was because "the params that the xml doc
                // comments produce never will."  This does not appear to have made sense in dev11 (skipping dropping the
                // candidate doesn't cause anything to blow up and may cause resolution to start succeeding) and it almost
                // certainly does not in roslyn (the signature comparer ignores the object-dynamic distiction anyway).

                Symbol signatureMember;
                switch (candidate.Kind)
                {
                case SymbolKind.Method:
                {
                    MethodSymbol candidateMethod         = (MethodSymbol)candidate;
                    MethodKind   candidateMethodKind     = candidateMethod.MethodKind;
                    bool         candidateMethodIsVararg = candidateMethod.IsVararg;

                    // If the arity from the cref is zero, then we accept methods of any arity.
                    int signatureMemberArity = candidateMethodKind == MethodKind.Constructor
                                ? 0
                                : (arity == 0 ? candidateMethod.Arity : arity);

                    // CONSIDER: we might want to reuse this method symbol (as long as the MethodKind and Vararg-ness match).
                    signatureMember = new SignatureOnlyMethodSymbol(
                        methodKind: candidateMethodKind,
                        typeParameters: IndexedTypeParameterSymbol.Take(signatureMemberArity),
                        parameters: parameterSymbols,
                        // This specific comparer only looks for varargs.
                        callingConvention: candidateMethodIsVararg ? Microsoft.Cci.CallingConvention.ExtraArguments : Microsoft.Cci.CallingConvention.HasThis,
                        // These are ignored by this specific MemberSignatureComparer.
                        containingType: null,
                        name: null,
                        returnType: null,
                        returnTypeCustomModifiers: ImmutableArray <CustomModifier> .Empty,
                        explicitInterfaceImplementations: ImmutableArray <MethodSymbol> .Empty);
                    break;
                }

                case SymbolKind.Property:
                {
                    // CONSIDER: we might want to reuse this property symbol.
                    signatureMember = new SignatureOnlyPropertySymbol(
                        parameters: parameterSymbols,
                        // These are ignored by this specific MemberSignatureComparer.
                        containingType: null,
                        name: null,
                        type: null,
                        typeCustomModifiers: ImmutableArray <CustomModifier> .Empty,
                        isStatic: false,
                        explicitInterfaceImplementations: ImmutableArray <PropertySymbol> .Empty);
                    break;
                }

                case SymbolKind.NamedType:
                    // Because we replaced them with constructors when we built the candidate list.
                    throw ExceptionUtilities.UnexpectedValue(candidate.Kind);

                default:
                    continue;
                }

                if (MemberSignatureComparer.CrefComparer.Equals(signatureMember, candidate))
                {
                    Debug.Assert(candidate.GetMemberArity() != 0 || candidate.Name == WellKnownMemberNames.InstanceConstructorName || arity == 0,
                                 "Can only have a 0-arity, non-constructor candidate if the desired arity is 0.");

                    if (viable == null)
                    {
                        viable = ArrayBuilder <Symbol> .GetInstance();

                        viable.Add(candidate);
                    }
                    else
                    {
                        bool oldArityIsZero = viable[0].GetMemberArity() == 0;
                        bool newArityIsZero = candidate.GetMemberArity() == 0;

                        // If the cref specified arity 0 and the current candidate has arity 0 but the previous
                        // match did not, then the current candidate is the unambiguous winner (unless there's
                        // another match with arity 0 in a subsequent iteration).
                        if (!oldArityIsZero || newArityIsZero)
                        {
                            if (!oldArityIsZero && newArityIsZero)
                            {
                                viable.Clear();
                            }

                            viable.Add(candidate);
                        }
                    }
                }
            }

            if (viable == null)
            {
                ambiguityWinner = null;
                return(ImmutableArray <Symbol> .Empty);
            }

            if (viable.Count > 1)
            {
                ambiguityWinner = viable[0];
                CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                diagnostics.Add(ErrorCode.WRN_AmbiguousXMLReference, crefSyntax.Location, crefSyntax.ToString(), ambiguityWinner, viable[1]);
            }
            else
            {
                ambiguityWinner = null;
            }

            return(viable.ToImmutableAndFree());
        }
        private ImmutableArray<Symbol> BindMemberCref(MemberCrefSyntax syntax, NamespaceOrTypeSymbol containerOpt, out Symbol ambiguityWinner, DiagnosticBag diagnostics)
        {
            if ((object)containerOpt != null && containerOpt.Kind == SymbolKind.TypeParameter)
            {
                // As in normal lookup (see CreateErrorIfLookupOnTypeParameter), you can't dot into a type parameter
                // (though you can dot into an expression of type parameter type).
                CrefSyntax crefSyntax = GetRootCrefSyntax(syntax);
                var noTrivia = syntax.WithLeadingTrivia(null).WithTrailingTrivia(null);
                diagnostics.Add(ErrorCode.WRN_BadXMLRef, crefSyntax.Location, noTrivia.ToFullString());

                ambiguityWinner = null;
                return ImmutableArray<Symbol>.Empty;
            }

            ImmutableArray<Symbol> result;
            switch (syntax.Kind)
            {
                case SyntaxKind.NameMemberCref:
                    result = BindNameMemberCref((NameMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics);
                    break;
                case SyntaxKind.IndexerMemberCref:
                    result = BindIndexerMemberCref((IndexerMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics);
                    break;
                case SyntaxKind.OperatorMemberCref:
                    result = BindOperatorMemberCref((OperatorMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics);
                    break;
                case SyntaxKind.ConversionOperatorMemberCref:
                    result = BindConversionOperatorMemberCref((ConversionOperatorMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics);
                    break;
                default:
                    Debug.Assert(false, "Unexpected member cref kind " + syntax.Kind);
                    ambiguityWinner = null;
                    result = ImmutableArray<Symbol>.Empty;
                    break;
            }

            if (!result.Any())
            {
                CrefSyntax crefSyntax = GetRootCrefSyntax(syntax);
                var noTrivia = syntax.WithLeadingTrivia(null).WithTrailingTrivia(null);
                diagnostics.Add(ErrorCode.WRN_BadXMLRef, crefSyntax.Location, noTrivia.ToFullString());
            }

            return result;
        }
 public TameMemberCrefSyntax(MemberCrefSyntax node)
 {
     Node = node;
     AddChildren();
 }
Ejemplo n.º 11
0
        /// <summary>
        /// At this point, we have a list of viable symbols and no parameter list with which to perform
        /// overload resolution.  We'll just return the first symbol, giving a diagnostic if there are
        /// others.
        /// Caveat: If there are multiple candidates and only one is from source, then the source symbol
        /// wins and no diagnostic is reported.
        /// </summary>
        private ImmutableArray <Symbol> ProcessParameterlessCrefMemberLookupResults(
            ImmutableArray <Symbol> symbols,
            int arity,
            MemberCrefSyntax memberSyntax,
            TypeArgumentListSyntax typeArgumentListSyntax,
            out Symbol ambiguityWinner,
            DiagnosticBag diagnostics)
        {
            // If the syntax indicates arity zero, then we match methods of any arity.
            // However, if there are both generic and non-generic methods, then the
            // generic methods should be ignored.
            if (symbols.Length > 1 && arity == 0)
            {
                bool hasNonGenericMethod = false;
                bool hasGenericMethod    = false;
                foreach (Symbol s in symbols)
                {
                    if (s.Kind != SymbolKind.Method)
                    {
                        continue;
                    }

                    if (((MethodSymbol)s).Arity == 0)
                    {
                        hasNonGenericMethod = true;
                    }
                    else
                    {
                        hasGenericMethod = true;
                    }

                    if (hasGenericMethod && hasNonGenericMethod)
                    {
                        break; //Nothing else to be learned.
                    }
                }

                if (hasNonGenericMethod && hasGenericMethod)
                {
                    symbols = symbols.WhereAsArray(s =>
                                                   s.Kind != SymbolKind.Method || ((MethodSymbol)s).Arity == 0);
                }
            }

            Debug.Assert(!symbols.IsEmpty);

            Symbol symbol = symbols[0];

            // If there's ambiguity, prefer source symbols.
            // Logic is similar to ResultSymbol, but separate because the error handling is totally different.
            if (symbols.Length > 1)
            {
                // Size is known, but IndexOfSymbolFromCurrentCompilation expects a builder.
                ArrayBuilder <Symbol> unwrappedSymbols = ArrayBuilder <Symbol> .GetInstance(symbols.Length);

                foreach (Symbol wrapped in symbols)
                {
                    unwrappedSymbols.Add(UnwrapAliasNoDiagnostics(wrapped));
                }

                BestSymbolInfo secondBest;
                BestSymbolInfo best = GetBestSymbolInfo(unwrappedSymbols, out secondBest);

                Debug.Assert(!best.IsNone);
                Debug.Assert(!secondBest.IsNone);

                unwrappedSymbols.Free();

                int symbolIndex = 0;

                if (best.IsFromCompilation)
                {
                    symbolIndex = best.Index;
                    symbol      = symbols[symbolIndex]; // NOTE: symbols, not unwrappedSymbols.
                }

                if (symbol.Kind == SymbolKind.TypeParameter)
                {
                    CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                    diagnostics.Add(ErrorCode.WRN_BadXMLRefTypeVar, crefSyntax.Location, crefSyntax.ToString());
                }
                else if (secondBest.IsFromCompilation == best.IsFromCompilation)
                {
                    CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                    int        otherIndex = symbolIndex == 0 ? 1 : 0;
                    diagnostics.Add(ErrorCode.WRN_AmbiguousXMLReference, crefSyntax.Location, crefSyntax.ToString(), symbol, symbols[otherIndex]);

                    ambiguityWinner = ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, symbol);
                    return(symbols.SelectAsArray(sym => ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, sym)));
                }
            }
            else if (symbol.Kind == SymbolKind.TypeParameter)
            {
                CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                diagnostics.Add(ErrorCode.WRN_BadXMLRefTypeVar, crefSyntax.Location, crefSyntax.ToString());
            }

            ambiguityWinner = null;
            return(ImmutableArray.Create <Symbol>(ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, symbol)));
        }
        /// <summary>
        /// Given a list of method and/or property candidates, choose the first one (if any) with a signature
        /// that matches the parameter list in the cref.  Return null if there isn't one.
        /// </summary>
        /// <remarks>
        /// Produces a diagnostic for ambiguous matches, but not for unresolved members - WRN_BadXMLRef is
        /// handled in BindMemberCref.
        /// </remarks>
        private static ImmutableArray<Symbol> PerformCrefOverloadResolution(ArrayBuilder<Symbol> candidates, ImmutableArray<ParameterSymbol> parameterSymbols, int arity, MemberCrefSyntax memberSyntax, out Symbol ambiguityWinner, DiagnosticBag diagnostics)
        {
            ArrayBuilder<Symbol> viable = null;

            foreach (Symbol candidate in candidates)
            {
                // BREAK: In dev11, any candidate with the type "dynamic" anywhere in its parameter list would be skipped
                // (see XmlDocCommentBinder::bindXmlReference).  Apparently, this was because "the params that the xml doc 
                // comments produce never will."  This does not appear to have made sense in dev11 (skipping dropping the
                // candidate doesn't cause anything to blow up and may cause resolution to start succeeding) and it almost
                // certainly does not in roslyn (the signature comparer ignores the object-dynamic distiction anyway).

                Symbol signatureMember;
                switch (candidate.Kind)
                {
                    case SymbolKind.Method:
                        {
                            MethodSymbol candidateMethod = (MethodSymbol)candidate;
                            MethodKind candidateMethodKind = candidateMethod.MethodKind;
                            bool candidateMethodIsVararg = candidateMethod.IsVararg;

                            // If the arity from the cref is zero, then we accept methods of any arity.
                            int signatureMemberArity = candidateMethodKind == MethodKind.Constructor
                                ? 0
                                : (arity == 0 ? candidateMethod.Arity : arity);

                            // CONSIDER: we might want to reuse this method symbol (as long as the MethodKind and Vararg-ness match).
                            signatureMember = new SignatureOnlyMethodSymbol(
                                methodKind: candidateMethodKind,
                                typeParameters: IndexedTypeParameterSymbol.Take(signatureMemberArity),
                                parameters: parameterSymbols,
                                // This specific comparer only looks for varargs.
                                callingConvention: candidateMethodIsVararg ? Microsoft.Cci.CallingConvention.ExtraArguments : Microsoft.Cci.CallingConvention.HasThis,
                                // These are ignored by this specific MemberSignatureComparer.
                                containingType: null,
                                name: null,
                                returnType: null,
                                returnTypeCustomModifiers: ImmutableArray<CustomModifier>.Empty,
                                explicitInterfaceImplementations: ImmutableArray<MethodSymbol>.Empty);
                            break;
                        }

                    case SymbolKind.Property:
                        {
                            // CONSIDER: we might want to reuse this property symbol.
                            signatureMember = new SignatureOnlyPropertySymbol(
                                parameters: parameterSymbols,
                                // These are ignored by this specific MemberSignatureComparer.
                                containingType: null,
                                name: null,
                                type: null,
                                typeCustomModifiers: ImmutableArray<CustomModifier>.Empty,
                                isStatic: false,
                                explicitInterfaceImplementations: ImmutableArray<PropertySymbol>.Empty);
                            break;
                        }

                    case SymbolKind.NamedType:
                        // Because we replaced them with constructors when we built the candidate list.
                        throw ExceptionUtilities.UnexpectedValue(candidate.Kind);

                    default:
                        continue;
                }

                if (MemberSignatureComparer.CrefComparer.Equals(signatureMember, candidate))
                {
                    Debug.Assert(candidate.GetMemberArity() != 0 || candidate.Name == WellKnownMemberNames.InstanceConstructorName || arity == 0,
                        "Can only have a 0-arity, non-constructor candidate if the desired arity is 0.");

                    if (viable == null)
                    {
                        viable = ArrayBuilder<Symbol>.GetInstance();
                        viable.Add(candidate);
                    }
                    else
                    {
                        bool oldArityIsZero = viable[0].GetMemberArity() == 0;
                        bool newArityIsZero = candidate.GetMemberArity() == 0;

                        // If the cref specified arity 0 and the current candidate has arity 0 but the previous
                        // match did not, then the current candidate is the unambiguous winner (unless there's
                        // another match with arity 0 in a subsequent iteration).
                        if (!oldArityIsZero || newArityIsZero)
                        {
                            if (!oldArityIsZero && newArityIsZero)
                            {
                                viable.Clear();
                            }

                            viable.Add(candidate);
                        }
                    }
                }
            }

            if (viable == null)
            {
                ambiguityWinner = null;
                return ImmutableArray<Symbol>.Empty;
            }

            if (viable.Count > 1)
            {
                ambiguityWinner = viable[0];
                CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                diagnostics.Add(ErrorCode.WRN_AmbiguousXMLReference, crefSyntax.Location, crefSyntax.ToString(), ambiguityWinner, viable[1]);
            }
            else
            {
                ambiguityWinner = null;
            }

            return viable.ToImmutableAndFree();
        }
        /// <summary>
        /// At this point, we have a list of viable symbols and no parameter list with which to perform
        /// overload resolution.  We'll just return the first symbol, giving a diagnostic if there are
        /// others.
        /// Caveat: If there are multiple candidates and only one is from source, then the source symbol
        /// wins and no diagnostic is reported.
        /// </summary>
        private ImmutableArray<Symbol> ProcessParameterlessCrefMemberLookupResults(
            ImmutableArray<Symbol> symbols,
            int arity,
            MemberCrefSyntax memberSyntax,
            TypeArgumentListSyntax typeArgumentListSyntax,
            out Symbol ambiguityWinner,
            DiagnosticBag diagnostics)
        {
            // If the syntax indicates arity zero, then we match methods of any arity.
            // However, if there are both generic and non-generic methods, then the
            // generic methods should be ignored.
            if (symbols.Length > 1 && arity == 0)
            {
                bool hasNonGenericMethod = false;
                bool hasGenericMethod = false;
                foreach (Symbol s in symbols)
                {
                    if (s.Kind != SymbolKind.Method)
                    {
                        continue;
                    }

                    if (((MethodSymbol)s).Arity == 0)
                    {
                        hasNonGenericMethod = true;
                    }
                    else
                    {
                        hasGenericMethod = true;
                    }

                    if (hasGenericMethod && hasNonGenericMethod)
                    {
                        break; //Nothing else to be learned.
                    }
                }

                if (hasNonGenericMethod && hasGenericMethod)
                {
                    symbols = symbols.WhereAsArray(s =>
                        s.Kind != SymbolKind.Method || ((MethodSymbol)s).Arity == 0);
                }
            }

            Debug.Assert(!symbols.IsEmpty);

            Symbol symbol = symbols[0];

            // If there's ambiguity, prefer source symbols.
            // Logic is similar to ResultSymbol, but separate because the error handling is totally different.
            if (symbols.Length > 1)
            {
                // Size is known, but IndexOfSymbolFromCurrentCompilation expects a builder.
                ArrayBuilder<Symbol> unwrappedSymbols = ArrayBuilder<Symbol>.GetInstance(symbols.Length);

                foreach (Symbol wrapped in symbols)
                {
                    unwrappedSymbols.Add(UnwrapAliasNoDiagnostics(wrapped));
                }

                BestSymbolInfo secondBest;
                BestSymbolInfo best = GetBestSymbolInfo(unwrappedSymbols, out secondBest);

                Debug.Assert(!best.IsNone);
                Debug.Assert(!secondBest.IsNone);

                unwrappedSymbols.Free();

                int symbolIndex = 0;

                if (best.IsFromCompilation)
                {
                    symbolIndex = best.Index;
                    symbol = symbols[symbolIndex]; // NOTE: symbols, not unwrappedSymbols.
                }

                if (symbol.Kind == SymbolKind.TypeParameter)
                {
                    CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                    diagnostics.Add(ErrorCode.WRN_BadXMLRefTypeVar, crefSyntax.Location, crefSyntax.ToString());
                }
                else if (secondBest.IsFromCompilation == best.IsFromCompilation)
                {
                    CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                    int otherIndex = symbolIndex == 0 ? 1 : 0;
                    diagnostics.Add(ErrorCode.WRN_AmbiguousXMLReference, crefSyntax.Location, crefSyntax.ToString(), symbol, symbols[otherIndex]);

                    ambiguityWinner = ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, symbol);
                    return symbols.SelectAsArray(sym => ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, sym));
                }
            }
            else if (symbol.Kind == SymbolKind.TypeParameter)
            {
                CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                diagnostics.Add(ErrorCode.WRN_BadXMLRefTypeVar, crefSyntax.Location, crefSyntax.ToString());
            }

            ambiguityWinner = null;
            return ImmutableArray.Create<Symbol>(ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, symbol));
        }
        /// <summary>
        /// Given a list of viable lookup results (based on the name, arity, and containing symbol),
        /// attempt to select one.
        /// </summary>
        private ImmutableArray<Symbol> ProcessCrefMemberLookupResults(
            ImmutableArray<Symbol> symbols,
            int arity,
            MemberCrefSyntax memberSyntax,
            TypeArgumentListSyntax typeArgumentListSyntax,
            BaseCrefParameterListSyntax parameterListSyntax,
            out Symbol ambiguityWinner,
            DiagnosticBag diagnostics)
        {
            Debug.Assert(!symbols.IsEmpty);

            if (parameterListSyntax == null)
            {
                return ProcessParameterlessCrefMemberLookupResults(symbols, arity, memberSyntax, typeArgumentListSyntax, out ambiguityWinner, diagnostics);
            }

            ArrayBuilder<Symbol> candidates = ArrayBuilder<Symbol>.GetInstance();
            GetCrefOverloadResolutionCandidates(symbols, arity, typeArgumentListSyntax, candidates);

            ImmutableArray<ParameterSymbol> parameterSymbols = BindCrefParameters(parameterListSyntax, diagnostics);
            ImmutableArray<Symbol> results = PerformCrefOverloadResolution(candidates, parameterSymbols, arity, memberSyntax, out ambiguityWinner, diagnostics);

            candidates.Free();

            // NOTE: This diagnostic is just a hint that might help fix a broken cref, so don't do
            // any work unless there are no viable candidates.
            if (results.Length == 0)
            {
                for (int i = 0; i < parameterSymbols.Length; i++)
                {
                    if (ContainsNestedTypeOfUnconstructedGenericType(parameterSymbols[i].Type))
                    {
                        // This warning is new in Roslyn, because our better-defined semantics for
                        // cref lookup disallow some things that were possible in dev12.
                        //
                        // Consider the following code:
                        //
                        //   public class C<T>
                        //   {
                        //       public class Inner { }
                        //   
                        //       public void M(Inner i) { }
                        //   
                        //       /// <see cref="M"/>
                        //       /// <see cref="C{T}.M"/>
                        //       /// <see cref="C{Q}.M"/>
                        //       /// <see cref="C{Q}.M(C{Q}.Inner)"/>
                        //       /// <see cref="C{Q}.M(Inner)"/> // WRN_UnqualifiedNestedTypeInCref
                        //       public void N() { }
                        //   }
                        //
                        // Dev12 binds all of the crefs as "M:C`1.M(C{`0}.Inner)".
                        // Roslyn accepts all but the last.  The issue is that the context for performing
                        // the lookup is not C<Q>, but C<T>.  Consequently, Inner binds to C<T>.Inner and
                        // then overload resolution fails because C<T>.Inner does not match C<Q>.Inner,
                        // the parameter type of C<Q>.M.  Since we could not agree that the old behavior
                        // was desirable (other than for backwards compatibility) and since mimicking it
                        // would have been expensive, we settled on introducing a new warning that at
                        // least hints to the user how then can work around the issue (i.e. by qualifying
                        // Inner as C{Q}.Inner).  Additional details are available in DevDiv #743425.
                        //
                        // CONSIDER: We could actually put the qualified form in the warning message,
                        // but that would probably just make it more frustrating (i.e. if the compiler
                        // knows exactly what I mean, why do I have to type it).
                        //
                        // NOTE: This is not a great location (whole parameter instead of problematic type),
                        // but it's better than nothing.
                        diagnostics.Add(ErrorCode.WRN_UnqualifiedNestedTypeInCref, parameterListSyntax.Parameters[i].Location);
                        break;
                    }
                }
            }

            return results;
        }
 private void AddTypeParameters(MemberCrefSyntax memberSyntax, MultiDictionary<string, TypeParameterSymbol> map)
 {
     // Other members have arity 0.
     if (memberSyntax.Kind() == SyntaxKind.NameMemberCref)
     {
         AddTypeParameters(((NameMemberCrefSyntax)memberSyntax).Name, map);
     }
 }
Ejemplo n.º 16
0
        /// <summary>
        /// Given a list of viable lookup results (based on the name, arity, and containing symbol),
        /// attempt to select one.
        /// </summary>
        private ImmutableArray <Symbol> ProcessCrefMemberLookupResults(
            ImmutableArray <Symbol> symbols,
            int arity,
            MemberCrefSyntax memberSyntax,
            TypeArgumentListSyntax typeArgumentListSyntax,
            BaseCrefParameterListSyntax parameterListSyntax,
            out Symbol ambiguityWinner,
            DiagnosticBag diagnostics)
        {
            Debug.Assert(!symbols.IsEmpty);

            if (parameterListSyntax == null)
            {
                return(ProcessParameterlessCrefMemberLookupResults(symbols, arity, memberSyntax, typeArgumentListSyntax, out ambiguityWinner, diagnostics));
            }

            ArrayBuilder <Symbol> candidates = ArrayBuilder <Symbol> .GetInstance();

            GetCrefOverloadResolutionCandidates(symbols, arity, typeArgumentListSyntax, candidates);

            ImmutableArray <ParameterSymbol> parameterSymbols = BindCrefParameters(parameterListSyntax, diagnostics);
            ImmutableArray <Symbol>          results          = PerformCrefOverloadResolution(candidates, parameterSymbols, arity, memberSyntax, out ambiguityWinner, diagnostics);

            candidates.Free();

            // NOTE: This diagnostic is just a hint that might help fix a broken cref, so don't do
            // any work unless there are no viable candidates.
            if (results.Length == 0)
            {
                for (int i = 0; i < parameterSymbols.Length; i++)
                {
                    if (ContainsNestedTypeOfUnconstructedGenericType(parameterSymbols[i].Type.TypeSymbol))
                    {
                        // This warning is new in Roslyn, because our better-defined semantics for
                        // cref lookup disallow some things that were possible in dev12.
                        //
                        // Consider the following code:
                        //
                        //   public class C<T>
                        //   {
                        //       public class Inner { }
                        //
                        //       public void M(Inner i) { }
                        //
                        //       /// <see cref="M"/>
                        //       /// <see cref="C{T}.M"/>
                        //       /// <see cref="C{Q}.M"/>
                        //       /// <see cref="C{Q}.M(C{Q}.Inner)"/>
                        //       /// <see cref="C{Q}.M(Inner)"/> // WRN_UnqualifiedNestedTypeInCref
                        //       public void N() { }
                        //   }
                        //
                        // Dev12 binds all of the crefs as "M:C`1.M(C{`0}.Inner)".
                        // Roslyn accepts all but the last.  The issue is that the context for performing
                        // the lookup is not C<Q>, but C<T>.  Consequently, Inner binds to C<T>.Inner and
                        // then overload resolution fails because C<T>.Inner does not match C<Q>.Inner,
                        // the parameter type of C<Q>.M.  Since we could not agree that the old behavior
                        // was desirable (other than for backwards compatibility) and since mimicking it
                        // would have been expensive, we settled on introducing a new warning that at
                        // least hints to the user how then can work around the issue (i.e. by qualifying
                        // Inner as C{Q}.Inner).  Additional details are available in DevDiv #743425.
                        //
                        // CONSIDER: We could actually put the qualified form in the warning message,
                        // but that would probably just make it more frustrating (i.e. if the compiler
                        // knows exactly what I mean, why do I have to type it).
                        //
                        // NOTE: This is not a great location (whole parameter instead of problematic type),
                        // but it's better than nothing.
                        diagnostics.Add(ErrorCode.WRN_UnqualifiedNestedTypeInCref, parameterListSyntax.Parameters[i].Location);
                        break;
                    }
                }
            }

            return(results);
        }
        /// <remarks>
        /// Keep in sync with <see cref="M:SemanticModel.GetSpeculativelyBoundExpression()"/>.
        /// </remarks>
        private TypeSymbol BindCrefParameterOrReturnType(TypeSyntax typeSyntax, MemberCrefSyntax memberCrefSyntax, DiagnosticBag diagnostics)
        {
            DiagnosticBag unusedDiagnostics = DiagnosticBag.GetInstance(); // Examined, but not reported.

            // After much deliberation, we eventually decided to suppress lookup of inherited members within
            // crefs, in order to match dev11's behavior (Changeset #829014).  Unfortunately, it turns out
            // that dev11 does not suppress these members when performing lookup within parameter and return
            // types, within crefs (DevDiv #586815, #598371).
            Debug.Assert(InCrefButNotParameterOrReturnType);
            Binder parameterOrReturnTypeBinder = this.WithAdditionalFlags(BinderFlags.CrefParameterOrReturnType);

            // It would be nice to pull this binder out of the factory so we wouldn't have to worry about them getting out
            // of sync, but this code is also used for included crefs, which don't have BinderFactories.
            // As a compromise, we'll assert that the binding locations match in scenarios where we can go through the factory.
            Debug.Assert(!this.Compilation.ContainsSyntaxTree(typeSyntax.SyntaxTree) ||
                this.Compilation.GetBinderFactory(typeSyntax.SyntaxTree).GetBinder(typeSyntax).Flags ==
                (parameterOrReturnTypeBinder.Flags & ~BinderFlags.SemanticModel));

            TypeSymbol type = parameterOrReturnTypeBinder.BindType(typeSyntax, unusedDiagnostics);

            if (unusedDiagnostics.HasAnyErrors())
            {
                if (HasNonObsoleteError(unusedDiagnostics))
                {
                    ErrorCode code = typeSyntax.Parent.Kind == SyntaxKind.ConversionOperatorMemberCref
                        ? ErrorCode.WRN_BadXMLRefReturnType
                        : ErrorCode.WRN_BadXMLRefParamType;
                    CrefSyntax crefSyntax = GetRootCrefSyntax(memberCrefSyntax);
                    diagnostics.Add(code, typeSyntax.Location, typeSyntax.ToString(), crefSyntax.ToString());
                }
            }
            else
            {
                Debug.Assert(type.TypeKind != TypeKind.Error || typeSyntax.ContainsDiagnostics || !typeSyntax.SyntaxTree.ReportDocumentationCommentDiagnostics(), "Why wasn't there a diagnostic?");
            }

            unusedDiagnostics.Free();

            return type;
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Given a list of method and/or property candidates, choose the first one (if any) with a signature
        /// that matches the parameter list in the cref.  Return null if there isn't one.
        /// </summary>
        /// <remarks>
        /// Produces a diagnostic for ambiguous matches, but not for unresolved members - WRN_BadXMLRef is
        /// handled in BindMemberCref.
        /// </remarks>
        private static ImmutableArray <Symbol> PerformCrefOverloadResolution(ArrayBuilder <Symbol> candidates, ImmutableArray <ParameterSymbol> parameterSymbols, int arity, MemberCrefSyntax memberSyntax, out Symbol ambiguityWinner, DiagnosticBag diagnostics)
        {
            ArrayBuilder <Symbol> viable = null;

            foreach (Symbol candidate in candidates)
            {
                // BREAK: In dev11, any candidate with the type "dynamic" anywhere in its parameter list would be skipped
                // (see XmlDocCommentBinder::bindXmlReference).  Apparently, this was because "the params that the xml doc
                // comments produce never will."  This does not appear to have made sense in dev11 (skipping dropping the
                // candidate doesn't cause anything to blow up and may cause resolution to start succeeding) and it almost
                // certainly does not in roslyn (the signature comparer ignores the object-dynamic distinction anyway).

                Symbol signatureMember;
                switch (candidate.Kind)
                {
                case SymbolKind.Method:
                {
                    MethodSymbol candidateMethod         = (MethodSymbol)candidate;
                    MethodKind   candidateMethodKind     = candidateMethod.MethodKind;
                    bool         candidateMethodIsVararg = candidateMethod.IsVararg;

                    // If the arity from the cref is zero, then we accept methods of any arity.
                    int signatureMemberArity = candidateMethodKind == MethodKind.Constructor
                                ? 0
                                : (arity == 0 ? candidateMethod.Arity : arity);

                    // CONSIDER: we might want to reuse this method symbol (as long as the MethodKind and Vararg-ness match).
                    signatureMember = new SignatureOnlyMethodSymbol(
                        methodKind: candidateMethodKind,
                        typeParameters: IndexedTypeParameterSymbol.Take(signatureMemberArity),
                        parameters: parameterSymbols,
                        // This specific comparer only looks for varargs.
                        callingConvention: candidateMethodIsVararg ? Microsoft.Cci.CallingConvention.ExtraArguments : Microsoft.Cci.CallingConvention.HasThis,
                        // These are ignored by this specific MemberSignatureComparer.
                        containingType: null,
                        name: null,
                        refKind: RefKind.None,
                        returnType: default,
                        refCustomModifiers: ImmutableArray <CustomModifier> .Empty,
                        explicitInterfaceImplementations: ImmutableArray <MethodSymbol> .Empty);
                    break;
                }
 private static CrefSyntax GetRootCrefSyntax(MemberCrefSyntax syntax)
 {
     SyntaxNode parentSyntax = syntax.Parent; // Could be null when speculating.
     return parentSyntax == null || parentSyntax.IsKind(SyntaxKind.XmlCrefAttribute)
         ? syntax
         : (CrefSyntax)parentSyntax;
 }