Skip to content

Commit

Permalink
Partial properties: misc compiler layer work (#73527)
Browse files Browse the repository at this point in the history
  • Loading branch information
RikkiGibson authored May 20, 2024
1 parent 616fd3c commit ee47efe
Show file tree
Hide file tree
Showing 11 changed files with 1,166 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,6 @@ public override Binder VisitAccessorDeclaration(AccessorDeclarationSyntax parent
if ((object)propertySymbol != null)
{
accessor = (parent.Kind() == SyntaxKind.GetAccessorDeclaration) ? propertySymbol.GetMethod : propertySymbol.SetMethod;
// PROTOTYPE(partial-properties): check if a property with no accessors could fail this assertion, in which case we should either adjust the assertion or remove it.
Debug.Assert(accessor is not null || parent.HasErrors);
}
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,14 @@ public override void DefaultVisit(Symbol symbol)
bool shouldSkipPartialDefinitionComments = false;
if (symbol.IsPartialDefinition())
{
if (symbol is MethodSymbol { PartialImplementationPart: MethodSymbol implementationPart })
Symbol? implementationPart = symbol switch
{
MethodSymbol method => method.PartialImplementationPart,
SourcePropertySymbol property => property.PartialImplementationPart,
_ => null
};

if (implementationPart is not null)
{
Visit(implementationPart);

Expand Down
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ internal enum MessageID
IDS_FeatureParamsCollections = MessageBase + 12842,

IDS_FeatureRefUnsafeInIteratorAsync = MessageBase + 12843,

// PROTOTYPE(partial-properties): pack
IDS_FeaturePartialProperties = MessageBase + 13000,
}

// Message IDs may refer to strings that need to be localized.
Expand Down Expand Up @@ -469,6 +472,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_FeatureLockObject:
case MessageID.IDS_FeatureParamsCollections:
case MessageID.IDS_FeatureRefUnsafeInIteratorAsync:
case MessageID.IDS_FeaturePartialProperties:
return LanguageVersion.Preview;

// C# 12.0 features.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,18 @@ private static (DeclarationModifiers modifiers, bool hasExplicitAccessMod) MakeM
hasExplicitAccessMod = true;
}

if ((mods & DeclarationModifiers.Partial) != 0)
{
Debug.Assert(location.SourceTree is not null);

LanguageVersion availableVersion = ((CSharpParseOptions)location.SourceTree.Options).LanguageVersion;
LanguageVersion requiredVersion = MessageID.IDS_FeaturePartialProperties.RequiredVersion();
if (availableVersion < requiredVersion)
{
ModifierUtils.ReportUnsupportedModifiersForLanguageVersion(mods, DeclarationModifiers.Partial, location, diagnostics, availableVersion, requiredVersion);
}
}

ModifierUtils.CheckFeatureAvailabilityForStaticAbstractMembersInInterfacesIfNeeded(mods, isExplicitInterfaceImplementation, location, diagnostics);

containingType.CheckUnsafeModifier(mods, location, diagnostics);
Expand Down Expand Up @@ -631,7 +643,6 @@ private void PartialPropertyChecks(SourcePropertySymbol implementation, BindingD
}

if ((!hasTypeDifferences && !MemberSignatureComparer.PartialMethodsStrictComparer.Equals(this, implementation))
// PROTOTYPE(partial-properties): test indexers with parameter name differences
|| !Parameters.SequenceEqual(implementation.Parameters, (a, b) => a.Name == b.Name))
{
diagnostics.Add(ErrorCode.WRN_PartialPropertySignatureDifference, implementation.GetFirstLocation(),
Expand Down Expand Up @@ -705,11 +716,13 @@ private void PartialPropertyChecks(SourcePropertySymbol implementation, BindingD
private static BaseParameterListSyntax? GetParameterListSyntax(CSharpSyntaxNode syntax)
=> (syntax as IndexerDeclarationSyntax)?.ParameterList;

public sealed override bool IsExtern => PartialImplementationPart is { } implementation ? implementation.IsExtern : HasExternModifier;

internal SourcePropertySymbol? OtherPartOfPartial => _otherPartOfPartial;

internal bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !IsExtern;
internal bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !HasExternModifier;

internal bool IsPartialImplementation => IsPartial && (AccessorsHaveImplementation || IsExtern);
internal bool IsPartialImplementation => IsPartial && (AccessorsHaveImplementation || HasExternModifier);

internal SourcePropertySymbol? PartialDefinitionPart => IsPartialImplementation ? OtherPartOfPartial : null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,20 @@ public override bool IsAbstract
get { return (_modifiers & DeclarationModifiers.Abstract) != 0; }
}

protected bool HasExternModifier
{
get
{
return (_modifiers & DeclarationModifiers.Extern) != 0;
}
}

public override bool IsExtern
{
get { return (_modifiers & DeclarationModifiers.Extern) != 0; }
get
{
return HasExternModifier;
}
}

public override bool IsStatic
Expand Down
72 changes: 64 additions & 8 deletions src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,6 @@ private void NonPartialMethod2() { }
Assert.True(completedCompilationUnits.Contains(tree1.FilePath));
}

// PROTOTYPE(partial-properties): also test compilation events for complete and incomplete partial properties and their accessors

[Fact, WorkItem(7477, "https://github.com/dotnet/roslyn/issues/7477")]
public void TestCompilationEventsForPartialMethod()
{
Expand Down Expand Up @@ -240,6 +238,69 @@ partial void PartialMethod() { }
Assert.True(completedCompilationUnits.Contains(tree1.FilePath));
}

[Fact]
public void TestCompilationEventsForPartialProperty()
{
var source1 = @"
namespace N1
{
partial class Class
{
int NonPartialProp1 { get; set; }
partial int DefOnlyPartialProp { get; set; }
partial int ImplOnlyPartialProp { get => 1; set { } }
partial int PartialProp { get; set; }
}
}
";
var source2 = @"
namespace N1
{
partial class Class
{
int NonPartialProp2 { get; set; }
partial int PartialProp { get => 1; set { } }
}
}
";

var tree1 = CSharpSyntaxTree.ParseText(source1, path: "file1");
var tree2 = CSharpSyntaxTree.ParseText(source2, path: "file2");
var eventQueue = new AsyncQueue<CompilationEvent>();
var compilation = CreateCompilationWithMscorlib45(new[] { tree1, tree2 }).WithEventQueue(eventQueue);

// Invoke SemanticModel.GetDiagnostics to force populate the event queue for symbols in the first source file.
var model = compilation.GetSemanticModel(tree1);
model.GetDiagnostics(tree1.GetRoot().FullSpan);

Assert.True(eventQueue.Count > 0);
bool compilationStartedFired;
HashSet<string> declaredSymbolNames, completedCompilationUnits;
Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out completedCompilationUnits));

// Verify symbol declared events fired for all symbols declared in the first source file.
Assert.True(compilationStartedFired);

// NB: NonPartialProp2 is missing here because we only asked for diagnostics in tree1
AssertEx.Equal([
"",
"Class",
"DefOnlyPartialProp",
"get_ImplOnlyPartialProp",
"get_NonPartialProp1",
"get_PartialProp",
"ImplOnlyPartialProp",
"N1",
"NonPartialProp1",
"PartialProp",
"set_ImplOnlyPartialProp",
"set_NonPartialProp1",
"set_PartialProp"
], declaredSymbolNames.OrderBy(name => name));

AssertEx.Equal(["file1"], completedCompilationUnits.OrderBy(name => name));
}

[Fact, WorkItem(8178, "https://github.com/dotnet/roslyn/issues/8178")]
public void TestEarlyCancellation()
{
Expand Down Expand Up @@ -288,12 +349,7 @@ private static bool DequeueCompilationEvents(AsyncQueue<CompilationEvent> eventQ
var added = declaredSymbolNames.Add(symbol.Name);
if (!added)
{
var method = symbol.GetSymbol() as Symbols.MethodSymbol;
Assert.NotNull(method);

var isPartialMethod = method.PartialDefinitionPart != null ||
method.PartialImplementationPart != null;
Assert.True(isPartialMethod, "Unexpected multiple symbol declared events for symbol " + symbol);
Assert.True(symbol.GetSymbol().IsPartialMember(), "Unexpected multiple symbol declared events for symbol " + symbol);
}
}
else
Expand Down
Loading

0 comments on commit ee47efe

Please sign in to comment.