/* -*- 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: */