Skip to content

Commit 205504d

Browse files
authored
Merge pull request #44391 from dotnet/merges/release/dev16.7-preview2-to-release/dev16.7-preview2-vs-deps
Merge release/dev16.7-preview2 to release/dev16.7-preview2-vs-deps
2 parents 75674ed + dd1050a commit 205504d

30 files changed

+3836
-363
lines changed

src/Compilers/CSharp/Portable/CSharpResources.resx

+22-7
Original file line numberDiff line numberDiff line change
@@ -2112,14 +2112,11 @@ If such a class is used as a base class and if the deriving class defines a dest
21122112
<value>Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit</value>
21132113
</data>
21142114
<data name="ERR_PartialMethodInvalidModifier" xml:space="preserve">
2115-
<value>A partial method cannot have access modifiers or the virtual, abstract, override, new, sealed, or extern modifiers</value>
2115+
<value>A partial method cannot have the 'abstract' modifier</value>
21162116
</data>
21172117
<data name="ERR_PartialMethodOnlyInPartialClass" xml:space="preserve">
21182118
<value>A partial method must be declared within a partial class, partial struct, or partial interface</value>
21192119
</data>
2120-
<data name="ERR_PartialMethodCannotHaveOutParameters" xml:space="preserve">
2121-
<value>A partial method cannot have out parameters</value>
2122-
</data>
21232120
<data name="ERR_PartialMethodNotExplicit" xml:space="preserve">
21242121
<value>A partial method may not explicitly implement an interface method</value>
21252122
</data>
@@ -2156,9 +2153,6 @@ If such a class is used as a base class and if the deriving class defines a dest
21562153
<data name="ERR_PartialMethodInExpressionTree" xml:space="preserve">
21572154
<value>Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees</value>
21582155
</data>
2159-
<data name="ERR_PartialMethodMustReturnVoid" xml:space="preserve">
2160-
<value>Partial methods must have a void return type</value>
2161-
</data>
21622156
<data name="WRN_ObsoleteOverridingNonObsolete" xml:space="preserve">
21632157
<value>Obsolete member '{0}' overrides non-obsolete member '{1}'</value>
21642158
</data>
@@ -6106,4 +6100,25 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
61066100
<data name="ERR_RelationalPatternWithNaN" xml:space="preserve">
61076101
<value>Relational patterns may not be used for a floating-point NaN.</value>
61086102
</data>
6103+
<data name="IDS_FeatureExtendedPartialMethods" xml:space="preserve">
6104+
<value>extended partial methods</value>
6105+
</data>
6106+
<data name="ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods" xml:space="preserve">
6107+
<value>Partial method '{0}' must have accessibility modifiers because it has a non-void return type.</value>
6108+
</data>
6109+
<data name="ERR_PartialMethodWithOutParamMustHaveAccessMods" xml:space="preserve">
6110+
<value>Partial method '{0}' must have accessibility modifiers because it has 'out' parameters.</value>
6111+
</data>
6112+
<data name="ERR_PartialMethodWithAccessibilityModsMustHaveImplementation" xml:space="preserve">
6113+
<value>Partial method '{0}' must have an implementation part because it has accessibility modifiers.</value>
6114+
</data>
6115+
<data name="ERR_PartialMethodWithExtendedModMustHaveAccessMods" xml:space="preserve">
6116+
<value>Partial method '{0}' must have accessibility modifiers because it has a 'virtual', 'override', 'sealed', 'new', or 'extern' modifier.</value>
6117+
</data>
6118+
<data name="ERR_PartialMethodAccessibilityDifference" xml:space="preserve">
6119+
<value>Both partial method declarations must have identical accessibility modifiers.</value>
6120+
</data>
6121+
<data name="ERR_PartialMethodExtendedModDifference" xml:space="preserve">
6122+
<value>Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers.</value>
6123+
</data>
61096124
</root>

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

