From 15fdb0bcb5feb142d59119b4c36006c8c69a8496 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 22 May 2025 15:31:04 -0700 Subject: [PATCH 01/10] Extensions: allow cref references to extension members --- .../CSharp/Portable/Binder/Binder_Crefs.cs | 135 ++ .../Binder/WithCrefTypeParametersBinder.cs | 18 +- .../Compilation/CSharpSemanticModel.cs | 4 +- ...cumentationCommentIDVisitor.PartVisitor.cs | 8 +- .../Portable/Generated/CSharp.Generated.g4 | 5 + .../Syntax.xml.Internal.Generated.cs | 145 ++ .../Syntax.xml.Main.Generated.cs | 27 + .../Syntax.xml.Syntax.Generated.cs | 74 + .../Parser/DocumentationCommentParser.cs | 39 +- .../CSharp/Portable/PublicAPI.Unshipped.txt | 23 + .../Source/SourceMemberContainerSymbol.cs | 83 +- .../CSharp/Portable/Syntax/Syntax.xml | 14 + .../CSharp/Portable/Syntax/SyntaxKind.cs | 1 + .../Test/Emit3/Semantics/ExtensionTests.cs | 123 +- .../Test/Emit3/Semantics/ExtensionTests2.cs | 1440 +++++++++++++++++ .../Generated/Syntax.Test.xml.Generated.cs | 86 + .../Test/Syntax/Parsing/CrefParsingTests.cs | 459 ++++++ .../Apis/Microsoft.CodeAnalysis.CSharp.txt | 23 + 18 files changed, 2637 insertions(+), 70 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs index 016c4f2a35392..68d5a0a6a302f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs @@ -35,6 +35,7 @@ private ImmutableArray BindCrefInternal(CrefSyntax syntax, out Symbol? a case SyntaxKind.IndexerMemberCref: case SyntaxKind.OperatorMemberCref: case SyntaxKind.ConversionOperatorMemberCref: + case SyntaxKind.ExtensionMemberCref: return BindMemberCref((MemberCrefSyntax)syntax, containerOpt: null, ambiguityWinner: out ambiguityWinner, diagnostics: diagnostics); default: throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); @@ -125,6 +126,9 @@ private ImmutableArray BindMemberCref(MemberCrefSyntax syntax, Namespace case SyntaxKind.ConversionOperatorMemberCref: result = BindConversionOperatorMemberCref((ConversionOperatorMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics); break; + case SyntaxKind.ExtensionMemberCref: + result = BindExtensionMemberCref((ExtensionMemberCrefSyntax)syntax, containerOpt, out ambiguityWinner, diagnostics); + break; default: throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); } @@ -216,6 +220,137 @@ private ImmutableArray BindIndexerMemberCref(IndexerMemberCrefSyntax syn diagnostics: diagnostics); } + private ImmutableArray BindExtensionMemberCref(ExtensionMemberCrefSyntax syntax, NamespaceOrTypeSymbol? containerOpt, out Symbol? ambiguityWinner, BindingDiagnosticBag diagnostics) + { + if (containerOpt is not NamedTypeSymbol namedContainer) + { + ambiguityWinner = null; + return ImmutableArray.Empty; + } + + ImmutableArray sortedSymbols = default; + int arity = 0; + TypeArgumentListSyntax? typeArgumentListSyntax = null; + CrefParameterListSyntax? parameters = null; + + if (syntax.Member is NameMemberCrefSyntax { Name: SimpleNameSyntax simpleName } nameMember) + { + arity = simpleName.Arity; + typeArgumentListSyntax = simpleName is GenericNameSyntax genericName ? genericName.TypeArgumentList : null; + parameters = nameMember.Parameters; + + TypeArgumentListSyntax? extensionTypeArguments = syntax.TypeArgumentList; + int extensionArity = extensionTypeArguments?.Arguments.Count ?? 0; + sortedSymbols = computeSortedAndFilteredCrefExtensionMembers(namedContainer, simpleName.Identifier.ValueText, extensionArity, arity, extensionTypeArguments, diagnostics, syntax); + } + + if (sortedSymbols.IsDefaultOrEmpty) + { + ambiguityWinner = null; + return []; + } + + Debug.Assert(sortedSymbols.All(s => s.GetIsNewExtensionMember())); + + return ProcessCrefMemberLookupResults(sortedSymbols, arity, syntax, typeArgumentListSyntax, parameters, out ambiguityWinner, diagnostics); + + ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSymbol container, string name, int extensionArity, int arity, TypeArgumentListSyntax? extensionTypeArguments, BindingDiagnosticBag diagnostics, ExtensionMemberCrefSyntax syntax) + { + Debug.Assert(name is not null); + + LookupOptions options = LookupOptions.AllMethodsOnArityZero | LookupOptions.MustNotBeParameter; + CompoundUseSiteInfo useSiteInfo = this.GetNewCompoundUseSiteInfo(diagnostics); + ArrayBuilder? sortedSymbolsBuilder = null; + + foreach (var nested in container.GetTypeMembers()) + { + if (!nested.IsExtension || nested.Arity != extensionArity || nested.ExtensionParameter is null) + { + continue; + } + + var constructedNested = (NamedTypeSymbol)ConstructWithCrefTypeParameters(extensionArity, extensionTypeArguments, nested); + + var candidateExtensionSignature = new SignatureOnlyMethodSymbol( + methodKind: MethodKind.Ordinary, + typeParameters: IndexedTypeParameterSymbol.TakeSymbols(constructedNested.Arity), + parameters: [constructedNested.ExtensionParameter], + callingConvention: Cci.CallingConvention.Default, + // These are ignored by this specific MemberSignatureComparer. + containingType: null, + name: null, + refKind: RefKind.None, + isInitOnly: false, + isStatic: false, + returnType: default, + refCustomModifiers: [], + explicitInterfaceImplementations: []); + + ImmutableArray extensionParameterSymbols = syntax.Parameters is { } extensionParameterListSyntax ? BindCrefParameters(extensionParameterListSyntax, diagnostics) : default; + + var providedExtensionSignature = new SignatureOnlyMethodSymbol( + methodKind: MethodKind.Ordinary, + typeParameters: IndexedTypeParameterSymbol.TakeSymbols(constructedNested.Arity), + parameters: extensionParameterSymbols, + callingConvention: Cci.CallingConvention.Default, + // These are ignored by this specific MemberSignatureComparer. + containingType: null, + name: null, + refKind: RefKind.None, + isInitOnly: false, + isStatic: false, + returnType: default, + refCustomModifiers: [], + explicitInterfaceImplementations: []); + + if (!MemberSignatureComparer.CrefComparer.Equals(candidateExtensionSignature, providedExtensionSignature)) + { + continue; + } + + var candidates = constructedNested.GetMembers(name); + + foreach (var candidate in candidates) + { + if (!SourceMemberContainerTypeSymbol.IsAllowedExtensionMember(candidate)) + { + continue; + } + + if (arity != 0 && candidate.GetArity() != arity) + { + continue; + } + + // Note: we bypass the arity check here, as it would check for total arity (extension + member arity) + SingleLookupResult result = this.CheckViability(candidate, arity: 0, options, accessThroughType: null, diagnose: true, useSiteInfo: ref useSiteInfo); + + if (result.Kind == LookupResultKind.Viable) + { + sortedSymbolsBuilder ??= ArrayBuilder.GetInstance(); + sortedSymbolsBuilder.Add(result.Symbol); + } + } + } + + diagnostics.Add(syntax, useSiteInfo); + + if (sortedSymbolsBuilder is null) + { + return ImmutableArray.Empty; + } + + // Since we resolve ambiguities by just picking the first symbol we encounter, + // the order of the symbols matters for repeatability. + if (sortedSymbolsBuilder.Count > 1) + { + sortedSymbolsBuilder.Sort(ConsistentSymbolOrder.Instance); + } + + return sortedSymbolsBuilder.ToImmutableAndFree(); + } + } + // NOTE: not guaranteed to be a method (e.g. class op_Addition) // NOTE: constructor fallback logic applies private ImmutableArray BindOperatorMemberCref(OperatorMemberCrefSyntax syntax, NamespaceOrTypeSymbol? containerOpt, out Symbol? ambiguityWinner, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs index 92a84b73a5f3f..556e32c56212a 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs @@ -90,7 +90,7 @@ private void AddTypeParameters(TypeSyntax typeSyntax, MultiDictionary map) { // Other members have arity 0. - if (memberSyntax.Kind() == SyntaxKind.NameMemberCref) + if (memberSyntax is NameMemberCrefSyntax nameMemberCref) { - AddTypeParameters(((NameMemberCrefSyntax)memberSyntax).Name, map); + AddTypeParameters(nameMemberCref.Name, map); + } + else if (memberSyntax is ExtensionMemberCrefSyntax extensionCref) + { + if (extensionCref.TypeArgumentList is { } extensionTypeArguments) + { + AddTypeParameters(extensionTypeArguments.Arguments, map); + } + + AddTypeParameters(extensionCref.Member, map); } } - private static void AddTypeParameters(GenericNameSyntax genericNameSyntax, MultiDictionary map) + private static void AddTypeParameters(SeparatedSyntaxList typeArguments, MultiDictionary map) { // NOTE: Dev11 does not warn about duplication, it just matches parameter types to the // *last* type parameter with the same name. That's why we're iterating backwards and // skipping subsequent symbols with the same name. This can result in some surprising // behavior. For example, both 'T's in "A.B" bind to the second implicitly // declared type parameter. - SeparatedSyntaxList typeArguments = genericNameSyntax.TypeArgumentList.Arguments; for (int i = typeArguments.Count - 1; i >= 0; i--) { // Other types (non-identifiers) are allowed in error scenarios, but they do not introduce new diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 43c04974b81ec..dcdc84c7f6706 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -357,6 +357,8 @@ internal static bool HasParameterList(CrefSyntax crefSyntax) return ((OperatorMemberCrefSyntax)crefSyntax).Parameters != null; case SyntaxKind.ConversionOperatorMemberCref: return ((ConversionOperatorMemberCrefSyntax)crefSyntax).Parameters != null; + case SyntaxKind.ExtensionMemberCref: + return HasParameterList(((ExtensionMemberCrefSyntax)crefSyntax).Member); } return false; @@ -379,7 +381,7 @@ private static SymbolInfo GetCrefSymbolInfo(OneOrMany symbols, SymbolInf LookupResultKind resultKind = LookupResultKind.Ambiguous; - // The boundary between Ambiguous and OverloadResolutionFailure is let clear-cut for crefs. + // The boundary between Ambiguous and OverloadResolutionFailure is less clear-cut for crefs. // We'll say that overload resolution failed if the syntax has a parameter list and if // all of the candidates have the same kind. SymbolKind firstCandidateKind = symbols[0].Kind; diff --git a/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs b/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs index d40824ac80acb..7ae76cf01e6ba 100644 --- a/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs +++ b/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs @@ -181,7 +181,7 @@ public override object VisitNamedType(NamedTypeSymbol symbol, StringBuilder buil builder.Append('.'); } - builder.Append(symbol.IsExtension ? symbol.ExtensionName : symbol.Name); + builder.Append(symbol.IsExtension ? escape(symbol.ExtensionName) : symbol.Name); if (symbol.Arity != 0) { @@ -215,6 +215,12 @@ public override object VisitNamedType(NamedTypeSymbol symbol, StringBuilder buil } return null; + + static string escape(string s) + { + Debug.Assert(!s.Contains("&")); + return s.Replace("<", "<").Replace(">", ">"); + } } public override object VisitPointerType(PointerTypeSymbol symbol, StringBuilder builder) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 400f6265ade9c..bb1e25cc3677d 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1198,6 +1198,7 @@ cref member_cref : conversion_operator_member_cref + | extension_member_cref | indexer_member_cref | name_member_cref | operator_member_cref @@ -1218,6 +1219,10 @@ cref_parameter | 'ref'? 'readonly'? type ; +extension_member_cref + : 'extension' type_argument_list? cref_parameter_list '.' member_cref + ; + indexer_member_cref : 'this' cref_bracketed_parameter_list? ; diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs index c6b5f5f828dbf..9c702233d40a7 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs @@ -22659,6 +22659,118 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) => new NameMemberCrefSyntax(this.Kind, this.name, this.parameters, GetDiagnostics(), annotations); } +internal sealed partial class ExtensionMemberCrefSyntax : MemberCrefSyntax +{ + internal readonly SyntaxToken extensionKeyword; + internal readonly TypeArgumentListSyntax? typeArgumentList; + internal readonly CrefParameterListSyntax parameters; + internal readonly SyntaxToken dotToken; + internal readonly MemberCrefSyntax member; + + internal ExtensionMemberCrefSyntax(SyntaxKind kind, SyntaxToken extensionKeyword, TypeArgumentListSyntax? typeArgumentList, CrefParameterListSyntax parameters, SyntaxToken dotToken, MemberCrefSyntax member, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + this.SlotCount = 5; + this.AdjustFlagsAndWidth(extensionKeyword); + this.extensionKeyword = extensionKeyword; + if (typeArgumentList != null) + { + this.AdjustFlagsAndWidth(typeArgumentList); + this.typeArgumentList = typeArgumentList; + } + this.AdjustFlagsAndWidth(parameters); + this.parameters = parameters; + this.AdjustFlagsAndWidth(dotToken); + this.dotToken = dotToken; + this.AdjustFlagsAndWidth(member); + this.member = member; + } + + internal ExtensionMemberCrefSyntax(SyntaxKind kind, SyntaxToken extensionKeyword, TypeArgumentListSyntax? typeArgumentList, CrefParameterListSyntax parameters, SyntaxToken dotToken, MemberCrefSyntax member, SyntaxFactoryContext context) + : base(kind) + { + this.SetFactoryContext(context); + this.SlotCount = 5; + this.AdjustFlagsAndWidth(extensionKeyword); + this.extensionKeyword = extensionKeyword; + if (typeArgumentList != null) + { + this.AdjustFlagsAndWidth(typeArgumentList); + this.typeArgumentList = typeArgumentList; + } + this.AdjustFlagsAndWidth(parameters); + this.parameters = parameters; + this.AdjustFlagsAndWidth(dotToken); + this.dotToken = dotToken; + this.AdjustFlagsAndWidth(member); + this.member = member; + } + + internal ExtensionMemberCrefSyntax(SyntaxKind kind, SyntaxToken extensionKeyword, TypeArgumentListSyntax? typeArgumentList, CrefParameterListSyntax parameters, SyntaxToken dotToken, MemberCrefSyntax member) + : base(kind) + { + this.SlotCount = 5; + this.AdjustFlagsAndWidth(extensionKeyword); + this.extensionKeyword = extensionKeyword; + if (typeArgumentList != null) + { + this.AdjustFlagsAndWidth(typeArgumentList); + this.typeArgumentList = typeArgumentList; + } + this.AdjustFlagsAndWidth(parameters); + this.parameters = parameters; + this.AdjustFlagsAndWidth(dotToken); + this.dotToken = dotToken; + this.AdjustFlagsAndWidth(member); + this.member = member; + } + + public SyntaxToken ExtensionKeyword => this.extensionKeyword; + public TypeArgumentListSyntax? TypeArgumentList => this.typeArgumentList; + public CrefParameterListSyntax Parameters => this.parameters; + public SyntaxToken DotToken => this.dotToken; + public MemberCrefSyntax Member => this.member; + + internal override GreenNode? GetSlot(int index) + => index switch + { + 0 => this.extensionKeyword, + 1 => this.typeArgumentList, + 2 => this.parameters, + 3 => this.dotToken, + 4 => this.member, + _ => null, + }; + + internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.ExtensionMemberCrefSyntax(this, parent, position); + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitExtensionMemberCref(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitExtensionMemberCref(this); + + public ExtensionMemberCrefSyntax Update(SyntaxToken extensionKeyword, TypeArgumentListSyntax typeArgumentList, CrefParameterListSyntax parameters, SyntaxToken dotToken, MemberCrefSyntax member) + { + if (extensionKeyword != this.ExtensionKeyword || typeArgumentList != this.TypeArgumentList || parameters != this.Parameters || dotToken != this.DotToken || member != this.Member) + { + var newNode = SyntaxFactory.ExtensionMemberCref(extensionKeyword, typeArgumentList, parameters, dotToken, member); + var diags = GetDiagnostics(); + if (diags?.Length > 0) + newNode = newNode.WithDiagnosticsGreen(diags); + var annotations = GetAnnotations(); + if (annotations?.Length > 0) + newNode = newNode.WithAnnotationsGreen(annotations); + return newNode; + } + + return this; + } + + internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics) + => new ExtensionMemberCrefSyntax(this.Kind, this.extensionKeyword, this.typeArgumentList, this.parameters, this.dotToken, this.member, diagnostics, GetAnnotations()); + + internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) + => new ExtensionMemberCrefSyntax(this.Kind, this.extensionKeyword, this.typeArgumentList, this.parameters, this.dotToken, this.member, GetDiagnostics(), annotations); +} + /// /// A MemberCrefSyntax specified by a this keyword and an optional parameter list. /// For example, "this" or "this[int]". @@ -27037,6 +27149,7 @@ internal partial class CSharpSyntaxVisitor public virtual TResult VisitTypeCref(TypeCrefSyntax node) => this.DefaultVisit(node); public virtual TResult VisitQualifiedCref(QualifiedCrefSyntax node) => this.DefaultVisit(node); public virtual TResult VisitNameMemberCref(NameMemberCrefSyntax node) => this.DefaultVisit(node); + public virtual TResult VisitExtensionMemberCref(ExtensionMemberCrefSyntax node) => this.DefaultVisit(node); public virtual TResult VisitIndexerMemberCref(IndexerMemberCrefSyntax node) => this.DefaultVisit(node); public virtual TResult VisitOperatorMemberCref(OperatorMemberCrefSyntax node) => this.DefaultVisit(node); public virtual TResult VisitConversionOperatorMemberCref(ConversionOperatorMemberCrefSyntax node) => this.DefaultVisit(node); @@ -27287,6 +27400,7 @@ internal partial class CSharpSyntaxVisitor public virtual void VisitTypeCref(TypeCrefSyntax node) => this.DefaultVisit(node); public virtual void VisitQualifiedCref(QualifiedCrefSyntax node) => this.DefaultVisit(node); public virtual void VisitNameMemberCref(NameMemberCrefSyntax node) => this.DefaultVisit(node); + public virtual void VisitExtensionMemberCref(ExtensionMemberCrefSyntax node) => this.DefaultVisit(node); public virtual void VisitIndexerMemberCref(IndexerMemberCrefSyntax node) => this.DefaultVisit(node); public virtual void VisitOperatorMemberCref(OperatorMemberCrefSyntax node) => this.DefaultVisit(node); public virtual void VisitConversionOperatorMemberCref(ConversionOperatorMemberCrefSyntax node) => this.DefaultVisit(node); @@ -27949,6 +28063,9 @@ public override CSharpSyntaxNode VisitQualifiedCref(QualifiedCrefSyntax node) public override CSharpSyntaxNode VisitNameMemberCref(NameMemberCrefSyntax node) => node.Update((TypeSyntax)Visit(node.Name), (CrefParameterListSyntax)Visit(node.Parameters)); + public override CSharpSyntaxNode VisitExtensionMemberCref(ExtensionMemberCrefSyntax node) + => node.Update((SyntaxToken)Visit(node.ExtensionKeyword), (TypeArgumentListSyntax)Visit(node.TypeArgumentList), (CrefParameterListSyntax)Visit(node.Parameters), (SyntaxToken)Visit(node.DotToken), (MemberCrefSyntax)Visit(node.Member)); + public override CSharpSyntaxNode VisitIndexerMemberCref(IndexerMemberCrefSyntax node) => node.Update((SyntaxToken)Visit(node.ThisKeyword), (CrefBracketedParameterListSyntax)Visit(node.Parameters)); @@ -32593,6 +32710,20 @@ public NameMemberCrefSyntax NameMemberCref(TypeSyntax name, CrefParameterListSyn return result; } + public ExtensionMemberCrefSyntax ExtensionMemberCref(SyntaxToken extensionKeyword, TypeArgumentListSyntax? typeArgumentList, CrefParameterListSyntax parameters, SyntaxToken dotToken, MemberCrefSyntax member) + { +#if DEBUG + if (extensionKeyword == null) throw new ArgumentNullException(nameof(extensionKeyword)); + if (extensionKeyword.Kind != SyntaxKind.ExtensionKeyword) throw new ArgumentException(nameof(extensionKeyword)); + if (parameters == null) throw new ArgumentNullException(nameof(parameters)); + if (dotToken == null) throw new ArgumentNullException(nameof(dotToken)); + if (dotToken.Kind != SyntaxKind.DotToken) throw new ArgumentException(nameof(dotToken)); + if (member == null) throw new ArgumentNullException(nameof(member)); +#endif + + return new ExtensionMemberCrefSyntax(SyntaxKind.ExtensionMemberCref, extensionKeyword, typeArgumentList, parameters, dotToken, member, this.context); + } + public IndexerMemberCrefSyntax IndexerMemberCref(SyntaxToken thisKeyword, CrefBracketedParameterListSyntax? parameters) { #if DEBUG @@ -37946,6 +38077,20 @@ public static NameMemberCrefSyntax NameMemberCref(TypeSyntax name, CrefParameter return result; } + public static ExtensionMemberCrefSyntax ExtensionMemberCref(SyntaxToken extensionKeyword, TypeArgumentListSyntax? typeArgumentList, CrefParameterListSyntax parameters, SyntaxToken dotToken, MemberCrefSyntax member) + { +#if DEBUG + if (extensionKeyword == null) throw new ArgumentNullException(nameof(extensionKeyword)); + if (extensionKeyword.Kind != SyntaxKind.ExtensionKeyword) throw new ArgumentException(nameof(extensionKeyword)); + if (parameters == null) throw new ArgumentNullException(nameof(parameters)); + if (dotToken == null) throw new ArgumentNullException(nameof(dotToken)); + if (dotToken.Kind != SyntaxKind.DotToken) throw new ArgumentException(nameof(dotToken)); + if (member == null) throw new ArgumentNullException(nameof(member)); +#endif + + return new ExtensionMemberCrefSyntax(SyntaxKind.ExtensionMemberCref, extensionKeyword, typeArgumentList, parameters, dotToken, member); + } + public static IndexerMemberCrefSyntax IndexerMemberCref(SyntaxToken thisKeyword, CrefBracketedParameterListSyntax? parameters) { #if DEBUG diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs index 4ebe1affaa52a..2b899f8318910 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs @@ -633,6 +633,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a NameMemberCrefSyntax node. public virtual TResult? VisitNameMemberCref(NameMemberCrefSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a ExtensionMemberCrefSyntax node. + public virtual TResult? VisitExtensionMemberCref(ExtensionMemberCrefSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a IndexerMemberCrefSyntax node. public virtual TResult? VisitIndexerMemberCref(IndexerMemberCrefSyntax node) => this.DefaultVisit(node); @@ -1374,6 +1377,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a NameMemberCrefSyntax node. public virtual void VisitNameMemberCref(NameMemberCrefSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a ExtensionMemberCrefSyntax node. + public virtual void VisitExtensionMemberCref(ExtensionMemberCrefSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a IndexerMemberCrefSyntax node. public virtual void VisitIndexerMemberCref(IndexerMemberCrefSyntax node) => this.DefaultVisit(node); @@ -2115,6 +2121,9 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor public override SyntaxNode? VisitNameMemberCref(NameMemberCrefSyntax node) => node.Update((TypeSyntax?)Visit(node.Name) ?? throw new ArgumentNullException("name"), (CrefParameterListSyntax?)Visit(node.Parameters)); + public override SyntaxNode? VisitExtensionMemberCref(ExtensionMemberCrefSyntax node) + => node.Update(VisitToken(node.ExtensionKeyword), (TypeArgumentListSyntax?)Visit(node.TypeArgumentList), (CrefParameterListSyntax?)Visit(node.Parameters) ?? throw new ArgumentNullException("parameters"), VisitToken(node.DotToken), (MemberCrefSyntax?)Visit(node.Member) ?? throw new ArgumentNullException("member")); + public override SyntaxNode? VisitIndexerMemberCref(IndexerMemberCrefSyntax node) => node.Update(VisitToken(node.ThisKeyword), (CrefBracketedParameterListSyntax?)Visit(node.Parameters)); @@ -5860,6 +5869,24 @@ public static NameMemberCrefSyntax NameMemberCref(TypeSyntax name, CrefParameter public static NameMemberCrefSyntax NameMemberCref(TypeSyntax name) => SyntaxFactory.NameMemberCref(name, default); + /// Creates a new ExtensionMemberCrefSyntax instance. + public static ExtensionMemberCrefSyntax ExtensionMemberCref(SyntaxToken extensionKeyword, TypeArgumentListSyntax? typeArgumentList, CrefParameterListSyntax parameters, SyntaxToken dotToken, MemberCrefSyntax member) + { + if (extensionKeyword.Kind() != SyntaxKind.ExtensionKeyword) throw new ArgumentException(nameof(extensionKeyword)); + if (parameters == null) throw new ArgumentNullException(nameof(parameters)); + if (dotToken.Kind() != SyntaxKind.DotToken) throw new ArgumentException(nameof(dotToken)); + if (member == null) throw new ArgumentNullException(nameof(member)); + return (ExtensionMemberCrefSyntax)Syntax.InternalSyntax.SyntaxFactory.ExtensionMemberCref((Syntax.InternalSyntax.SyntaxToken)extensionKeyword.Node!, typeArgumentList == null ? null : (Syntax.InternalSyntax.TypeArgumentListSyntax)typeArgumentList.Green, (Syntax.InternalSyntax.CrefParameterListSyntax)parameters.Green, (Syntax.InternalSyntax.SyntaxToken)dotToken.Node!, (Syntax.InternalSyntax.MemberCrefSyntax)member.Green).CreateRed(); + } + + /// Creates a new ExtensionMemberCrefSyntax instance. + public static ExtensionMemberCrefSyntax ExtensionMemberCref(TypeArgumentListSyntax? typeArgumentList, CrefParameterListSyntax parameters, MemberCrefSyntax member) + => SyntaxFactory.ExtensionMemberCref(SyntaxFactory.Token(SyntaxKind.ExtensionKeyword), typeArgumentList, parameters, SyntaxFactory.Token(SyntaxKind.DotToken), member); + + /// Creates a new ExtensionMemberCrefSyntax instance. + public static ExtensionMemberCrefSyntax ExtensionMemberCref(MemberCrefSyntax member) + => SyntaxFactory.ExtensionMemberCref(SyntaxFactory.Token(SyntaxKind.ExtensionKeyword), default, SyntaxFactory.CrefParameterList(), SyntaxFactory.Token(SyntaxKind.DotToken), member); + /// Creates a new IndexerMemberCrefSyntax instance. public static IndexerMemberCrefSyntax IndexerMemberCref(SyntaxToken thisKeyword, CrefBracketedParameterListSyntax? parameters) { diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs index c5074d1c316d9..3f8e74385bdfe 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs @@ -14288,6 +14288,80 @@ public NameMemberCrefSyntax AddParametersParameters(params CrefParameterSyntax[] } } +/// +/// This node is associated with the following syntax kinds: +/// +/// +/// +/// +public sealed partial class ExtensionMemberCrefSyntax : MemberCrefSyntax +{ + private TypeArgumentListSyntax? typeArgumentList; + private CrefParameterListSyntax? parameters; + private MemberCrefSyntax? member; + + internal ExtensionMemberCrefSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public SyntaxToken ExtensionKeyword => new SyntaxToken(this, ((InternalSyntax.ExtensionMemberCrefSyntax)this.Green).extensionKeyword, Position, 0); + + public TypeArgumentListSyntax? TypeArgumentList => GetRed(ref this.typeArgumentList, 1); + + public CrefParameterListSyntax Parameters => GetRed(ref this.parameters, 2)!; + + public SyntaxToken DotToken => new SyntaxToken(this, ((InternalSyntax.ExtensionMemberCrefSyntax)this.Green).dotToken, GetChildPosition(3), GetChildIndex(3)); + + public MemberCrefSyntax Member => GetRed(ref this.member, 4)!; + + internal override SyntaxNode? GetNodeSlot(int index) + => index switch + { + 1 => GetRed(ref this.typeArgumentList, 1), + 2 => GetRed(ref this.parameters, 2)!, + 4 => GetRed(ref this.member, 4)!, + _ => null, + }; + + internal override SyntaxNode? GetCachedSlot(int index) + => index switch + { + 1 => this.typeArgumentList, + 2 => this.parameters, + 4 => this.member, + _ => null, + }; + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitExtensionMemberCref(this); + public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitExtensionMemberCref(this); + + public ExtensionMemberCrefSyntax Update(SyntaxToken extensionKeyword, TypeArgumentListSyntax? typeArgumentList, CrefParameterListSyntax parameters, SyntaxToken dotToken, MemberCrefSyntax member) + { + if (extensionKeyword != this.ExtensionKeyword || typeArgumentList != this.TypeArgumentList || parameters != this.Parameters || dotToken != this.DotToken || member != this.Member) + { + var newNode = SyntaxFactory.ExtensionMemberCref(extensionKeyword, typeArgumentList, parameters, dotToken, member); + var annotations = GetAnnotations(); + return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; + } + + return this; + } + + public ExtensionMemberCrefSyntax WithExtensionKeyword(SyntaxToken extensionKeyword) => Update(extensionKeyword, this.TypeArgumentList, this.Parameters, this.DotToken, this.Member); + public ExtensionMemberCrefSyntax WithTypeArgumentList(TypeArgumentListSyntax? typeArgumentList) => Update(this.ExtensionKeyword, typeArgumentList, this.Parameters, this.DotToken, this.Member); + public ExtensionMemberCrefSyntax WithParameters(CrefParameterListSyntax parameters) => Update(this.ExtensionKeyword, this.TypeArgumentList, parameters, this.DotToken, this.Member); + public ExtensionMemberCrefSyntax WithDotToken(SyntaxToken dotToken) => Update(this.ExtensionKeyword, this.TypeArgumentList, this.Parameters, dotToken, this.Member); + public ExtensionMemberCrefSyntax WithMember(MemberCrefSyntax member) => Update(this.ExtensionKeyword, this.TypeArgumentList, this.Parameters, this.DotToken, member); + + public ExtensionMemberCrefSyntax AddTypeArgumentListArguments(params TypeSyntax[] items) + { + var typeArgumentList = this.TypeArgumentList ?? SyntaxFactory.TypeArgumentList(); + return WithTypeArgumentList(typeArgumentList.WithArguments(typeArgumentList.Arguments.AddRange(items))); + } + public ExtensionMemberCrefSyntax AddParametersParameters(params CrefParameterSyntax[] items) => WithParameters(this.Parameters.WithParameters(this.Parameters.Parameters.AddRange(items))); +} + /// /// A MemberCrefSyntax specified by a this keyword and an optional parameter list. /// For example, "this" or "this[int]". diff --git a/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs b/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs index e058696bd3cb4..08e899e4f1ea2 100644 --- a/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs @@ -946,6 +946,8 @@ private MemberCrefSyntax ParseMemberCref() case SyntaxKind.ExplicitKeyword: case SyntaxKind.ImplicitKeyword: return ParseConversionOperatorMemberCref(); + case SyntaxKind.IdentifierToken when CurrentToken.ContextualKind == SyntaxKind.ExtensionKeyword: + return ParsePossibleExtensionMemberCref(); default: return ParseNameMemberCref(); } @@ -975,6 +977,35 @@ private IndexerMemberCrefSyntax ParseIndexerMemberCref() return SyntaxFactory.IndexerMemberCref(thisKeyword, parameters); } +#nullable enable + /// + /// If we have `extension` (with optional type arguments) and parameter list and a dot, then we have an extension member cref. + /// Otherwise, we fall back to producing the same result as + /// + private MemberCrefSyntax ParsePossibleExtensionMemberCref() + { + Debug.Assert(CurrentToken.ContextualKind == SyntaxKind.ExtensionKeyword); + + SyntaxToken identifierToken = EatToken(); + TypeArgumentListSyntax? typeArguments = (CurrentToken.Kind == SyntaxKind.LessThanToken) ? ParseTypeArguments(typeArgumentsMustBeIdentifiers: true) : null; + CrefParameterListSyntax? parameters = (CurrentToken.Kind == SyntaxKind.OpenParenToken) ? ParseCrefParameterList() : null; + + if (parameters is null || CurrentToken.Kind != SyntaxKind.DotToken) + { + SimpleNameSyntax name = typeArguments is not null + ? SyntaxFactory.GenericName(identifierToken, typeArguments) + : SyntaxFactory.IdentifierName(identifierToken); + + return SyntaxFactory.NameMemberCref(name, parameters); + } + + SyntaxToken dotToken = EatToken(SyntaxKind.DotToken); + MemberCrefSyntax member = ParseMemberCref(); + + return SyntaxFactory.ExtensionMemberCref(ConvertToKeyword(identifierToken), typeArguments, parameters, dotToken, member); + } +#nullable disable + /// /// Parse an overloadable operator, with optional parameters. /// @@ -1323,6 +1354,12 @@ private SimpleNameSyntax ParseCrefName(bool typeArgumentsMustBeIdentifiers) return SyntaxFactory.IdentifierName(identifierToken); } + return SyntaxFactory.GenericName(identifierToken, ParseTypeArguments(typeArgumentsMustBeIdentifiers)); + } + + private TypeArgumentListSyntax ParseTypeArguments(bool typeArgumentsMustBeIdentifiers) + { + Debug.Assert(CurrentToken.Kind == SyntaxKind.LessThanToken); var open = EatToken(); var list = _pool.AllocateSeparated(); @@ -1358,7 +1395,7 @@ private SimpleNameSyntax ParseCrefName(bool typeArgumentsMustBeIdentifiers) open = CheckFeatureAvailability(open, MessageID.IDS_FeatureGenerics, forceWarning: true); - return SyntaxFactory.GenericName(identifierToken, SyntaxFactory.TypeArgumentList(open, list, close)); + return SyntaxFactory.TypeArgumentList(open, list, close); } finally { diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 64da552a81362..dcac09b3296c5 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -17,9 +17,25 @@ Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.WithOpenBra Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.WithParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax? parameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax! Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax! Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.WithTypeParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax? typeParameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.AddParametersParameters(params Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.AddTypeArgumentListArguments(params Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.DotToken.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.ExtensionKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.Member.get -> Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.Parameters.get -> Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.TypeArgumentList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeArgumentListSyntax? +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken extensionKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeArgumentListSyntax? typeArgumentList, Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax! parameters, Microsoft.CodeAnalysis.SyntaxToken dotToken, Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax! member) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithDotToken(Microsoft.CodeAnalysis.SyntaxToken dotToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithExtensionKeyword(Microsoft.CodeAnalysis.SyntaxToken extensionKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithMember(Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax! member) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithParameters(Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax! parameters) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithTypeArgumentList(Microsoft.CodeAnalysis.CSharp.Syntax.TypeArgumentListSyntax? typeArgumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionBlockDeclaration = 9079 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionKeyword = 8451 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionMemberCref = 8607 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExtensionBlockDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax! node) -> Microsoft.CodeAnalysis.SyntaxNode? +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! node) -> Microsoft.CodeAnalysis.SyntaxNode? override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> TResult? override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.AttributeLists.get -> Microsoft.CodeAnalysis.SyntaxList @@ -34,10 +50,16 @@ override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.Op override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.ParameterList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax? override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.SemicolonToken.get -> Microsoft.CodeAnalysis.SyntaxToken override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.TypeParameterList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax? +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> TResult? static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionBlockDeclaration() -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax! static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionBlockDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax? typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax? parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.SyntaxList members) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax! static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionBlockDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax? typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax? parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.SyntaxToken openBraceToken, Microsoft.CodeAnalysis.SyntaxList members, Microsoft.CodeAnalysis.SyntaxToken closeBraceToken, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax! +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax! member) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.TypeArgumentListSyntax? typeArgumentList, Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax! parameters, Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax! member) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionMemberCref(Microsoft.CodeAnalysis.SyntaxToken extensionKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeArgumentListSyntax? typeArgumentList, Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax! parameters, Microsoft.CodeAnalysis.SyntaxToken dotToken, Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax! member) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionBlockDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax! node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionBlockDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax! node) -> TResult? Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken @@ -61,5 +83,6 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(bool i static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(Microsoft.CodeAnalysis.SyntaxToken content, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(Microsoft.CodeAnalysis.SyntaxToken hashToken, Microsoft.CodeAnalysis.SyntaxToken colonToken, Microsoft.CodeAnalysis.SyntaxToken content, Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax! node) -> TResult? virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! node) -> TResult? static Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsOverloadableCompoundAssignmentOperator(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind) -> bool diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 2e8c0b00c8ec1..83f39421a1aa0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -4618,53 +4618,56 @@ private static void CheckExtensionMembers(ImmutableArray members, Bindin static void checkExtensionMember(Symbol member, BindingDiagnosticBag diagnostics) { - switch (member.Kind) + if (!IsAllowedExtensionMember(member)) { - case SymbolKind.Method: - var meth = (MethodSymbol)member; - switch (meth.MethodKind) - { - case MethodKind.Constructor: - case MethodKind.Conversion: - case MethodKind.UserDefinedOperator: - case MethodKind.Destructor: - case MethodKind.EventAdd: - case MethodKind.EventRemove: - case MethodKind.StaticConstructor: - break; - case MethodKind.ExplicitInterfaceImplementation: - // error, but reported elsewhere - return; - case MethodKind.Ordinary: - case MethodKind.PropertyGet: - case MethodKind.PropertySet: - return; - default: - throw ExceptionUtilities.UnexpectedValue(meth.MethodKind); - } - break; + diagnostics.Add(ErrorCode.ERR_ExtensionDisallowsMember, member.GetFirstLocation()); + } + } + } - case SymbolKind.Property: - if (!((PropertySymbol)member).IsIndexer) - { - return; - } - else - { + internal static bool IsAllowedExtensionMember(Symbol member) + { + switch (member.Kind) + { + case SymbolKind.Method: + var meth = (MethodSymbol)member; + switch (meth.MethodKind) + { + case MethodKind.Constructor: + case MethodKind.Conversion: + case MethodKind.UserDefinedOperator: + case MethodKind.Destructor: + case MethodKind.EventAdd: + case MethodKind.EventRemove: + case MethodKind.StaticConstructor: + case MethodKind.ExplicitInterfaceImplementation: break; - } + case MethodKind.Ordinary: + case MethodKind.PropertyGet: + case MethodKind.PropertySet: + return true; + default: + throw ExceptionUtilities.UnexpectedValue(meth.MethodKind); + } + break; - case SymbolKind.Field: - case SymbolKind.Event: - case SymbolKind.NamedType: - break; + case SymbolKind.Property: + if (!((PropertySymbol)member).IsIndexer) + { + return true; + } + break; - default: - throw ExceptionUtilities.UnexpectedValue(member.Kind); - } + case SymbolKind.Field: + case SymbolKind.Event: + case SymbolKind.NamedType: + break; - diagnostics.Add(ErrorCode.ERR_ExtensionDisallowsMember, member.GetFirstLocation()); + default: + throw ExceptionUtilities.UnexpectedValue(member.Kind); } + + return false; } private static void CheckForStructDefaultConstructors( diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index 6caf07de98cb2..ee5d7ede378e0 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -4473,6 +4473,20 @@ + + + + + + + + + + + + + + diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index 673f897808654..171871cfaa20b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -585,6 +585,7 @@ public enum SyntaxKind : ushort CrefParameterList = 8603, CrefBracketedParameterList = 8604, CrefParameter = 8605, + ExtensionMemberCref = 8607, // names & type-names IdentifierName = 8616, diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs index 324c8feac1e58..29098276e9189 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -1915,7 +1915,10 @@ void I.M() { } comp.VerifyEmitDiagnostics( // (10,16): error CS0541: 'Extensions.extension(object).M()': explicit interface declaration can only be declared in a class, record, struct or interface // void I.M() { } - Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationInNonClassOrStruct, "M").WithArguments("Extensions.extension(object).M()").WithLocation(10, 16)); + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationInNonClassOrStruct, "M").WithArguments("Extensions.extension(object).M()").WithLocation(10, 16), + // (10,16): error CS9282: Extension declarations can include only methods or properties + // void I.M() { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "M").WithLocation(10, 16)); } [Fact] @@ -2320,7 +2323,7 @@ public static class Extensions } [Fact] - public void Member_Type() + public void Member_Type_01() { var src = """ public static class Extensions @@ -2348,6 +2351,66 @@ class Nested { } Assert.Equal("Extensions.<>E__0.Nested", symbol.GetTypeMember("Nested").ToTestDisplayString()); } + [Fact] + public void Member_Type_02() + { + var src = """ +C.Nested x = null; + +public static class Extensions +{ + extension(C) + { + class Nested { } + } +} +class C { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,3): error CS0426: The type name 'Nested' does not exist in the type 'C' + // C.Nested x = null; + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInAgg, "Nested").WithArguments("Nested", "C").WithLocation(1, 3), + // (7,15): error CS9282: Extension declarations can include only methods or properties + // class Nested { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(7, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var syntax = GetSyntax(tree, "C.Nested"); + Assert.Null(model.GetSymbolInfo(syntax).Symbol); + } + + [Fact] + public void Member_Type_03() + { + var src = """ +object.Nested x = null; + +public static class Extensions +{ + extension(object) + { + class Nested { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'Nested' + // object.Nested x = null; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Nested").WithArguments("object", "Nested").WithLocation(1, 8), + // (1,15): error CS1002: ; expected + // object.Nested x = null; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "x").WithLocation(1, 15), + // (1,15): error CS0103: The name 'x' does not exist in the current context + // object.Nested x = null; + Diagnostic(ErrorCode.ERR_NameNotInContext, "x").WithArguments("x").WithLocation(1, 15), + // (7,15): error CS9282: Extension declarations can include only methods or properties + // class Nested { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(7, 15)); + } + [Fact] public void Member_Constructor() { @@ -39819,9 +39882,9 @@ static class E """, e.GetDocumentationCommentXml()); var extension = e.GetTypeMembers().Single(); - Assert.Equal("T:E.<>E__0`1", extension.GetDocumentationCommentId()); + Assert.Equal("T:E.<>E__0`1", extension.GetDocumentationCommentId()); AssertEx.Equal(""" - + Summary for extension block Description for T Description for t @@ -39831,7 +39894,7 @@ static class E var mSkeleton = extension.GetMember("M"); AssertEx.Equal(""" - + Summary for M Description for U Description for u @@ -39842,14 +39905,14 @@ static class E var mImplementation = e.GetMember("M"); AssertEx.Equal(""" - + """, mImplementation.GetDocumentationCommentXml()); var p = extension.GetMember("P"); AssertEx.Equal(""" - + Summary for P @@ -39858,7 +39921,7 @@ static class E var pGetImplementation = e.GetMember("get_P"); AssertEx.Equal(""" - + """, pGetImplementation.GetDocumentationCommentXml()); @@ -39873,24 +39936,24 @@ static class E Summary for E - + Summary for extension block Description for T Description for t - + Summary for M Description for U Description for u - + Summary for P - + - + @@ -39918,6 +39981,22 @@ string print(XmlNameAttributeSyntax name) } } + private static IEnumerable PrintXmlCrefSymbols(SyntaxTree tree, SemanticModel model) + { + var docComments = tree.GetCompilationUnitRoot().DescendantTrivia().Select(trivia => trivia.GetStructure()).OfType(); + var crefs = docComments.SelectMany(doc => doc.DescendantNodes().OfType()); + var result = crefs.Select(name => print(name)); + return result; + + string print(XmlCrefAttributeSyntax cref) + { + CrefSyntax crefSyntax = cref.Cref; + var symbol = model.GetSymbolInfo(crefSyntax).Symbol; + var symbolDisplay = symbol is null ? "null" : symbol.ToTestDisplayString(); + return (crefSyntax, symbolDisplay).ToString(); + } + } + [Fact] public void XmlDoc_02() { @@ -39954,7 +40033,7 @@ static class E var extension = e.GetTypeMembers().Single(); AssertEx.Equal(""" - + Summary for extension block Description for T Description for t @@ -39964,7 +40043,7 @@ static class E var mSkeleton = extension.GetMember("M"); AssertEx.Equal(""" - + Summary for M Description for U Description for u @@ -39975,14 +40054,14 @@ static class E var mImplementation = e.GetMember("M"); AssertEx.Equal(""" - + """, mImplementation.GetDocumentationCommentXml()); var p = extension.GetMember("P"); AssertEx.Equal(""" - + Summary for P @@ -39991,7 +40070,7 @@ static class E var pGetImplementation = e.GetMember("get_P"); AssertEx.Equal(""" - + """, pGetImplementation.GetDocumentationCommentXml()); @@ -40067,28 +40146,28 @@ static class E var mSkeleton = extension.GetMember("M"); AssertEx.Equal(""" - + """, mSkeleton.GetDocumentationCommentXml()); var mImplementation = e.GetMember("M"); AssertEx.Equal(""" - + """, mImplementation.GetDocumentationCommentXml()); var p = extension.GetMember("P"); AssertEx.Equal(""" - + """, p.GetDocumentationCommentXml()); var pGetImplementation = e.GetMember("get_P"); AssertEx.Equal(""" - + """, pGetImplementation.GetDocumentationCommentXml()); diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 712b431e107a8..ac8c295f21395 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -2285,5 +2285,1445 @@ public static class E // [System.Diagnostics.CodeAnalysis.UnscopedRef] Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "System.Diagnostics.CodeAnalysis.UnscopedRef").WithLocation(8, 10)); } + + [Fact] + public void Cref_01() + { + var src = """ +/// +/// +static class E +{ + extension(int i) + { + public void M(string s) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(E.extension(int).M(string), void E.<>E__0.M(System.String s))", + "(E.M(int, string), void E.M(this System.Int32 i, System.String s))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_02() + { + var src = """ +/// +static class E +{ + extension(T t) + { + public void M(U u) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension{T}(T).M{U}(U), void E.<>E__0.M(U u))"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_03() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(ref int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(ref int).M()").WithArguments("extension(ref int).M()").WithLocation(1, 16)); + } + + [Fact] + public void Cref_04() + { + var src = """ +/// +/// +static class E +{ + extension(ref int i) + { + public void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (2,16): warning CS1574: XML comment has cref attribute 'extension(int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).M()").WithArguments("extension(int).M()").WithLocation(2, 16)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(E.extension(ref int).M(), void E.<>E__0.M())", + "(E.extension(int).M(), null)"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_05() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null!; + } + extension(string s) + { + public void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).M(), void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_06() + { + var src = """ +/// +/// +static class E +{ + extension(int i) + { + public void M() => throw null!; + } + extension(int) + { + public static void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS0419: Ambiguous reference in cref attribute: 'E.extension(int).M()'. Assuming 'E.extension(int).M()', but could have also matched other overloads including 'E.extension(int).M()'. + // /// + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "E.extension(int).M()").WithArguments("E.extension(int).M()", "E.extension(int).M()", "E.extension(int).M()").WithLocation(1, 16), + // (2,16): warning CS0419: Ambiguous reference in cref attribute: 'E.extension(int).M'. Assuming 'E.extension(int).M()', but could have also matched other overloads including 'E.extension(int).M()'. + // /// + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "E.extension(int).M").WithArguments("E.extension(int).M", "E.extension(int).M()", "E.extension(int).M()").WithLocation(2, 16), + // (11,28): error CS0111: Type 'E' already defines a member called 'M' with the same parameter types + // public static void M() => throw null!; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M").WithArguments("M", "E").WithLocation(11, 28)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(E.extension(int).M(), null)", + "(E.extension(int).M, null)"], + PrintXmlCrefSymbols(tree, model)); + + var docComments = tree.GetCompilationUnitRoot().DescendantTrivia().Select(trivia => trivia.GetStructure()).OfType(); + var crefs = docComments.SelectMany(doc => doc.DescendantNodes().OfType()).ToArray(); + Assert.Equal(CandidateReason.OverloadResolutionFailure, model.GetSymbolInfo(crefs[0].Cref).CandidateReason); + Assert.Equal(CandidateReason.Ambiguous, model.GetSymbolInfo(crefs[1].Cref).CandidateReason); + } + + [Fact] + public void Cref_08() + { + var src = """ +/// +static class E +{ + extension(int i, int j) + { + public void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (4,22): error CS9285: An extension container can have only one receiver parameter + // extension(int i, int j) + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "int j").WithLocation(4, 22)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).M(), void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_09() + { + var src = """ +/// +static class E +{ + extension(int i, int j) + { + public void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int, int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int, int).M()").WithArguments("extension(int, int).M()").WithLocation(1, 16), + // (4,22): error CS9285: An extension container can have only one receiver parameter + // extension(int i, int j) + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "int j").WithLocation(4, 22)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int, int).M(), null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_10() + { + // Missing closing parens + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1584: XML comment has syntactically incorrect cref attribute 'E.extension(.M()' + // /// + Diagnostic(ErrorCode.WRN_BadXMLRefSyntax, "E.extension(.M()").WithArguments("E.extension(.M()").WithLocation(1, 16), + // (1,28): warning CS1658: ) expected. See also error CS1026. + // /// + Diagnostic(ErrorCode.WRN_ErrorOverride, ".").WithArguments(") expected", "1026").WithLocation(1, 28)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(.M(), null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_11() + { + // Missing extension parameter + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension().M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension().M()").WithArguments("extension().M()").WithLocation(1, 16)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension().M(), null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_12() + { + // Two extension parameters + var src = """ +/// +static class E +{ + extension(int i, int j) + { + public void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int, int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int, int).M()").WithArguments("extension(int, int).M()").WithLocation(1, 16), + // (4,22): error CS9285: An extension container can have only one receiver parameter + // extension(int i, int j) + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "int j").WithLocation(4, 22)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int, int).M(), null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_13() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public int P => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).P, System.Int32 E.<>E__0.P { get; })"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_14() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public int P => throw null!; + } + extension(string s) + { + public string P => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).P, System.Int32 E.<>E__0.P { get; })"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_15() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public int P => throw null!; + } + extension(string s) + { + public string P => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(string).P, System.String E.<>E__1.P { get; })"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_16() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).M, void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_17() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(string).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(string).M").WithArguments("extension(string).M").WithLocation(1, 16)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(string).M, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_18() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).M, void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_19() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null; + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).M, void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_20() + { + var src = """ +/// +/// +static class E +{ + extension(int i) + { + public void M() => throw null; + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(E.extension(int).M{U}, void E.<>E__0.M())", + "(E.M{U}, void E.M(this System.Int32 i))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_21() + { + // Arity for extension in cref differs from that in declaration + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).M").WithArguments("extension(int).M").WithLocation(1, 16), + // (4,22): error CS9295: The extended type 'int' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(int i) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "i").WithArguments("int", "T").WithLocation(4, 22)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).M, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_22() + { + // Arity for extension in cref differs from that in declaration + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension{T}(int).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension{T}(int).M").WithArguments("extension{T}(int).M").WithLocation(1, 16)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension{T}(int).M, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_23() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (4,22): error CS9295: The extended type 'int' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(int i) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "i").WithArguments("int", "T").WithLocation(4, 22)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension{T}(int).M, void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_24() + { + var src = """ +/// +static class E +{ + public static void M() => throw null; +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.M").WithArguments("M").WithLocation(1, 16)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.M, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_25() + { + // Type argument name differs from type parameter name + var src = """ +/// +static class E +{ + extension(T t) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension{U}(U).M, void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_26() + { + // __arglist + var src = """ +/// +static class E +{ + extension(__arglist) + { + public int P => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(string).P' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(string).P").WithArguments("extension(string).P").WithLocation(1, 16), + // (4,15): error CS1669: __arglist is not valid in this context + // extension(__arglist) + Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(4, 15)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(string).P, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_27() + { + // member named "extension" + var src = """ +/// +static class E +{ + public static void extension(string s) => throw null!; +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(string), void E.extension(System.String s))"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_28() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public int P => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1584: XML comment has syntactically incorrect cref attribute 'E.extension(int).' + // /// + Diagnostic(ErrorCode.WRN_BadXMLRefSyntax, "E.extension(int).").WithArguments("E.extension(int).").WithLocation(1, 16), + // (1,33): warning CS1658: Identifier expected. See also error CS1001. + // /// + Diagnostic(ErrorCode.WRN_ErrorOverride, @"""").WithArguments("Identifier expected", "1001").WithLocation(1, 33)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int)., null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_29() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public class Nested { } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).Nested' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).Nested").WithArguments("extension(int).Nested").WithLocation(1, 16), + // (6,22): error CS9282: Extension declarations can include only methods or properties + // public class Nested { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(6, 22)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).Nested, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_30() + { + var src = """ +/// +static class E +{ + extension(int i) + { + void I.M() { } + } +} + +interface I +{ + void M(); +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).M").WithArguments("extension(int).M").WithLocation(1, 16), + // (6,16): error CS0541: 'E.extension(int).M()': explicit interface declaration can only be declared in a class, record, struct or interface + // void I.M() { } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationInNonClassOrStruct, "M").WithArguments("E.extension(int).M()").WithLocation(6, 16), + // (6,16): error CS9282: Extension declarations can include only methods or properties + // void I.M() { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "M").WithLocation(6, 16)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).M, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_31() + { + var src = """ +/// +static class E +{ + extension(object) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(missing).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(missing).M").WithArguments("extension(missing).M").WithLocation(1, 16), + // (1,28): warning CS1580: Invalid type for parameter missing in XML comment cref attribute: 'E.extension(missing).M' + // /// + Diagnostic(ErrorCode.WRN_BadXMLRefParamType, "missing").WithArguments("missing", "E.extension(missing).M").WithLocation(1, 28)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(missing).M, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_32() + { + // nested type named "extension" + var src = """ +/// +/// +/// +static class E +{ + class @extension + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(E.extension, E.extension)", + "(E.extension.M, void E.extension.M())", + "(E.extension.M(), void E.extension.M())"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_33() + { + // nested type named "extension", error cases + var src = """ +/// +/// +/// +static class E +{ + class @extension + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (2,16): warning CS1574: XML comment has cref attribute 'extension().M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension().M").WithArguments("extension().M").WithLocation(2, 16), + // (3,16): warning CS1574: XML comment has cref attribute 'extension(int).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).M").WithArguments("extension(int).M").WithLocation(3, 16)); + } + + [Fact] + public void Cref_34() + { + // generic nested type named "extension" + var src = """ +/// +/// +static class E +{ + class @extension + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(E.extension{T}, E.extension)", + "(E.extension{T}.M, void E.extension.M())"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_35() + { + // generic nested type named "extension", error cases + var src = """ +/// +static class E +{ + class @extension + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension{T}(int).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension{T}(int).M").WithArguments("extension{T}(int).M").WithLocation(1, 16)); + } + + [Fact] + public void Cref_36() + { + // method named "extension" + var src = """ +/// +/// +static class E +{ + public static void extension() { } + public static void extension(int i) { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(E.extension(), void E.extension())", + "(E.extension(int), void E.extension(System.Int32 i))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_37() + { + // method named "extension" + var src = """ +/// +/// +static class E +{ + public static void extension() { } + public static void extension(int i) { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(E.extension(), void E.extension())", + "(E.extension(int), void E.extension(System.Int32 i))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_38() + { + // method named "extension", error case + var src = """ +/// +/// +static class E +{ + public static void extension() { } + public static void extension(int i) { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension().M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension().M").WithArguments("extension().M").WithLocation(1, 16), + // (2,16): warning CS1574: XML comment has cref attribute 'extension(int).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).M").WithArguments("extension(int).M").WithLocation(2, 16)); + } + + [Fact] + public void Cref_39() + { + // nested type named "extension" + var src = """ +/// +static class E +{ + class @extension + { + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension, E.extension)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_40() + { + // inaccessible due to file accessibility on type + var src1 = """ +/// +class C { } +"""; + var src2 = """ +file static class E +{ + extension(object) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation([(src1, "file1"), (src2, "file2")], parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // file1(1,16): warning CS1574: XML comment has cref attribute 'extension(object).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(object).M").WithArguments("extension(object).M").WithLocation(1, 16)); + + var c = comp.GetMember("C"); + AssertEx.Equal(""" + + + + +""", c.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(object).M, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_41() + { + // inaccessible due to internal + var src = """ +/// +class C { } +"""; + var libSrc = """ +public static class E +{ + extension(object) + { + internal static void M() { } + } +} +"""; + var libComp = CreateCompilation(libSrc); + var comp = CreateCompilation(src, references: [libComp.EmitToImageReference()], + parseOptions: TestOptions.RegularPreviewWithDocumentationComments, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + + comp.VerifyEmitDiagnostics(); + + var c = comp.GetMember("C"); + AssertEx.Equal(""" + + + + +""", c.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(object).M, void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_42() + { + // inaccessible due to private + var src = """ +/// +class C { } +"""; + var libSrc = """ +public static class E +{ + extension(object) + { + private static void M() { } + } +} +"""; + var libComp = CreateCompilation(libSrc); + var comp = CreateCompilation(src, references: [libComp.EmitToImageReference()], parseOptions: TestOptions.RegularPreviewWithDocumentationComments, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(object).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(object).M").WithArguments("extension(object).M").WithLocation(1, 16)); + + var c = comp.GetMember("C"); + AssertEx.Equal(""" + + + + +""", c.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(object).M, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_43() + { + var src = """ +/// +static class E +{ + extension(T t) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1584: XML comment has syntactically incorrect cref attribute 'E.extension{int}(int).M' + // /// + Diagnostic(ErrorCode.WRN_BadXMLRefSyntax, "E.extension{int}(int).M").WithArguments("E.extension{int}(int).M").WithLocation(1, 16), + // (1,28): warning CS1658: Type parameter declaration must be an identifier not a type. See also error CS0081. + // /// + Diagnostic(ErrorCode.WRN_ErrorOverride, "int").WithArguments("Type parameter declaration must be an identifier not a type", "0081").WithLocation(1, 28)); + } + + [Fact] + public void Cref_44() + { + var src = """ +/// +extension(int) +{ + public static void M() { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M").WithArguments("extension(int).M").WithLocation(1, 16), + // (2,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(2, 1), + // (4,24): warning CS1591: Missing XML comment for publicly visible type or member 'extension(int).M()' + // public static void M() { } + Diagnostic(ErrorCode.WRN_MissingXMLComment, "M").WithArguments("extension(int).M()").WithLocation(4, 24)); + } + + [Fact] + public void Cref_45() + { + var src = """ +/// +static class E +{ + extension(int) + { + extension(string) + { + public static void M() { } + } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).extension(string).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).extension(string).M").WithArguments("extension(int).extension(string).M").WithLocation(1, 16), + // (6,9): error CS9282: Extension declarations can include only methods or properties + // extension(string) + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(6, 9)); + } + + [Fact] + public void Cref_46() + { + var src = """ +/// +/// +static class E +{ + extension(int i) + { + public void M() => throw null!; + public void M(int j) => throw null!; + public void M2(int j) => throw null!; + public void M2() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS0419: Ambiguous reference in cref attribute: 'E.extension(int).M'. Assuming 'E.extension(int).M()', but could have also matched other overloads including 'E.extension(int).M(int)'. + // /// + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "E.extension(int).M").WithArguments("E.extension(int).M", "E.extension(int).M()", "E.extension(int).M(int)").WithLocation(1, 16), + // (2,16): warning CS0419: Ambiguous reference in cref attribute: 'E.extension(int).M2'. Assuming 'E.extension(int).M2(int)', but could have also matched other overloads including 'E.extension(int).M2()'. + // /// + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "E.extension(int).M2").WithArguments("E.extension(int).M2", "E.extension(int).M2(int)", "E.extension(int).M2()").WithLocation(2, 16)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).M, null)", "(E.extension(int).M2, null)"], PrintXmlCrefSymbols(tree, model)); + } } diff --git a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs index 0edf1f6e5fe4a..4de4f0482aad2 100644 --- a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs +++ b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs @@ -628,6 +628,9 @@ private static Syntax.InternalSyntax.QualifiedCrefSyntax GenerateQualifiedCref() private static Syntax.InternalSyntax.NameMemberCrefSyntax GenerateNameMemberCref() => InternalSyntaxFactory.NameMemberCref(GenerateIdentifierName(), null); + private static Syntax.InternalSyntax.ExtensionMemberCrefSyntax GenerateExtensionMemberCref() + => InternalSyntaxFactory.ExtensionMemberCref(InternalSyntaxFactory.Token(SyntaxKind.ExtensionKeyword), null, GenerateCrefParameterList(), InternalSyntaxFactory.Token(SyntaxKind.DotToken), GenerateNameMemberCref()); + private static Syntax.InternalSyntax.IndexerMemberCrefSyntax GenerateIndexerMemberCref() => InternalSyntaxFactory.IndexerMemberCref(InternalSyntaxFactory.Token(SyntaxKind.ThisKeyword), null); @@ -3377,6 +3380,20 @@ public void TestNameMemberCrefFactoryAndProperties() AttachAndCheckDiagnostics(node); } + [Fact] + public void TestExtensionMemberCrefFactoryAndProperties() + { + var node = GenerateExtensionMemberCref(); + + Assert.Equal(SyntaxKind.ExtensionKeyword, node.ExtensionKeyword.Kind); + Assert.Null(node.TypeArgumentList); + Assert.NotNull(node.Parameters); + Assert.Equal(SyntaxKind.DotToken, node.DotToken.Kind); + Assert.NotNull(node.Member); + + AttachAndCheckDiagnostics(node); + } + [Fact] public void TestIndexerMemberCrefFactoryAndProperties() { @@ -9272,6 +9289,32 @@ public void TestNameMemberCrefIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestExtensionMemberCrefTokenDeleteRewriter() + { + var oldNode = GenerateExtensionMemberCref(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestExtensionMemberCrefIdentityRewriter() + { + var oldNode = GenerateExtensionMemberCref(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestIndexerMemberCrefTokenDeleteRewriter() { @@ -10935,6 +10978,9 @@ private static QualifiedCrefSyntax GenerateQualifiedCref() private static NameMemberCrefSyntax GenerateNameMemberCref() => SyntaxFactory.NameMemberCref(GenerateIdentifierName(), default(CrefParameterListSyntax)); + private static ExtensionMemberCrefSyntax GenerateExtensionMemberCref() + => SyntaxFactory.ExtensionMemberCref(SyntaxFactory.Token(SyntaxKind.ExtensionKeyword), default(TypeArgumentListSyntax), GenerateCrefParameterList(), SyntaxFactory.Token(SyntaxKind.DotToken), GenerateNameMemberCref()); + private static IndexerMemberCrefSyntax GenerateIndexerMemberCref() => SyntaxFactory.IndexerMemberCref(SyntaxFactory.Token(SyntaxKind.ThisKeyword), default(CrefBracketedParameterListSyntax)); @@ -13684,6 +13730,20 @@ public void TestNameMemberCrefFactoryAndProperties() Assert.Equal(node, newNode); } + [Fact] + public void TestExtensionMemberCrefFactoryAndProperties() + { + var node = GenerateExtensionMemberCref(); + + Assert.Equal(SyntaxKind.ExtensionKeyword, node.ExtensionKeyword.Kind()); + Assert.Null(node.TypeArgumentList); + Assert.NotNull(node.Parameters); + Assert.Equal(SyntaxKind.DotToken, node.DotToken.Kind()); + Assert.NotNull(node.Member); + var newNode = node.WithExtensionKeyword(node.ExtensionKeyword).WithTypeArgumentList(node.TypeArgumentList).WithParameters(node.Parameters).WithDotToken(node.DotToken).WithMember(node.Member); + Assert.Equal(node, newNode); + } + [Fact] public void TestIndexerMemberCrefFactoryAndProperties() { @@ -19579,6 +19639,32 @@ public void TestNameMemberCrefIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestExtensionMemberCrefTokenDeleteRewriter() + { + var oldNode = GenerateExtensionMemberCref(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestExtensionMemberCrefIdentityRewriter() + { + var oldNode = GenerateExtensionMemberCref(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestIndexerMemberCrefTokenDeleteRewriter() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/CrefParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/CrefParsingTests.cs index b478bdf25b928..aa6a4365f310b 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/CrefParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/CrefParsingTests.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using System; using System.Linq; @@ -3573,5 +3574,463 @@ public void AliasQualifiedGenericTypeConstructor() } #endregion Non-simple-type constructors + + #region Extension members + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_01() + { + UsingNode("extension"); + + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_02() + { + UsingNode("E.extension"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_03() + { + UsingNode("E.extension()"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_04() + { + UsingNode("E.extension(int)"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CrefParameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_05() + { + UsingNode("E.extension{T}"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_06() + { + UsingNode("E.extension{T}()"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_07() + { + UsingNode("E.extension{T}(int)"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CrefParameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_08() + { + UsingNode("E.extension{T}(int)."); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.ExtensionMemberCref); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CrefParameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.NameMemberCref); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_09() + { + UsingNode("E.extension{T}(int).M"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.ExtensionMemberCref); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CrefParameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_10() + { + UsingNode("E.extension{T}().M"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.ExtensionMemberCref); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_11() + { + UsingNode("E.extension().M"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.ExtensionMemberCref); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_12() + { + UsingNode("E.extension().extension().M"); + + N(SyntaxKind.QualifiedCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.ExtensionMemberCref); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.ExtensionMemberCref); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + } + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void ExtensionCref_13() + { + UsingNode("extension().M"); + + N(SyntaxKind.ExtensionMemberCref); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.CrefParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.NameMemberCref); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + } + EOF(); + } + + #endregion } } diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt index 53594c67d7a29..3eb8e1fb0791d 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt @@ -408,6 +408,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExpressionColon(Microsof Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExpressionElement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExpressionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExtensionBlockDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExternAliasDirective(Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitFieldDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FieldDeclarationSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitFieldExpression(Microsoft.CodeAnalysis.CSharp.Syntax.FieldExpressionSyntax) @@ -704,6 +705,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionColon(Microsoft Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionElement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionBlockDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExternAliasDirective(Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFieldDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FieldDeclarationSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFieldExpression(Microsoft.CodeAnalysis.CSharp.Syntax.FieldExpressionSyntax) @@ -954,6 +956,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExpressionColon(Microso Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExpressionElement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExpressionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExtensionBlockDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExternAliasDirective(Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitFieldDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FieldDeclarationSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitFieldExpression(Microsoft.CodeAnalysis.CSharp.Syntax.FieldExpressionSyntax) @@ -2489,6 +2492,22 @@ Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.get_OpenBra Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.get_ParameterList Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.get_SemicolonToken Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.get_TypeParameterList +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.AddParametersParameters(Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax[]) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.AddTypeArgumentListArguments(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax[]) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.TypeArgumentListSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithDotToken(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithExtensionKeyword(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithMember(Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithParameters(Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.WithTypeArgumentList(Microsoft.CodeAnalysis.CSharp.Syntax.TypeArgumentListSyntax) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.get_DotToken +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.get_ExtensionKeyword +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.get_Member +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.get_Parameters +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionMemberCrefSyntax.get_TypeArgumentList Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) @@ -5004,6 +5023,9 @@ Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExpressionStatement(Microsoft.CodeAn Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionBlockDeclaration Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionBlockDeclaration(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax,Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterConstraintClauseSyntax},Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax}) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionBlockDeclaration(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax,Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterConstraintClauseSyntax},Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax},Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax) +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionMemberCref(Microsoft.CodeAnalysis.CSharp.Syntax.TypeArgumentListSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax) +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionMemberCref(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.TypeArgumentListSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.MemberCrefSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExternAliasDirective(Microsoft.CodeAnalysis.SyntaxToken) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExternAliasDirective(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExternAliasDirective(System.String) @@ -5885,6 +5907,7 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionElement Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionStatement Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionBlockDeclaration Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionKeyword +Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionMemberCref Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExternAliasDirective Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExternKeyword Microsoft.CodeAnalysis.CSharp.SyntaxKind.FalseKeyword From e6876dd1e247b86bbd6a8048c5b1a51556077a51 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 2 Jun 2025 13:37:05 -0700 Subject: [PATCH 02/10] Address feedback --- .../CSharp/Portable/Binder/Binder_Crefs.cs | 8 +- ...mentCompiler.DocumentationCommentWalker.cs | 2 +- ...nCommentCompiler.IncludeElementExpander.cs | 2 +- .../Compiler/DocumentationCommentCompiler.cs | 16 +- ...cumentationCommentIDVisitor.PartVisitor.cs | 8 +- .../CSharp/Portable/Symbols/Symbol.cs | 11 ++ .../Test/Emit3/Semantics/ExtensionTests.cs | 2 +- .../Test/Emit3/Semantics/ExtensionTests2.cs | 156 ++++++++++++++++++ .../Core/Compilation/CompilationExtensions.cs | 10 +- 9 files changed, 191 insertions(+), 24 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs index 68d5a0a6a302f..f785d2f19ae82 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs @@ -222,6 +222,8 @@ private ImmutableArray BindIndexerMemberCref(IndexerMemberCrefSyntax syn private ImmutableArray BindExtensionMemberCref(ExtensionMemberCrefSyntax syntax, NamespaceOrTypeSymbol? containerOpt, out Symbol? ambiguityWinner, BindingDiagnosticBag diagnostics) { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle extension operators + if (containerOpt is not NamedTypeSymbol namedContainer) { ambiguityWinner = null; @@ -262,6 +264,9 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym CompoundUseSiteInfo useSiteInfo = this.GetNewCompoundUseSiteInfo(diagnostics); ArrayBuilder? sortedSymbolsBuilder = null; + Debug.Assert(syntax.Parameters is not null); + ImmutableArray extensionParameterSymbols = BindCrefParameters(syntax.Parameters, diagnostics); + foreach (var nested in container.GetTypeMembers()) { if (!nested.IsExtension || nested.Arity != extensionArity || nested.ExtensionParameter is null) @@ -271,6 +276,7 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym var constructedNested = (NamedTypeSymbol)ConstructWithCrefTypeParameters(extensionArity, extensionTypeArguments, nested); + // Use signature method symbols to match extension blocks var candidateExtensionSignature = new SignatureOnlyMethodSymbol( methodKind: MethodKind.Ordinary, typeParameters: IndexedTypeParameterSymbol.TakeSymbols(constructedNested.Arity), @@ -286,8 +292,6 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym refCustomModifiers: [], explicitInterfaceImplementations: []); - ImmutableArray extensionParameterSymbols = syntax.Parameters is { } extensionParameterListSyntax ? BindCrefParameters(extensionParameterListSyntax, diagnostics) : default; - var providedExtensionSignature = new SignatureOnlyMethodSymbol( methodKind: MethodKind.Ordinary, typeParameters: IndexedTypeParameterSymbol.TakeSymbols(constructedNested.Arity), diff --git a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.DocumentationCommentWalker.cs b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.DocumentationCommentWalker.cs index aec71992eb365..ee86d7b37e3b9 100644 --- a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.DocumentationCommentWalker.cs +++ b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.DocumentationCommentWalker.cs @@ -151,7 +151,7 @@ public override void DefaultVisit(SyntaxNode node) // Do this for the diagnostics, even if it won't be written. BindingDiagnosticBag diagnostics = diagnose ? _diagnostics : BindingDiagnosticBag.GetInstance(withDiagnostics: false, withDependencies: _diagnostics.AccumulatesDependencies); - string docCommentId = GetDocumentationCommentId(cref, binder, diagnostics); + string docCommentId = GetEscapedDocumentationCommentId(cref, binder, diagnostics); if (!diagnose) { diff --git a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.IncludeElementExpander.cs b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.IncludeElementExpander.cs index 0b080f41b9b48..7ff296cff9a5a 100644 --- a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.IncludeElementExpander.cs +++ b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.IncludeElementExpander.cs @@ -516,7 +516,7 @@ private void BindAndReplaceCref(XAttribute attribute, CSharpSyntaxNode originati Binder binder = BinderFactory.MakeCrefBinder(crefSyntax, memberDeclSyntax, _compilation.GetBinderFactory(memberDeclSyntax.SyntaxTree)); var crefDiagnostics = BindingDiagnosticBag.GetInstance(_diagnostics); - attribute.Value = GetDocumentationCommentId(crefSyntax, binder, crefDiagnostics); // NOTE: mutation (element must be a copy) + attribute.Value = GetEscapedDocumentationCommentId(crefSyntax, binder, crefDiagnostics); // NOTE: mutation (element must be a copy) RecordBindingDiagnostics(crefDiagnostics, sourceLocation); // Respects DocumentationMode. crefDiagnostics.Free(); } diff --git a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs index 826201c5177fa..2d408b4c095ee 100644 --- a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs @@ -251,9 +251,9 @@ public override void VisitMethod(MethodSymbol symbol) return; } - WriteLine("", symbol.GetDocumentationCommentId()); + WriteLine("", symbol.GetEscapedDocumentationCommentId()); Indent(); - WriteLine("", symbolForDocComment.GetDocumentationCommentId()); + WriteLine("", symbolForDocComment.GetEscapedDocumentationCommentId()); Unindent(); WriteLine(""); return; @@ -331,7 +331,7 @@ public override void DefaultVisit(Symbol symbol) // If the XML in any of the doc comments is invalid, skip all further processing (for this symbol) and // just write a comment saying that info was lost for this symbol. string message = ErrorFacts.GetMessage(MessageID.IDS_XMLIGNORED, CultureInfo.CurrentUICulture); - WriteLine(string.Format(CultureInfo.CurrentUICulture, message, symbol.GetDocumentationCommentId())); + WriteLine(string.Format(CultureInfo.CurrentUICulture, message, symbol.GetEscapedDocumentationCommentId())); return; } @@ -381,7 +381,7 @@ public override void DefaultVisit(Symbol symbol) // If the XML in any of the doc comments is invalid, skip all further processing (for this symbol) and // just write a comment saying that info was lost for this symbol. string message = ErrorFacts.GetMessage(MessageID.IDS_XMLIGNORED, CultureInfo.CurrentUICulture); - WriteLine(string.Format(CultureInfo.CurrentUICulture, message, symbol.GetDocumentationCommentId())); + WriteLine(string.Format(CultureInfo.CurrentUICulture, message, symbol.GetEscapedDocumentationCommentId())); return; } @@ -467,7 +467,7 @@ private bool TryProcessRecordPropertyDocumentation( Debug.Assert(paramTags.Count > 0); BeginTemporaryString(); - WriteLine("", recordPropertySymbol.GetDocumentationCommentId()); + WriteLine("", recordPropertySymbol.GetEscapedDocumentationCommentId()); Indent(); var substitutedTextBuilder = PooledStringBuilder.GetInstance(); var includeElementNodesBuilder = _processIncludes ? ArrayBuilder.GetInstance() : null; @@ -573,7 +573,7 @@ private bool TryProcessDocumentationCommentTriviaNodes( // because we need to have XML to process. if (!shouldSkipPartialDefinitionComments || _processIncludes) { - WriteLine("", symbol.GetDocumentationCommentId()); + WriteLine("", symbol.GetEscapedDocumentationCommentId()); Indent(); } @@ -1096,7 +1096,7 @@ private static string LongestCommonPrefix(string str1, string str2) /// /// Does not respect DocumentationMode, so use a temporary bag if diagnostics are not desired. /// - private static string GetDocumentationCommentId(CrefSyntax crefSyntax, Binder binder, BindingDiagnosticBag diagnostics) + private static string GetEscapedDocumentationCommentId(CrefSyntax crefSyntax, Binder binder, BindingDiagnosticBag diagnostics) { if (crefSyntax.ContainsDiagnostics) { @@ -1135,7 +1135,7 @@ private static string GetDocumentationCommentId(CrefSyntax crefSyntax, Binder bi diagnostics.AddDependencies(symbol as TypeSymbol ?? symbol.ContainingType); } - return symbol.OriginalDefinition.GetDocumentationCommentId(); + return symbol.OriginalDefinition.GetEscapedDocumentationCommentId(); } /// diff --git a/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs b/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs index 7ae76cf01e6ba..d40824ac80acb 100644 --- a/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs +++ b/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs @@ -181,7 +181,7 @@ public override object VisitNamedType(NamedTypeSymbol symbol, StringBuilder buil builder.Append('.'); } - builder.Append(symbol.IsExtension ? escape(symbol.ExtensionName) : symbol.Name); + builder.Append(symbol.IsExtension ? symbol.ExtensionName : symbol.Name); if (symbol.Arity != 0) { @@ -215,12 +215,6 @@ public override object VisitNamedType(NamedTypeSymbol symbol, StringBuilder buil } return null; - - static string escape(string s) - { - Debug.Assert(!s.Contains("&")); - return s.Replace("<", "<").Replace(">", ">"); - } } public override object VisitPointerType(PointerTypeSymbol symbol, StringBuilder builder) diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 1b843d8c13d89..8c3bf4bd91542 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -991,6 +991,17 @@ public virtual string GetDocumentationCommentId() } #nullable enable + public string GetEscapedDocumentationCommentId() + { + return escape(GetDocumentationCommentId()); + + static string escape(string s) + { + Debug.Assert(!s.Contains("&")); + return s.Replace("<", "<").Replace(">", ">"); + } + } + /// /// Fetches the documentation comment for this element with a cancellation token. /// diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs index 29098276e9189..baa0defba822b 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -39882,7 +39882,7 @@ static class E """, e.GetDocumentationCommentXml()); var extension = e.GetTypeMembers().Single(); - Assert.Equal("T:E.<>E__0`1", extension.GetDocumentationCommentId()); + Assert.Equal("T:E.<>E__0`1", extension.GetDocumentationCommentId()); AssertEx.Equal(""" Summary for extension block diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index ac8c295f21395..49ae145e0d4f3 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -4,7 +4,9 @@ #nullable disable using System; +using System.Globalization; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -2312,6 +2314,8 @@ static class E """, e.GetDocumentationCommentXml()); + AssertEx.Equal("T:E.<>E__0", e.GetTypeMembers().Single().GetDocumentationCommentId()); + var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal([ @@ -2344,6 +2348,8 @@ static class E """, e.GetDocumentationCommentXml()); + AssertEx.Equal("T:E.<>E__0`1", e.GetTypeMembers().Single().GetDocumentationCommentId()); + var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal(["(E.extension{T}(T).M{U}(U), void E.<>E__0.M(U u))"], PrintXmlCrefSymbols(tree, model)); @@ -3725,5 +3731,155 @@ static class E var model = comp.GetSemanticModel(tree); AssertEx.Equal(["(E.extension(int).M, null)", "(E.extension(int).M2, null)"], PrintXmlCrefSymbols(tree, model)); } + + [Fact] + public void Cref_47() + { + // Xml doc APIs on PE symbols + var src = """ +static class E +{ + extension(int i) + { + public void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var comp2 = CreateCompilation("", references: [comp.EmitToImageReference(documentation: new TestDocumentationProvider())]); + + var mSkeleton = comp2.GetMember("E").GetTypeMembers().Single().GetMember("M"); + Assert.Equal("M:E.<>E__0.M", mSkeleton.GetDocumentationCommentId()); + Assert.Equal("M:E.<>E__0.M", mSkeleton.GetDocumentationCommentXml()); + } + + private class TestDocumentationProvider : DocumentationProvider + { + protected internal override string GetDocumentationForSymbol(string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default) + { + return documentationMemberID; + } + + public override bool Equals(object obj) => throw new NotImplementedException(); + + public override int GetHashCode() => throw new NotImplementedException(); + } + + [Fact] + public void Cref_48() + { + var libSrc = """ +public static class E +{ + extension(int i) + { + public void M() => throw null!; + } +} +"""; + var libComp = CreateCompilation(libSrc); + + var src = """ +/// +class C +{ +} +"""; + var comp = CreateCompilation(src, references: [libComp.EmitToImageReference()], parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var c = comp.GetMember("C"); + AssertEx.Equal(""" + + + + +""", c.GetDocumentationCommentXml()); + } + + [Fact] + public void Cref_49() + { + var src = """ +/// +static class E +{ +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(missing).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(missing).M").WithArguments("extension(missing).M").WithLocation(1, 16), + // (1,28): warning CS1580: Invalid type for parameter missing in XML comment cref attribute: 'E.extension(missing).M' + // /// + Diagnostic(ErrorCode.WRN_BadXMLRefParamType, "missing").WithArguments("missing", "E.extension(missing).M").WithLocation(1, 28)); + } + + [Fact] + public void Cref_50() + { + var src = """ +/// +/// +static class E +{ + extension(int i) + { + public void M() => throw null!; + public void M2(int j) => throw null!; + } + extension(int i) + { + public void M(int j) => throw null!; + public void M2() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS0419: Ambiguous reference in cref attribute: 'E.extension(int).M'. Assuming 'E.extension(int).M()', but could have also matched other overloads including 'E.extension(int).M(int)'. + // /// + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "E.extension(int).M").WithArguments("E.extension(int).M", "E.extension(int).M()", "E.extension(int).M(int)").WithLocation(1, 16), + // (2,16): warning CS0419: Ambiguous reference in cref attribute: 'E.extension(int).M2'. Assuming 'E.extension(int).M2(int)', but could have also matched other overloads including 'E.extension(int).M2()'. + // /// + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "E.extension(int).M2").WithArguments("E.extension(int).M2", "E.extension(int).M2(int)", "E.extension(int).M2()").WithLocation(2, 16)); + + var e = comp.GetMember("E"); + AssertEx.Equal(""" + + + + + +""", e.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).M, null)", "(E.extension(int).M2, null)"], PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_51() + { + var src = """ +/// +static class E +{ + extension(int) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(E.extension(int).@M, void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); + } } diff --git a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs index 32f881abf5f02..d5874d984e045 100644 --- a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs +++ b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs @@ -117,23 +117,25 @@ public static MetadataReference EmitToImageReference( EmitOptions options = null, bool embedInteropTypes = false, ImmutableArray aliases = default, - DiagnosticDescription[] expectedWarnings = null) => EmitToPortableExecutableReference(comp, options, embedInteropTypes, aliases, expectedWarnings); + DiagnosticDescription[] expectedWarnings = null, + DocumentationProvider documentation = null) => EmitToPortableExecutableReference(comp, options, embedInteropTypes, aliases, expectedWarnings, documentation); public static PortableExecutableReference EmitToPortableExecutableReference( this Compilation comp, EmitOptions options = null, bool embedInteropTypes = false, ImmutableArray aliases = default, - DiagnosticDescription[] expectedWarnings = null) + DiagnosticDescription[] expectedWarnings = null, + DocumentationProvider documentation = null) { var image = comp.EmitToArray(options, expectedWarnings: expectedWarnings); if (comp.Options.OutputKind == OutputKind.NetModule) { - return ModuleMetadata.CreateFromImage(image).GetReference(display: comp.MakeSourceModuleName()); + return ModuleMetadata.CreateFromImage(image).GetReference(documentation, display: comp.MakeSourceModuleName()); } else { - return AssemblyMetadata.CreateFromImage(image).GetReference(aliases: aliases, embedInteropTypes: embedInteropTypes, display: comp.MakeSourceAssemblySimpleName()); + return AssemblyMetadata.CreateFromImage(image).GetReference(documentation, aliases: aliases, embedInteropTypes: embedInteropTypes, display: comp.MakeSourceAssemblySimpleName()); } } From 6441497d66475365b780967bc9fc449d0880b4b6 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 2 Jun 2025 16:25:33 -0700 Subject: [PATCH 03/10] Update diagnostic message --- .../CSharp/Portable/CSharpResources.resx | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../Test/Emit3/Semantics/ExtensionTests.cs | 162 +++++++++--------- .../Test/Emit3/Semantics/ExtensionTests2.cs | 6 +- .../Syntax/Parsing/ExtensionsParsingTests.cs | 32 ++-- 17 files changed, 127 insertions(+), 127 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index b3f569a40c14f..586502f7d0ee5 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -8096,7 +8096,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Extension declarations may not have a name. - Extension declarations can include only methods or properties + This member is not allowed in an extension block Extensions must be declared in a top-level, non-generic, static class diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 164941bfe1cf5..c6c1990c424a8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 2cfd37350a1fa..606c733c6dd5e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 5b904b8347d5e..b6dfde5a6d011 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 3643165b3ae3c..461b11b621372 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8f62ff52a10f7..8d49ac7b23edc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index af9147be02882..d5eb43d80c532 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 8f1588fb75407..dd96359efc4ee 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 8a892cfd63dc2..278ee671be37d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 827cc499e6797..4011f9eabd8ec 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 7488a8c4a28a1..2e223b8e29443 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 1e55e37679f67..72000a1b54dc3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index d205367c9b87f..25d9f43badf87 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index fa2ad94a2450f..81dc3990f5b6d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -788,8 +788,8 @@ - Extension declarations can include only methods or properties - Extension declarations can include only methods or properties + This member is not allowed in an extension block + This member is not allowed in an extension block diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs index baa0defba822b..6eb337852373f 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -738,7 +738,7 @@ static void Method() // (14,20): error CS0117: 'string' does not contain a definition for 'M' // string.M(); Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("string", "M").WithLocation(14, 20), - // (17,9): error CS9282: Extension declarations can include only methods or properties + // (17,9): error CS9282: This member is not allowed in an extension block // extension(string) { public static void M() { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(17, 9)); @@ -1916,7 +1916,7 @@ void I.M() { } // (10,16): error CS0541: 'Extensions.extension(object).M()': explicit interface declaration can only be declared in a class, record, struct or interface // void I.M() { } Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationInNonClassOrStruct, "M").WithArguments("Extensions.extension(object).M()").WithLocation(10, 16), - // (10,16): error CS9282: Extension declarations can include only methods or properties + // (10,16): error CS9282: This member is not allowed in an extension block // void I.M() { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "M").WithLocation(10, 16)); } @@ -2070,7 +2070,7 @@ public static class Extensions """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,13): error CS9282: Extension declarations can include only methods or properties + // (5,13): error CS9282: This member is not allowed in an extension block // int Property { get; set; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Property").WithLocation(5, 13)); @@ -2229,7 +2229,7 @@ public static class Extensions """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,20): error CS9282: Extension declarations can include only methods or properties + // (5,20): error CS9282: This member is not allowed in an extension block // static int Property { get; set; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Property").WithLocation(5, 20)); @@ -2264,7 +2264,7 @@ public static class Extensions """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,13): error CS9282: Extension declarations can include only methods or properties + // (5,13): error CS9282: This member is not allowed in an extension block // int this[int i] { get => 42; set { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(5, 13)); @@ -2289,7 +2289,7 @@ public static class Extensions // (3,5): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? // extension(object o) Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "extension").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(3, 5), - // (5,13): error CS9282: Extension declarations can include only methods or properties + // (5,13): error CS9282: This member is not allowed in an extension block // int this[int i] { get => 42; set { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(5, 13) ); @@ -2316,7 +2316,7 @@ public static class Extensions // (5,20): error CS0106: The modifier 'static' is not valid for this item // static int this[int i] { get => 42; set { } } Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(5, 20), - // (5,20): error CS9282: Extension declarations can include only methods or properties + // (5,20): error CS9282: This member is not allowed in an extension block // static int this[int i] { get => 42; set { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(5, 20) ); @@ -2336,7 +2336,7 @@ class Nested { } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,15): error CS9282: Extension declarations can include only methods or properties + // (5,15): error CS9282: This member is not allowed in an extension block // class Nested { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(5, 15)); @@ -2371,7 +2371,7 @@ class C { } // (1,3): error CS0426: The type name 'Nested' does not exist in the type 'C' // C.Nested x = null; Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInAgg, "Nested").WithArguments("Nested", "C").WithLocation(1, 3), - // (7,15): error CS9282: Extension declarations can include only methods or properties + // (7,15): error CS9282: This member is not allowed in an extension block // class Nested { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(7, 15)); @@ -2406,7 +2406,7 @@ class Nested { } // (1,15): error CS0103: The name 'x' does not exist in the current context // object.Nested x = null; Diagnostic(ErrorCode.ERR_NameNotInContext, "x").WithArguments("x").WithLocation(1, 15), - // (7,15): error CS9282: Extension declarations can include only methods or properties + // (7,15): error CS9282: This member is not allowed in an extension block // class Nested { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(7, 15)); } @@ -2425,7 +2425,7 @@ public static class Extensions // (3,25): error CS1520: Method must have a return type // extension(object) { Extensions() { } } Diagnostic(ErrorCode.ERR_MemberNeedsType, "Extensions").WithLocation(3, 25), - // (3,25): error CS9282: Extension declarations can include only methods or properties + // (3,25): error CS9282: This member is not allowed in an extension block // extension(object) { Extensions() { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Extensions").WithLocation(3, 25)); @@ -2453,7 +2453,7 @@ public static class Extensions """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (3,26): error CS9282: Extension declarations can include only methods or properties + // (3,26): error CS9282: This member is not allowed in an extension block // extension(object) { ~Extensions() { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Extensions").WithLocation(3, 26)); @@ -2484,7 +2484,7 @@ public static class Extensions // (1,18): error CS1061: 'object' does not contain a definition for 'field' and no accessible extension method 'field' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) // _ = new object().field; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "field").WithArguments("object", "field").WithLocation(1, 18), - // (5,31): error CS9282: Extension declarations can include only methods or properties + // (5,31): error CS9282: This member is not allowed in an extension block // extension(object o) { int field = 0; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "field").WithLocation(5, 31), // (5,31): warning CS0169: The field 'Extensions.extension(object).field' is never used @@ -2513,7 +2513,7 @@ public static class Extensions """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (3,35): error CS9282: Extension declarations can include only methods or properties + // (3,35): error CS9282: This member is not allowed in an extension block // extension(object) { const int i = 0; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "i").WithLocation(3, 35)); @@ -2549,13 +2549,13 @@ event System.Action I.E { add { } remove { } } // (9,31): error CS0541: 'Extensions.extension(object).E': explicit interface declaration can only be declared in a class, record, struct or interface // event System.Action I.E { add { } remove { } } Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationInNonClassOrStruct, "E").WithArguments("Extensions.extension(object).E").WithLocation(9, 31), - // (9,31): error CS9282: Extension declarations can include only methods or properties + // (9,31): error CS9282: This member is not allowed in an extension block // event System.Action I.E { add { } remove { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "E").WithLocation(9, 31), - // (9,35): error CS9282: Extension declarations can include only methods or properties + // (9,35): error CS9282: This member is not allowed in an extension block // event System.Action I.E { add { } remove { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "add").WithLocation(9, 35), - // (9,43): error CS9282: Extension declarations can include only methods or properties + // (9,43): error CS9282: This member is not allowed in an extension block // event System.Action I.E { add { } remove { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "remove").WithLocation(9, 43)); } @@ -4034,16 +4034,16 @@ class C {} // (6,20): error CS0055: Inconsistent accessibility: parameter type 'C' is less accessible than indexer or property 'Extensions.extension(C).P' // public int P { get => 0; set {}} Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "P").WithArguments("Extensions.extension(C).P", "C").WithLocation(6, 20), - // (7,20): error CS9282: Extension declarations can include only methods or properties + // (7,20): error CS9282: This member is not allowed in an extension block // public int this[int i] { get => 0; set {}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 20), // (7,20): error CS0055: Inconsistent accessibility: parameter type 'C' is less accessible than indexer or property 'Extensions.extension(C).this[int]' // public int this[int i] { get => 0; set {}} Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "this").WithArguments("Extensions.extension(C).this[int]", "C").WithLocation(7, 20), - // (11,21): error CS9282: Extension declarations can include only methods or properties + // (11,21): error CS9282: This member is not allowed in an extension block // private int this[long i] { get => 0; set {}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(11, 21), - // (15,22): error CS9282: Extension declarations can include only methods or properties + // (15,22): error CS9282: This member is not allowed in an extension block // internal int this[byte i] { get => 0; set {}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(15, 22) ); @@ -4100,13 +4100,13 @@ private class C {} // (6,20): error CS0055: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than indexer or property 'Extensions.extension(Extensions.C).P' // public int P { get => 0; set {}} Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "P").WithArguments("Extensions.extension(Extensions.C).P", "Extensions.C").WithLocation(6, 20), - // (7,20): error CS9282: Extension declarations can include only methods or properties + // (7,20): error CS9282: This member is not allowed in an extension block // public int this[int i] { get => 0; set {}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 20), // (7,20): error CS0055: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than indexer or property 'Extensions.extension(Extensions.C).this[int]' // public int this[int i] { get => 0; set {}} Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "this").WithArguments("Extensions.extension(Extensions.C).this[int]", "Extensions.C").WithLocation(7, 20), - // (11,21): error CS9282: Extension declarations can include only methods or properties + // (11,21): error CS9282: This member is not allowed in an extension block // private int this[long i] { get => 0; set {}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(11, 21), // (13,23): error CS0051: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than method 'Extensions.extension(Extensions.C).M2()' @@ -4115,7 +4115,7 @@ private class C {} // (14,22): error CS0055: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than indexer or property 'Extensions.extension(Extensions.C).P2' // internal int P2 { get => 0; set {}} Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "P2").WithArguments("Extensions.extension(Extensions.C).P2", "Extensions.C").WithLocation(14, 22), - // (15,22): error CS9282: Extension declarations can include only methods or properties + // (15,22): error CS9282: This member is not allowed in an extension block // internal int this[byte i] { get => 0; set {}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(15, 22), // (15,22): error CS0055: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than indexer or property 'Extensions.extension(Extensions.C).this[byte]' @@ -4316,7 +4316,7 @@ private static void M3(this C x) {} var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (9,20): error CS9282: Extension declarations can include only methods or properties + // (9,20): error CS9282: This member is not allowed in an extension block // public int this[int i] => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(9, 20)); } @@ -4343,10 +4343,10 @@ public static void M2(this int x, C c) {} var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (9,18): error CS9282: Extension declarations can include only methods or properties + // (9,18): error CS9282: This member is not allowed in an extension block // public C this[int y] => null; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(9, 18), - // (10,20): error CS9282: Extension declarations can include only methods or properties + // (10,20): error CS9282: This member is not allowed in an extension block // public int this[C y] => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(10, 20)); } @@ -13570,7 +13570,7 @@ public static class StaticType { } // (1,14): error CS1061: 'C' does not contain a definition for 'StaticType' and no accessible extension method 'StaticType' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) // new C().StaticType(); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "StaticType").WithArguments("C", "StaticType").WithLocation(1, 14), - // (9,29): error CS9282: Extension declarations can include only methods or properties + // (9,29): error CS9282: This member is not allowed in an extension block // public static class StaticType { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "StaticType").WithLocation(9, 29)); @@ -21729,7 +21729,7 @@ static class E // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5), - // (13,20): error CS9282: Extension declarations can include only methods or properties + // (13,20): error CS9282: This member is not allowed in an extension block // public int this[int i] => throw null; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(13, 20)); } @@ -21760,7 +21760,7 @@ static class E // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5), - // (13,20): error CS9282: Extension declarations can include only methods or properties + // (13,20): error CS9282: This member is not allowed in an extension block // public int this[System.Index i] => throw null; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(13, 20)); @@ -21902,7 +21902,7 @@ static class E // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5), - // (13,20): error CS9282: Extension declarations can include only methods or properties + // (13,20): error CS9282: This member is not allowed in an extension block // public int this[System.Range r] => throw null; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(13, 20)); @@ -21987,7 +21987,7 @@ static class E // (1,16): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = new C() is [1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[1]").WithArguments("C").WithLocation(1, 16), - // (12,20): error CS9282: Extension declarations can include only methods or properties + // (12,20): error CS9282: This member is not allowed in an extension block // public int this[int i] => throw null; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(12, 20)); } @@ -22018,7 +22018,7 @@ static class E // (1,16): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = new C() is [1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[1]").WithArguments("C").WithLocation(1, 16), - // (12,20): error CS9282: Extension declarations can include only methods or properties + // (12,20): error CS9282: This member is not allowed in an extension block // public int this[System.Index i] => throw null; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(12, 20)); @@ -22134,7 +22134,7 @@ static class E // (1,20): error CS1503: Argument 1: cannot convert from 'System.Range' to 'System.Index' // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_BadArgType, ".. var x").WithArguments("1", "System.Range", "System.Index").WithLocation(1, 20), - // (13,20): error CS9282: Extension declarations can include only methods or properties + // (13,20): error CS9282: This member is not allowed in an extension block // public int this[System.Range r] => throw null; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(13, 20)); @@ -23924,7 +23924,7 @@ static void M3(int T){} // (15,28): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter // static void M3(int T){} Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(15, 28), - // (16,13): error CS9282: Extension declarations can include only methods or properties + // (16,13): error CS9282: This member is not allowed in an extension block // int this[int T] => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(16, 13), // (16,22): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter @@ -23981,13 +23981,13 @@ int this[int i] {set{}} // (4,15): warning CS8981: The type name 'value' only contains lower-cased ascii characters. Such names may become reserved for the language. // extension(value[] p) Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "value").WithArguments("value").WithLocation(4, 15), - // (6,13): error CS9282: Extension declarations can include only methods or properties + // (6,13): error CS9282: This member is not allowed in an extension block // int this[int i] {set{}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(6, 13), // (6,26): error CS9294: 'value': an automatically-generated parameter name conflicts with an extension type parameter name // int this[int i] {set{}} Diagnostic(ErrorCode.ERR_ValueParameterSameNameAsExtensionTypeParameter, "set").WithLocation(6, 26), - // (7,13): error CS9282: Extension declarations can include only methods or properties + // (7,13): error CS9282: This member is not allowed in an extension block // int this[long i] => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 13) ); @@ -24464,7 +24464,7 @@ class C8 {} // (13,18): error CS0119: 'T' is a type, which is not valid in the given context // int T => T; Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(13, 18), - // (19,13): error CS9282: Extension declarations can include only methods or properties + // (19,13): error CS9282: This member is not allowed in an extension block // int this[int x] => T; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(19, 13), // (19,28): error CS0119: 'T' is a type, which is not valid in the given context @@ -24476,10 +24476,10 @@ class C8 {} // (32,25): error CS0119: 'T' is a type, which is not valid in the given context // static int T => T; Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(32, 25), - // (43,13): error CS9282: Extension declarations can include only methods or properties + // (43,13): error CS9282: This member is not allowed in an extension block // int this[int x] => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(43, 13), - // (48,13): error CS9282: Extension declarations can include only methods or properties + // (48,13): error CS9282: This member is not allowed in an extension block // int this[int x] => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(48, 13) ); @@ -24566,7 +24566,7 @@ static int P1 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (27,13): error CS9282: Extension declarations can include only methods or properties + // (27,13): error CS9282: This member is not allowed in an extension block // int this[int y] Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(27, 13), // (42,23): error CS9293: Cannot use extension parameter 'short M1' in this context. @@ -24575,10 +24575,10 @@ static int P1 // (53,17): error CS9293: Cannot use extension parameter 'string P1' in this context. // P1 = "val"; Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "P1").WithArguments("string P1").WithLocation(53, 17), - // (67,13): error CS9282: Extension declarations can include only methods or properties + // (67,13): error CS9282: This member is not allowed in an extension block // int this[int x] => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(67, 13), - // (72,13): error CS9282: Extension declarations can include only methods or properties + // (72,13): error CS9282: This member is not allowed in an extension block // int this[int x] => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(72, 13) ); @@ -24700,7 +24700,7 @@ void M3(int p2, int p2) {} // (6,28): error CS9290: 'p': a parameter, local variable, or local function cannot have the same name as an extension parameter // static void M3(int p){} Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionParameter, "p").WithArguments("p").WithLocation(6, 28), - // (7,13): error CS9282: Extension declarations can include only methods or properties + // (7,13): error CS9282: This member is not allowed in an extension block // int this[int p] => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 13), // (7,22): error CS9290: 'p': a parameter, local variable, or local function cannot have the same name as an extension parameter @@ -24736,16 +24736,16 @@ static int P7 {set{}} // (6,17): error CS9291: 'value': an automatically-generated parameter name conflicts with an extension parameter name // int P2 {set{}} Diagnostic(ErrorCode.ERR_ValueParameterSameNameAsExtensionParameter, "set").WithLocation(6, 17), - // (7,13): error CS9282: Extension declarations can include only methods or properties + // (7,13): error CS9282: This member is not allowed in an extension block // int this[int x] {get=>0;} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 13), - // (8,13): error CS9282: Extension declarations can include only methods or properties + // (8,13): error CS9282: This member is not allowed in an extension block // int this[long x] {set{}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(8, 13), // (8,27): error CS9291: 'value': an automatically-generated parameter name conflicts with an extension parameter name // int this[long x] {set{}} Diagnostic(ErrorCode.ERR_ValueParameterSameNameAsExtensionParameter, "set").WithLocation(8, 27), - // (9,13): error CS9282: Extension declarations can include only methods or properties + // (9,13): error CS9282: This member is not allowed in an extension block // int this[long x, int value] {set{}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(9, 13), // (9,30): error CS9290: 'value': a parameter, local variable, or local function cannot have the same name as an extension parameter @@ -25395,7 +25395,7 @@ public MyAttr(string p) {} var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (7,13): error CS9282: Extension declarations can include only methods or properties + // (7,13): error CS9282: This member is not allowed in an extension block // int this[int y] Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 13)); } @@ -25428,7 +25428,7 @@ int this[int y] // (6,54): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [System.Runtime.CompilerServices.IndexerName(p)] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "p").WithLocation(6, 54), - // (7,13): error CS9282: Extension declarations can include only methods or properties + // (7,13): error CS9282: This member is not allowed in an extension block // int this[int y] Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 13) ); @@ -25520,7 +25520,7 @@ string M3() var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (6,15): error CS9282: Extension declarations can include only methods or properties + // (6,15): error CS9282: This member is not allowed in an extension block // class Nested Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(6, 15), // (11,24): error CS9293: Cannot use extension parameter 'int p' in this context. @@ -25556,7 +25556,7 @@ int this[int y] // (7,54): error CS8078: An expression is too long or complex to compile // [System.Runtime.CompilerServices.IndexerName(Str)] Diagnostic(ErrorCode.ERR_InsufficientStack, "Str").WithLocation(7, 54), - // (8,13): error CS9282: Extension declarations can include only methods or properties + // (8,13): error CS9282: This member is not allowed in an extension block // int this[int y] Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(8, 13) ); @@ -26086,19 +26086,19 @@ static class Extensions6 // (49,20): error CS0082: Type 'Extensions4' already reserves a member called 'set_P4' with the same parameter types // public int P4 => 4; Diagnostic(ErrorCode.ERR_MemberReserved, "P4").WithArguments("set_P4", "Extensions4").WithLocation(49, 20), - // (57,20): error CS9282: Extension declarations can include only methods or properties + // (57,20): error CS9282: This member is not allowed in an extension block // public int this[int x] => 1; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(57, 20), // (57,35): error CS0082: Type 'Extensions5' already reserves a member called 'get_Item' with the same parameter types // public int this[int x] => 1; Diagnostic(ErrorCode.ERR_MemberReserved, "1").WithArguments("get_Item", "Extensions5").WithLocation(57, 35), - // (64,20): error CS9282: Extension declarations can include only methods or properties + // (64,20): error CS9282: This member is not allowed in an extension block // public int this[long b] => 4; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(64, 20), // (64,36): error CS0082: Type 'Extensions5' already reserves a member called 'get_Item' with the same parameter types // public int this[long b] => 4; Diagnostic(ErrorCode.ERR_MemberReserved, "4").WithArguments("get_Item", "Extensions5").WithLocation(64, 36), - // (73,20): error CS9282: Extension declarations can include only methods or properties + // (73,20): error CS9282: This member is not allowed in an extension block // public int this[int x] => 1; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(73, 20), // (73,35): error CS0082: Type 'Extensions6' already reserves a member called 'get_Indexer' with the same parameter types @@ -26143,13 +26143,13 @@ public int this[int y] {set{}} // (10,20): error CS0102: The type 'Extensions1' already contains a definition for 'P1' // public int P1 {set{}} Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P1").WithArguments("Extensions1", "P1").WithLocation(10, 20), - // (18,20): error CS9282: Extension declarations can include only methods or properties + // (18,20): error CS9282: This member is not allowed in an extension block // public int this[int x] => 1; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(18, 20), // (23,20): error CS0111: Type 'Extensions2' already defines a member called 'this' with the same parameter types // public int this[int y] {set{}} Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "Extensions2").WithLocation(23, 20), - // (23,20): error CS9282: Extension declarations can include only methods or properties + // (23,20): error CS9282: This member is not allowed in an extension block // public int this[int y] {set{}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(23, 20) ); @@ -27213,7 +27213,7 @@ void Item(U x) {} // (8,13): error CS0102: The type 'Extensions' already contains a definition for 'Item' // int this[T x] => default; Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "this").WithArguments("Extensions", "Item").WithLocation(8, 13), - // (8,13): error CS9282: Extension declarations can include only methods or properties + // (8,13): error CS9282: This member is not allowed in an extension block // int this[T x] => default; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(8, 13) ); @@ -27253,13 +27253,13 @@ int this[U x] { set{}} var comp = CreateCompilation(src); comp.VerifyDiagnostics( - // (8,13): error CS9282: Extension declarations can include only methods or properties + // (8,13): error CS9282: This member is not allowed in an extension block // int this[T x] => default; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(8, 13), // (18,13): error CS0111: Type 'Extensions' already defines a member called 'this' with the same parameter types // int this[U x] { set{}} Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "Extensions").WithLocation(18, 13), - // (18,13): error CS9282: Extension declarations can include only methods or properties + // (18,13): error CS9282: This member is not allowed in an extension block // int this[U x] { set{}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(18, 13) ); @@ -27301,13 +27301,13 @@ int this[int x] { set{}} // Tracked by https://github.com/dotnet/roslyn/issues/76130 : The "within a type" part of the message might be somewhat misleading comp.VerifyDiagnostics( - // (8,13): error CS9282: Extension declarations can include only methods or properties + // (8,13): error CS9282: This member is not allowed in an extension block // int this[T x] => default; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(8, 13), // (19,13): error CS0668: Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type // int this[int x] { set{}} Diagnostic(ErrorCode.ERR_InconsistentIndexerNames, "this").WithLocation(19, 13), - // (19,13): error CS9282: Extension declarations can include only methods or properties + // (19,13): error CS9282: This member is not allowed in an extension block // int this[int x] { set{}} Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(19, 13) ); @@ -30617,7 +30617,7 @@ static class E // (1,9): error CS0117: 'int' does not contain a definition for 'Const' // _ = int.Const; Diagnostic(ErrorCode.ERR_NoSuchMember, "Const").WithArguments("int", "Const").WithLocation(1, 9), - // (7,26): error CS9282: Extension declarations can include only methods or properties + // (7,26): error CS9282: This member is not allowed in an extension block // public const int Const = 42; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Const").WithLocation(7, 26)); } @@ -31352,7 +31352,7 @@ static class Nested { } var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (6,22): error CS9282: Extension declarations can include only methods or properties + // (6,22): error CS9282: This member is not allowed in an extension block // static class Nested { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(6, 22)); @@ -34214,7 +34214,7 @@ static Expression> Test() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (8,23): error CS9282: Extension declarations can include only methods or properties + // (8,23): error CS9282: This member is not allowed in an extension block // public string this[string s] => o + s; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(8, 23), // (24,26): error CS0021: Cannot apply indexing with [] to an expression of type 'object' @@ -35021,7 +35021,7 @@ public static void M4() { } // (14,20): error CS9303: 'P3': cannot declare instance members in an extension block with an unnamed receiver parameter // public int P3 => 0; // 2 Diagnostic(ErrorCode.ERR_InstanceMemberWithUnnamedExtensionsParameter, "P3").WithArguments("P3").WithLocation(14, 20), - // (16,20): error CS9282: Extension declarations can include only methods or properties + // (16,20): error CS9282: This member is not allowed in an extension block // public int this[int j] => 0; // 3 Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(16, 20), // (16,20): error CS9303: 'this[]': cannot declare instance members in an extension block with an unnamed receiver parameter @@ -35255,13 +35255,13 @@ static class E // (6,29): error CS0106: The modifier 'abstract' is not valid for this item // public abstract int P { get; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "P").WithArguments("abstract").WithLocation(6, 29), - // (6,29): error CS9282: Extension declarations can include only methods or properties + // (6,29): error CS9282: This member is not allowed in an extension block // public abstract int P { get; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "P").WithLocation(6, 29), // (7,29): error CS0106: The modifier 'abstract' is not valid for this item // public abstract int P2 { set; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "P2").WithArguments("abstract").WithLocation(7, 29), - // (7,29): error CS9282: Extension declarations can include only methods or properties + // (7,29): error CS9282: This member is not allowed in an extension block // public abstract int P2 { set; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "P2").WithLocation(7, 29), // (7,34): error CS8051: Auto-implemented properties must have get accessors. @@ -35270,7 +35270,7 @@ static class E // (8,29): error CS0106: The modifier 'abstract' is not valid for this item // public abstract int this[int j] { get; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("abstract").WithLocation(8, 29), - // (8,29): error CS9282: Extension declarations can include only methods or properties + // (8,29): error CS9282: This member is not allowed in an extension block // public abstract int this[int j] { get; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(8, 29), // (8,43): error CS0501: 'E.extension(int).this[int].get' must declare a body because it is not marked abstract, extern, or partial @@ -35303,7 +35303,7 @@ static class E // (7,24): error CS0106: The modifier 'new' is not valid for this item // public new int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("new").WithLocation(7, 24), - // (7,24): error CS9282: Extension declarations can include only methods or properties + // (7,24): error CS9282: This member is not allowed in an extension block // public new int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 24)); } @@ -35333,7 +35333,7 @@ static class E // (7,29): error CS0106: The modifier 'override' is not valid for this item // public override int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("override").WithLocation(7, 29), - // (7,29): error CS9282: Extension declarations can include only methods or properties + // (7,29): error CS9282: This member is not allowed in an extension block // public override int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 29)); } @@ -35366,7 +35366,7 @@ partial void M() { } // (7,21): error CS0751: A partial member must be declared within a partial type // partial int P { get; set; } Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "P").WithLocation(7, 21), - // (9,21): error CS9282: Extension declarations can include only methods or properties + // (9,21): error CS9282: This member is not allowed in an extension block // partial int this[int j] { get; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(9, 21), // (9,21): error CS0751: A partial member must be declared within a partial type @@ -35399,7 +35399,7 @@ sealed void M() { } // (7,20): error CS0106: The modifier 'sealed' is not valid for this item // sealed int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("sealed").WithLocation(7, 20), - // (7,20): error CS9282: Extension declarations can include only methods or properties + // (7,20): error CS9282: This member is not allowed in an extension block // sealed int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 20)); } @@ -35436,7 +35436,7 @@ readonly void M() { } // (8,22): error CS0106: The modifier 'readonly' is not valid for this item // readonly int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("readonly").WithLocation(8, 22), - // (8,22): error CS9282: Extension declarations can include only methods or properties + // (8,22): error CS9282: This member is not allowed in an extension block // readonly int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(8, 22)); } @@ -35466,7 +35466,7 @@ required void M() { } // (7,22): error CS0106: The modifier 'required' is not valid for this item // required int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("required").WithLocation(7, 22), - // (7,22): error CS9282: Extension declarations can include only methods or properties + // (7,22): error CS9282: This member is not allowed in an extension block // required int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 22)); } @@ -35496,7 +35496,7 @@ extern void M() { } // (7,20): error CS0106: The modifier 'extern' is not valid for this item // extern int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("extern").WithLocation(7, 20), - // (7,20): error CS9282: Extension declarations can include only methods or properties + // (7,20): error CS9282: This member is not allowed in an extension block // extern int this[int j] { get => 0; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 20)); } @@ -35517,7 +35517,7 @@ static class E """; var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); comp.VerifyEmitDiagnostics( - // (7,21): error CS9282: Extension declarations can include only methods or properties + // (7,21): error CS9282: This member is not allowed in an extension block // unsafe int* this[int j] { get => throw null; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(7, 21)); } @@ -35553,13 +35553,13 @@ protected void M() { } // (8,45): error CS9302: 'E.extension(int).P3.set': new protected member declared in an extension block // public int P3 { get => 0; protected set { } } Diagnostic(ErrorCode.ERR_ProtectedInExtension, "set").WithArguments("E.extension(int).P3.set").WithLocation(8, 45), - // (9,23): error CS9282: Extension declarations can include only methods or properties + // (9,23): error CS9282: This member is not allowed in an extension block // protected int this[int j] { get => throw null; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(9, 23), // (9,23): error CS9302: 'E.extension(int).this[int]': new protected member declared in an extension block // protected int this[int j] { get => throw null; } Diagnostic(ErrorCode.ERR_ProtectedInExtension, "this").WithArguments("E.extension(int).this[int]").WithLocation(9, 23), - // (10,20): error CS9282: Extension declarations can include only methods or properties + // (10,20): error CS9282: This member is not allowed in an extension block // public int this[int j, int k] { protected get => throw null; set { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(10, 20), // (10,51): error CS9302: 'E.extension(int).this[int, int].get': new protected member declared in an extension block @@ -35598,13 +35598,13 @@ protected internal void M() { } // (8,54): error CS9302: 'E.extension(int).P3.set': new protected member declared in an extension block // public int P3 { get => 0; protected internal set { } } Diagnostic(ErrorCode.ERR_ProtectedInExtension, "set").WithArguments("E.extension(int).P3.set").WithLocation(8, 54), - // (9,32): error CS9282: Extension declarations can include only methods or properties + // (9,32): error CS9282: This member is not allowed in an extension block // protected internal int this[int j] { get => throw null; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(9, 32), // (9,32): error CS9302: 'E.extension(int).this[int]': new protected member declared in an extension block // protected internal int this[int j] { get => throw null; } Diagnostic(ErrorCode.ERR_ProtectedInExtension, "this").WithArguments("E.extension(int).this[int]").WithLocation(9, 32), - // (10,20): error CS9282: Extension declarations can include only methods or properties + // (10,20): error CS9282: This member is not allowed in an extension block // public int this[int j, int k] { protected internal get => throw null; set { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(10, 20), // (10,60): error CS9302: 'E.extension(int).this[int, int].get': new protected member declared in an extension block @@ -35643,13 +35643,13 @@ private protected void M() { } // (8,53): error CS9302: 'E.extension(int).P3.set': new protected member declared in an extension block // public int P3 { get => 0; private protected set { } } Diagnostic(ErrorCode.ERR_ProtectedInExtension, "set").WithArguments("E.extension(int).P3.set").WithLocation(8, 53), - // (9,31): error CS9282: Extension declarations can include only methods or properties + // (9,31): error CS9282: This member is not allowed in an extension block // private protected int this[int j] { get => throw null; } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(9, 31), // (9,31): error CS9302: 'E.extension(int).this[int]': new protected member declared in an extension block // private protected int this[int j] { get => throw null; } Diagnostic(ErrorCode.ERR_ProtectedInExtension, "this").WithArguments("E.extension(int).this[int]").WithLocation(9, 31), - // (10,20): error CS9282: Extension declarations can include only methods or properties + // (10,20): error CS9282: This member is not allowed in an extension block // public int this[int j, int k] { private protected get => throw null; set { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "this").WithLocation(10, 20), // (10,59): error CS9302: 'E.extension(int).this[int, int].get': new protected member declared in an extension block diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 49ae145e0d4f3..6298f9c98c354 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -3195,7 +3195,7 @@ public class Nested { } // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).Nested' that could not be resolved // /// Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).Nested").WithArguments("extension(int).Nested").WithLocation(1, 16), - // (6,22): error CS9282: Extension declarations can include only methods or properties + // (6,22): error CS9282: This member is not allowed in an extension block // public class Nested { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(6, 22)); @@ -3238,7 +3238,7 @@ interface I // (6,16): error CS0541: 'E.extension(int).M()': explicit interface declaration can only be declared in a class, record, struct or interface // void I.M() { } Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationInNonClassOrStruct, "M").WithArguments("E.extension(int).M()").WithLocation(6, 16), - // (6,16): error CS9282: Extension declarations can include only methods or properties + // (6,16): error CS9282: This member is not allowed in an extension block // void I.M() { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "M").WithLocation(6, 16)); @@ -3687,7 +3687,7 @@ public static void M() { } // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).extension(string).M' that could not be resolved // /// Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).extension(string).M").WithArguments("extension(int).extension(string).M").WithLocation(1, 16), - // (6,9): error CS9282: Extension declarations can include only methods or properties + // (6,9): error CS9282: This member is not allowed in an extension block // extension(string) Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(6, 9)); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs index 8e383919cef6a..8d7bf2990fe4a 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs @@ -1568,7 +1568,7 @@ static class C """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,9): error CS9282: Extension declarations can include only methods or properties + // (5,9): error CS9282: This member is not allowed in an extension block // extension(Type2) { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(5, 9)); @@ -2143,7 +2143,7 @@ static class C """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,19): error CS9282: Extension declarations can include only methods or properties + // (5,19): error CS9282: This member is not allowed in an extension block // const int i = 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "i").WithLocation(5, 19)); @@ -2226,7 +2226,7 @@ static class C // (5,19): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed int field[10]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "field[10]").WithLocation(5, 19), - // (5,19): error CS9282: Extension declarations can include only methods or properties + // (5,19): error CS9282: This member is not allowed in an extension block // fixed int field[10]; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "field").WithLocation(5, 19)); @@ -2308,13 +2308,13 @@ static class C """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,35): error CS9282: Extension declarations can include only methods or properties + // (5,35): error CS9282: This member is not allowed in an extension block // event System.EventHandler eventField; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "eventField").WithLocation(5, 35), - // (5,35): error CS9282: Extension declarations can include only methods or properties + // (5,35): error CS9282: This member is not allowed in an extension block // event System.EventHandler eventField; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "eventField").WithLocation(5, 35), - // (5,35): error CS9282: Extension declarations can include only methods or properties + // (5,35): error CS9282: This member is not allowed in an extension block // event System.EventHandler eventField; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "eventField").WithLocation(5, 35), // (5,35): warning CS0067: The event 'C.extension(object).eventField' is never used @@ -2395,13 +2395,13 @@ event System.EventHandler Event { add { } remove { } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,35): error CS9282: Extension declarations can include only methods or properties + // (5,35): error CS9282: This member is not allowed in an extension block // event System.EventHandler Event { add { } remove { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Event").WithLocation(5, 35), - // (5,43): error CS9282: Extension declarations can include only methods or properties + // (5,43): error CS9282: This member is not allowed in an extension block // event System.EventHandler Event { add { } remove { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "add").WithLocation(5, 43), - // (5,51): error CS9282: Extension declarations can include only methods or properties + // (5,51): error CS9282: This member is not allowed in an extension block // event System.EventHandler Event { add { } remove { } } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "remove").WithLocation(5, 51)); @@ -2583,7 +2583,7 @@ class Nested { } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,15): error CS9282: Extension declarations can include only methods or properties + // (5,15): error CS9282: This member is not allowed in an extension block // class Nested { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(5, 15)); @@ -2646,7 +2646,7 @@ static class C // (5,9): error CS1520: Method must have a return type // Constructor() { } Diagnostic(ErrorCode.ERR_MemberNeedsType, "Constructor").WithLocation(5, 9), - // (5,9): error CS9282: Extension declarations can include only methods or properties + // (5,9): error CS9282: This member is not allowed in an extension block // Constructor() { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Constructor").WithLocation(5, 9)); @@ -2717,7 +2717,7 @@ static Constructor() { } // (5,16): error CS1520: Method must have a return type // static Constructor() { } Diagnostic(ErrorCode.ERR_MemberNeedsType, "Constructor").WithLocation(5, 16), - // (5,16): error CS9282: Extension declarations can include only methods or properties + // (5,16): error CS9282: This member is not allowed in an extension block // static Constructor() { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Constructor").WithLocation(5, 16)); @@ -2785,7 +2785,7 @@ static class C """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,10): error CS9282: Extension declarations can include only methods or properties + // (5,10): error CS9282: This member is not allowed in an extension block // ~Finalizer() { } Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Finalizer").WithLocation(5, 10)); @@ -2854,7 +2854,7 @@ static class C """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,13): error CS9282: Extension declarations can include only methods or properties + // (5,13): error CS9282: This member is not allowed in an extension block // int field; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "field").WithLocation(5, 13), // (5,13): warning CS0169: The field 'C.extension(object).field' is never used @@ -3024,7 +3024,7 @@ static class C // (5,39): error CS0563: One of the parameters of a binary operator must be the containing type // public static object operator +(object a, object b) => a; Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "+").WithLocation(5, 39), - // (5,39): error CS9282: Extension declarations can include only methods or properties + // (5,39): error CS9282: This member is not allowed in an extension block // public static object operator +(object a, object b) => a; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "+").WithLocation(5, 39)); @@ -3121,7 +3121,7 @@ static class C // (5,41): error CS0556: User-defined conversion must convert to or from the enclosing type // public static implicit operator int(object t) => 0; Diagnostic(ErrorCode.ERR_ConversionNotInvolvingContainedType, "int").WithLocation(5, 41), - // (5,41): error CS9282: Extension declarations can include only methods or properties + // (5,41): error CS9282: This member is not allowed in an extension block // public static implicit operator int(object t) => 0; Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "int").WithLocation(5, 41)); From 6cbca86737334aefa974873093f72e7d455b8be8 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 3 Jun 2025 11:27:10 -0700 Subject: [PATCH 04/10] Address feedback --- .../CSharp/Portable/Binder/Binder_Crefs.cs | 39 ++-- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../Parser/DocumentationCommentParser.cs | 4 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Test/Emit3/Semantics/ExtensionTests2.cs | 184 +++++++++++++++++- .../Test/Syntax/Parsing/CrefParsingTests.cs | 38 ++-- 19 files changed, 295 insertions(+), 40 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs index f785d2f19ae82..8758bf909e6f3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs @@ -223,6 +223,7 @@ private ImmutableArray BindIndexerMemberCref(IndexerMemberCrefSyntax syn private ImmutableArray BindExtensionMemberCref(ExtensionMemberCrefSyntax syntax, NamespaceOrTypeSymbol? containerOpt, out Symbol? ambiguityWinner, BindingDiagnosticBag diagnostics) { // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle extension operators + CheckFeatureAvailability(syntax, MessageID.IDS_FeatureExtensions, diagnostics); if (containerOpt is not NamedTypeSymbol namedContainer) { @@ -260,13 +261,29 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym { Debug.Assert(name is not null); + Debug.Assert(syntax.Parameters is not null); + ImmutableArray extensionParameterSymbols = BindCrefParameters(syntax.Parameters, diagnostics); + + // Use signature method symbols to match extension blocks + var providedExtensionSignature = new SignatureOnlyMethodSymbol( + methodKind: MethodKind.Ordinary, + typeParameters: IndexedTypeParameterSymbol.TakeSymbols(extensionArity), + parameters: extensionParameterSymbols, + callingConvention: Cci.CallingConvention.Default, + // These are ignored by this specific MemberSignatureComparer. + containingType: null, + name: null, + refKind: RefKind.None, + isInitOnly: false, + isStatic: false, + returnType: default, + refCustomModifiers: [], + explicitInterfaceImplementations: []); + LookupOptions options = LookupOptions.AllMethodsOnArityZero | LookupOptions.MustNotBeParameter; CompoundUseSiteInfo useSiteInfo = this.GetNewCompoundUseSiteInfo(diagnostics); ArrayBuilder? sortedSymbolsBuilder = null; - Debug.Assert(syntax.Parameters is not null); - ImmutableArray extensionParameterSymbols = BindCrefParameters(syntax.Parameters, diagnostics); - foreach (var nested in container.GetTypeMembers()) { if (!nested.IsExtension || nested.Arity != extensionArity || nested.ExtensionParameter is null) @@ -276,7 +293,6 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym var constructedNested = (NamedTypeSymbol)ConstructWithCrefTypeParameters(extensionArity, extensionTypeArguments, nested); - // Use signature method symbols to match extension blocks var candidateExtensionSignature = new SignatureOnlyMethodSymbol( methodKind: MethodKind.Ordinary, typeParameters: IndexedTypeParameterSymbol.TakeSymbols(constructedNested.Arity), @@ -292,21 +308,6 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym refCustomModifiers: [], explicitInterfaceImplementations: []); - var providedExtensionSignature = new SignatureOnlyMethodSymbol( - methodKind: MethodKind.Ordinary, - typeParameters: IndexedTypeParameterSymbol.TakeSymbols(constructedNested.Arity), - parameters: extensionParameterSymbols, - callingConvention: Cci.CallingConvention.Default, - // These are ignored by this specific MemberSignatureComparer. - containingType: null, - name: null, - refKind: RefKind.None, - isInitOnly: false, - isStatic: false, - returnType: default, - refCustomModifiers: [], - explicitInterfaceImplementations: []); - if (!MemberSignatureComparer.CrefComparer.Equals(candidateExtensionSignature, providedExtensionSignature)) { continue; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 586502f7d0ee5..7a097784dde5a 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -8185,4 +8185,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ '#!' directives can be only used in scripts or file-based programs + + An extension member syntax is disallowed in nested position within an extension member syntax + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 7211e43d60c83..3db0d158c3a63 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2400,7 +2400,7 @@ internal enum ErrorCode ERR_ExpressionTreeContainsNamedArgumentOutOfPosition = 9307, ERR_OperatorsMustBePublic = 9308, - // available 9309, + ERR_MisplacedExtension = 9309, ERR_OperatorMustReturnVoid = 9310, ERR_CloseUnimplementedInterfaceMemberOperatorMismatch = 9311, ERR_OperatorMismatchOnOverride = 9312, diff --git a/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs b/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs index 08e899e4f1ea2..e5417a5f08f5f 100644 --- a/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs @@ -1001,6 +1001,10 @@ private MemberCrefSyntax ParsePossibleExtensionMemberCref() SyntaxToken dotToken = EatToken(SyntaxKind.DotToken); MemberCrefSyntax member = ParseMemberCref(); + if (member is ExtensionMemberCrefSyntax) + { + member = AddErrorAsWarning(member, ErrorCode.ERR_MisplacedExtension); + } return SyntaxFactory.ExtensionMemberCref(ConvertToKeyword(identifierToken), typeArguments, parameters, dotToken, member); } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index c6c1990c424a8..865da5ae7c5fa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1472,6 +1472,11 @@ Žádná přetížená metoda {0} neodpovídá ukazateli na funkci {1}. + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Neočekávané klíčové slovo record. Měli jste na mysli record struct nebo record class? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 606c733c6dd5e..75c280a0f4d2c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1472,6 +1472,11 @@ Keine Überladung für "{0}" stimmt mit dem Funktionszeiger "{1}" überein. + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Unerwartetes Schlüsselwort „Datensatz“. Meinten Sie „Datensatzstruktur“ oder „Datensatzklasse“? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index b6dfde5a6d011..b35118a0c5786 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1472,6 +1472,11 @@ Ninguna sobrecarga correspondiente a "{0}" coincide con el puntero de función "{1}". + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Palabra clave \"record\" inesperada. ¿Quería decir \"record struct\" o \"record class\"? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 461b11b621372..ed5b708a8cdbb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1472,6 +1472,11 @@ Aucune surcharge pour '{0}' ne correspond au pointeur de fonction '{1}' + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Mot clé 'record' inattendu. Vouliez-vous dire « struct d’enregistrement » ou « classe d’enregistrement » ? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8d49ac7b23edc..96b6ebc38e083 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1472,6 +1472,11 @@ Nessun overload per '{0}' corrisponde al puntatore a funzione '{1}' + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Parola chiave 'record' imprevista. Si intendeva 'struct record' o 'classe record'? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index d5eb43d80c532..f2f3c939110ff 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1472,6 +1472,11 @@ 関数ポインター '{1}' に一致する '{0}' のオーバーロードはありません + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? 予期しないキーワード 'record' です。'record struct' または 'record class' のつもりでしたか? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index dd96359efc4ee..a795041fd9de8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1472,6 +1472,11 @@ 함수 포인터 '{1}'과(와) 일치하는 '{0}'에 대한 오버로드가 없습니다. + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? 예기치 않은 'record' 키워드가 있습니다. 'record struct' 또는 'record class'를 사용할까요? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 278ee671be37d..86b048746a211 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1472,6 +1472,11 @@ Żadne z przeciążeń dla elementu „{0}” nie pasuje do wskaźnika funkcji „{1}” + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Nieoczekiwane słowo kluczowe „record”. Czy chodziło o „record struct” lub „record class”? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 4011f9eabd8ec..d513278e57ede 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1472,6 +1472,11 @@ Nenhuma sobrecarga de '{0}' corresponde ao ponteiro de função '{1}' + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Palavra-chave inesperada “record”. Você quis dizer “record struct” or “record class”? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 2e223b8e29443..420fc1c5c85b6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1472,6 +1472,11 @@ Нет перегруженного метода для "{0}", который соответствует указателю на функцию "{1}". + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Непредвиденное ключевое слово \"record\". Возможно, вы имели в виду \"record struct\" или \"record class\"? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 72000a1b54dc3..39de08beef403 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1472,6 +1472,11 @@ '{0}' için aşırı yüklemelerin hiçbiri '{1}' işlev işaretçisiyle eşleşmiyor + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Beklenmeyen 'record' anahtar sözcüğü. 'record struct' veya 'record class' mi demek istediniz? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 25d9f43badf87..ee20fa951e0ad 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1472,6 +1472,11 @@ “{0}”没有与函数指针“{1}”匹配的重载 + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? 意外的关键字 \"record\"。你的意思是 \"record struct\" 还是 \"record class\"? diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 81dc3990f5b6d..57eb810876861 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1472,6 +1472,11 @@ '{0}' 沒有任何多載符合函式指標 '{1}' + + An extension member syntax is disallowed in nested position within an extension member syntax + An extension member syntax is disallowed in nested position within an extension member syntax + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? 未預期的關鍵字 'record'。您是指 'record struct' 或 'record class'? diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 6298f9c98c354..cde642ba7d475 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -2294,33 +2294,58 @@ public void Cref_01() var src = """ /// /// +/// +/// static class E { extension(int i) { + /// + /// + /// public void M(string s) => throw null!; } } """; var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); - comp.VerifyEmitDiagnostics(); + comp.VerifyEmitDiagnostics( + // (10,24): warning CS1574: XML comment has cref attribute 'M(string)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "M(string)").WithArguments("M(string)").WithLocation(10, 24)); var e = comp.GetMember("E"); AssertEx.Equal(""" + + """, e.GetDocumentationCommentXml()); AssertEx.Equal("T:E.<>E__0", e.GetTypeMembers().Single().GetDocumentationCommentId()); + var mSkeleton = comp.GetMember("E").GetTypeMembers().Single().GetMember("M"); + AssertEx.Equal(""" + + + + + + +""", mSkeleton.GetDocumentationCommentXml()); + var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal([ "(E.extension(int).M(string), void E.<>E__0.M(System.String s))", - "(E.M(int, string), void E.M(this System.Int32 i, System.String s))"], + "(E.M(int, string), void E.M(this System.Int32 i, System.String s))", + "(E.extension(int).M, void E.<>E__0.M(System.String s))", + "(E.M, void E.M(this System.Int32 i, System.String s))", + "(M(int, string), void E.M(this System.Int32 i, System.String s))", + "(M(string), null)", + "(M, void E.M(this System.Int32 i, System.String s))"], PrintXmlCrefSymbols(tree, model)); } @@ -3414,6 +3439,7 @@ public static void M() { } public void Cref_36() { // method named "extension" + // Note: one cannot refer to extension block var src = """ /// /// @@ -3422,15 +3448,32 @@ static class E public static void extension() { } public static void extension(int i) { } } + +/// +/// +static class E2 +{ + extension(int) + { + } +} """; var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); - comp.VerifyEmitDiagnostics(); + comp.VerifyEmitDiagnostics( + // (9,16): warning CS1574: XML comment has cref attribute 'extension()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E2.extension()").WithArguments("extension()").WithLocation(9, 16), + // (10,16): warning CS1574: XML comment has cref attribute 'extension(int)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E2.extension(int)").WithArguments("extension(int)").WithLocation(10, 16)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal([ "(E.extension(), void E.extension())", - "(E.extension(int), void E.extension(System.Int32 i))"], + "(E.extension(int), void E.extension(System.Int32 i))", + "(E2.extension(), null)", + "(E2.extension(int), null)"], PrintXmlCrefSymbols(tree, model)); } @@ -3684,9 +3727,12 @@ public static void M() { } """; var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics( - // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).extension(string).M' that could not be resolved + // (1,16): warning CS1584: XML comment has syntactically incorrect cref attribute 'E.extension(int).extension(string).M' // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).extension(string).M").WithArguments("extension(int).extension(string).M").WithLocation(1, 16), + Diagnostic(ErrorCode.WRN_BadXMLRefSyntax, "E.extension(int).extension(string).M").WithArguments("E.extension(int).extension(string).M").WithLocation(1, 16), + // (1,33): warning CS1658: An extension member syntax is disallowed in nested position within an extension member syntax. See also error CS9309. + // /// + Diagnostic(ErrorCode.WRN_ErrorOverride, "extension(string).M").WithArguments("An extension member syntax is disallowed in nested position within an extension member syntax", "9309").WithLocation(1, 33), // (6,9): error CS9282: This member is not allowed in an extension block // extension(string) Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(6, 9)); @@ -3735,6 +3781,7 @@ static class E [Fact] public void Cref_47() { + // TODO2 error in Ioperation // Xml doc APIs on PE symbols var src = """ static class E @@ -3762,7 +3809,7 @@ protected internal override string GetDocumentationForSymbol(string documentatio return documentationMemberID; } - public override bool Equals(object obj) => throw new NotImplementedException(); + public override bool Equals(object obj) => (object)this == obj; public override int GetHashCode() => throw new NotImplementedException(); } @@ -3881,5 +3928,128 @@ public static void M() { } var model = comp.GetSemanticModel(tree); AssertEx.Equal(["(E.extension(int).@M, void E.<>E__0.M())"], PrintXmlCrefSymbols(tree, model)); } + + [Fact] + public void Cref_52() + { + // unqualified reference + var src = """ +static class E +{ + extension(int) + { + /// + public static void M() { } + + /// + public static void M2() { } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider allowing unqualified references in CREF + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (5,24): warning CS1574: XML comment has cref attribute 'extension(int).M2' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M2").WithArguments("extension(int).M2").WithLocation(5, 24), + // (8,24): warning CS1574: XML comment has cref attribute 'extension(int).M' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M").WithArguments("extension(int).M").WithLocation(8, 24)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal(["(extension(int).M2, null)", "(extension(int).M, null)"], PrintXmlCrefSymbols(tree, model)); + + src = """ +static class E +{ + static class Nested + { + /// + public static void M() { } + + /// + public static void M2() { } + } +} +"""; + comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void Cref_53() + { + var src = """ +static class E +{ + extension(int i) + { + /// + /// + public void M(string s) => throw null!; + } + extension(int i) + { + public void M2(string s) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (5,24): warning CS1574: XML comment has cref attribute 'M2(string)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "M2(string)").WithArguments("M2(string)").WithLocation(5, 24)); + + var mSkeleton = comp.GetMember("E").GetTypeMembers().First().GetMember("M"); + AssertEx.Equal(""" + + + + + +""", mSkeleton.GetDocumentationCommentXml()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(M2(string), null)", + "(M2, void E.M2(this System.Int32 i, System.String s))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_54() + { + var libSrc = """ +public static class E +{ + extension(int) + { + public static void M() { } + } +} +"""; + var libComp = CreateCompilation(libSrc); + var libRef = libComp.EmitToImageReference(); + + var src = """ +/// +class C +{ +} +"""; + var comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.Regular13.WithDocumentationMode(DocumentationMode.Diagnose)); + comp.VerifyEmitDiagnostics( + // (1,18): error CS8652: The feature 'extensions' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // /// + Diagnostic(ErrorCode.ERR_FeatureInPreview, "extension(int).M").WithArguments("extensions").WithLocation(1, 18)); + + comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.RegularNext.WithDocumentationMode(DocumentationMode.Diagnose)); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/CrefParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/CrefParsingTests.cs index aa6a4365f310b..da1e331238d5e 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/CrefParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/CrefParsingTests.cs @@ -3580,7 +3580,7 @@ public void AliasQualifiedGenericTypeConstructor() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_01() { - UsingNode("extension"); + UsingNode("extension", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.NameMemberCref); { @@ -3594,7 +3594,7 @@ public void ExtensionCref_01() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_02() { - UsingNode("E.extension"); + UsingNode("E.extension", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.QualifiedCref); { @@ -3617,7 +3617,7 @@ public void ExtensionCref_02() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_03() { - UsingNode("E.extension()"); + UsingNode("E.extension()", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.QualifiedCref); { @@ -3645,7 +3645,7 @@ public void ExtensionCref_03() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_04() { - UsingNode("E.extension(int)"); + UsingNode("E.extension(int)", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.QualifiedCref); { @@ -3680,7 +3680,7 @@ public void ExtensionCref_04() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_05() { - UsingNode("E.extension{T}"); + UsingNode("E.extension{T}", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.QualifiedCref); { @@ -3712,7 +3712,7 @@ public void ExtensionCref_05() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_06() { - UsingNode("E.extension{T}()"); + UsingNode("E.extension{T}()", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.QualifiedCref); { @@ -3749,7 +3749,7 @@ public void ExtensionCref_06() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_07() { - UsingNode("E.extension{T}(int)"); + UsingNode("E.extension{T}(int)", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.QualifiedCref); { @@ -3793,7 +3793,13 @@ public void ExtensionCref_07() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_08() { - UsingNode("E.extension{T}(int)."); + UsingNode("E.extension{T}(int).", options: TestOptions.RegularPreviewWithDocumentationComments, + // (1,16): warning CS1584: XML comment has syntactically incorrect cref attribute 'E.extension{T}(int).' + // /// + Diagnostic(ErrorCode.WRN_BadXMLRefSyntax, "E.extension{T}(int).").WithArguments("E.extension{T}(int).").WithLocation(1, 16), + // (1,36): warning CS1658: Identifier expected. See also error CS1001. + // /// + Diagnostic(ErrorCode.WRN_ErrorOverride, @"""").WithArguments("Identifier expected", "1001").WithLocation(1, 36)); N(SyntaxKind.QualifiedCref); { @@ -3842,7 +3848,7 @@ public void ExtensionCref_08() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_09() { - UsingNode("E.extension{T}(int).M"); + UsingNode("E.extension{T}(int).M", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.QualifiedCref); { @@ -3891,7 +3897,7 @@ public void ExtensionCref_09() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_10() { - UsingNode("E.extension{T}().M"); + UsingNode("E.extension{T}().M", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.QualifiedCref); { @@ -3933,7 +3939,7 @@ public void ExtensionCref_10() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_11() { - UsingNode("E.extension().M"); + UsingNode("E.extension().M", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.QualifiedCref); { @@ -3966,7 +3972,13 @@ public void ExtensionCref_11() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_12() { - UsingNode("E.extension().extension().M"); + UsingNode("E.extension().extension().M", options: TestOptions.RegularPreviewWithDocumentationComments, + // (1,16): warning CS1584: XML comment has syntactically incorrect cref attribute 'E.extension().extension().M' + // /// + Diagnostic(ErrorCode.WRN_BadXMLRefSyntax, "E.extension().extension().M").WithArguments("E.extension().extension().M").WithLocation(1, 16), + // (1,30): warning CS1658: An extension member syntax is disallowed in nested position within an extension member syntax. See also error CS9309. + // /// + Diagnostic(ErrorCode.WRN_ErrorOverride, "extension().M").WithArguments("An extension member syntax is disallowed in nested position within an extension member syntax", "9309").WithLocation(1, 30)); N(SyntaxKind.QualifiedCref); { @@ -4009,7 +4021,7 @@ public void ExtensionCref_12() [Fact, CompilerTrait(CompilerFeature.Extensions)] public void ExtensionCref_13() { - UsingNode("extension().M"); + UsingNode("extension().M", options: TestOptions.RegularPreviewWithDocumentationComments); N(SyntaxKind.ExtensionMemberCref); { From c139bae2e73b6ced07fc5017e8ca8d6fee9b94e4 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 3 Jun 2025 21:49:03 -0700 Subject: [PATCH 05/10] Tweak --- src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 278cbd4318a8a..7164fe902f782 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -830,6 +830,7 @@ or ErrorCode.ERR_PartialWrongTypeParams or ErrorCode.ERR_PartialWrongConstraints or ErrorCode.ERR_NoImplicitConvCast or ErrorCode.ERR_PartialMisplaced + or ErrorCode.ERR_PartialMisplaced or ErrorCode.ERR_ImportedCircularBase or ErrorCode.ERR_UseDefViolationOut or ErrorCode.ERR_ArraySizeInDeclaration From 015a342426f14a54df34f5a37d418d98b9cadd23 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 9 Jun 2025 18:02:51 -0700 Subject: [PATCH 06/10] Reflect LDM decisions --- .../Test/Emit3/Semantics/ExtensionTests2.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index cde642ba7d475..515b1f6d00fee 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -3438,8 +3438,7 @@ public static void M() { } [Fact] public void Cref_36() { - // method named "extension" - // Note: one cannot refer to extension block + // can refer to method named "extension", but cannot refer to extension block var src = """ /// /// @@ -3456,6 +3455,10 @@ static class E2 extension(int) { } + + /// + /// + static void M() { } } """; var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); @@ -3465,7 +3468,13 @@ static class E2 Diagnostic(ErrorCode.WRN_BadXMLRef, "E2.extension()").WithArguments("extension()").WithLocation(9, 16), // (10,16): warning CS1574: XML comment has cref attribute 'extension(int)' that could not be resolved // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "E2.extension(int)").WithArguments("extension(int)").WithLocation(10, 16)); + Diagnostic(ErrorCode.WRN_BadXMLRef, "E2.extension(int)").WithArguments("extension(int)").WithLocation(10, 16), + // (17,20): warning CS1574: XML comment has cref attribute 'extension()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension()").WithArguments("extension()").WithLocation(17, 20), + // (18,20): warning CS1574: XML comment has cref attribute 'extension(int)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int)").WithArguments("extension(int)").WithLocation(18, 20)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -3473,7 +3482,9 @@ static class E2 "(E.extension(), void E.extension())", "(E.extension(int), void E.extension(System.Int32 i))", "(E2.extension(), null)", - "(E2.extension(int), null)"], + "(E2.extension(int), null)", + "(extension(), null)", + "(extension(int), null)"], PrintXmlCrefSymbols(tree, model)); } @@ -3946,7 +3957,7 @@ public static void M2() { } } } """; - // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider allowing unqualified references in CREF + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : cref, such unqualified references in CREF should work within context of enclosing static type var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics( // (5,24): warning CS1574: XML comment has cref attribute 'extension(int).M2' that could not be resolved From e59e48cfcf662c39f7220b584352acefb9c1c31d Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 9 Jun 2025 18:16:35 -0700 Subject: [PATCH 07/10] Address feedback --- .../CSharp/Portable/Errors/ErrorFacts.cs | 2 +- .../Test/Emit3/Semantics/ExtensionTests2.cs | 57 ++++++++++++++----- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 7164fe902f782..3b7d348d06638 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -830,7 +830,7 @@ or ErrorCode.ERR_PartialWrongTypeParams or ErrorCode.ERR_PartialWrongConstraints or ErrorCode.ERR_NoImplicitConvCast or ErrorCode.ERR_PartialMisplaced - or ErrorCode.ERR_PartialMisplaced + or ErrorCode.ERR_MisplacedExtension or ErrorCode.ERR_ImportedCircularBase or ErrorCode.ERR_UseDefViolationOut or ErrorCode.ERR_ArraySizeInDeclaration diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 515b1f6d00fee..565325c9d0a77 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -2307,6 +2307,7 @@ static class E } } """; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : cref, confirm that extension members cannot be referred to without some qualifier var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics( // (10,24): warning CS1574: XML comment has cref attribute 'M(string)' that could not be resolved @@ -3792,7 +3793,6 @@ static class E [Fact] public void Cref_47() { - // TODO2 error in Ioperation // Xml doc APIs on PE symbols var src = """ static class E @@ -3945,42 +3945,69 @@ public void Cref_52() { // unqualified reference var src = """ +/// +/// static class E { extension(int) { - /// - public static void M() { } + /// + /// + public static void M1() { } - /// - public static void M2() { } + public static void Method() { } + public static int Property => 42; } + + /// + /// + public static void M2() { } } """; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : cref, such unqualified references in CREF should work within context of enclosing static type var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics( - // (5,24): warning CS1574: XML comment has cref attribute 'extension(int).M2' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M2").WithArguments("extension(int).M2").WithLocation(5, 24), - // (8,24): warning CS1574: XML comment has cref attribute 'extension(int).M' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M").WithArguments("extension(int).M").WithLocation(8, 24)); + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).Method' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Method").WithArguments("extension(int).Method").WithLocation(1, 16), + // (2,16): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(2, 16), + // (7,24): warning CS1574: XML comment has cref attribute 'extension(int).Method' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Method").WithArguments("extension(int).Method").WithLocation(7, 24), + // (8,24): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(8, 24), + // (15,20): warning CS1574: XML comment has cref attribute 'extension(int).M2' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M2").WithArguments("extension(int).M2").WithLocation(15, 20), + // (16,20): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(16, 20)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); - AssertEx.Equal(["(extension(int).M2, null)", "(extension(int).M, null)"], PrintXmlCrefSymbols(tree, model)); + AssertEx.Equal([ + "(extension(int).Method, null)", + "(extension(int).Property, null)", + "(extension(int).Method, null)", + "(extension(int).Property, null)", + "(extension(int).M2, null)", + "(extension(int).Property, null)"], + PrintXmlCrefSymbols(tree, model)); src = """ +/// static class E { + /// static class Nested { - /// + /// public static void M() { } - /// - public static void M2() { } + public static void Method() { } } } """; From 9a2906434939dc5e7b0cd389baa23ef62030c3db Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 9 Jun 2025 21:07:09 -0700 Subject: [PATCH 08/10] Remove comment --- src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 565325c9d0a77..c6a44b7e8333f 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -2307,7 +2307,6 @@ static class E } } """; - // Tracked by https://github.com/dotnet/roslyn/issues/76130 : cref, confirm that extension members cannot be referred to without some qualifier var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics( // (10,24): warning CS1574: XML comment has cref attribute 'M(string)' that could not be resolved From 5d277a9db3841bde89327151fe391bdb6f1387da Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 9 Jun 2025 22:02:54 -0700 Subject: [PATCH 09/10] Tweaks --- .../Test/Emit3/Semantics/ExtensionTests2.cs | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 87dfc2f2a0bdb..18677af229482 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -3105,10 +3105,7 @@ static class E comp.VerifyEmitDiagnostics( // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).M' that could not be resolved // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).M").WithArguments("extension(int).M").WithLocation(1, 16), - // (4,22): error CS9295: The extended type 'int' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. - // extension(int i) - Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "i").WithArguments("int", "T").WithLocation(4, 22)); + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).M").WithArguments("extension(int).M").WithLocation(1, 16)); var e = comp.GetMember("E"); AssertEx.Equal(""" @@ -3170,10 +3167,7 @@ static class E } """; var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); - comp.VerifyEmitDiagnostics( - // (4,22): error CS9295: The extended type 'int' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. - // extension(int i) - Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "i").WithArguments("int", "T").WithLocation(4, 22)); + comp.VerifyEmitDiagnostics(); var e = comp.GetMember("E"); AssertEx.Equal(""" @@ -4101,6 +4095,12 @@ public static void Method() { } public static int Property => 42; } + /// + /// + extension(object) + { + } + /// /// public static void M2() { } @@ -4121,12 +4121,18 @@ public static void M2() { } // (8,24): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved // /// Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(8, 24), - // (15,20): warning CS1574: XML comment has cref attribute 'extension(int).M2' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M2").WithArguments("extension(int).M2").WithLocation(15, 20), + // (15,20): warning CS1574: XML comment has cref attribute 'extension(int).Method' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Method").WithArguments("extension(int).Method").WithLocation(15, 20), // (16,20): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(16, 20)); + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(16, 20), + // (21,20): warning CS1574: XML comment has cref attribute 'extension(int).M2' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M2").WithArguments("extension(int).M2").WithLocation(21, 20), + // (22,20): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(22, 20)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -4135,6 +4141,8 @@ public static void M2() { } "(extension(int).Property, null)", "(extension(int).Method, null)", "(extension(int).Property, null)", + "(extension(int).Method, null)", + "(extension(int).Property, null)", "(extension(int).M2, null)", "(extension(int).Property, null)"], PrintXmlCrefSymbols(tree, model)); From abdae5b78eed8433a29a3130ddcb6ee33bb38775 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 11 Jun 2025 14:03:02 -0700 Subject: [PATCH 10/10] Add test --- .../Test/Emit3/Semantics/ExtensionTests2.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 18677af229482..6d110dfbcc551 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -4239,5 +4239,25 @@ class C comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics(); } + + [Fact] + public void Cref_55() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public void M(string s) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'M(string)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.M(string)").WithArguments("M(string)").WithLocation(1, 16)); + } }