2 * This file is part of the LibreOffice project.
6 * This file is distributed under the University of Illinois Open Source
7 * License. See LICENSE.TXT for details.
11 #include "bodynotinblock.hxx"
13 #include <clang/AST/ASTContext.h>
14 #include <clang/Basic/SourceManager.h>
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
)
31 void BodyNotInBlock::run()
33 TraverseDecl( context
.getTranslationUnitDecl());
36 bool BodyNotInBlock::VisitFunctionDecl( FunctionDecl
* declaration
)
38 if( ignoreLocation( declaration
))
40 if( !declaration
->doesThisDeclarationHaveABody())
43 traverseStatement( declaration
->getBody(), parents
);
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();
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 );
72 assert( parents
.back() == stmt
);
76 void BodyNotInBlock::checkBody( const Stmt
* body
, SourceLocation stmtLocation
, const StmtParents
& parents
,
77 int stmtType
, bool dontGoUp
)
79 if( body
== NULL
|| parents
.size() < 2 )
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())
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
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
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
)
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
;
125 // else we need to go higher to find the next statement
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
] ))
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.