Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ internal override void GetCandidateExtensionMembersInSingleBinder(ArrayBuilder<S
{
Debug.Assert(members.Count == 0);

// Tracked by https://github.com/dotnet/roslyn/issues/79440 : using directives, test this flag (see TestUnusedExtensionMarksImportsAsUsed)
Copy link
Member Author

@jcouv jcouv Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Added TestUnusedExtensionMarksImportsAsUsed_02

bool callerIsSemanticModel = originalBinder.IsSemanticModelBinder;

// We need to avoid collecting multiple candidates for an extension declaration imported both through a namespace and a static class
Expand Down
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -8202,4 +8202,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_ExplicitInterfaceMemberReturnTypeMismatch" xml:space="preserve">
<value>'{0}': return type must be '{1}' to match implemented member '{2}'</value>
</data>
<data name="ParameterRequiresTypeOrIdentifier" xml:space="preserve">
<value>ParameterSyntax requires either a type or an identifier.</value>
</data>
</root>
1 change: 0 additions & 1 deletion src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,7 +1181,6 @@ forSemanticModel.Syntax is { } semanticModelSyntax &&
});
}

// Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, Ensure we are not messing up relative order of events for extension members (with relation to events for enclosing types, etc.)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Already covered by AnalyzerActions_ tests

_compilation.EventQueue.TryEnqueue(new SymbolDeclaredCompilationEvent(
_compilation, methodSymbol, semanticModelWithCachedBoundNodes));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,6 @@ Cci.ITypeDefinition Cci.ITypeDefinitionMember.ContainingTypeDefinition
return synthesizedGlobalMethod.ContainingPrivateImplementationDetailsType;
}

// Tracked by https://github.com/dotnet/roslyn/issues/78827 : code quality, share logic with Cci.ITypeMemberReference.GetContainingType implementation?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 I removed a few similar comments, as I don't think the suggestion is necessary or that it would improve the code.

if (AdaptedMethodSymbol is SynthesizedExtensionMarker marker)
{
return ((SourceMemberContainerTypeSymbol)AdaptedMethodSymbol.ContainingType.ContainingType).GetExtensionGroupingInfo().GetCorrespondingMarkerType(marker);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ ITypeDefinition ITypeDefinitionMember.ContainingTypeDefinition
{
CheckDefinitionInvariant();

// Tracked by https://github.com/dotnet/roslyn/issues/78827 : code quality, share logic with Cci.ITypeMemberReference.GetContainingType implementation?
if (AdaptedPropertySymbol.GetIsNewExtensionMember())
{
var containingType = AdaptedPropertySymbol.ContainingType;
Expand Down Expand Up @@ -249,7 +248,6 @@ ITypeReference ITypeMemberReference.GetContainingType(EmitContext context)
{
CheckDefinitionInvariant();

// Tracked by https://github.com/dotnet/roslyn/issues/78827 : code quality, share logic with Cci.ITypeMemberReference.GetContainingType implementation in MethodSymbolAdapter?
if (AdaptedPropertySymbol.GetIsNewExtensionMember())
{
var containingType = AdaptedPropertySymbol.ContainingType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13766,8 +13766,11 @@ public sealed partial class ParameterSyntax : BaseParameterSyntax
internal ParameterSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position)
: base(green, parent, position)
{
Validate();
}

private partial void Validate();

/// <summary>Gets the attribute declaration list.</summary>
public override SyntaxList<AttributeListSyntax> AttributeLists => new SyntaxList<AttributeListSyntax>(GetRed(ref this.attributeLists, 0));

Expand Down
8 changes: 8 additions & 0 deletions src/Compilers/CSharp/Portable/Syntax/ParameterSyntax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,13 @@ internal bool IsArgList
return this.Type == null && this.Identifier.ContextualKind() == SyntaxKind.ArgListKeyword;
}
}

private partial void Validate()
{
if (Type is null && Identifier.IsKind(SyntaxKind.None))
{
throw new System.ArgumentException(CSharpResources.ParameterRequiresTypeOrIdentifier);
}
}
}
}
5 changes: 2 additions & 3 deletions src/Compilers/CSharp/Portable/Syntax/Syntax.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4342,7 +4342,7 @@
</Field>
<Field Name="Type" Type="TypeSyntax" Optional="true"/>
</AbstractNode>
<Node Name="ParameterSyntax" Base="BaseParameterSyntax" SkipConvenienceFactories="true">
<Node Name="ParameterSyntax" Base="BaseParameterSyntax" SkipConvenienceFactories="true" HasValidate="true">
<TypeComment>
<summary>Parameter syntax.</summary>
</TypeComment>
Expand All @@ -4357,9 +4357,8 @@
<summary>Gets the modifier list.</summary>
</PropertyComment>
</Field>
<Field Name="Type" Type="TypeSyntax" Optional="true" Override="true"/>
<Field Name="Type" Type="TypeSyntax" Optional="true" RequiredForTest="true" Override="true"/>
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequiredForTest="true"

