Skip to content

Commit ee47efe

Browse files
authored
Partial properties: misc compiler layer work (#73527)
1 parent 616fd3c commit ee47efe

File tree

11 files changed

+1166
-45
lines changed

11 files changed

+1166
-45
lines changed

src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,6 @@ public override Binder VisitAccessorDeclaration(AccessorDeclarationSyntax parent
290290
if ((object)propertySymbol != null)
291291
{
292292
accessor = (parent.Kind() == SyntaxKind.GetAccessorDeclaration) ? propertySymbol.GetMethod : propertySymbol.SetMethod;
293-
// 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.
294293
Debug.Assert(accessor is not null || parent.HasErrors);
295294
}
296295
break;

src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,14 @@ public override void DefaultVisit(Symbol symbol)
258258
bool shouldSkipPartialDefinitionComments = false;
259259
if (symbol.IsPartialDefinition())
260260
{
261-
if (symbol is MethodSymbol { PartialImplementationPart: MethodSymbol implementationPart })
261+
Symbol? implementationPart = symbol switch
262+
{
263+
MethodSymbol method => method.PartialImplementationPart,
264+
SourcePropertySymbol property => property.PartialImplementationPart,
265+
_ => null
266+
};
267+
268+
if (implementationPart is not null)
262269
{
263270
Visit(implementationPart);
264271

src/Compilers/CSharp/Portable/Errors/MessageID.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,9 @@ internal enum MessageID
284284
IDS_FeatureParamsCollections = MessageBase + 12842,
285285

286286
IDS_FeatureRefUnsafeInIteratorAsync = MessageBase + 12843,
287+
288+
// PROTOTYPE(partial-properties): pack
289+
IDS_FeaturePartialProperties = MessageBase + 13000,
287290
}
288291

289292
// Message IDs may refer to strings that need to be localized.
@@ -469,6 +472,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
469472
case MessageID.IDS_FeatureLockObject:
470473
case MessageID.IDS_FeatureParamsCollections:
471474
case MessageID.IDS_FeatureRefUnsafeInIteratorAsync:
475+
case MessageID.IDS_FeaturePartialProperties:
472476
return LanguageVersion.Preview;
473477

474478
// C# 12.0 features.

src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,18 @@ private static (DeclarationModifiers modifiers, bool hasExplicitAccessMod) MakeM
384384
hasExplicitAccessMod = true;
385385
}
386386

387+
if ((mods & DeclarationModifiers.Partial) != 0)
388+
{
389+
Debug.Assert(location.SourceTree is not null);
390+
391+
LanguageVersion availableVersion = ((CSharpParseOptions)location.SourceTree.Options).LanguageVersion;
392+
LanguageVersion requiredVersion = MessageID.IDS_FeaturePartialProperties.RequiredVersion();
393+
if (availableVersion < requiredVersion)
394+
{
395+
ModifierUtils.ReportUnsupportedModifiersForLanguageVersion(mods, DeclarationModifiers.Partial, location, diagnostics, availableVersion, requiredVersion);
396+
}
397+
}
398+
387399
ModifierUtils.CheckFeatureAvailabilityForStaticAbstractMembersInInterfacesIfNeeded(mods, isExplicitInterfaceImplementation, location, diagnostics);
388400

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

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

719+
public sealed override bool IsExtern => PartialImplementationPart is { } implementation ? implementation.IsExtern : HasExternModifier;
720+
708721
internal SourcePropertySymbol? OtherPartOfPartial => _otherPartOfPartial;
709722

710-
internal bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !IsExtern;
723+
internal bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !HasExternModifier;
711724

712-
internal bool IsPartialImplementation => IsPartial && (AccessorsHaveImplementation || IsExtern);
725+
internal bool IsPartialImplementation => IsPartial && (AccessorsHaveImplementation || HasExternModifier);
713726

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

src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,9 +486,20 @@ public override bool IsAbstract
486486
get { return (_modifiers & DeclarationModifiers.Abstract) != 0; }
487487
}
488488

489+
protected bool HasExternModifier
490+
{
491+
get
492+
{
493+
return (_modifiers & DeclarationModifiers.Extern) != 0;
494+
}
495+
}
496+
489497
public override bool IsExtern
490498
{
491-
get { return (_modifiers & DeclarationModifiers.Extern) != 0; }
499+
get
500+
{
501+
return HasExternModifier;
502+
}
492503
}
493504

494505
public override bool IsStatic

src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,6 @@ private void NonPartialMethod2() { }
186186
Assert.True(completedCompilationUnits.Contains(tree1.FilePath));
187187
}
188188

