@@ -102,6 +102,210 @@ void BranchCloneCheck::registerMatchers(MatchFinder *Finder) {
102102 this );
103103 Finder->addMatcher (switchStmt ().bind (" switch" ), this );
104104 Finder->addMatcher (conditionalOperator ().bind (" condOp" ), this );
105+ Finder->addMatcher (ifStmt (hasDescendant (ifStmt ())).bind (" ifWithDescendantIf" ),
106+ this );
107+ }
108+
109+ // / Determines whether two statement trees are identical regarding
110+ // / operators and symbols.
111+ // /
112+ // / Exceptions: expressions containing macros or functions with possible side
113+ // / effects are never considered identical.
114+ // / Limitations: (t + u) and (u + t) are not considered identical.
115+ // / t*(u + t) and t*u + t*t are not considered identical.
116+ // /
117+ static bool isIdenticalStmt (const ASTContext &Ctx, const Stmt *Stmt1,
118+ const Stmt *Stmt2, bool IgnoreSideEffects) {
119+
120+ if (!Stmt1 || !Stmt2)
121+ return !Stmt1 && !Stmt2;
122+
123+ // If Stmt1 & Stmt2 are of different class then they are not
124+ // identical statements.
125+ if (Stmt1->getStmtClass () != Stmt2->getStmtClass ())
126+ return false ;
127+
128+ const auto *Expr1 = dyn_cast<Expr>(Stmt1);
129+ const auto *Expr2 = dyn_cast<Expr>(Stmt2);
130+
131+ if (Expr1 && Expr2) {
132+ // If Stmt1 has side effects then don't warn even if expressions
133+ // are identical.
134+ if (!IgnoreSideEffects && Expr1->HasSideEffects (Ctx))
135+ return false ;
136+ // If either expression comes from a macro then don't warn even if
137+ // the expressions are identical.
138+ if ((Expr1->getExprLoc ().isMacroID ()) || (Expr2->getExprLoc ().isMacroID ()))
139+ return false ;
140+
141+ // If all children of two expressions are identical, return true.
142+ Expr::const_child_iterator I1 = Expr1->child_begin ();
143+ Expr::const_child_iterator I2 = Expr2->child_begin ();
144+ while (I1 != Expr1->child_end () && I2 != Expr2->child_end ()) {
145+ if (!*I1 || !*I2 || !isIdenticalStmt (Ctx, *I1, *I2, IgnoreSideEffects))
146+ return false ;
147+ ++I1;
148+ ++I2;
149+ }
150+ // If there are different number of children in the statements, return
151+ // false.
152+ if (I1 != Expr1->child_end ())
153+ return false ;
154+ if (I2 != Expr2->child_end ())
155+ return false ;
156+ }
157+
158+ switch (Stmt1->getStmtClass ()) {
159+ default :
160+ return false ;
161+ case Stmt::CallExprClass:
162+ case Stmt::ArraySubscriptExprClass:
163+ case Stmt::ArraySectionExprClass:
164+ case Stmt::OMPArrayShapingExprClass:
165+ case Stmt::OMPIteratorExprClass:
166+ case Stmt::ImplicitCastExprClass:
167+ case Stmt::ParenExprClass:
168+ case Stmt::BreakStmtClass:
169+ case Stmt::ContinueStmtClass:
170+ case Stmt::NullStmtClass:
171+ return true ;
172+ case Stmt::CStyleCastExprClass: {
173+ const auto *CastExpr1 = cast<CStyleCastExpr>(Stmt1);
174+ const auto *CastExpr2 = cast<CStyleCastExpr>(Stmt2);
175+
176+ return CastExpr1->getTypeAsWritten () == CastExpr2->getTypeAsWritten ();
177+ }
178+ case Stmt::ReturnStmtClass: {
179+ const auto *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
180+ const auto *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
181+
182+ return isIdenticalStmt (Ctx, ReturnStmt1->getRetValue (),
183+ ReturnStmt2->getRetValue (), IgnoreSideEffects);
184+ }
185+ case Stmt::ForStmtClass: {
186+ const auto *ForStmt1 = cast<ForStmt>(Stmt1);
187+ const auto *ForStmt2 = cast<ForStmt>(Stmt2);
188+
189+ if (!isIdenticalStmt (Ctx, ForStmt1->getInit (), ForStmt2->getInit (),
190+ IgnoreSideEffects))
191+ return false ;
192+ if (!isIdenticalStmt (Ctx, ForStmt1->getCond (), ForStmt2->getCond (),
193+ IgnoreSideEffects))
194+ return false ;
195+ if (!isIdenticalStmt (Ctx, ForStmt1->getInc (), ForStmt2->getInc (),
196+ IgnoreSideEffects))
197+ return false ;
198+ if (!isIdenticalStmt (Ctx, ForStmt1->getBody (), ForStmt2->getBody (),
199+ IgnoreSideEffects))
200+ return false ;
201+ return true ;
202+ }
203+ case Stmt::DoStmtClass: {
204+ const auto *DStmt1 = cast<DoStmt>(Stmt1);
205+ const auto *DStmt2 = cast<DoStmt>(Stmt2);
206+
207+ if (!isIdenticalStmt (Ctx, DStmt1->getCond (), DStmt2->getCond (),
208+ IgnoreSideEffects))
209+ return false ;
210+ if (!isIdenticalStmt (Ctx, DStmt1->getBody (), DStmt2->getBody (),
211+ IgnoreSideEffects))
212+ return false ;
213+ return true ;
214+ }
215+ case Stmt::WhileStmtClass: {
216+ const auto *WStmt1 = cast<WhileStmt>(Stmt1);
217+ const auto *WStmt2 = cast<WhileStmt>(Stmt2);
218+
219+ if (!isIdenticalStmt (Ctx, WStmt1->getCond (), WStmt2->getCond (),
220+ IgnoreSideEffects))
221+ return false ;
222+ if (!isIdenticalStmt (Ctx, WStmt1->getBody (), WStmt2->getBody (),
223+ IgnoreSideEffects))
224+ return false ;
225+ return true ;
226+ }
227+ case Stmt::IfStmtClass: {
228+ const auto *IStmt1 = cast<IfStmt>(Stmt1);
229+ const auto *IStmt2 = cast<IfStmt>(Stmt2);
230+
231+ if (!isIdenticalStmt (Ctx, IStmt1->getCond (), IStmt2->getCond (),
232+ IgnoreSideEffects))
233+ return false ;
234+ if (!isIdenticalStmt (Ctx, IStmt1->getThen (), IStmt2->getThen (),
235+ IgnoreSideEffects))
236+ return false ;
237+ if (!isIdenticalStmt (Ctx, IStmt1->getElse (), IStmt2->getElse (),
238+ IgnoreSideEffects))
239+ return false ;
240+ return true ;
241+ }
242+ case Stmt::CompoundStmtClass: {
243+ const auto *CompStmt1 = cast<CompoundStmt>(Stmt1);
244+ const auto *CompStmt2 = cast<CompoundStmt>(Stmt2);
245+
246+ if (CompStmt1->size () != CompStmt2->size ())
247+ return false ;
248+
249+ if (!llvm::all_of (llvm::zip (CompStmt1->body (), CompStmt2->body ()),
250+ [&Ctx, IgnoreSideEffects](
251+ std::tuple<const Stmt *, const Stmt *> stmtPair) {
252+ const Stmt *stmt0 = std::get<0 >(stmtPair);
253+ const Stmt *stmt1 = std::get<1 >(stmtPair);
254+ return isIdenticalStmt (Ctx, stmt0, stmt1,
255+ IgnoreSideEffects);
256+ })) {
257+ return false ;
258+ }
259+
260+ return true ;
261+ }
262+ case Stmt::CompoundAssignOperatorClass:
263+ case Stmt::BinaryOperatorClass: {
264+ const auto *BinOp1 = cast<BinaryOperator>(Stmt1);
265+ const auto *BinOp2 = cast<BinaryOperator>(Stmt2);
266+ return BinOp1->getOpcode () == BinOp2->getOpcode ();
267+ }
268+ case Stmt::CharacterLiteralClass: {
269+ const auto *CharLit1 = cast<CharacterLiteral>(Stmt1);
270+ const auto *CharLit2 = cast<CharacterLiteral>(Stmt2);
271+ return CharLit1->getValue () == CharLit2->getValue ();
272+ }
273+ case Stmt::DeclRefExprClass: {
274+ const auto *DeclRef1 = cast<DeclRefExpr>(Stmt1);
275+ const auto *DeclRef2 = cast<DeclRefExpr>(Stmt2);
276+ return DeclRef1->getDecl () == DeclRef2->getDecl ();
277+ }
278+ case Stmt::IntegerLiteralClass: {
279+ const auto *IntLit1 = cast<IntegerLiteral>(Stmt1);
280+ const auto *IntLit2 = cast<IntegerLiteral>(Stmt2);
281+
282+ llvm::APInt I1 = IntLit1->getValue ();
283+ llvm::APInt I2 = IntLit2->getValue ();
284+ if (I1.getBitWidth () != I2.getBitWidth ())
285+ return false ;
286+ return I1 == I2;
287+ }
288+ case Stmt::FloatingLiteralClass: {
289+ const auto *FloatLit1 = cast<FloatingLiteral>(Stmt1);
290+ const auto *FloatLit2 = cast<FloatingLiteral>(Stmt2);
291+ return FloatLit1->getValue ().bitwiseIsEqual (FloatLit2->getValue ());
292+ }
293+ case Stmt::StringLiteralClass: {
294+ const auto *StringLit1 = cast<StringLiteral>(Stmt1);
295+ const auto *StringLit2 = cast<StringLiteral>(Stmt2);
296+ return StringLit1->getBytes () == StringLit2->getBytes ();
297+ }
298+ case Stmt::MemberExprClass: {
299+ const auto *MemberStmt1 = cast<MemberExpr>(Stmt1);
300+ const auto *MemberStmt2 = cast<MemberExpr>(Stmt2);
301+ return MemberStmt1->getMemberDecl () == MemberStmt2->getMemberDecl ();
302+ }
303+ case Stmt::UnaryOperatorClass: {
304+ const auto *UnaryOp1 = cast<UnaryOperator>(Stmt1);
305+ const auto *UnaryOp2 = cast<UnaryOperator>(Stmt2);
306+ return UnaryOp1->getOpcode () == UnaryOp2->getOpcode ();
307+ }
308+ }
105309}
106310
107311void BranchCloneCheck::check (const MatchFinder::MatchResult &Result) {
@@ -269,6 +473,21 @@ void BranchCloneCheck::check(const MatchFinder::MatchResult &Result) {
269473 return ;
270474 }
271475
476+ if (const auto *IS = Result.Nodes .getNodeAs <IfStmt>(" ifWithDescendantIf" )) {
477+ const Stmt *Then = IS->getThen ();
478+ auto CS = dyn_cast<CompoundStmt>(Then);
479+ if (CS && (!CS->body_empty ())) {
480+ const auto *InnerIf = dyn_cast<IfStmt>(*CS->body_begin ());
481+ if (InnerIf && isIdenticalStmt (Context, IS->getCond (), InnerIf->getCond (),
482+ /* IgnoreSideEffects=*/ false )) {
483+ diag (IS->getBeginLoc (), " if with identical inner if statement" );
484+ diag (InnerIf->getBeginLoc (), " inner if starts here" ,
485+ DiagnosticIDs::Note);
486+ }
487+ }
488+ return ;
489+ }
490+
272491 llvm_unreachable (" No if statement and no switch statement." );
273492}
274493
0 commit comments