Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
91 changes: 86 additions & 5 deletions src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2584,6 +2584,15 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind
TypeParameterListSyntax typeParameterListOpt;
this.ParseMemberName(out explicitInterfaceOpt, out identifierOrThisOpt, out typeParameterListOpt, isEvent: false);

if (!haveModifiers && !haveAttributes && !IsScript &&
explicitInterfaceOpt == null && identifierOrThisOpt == null && typeParameterListOpt == null &&
!type.IsMissing && type.Kind != SyntaxKind.RefType &&
!isFollowedByPossibleUsingDirective() &&
tryParseLocalDeclarationStatementFromStartPoint<LocalDeclarationStatementSyntax>(attributes, ref afterAttributesPoint, out result))
{
return result;
}

// First, check if we got absolutely nothing. If so, then
// We need to consume a bad member and try again.
if (IsNoneOrIncompleteMember(parentKind, attributes, modifiers, type, explicitInterfaceOpt, identifierOrThisOpt, typeParameterListOpt, out result))
Expand Down Expand Up @@ -2614,14 +2623,23 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind
return result;
}

// treat anything else as a method.

if (!IsScript && explicitInterfaceOpt is null &&
tryParseLocalDeclarationStatementFromStartPoint<LocalFunctionStatementSyntax>(attributes, ref afterAttributesPoint, out result))
if (!IsScript)
{
return result;
if (explicitInterfaceOpt is null &&
tryParseLocalDeclarationStatementFromStartPoint<LocalFunctionStatementSyntax>(attributes, ref afterAttributesPoint, out result))
{
return result;
}

if (!haveModifiers &&
tryParseStatement(attributes, ref afterAttributesPoint, out result))
{
return result;
}
}

// treat anything else as a method.

return this.ParseMethodDeclaration(attributes, modifiers, type, explicitInterfaceOpt, identifierOrThisOpt, typeParameterListOpt);
}
finally
Expand Down Expand Up @@ -2656,6 +2674,43 @@ bool tryParseLocalDeclarationStatement<DeclarationSyntax>(SyntaxList<AttributeLi
return false;
}

bool tryParseStatement(SyntaxList<AttributeListSyntax> attributes, ref ResetPoint afterAttributesPoint, out MemberDeclarationSyntax result)
{
var resetOnFailurePoint = this.GetResetPoint();
try
{
this.Reset(ref afterAttributesPoint);

if (this.IsPossibleStatement(acceptAccessibilityMods: false))
{
var saveTerm = _termState;
_termState |= TerminatorState.IsPossibleStatementStartOrStop; // partial statements can abort if a new statement starts
bool wasInAsync = IsInAsync;
IsInAsync = true; // We are implicitly in an async context

var statement = this.ParseStatementCore(attributes, isGlobal: true);

IsInAsync = wasInAsync;
_termState = saveTerm;

if (statement is not null)
{
result = CheckTopLevelStatementsFeatureAvailability(_syntaxFactory.GlobalStatement(statement));
return true;
}
}

this.Reset(ref resetOnFailurePoint);
}
finally
{
this.Release(ref resetOnFailurePoint);
}

result = null;
return false;
}

bool tryParseLocalDeclarationStatementFromStartPoint<DeclarationSyntax>(SyntaxList<AttributeListSyntax> attributes, ref ResetPoint startPoint, out MemberDeclarationSyntax result) where DeclarationSyntax : StatementSyntax
{
var resetOnFailurePoint = this.GetResetPoint();
Expand Down Expand Up @@ -2700,6 +2755,32 @@ static bool isAcceptableNonDeclarationStatement(StatementSyntax statement, bool
return true;
}
}

bool isFollowedByPossibleUsingDirective()
{
if (CurrentToken.Kind == SyntaxKind.UsingKeyword)
{
return !IsPossibleTopLevelUsingLocalDeclarationStatement();
}

if (CurrentToken.ContextualKind == SyntaxKind.GlobalKeyword && this.PeekToken(1).Kind == SyntaxKind.UsingKeyword)
{
var resetPoint = this.GetResetPoint();
try
{
// Skip 'global' keyword
EatToken();
return !IsPossibleTopLevelUsingLocalDeclarationStatement();
}
finally
{
this.Reset(ref resetPoint);
this.Release(ref resetPoint);
}
}

return false;
}
}

private bool IsMisplacedModifier(SyntaxListBuilder modifiers, SyntaxList<AttributeListSyntax> attributes, TypeSyntax type, out MemberDeclarationSyntax result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,24 @@ public void UsingAliasTest()
using s = delegate*<void>;");

comp.VerifyDiagnostics(
// (2,26): error CS8805: Program using top-level statements must be an executable.
// using s = delegate*<void>;
Diagnostic(ErrorCode.ERR_SimpleProgramNotAnExecutable, ";").WithLocation(2, 26),
// (2,1): hidden CS8019: Unnecessary using directive.
// using s = delegate*<void>;
Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using s = ").WithLocation(2, 1),
// (2,11): error CS1041: Identifier expected; 'delegate' is a keyword
// using s = delegate*<void>;
Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "delegate").WithArguments("", "delegate").WithLocation(2, 11),
// (2,25): error CS0116: A namespace cannot directly contain members such as fields or methods
// (2,26): error CS1001: Identifier expected
// using s = delegate*<void>;
Diagnostic(ErrorCode.ERR_NamespaceUnexpected, ">").WithLocation(2, 25),
Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(2, 26),
// (2,7): warning CS8981: The type name 's' only contains lower-cased ascii characters. Such names may become reserved for the language.
// using s = delegate*<void>;
Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "s").WithArguments("s").WithLocation(2, 7),

