update credits
[LibreOffice.git] / compilerplugins / clang / bodynotinblock.cxx
blob104a146eea5ec7ae048f29d346928b3c660130ef
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 namespace loplugin
17 This is a compile check.
19 Check for two statements that are both indented to look like a body of if/while/for
20 but are not inside a compound statement and thus the second one is unrelated.
22 For example:
24 if( a != 0 )
25 b = 2;
26 c = 3;
28 Here either both statements should be inside {} or the second statement in indented wrong.
31 BodyNotInBlock::BodyNotInBlock( CompilerInstance& compiler )
32 : Plugin( compiler )
36 void BodyNotInBlock::run()
38 TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
41 bool BodyNotInBlock::VisitFunctionDecl( const FunctionDecl* declaration )
43 if( ignoreLocation( declaration ))
44 return true;
45 if( !declaration->doesThisDeclarationHaveABody())
46 return true;
47 StmtParents parents;
48 traverseStatement( declaration->getBody(), parents );
49 return true;
52 void BodyNotInBlock::traverseStatement( const Stmt* stmt, StmtParents& parents )
54 parents.push_back( stmt );
55 for( ConstStmtIterator it = stmt->child_begin();
56 it != stmt->child_end();
57 ++it )
59 if( *it != NULL ) // some children can be apparently NULL
61 traverseStatement( *it, parents ); // substatements first
62 parents.push_back( *it );
63 if( const IfStmt* ifstmt = dyn_cast< IfStmt >( *it ))
65 checkBody( ifstmt->getThen(), ifstmt->getIfLoc(), parents, 0, ifstmt->getElse() != NULL );
66 checkBody( ifstmt->getElse(), ifstmt->getElseLoc(), parents, 0 );
68 else if( const WhileStmt* whilestmt = dyn_cast< WhileStmt >( *it ))
69 checkBody( whilestmt->getBody(), whilestmt->getWhileLoc(), parents, 1 );
70 else if( const ForStmt* forstmt = dyn_cast< ForStmt >( *it ))
71 checkBody( forstmt->getBody(), forstmt->getForLoc(), parents, 2 );
72 else if( const CXXForRangeStmt* forstmt = dyn_cast< CXXForRangeStmt >( *it ))
73 checkBody( forstmt->getBody(), forstmt->getForLoc(), parents, 2 );
74 parents.pop_back();
77 assert( parents.back() == stmt );
78 parents.pop_back();
81 void BodyNotInBlock::checkBody( const Stmt* body, SourceLocation stmtLocation, const StmtParents& parents,
82 int stmtType, bool dontGoUp )
84 if( body == NULL || parents.size() < 2 )
85 return;
86 // TODO: If the if/else/while/for comes from a macro expansion, ignore it completely for
87 // now. The code below could assume everything is in the same place (and thus also column)
88 // and give a false warning. Moreover some macros are rather lousily written and would
89 // result in poor formatting. To be evaluated later, maybe this could be handled
90 // including macro expansion.
91 if( stmtLocation.isMacroID())
92 return;
93 if( dyn_cast< CompoundStmt >( body ))
94 return; // if body is a compound statement, then it is in {}
95 // Find the next statement (in source position) after 'body'.
96 for( int parent_pos = parents.size() - 2; // start from grandparent
97 parent_pos >= 0;
98 --parent_pos )
100 for( ConstStmtIterator it = parents[ parent_pos ]->child_begin();
101 it != parents[ parent_pos ]->child_end();
104 if( *it == parents[ parent_pos + 1 ] ) // found grand(grand...)parent
106 // get next statement after our (grand...)parent
107 ++it;
108 while( it != parents[ parent_pos ]->child_end() && *it == NULL )
109 ++it; // skip empty ones (missing 'else' bodies for example)
110 if( it != parents[ parent_pos ]->child_end())
112 bool invalid1, invalid2;
113 unsigned bodyColumn = compiler.getSourceManager()
114 .getPresumedColumnNumber( body->getLocStart(), &invalid1 );
115 unsigned nextStatementColumn = compiler.getSourceManager()
116 .getPresumedColumnNumber( (*it)->getLocStart(), &invalid2 );
117 if( invalid1 || invalid2 )
118 return;
119 if( bodyColumn == nextStatementColumn )
121 report( DiagnosticsEngine::Warning,
122 "statement aligned as second statement in %select{if|while|for}0 body but not in a statement block",
123 (*it)->getLocStart()) << stmtType;
124 report( DiagnosticsEngine::Note,
125 "%select{if|while|for}0 body statement is here",
126 body->getLocStart()) << stmtType;
128 return;
130 // else we need to go higher to find the next statement
132 else
133 ++it;
135 // If going up would mean leaving a {} block, stop, because the } should
136 // make it visible the two statements are not in the same body.
137 if( dyn_cast< CompoundStmt >( parents[ parent_pos ] ))
138 return;
139 // If the body to be checked is a body of an if statement that has also
140 // an else part, don't go up, the else is after the body and should make
141 // it clear the body does not continue there.
142 if( dontGoUp )
143 return;
147 static Plugin::Registration< BodyNotInBlock > X( "bodynotinblock" );
149 } // namespace