Skip to content

Provide better API and documentation for SyntaxReferences of partial methods  #22598

@MaStr11

Description

@MaStr11

Version Used:
v2.3.2
Steps to Reproduce:
Get an ISymbol for a partial method.
e.g. Console App with Microsoft.CodeAnalysis Nuget package

        static void Main(string[] args)
        {
            var ws = new AdhocWorkspace();
            var project = ws.AddProject("Ass1", LanguageNames.CSharp);
            ws.AddDocument(project.Id, "DefinitionPart.cs", SourceText.From(
@"
namespace N1
{
    partial class C1
    {
        partial void PartialM();
    }
}
"));
            ws.AddDocument(project.Id, "ImplementationPart.cs", SourceText.From(
@"
namespace N1
{
    partial class C1
    {
        partial void PartialM() { }
    }
}
"));
            var compilation = ws.CurrentSolution.Projects.First().GetCompilationAsync().GetAwaiter().GetResult();
            var methodSymbols = compilation.GetSymbolsWithName(s => s == "PartialM");
            var method = methodSymbols.First() as IMethodSymbol;
            Console.WriteLine($"Number of symbols                   : { methodSymbols.Count() }");
            Console.WriteLine($"DeclaringSyntaxReferences           : { method.DeclaringSyntaxReferences.Count() }");
            Console.WriteLine($"DeclaringSyntaxReference.Location   : { method.DeclaringSyntaxReferences.First().SyntaxTree.FilePath }");
            Console.WriteLine($"Locations                           : { method.Locations.Count() }");
            Console.WriteLine($"Location                            : { method.Locations.First().SourceTree.FilePath }");
            Console.WriteLine($"Has PartialDefinitionPart           : { method.PartialDefinitionPart != null }");
            Console.WriteLine($"Has PartialImplementationPart       : { method.PartialImplementationPart != null }");
            Console.WriteLine($"PartialImplementationPart.Location  : { method.PartialImplementationPart.Locations.First().SourceTree.FilePath }");
            Console.ReadLine();
        }

Expected Behavior:

method.DeclaringSyntaxReferences and method.Locations contain references to both files (as does the ISymbol for class C1).

Actual Behavior:

Number of symbols                   : 1
DeclaringSyntaxReferences           : 1
DeclaringSyntaxReference.Location   : DefinitionPart.cs
Locations                           : 1
Location                            : DefinitionPart.cs
Has PartialDefinitionPart           : False
Has PartialImplementationPart       : True
PartialImplementationPart.Location  : ImplementationPart.cs

From the documentation comment of DeclaringSyntaxReferences and Locations

.. Some Symbols (for example, partial classes) may be defined in more than one location. ..

I would expect that for partial methods the implementation and definition part would be included in the DeclaringSyntaxReferences and/or Locations collections.
It requires a cast to IMethodSymbol to get both locations and to be sure to get all locations you have to something along the lines:

var methodSymbols = 
    Enumerable.Repeat(method, 1) 
    .Union(Enumerable.Repeat(method.PartialDefinitionPart, 1)  
    .Union(Enumerable.Repeat(method.PartialImplementationPart, 1)))  
    .Where(methodSymbol => methodSymbol != null)  
    .Distinct(); 

This is hard to discover and get right as an API consumer.

It would be nice to

  1. Extend the documentation for DeclaringSyntaxReferences and Locations to mention the Partial...Part properties of IMethodSymbol.
  2. Provide an extension method to get all source code locations relevant to an ISymbol.

Metadata

Metadata

Assignees

Labels

Area-CompilersConcept-APIThis issue involves adding, removing, clarification, or modification of an API.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions