| File: Symbols\Source\CrefTypeParameterSymbol.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// <summary> /// Type parameters in documentation comments are complicated since they sort of act as declarations, /// rather than references. Consider the following example: /// /// <![CDATA[ /// /// <summary>See <see cref="B{U}.M(U)" />.</summary> /// class B<T> { void M(T t) { } } /// ]]> /// /// We make some key observations: /// 1) The type parameter name in the cref is not tied to the type parameter name in the type declaration. /// 2) A relationship exists between the two occurrences of "U" in the cref: they both refer to (or define) /// the same symbol. /// /// In Roslyn, we've decided on the following representation: within the (entire) scope of a cref, the names /// of all type parameters "declared" in the cref are in scope and bind to the corresponding type parameters. /// This representation has one major advantage: as long as the appropriate binder (i.e. the one that knows /// about the implicitly-declared type parameters) is used, TypeSyntaxes within the cref can be bound by /// calling BindType. In addition to eliminating the necessity for custom binding code in the batch case, /// this reduces the problem of exposing such nodes in the SemanticModel to one of ensuring that the right /// enclosing binder is chosen. That is, new code will have to be written to handle CrefSyntaxes, but the /// existing code for TypeSyntaxes should just work! /// /// In the example above, this means that, between the cref quotation marks, the name "U" binds to an /// implicitly declared type parameter, whether it is in "B{U}", "M{U}", or "M{List{U[]}}". /// /// Of course, it's not all gravy. One thing we're giving up by using this representation is the ability to /// distinguish between "declared" type parameters with the same name. Consider the following example: /// /// <![CDATA[ /// <summary>See <see cref=""A{T, T}.M(T)""/>.</summary> /// class A<T, U> /// { /// void M(T t) { } /// void M(U u) { } /// } /// ]]> /// </summary> /// /// The native compiler interprets this in the same way as it would interpret A{T1, T2}.M(T2) and unambiguously /// (i.e. without a warning) binds to A{T, U}.M(U). Since Roslyn does not distinguish between the T's, Roslyn /// reports an ambiguity warning and picks the first method. Furthermore, renaming one 'T' will rename all of /// them. /// /// This class represents such an implicitly declared type parameter. The declaring syntax is expected to be /// an IdentifierNameSyntax in the type argument list of a QualifiedNameSyntax. internal sealed class CrefTypeParameterSymbol : TypeParameterSymbol { private readonly string _name; private readonly int _ordinal; private readonly SyntaxReference _declaringSyntax; public CrefTypeParameterSymbol(string name, int ordinal, IdentifierNameSyntax declaringSyntax) { _name = name; _ordinal = ordinal; _declaringSyntax = declaringSyntax.GetReference(); } public override TypeParameterKind TypeParameterKind { get { return TypeParameterKind.Cref; } } public override string Name { get { return _name; } } public override int Ordinal { get { return _ordinal; } } internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison) { if (ReferenceEquals(this, t2)) { return true; } if ((object)t2 == null) { return false; } CrefTypeParameterSymbol other = t2 as CrefTypeParameterSymbol; return (object)other != null && other._name == _name && other._ordinal == _ordinal && other._declaringSyntax.GetSyntax() == _declaringSyntax.GetSyntax(); } public override int GetHashCode() { return Hash.Combine(_name, _ordinal); } public override VarianceKind Variance { get { return VarianceKind.None; } } public override bool HasValueTypeConstraint { get { return false; } } public override bool AllowsRefLikeType { get { return false; } } public override bool IsValueTypeFromConstraintTypes { get { return false; } } public override bool HasReferenceTypeConstraint { get { return false; } } public override bool IsReferenceTypeFromConstraintTypes { get { return false; } } internal override bool? ReferenceTypeConstraintIsNullable { get { return false; } } public override bool HasNotNullConstraint => false; internal override bool? IsNotNullable => null; public override bool HasUnmanagedTypeConstraint { get { return false; } } public override bool HasConstructorConstraint { get { return false; } } public override Symbol ContainingSymbol { get { return null; } } public override ImmutableArray<Location> Locations { get { return ImmutableArray.Create<Location>(_declaringSyntax.GetLocation()); } } public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences { get { return ImmutableArray.Create<SyntaxReference>(_declaringSyntax); } } internal override void EnsureAllConstraintsAreResolved() { } internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsList<TypeParameterSymbol> inProgress) { return ImmutableArray<TypeWithAnnotations>.Empty; } internal override ImmutableArray<NamedTypeSymbol> GetInterfaces(ConsList<TypeParameterSymbol> inProgress) { return ImmutableArray<NamedTypeSymbol>.Empty; } internal override NamedTypeSymbol GetEffectiveBaseClass(ConsList<TypeParameterSymbol> inProgress) { // Constraints are not checked in crefs, so this should never be examined. throw ExceptionUtilities.Unreachable(); } internal override TypeSymbol GetDeducedBaseType(ConsList<TypeParameterSymbol> inProgress) { // Constraints are not checked in crefs, so this should never be examined. throw ExceptionUtilities.Unreachable(); } public override bool IsImplicitlyDeclared { get { return false; } } } }