Skip to content

Commit

Permalink
Add IMethodSymbol.PartialDefinitionPart
Browse files Browse the repository at this point in the history
Fixes dotnet#51082. This adds a new method to IMethodSymbol that returns true if the method is a partial method declaration without a body. This info can be determined from syntax APIs today, but requires users to bifurcate code between C# and VB. We already have the info internally, so this just exposes that API.
  • Loading branch information
333fred committed Feb 16, 2021
1 parent 1cbe495 commit 1cc8326
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ IMethodSymbol IMethodSymbol.PartialDefinitionPart
}
}

bool IMethodSymbol.IsPartialDefinition => _underlying.IsPartialDefinition();

INamedTypeSymbol IMethodSymbol.AssociatedAnonymousDelegate
{
get
Expand Down
129 changes: 129 additions & 0 deletions src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2386,5 +2386,134 @@ class C
var method = compilation.GetMember<MethodSymbol>("C.M");
Assert.True(method.IsConditional);
}

[Fact, WorkItem(51082, "https://github.com/dotnet/roslyn/issues/51082")]
public void IsPartialDefinitionOnNonPartial()
{
var source = @"
class C
{
void M() {}
}
";

var comp = CreateCompilation(source);
comp.VerifyDiagnostics();
var m = comp.GetMember<MethodSymbol>("C.M").GetPublicSymbol();
Assert.False(m.IsPartialDefinition);
}

[Fact, WorkItem(51082, "https://github.com/dotnet/roslyn/issues/51082")]
public void IsPartialDefinitionOnPartialDefinitionOnly()
{
var source = @"
partial class C
{
partial void M();
}
";

var comp = CreateCompilation(source);
comp.VerifyDiagnostics();
var m = comp.GetMember<MethodSymbol>("C.M").GetPublicSymbol();
Assert.True(m.IsPartialDefinition);
Assert.Null(m.PartialDefinitionPart);
Assert.Null(m.PartialImplementationPart);
}

[Fact, WorkItem(51082, "https://github.com/dotnet/roslyn/issues/51082")]
public void IsPartialDefinitionWithPartialImplementation()
{
var source = @"
partial class C
{
partial void M();
partial void M() {}
}
";

var comp = CreateCompilation(source);
comp.VerifyDiagnostics();
var m = comp.GetMember<MethodSymbol>("C.M").GetPublicSymbol();
Assert.True(m.IsPartialDefinition);
Assert.Null(m.PartialDefinitionPart);
Assert.False(m.PartialImplementationPart.IsPartialDefinition);
}

[Fact, WorkItem(51082, "https://github.com/dotnet/roslyn/issues/51082")]
public void IsPartialDefinitionOnPartialImplementation_NonPartialClass()
{
var source = @"
class C
{
partial void M();
partial void M() {}
}
";

var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (4,18): error CS0751: A partial method must be declared within a partial type
// partial void M();
Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "M").WithLocation(4, 18),
// (5,18): error CS0751: A partial method must be declared within a partial type
// partial void M() {}
Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "M").WithLocation(5, 18)
);
var m = comp.GetMember<MethodSymbol>("C.M").GetPublicSymbol();
Assert.True(m.IsPartialDefinition);
Assert.Null(m.PartialDefinitionPart);
Assert.False(m.PartialImplementationPart.IsPartialDefinition);
}

[Fact, WorkItem(51082, "https://github.com/dotnet/roslyn/issues/51082")]
public void IsPartialDefinitionOnPartialImplementationOnly()
{
var source = @"
partial class C
{
partial void M() {}
}
";

var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (4,18): error CS0759: No defining declaration found for implementing declaration of partial method 'C.M()'
// partial void M() {}
Diagnostic(ErrorCode.ERR_PartialMethodMustHaveLatent, "M").WithArguments("C.M()").WithLocation(4, 18)
);
var m = comp.GetMember<MethodSymbol>("C.M").GetPublicSymbol();
Assert.False(m.IsPartialDefinition);
Assert.Null(m.PartialDefinitionPart);
Assert.Null(m.PartialImplementationPart);
}

