blob: aeefc4260b11abc0c5881957563b4198753473ab [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
29static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
Jordan Rose60bd88d2013-12-10 18:18:06 +000030 const Expr *Expr2, 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 Rose60bd88d2013-12-10 18:18:06 +000044 // that are binary operators or conditional operators.
Jordan Rose51327f92013-11-08 01:15:39 +000045 bool VisitBinaryOperator(const BinaryOperator *B);
Jordan Rose60bd88d2013-12-10 18:18:06 +000046 bool VisitConditionalOperator(const ConditionalOperator *C);
Jordan Rose51327f92013-11-08 01:15:39 +000047
48private:
49 BugReporter &BR;
Alexander Kornienko4aca9b12014-02-11 21:49:21 +000050 const CheckerBase *Checker;
Jordan Rose51327f92013-11-08 01:15:39 +000051 AnalysisDeclContext *AC;
52};
Benjamin Kramere8a2c182013-11-14 15:46:10 +000053} // end anonymous namespace
Jordan Rose51327f92013-11-08 01:15:39 +000054
55bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
56 BinaryOperator::Opcode Op = B->getOpcode();
57 if (!BinaryOperator::isComparisonOp(Op))
58 return true;
59 //
60 // Special case for floating-point representation.
61 //
62 // If expressions on both sides of comparison operator are of type float,
63 // then for some comparison operators no warning shall be
64 // reported even if the expressions are identical from a symbolic point of
65 // view. Comparison between expressions, declared variables and literals
66 // are treated differently.
67 //
68 // != and == between float literals that have the same value should NOT warn.
69 // < > between float literals that have the same value SHOULD warn.
70 //
71 // != and == between the same float declaration should NOT warn.
72 // < > between the same float declaration SHOULD warn.
73 //
74 // != and == between eq. expressions that evaluates into float
75 // should NOT warn.
76 // < > between eq. expressions that evaluates into float
77 // should NOT warn.
78 //
79 const Expr *LHS = B->getLHS()->IgnoreParenImpCasts();
80 const Expr *RHS = B->getRHS()->IgnoreParenImpCasts();
81
82 const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
83 const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
84 const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
85 const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
86 if ((DeclRef1) && (DeclRef2)) {
87 if ((DeclRef1->getType()->hasFloatingRepresentation()) &&
88 (DeclRef2->getType()->hasFloatingRepresentation())) {
89 if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
90 if ((Op == BO_EQ) || (Op == BO_NE)) {
91 return true;
92 }
93 }
94 }
95 } else if ((FloatLit1) && (FloatLit2)) {
96 if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
97 if ((Op == BO_EQ) || (Op == BO_NE)) {
98 return true;
99 }
100 }
101 } else if (LHS->getType()->hasFloatingRepresentation()) {
102 // If any side of comparison operator still has floating-point
103 // representation, then it's an expression. Don't warn.
104 // Here only LHS is checked since RHS will be implicit casted to float.
105 return true;
106 } else {
107 // No special case with floating-point representation, report as usual.
108 }
109
110 if (isIdenticalExpr(AC->getASTContext(), B->getLHS(), B->getRHS())) {
111 PathDiagnosticLocation ELoc =
112 PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
113 StringRef Message;
114 if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
115 Message = "comparison of identical expressions always evaluates to true";
116 else
117 Message = "comparison of identical expressions always evaluates to false";
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000118 BR.EmitBasicReport(AC->getDecl(), Checker,
119 "Compare of identical expressions",
Jordan Rose51327f92013-11-08 01:15:39 +0000120 categories::LogicError, Message, ELoc);
121 }
122 // We want to visit ALL nodes (subexpressions of binary comparison
123 // expressions too) that contains comparison operators.
124 // True is always returned to traverse ALL nodes.
125 return true;
126}
Jordan Rose60bd88d2013-12-10 18:18:06 +0000127
128bool FindIdenticalExprVisitor::VisitConditionalOperator(
129 const ConditionalOperator *C) {
130
131 // Check if expressions in conditional expression are identical
132 // from a symbolic point of view.
133
134 if (isIdenticalExpr(AC->getASTContext(), C->getTrueExpr(),
135 C->getFalseExpr(), true)) {
136 PathDiagnosticLocation ELoc =
137 PathDiagnosticLocation::createConditionalColonLoc(
138 C, BR.getSourceManager());
139
140 SourceRange Sr[2];
141 Sr[0] = C->getTrueExpr()->getSourceRange();
142 Sr[1] = C->getFalseExpr()->getSourceRange();
143 BR.EmitBasicReport(
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000144 AC->getDecl(), Checker,
145 "Identical expressions in conditional expression",
Jordan Rose60bd88d2013-12-10 18:18:06 +0000146 categories::LogicError,
147 "identical expressions on both sides of ':' in conditional expression",
148 ELoc, Sr);
149 }
150 // We want to visit ALL nodes (expressions in conditional
151 // expressions too) that contains conditional operators,
152 // thus always return true to traverse ALL nodes.
153 return true;
154}
155
Jordan Rose51327f92013-11-08 01:15:39 +0000156/// \brief Determines whether two expression trees are identical regarding
157/// operators and symbols.
158///
159/// Exceptions: expressions containing macros or functions with possible side
160/// effects are never considered identical.
161/// Limitations: (t + u) and (u + t) are not considered identical.
162/// t*(u + t) and t*u + t*t are not considered identical.
163///
164static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
Jordan Rose60bd88d2013-12-10 18:18:06 +0000165 const Expr *Expr2, bool IgnoreSideEffects) {
Jordan Rose51327f92013-11-08 01:15:39 +0000166 // If Expr1 & Expr2 are of different class then they are not
167 // identical expression.
168 if (Expr1->getStmtClass() != Expr2->getStmtClass())
169 return false;
170 // If Expr1 has side effects then don't warn even if expressions
171 // are identical.
Jordan Rose60bd88d2013-12-10 18:18:06 +0000172 if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))
Jordan Rose51327f92013-11-08 01:15:39 +0000173 return false;
Jordan Rose6f2f3902013-12-10 18:18:10 +0000174 // If either expression comes from a macro then don't warn even if
Jordan Rose51327f92013-11-08 01:15:39 +0000175 // the expressions are identical.
176 if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
177 return false;
178 // If all children of two expressions are identical, return true.
179 Expr::const_child_iterator I1 = Expr1->child_begin();
180 Expr::const_child_iterator I2 = Expr2->child_begin();
181 while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
182 const Expr *Child1 = dyn_cast<Expr>(*I1);
183 const Expr *Child2 = dyn_cast<Expr>(*I2);
Jordan Rose60bd88d2013-12-10 18:18:06 +0000184 if (!Child1 || !Child2 || !isIdenticalExpr(Ctx, Child1, Child2,
185 IgnoreSideEffects))
Jordan Rose51327f92013-11-08 01:15:39 +0000186 return false;
187 ++I1;
188 ++I2;
189 }
190 // If there are different number of children in the expressions, return false.
191 // (TODO: check if this is a redundant condition.)
192 if (I1 != Expr1->child_end())
193 return false;
194 if (I2 != Expr2->child_end())
195 return false;
196
197 switch (Expr1->getStmtClass()) {
198 default:
199 return false;
Jordan Rose60bd88d2013-12-10 18:18:06 +0000200 case Stmt::CallExprClass:
Jordan Rose51327f92013-11-08 01:15:39 +0000201 case Stmt::ArraySubscriptExprClass:
202 case Stmt::CStyleCastExprClass:
203 case Stmt::ImplicitCastExprClass:
204 case Stmt::ParenExprClass:
205 return true;
206 case Stmt::BinaryOperatorClass: {
Jordan Rose6f2f3902013-12-10 18:18:10 +0000207 const BinaryOperator *BinOp1 = cast<BinaryOperator>(Expr1);
208 const BinaryOperator *BinOp2 = cast<BinaryOperator>(Expr2);
Jordan Rose51327f92013-11-08 01:15:39 +0000209 return BinOp1->getOpcode() == BinOp2->getOpcode();
210 }
211 case Stmt::CharacterLiteralClass: {
Jordan Rose6f2f3902013-12-10 18:18:10 +0000212 const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Expr1);
213 const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Expr2);
Jordan Rose51327f92013-11-08 01:15:39 +0000214 return CharLit1->getValue() == CharLit2->getValue();
215 }
216 case Stmt::DeclRefExprClass: {
Jordan Rose6f2f3902013-12-10 18:18:10 +0000217 const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Expr1);
218 const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Expr2);
Jordan Rose51327f92013-11-08 01:15:39 +0000219 return DeclRef1->getDecl() == DeclRef2->getDecl();
220 }
221 case Stmt::IntegerLiteralClass: {
Jordan Rose6f2f3902013-12-10 18:18:10 +0000222 const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Expr1);
223 const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Expr2);
Jordan Rose51327f92013-11-08 01:15:39 +0000224 return IntLit1->getValue() == IntLit2->getValue();
225 }
226 case Stmt::FloatingLiteralClass: {
Jordan Rose6f2f3902013-12-10 18:18:10 +0000227 const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Expr1);
228 const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Expr2);
Jordan Rose51327f92013-11-08 01:15:39 +0000229 return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
230 }
231 case Stmt::MemberExprClass: {
Jordan Rose6f2f3902013-12-10 18:18:10 +0000232 const MemberExpr *MemberExpr1 = cast<MemberExpr>(Expr1);
233 const MemberExpr *MemberExpr2 = cast<MemberExpr>(Expr2);
Jordan Rose51327f92013-11-08 01:15:39 +0000234 return MemberExpr1->getMemberDecl() == MemberExpr2->getMemberDecl();
235 }
236 case Stmt::UnaryOperatorClass: {
Jordan Rose6f2f3902013-12-10 18:18:10 +0000237 const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Expr1);
238 const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Expr2);
239 return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
Jordan Rose51327f92013-11-08 01:15:39 +0000240 }
241 }
242}
243
244//===----------------------------------------------------------------------===//
245// FindIdenticalExprChecker
246//===----------------------------------------------------------------------===//
247
Benjamin Kramere8a2c182013-11-14 15:46:10 +0000248namespace {
Jordan Rose51327f92013-11-08 01:15:39 +0000249class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {
250public:
251 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
252 BugReporter &BR) const {
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000253 FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
Jordan Rose51327f92013-11-08 01:15:39 +0000254 Visitor.TraverseDecl(const_cast<Decl *>(D));
255 }
256};
Benjamin Kramere8a2c182013-11-14 15:46:10 +0000257} // end anonymous namespace
Jordan Rose51327f92013-11-08 01:15:39 +0000258
259void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
260 Mgr.registerChecker<FindIdenticalExprChecker>();
261}