Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,13 @@ ClassDeclarationSyntax or
}

var modifiers = node.Modifiers.ToDeclarationModifiers(isForTypeDeclaration: true, diagnostics: diagnostics);
// Extensions never allow 'partial', so there is no need to report an ordering error.
// The invalid modifier is reported later.
if (kind != DeclarationKind.Extension)
{
node.Modifiers.CheckPartialModifierOrder(diagnostics, isOrdinaryMethod: false);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: an alternative is to still have CheckPartialModifierOrder inside ToDeclarationModifiers, and then pass in a flag if you don't want them to be checked (because you know it would be be unhelpful with other errors you know will happen).

I'm fine with either approach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this case, we'd have ToDeclarationModifiers(..., checkPartialModifierOrder: kind != DeclarationKind.Extension)


var quickAttributes = GetQuickAttributes(node.AttributeLists);

foreach (var modifier in node.Modifiers)
Expand Down
47 changes: 33 additions & 14 deletions src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ internal static DeclarationModifiers MakeAndCheckNonTypeMemberModifiers(
out bool modifierErrors,
out bool hasExplicitAccessModifier)
{
var result = modifiers.ToDeclarationModifiers(isForTypeDeclaration: false, diagnostics.DiagnosticBag ?? new DiagnosticBag(), isOrdinaryMethod: isOrdinaryMethod);
var diagnosticBag = diagnostics.DiagnosticBag ?? new DiagnosticBag();
var result = modifiers.ToDeclarationModifiers(isForTypeDeclaration: false, diagnosticBag);
// There is no need to report an ordering error when 'partial' is not allowed.
// The invalid modifier is reported below.
if ((allowedModifiers & DeclarationModifiers.Partial) != 0)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here we'd do ToDeclarationModifiers(..., checkPartialModifierOrder: (allowedModifiers & DeclarationModifiers.Partial) != 0)

{
modifiers.CheckPartialModifierOrder(diagnosticBag, isOrdinaryMethod);
}

result = CheckModifiers(isForTypeDeclaration: false, isForInterfaceMember, result, allowedModifiers, errorLocation, diagnostics, modifiers, out modifierErrors);

var readonlyToken = modifiers.FirstOrDefault(SyntaxKind.ReadOnlyKeyword);
Expand Down Expand Up @@ -463,7 +471,9 @@ private static DeclarationModifiers GetDeclarationModifiersAndCheckForDuplicateM
}

public static DeclarationModifiers ToDeclarationModifiers(
this SyntaxTokenList modifiers, bool isForTypeDeclaration, DiagnosticBag diagnostics, bool isOrdinaryMethod = false)
this SyntaxTokenList modifiers,
bool isForTypeDeclaration,
DiagnosticBag diagnostics)
{
var result = GetDeclarationModifiersAndCheckForDuplicateModifiers(modifiers, diagnostics);
if ((result & DeclarationModifiers.Partial) == DeclarationModifiers.Partial)
Expand All @@ -473,18 +483,6 @@ public static DeclarationModifiers ToDeclarationModifiers(

var messageId = isForTypeDeclaration ? MessageID.IDS_FeaturePartialTypes : MessageID.IDS_FeaturePartialMethod;
messageId.CheckFeatureAvailability(diagnostics, modifier);

// `partial` must always be the last modifier according to the language. However, there was a bug
// where we allowed `partial async` at the end of modifiers on methods. We keep this behavior for
// backcompat.
var isLast = i == modifiers.Count - 1;
var isPartialAsyncMethod = isOrdinaryMethod && i == modifiers.Count - 2 && modifiers[i + 1].ContextualKind() is SyntaxKind.AsyncKeyword;
if (!isLast && !isPartialAsyncMethod)
{
diagnostics.Add(
ErrorCode.ERR_PartialMisplaced,
modifier.GetLocation());
}
}

switch (result & DeclarationModifiers.AccessibilityMask)
Expand All @@ -505,6 +503,27 @@ public static DeclarationModifiers ToDeclarationModifiers(
return result;
}

internal static void CheckPartialModifierOrder(this SyntaxTokenList modifiers, DiagnosticBag diagnostics, bool isOrdinaryMethod)
{
var i = modifiers.IndexOf(SyntaxKind.PartialKeyword);
if (i < 0)
{
return;
}

// `partial` must always be the last modifier according to the language. However, there was a bug
// where we allowed `partial async` at the end of modifiers on methods. We keep this behavior for
// backcompat.
var isLast = i == modifiers.Count - 1;
var isPartialAsyncMethod = isOrdinaryMethod && i == modifiers.Count - 2 && modifiers[i + 1].ContextualKind() is SyntaxKind.AsyncKeyword;
if (!isLast && !isPartialAsyncMethod)
{
diagnostics.Add(
ErrorCode.ERR_PartialMisplaced,
modifiers[i].GetLocation());
}
}

private static void ReportDuplicateModifiers(
SyntaxToken modifierToken,
DeclarationModifiers modifierKind,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45105,18 +45105,12 @@ interface I19
// (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static I14();
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(66, 5),
// (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static I14();
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(66, 5),
// (70,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// static partial I15();
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(70, 12),
// (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static I16() {}
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(74, 5),
// (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static I16() {}
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(74, 5),
// (78,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// static partial I17() => throw null;
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(78, 12),
Expand Down
3 changes: 0 additions & 3 deletions src/Compilers/CSharp/Test/Symbol/Symbols/EnumTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,6 @@ static void Main()
public void PartialPublicEnum()
{
CreateCompilation("partial public enum E { }").VerifyDiagnostics(
// (1,1): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type.
// partial public enum E { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(1, 1),
// (1,21): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type.
// partial public enum E { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "E").WithLocation(1, 21));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -841,9 +841,6 @@ public static void Main()
public void PartialPublicDelegate()
{
CreateCompilation("partial public delegate void M();").VerifyDiagnostics(
// (1,1): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type.
// partial public delegate void M();
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(1, 1),
// (1,30): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type.
// partial public delegate void M();
Diagnostic(ErrorCode.ERR_PartialMisplaced, "M").WithLocation(1, 30));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -822,30 +822,18 @@ partial sealed static I3() {}
// (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial sealed static I2();
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 5),
// (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial sealed static I2();
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 5),
// (9,27): error CS0106: The modifier 'sealed' is not valid for this item
// partial sealed static I2();
Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("sealed").WithLocation(9, 27),
// (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static I2() {}
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 5),
// (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static I2() {}
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 5),
// (14,20): error CS0111: Type 'I2' already defines a member called 'I2' with the same parameter types
// partial static I2() {}
Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I2").WithArguments("I2", "I2").WithLocation(14, 20),
// (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static I3();
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 5),
// (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static I3();
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 5),
// (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial sealed static I3() {}
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(24, 5),
// (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial sealed static I3() {}
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(24, 5),
Expand Down
15 changes: 0 additions & 15 deletions src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22309,24 +22309,12 @@ partial public static PartialPublicStaticCtor() { }
// 6.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static public PartialStaticPublicCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5),
// 6.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static public PartialStaticPublicCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5),
// 0.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static PartialStaticCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5),
// 0.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial static PartialStaticCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5),
// 7.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial public static PartialPublicStaticCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5),
// 7.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// partial public static PartialPublicStaticCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5),
// 3.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// public partial static PublicPartialStaticCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12),
// 3.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// public partial static PublicPartialStaticCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12),
Expand All @@ -22336,9 +22324,6 @@ partial public static PartialPublicStaticCtor() { }
// 5.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// static partial public StaticPartialPublicCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12),
// 5.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// static partial public StaticPartialPublicCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12),
// 2.cs(3,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// public static partial PublicStaticPartialCtor() { }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 19),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,6 @@ partial class C

var comp = CreateCompilation(text);
comp.VerifyDiagnostics(
// (4,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type.
// partial public static int operator + (C c1, C c2) { return 0; }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 5),
// (4,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type.
// partial public static int operator + (C c1, C c2) { return 0; }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 5),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,6 @@ class C
EOF();

CreateCompilation(source).VerifyDiagnostics(
// (3,13): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// int P { partial ref get; }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 13),
// (3,13): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.
// int P { partial ref get; }
Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 13),
Expand Down
Loading