Skip to content
52 changes: 52 additions & 0 deletions src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2576,5 +2576,57 @@ public partial void M<T>() { }
Assert.False(partialImpl.IsPartialDefinition);
Assert.False(partialImplConstructed.IsPartialDefinition);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/13456")]

@jcouv jcouv Oct 20, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Link is wrong here too #Closed

Comment thread
CyrusNajmabadi marked this conversation as resolved.
Outdated
public void PartialMethodsLocationsAndSyntaxReferences()
{
var source1 = """
namespace N1
{
partial class C1
{
partial void PartialM();
}
}
""";

var source2 = """
namespace N1
{
partial class C1
{
partial void PartialM() { }
}
}
""";

var comp = CreateCompilation([(source1, "source1"), (source2, "source2")]);
comp.VerifyDiagnostics();

var methodSymbols = comp.GetSymbolsWithName("PartialM");

@jcouv jcouv Oct 15, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is unnecessary lengthy and complicated. Since we know there's only one and it's a method symbol: we can do 1 line, var method = (IMethodSymbol)comp.GetSymbolsWithName("PartialM").Single();, instead of 5 lines #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@copilot remove lengthy and complicated assertion checking. Also add to the copilot-instructions file that tests should avoid unnecessary assertations and should do the minimal work necessary to get to the core assertions that validate the issue being addressed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Simplified test and updated Compiler.instructions.md to guide future tests in commit 2c7c5d9

@jcouv jcouv Oct 20, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Consider inlining methodSymbols since only used once #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Inlined in commit 2c7c5d9


var method = (IMethodSymbol)methodSymbols.Single();
Assert.NotNull(method);

@jcouv jcouv Oct 20, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Unnecessary assertion #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed in commit 2c7c5d9


// For partial methods, Locations and DeclaringSyntaxReferences contain only one location
Assert.Equal(1, method.Locations.Length);
Assert.Equal(1, method.DeclaringSyntaxReferences.Length);

// The single location is the definition part
Assert.True(method.IsPartialDefinition);
Assert.Null(method.PartialDefinitionPart);
Assert.NotNull(method.PartialImplementationPart);

// To get all locations, you need to use PartialImplementationPart
var implementationPart = method.PartialImplementationPart;
Assert.Equal(1, implementationPart.Locations.Length);
Assert.Equal(1, implementationPart.DeclaringSyntaxReferences.Length);

// Verify the locations are different
Assert.NotEqual(method.Locations[0], implementationPart.Locations[0]);

@jcouv jcouv Oct 15, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

By naming the sources when creating the compilation (CreateCompilation([(source, "source"), (source2, "source2")])) it should be easy to observe each location #Closed


Assert.Equal("source1", method.Locations[0].SourceTree.FilePath);
Assert.Equal("source2", implementationPart.Locations[0].SourceTree.FilePath);
}
}
}
16 changes: 13 additions & 3 deletions src/Compilers/Core/Portable/Symbols/ISymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,28 @@ public interface ISymbol : IEquatable<ISymbol?>

/// <summary>
/// Gets the locations where the symbol was originally defined, either in source or
/// metadata. Some symbols (for example, partial classes) may be defined in more than one
/// location.
/// metadata. Some symbols (for example, partial types such as classes, structs, and interfaces) may be defined in more than one
/// location. Note that for partial members (methods, properties, events), this property returns
Comment thread
CyrusNajmabadi marked this conversation as resolved.
Outdated
/// only one location. To get all locations for a partial member, use the <c>PartialDefinitionPart</c> and
/// <c>PartialImplementationPart</c> properties on <see cref="IMethodSymbol"/>, <see cref="IPropertySymbol"/>, or
/// <see cref="IEventSymbol"/>.
/// </summary>
ImmutableArray<Location> Locations { get; }

/// <summary>
/// Get the syntax node(s) where this symbol was declared in source. Some symbols (for example,
/// partial classes) may be defined in more than one location. This property should return
/// partial types such as classes, structs, and interfaces) may be defined in more than one location. This property should return
/// one or more syntax nodes only if the symbol was declared in source code and also was
/// not implicitly declared (see the IsImplicitlyDeclared property).
///
/// <para>
/// Note that for partial members (methods, properties, events), this property returns only one
/// syntax node. To get all syntax nodes for a partial member, use the <c>PartialDefinitionPart</c> and
/// <c>PartialImplementationPart</c> properties on <see cref="IMethodSymbol"/>, <see cref="IPropertySymbol"/>, or
/// <see cref="IEventSymbol"/>.
/// </para>
///
/// <para>
/// Note that for namespace symbol, the declaring syntax might be declaring a nested namespace.
/// For example, the declaring syntax node for N1 in "namespace N1.N2 {...}" is the entire
/// NamespaceDeclarationSyntax for N1.N2. For the global namespace, the declaring syntax will
Expand Down