Page MenuHomePhabricator

[clang][Parse] Diagnose useless null statements (PR39111)

Authored by lebedev.ri on Sep 29 2018, 10:43 AM.



clang has -Wextra-semi (D43162), which is not dictated by the currently selected standard.
While that is great, there is at least one more source of need-less semis - 'null statements'.
Sometimes, they are needed:

for(int x = 0; continueToDoWork(x); x++)
  ; // Ugly code, but the semi is needed here.

But sometimes they are just there for no reason:

switch(X) {
case 0:
  return -2345;
case 5:
  return 0;
  return 42;
}; // <- oops

;;;;;;;;;;; <- OOOOPS, still not diagnosed. Clearly this is junk.

As usual, things may or may not go sideways in the presence of macros.
While evaluating this diag on my codebase of interest, it was unsurprisingly
discovered that Google Test macros are *very* prone to this.
And it seems many issues are deep within the GTest itself, not
in the snippets passed from the codebase that uses GTest.

So after some thought, i decided not do issue a diagnostic if the semi
is within *any* macro, be it either from the normal header, or system header.

Fixes PR39111

Diff Detail

rC Clang

Event Timeline

lebedev.ri created this revision.Sep 29 2018, 10:43 AM

Again, I'm not sure we really want this... we don't really like adding new off-by-default warnings, and we don't usually add diagnostics to enforce style.


We usually only use "pedantic" in the names of warnings which are enabled by the "-pedantic" flag.

rsmith added a comment.Oct 1 2018, 3:49 PM

Like Eli, I don't see much value in this warning. But it doesn't seem much different from -Wextra-semi in that regard for languages in which such extra semicolons are generally valid -- I expect both warnings will generally diagnose typos and little else.

Does this warn on:

switch (my_enum) {
case E1:
  // stuff
case E2:
  ; // nothing to do, but a label requires a following statement


for (/*...outer...*/) {
  for (/*...inner...*/) {
    goto contin_outer;
  contin_outer: ;

? We shouldn't warn on a semicolon after a label, because a statement is required there, and use of a null statement is idiomatic.


Maybe -Wextra-semi-stmt or -Wredundant-null-stmt or similar would be more appropriate?


I'm a little concerned that checking whether the scope is a compound statement isn't really checking the right thing -- what you care about is whether the syntactic context is a compound statement, not which scope a declaration at this level would be injected into. (Example of the difference: back in the pre-standard times when { for (int x = 0; x < 10; ++x) //... injected x into the enclosing scope, the first substatement in that for statement would be in a compound-statement scope, but we definitely shouldn't warn on a stray ; there.) If you moved this check into ParseCompoundStatementBody, there'd be no risk of such problems.


It'd seem reasonable to warn on a null-statement as the *init-statement* of an if / switch / range-based for.

lebedev.ri marked 4 inline comments as done.

Thank you for taking a look!

  • Move it into ParseCompoundStatementBody(), thus fixing false-positives with case X: ; e.g.
  • Rename to -Wextra-semi-stmt
  • Add -Wempty-init-stmt (tentatively included into -Wextra too), to diagnose empty init-statements of if/switch/range-based for

Great idea, thank you!
Testcases added, that fixed them.


If you so insist :)

rsmith added inline comments.Oct 2 2018, 1:34 PM

I think this should either be folded into -Wextra-semi-stmt or should perhaps be a subgroup of it.


I would prefer to keep -Wextra-semi and -Wextra-semi-stmt separate. -Wextra-semi is for those cases that are ill-formed in C++98, whereas your new warning is only about code style / typo detection.


You can just use a SourceLocation here; it already has an 'invalid' state.

lebedev.ri updated this revision to Diff 168017.Oct 2 2018, 1:47 PM
lebedev.ri marked 3 inline comments as done.
  • Moved -Wempty-init-stmt into -Wextra-semi-stmt
  • Moved -Wextra-semi-stmt out of -Wextra-semi
  • Tentatively enabled -Wextra-semi-stmt in -Wextra (and removed -Wempty-init-stmt from -Wextra, since it will be transitively enabled by -Wextra-semi-stmt)

Hmm, does that mean ExtraSemiStmt[+EmptyInitStatement] should be in -Wextra?



lebedev.ri updated this revision to Diff 168020.Oct 2 2018, 1:54 PM

Slightly improved test coverage for macros in extra-semi-resulting-in-nullstmt-in-init-statement.cpp

lebedev.ri added inline comments.Oct 2 2018, 2:06 PM

I'm really unsure of this. Maybe this should only be EmptyInitStatement.

lebedev.ri marked 4 inline comments as done.

After looking at stage-2 of LLVM, remove -Wextra-semi-stmt from -Wextra, and add -Wempty-init-stmt back into -Wextra.
Anything else?

rsmith added a comment.Oct 3 2018, 5:39 PM

I feel indifferent about this warning, but clearly you care about it enough to work on it, and it's a pretty low-cost feature from a maintenance perspective, so I suppose it's fine.


The warning should explain why we're warning: what the problem is, and why it warrants action from the user. I would expect people seeing this warning to think "yes, but so what?". Something like "statement has no effect" might be better, maybe? (A customized warning for ;; might be nice too.)

lebedev.ri updated this revision to Diff 168240.Oct 4 2018, 1:47 AM
lebedev.ri marked an inline comment as done.

Thank you for taking a look!

  • Reworded the diag as suggested a little
  • Do consume consequtive semis as one
    • Do still record each one of them in AST. I'm not sure if we want to do that, but else existing tests break.

I wonder if @aaron.ballman has any opinion on this.

Ping. Is even the -Wempty-init-stmt is not interesting?

Sorry about the delayed review on this -- I had intended to provide comments earlier, but somehow this fell off my radar.


, which is, much like -Wextra-semi, diagnoses extra semicolons. -> that diagnoses extra semicolons, much like -Wextra-semi.


fires on all -> diagnoses


semi -> semicolon


nothing; the semi is -> nothing or if the semicolon

I'd make the parenthetical a stand-alone sentence. Something like: "This applies to macros defined in system headers as well as user-defined macros."


I'd drop this from the example.


diagnostic, which is diagnoses empty -> diagnostic that diagnoses empty


semi -> semicolon


nothing; the semi is -> nothing or if the semicolon

I'd make the parenthetical a stand-alone sentence. Something like: "This applies to macros defined in system headers as well as user-defined macros."


diagnostic in -> diagnostic is in the

Drop the comma.


How about: empty expression statement has no effect; remove unnecessary ';' to silence this warning ?


How about: empty initialization statement of '%select{if|switch|range-based for}0' has no effect?


There are four additional tests I'd like to see: 1) a semicolon at global scope, 2) a semicolon after a namespace declaration (e.g., namespace foo {};), 3) a [[fallthrough]]; null statement, 4) a [[]]; null statement.

lebedev.ri marked 12 inline comments as done.

Address @aaron.ballman review notes.


None of these warn, added.

aaron.ballman accepted this revision.Nov 20 2018, 6:49 AM

Aside from some small nits, LGTM!


diagnostic, that -> diagnostic that


expanded nothing -> expanded to nothing


Spurious whitespace at the start of the diagnostic text.

This revision is now accepted and ready to land.Nov 20 2018, 6:49 AM

Aside from some small nits, LGTM!

Thank you for the review!
Will address nits and land.

This revision was automatically updated to reflect the committed changes.