Skip to content

Commit

Permalink
Parse partial events and constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
jjonescz committed Jan 22, 2025
1 parent 347e576 commit 91ccc2a
Show file tree
Hide file tree
Showing 23 changed files with 1,236 additions and 174 deletions.
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1171,7 +1171,7 @@
<value>Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?)</value>
</data>
<data name="ERR_PartialMisplaced" xml:space="preserve">
<value>The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type.</value>
<value>The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor identifier, or a method or property return type.</value>
</data>
<data name="ERR_ImportedCircularBase" xml:space="preserve">
<value>Imported type '{0}' is invalid. It contains a circular base type dependency.</value>
Expand Down
46 changes: 24 additions & 22 deletions src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,7 @@ private void ParseModifiers(SyntaxListBuilder tokens, bool forAccessors, bool fo
{
case DeclarationModifiers.Partial:
var nextToken = PeekToken(1);
if (this.IsPartialType() || this.IsPartialMember())
if (this.IsPartialType() || this.IsPartialMember(allowPartialCtor: !forTopLevelStatements))
{
// Standard legal cases.
modTok = ConvertToKeyword(this.EatToken());
Expand Down Expand Up @@ -1632,26 +1632,28 @@ private bool IsPartialType()
return false;
}

private bool IsPartialMember()
private bool IsPartialMember(bool allowPartialCtor)
{
// note(cyrusn): this could have been written like so:
//
// return
// this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword &&
// this.PeekToken(1).Kind == SyntaxKind.VoidKeyword;
//
// However, we want to be lenient and allow the user to write
// 'partial' in most modifier lists. We will then provide them with
// a more specific message later in binding that they are doing
// something wrong.
//
// Some might argue that the simple check would suffice.
// However, we'd like to maintain behavior with
// previously shipped versions, and so we're keeping this code.
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword);

// Check for:
// partial event
if (this.PeekToken(1).Kind == SyntaxKind.EventKeyword)
{
return true;
}

// Here we check for:
// Check for constructor:
// partial Identifier(
if (allowPartialCtor &&
this.PeekToken(1).Kind == SyntaxKind.IdentifierToken &&
this.PeekToken(2).Kind == SyntaxKind.OpenParenToken)
{
return true;
}

// Check for method/property:
// partial ReturnType MemberName
Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword);
using var _ = this.GetDisposableResetPoint(resetOnDispose: true);

this.EatToken(); // partial
Expand Down Expand Up @@ -5680,7 +5682,7 @@ private bool IsTrueIdentifier()
{
if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken)
{
if (!IsCurrentTokenPartialKeywordOfPartialMethodOrType() &&
if (!IsCurrentTokenPartialKeywordOfPartialMemberOrType() &&
!IsCurrentTokenQueryKeywordInQuery() &&
!IsCurrentTokenWhereOfConstraintClause())
{
Expand Down Expand Up @@ -5727,7 +5729,7 @@ private SyntaxToken ParseIdentifierToken(ErrorCode code = ErrorCode.ERR_Identifi
// show the correct parameter help in this case. So, when we see "partial" we check if it's being used
// as an identifier or as a contextual keyword. If it's the latter then we bail out. See
// Bug: vswhidbey/542125
if (IsCurrentTokenPartialKeywordOfPartialMethodOrType() || IsCurrentTokenQueryKeywordInQuery())
if (IsCurrentTokenPartialKeywordOfPartialMemberOrType() || IsCurrentTokenQueryKeywordInQuery())
{
var result = CreateMissingIdentifierToken();
result = this.AddError(result, ErrorCode.ERR_InvalidExprTerm, this.CurrentToken.Text);
Expand All @@ -5754,11 +5756,11 @@ private bool IsCurrentTokenQueryKeywordInQuery()
return this.IsInQuery && this.IsCurrentTokenQueryContextualKeyword;
}

private bool IsCurrentTokenPartialKeywordOfPartialMethodOrType()
private bool IsCurrentTokenPartialKeywordOfPartialMemberOrType()
{
if (this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword)
{
if (this.IsPartialType() || this.IsPartialMember())
if (this.IsPartialType() || this.IsPartialMember(allowPartialCtor: false))
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,17 @@ private static DeclarationModifiers MakeModifiers(
var defaultAccess = (methodKind == MethodKind.StaticConstructor) ? DeclarationModifiers.None : DeclarationModifiers.Private;

// Check that the set of modifiers is allowed
const DeclarationModifiers allowedModifiers =
DeclarationModifiers allowedModifiers =
DeclarationModifiers.AccessibilityMask |
DeclarationModifiers.Static |
DeclarationModifiers.Extern |
DeclarationModifiers.Unsafe;

if (methodKind == MethodKind.Constructor)
{
allowedModifiers |= DeclarationModifiers.Partial;
}

bool isInterface = containingType.IsInterface;
var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: isInterface, syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors);

Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10588,5 +10588,35 @@ void M()
// [A(p)] void F() { }
Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F").WithArguments("F").WithLocation(13, 21));
}

[Fact]
public void ReturningPartialType_InMethod()
{
var source = """
class @partial
{
static void Main()
{
System.Console.Write(F().GetType().Name);

partial F() => new();
}
}
""";
CompileAndVerify(source, expectedOutput: "partial").VerifyDiagnostics();
}

[Fact]
public void ReturningPartialType_TopLevel()
{
var source = """
System.Console.Write(F().GetType().Name);

partial F() => new();

class @partial;
""";
CompileAndVerify(source, expectedOutput: "partial").VerifyDiagnostics();
}
}
}
Loading

0 comments on commit 91ccc2a

Please sign in to comment.