diff options
Diffstat (limited to 'compilerplugins/clang/store/bodynotinblock.cxx')
-rw-r--r-- | compilerplugins/clang/store/bodynotinblock.cxx | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/compilerplugins/clang/store/bodynotinblock.cxx b/compilerplugins/clang/store/bodynotinblock.cxx new file mode 100644 index 000000000..ca4f90499 --- /dev/null +++ b/compilerplugins/clang/store/bodynotinblock.cxx @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * Based on LLVM/Clang. + * + * This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + * + */ + +#include "bodynotinblock.hxx" + +namespace loplugin +{ + +/* +This is a compile check. + +Check for two statements that are both indented to look like a body of if/while/for +but are not inside a compound statement and thus the second one is unrelated. + +For example: + + if( a != 0 ) + b = 2; + c = 3; + +Here either both statements should be inside {} or the second statement in indented wrong. +*/ + +BodyNotInBlock::BodyNotInBlock( const InstantiationData& data ) + : Plugin( data ) + { + } + +void BodyNotInBlock::run() + { + TraverseDecl( compiler.getASTContext().getTranslationUnitDecl()); + } + +bool BodyNotInBlock::VisitIfStmt( const IfStmt* stmt ) + { + if( ignoreLocation( stmt )) + return true; + checkBody( stmt->getThen(), stmt->getIfLoc(), 0, stmt->getElse() != NULL ); + checkBody( stmt->getElse(), stmt->getElseLoc(), 0 ); + return true; + } + +bool BodyNotInBlock::VisitWhileStmt( const WhileStmt* stmt ) + { + if( ignoreLocation( stmt )) + return true; + checkBody( stmt->getBody(), stmt->getWhileLoc(), 1 ); + return true; + } + +bool BodyNotInBlock::VisitForStmt( const ForStmt* stmt ) + { + if( ignoreLocation( stmt )) + return true; + checkBody( stmt->getBody(), stmt->getForLoc(), 2 ); + return true; + } + +bool BodyNotInBlock::VisitCXXForRangeStmt( const CXXForRangeStmt* stmt ) + { + if( ignoreLocation( stmt )) + return true; + checkBody( stmt->getBody(), stmt->getForLoc(), 2 ); + return true; + } + +void BodyNotInBlock::checkBody( const Stmt* body, SourceLocation stmtLocation, int stmtType, bool dontGoUp ) + { + if( body == NULL ) + return; + // TODO: If the if/else/while/for comes from a macro expansion, ignore it completely for + // now. The code below could assume everything is in the same place (and thus also column) + // and give a false warning. Moreover some macros are rather loosely written and would + // result in poor formatting. To be evaluated later, maybe this could be handled + // including macro expansion. + if( stmtLocation.isMacroID()) + return; + if( dyn_cast< CompoundStmt >( body )) + return; // if body is a compound statement, then it is in {} + const Stmt* previousParent = parentStmt( body ); // Here the statement itself. + // Find the next statement (in source position) after 'body'. + for(;;) + { + const Stmt* parent = parentStmt( previousParent ); + if( parent == NULL ) + break; + for( ConstStmtIterator it = parent->child_begin(); + it != parent->child_end(); + ) + { + if( *it == previousParent ) // found grand(grand...)parent + { + // get next statement after our (grand...)parent + ++it; + while( it != parent->child_end() && *it == NULL ) + ++it; // skip empty ones (missing 'else' bodies for example) + if( it != parent->child_end()) + { + bool invalid1, invalid2; + unsigned bodyColumn = compiler.getSourceManager() + .getPresumedColumnNumber( body->getLocStart(), &invalid1 ); + unsigned nextStatementColumn = compiler.getSourceManager() + .getPresumedColumnNumber( (*it)->getLocStart(), &invalid2 ); + if( invalid1 || invalid2 ) + return; + if( bodyColumn == nextStatementColumn ) + { + report( DiagnosticsEngine::Warning, + "statement aligned as second statement in %select{if|while|for}0 body but not in a statement block", + (*it)->getLocStart()) << stmtType; + report( DiagnosticsEngine::Note, + "%select{if|while|for}0 body statement is here", + body->getLocStart()) << stmtType; + } + return; + } + // else we need to go higher to find the next statement + } + else + ++it; + } + // If going up would mean leaving a {} block, stop, because the } should + // make it visible the two statements are not in the same body. + if( dyn_cast< CompoundStmt >( parent )) + return; + // If the body to be checked is a body of an if statement that has also + // an else part, don't go up, the else is after the body and should make + // it clear the body does not continue there. + if( dontGoUp ) + return; + previousParent = parent; + } + } + +static Plugin::Registration< BodyNotInBlock > X( "bodynotinblock" ); + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |