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