Skip to content

Commit 8f80764

Browse files
author
Vince Bridgers
committed
[analyzer] Port alpha.core.IdenticalExpr to Tidy checks and remove
This change removes the alpha.core.IdenticalExpr static analysis checker since it's checks are present in the clang-tidy checks misc-redundant-expression and bugprone-branch-clone. This check was implemented as a static analysis check using AST matching, and since alpha and duplicated in 2 clang-tidy checks may be removed. The existing LIT test was checked case by case, and the tidy checks were improved to maintain alpha.core.IdenticalExpr features.
1 parent 0daca80 commit 8f80764

File tree

8 files changed

+533
-659
lines changed

8 files changed

+533
-659
lines changed

clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 Expr *Expr1 = dyn_cast<Expr>(Stmt1);
130+
const Expr *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 CStyleCastExpr *CastExpr1 = cast<CStyleCastExpr>(Stmt1);
175+
const CStyleCastExpr *CastExpr2 = cast<CStyleCastExpr>(Stmt2);
176+
177+
return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
178+
}
179+
case Stmt::ReturnStmtClass: {
180+
const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
181+
const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
182+
183+
return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
184+
ReturnStmt2->getRetValue(), IgnoreSideEffects);
185+
}
186+
case Stmt::ForStmtClass: {
187+
const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);
188+
const ForStmt *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 DoStmt *DStmt1 = cast<DoStmt>(Stmt1);
206+
const DoStmt *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 WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);
218+
const WhileStmt *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 IfStmt *IStmt1 = cast<IfStmt>(Stmt1);
230+
const IfStmt *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 CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);
245+
const CompoundStmt *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 BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);
264+
const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2);
265+
return BinOp1->getOpcode() == BinOp2->getOpcode();
266+
}
267+
case Stmt::CharacterLiteralClass: {
268+
const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);
269+
const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2);
270+
return CharLit1->getValue() == CharLit2->getValue();
271+
}
272+
case Stmt::DeclRefExprClass: {
273+
const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);
274+
const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2);
275+
return DeclRef1->getDecl() == DeclRef2->getDecl();
276+
}
277+
case Stmt::IntegerLiteralClass: {
278+
const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);
279+
const IntegerLiteral *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 FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1);
289+
const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2);
290+
return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
291+
}
292+
case Stmt::StringLiteralClass: {
293+
const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1);
294+
const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2);
295+
return StringLit1->getBytes() == StringLit2->getBytes();
296+
}
297+
case Stmt::MemberExprClass: {
298+
const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);
299+
const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2);
300+
return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
301+
}
302+
case Stmt::UnaryOperatorClass: {
303+
const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);
304+
const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2);
305+
return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
306+
}
307+
}
105308
}
106309

107310
void 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 IfStmt *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

clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -865,21 +865,20 @@ void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) {
865865

866866
// Binary with equivalent operands, like (X != 2 && X != 2).
867867
Finder->addMatcher(
868-
traverse(TK_AsIs,
869-
binaryOperator(
870-
anyOf(isComparisonOperator(),
871-
hasAnyOperatorName("-", "/", "%", "|", "&", "^", "&&",
872-
"||", "=")),
873-
operandsAreEquivalent(),
874-
// Filter noisy false positives.
875-
unless(isInTemplateInstantiation()),
876-
unless(binaryOperatorIsInMacro()),
877-
unless(hasType(realFloatingPointType())),
878-
unless(hasEitherOperand(hasType(realFloatingPointType()))),
879-
unless(hasLHS(AnyLiteralExpr)),
880-
unless(hasDescendant(BannedIntegerLiteral)),
881-
unless(IsInUnevaluatedContext))
882-
.bind("binary")),
868+
traverse(
869+
TK_AsIs,
870+
binaryOperator(
871+
anyOf(isComparisonOperator(),
872+
hasAnyOperatorName("-", "/", "%", "|", "&", "^", "&&", "||",
873+
"=")),
874+
operandsAreEquivalent(),
875+
// Filter noisy false positives.
876+
unless(isInTemplateInstantiation()),
877+
unless(binaryOperatorIsInMacro()),
878+
unless(hasAncestor(arraySubscriptExpr())),
879+
unless(hasDescendant(BannedIntegerLiteral)),
880+
unless(IsInUnevaluatedContext))
881+
.bind("binary")),
883882
this);
884883

885884
// Logical or bitwise operator with equivalent nested operands, like (X && Y
@@ -1238,6 +1237,59 @@ void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) {
12381237
if (const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binary")) {
12391238
// If the expression's constants are macros, check whether they are
12401239
// intentional.
1240+
1241+
//
1242+
// Special case for floating-point representation.
1243+
//
1244+
// If expressions on both sides of comparison operator are of type float,
1245+
// then for some comparison operators no warning shall be
1246+
// reported even if the expressions are identical from a symbolic point of
1247+
// view. Comparison between expressions, declared variables and literals
1248+
// are treated differently.
1249+
//
1250+
// != and == between float literals that have the same value should NOT
1251+
// warn. < > between float literals that have the same value SHOULD warn.
1252+
//
1253+
// != and == between the same float declaration should NOT warn.
1254+
// < > between the same float declaration SHOULD warn.
1255+
//
1256+
// != and == between eq. expressions that evaluates into float
1257+
// should NOT warn.
1258+
// < > between eq. expressions that evaluates into float
1259+
// should NOT warn.
1260+
//
1261+
const Expr *LHS = BinOp->getLHS()->IgnoreParenImpCasts();
1262+
const Expr *RHS = BinOp->getRHS()->IgnoreParenImpCasts();
1263+
BinaryOperator::Opcode Op = BinOp->getOpcode();
1264+
1265+
const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
1266+
const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
1267+
const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
1268+
const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
1269+
if ((DeclRef1) && (DeclRef2)) {
1270+
if ((DeclRef1->getType()->hasFloatingRepresentation()) &&
1271+
(DeclRef2->getType()->hasFloatingRepresentation())) {
1272+
if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
1273+
if ((Op == BO_EQ) || (Op == BO_NE)) {
1274+
return;
1275+
}
1276+
}
1277+
}
1278+
} else if ((FloatLit1) && (FloatLit2)) {
1279+
if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
1280+
if ((Op == BO_EQ) || (Op == BO_NE)) {
1281+
return;
1282+
}
1283+
}
1284+
} else if (LHS->getType()->hasFloatingRepresentation()) {
1285+
// If any side of comparison operator still has floating-point
1286+
// representation, then it's an expression. Don't warn.
1287+
// Here only LHS is checked since RHS will be implicit casted to float.
1288+
return;
1289+
} else {
1290+
// No special case with floating-point representation, report as usual.
1291+
}
1292+
12411293
if (areSidesBinaryConstExpressions(BinOp, Result.Context)) {
12421294
const Expr *LhsConst = nullptr, *RhsConst = nullptr;
12431295
BinaryOperatorKind MainOpcode{}, SideOpcode{};

0 commit comments

Comments
 (0)