189-
// PROTOTYPE(partial-properties): also test compilation events for complete and incomplete partial properties and their accessors
190-
191189
[Fact, WorkItem(7477, "https://github.com/dotnet/roslyn/issues/7477")]
192190
public void TestCompilationEventsForPartialMethod()
193191
{
@@ -240,6 +238,69 @@ partial void PartialMethod() { }
240238
Assert.True(completedCompilationUnits.Contains(tree1.FilePath));
241239
}
242240

241+
[Fact]
242+
public void TestCompilationEventsForPartialProperty()
243+
{
244+
var source1 = @"
245+
namespace N1
246+
{
247+
partial class Class
248+
{
249+
int NonPartialProp1 { get; set; }
250+
partial int DefOnlyPartialProp { get; set; }
251+
partial int ImplOnlyPartialProp { get => 1; set { } }
252+
partial int PartialProp { get; set; }
253+
}
254+
}
255+
";
256+
var source2 = @"
257+
namespace N1
258+
{
259+
partial class Class
260+
{
261+
int NonPartialProp2 { get; set; }
262+
partial int PartialProp { get => 1; set { } }
263+
}
264+
}
265+
";
266+
267+
var tree1 = CSharpSyntaxTree.ParseText(source1, path: "file1");
268+
var tree2 = CSharpSyntaxTree.ParseText(source2, path: "file2");
269+
var eventQueue = new AsyncQueue<CompilationEvent>();
270+
var compilation = CreateCompilationWithMscorlib45(new[] { tree1, tree2 }).WithEventQueue(eventQueue);
271+
272+
// Invoke SemanticModel.GetDiagnostics to force populate the event queue for symbols in the first source file.
273+
var model = compilation.GetSemanticModel(tree1);
274+
model.GetDiagnostics(tree1.GetRoot().FullSpan);
275+
276+
Assert.True(eventQueue.Count > 0);
277+
bool compilationStartedFired;
278+
HashSet<string> declaredSymbolNames, completedCompilationUnits;
279+
Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out completedCompilationUnits));
280+
281+
// Verify symbol declared events fired for all symbols declared in the first source file.
282+
Assert.True(compilationStartedFired);
283+
284+
// NB: NonPartialProp2 is missing here because we only asked for diagnostics in tree1
285+
AssertEx.Equal([
286+
"",
287+
"Class",
288+
"DefOnlyPartialProp",
289+
"get_ImplOnlyPartialProp",
290+
"get_NonPartialProp1",
291+
"get_PartialProp",
292+
"ImplOnlyPartialProp",
293+
"N1",
294+
"NonPartialProp1",
295+
"PartialProp",
296+
"set_ImplOnlyPartialProp",
297+
"set_NonPartialProp1",
298+
"set_PartialProp"
299+
], declaredSymbolNames.OrderBy(name => name));
300+
301+
AssertEx.Equal(["file1"], completedCompilationUnits.OrderBy(name => name));
302+
}
303+
243304
[Fact, WorkItem(8178, "https://github.com/dotnet/roslyn/issues/8178")]
244305
public void TestEarlyCancellation()
245306
{
@@ -288,12 +349,7 @@ private static bool DequeueCompilationEvents(AsyncQueue<CompilationEvent> eventQ
288349
var added = declaredSymbolNames.Add(symbol.Name);
289350
if (!added)
290351
{
291-
var method = symbol.GetSymbol() as Symbols.MethodSymbol;
292-
Assert.NotNull(method);
293-
294-
var isPartialMethod = method.PartialDefinitionPart != null ||
295-
method.PartialImplementationPart != null;
296-
Assert.True(isPartialMethod, "Unexpected multiple symbol declared events for symbol " + symbol);
352+
Assert.True(symbol.GetSymbol().IsPartialMember(), "Unexpected multiple symbol declared events for symbol " + symbol);
297353
}
298354
}
299355
else

0 commit comments

Comments
 (0)