@@ -102,6 +102,209 @@ 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+
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+ CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin ();
251+ CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin ();
252+ while (I1 != CompStmt1->body_end () && I2 != CompStmt2->body_end ()) {
253+ if (!isIdenticalStmt (Ctx, *I1, *I2, IgnoreSideEffects))
254+ return false ;
255+ ++I1;
256+ ++I2;
257+ }
258+
259+ return true ;
260+ }
261+ case Stmt::CompoundAssignOperatorClass:
262+ case Stmt::BinaryOperatorClass: {
263+ const auto *BinOp1 = cast<BinaryOperator>(Stmt1);
264+ const auto *BinOp2 = cast<BinaryOperator>(Stmt2);
265+ return BinOp1->getOpcode () == BinOp2->getOpcode ();
266+ }
267+ case Stmt::CharacterLiteralClass: {
268+ const auto *CharLit1 = cast<CharacterLiteral>(Stmt1);
269+ const auto *CharLit2 = cast<CharacterLiteral>(Stmt2);
270+ return CharLit1->getValue () == CharLit2->getValue ();
271+ }
272+ case Stmt::DeclRefExprClass: {
273+ const auto *DeclRef1 = cast<DeclRefExpr>(Stmt1);
274+ const auto *DeclRef2 = cast<DeclRefExpr>(Stmt2);
275+ return DeclRef1->getDecl () == DeclRef2->getDecl ();
276+ }
277+ case Stmt::IntegerLiteralClass: {
278+ const auto *IntLit1 = cast<IntegerLiteral>(Stmt1);
279+ const auto *IntLit2 = cast<IntegerLiteral>(Stmt2);
280+
281+ llvm::APInt I1 = IntLit1->getValue ();
282+ llvm::APInt I2 = IntLit2->getValue ();
283+ if (I1.getBitWidth () != I2.getBitWidth ())
284+ return false ;
285+ return I1 == I2;
286+ }
287+ case Stmt::FloatingLiteralClass: {
288+ const auto *FloatLit1 = cast<FloatingLiteral>(Stmt1);
289+ const auto *FloatLit2 = cast<FloatingLiteral>(Stmt2);
290+ return FloatLit1->getValue ().bitwiseIsEqual (FloatLit2->getValue ());
291+ }
292+ case Stmt::StringLiteralClass: {
293+ const auto *StringLit1 = cast<StringLiteral>(Stmt1);
294+ const auto *StringLit2 = cast<StringLiteral>(Stmt2);
295+ return StringLit1->getBytes () == StringLit2->getBytes ();
296+ }
297+ case Stmt::MemberExprClass: {
298+ const auto *MemberStmt1 = cast<MemberExpr>(Stmt1);
299+ const auto *MemberStmt2 = cast<MemberExpr>(Stmt2);
300+ return MemberStmt1->getMemberDecl () == MemberStmt2->getMemberDecl ();
301+ }
302+ case Stmt::UnaryOperatorClass: {
303+ const auto *UnaryOp1 = cast<UnaryOperator>(Stmt1);
304+ const auto *UnaryOp2 = cast<UnaryOperator>(Stmt2);
305+ return UnaryOp1->getOpcode () == UnaryOp2->getOpcode ();
306+ }
307+ }
105308}
106309
107310void BranchCloneCheck::check (const MatchFinder::MatchResult &Result) {
@@ -269,6 +472,23 @@ void BranchCloneCheck::check(const MatchFinder::MatchResult &Result) {
269472 return ;
270473 }
271474
475+ if (const auto *IS = Result.Nodes .getNodeAs <IfStmt>(" ifWithDescendantIf" )) {
476+ const Stmt *Then = IS->getThen ();
477+ if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Then)) {
478+ if (!CS->body_empty ()) {
479+ const auto *InnerIf = dyn_cast<IfStmt>(*CS->body_begin ());
480+ if (InnerIf &&
481+ 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+ }
489+ return ;
490+ }
491+
272492 llvm_unreachable (" No if statement and no switch statement." );
273493}
274494
0 commit comments