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