-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Report all-empty top level statements. #54385
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,6 +59,7 @@ private ImmutableArray<SingleNamespaceOrTypeDeclaration> VisitNamespaceChildren( | |
bool isIterator = false; | ||
bool hasReturnWithExpression = false; | ||
GlobalStatementSyntax firstGlobalStatement = null; | ||
bool hasNonEmptyGlobalSatement = false; | ||
|
||
var childrenBuilder = ArrayBuilder<SingleNamespaceOrTypeDeclaration>.GetInstance(); | ||
foreach (var member in members) | ||
|
@@ -74,6 +75,11 @@ private ImmutableArray<SingleNamespaceOrTypeDeclaration> VisitNamespaceChildren( | |
firstGlobalStatement ??= global; | ||
var topLevelStatement = global.Statement; | ||
|
||
if (!topLevelStatement.IsKind(SyntaxKind.EmptyStatement)) | ||
{ | ||
hasNonEmptyGlobalSatement = true; | ||
} | ||
|
||
if (!hasAwaitExpressions) | ||
{ | ||
hasAwaitExpressions = SyntaxFacts.HasAwaitOperations(topLevelStatement); | ||
|
@@ -98,7 +104,16 @@ private ImmutableArray<SingleNamespaceOrTypeDeclaration> VisitNamespaceChildren( | |
// wrap all global statements in a compilation unit into a simple program type: | ||
if (firstGlobalStatement is object) | ||
{ | ||
childrenBuilder.Add(CreateSimpleProgram(firstGlobalStatement, hasAwaitExpressions, isIterator, hasReturnWithExpression)); | ||
var diagnostics = ImmutableArray<Diagnostic>.Empty; | ||
|
||
if (!hasNonEmptyGlobalSatement) | ||
{ | ||
var bag = DiagnosticBag.GetInstance(); | ||
bag.Add(ErrorCode.ERR_SimpleProgramIsEmpty, ((EmptyStatementSyntax)firstGlobalStatement.Statement).SemicolonToken.GetLocation()); | ||
diagnostics = bag.ToReadOnlyAndFree(); | ||
} | ||
|
||
childrenBuilder.Add(CreateSimpleProgram(firstGlobalStatement, hasAwaitExpressions, isIterator, hasReturnWithExpression, diagnostics)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: extra empty line below #Resolved |
||
} | ||
|
||
// wrap all members that are defined in a namespace or compilation unit into an implicit type: | ||
|
@@ -130,7 +145,7 @@ private static SingleNamespaceOrTypeDeclaration CreateImplicitClass(ImmutableSeg | |
diagnostics: ImmutableArray<Diagnostic>.Empty); | ||
} | ||
|
||
private static SingleNamespaceOrTypeDeclaration CreateSimpleProgram(GlobalStatementSyntax firstGlobalStatement, bool hasAwaitExpressions, bool isIterator, bool hasReturnWithExpression) | ||
private static SingleNamespaceOrTypeDeclaration CreateSimpleProgram(GlobalStatementSyntax firstGlobalStatement, bool hasAwaitExpressions, bool isIterator, bool hasReturnWithExpression, ImmutableArray<Diagnostic> diagnostics) | ||
{ | ||
return new SingleTypeDeclaration( | ||
kind: DeclarationKind.SimpleProgram, | ||
|
@@ -144,7 +159,7 @@ private static SingleNamespaceOrTypeDeclaration CreateSimpleProgram(GlobalStatem | |
nameLocation: new SourceLocation(firstGlobalStatement.GetFirstToken()), | ||
memberNames: ImmutableSegmentedDictionary<string, VoidResult>.Empty, | ||
children: ImmutableArray<SingleTypeDeclaration>.Empty, | ||
diagnostics: ImmutableArray<Diagnostic>.Empty); | ||
diagnostics: diagnostics); | ||
} | ||
|
||
/// <summary> | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8819,5 +8819,98 @@ void M() | |
Statements (0) | ||
"); | ||
} | ||
|
||
[Fact] | ||
public void EmptyStatements_01() | ||
{ | ||
var text = @";"; | ||
|
||
var comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions); | ||
comp.VerifyDiagnostics( | ||
// (1,1): error CS8937: At least one top-level statement must be non-empty. | ||
// ; | ||
Diagnostic(ErrorCode.ERR_SimpleProgramIsEmpty, ";").WithLocation(1, 1) | ||
); | ||
} | ||
|
||
[Fact] | ||
public void EmptyStatements_02() | ||
{ | ||
var text = @";; | ||
|
||
|
||
;; | ||
;"; | ||
|
||
var comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions); | ||
comp.VerifyDiagnostics( | ||
// (1,1): error CS8937: At least one top-level statement must be non-empty. | ||
// ;; | ||
Diagnostic(ErrorCode.ERR_SimpleProgramIsEmpty, ";").WithLocation(1, 1) | ||
); | ||
} | ||
|
||
[Fact] | ||
public void EmptyStatements_03() | ||
{ | ||
var text = @" | ||
System.Console.WriteLine(""Hi!""); | ||
;; | ||
; | ||
"; | ||
|
||
var comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions); | ||
CompileAndVerify(comp, expectedOutput: "Hi!"); | ||
} | ||
|
||
[Fact] | ||
public void EmptyStatements_04() | ||
{ | ||
var text = @" | ||
;; | ||
; | ||
System.Console.WriteLine(""Hi!"");"; | ||
|
||
var comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions); | ||
CompileAndVerify(comp, expectedOutput: "Hi!"); | ||
} | ||
|
||
[Fact] | ||
public void EmptyStatements_05() | ||
{ | ||
var text = @" | ||
; | ||
System.Console.WriteLine(""Hi!""); | ||
; | ||
"; | ||
|
||
var comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions); | ||
CompileAndVerify(comp, expectedOutput: "Hi!"); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Consider adding a test from the original issue |
||
|
||
[Fact] | ||
public void EmptyStatements_06() | ||
{ | ||
var text = | ||
@" | ||
using System; | ||
; | ||
|
||
class Program | ||
{ | ||
static void Main(String[] args) {} | ||
} | ||
"; | ||
|
||
var comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions); | ||
comp.VerifyDiagnostics( | ||
// (3,1): error CS8937: At least one top-level statement must be non-empty. | ||
// ; | ||
Diagnostic(ErrorCode.ERR_SimpleProgramIsEmpty, ";").WithLocation(3, 1), | ||
// (7,17): warning CS7022: The entry point of the program is global code; ignoring 'Program.Main(string[])' entry point. | ||
// static void Main(String[] args) {} | ||
Diagnostic(ErrorCode.WRN_MainIgnored, "Main").WithArguments("Program.Main(string[])").WithLocation(7, 17) | ||
); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the error be reported if the top-level program contains only local functions?
i.e,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#53472 (comment) - "LDM decided to make it an error if all top-level statements are empty." I do not see LDM deciding anyjthing about local function statements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlekseyTs Yeah I see the PR matches LDM decision, but I mean, was that considered?
The goal of the error is to tell the user "your program will execute nothing and just return 0 immediately", which is the case when the top-level statements are local functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think that is the goal, an entry point function is always executed. There is plenty of ways to write code that does nothing even when it has non-empty statements. We were addressing specific customers scenario and I don't think there was a goal to generalize the issue. If you feel that the issue should be generailized, open an issue and provide your reasons. I beleive a local function that is never called produces a warning, in my opinion that is good enough to give a hint that the code probably doesn't do what was intended.