Version 4.0.2.1, tag libreoffice-4.0.2.1
[LibreOffice.git] / compilerplugins / clang / bodynotinblock.cxx
bloba2e79680d8e2d5b6924acdf492765534bc2c0286
1 /*
2 * This file is part of the LibreOffice project.
4 * Based on LLVM/Clang.
6 * This file is distributed under the University of Illinois Open Source
7 * License. See LICENSE.TXT for details.
9 */
11 #include "bodynotinblock.hxx"
13 #include <clang/AST/ASTContext.h>
14 #include <clang/Basic/SourceManager.h>
16 namespace loplugin
20 This is a compile check.
22 Check for two statements that are both indented to look like a body of if/while/for
23 but are not inside a compound statement and thus the second one is unrelated.
26 BodyNotInBlock::BodyNotInBlock( ASTContext& context )
27 : Plugin( context )
31 void BodyNotInBlock::run()
33 TraverseDecl( context.getTranslationUnitDecl());
36 bool BodyNotInBlock::VisitFunctionDecl( FunctionDecl* declaration )
38 if( ignoreLocation( declaration ))
39 return true;
40 if( !declaration->doesThisDeclarationHaveABody())
41 return true;
42 StmtParents parents;
43 traverseStatement( declaration->getBody(), parents );
44 return true;
47 void BodyNotInBlock::traverseStatement( const Stmt* stmt, StmtParents& parents )
49 parents.push_back( stmt );
50 for( ConstStmtIterator it = stmt->child_begin();
51 it != stmt->child_end();
52 ++it )
54 if( *it != NULL ) // some children can be apparently NULL
56 traverseStatement( *it, parents ); // substatements first
57 parents.push_back( *it );
58 if( const IfStmt* ifstmt = dyn_cast< IfStmt >( *it ))
60 checkBody( ifstmt->getThen(), ifstmt->getIfLoc(), parents, 0, ifstmt->getElse() != NULL );
61 checkBody( ifstmt->getElse(), ifstmt->getElseLoc(), parents, 0 );
63 else if( const WhileStmt* whilestmt = dyn_cast< WhileStmt >( *it ))
64 checkBody( whilestmt->getBody(), whilestmt->getWhileLoc(), parents, 1 );
65 else if( const ForStmt* forstmt = dyn_cast< ForStmt >( *it ))
66 checkBody( forstmt->getBody(), forstmt->getForLoc(), parents, 2 );
67 else if( const CXXForRangeStmt* forstmt = dyn_cast< CXXForRangeStmt >( *it ))
68 checkBody( forstmt->getBody(), forstmt->getForLoc(), parents, 2 );
69 parents.pop_back();
72 assert( parents.back() == stmt );
73 parents.pop_back();
76 void BodyNotInBlock::checkBody( const Stmt* body, SourceLocation stmtLocation, const StmtParents& parents,
77 int stmtType, bool dontGoUp )
79 if( body == NULL || parents.size() < 2 )
80 return;
81 // TODO: If the if/else/while/for comes from a macro expansion, ignore it completely for
82 // now. The code below could assume everything is in the same place (and thus also column)
83 // and give a false warning. Moreover some macros are rather lousily written and would
84 // result in poor formatting. To be evaluated later, maybe this could be handled
85 // including macro expansion.
86 if( stmtLocation.isMacroID())
87 return;
88 if( dyn_cast< CompoundStmt >( body ))
89 return; // if body is a compound statement, then it is in {}
90 // Find the next statement (in source position) after 'body'.
91 for( int parent_pos = parents.size() - 2; // start from grandparent
92 parent_pos >= 0;
93 --parent_pos )
95 for( ConstStmtIterator it = parents[ parent_pos ]->child_begin();
96 it != parents[ parent_pos ]->child_end();
99 if( *it == parents[ parent_pos + 1 ] ) // found grand(grand...)parent
101 // get next statement after our (grand...)parent
102 ++it;
103 while( it != parents[ parent_pos ]->child_end() && *it == NULL )
104 ++it; // skip empty ones (missing 'else' bodies for example)
105 if( it != parents[ parent_pos ]->child_end())
107 bool invalid1, invalid2;
108 unsigned bodyColumn = context.getSourceManager()
109 .getPresumedColumnNumber( body->getLocStart(), &invalid1 );
110 unsigned nextStatementColumn = context.getSourceManager()
111 .getPresumedColumnNumber( (*it)->getLocStart(), &invalid2 );
112 if( invalid1 || invalid2 )
113 return;
114 if( bodyColumn == nextStatementColumn )
116 report( DiagnosticsEngine::Warning,
117 "statement aligned as second statement in %select{if|while|for}0 body but not in a statement block [loplugin]",
118 (*it)->getLocStart()) << stmtType;
119 report( DiagnosticsEngine::Note,
120 "%select{if|while|for}0 body statement is here [loplugin]",
121 body->getLocStart()) << stmtType;
123 return;
125 // else we need to go higher to find the next statement
127 else
128 ++it;
130 // If going up would mean leaving a {} block, stop, because the } should
131 // make it visible the two statements are not in the same body.
132 if( dyn_cast< CompoundStmt >( parents[ parent_pos ] ))
133 return;
134 // If the body to be checked is a body of an if statement that has also
135 // an else part, don't go up, the else is after the body and should make
136 // it clear the body does not continue there.
137 if( dontGoUp )
138 return;
142 } // namespace