[Fact, WorkItem(51082, "https://github.com/dotnet/roslyn/issues/51082")]
public void IsPartialDefinition_ReturnsFalseFromMetadata()
{
var source = @"
public partial class C
{
public partial void M();
public partial void M() {}
}
";

CompileAndVerify(source,
sourceSymbolValidator: module =>
{
var m = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").GetPublicSymbol();
Assert.True(m.IsPartialDefinition);
Assert.Null(m.PartialDefinitionPart);
Assert.False(m.PartialImplementationPart.IsPartialDefinition);
},
symbolValidator: module =>
{
var m = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").GetPublicSymbol();
Assert.False(m.IsPartialDefinition);
Assert.Null(m.PartialDefinitionPart);
Assert.Null(m.PartialImplementationPart);
});
}
}
}
3 changes: 2 additions & 1 deletion src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Microsoft.CodeAnalysis.GeneratorSyntaxContext
Microsoft.CodeAnalysis.GeneratorSyntaxContext.GeneratorSyntaxContext() -> void
Microsoft.CodeAnalysis.GeneratorSyntaxContext.Node.get -> Microsoft.CodeAnalysis.SyntaxNode!
Microsoft.CodeAnalysis.GeneratorSyntaxContext.SemanticModel.get -> Microsoft.CodeAnalysis.SemanticModel!
Microsoft.CodeAnalysis.IMethodSymbol.IsPartialDefinition.get -> bool
Microsoft.CodeAnalysis.ISyntaxContextReceiver
Microsoft.CodeAnalysis.ISyntaxContextReceiver.OnVisitSyntaxNode(Microsoft.CodeAnalysis.GeneratorSyntaxContext context) -> void
Microsoft.CodeAnalysis.GeneratorInitializationContext.RegisterForPostInitialization(System.Action<Microsoft.CodeAnalysis.GeneratorPostInitializationContext>! callback) -> void
Expand All @@ -28,4 +29,4 @@ override Microsoft.CodeAnalysis.Operations.OperationWalker<TArgument>.DefaultVis
override Microsoft.CodeAnalysis.Operations.OperationWalker<TArgument>.Visit(Microsoft.CodeAnalysis.IOperation? operation, TArgument argument) -> object?
virtual Microsoft.CodeAnalysis.Diagnostics.AnalyzerReference.GetGenerators(string! language) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISourceGenerator!>
virtual Microsoft.CodeAnalysis.Diagnostics.AnalyzerReference.GetGeneratorsForAllLanguages() -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISourceGenerator!>
abstract Microsoft.CodeAnalysis.Compilation.GetUsedAssemblyReferences(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.MetadataReference!>
abstract Microsoft.CodeAnalysis.Compilation.GetUsedAssemblyReferences(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.MetadataReference!>
6 changes: 6 additions & 0 deletions src/Compilers/Core/Portable/Symbols/IMethodSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ public interface IMethodSymbol : ISymbol
/// </summary>
IMethodSymbol? PartialImplementationPart { get; }

/// <summary>
/// Return true if this is a partial method definition without a body. If there
/// is an implementing body, it can be retrieved with <see cref="PartialImplementationPart"/>.
/// </summary>
bool IsPartialDefinition { get; }

/// <summary>
/// Platform invoke information, or null if the method isn't a P/Invoke.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
End Get
End Property

Private ReadOnly Property IMethodSymbol_IsPartialDefinition As Boolean Implements IMethodSymbol.IsPartialDefinition
Get
Return If(TryCast(Me, SourceMemberMethodSymbol)?.IsPartialDefinition, False)
End Get
End Property

Private ReadOnly Property IMethodSymbol_ReturnsVoid As Boolean Implements IMethodSymbol.ReturnsVoid
Get
Return Me.IsSub
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1732,5 +1732,56 @@ BC32065: Type parameters cannot be specified on this declaration.
]]></errors>)
End Sub

<Fact, WorkItem(51082, "https://github.com/dotnet/roslyn/issues/51082")>
Public Sub IsPartialDefinitionOnNonPartial()
Dim source = <![CDATA[
Public Class C
Sub M()
End Sub
End Class
]]>.Value

Dim comp = CreateCompilation(source)
comp.AssertTheseDiagnostics()
Dim m As IMethodSymbol = comp.GetMember(Of MethodSymbol)("C.M")
Assert.False(m.IsPartialDefinition)
End Sub

<Fact, WorkItem(51082, "https://github.com/dotnet/roslyn/issues/51082")>
Public Sub IsPartialDefinitionOnPartialDefinitionOnly()
Dim source = <![CDATA[
Public Class C
Private Partial Sub M()
End Sub
End Class
]]>.Value

Dim comp = CreateCompilation(source)
comp.AssertTheseDiagnostics()
Dim m As IMethodSymbol = comp.GetMember(Of MethodSymbol)("C.M")
Assert.True(m.IsPartialDefinition)
Assert.Null(m.PartialDefinitionPart)
Assert.Null(m.PartialImplementationPart)
End Sub

<Fact, WorkItem(51082, "https://github.com/dotnet/roslyn/issues/51082")>
Public Sub IsPartialDefinitionWithPartialImplementation()
Dim source = <![CDATA[
Public Class C
Private Partial Sub M()
End Sub

Private Sub M()
End Sub
End Class
]]>.Value

Dim comp = CreateCompilation(source)
comp.AssertTheseDiagnostics()
Dim m As IMethodSymbol = comp.GetMember(Of MethodSymbol)("C.M")
Assert.True(m.IsPartialDefinition)
Assert.Null(m.PartialDefinitionPart)
Assert.False(m.PartialImplementationPart.IsPartialDefinition)
End Sub
End Class
End Namespace
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ protected CodeGenerationAbstractMethodSymbol(
public abstract ImmutableArray<IMethodSymbol> ExplicitInterfaceImplementations { get; }
public abstract IMethodSymbol PartialDefinitionPart { get; }
public abstract IMethodSymbol PartialImplementationPart { get; }
public abstract bool IsPartialDefinition { get; }

public NullableAnnotation ReceiverNullableAnnotation => ReceiverType.NullableAnnotation;
public NullableAnnotation ReturnNullableAnnotation => ReturnType.NullableAnnotation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ public override IMethodSymbol ReduceExtensionMethod(ITypeSymbol receiverType)
// TODO(cyrusn): Construct this.
_constructedFrom.PartialImplementationPart;

public override bool IsPartialDefinition => _constructedFrom.IsPartialDefinition;

protected override CodeGenerationSymbol Clone()
=> new CodeGenerationConstructedMethodSymbol(_constructedFrom, _typeArguments);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,7 @@ public override IMethodSymbol ReduceExtensionMethod(ITypeSymbol receiverType)
public override IMethodSymbol PartialImplementationPart => null;

public override IMethodSymbol PartialDefinitionPart => null;

public override bool IsPartialDefinition => false;
}
}

0 comments on commit 1cc8326

Please sign in to comment.