summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/store/bodynotinblock.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/store/bodynotinblock.cxx')
-rw-r--r--compilerplugins/clang/store/bodynotinblock.cxx147
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: */