diff options
author | Dmitri Gribenko <gribozavr@gmail.com> | 2012-02-14 22:14:32 +0000 |
---|---|---|
committer | Dmitri Gribenko <gribozavr@gmail.com> | 2012-02-14 22:14:32 +0000 |
commit | 800ddf3dda7b21f652da3d9720374997697f17e6 (patch) | |
tree | c4c8ce89332122705d7a8aaed4a1ebb43508b0b4 /clang/lib | |
parent | 973cf9e8ae0a79c33d4e0f58af0b596ddd96faa1 (diff) | |
download | bcm5719-llvm-800ddf3dda7b21f652da3d9720374997697f17e6.tar.gz bcm5719-llvm-800ddf3dda7b21f652da3d9720374997697f17e6.zip |
Generalize -Wempty-body: warn when statement body is empty (closes: PR11329)
* if, switch, range-based for: warn if semicolon is on the same line.
* for, while: warn if semicolon is on the same line and either next
statement is compound statement or next statement has more
indentation.
Replacing the semicolon with {} or moving the semicolon to the next
line will always silence the warning.
Tests from SemaCXX/if-empty-body.cpp merged into SemaCXX/warn-empty-body.cpp.
llvm-svn: 150515
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Parse/ParseObjc.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Parse/ParseStmt.cpp | 20 | ||||
-rw-r--r-- | clang/lib/Sema/Sema.cpp | 11 | ||||
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 127 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 30 | ||||
-rw-r--r-- | clang/lib/Sema/SemaStmt.cpp | 53 | ||||
-rw-r--r-- | clang/lib/Sema/TreeTransform.h | 2 |
7 files changed, 216 insertions, 31 deletions
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 41dfeddaab9..416cb39a639 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -2635,9 +2635,11 @@ Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) { StmtResult FnBody(ParseCompoundStatementBody()); // If the function body could not be parsed, make a bogus compoundstmt. - if (FnBody.isInvalid()) + if (FnBody.isInvalid()) { + Sema::CompoundScopeRAII CompoundScope(Actions); FnBody = Actions.ActOnCompoundStmt(BraceLoc, BraceLoc, MultiStmtArg(Actions), false); + } // Leave the function body scope. BodyScope.Exit(); diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index aa9ba06b67b..8af4da6a846 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -700,7 +700,6 @@ StmtResult Parser::ParseCompoundStatement(ParsedAttributes &attrs, return ParseCompoundStatementBody(isStmtExpr); } - /// ParseCompoundStatementBody - Parse a sequence of statements and invoke the /// ActOnCompoundStmt action. This expects the '{' to be the current token, and /// consume the '}' at the end of the block. It does not manipulate the scope @@ -714,6 +713,8 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { if (T.consumeOpen()) return StmtError(); + Sema::CompoundScopeRAII CompoundScope(Actions); + StmtVector Stmts(Actions); // "__label__ X, Y, Z;" is the GNU "Local Label" extension. These are @@ -1084,9 +1085,14 @@ StmtResult Parser::ParseSwitchStatement(ParsedAttributes &attrs, InnerScope.Exit(); SwitchScope.Exit(); - if (Body.isInvalid()) + if (Body.isInvalid()) { // FIXME: Remove the case statement list from the Switch statement. - Body = Actions.ActOnNullStmt(Tok.getLocation()); + + // Put the synthesized null statement on the same line as the end of switch + // condition. + SourceLocation SynthesizedNullStmtLocation = Cond.get()->getLocEnd(); + Body = Actions.ActOnNullStmt(SynthesizedNullStmtLocation); + } return Actions.ActOnFinishSwitchStmt(SwitchLoc, Switch.get(), Body.get()); } @@ -1956,9 +1962,11 @@ Decl *Parser::ParseFunctionStatementBody(Decl *Decl, ParseScope &BodyScope) { StmtResult FnBody(ParseCompoundStatementBody()); // If the function body could not be parsed, make a bogus compoundstmt. - if (FnBody.isInvalid()) + if (FnBody.isInvalid()) { + Sema::CompoundScopeRAII CompoundScope(Actions); FnBody = Actions.ActOnCompoundStmt(LBraceLoc, LBraceLoc, MultiStmtArg(Actions), false); + } BodyScope.Exit(); return Actions.ActOnFinishFunctionBody(Decl, FnBody.take()); @@ -1993,9 +2001,11 @@ Decl *Parser::ParseFunctionTryBlock(Decl *Decl, ParseScope &BodyScope) { StmtResult FnBody(ParseCXXTryBlockCommon(TryLoc)); // If we failed to parse the try-catch, we just give the function an empty // compound statement as the body. - if (FnBody.isInvalid()) + if (FnBody.isInvalid()) { + Sema::CompoundScopeRAII CompoundScope(Actions); FnBody = Actions.ActOnCompoundStmt(LBraceLoc, LBraceLoc, MultiStmtArg(Actions), false); + } BodyScope.Exit(); return Actions.ActOnFinishFunctionBody(Decl, FnBody.take()); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 42b102f55a0..716ffd131b2 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -861,6 +861,17 @@ void Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, } } +void Sema::PushCompoundScope() { + getCurFunction()->CompoundScopes.push_back(CompoundScopeInfo()); +} + +void Sema::PopCompoundScope() { + FunctionScopeInfo *CurFunction = getCurFunction(); + assert(!CurFunction->CompoundScopes.empty() && "mismatched push/pop"); + + CurFunction->CompoundScopes.pop_back(); +} + /// \brief Determine whether any errors occurred within this function/method/ /// block. bool Sema::hasAnyUnrecoverableErrorsInThisFunction() const { diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index cf0469592f8..3393cf73f1c 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4827,3 +4827,130 @@ void Sema::checkUnsafeExprAssigns(SourceLocation Loc, } } } + +//===--- CHECK: Empty statement body (-Wempty-body) ---------------------===// + +namespace { +bool ShouldDiagnoseEmptyStmtBody(const SourceManager &SourceMgr, + SourceLocation StmtLoc, + const NullStmt *Body) { + // Do not warn if the body is a macro that expands to nothing, e.g: + // + // #define CALL(x) + // if (condition) + // CALL(0); + // + if (Body->hasLeadingEmptyMacro()) + return false; + + // Get line numbers of statement and body. + bool StmtLineInvalid; + unsigned StmtLine = SourceMgr.getSpellingLineNumber(StmtLoc, + &StmtLineInvalid); + if (StmtLineInvalid) + return false; + + bool BodyLineInvalid; + unsigned BodyLine = SourceMgr.getSpellingLineNumber(Body->getSemiLoc(), + &BodyLineInvalid); + if (BodyLineInvalid) + return false; + + // Warn if null statement and body are on the same line. + if (StmtLine != BodyLine) + return false; + + return true; +} +} // Unnamed namespace + +void Sema::DiagnoseEmptyStmtBody(SourceLocation StmtLoc, + const Stmt *Body, + unsigned DiagID) { + // Since this is a syntactic check, don't emit diagnostic for template + // instantiations, this just adds noise. + if (CurrentInstantiationScope) + return; + + // The body should be a null statement. + const NullStmt *NBody = dyn_cast<NullStmt>(Body); + if (!NBody) + return; + + // Do the usual checks. + if (!ShouldDiagnoseEmptyStmtBody(SourceMgr, StmtLoc, NBody)) + return; + + Diag(NBody->getSemiLoc(), DiagID); + Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line); +} + +void Sema::DiagnoseEmptyLoopBody(const Stmt *S, + const Stmt *PossibleBody) { + assert(!CurrentInstantiationScope); // Ensured by caller + + SourceLocation StmtLoc; + const Stmt *Body; + unsigned DiagID; + if (const ForStmt *FS = dyn_cast<ForStmt>(S)) { + StmtLoc = FS->getRParenLoc(); + Body = FS->getBody(); + DiagID = diag::warn_empty_for_body; + } else if (const WhileStmt *WS = dyn_cast<WhileStmt>(S)) { + StmtLoc = WS->getCond()->getSourceRange().getEnd(); + Body = WS->getBody(); + DiagID = diag::warn_empty_while_body; + } else + return; // Neither `for' nor `while'. + + // The body should be a null statement. + const NullStmt *NBody = dyn_cast<NullStmt>(Body); + if (!NBody) + return; + + // Skip expensive checks if diagnostic is disabled. + if (Diags.getDiagnosticLevel(DiagID, NBody->getSemiLoc()) == + DiagnosticsEngine::Ignored) + return; + + // Do the usual checks. + if (!ShouldDiagnoseEmptyStmtBody(SourceMgr, StmtLoc, NBody)) + return; + + // `for(...);' and `while(...);' are popular idioms, so in order to keep + // noise level low, emit diagnostics only if for/while is followed by a + // CompoundStmt, e.g.: + // for (int i = 0; i < n; i++); + // { + // a(i); + // } + // or if for/while is followed by a statement with more indentation + // than for/while itself: + // for (int i = 0; i < n; i++); + // a(i); + bool ProbableTypo = isa<CompoundStmt>(PossibleBody); + if (!ProbableTypo) { + bool BodyColInvalid; + unsigned BodyCol = SourceMgr.getPresumedColumnNumber( + PossibleBody->getLocStart(), + &BodyColInvalid); + if (BodyColInvalid) + return; + + bool StmtColInvalid; + unsigned StmtCol = SourceMgr.getPresumedColumnNumber( + S->getLocStart(), + &StmtColInvalid); + if (StmtColInvalid) + return; + + if (BodyCol > StmtCol) + ProbableTypo = true; + } + + if (ProbableTypo) { + Diag(NBody->getSemiLoc(), DiagID); + Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line); + } +} + diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 68c938149ea..84063e5fd0f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -8218,10 +8218,14 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, CopyAssignOperator->setInvalidDecl(); return; } - - StmtResult Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements), - /*isStmtExpr=*/false); - assert(!Body.isInvalid() && "Compound statement creation cannot fail"); + + StmtResult Body; + { + CompoundScopeRAII CompoundScope(*this); + Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements), + /*isStmtExpr=*/false); + assert(!Body.isInvalid() && "Compound statement creation cannot fail"); + } CopyAssignOperator->setBody(Body.takeAs<Stmt>()); if (ASTMutationListener *L = getASTMutationListener()) { @@ -8650,10 +8654,14 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation, MoveAssignOperator->setInvalidDecl(); return; } - - StmtResult Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements), - /*isStmtExpr=*/false); - assert(!Body.isInvalid() && "Compound statement creation cannot fail"); + + StmtResult Body; + { + CompoundScopeRAII CompoundScope(*this); + Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements), + /*isStmtExpr=*/false); + assert(!Body.isInvalid() && "Compound statement creation cannot fail"); + } MoveAssignOperator->setBody(Body.takeAs<Stmt>()); if (ASTMutationListener *L = getASTMutationListener()) { @@ -8852,9 +8860,10 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation, << CXXCopyConstructor << Context.getTagDeclType(ClassDecl); CopyConstructor->setInvalidDecl(); } else { + Sema::CompoundScopeRAII CompoundScope(*this); CopyConstructor->setBody(ActOnCompoundStmt(CopyConstructor->getLocation(), CopyConstructor->getLocation(), - MultiStmtArg(*this, 0, 0), + MultiStmtArg(*this, 0, 0), /*isStmtExpr=*/false) .takeAs<Stmt>()); CopyConstructor->setImplicitlyDefined(true); @@ -9006,9 +9015,10 @@ void Sema::DefineImplicitMoveConstructor(SourceLocation CurrentLocation, << CXXMoveConstructor << Context.getTagDeclType(ClassDecl); MoveConstructor->setInvalidDecl(); } else { + Sema::CompoundScopeRAII CompoundScope(*this); MoveConstructor->setBody(ActOnCompoundStmt(MoveConstructor->getLocation(), MoveConstructor->getLocation(), - MultiStmtArg(*this, 0, 0), + MultiStmtArg(*this, 0, 0), /*isStmtExpr=*/false) .takeAs<Stmt>()); MoveConstructor->setImplicitlyDefined(true); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 0adfb948009..b47e3d29d17 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -225,6 +225,18 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { DiagRuntimeBehavior(Loc, 0, PDiag(DiagID) << R1 << R2); } +void Sema::ActOnStartOfCompoundStmt() { + PushCompoundScope(); +} + +void Sema::ActOnFinishOfCompoundStmt() { + PopCompoundScope(); +} + +sema::CompoundScopeInfo &Sema::getCurCompoundScope() const { + return getCurFunction()->CompoundScopes.back(); +} + StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, MultiStmtArg elts, bool isStmtExpr) { @@ -257,6 +269,15 @@ Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, DiagnoseUnusedExprResult(Elts[i]); } + // Check for suspicious empty body (null statement) in `for' and `while' + // statements. Don't do anything for template instantiations, this just adds + // noise. + if (NumElts != 0 && !CurrentInstantiationScope && + getCurCompoundScope().HasEmptyLoopBodies) { + for (unsigned i = 0; i != NumElts - 1; ++i) + DiagnoseEmptyLoopBody(Elts[i], Elts[i + 1]); + } + return Owned(new (Context) CompoundStmt(Context, Elts, NumElts, L, R)); } @@ -355,21 +376,9 @@ Sema::ActOnIfStmt(SourceLocation IfLoc, FullExprArg CondVal, Decl *CondVar, DiagnoseUnusedExprResult(thenStmt); - // Warn if the if block has a null body without an else value. - // this helps prevent bugs due to typos, such as - // if (condition); - // do_stuff(); - // if (!elseStmt) { - if (NullStmt* stmt = dyn_cast<NullStmt>(thenStmt)) - // But do not warn if the body is a macro that expands to nothing, e.g: - // - // #define CALL(x) - // if (condition) - // CALL(0); - // - if (!stmt->hasLeadingEmptyMacro()) - Diag(stmt->getSemiLoc(), diag::warn_empty_if_body); + DiagnoseEmptyStmtBody(ConditionExpr->getLocEnd(), thenStmt, + diag::warn_empty_if_body); } DiagnoseUnusedExprResult(elseStmt); @@ -957,6 +966,9 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, } } + DiagnoseEmptyStmtBody(CondExpr->getLocEnd(), BodyStmt, + diag::warn_empty_switch_body); + // FIXME: If the case list was broken is some way, we don't have a good system // to patch it up. Instead, just return the whole substmt as broken. if (CaseListIsErroneous) @@ -983,6 +995,9 @@ Sema::ActOnWhileStmt(SourceLocation WhileLoc, FullExprArg Cond, DiagnoseUnusedExprResult(Body); + if (isa<NullStmt>(Body)) + getCurCompoundScope().setHasEmptyLoopBodies(); + return Owned(new (Context) WhileStmt(Context, ConditionVar, ConditionExpr, Body, WhileLoc)); } @@ -1046,6 +1061,9 @@ Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, DiagnoseUnusedExprResult(Third); DiagnoseUnusedExprResult(Body); + if (isa<NullStmt>(Body)) + getCurCompoundScope().setHasEmptyLoopBodies(); + return Owned(new (Context) ForStmt(Context, First, SecondResult.take(), ConditionVar, Third, Body, ForLoc, LParenLoc, @@ -1587,7 +1605,12 @@ StmtResult Sema::FinishCXXForRangeStmt(Stmt *S, Stmt *B) { if (!S || !B) return StmtError(); - cast<CXXForRangeStmt>(S)->setBody(B); + CXXForRangeStmt *ForStmt = cast<CXXForRangeStmt>(S); + ForStmt->setBody(B); + + DiagnoseEmptyStmtBody(ForStmt->getRParenLoc(), B, + diag::warn_empty_range_based_for_body); + return S; } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 71037b6b3e3..4c69dd51f70 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -5006,6 +5006,8 @@ template<typename Derived> StmtResult TreeTransform<Derived>::TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr) { + Sema::CompoundScopeRAII CompoundScope(getSema()); + bool SubStmtInvalid = false; bool SubStmtChanged = false; ASTOwningVector<Stmt*> Statements(getSema()); |