+13-2
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ internal enum ErrorCode
531531
ERR_InconsistentLambdaParameterUsage = 748,
532532
ERR_PartialMethodInvalidModifier = 750,
533533
ERR_PartialMethodOnlyInPartialClass = 751,
534-
ERR_PartialMethodCannotHaveOutParameters = 752,
534+
// ERR_PartialMethodCannotHaveOutParameters = 752, Removed as part of 'extended partial methods' feature
535535
// ERR_PartialMethodOnlyMethods = 753, Removed as it is subsumed by ERR_PartialMisplaced
536536
ERR_PartialMethodNotExplicit = 754,
537537
ERR_PartialMethodExtensionDifference = 755,
@@ -544,7 +544,7 @@ internal enum ErrorCode
544544
ERR_PartialMethodStaticDifference = 763,
545545
ERR_PartialMethodUnsafeDifference = 764,
546546
ERR_PartialMethodInExpressionTree = 765,
547-
ERR_PartialMethodMustReturnVoid = 766,
547+
// ERR_PartialMethodMustReturnVoid = 766, Removed as part of 'extended partial methods' feature
548548
ERR_ExplicitImplCollisionOnRefOut = 767,
549549
ERR_IndirectRecursiveConstructorCall = 768,
550550

@@ -1788,6 +1788,17 @@ internal enum ErrorCode
17881788

17891789
#endregion diagnostics introduced for C# 9.0
17901790

1791+
#region diagnostics introduced for C# 9
1792+
1793+
ERR_PartialMethodWithAccessibilityModsMustHaveImplementation = 8795,
1794+
ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods = 8796,
1795+
ERR_PartialMethodWithOutParamMustHaveAccessMods = 8797,
1796+
ERR_PartialMethodWithExtendedModMustHaveAccessMods = 8798,
1797+
ERR_PartialMethodAccessibilityDifference = 8799,
1798+
ERR_PartialMethodExtendedModDifference = 8800,
1799+
1800+
#endregion
1801+
17911802
// Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd)
17921803
}
17931804
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ internal enum MessageID
198198
IDS_FeatureAndPattern = MessageBase + 12774,
199199
IDS_FeatureNotPattern = MessageBase + 12775,
200200
IDS_FeatureRelationalPattern = MessageBase + 12776,
201+
IDS_FeatureExtendedPartialMethods = MessageBase + 12777,
201202
}
202203

203204
// Message IDs may refer to strings that need to be localized.
@@ -317,6 +318,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
317318
case MessageID.IDS_FeatureTypePattern:
318319
case MessageID.IDS_FeatureRelationalPattern:
319320
case MessageID.IDS_FeatureNativeInt:
321+
case MessageID.IDS_FeatureExtendedPartialMethods: // semantic check
320322
return LanguageVersion.Preview;
321323

322324
// C# 8.0 features.

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

-7
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,6 @@ internal static DeclarationModifiers CheckModifiers(
6161
modifierErrors = true;
6262
}
6363

