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; } } }
private void AddTypeParameters(MemberCrefSyntax memberSyntax, MultiDictionary <string, TypeParameterSymbol> map) { // Other members have arity 0. if (memberSyntax.Kind() == SyntaxKind.NameMemberCref) { AddTypeParameters(((NameMemberCrefSyntax)memberSyntax).Name, map); } }
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; }
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); }
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; } } }
/// <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); }
/// <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(); }
/// <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); } }
/// <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; }
/// <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; }