blob: 6c6f472f3f70ae4d92943486c4c9c20156af18aa [file] [log] [blame]
Jordan Rose51327f92013-11-08 01:15:39 +00001//== IdenticalExprChecker.cpp - Identical expression checker----------------==//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// \brief This defines IdenticalExprChecker, a check that warns about
12/// unintended use of identical expressions.
13///
Jordan Rose60bd88d2013-12-10 18:18:06 +000014/// It checks for use of identical expressions with comparison operators and
15/// inside conditional expressions.
Jordan Rose51327f92013-11-08 01:15:39 +000016///
17//===----------------------------------------------------------------------===//
18
19#include "ClangSACheckers.h"
Chandler Carruth5553d0d2014-01-07 11:51:46 +000020#include "clang/AST/RecursiveASTVisitor.h"
Jordan Rose51327f92013-11-08 01:15:39 +000021#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22#include "clang/StaticAnalyzer/Core/Checker.h"
23#include "clang/StaticAnalyzer/Core/CheckerManager.h"
24#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Jordan Rose51327f92013-11-08 01:15:39 +000025
26using namespace clang;
27using namespace ento;
28
Jordan Rose70e7e872014-02-19 17:44:11 +000029static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
30 const Stmt *Stmt2, bool IgnoreSideEffects = false);
Jordan Rose51327f92013-11-08 01:15:39 +000031//===----------------------------------------------------------------------===//
32// FindIdenticalExprVisitor - Identify nodes using identical expressions.
33//===----------------------------------------------------------------------===//
34
Benjamin Kramere8a2c182013-11-14 15:46:10 +000035namespace {
Jordan Rose51327f92013-11-08 01:15:39 +000036class FindIdenticalExprVisitor
37 : public RecursiveASTVisitor<FindIdenticalExprVisitor> {
38public:
Alexander Kornienko4aca9b12014-02-11 21:49:21 +000039 explicit FindIdenticalExprVisitor(BugReporter &B,
40 const CheckerBase *Checker,
41 AnalysisDeclContext *A)
42 : BR(B), Checker(Checker), AC(A) {}
Jordan Rose51327f92013-11-08 01:15:39 +000043 // FindIdenticalExprVisitor only visits nodes
Jordan Rose70e7e872014-02-19 17:44:11 +000044 // that are binary operators, if statements or
45 // conditional operators.
Jordan Rose51327f92013-11-08 01:15:39 +000046 bool VisitBinaryOperator(const BinaryOperator *B);
Jordan Rose70e7e872014-02-19 17:44:11 +000047 bool VisitIfStmt(const IfStmt *I);
Jordan Rose60bd88d2013-12-10 18:18:06 +000048 bool VisitConditionalOperator(const ConditionalOperator *C);
Jordan Rose51327f92013-11-08 01:15:39 +000049
50private:
51 BugReporter &BR;
Alexander Kornienko4aca9b12014-02-11 21:49:21 +000052 const CheckerBase *Checker;
Jordan Rose51327f92013-11-08 01:15:39 +000053 AnalysisDeclContext *AC;
54};
Benjamin Kramere8a2c182013-11-14 15:46:10 +000055} // end anonymous namespace
Jordan Rose51327f92013-11-08 01:15:39 +000056
Jordan Rose70e7e872014-02-19 17:44:11 +000057bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) {
58 const Stmt *Stmt1 = I->getThen();
59 const Stmt *Stmt2 = I->getElse();
60
61 if (!Stmt1 || !Stmt2)
62 return true;
63
64 // Special handling for code like:
65 //
66 // if (b) {
67 // i = 1;
68 // } else
69 // i = 1;
70 if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) {
71 if (CompStmt->size() == 1)
72 Stmt1 = CompStmt->body_back();
73 }
74 if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) {
75 if (CompStmt->size() == 1)
76 Stmt2 = CompStmt->body_back();
77 }
78
79 if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) {
80 PathDiagnosticLocation ELoc =
81 PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC);
82 BR.EmitBasicReport(AC->getDecl(), Checker,
83 "Identical branches",
84 categories::LogicError,
85 "true and false branches are identical", ELoc);
86 }
87 return true;
88}
89
Jordan Rose51327f92013-11-08 01:15:39 +000090bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
91 BinaryOperator::Opcode Op = B->getOpcode();
92 if (!BinaryOperator::isComparisonOp(Op))
93 return true;
94 //
95 // Special case for floating-point representation.
96 //
97 // If expressions on both sides of comparison operator are of type float,
98 // then for some comparison operators no warning shall be
99 // reported even if the expressions are identical from a symbolic point of
100 // view. Comparison between expressions, declared variables and literals
101 // are treated differently.
102 //
103 // != and == between float literals that have the same value should NOT warn.
104 // < > between float literals that have the same value SHOULD warn.
105 //
106 // != and == between the same float declaration should NOT warn.
107 // < > between the same float declaration SHOULD warn.
108 //
109 // != and == between eq. expressions that evaluates into float
110 // should NOT warn.
111 // < > between eq. expressions that evaluates into float
112 // should NOT warn.
113 //
114 const Expr *LHS = B->getLHS()->IgnoreParenImpCasts();
115 const Expr *RHS = B->getRHS()->IgnoreParenImpCasts();
116
117 const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
118 const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
119 const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
120 const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
121 if ((DeclRef1) && (DeclRef2)) {
122 if ((DeclRef1->getType()->hasFloatingRepresentation()) &&
123 (DeclRef2->getType()->hasFloatingRepresentation())) {
124 if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
125 if ((Op == BO_EQ) || (Op == BO_NE)) {
126 return true;
127 }
128 }
129 }
130 } else if ((FloatLit1) && (FloatLit2)) {
131 if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
132 if ((Op == BO_EQ) || (Op == BO_NE)) {
133 return true;
134 }
135 }
136 } else if (LHS->getType()->hasFloatingRepresentation()) {
137 // If any side of comparison operator still has floating-point
138 // representation, then it's an expression. Don't warn.
139 // Here only LHS is checked since RHS will be implicit casted to float.
140 return true;
141 } else {
142 // No special case with floating-point representation, report as usual.
143 }
144
Jordan Rose70e7e872014-02-19 17:44:11 +0000145 if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) {
Jordan Rose51327f92013-11-08 01:15:39 +0000146 PathDiagnosticLocation ELoc =
147 PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
148 StringRef Message;
149 if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
150 Message = "comparison of identical expressions always evaluates to true";
151 else
152 Message = "comparison of identical expressions always evaluates to false";
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000153 BR.EmitBasicReport(AC->getDecl(), Checker,
154 "Compare of identical expressions",
Jordan Rose51327f92013-11-08 01:15:39 +0000155 categories::LogicError, Message, ELoc);
156 }
157 // We want to visit ALL nodes (subexpressions of binary comparison
158 // expressions too) that contains comparison operators.
159 // True is always returned to traverse ALL nodes.
160 return true;
161}
Jordan Rose60bd88d2013-12-10 18:18:06 +0000162
163bool FindIdenticalExprVisitor::VisitConditionalOperator(
164 const ConditionalOperator *C) {
165
166 // Check if expressions in conditional expression are identical
167 // from a symbolic point of view.
168
Jordan Rose70e7e872014-02-19 17:44:11 +0000169 if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(),
Jordan Rose60bd88d2013-12-10 18:18:06 +0000170 C->getFalseExpr(), true)) {
171 PathDiagnosticLocation ELoc =
172 PathDiagnosticLocation::createConditionalColonLoc(
173 C, BR.getSourceManager());
174
175 SourceRange Sr[2];
176 Sr[0] = C->getTrueExpr()->getSourceRange();
177 Sr[1] = C->getFalseExpr()->getSourceRange();
178 BR.EmitBasicReport(
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000179 AC->getDecl(), Checker,
180 "Identical expressions in conditional expression",
Jordan Rose60bd88d2013-12-10 18:18:06 +0000181 categories::LogicError,
182 "identical expressions on both sides of ':' in conditional expression",
183 ELoc, Sr);
184 }
185 // We want to visit ALL nodes (expressions in conditional
186 // expressions too) that contains conditional operators,
187 // thus always return true to traverse ALL nodes.
188 return true;
189}
190
Jordan Rose70e7e872014-02-19 17:44:11 +0000191/// \brief Determines whether two statement trees are identical regarding
Jordan Rose51327f92013-11-08 01:15:39 +0000192/// operators and symbols.
193///
194/// Exceptions: expressions containing macros or functions with possible side
195/// effects are never considered identical.
196/// Limitations: (t + u) and (u + t) are not considered identical.
197/// t*(u + t) and t*u + t*t are not considered identical.
198///
Jordan Rose70e7e872014-02-19 17:44:11 +0000199static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
200 const Stmt *Stmt2, bool IgnoreSideEffects) {
201
202 if (!Stmt1 || !Stmt2) {
203 if (!Stmt1 && !Stmt2)
204 return true;
Jordan Rose51327f92013-11-08 01:15:39 +0000205 return false;
Jordan Rose51327f92013-11-08 01:15:39 +0000206 }
Jordan Rose70e7e872014-02-19 17:44:11 +0000207
208 // If Stmt1 & Stmt2 are of different class then they are not
209 // identical statements.
210 if (Stmt1->getStmtClass() != Stmt2->getStmtClass())
Jordan Rose51327f92013-11-08 01:15:39 +0000211 return false;
212
Jordan Rose70e7e872014-02-19 17:44:11 +0000213 const Expr *Expr1 = dyn_cast<Expr>(Stmt1);
214 const Expr *Expr2 = dyn_cast<Expr>(Stmt2);
215
216 if (Expr1 && Expr2) {
217 // If Stmt1 has side effects then don't warn even if expressions
218 // are identical.
219 if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))
220 return false;
221 // If either expression comes from a macro then don't warn even if
222 // the expressions are identical.
223 if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
224 return false;
225
226 // If all children of two expressions are identical, return true.
227 Expr::const_child_iterator I1 = Expr1->child_begin();
228 Expr::const_child_iterator I2 = Expr2->child_begin();
229 while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
230 if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
231 return false;
232 ++I1;
233 ++I2;
234 }
235 // If there are different number of children in the statements, return
236 // false.
237 if (I1 != Expr1->child_end())
238 return false;
239 if (I2 != Expr2->child_end())
240 return false;
241 }
242
243 switch (Stmt1->getStmtClass()) {
Jordan Rose51327f92013-11-08 01:15:39 +0000244 default:
245 return false;
Jordan Rose60bd88d2013-12-10 18:18:06 +0000246 case Stmt::CallExprClass:
Jordan Rose51327f92013-11-08 01:15:39 +0000247 case Stmt::ArraySubscriptExprClass:
Jordan Rose51327f92013-11-08 01:15:39 +0000248 case Stmt::ImplicitCastExprClass:
249 case Stmt::ParenExprClass:
Jordan Rose70e7e872014-02-19 17:44:11 +0000250 case Stmt::BreakStmtClass:
251 case Stmt::ContinueStmtClass:
252 case Stmt::NullStmtClass:
Jordan Rose51327f92013-11-08 01:15:39 +0000253 return true;
Jordan Rose70e7e872014-02-19 17:44:11 +0000254 case Stmt::CStyleCastExprClass: {
255 const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1);
256 const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2);
257
258 return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
259 }
260 case Stmt::ReturnStmtClass: {
261 const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
262 const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
263
264 return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
265 ReturnStmt2->getRetValue(), IgnoreSideEffects);
266 }
267 case Stmt::ForStmtClass: {
268 const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);
269 const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2);
270
271 if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),
272 IgnoreSideEffects))
273 return false;
274 if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),
275 IgnoreSideEffects))
276 return false;
277 if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),
278 IgnoreSideEffects))
279 return false;
280 if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),
281 IgnoreSideEffects))
282 return false;
283 return true;
284 }
285 case Stmt::DoStmtClass: {
286 const DoStmt *DStmt1 = cast<DoStmt>(Stmt1);
287 const DoStmt *DStmt2 = cast<DoStmt>(Stmt2);
288
289 if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),
290 IgnoreSideEffects))
291 return false;
292 if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),
293 IgnoreSideEffects))
294 return false;
295 return true;
296 }
297 case Stmt::WhileStmtClass: {
298 const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);
299 const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2);
300
301 return isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),
302 IgnoreSideEffects);
303 }
304 case Stmt::IfStmtClass: {
305 const IfStmt *IStmt1 = cast<IfStmt>(Stmt1);
306 const IfStmt *IStmt2 = cast<IfStmt>(Stmt2);
307
308 if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),
309 IgnoreSideEffects))
310 return false;
311 if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),
312 IgnoreSideEffects))
313 return false;
314 if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),
315 IgnoreSideEffects))
316 return false;
317 return true;
318 }
319 case Stmt::CompoundStmtClass: {
320 const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);
321 const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2);
322
323 if (CompStmt1->size() != CompStmt2->size())
324 return false;
325
326 CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin();
327 CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin();
328 while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) {
329 if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
330 return false;
331 ++I1;
332 ++I2;
333 }
334
335 return true;
336 }
337 case Stmt::CompoundAssignOperatorClass:
Jordan Rose51327f92013-11-08 01:15:39 +0000338 case Stmt::BinaryOperatorClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000339 const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);
340 const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000341 return BinOp1->getOpcode() == BinOp2->getOpcode();
342 }
343 case Stmt::CharacterLiteralClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000344 const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);
345 const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000346 return CharLit1->getValue() == CharLit2->getValue();
347 }
348 case Stmt::DeclRefExprClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000349 const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);
350 const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000351 return DeclRef1->getDecl() == DeclRef2->getDecl();
352 }
353 case Stmt::IntegerLiteralClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000354 const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);
355 const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000356 return IntLit1->getValue() == IntLit2->getValue();
357 }
358 case Stmt::FloatingLiteralClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000359 const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1);
360 const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000361 return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
362 }
Jordan Rose70e7e872014-02-19 17:44:11 +0000363 case Stmt::StringLiteralClass: {
364 const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1);
365 const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2);
366 return StringLit1->getString() == StringLit2->getString();
367 }
Jordan Rose51327f92013-11-08 01:15:39 +0000368 case Stmt::MemberExprClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000369 const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);
370 const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2);
371 return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
Jordan Rose51327f92013-11-08 01:15:39 +0000372 }
373 case Stmt::UnaryOperatorClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000374 const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);
375 const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2);
Jordan Rose6f2f3902013-12-10 18:18:10 +0000376 return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
Jordan Rose51327f92013-11-08 01:15:39 +0000377 }
378 }
379}
380
381//===----------------------------------------------------------------------===//
382// FindIdenticalExprChecker
383//===----------------------------------------------------------------------===//
384
Benjamin Kramere8a2c182013-11-14 15:46:10 +0000385namespace {
Jordan Rose51327f92013-11-08 01:15:39 +0000386class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {
387public:
388 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
389 BugReporter &BR) const {
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000390 FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
Jordan Rose51327f92013-11-08 01:15:39 +0000391 Visitor.TraverseDecl(const_cast<Decl *>(D));
392 }
393};
Benjamin Kramere8a2c182013-11-14 15:46:10 +0000394} // end anonymous namespace
Jordan Rose51327f92013-11-08 01:15:39 +0000395
396void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
397 Mgr.registerChecker<FindIdenticalExprChecker>();
398}