What effect does this have? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated tests generate nodes. The basic logic is to use null or default for all optional nodes. That means GenerateParameter() produces a node without type or identifier and the generated tests hit the added validation.
This flag says that even though the type is optional, for purpose of test we treat it as non-optional.

<Field Name="Identifier" Type="SyntaxToken" Optional="true">
<!-- Tracked by https://github.com/dotnet/roslyn/issues/78961 : enforce that a ParameterSyntax cannot be created with both Type and Identifier missing -->
<PropertyComment>
<summary>Gets the identifier.</summary>
</PropertyComment>
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,6 @@ public static SyntaxKind GetBaseTypeDeclarationKind(SyntaxKind kind)
return kind == SyntaxKind.EnumKeyword ? SyntaxKind.EnumDeclaration : GetTypeDeclarationKind(kind);
}

// Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, decide what we want for extension declaration
public static SyntaxKind GetTypeDeclarationKind(SyntaxKind kind)
{
switch (kind)
Expand All @@ -866,6 +865,8 @@ public static SyntaxKind GetTypeDeclarationKind(SyntaxKind kind)
return SyntaxKind.InterfaceDeclaration;
case SyntaxKind.RecordKeyword:
return SyntaxKind.RecordDeclaration;
case SyntaxKind.ExtensionKeyword:
return SyntaxKind.ExtensionBlockDeclaration;
default:
return SyntaxKind.None;
}
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -7718,7 +7718,7 @@ static void Main()
Diagnostic(ErrorCode.ERR_AmbigCall, "--").WithArguments("Extensions1.extension(C1).operator checked --()", "Extensions2.extension(C1).operator --()").WithLocation(36, 17)
);
#else
// https://github.com/dotnet/roslyn/issues/78968: Understand what is causing DEBUG/RELEASE behavior difference
// Ordering difference is acceptable and doesn't affect determinism. It is caused by ConditionallyDeOrder
comp.VerifyEmitDiagnostics(
// (32,13): error CS0121: The call is ambiguous between the following methods or properties: 'Extensions1.extension(C1).operator --()' and 'Extensions2.extension(C1).operator --()'
// _ = --c1;
Expand Down Expand Up @@ -23800,7 +23800,7 @@ static void Main()
Diagnostic(ErrorCode.ERR_AmbigCall, "-=").WithArguments("Extensions1.extension(C1).operator checked -=(C1)", "Extensions2.extension(C1).operator -=(C1)").WithLocation(39, 20)
);
#else
// https://github.com/dotnet/roslyn/issues/78968: Understand what is causing DEBUG/RELEASE behavior difference
// Ordering difference is acceptable and doesn't affect determinism. It is caused by ConditionallyDeOrder
comp.VerifyEmitDiagnostics(
// (35,16): error CS0121: The call is ambiguous between the following methods or properties: 'Extensions1.extension(C1).operator -=(C1)' and 'Extensions2.extension(C1).operator -=(C1)'
// _ = c1 -= c1;
Expand Down
38 changes: 35 additions & 3 deletions src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38148,6 +38148,41 @@ public void M() { }
Assert.Empty(model.LookupNamespacesAndTypes(position, o, name: "M"));
}

[Fact]
public void LookupSymbols_UsedImports()
{
var src = """
// here

using N;

namespace N
{
public static class E
{
extension(object)
{
public static void M() => throw null;
}
}
}
""";

var comp = CreateCompilation(src);

var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);

var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object);
AssertEqualAndNoDuplicates(["void N.E.<G>$C43E2675C7BBF9284AF22FB8A9BF0280.M()"],
model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings());

model.GetDiagnostics().Verify(
// (3,1): hidden CS8019: Unnecessary using directive.
// using N;
Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N;").WithLocation(3, 1));
}

[Fact]
public void GetMemberGroup_01()
{
Expand Down Expand Up @@ -44472,9 +44507,6 @@ static class E
Diagnostic(ErrorCode.ERR_MainClassNotFound).WithArguments("E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69").WithLocation(1, 1));

AssertEx.Equal("<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69", comp.GetTypeByMetadataName("E").GetTypeMembers().Single().ExtensionGroupingName);

// Tracked by https://github.com/dotnet/roslyn/issues/78968 : we should find the unspeakable nested type
Assert.Null(comp.GetTypeByMetadataName("E+<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69"));
}

[Fact]
Expand Down
Loading
Loading