64-
bool isMethod = (allowedModifiers & (DeclarationModifiers.Partial | DeclarationModifiers.Virtual)) == (DeclarationModifiers.Partial | DeclarationModifiers.Virtual);
65-
if (isMethod && ((result & (DeclarationModifiers.Partial | DeclarationModifiers.Private)) == (DeclarationModifiers.Partial | DeclarationModifiers.Private)))
66-
{
67-
diagnostics.Add(ErrorCode.ERR_PartialMethodInvalidModifier, errorLocation);
68-
modifierErrors = true;
69-
}
70-
7164
if ((result & DeclarationModifiers.PrivateProtected) != 0)
7265
{
7366
modifierErrors |= !Binder.CheckFeatureAvailability(errorLocation.SourceTree, MessageID.IDS_FeaturePrivateProtected, diagnostics, errorLocation);

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

+10-3
Original file line numberDiff line numberDiff line change
@@ -1683,10 +1683,13 @@ private void CheckMemberNameConflicts(DiagnosticBag diagnostics)
16831683
// UNDONE: Consider adding a secondary location pointing to the second method.
16841684
private void ReportMethodSignatureCollision(DiagnosticBag diagnostics, SourceMemberMethodSymbol method1, SourceMemberMethodSymbol method2)
16851685
{
1686-
// Partial methods are allowed to collide by signature.
1687-
if (method1.IsPartial && method2.IsPartial)
1686+
switch (method1, method2)
16881687
{
1689-
return;
1688+
case (SourceOrdinaryMethodSymbol { IsPartialDefinition: true }, SourceOrdinaryMethodSymbol { IsPartialImplementation: true }):
1689+
case (SourceOrdinaryMethodSymbol { IsPartialImplementation: true }, SourceOrdinaryMethodSymbol { IsPartialDefinition: true }):
1690+
// these could be 2 parts of the same partial method.
1691+
// Partial methods are allowed to collide by signature.
1692+
return;
16901693
}
16911694

16921695
// If method1 is a constructor only because its return type is missing, then
@@ -2567,6 +2570,10 @@ private static void MergePartialMembers(
25672570
{
25682571
diagnostics.Add(ErrorCode.ERR_PartialMethodInconsistentTupleNames, method.Locations[0], method, method.OtherPartOfPartial);
25692572
}
2573+
else if (method is { IsPartialDefinition: true, OtherPartOfPartial: null, HasExplicitAccessModifier: true })
2574+
{
2575+
diagnostics.Add(ErrorCode.ERR_PartialMethodWithAccessibilityModsMustHaveImplementation, method.Locations[0], method);
2576+
}
25702577
}
25712578
}
25722579
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ private static void CheckOverrideMember(Symbol overridingMember, OverriddenOrHid
827827
diagnostics.Add(ErrorCode.ERR_CantOverrideSealed, overridingMemberLocation, overridingMember, overriddenMember);
828828
suppressAccessors = true;
829829
}
830-
else if (!overridingMember.IsPartialMethod() && !OverrideHasCorrectAccessibility(overriddenMember, overridingMember))
830+
else if (!OverrideHasCorrectAccessibility(overriddenMember, overridingMember))
831831
{
832832
var accessibility = SyntaxFacts.GetText(overriddenMember.DeclaredAccessibility);
833833
diagnostics.Add(ErrorCode.ERR_CantChangeAccessOnOverride, overridingMemberLocation, overridingMember, accessibility, overriddenMember);

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -441,14 +441,22 @@ public override Accessibility DeclaredAccessibility
441441
}
442442
}
443443

444-
public sealed override bool IsExtern
444+
internal bool HasExternModifier
445445
{
446446
get
447447
{
448448
return (this.DeclarationModifiers & DeclarationModifiers.Extern) != 0;
449449
}
450450
}
451451