// See same-named test in TopLevelStatementsParsingTests, there is a single top-level statement in the tree and it is an empty statement.
// (2,26): error CS8937: At least one top-level statement must be non-empty.
// (2,11): error CS8805: Program using top-level statements must be an executable.
// using s = delegate*<void>;
Diagnostic(ErrorCode.ERR_SimpleProgramNotAnExecutable, "delegate*<void>;").WithLocation(2, 11),
// (2,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
// using s = delegate*<void>;
Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(2, 11),
// (2,1): hidden CS8019: Unnecessary using directive.
// using s = delegate*<void>;
Diagnostic(ErrorCode.ERR_SimpleProgramIsEmpty, ";").WithLocation(2, 26)
Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using s = ").WithLocation(2, 1)
);
}

Expand Down
6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Test/Semantic/Semantics/LookupTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1403,7 +1403,7 @@ static void Main(string[] args)
[Fact, WorkItem(546523, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546523")]
public void TestLookupSymbolsNestedNamespacesNotImportedByUsings_02()
{
var usings = new[] { "using X;" };
var usings = "using X;";

var source =
@"
Expand Down Expand Up @@ -1443,10 +1443,10 @@ public static void Main()
}
";
// Get the list of LookupSymbols at the location of the CSharpSyntaxNode
var actual_lookupSymbols = GetLookupSymbols(usings.ToString() + source, isScript: false);
var actual_lookupSymbols = GetLookupSymbols(usings + source, isScript: false);
TestLookupSymbolsNestedNamespaces(actual_lookupSymbols);

actual_lookupSymbols = GetLookupSymbols(source, isScript: true, globalUsings: usings);
actual_lookupSymbols = GetLookupSymbols(source, isScript: true, globalUsings: new[] { usings });
TestLookupSymbolsNestedNamespaces(actual_lookupSymbols);

Action<ModuleSymbol> validator = (module) =>
Expand Down
22 changes: 14 additions & 8 deletions src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,24 +243,30 @@ record class Point(int x, int y);
";
var comp = CreateCompilation(src, parseOptions: TestOptions.Regular8, options: TestOptions.ReleaseDll);
comp.VerifyDiagnostics(
// (2,1): error CS0116: A namespace cannot directly contain members such as fields or methods
// (2,1): error CS8400: Feature 'top-level statements' is not available in C# 8.0. Please use language version 9.0 or greater.
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "record ").WithArguments("top-level statements", "9.0").WithLocation(2, 1),
// (2,1): error CS8805: Program using top-level statements must be an executable.
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_SimpleProgramNotAnExecutable, "record ").WithLocation(2, 1),
// (2,1): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?)
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "record").WithLocation(2, 1),
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(2, 1),
// (2,8): error CS1001: Identifier expected
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_IdentifierExpected, "class").WithLocation(2, 8),
// (2,8): error CS1002: ; expected
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_SemicolonExpected, "class").WithLocation(2, 8),
// (2,19): error CS1514: { expected
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_LbraceExpected, "(").WithLocation(2, 19),
// (2,19): error CS1513: } expected
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(2, 19),
// (2,19): error CS8400: Feature 'top-level statements' is not available in C# 8.0. Please use language version 9.0 or greater.
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "(int x, int y);").WithArguments("top-level statements", "9.0").WithLocation(2, 19),
// (2,19): error CS8803: Top-level statements must precede namespace and type declarations.
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int x, int y);").WithLocation(2, 19),
// (2,19): error CS8805: Program using top-level statements must be an executable.
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_SimpleProgramNotAnExecutable, "(int x, int y);").WithLocation(2, 19),
// (2,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
// record class Point(int x, int y);
Diagnostic(ErrorCode.ERR_IllegalStatement, "(int x, int y)").WithLocation(2, 19),
Expand Down
10 changes: 8 additions & 2 deletions src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10937,9 +10937,15 @@ scoped readonly ref struct C { }
";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion));
comp.VerifyDiagnostics(
// (1,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements
// (1,1): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?)
// scoped struct A { }
Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "scoped").WithLocation(1, 1),
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(1, 1),
// (1,8): error CS1001: Identifier expected
// scoped struct A { }
Diagnostic(ErrorCode.ERR_IdentifierExpected, "struct").WithLocation(1, 8),
// (1,8): error CS1002: ; expected
// scoped struct A { }
Diagnostic(ErrorCode.ERR_SemicolonExpected, "struct").WithLocation(1, 8),
// (2,12): error CS1031: Type expected
// scoped ref struct B { }
Diagnostic(ErrorCode.ERR_TypeExpected, "struct").WithLocation(2, 12),
Expand Down
Loading