452+
public override bool IsExtern
453+
{
454+
get
455+
{
456+
return HasExternModifier;
457+
}
458+
}
459+
452460
public sealed override bool IsSealed
453461
{
454462
get

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,8 @@ private void DecodeDllImportAttribute(ref DecodeWellKnownAttributeArguments<Attr
642642
Debug.Assert(!attribute.HasErrors);
643643
bool hasErrors = false;
644644

645-
if (!this.IsExtern || !this.IsStatic)
645+
var implementationPart = this.PartialImplementationPart ?? this;
646+
if (!implementationPart.IsExtern || !implementationPart.IsStatic)
646647
{
647648
arguments.Diagnostics.Add(ErrorCode.ERR_DllImportOnInvalidMethod, arguments.AttributeSyntaxOpt.Name.Location);
648649
hasErrors = true;

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

+70-27
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ private SourceOrdinaryMethodSymbol(
100100
_hasAnyBody = hasBody;
101101

102102
bool modifierErrors;
103-
var declarationModifiers = this.MakeModifiers(modifiers, methodKind, hasBody, location, diagnostics, out modifierErrors);
103+
DeclarationModifiers declarationModifiers;
104+
(declarationModifiers, HasExplicitAccessModifier, modifierErrors) = this.MakeModifiers(modifiers, methodKind, hasBody, location, diagnostics);
104105

105106
var isMetadataVirtualIgnoringModifiers = (object)explicitInterfaceType != null; //explicit impls must be marked metadata virtual
106107

@@ -289,16 +290,6 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB
289290

290291
if (IsPartial)
291292
{
292-
// check that there are no out parameters in a partial
293-
foreach (var p in this.Parameters)
294-
{
295-
if (p.RefKind == RefKind.Out)
296-
{
297-
diagnostics.Add(ErrorCode.ERR_PartialMethodCannotHaveOutParameters, location);
298-
break;
299-
}
300-
}
301-
302293
if (MethodKind == MethodKind.ExplicitInterfaceImplementation)
303294
{
304295
diagnostics.Add(ErrorCode.ERR_PartialMethodNotExplicit, location);
@@ -611,7 +602,7 @@ internal bool IsPartialDefinition
611602
{
612603
get
613604
{
614-
return this.IsPartial && !_hasAnyBody;
605+
return this.IsPartial && !_hasAnyBody && !HasExternModifier;
615606
}
616607
}
617608

@@ -622,7 +613,7 @@ internal bool IsPartialImplementation
622613
{
623614
get
624615
{
625-
return this.IsPartial && _hasAnyBody;
616+
return this.IsPartial && (_hasAnyBody || HasExternModifier);
626617
}
627618
}
628619

@@ -677,6 +668,16 @@ public override MethodSymbol PartialImplementationPart
677668
}
678669
}
679670

671+
public sealed override bool IsExtern
672+
{
673+
get
674+
{
675+
return IsPartialDefinition
676+
? _otherPartOfPartial?.IsExtern ?? false
677+
: HasExternModifier;
678+
}
679+
}
680+
680681
public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
681682
{
682683
ref var lazyDocComment = ref expandIncludes ? ref this.lazyExpandedDocComment : ref this.lazyDocComment;
@@ -756,7 +757,9 @@ internal override bool IsExpressionBodied
756757
get { return _isExpressionBodied; }
757758
}
758759

759-
private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, MethodKind methodKind, bool hasBody, Location location, DiagnosticBag diagnostics, out bool modifierErrors)
760+
internal bool HasExplicitAccessModifier { get; }
761+
762+
private (DeclarationModifiers mods, bool hasExplicitAccessMod, bool modifierErrors) MakeModifiers(SyntaxTokenList modifiers, MethodKind methodKind, bool hasBody, Location location, DiagnosticBag diagnostics)
760763
{
761764
bool isInterface = this.ContainingType.IsInterface;
762765
bool isExplicitInterfaceImplementation = methodKind == MethodKind.ExplicitInterfaceImplementation;
@@ -808,7 +811,19 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, MethodKind
808811
allowedModifiers |= DeclarationModifiers.ReadOnly;
809812
}
810813

811-
var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors);
814+
// In order to detect whether explicit accessibility mods were provided, we pass the default value
815+
// for 'defaultAccess' and manually add in the 'defaultAccess' flags after the call.
816+
bool hasExplicitAccessMod;
817+
var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(modifiers, defaultAccess: DeclarationModifiers.None, allowedModifiers, location, diagnostics, out bool modifierErrors);
818+
if ((mods & DeclarationModifiers.AccessibilityMask) == 0)
819+
{
820+
hasExplicitAccessMod = false;
821+
mods |= defaultAccess;
822+
}
823+
else
824+
{
825+
hasExplicitAccessMod = true;
826+
}
812827

813828
this.CheckUnsafeModifier(mods, diagnostics);
814829

@@ -817,7 +832,7 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, MethodKind
817832
location, diagnostics);
818833

819834
mods = AddImpliedModifiers(mods, isInterface, methodKind, hasBody);
820-
return mods;
835+
return (mods, hasExplicitAccessMod, modifierErrors);
821836
}
822837

823838
private static DeclarationModifiers AddImpliedModifiers(DeclarationModifiers mods, bool containingTypeIsInterface, MethodKind methodKind, bool hasBody)
@@ -905,26 +920,40 @@ private ImmutableArray<TypeParameterSymbol> MakeTypeParameters(MethodDeclaration
905920
return result.ToImmutableAndFree();
906921
}
907922

923+
private const DeclarationModifiers PartialMethodExtendedModifierMask =
924+
DeclarationModifiers.Virtual |
925+
DeclarationModifiers.Override |
926+
DeclarationModifiers.New |
927+
DeclarationModifiers.Sealed |
928+
DeclarationModifiers.Extern;
929+
930+
internal bool HasExtendedPartialModifier => (DeclarationModifiers & PartialMethodExtendedModifierMask) != 0;
931+
908932
private void CheckModifiers(bool isExplicitInterfaceImplementation, bool hasBody, Location location, DiagnosticBag diagnostics)
909933
{
910-
const DeclarationModifiers partialMethodInvalidModifierMask = (DeclarationModifiers.AccessibilityMask & ~DeclarationModifiers.Private) |
911-
DeclarationModifiers.Virtual |
912-
DeclarationModifiers.Abstract |
913-
DeclarationModifiers.Override |
914-
DeclarationModifiers.New |
915-
DeclarationModifiers.Sealed |
916-
DeclarationModifiers.Extern;
917-
918934
bool isExplicitInterfaceImplementationInInterface = isExplicitInterfaceImplementation && ContainingType.IsInterface;
919935

920-
if (IsPartial && !ReturnsVoid)
936+
if (IsPartial && HasExplicitAccessModifier)
921937
{
922-
diagnostics.Add(ErrorCode.ERR_PartialMethodMustReturnVoid, location);
938+
Binder.CheckFeatureAvailability(SyntaxNode, MessageID.IDS_FeatureExtendedPartialMethods, diagnostics, location);
923939
}
924-
else if (IsPartial && (DeclarationModifiers & partialMethodInvalidModifierMask) != 0)
940+
941+
if (IsPartial && IsAbstract)
925942
{
926943
diagnostics.Add(ErrorCode.ERR_PartialMethodInvalidModifier, location);
927944
}
945+
else if (IsPartial && !HasExplicitAccessModifier && !ReturnsVoid)
946+
{
947+
diagnostics.Add(ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods, location, this);
948+
}
949+
else if (IsPartial && !HasExplicitAccessModifier && HasExtendedPartialModifier)
950+
{
951+
diagnostics.Add(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, location, this);
952+
}
953+
else if (IsPartial && !HasExplicitAccessModifier && Parameters.Any(p => p.RefKind == RefKind.Out))
954+
{
955+
diagnostics.Add(ErrorCode.ERR_PartialMethodWithOutParamMustHaveAccessMods, location, this);
956+
}
928957
else if (this.DeclaredAccessibility == Accessibility.Private && (IsVirtual || (IsAbstract && !isExplicitInterfaceImplementationInInterface) || IsOverride))
929958
{
930959
diagnostics.Add(ErrorCode.ERR_VirtualPrivate, location, this);
@@ -1144,6 +1173,20 @@ private static void PartialMethodChecks(SourceOrdinaryMethodSymbol definition, S
11441173
diagnostics.Add(ErrorCode.ERR_PartialMethodParamsDifference, implementation.Locations[0]);
11451174
}
11461175

1176+
if (definition.HasExplicitAccessModifier != implementation.HasExplicitAccessModifier
1177+
|| definition.DeclaredAccessibility != implementation.DeclaredAccessibility)
1178+
{
1179+
diagnostics.Add(ErrorCode.ERR_PartialMethodAccessibilityDifference, implementation.Locations[0]);
1180+
}
1181+
1182+
if (definition.IsVirtual != implementation.IsVirtual
1183+
|| definition.IsOverride != implementation.IsOverride
1184+
|| definition.IsSealed != implementation.IsSealed
1185+
|| definition.IsNew != implementation.IsNew)
1186+
{
1187+
diagnostics.Add(ErrorCode.ERR_PartialMethodExtendedModDifference, implementation.Locations[0]);
1188+
}
1189+
11471190
PartialMethodConstraintsChecks(definition, implementation, diagnostics);
11481191

11491192
SourceMemberContainerTypeSymbol.CheckValidNullableMethodOverride(

0 commit comments

Comments
 (0)