blob: 6f5f7bdbc905702131d22bf16a26c5b0751f60c3 [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///
14/// It checks for use of identical expressions with comparison operators.
15///
16//===----------------------------------------------------------------------===//
17
18#include "ClangSACheckers.h"
19#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20#include "clang/StaticAnalyzer/Core/Checker.h"
21#include "clang/StaticAnalyzer/Core/CheckerManager.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23#include "clang/AST/RecursiveASTVisitor.h"
24
25using namespace clang;
26using namespace ento;
27
28static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
29 const Expr *Expr2);
30//===----------------------------------------------------------------------===//
31// FindIdenticalExprVisitor - Identify nodes using identical expressions.
32//===----------------------------------------------------------------------===//
33
34class FindIdenticalExprVisitor
35 : public RecursiveASTVisitor<FindIdenticalExprVisitor> {
36public:
37 explicit FindIdenticalExprVisitor(BugReporter &B, AnalysisDeclContext *A)
38 : BR(B), AC(A) {}
39 // FindIdenticalExprVisitor only visits nodes
40 // that are binary operators.
41 bool VisitBinaryOperator(const BinaryOperator *B);
42
43private:
44 BugReporter &BR;
45 AnalysisDeclContext *AC;
46};
47
48bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
49 BinaryOperator::Opcode Op = B->getOpcode();
50 if (!BinaryOperator::isComparisonOp(Op))
51 return true;
52 //
53 // Special case for floating-point representation.
54 //
55 // If expressions on both sides of comparison operator are of type float,
56 // then for some comparison operators no warning shall be
57 // reported even if the expressions are identical from a symbolic point of
58 // view. Comparison between expressions, declared variables and literals
59 // are treated differently.
60 //
61 // != and == between float literals that have the same value should NOT warn.
62 // < > between float literals that have the same value SHOULD warn.
63 //
64 // != and == between the same float declaration should NOT warn.
65 // < > between the same float declaration SHOULD warn.
66 //
67 // != and == between eq. expressions that evaluates into float
68 // should NOT warn.
69 // < > between eq. expressions that evaluates into float
70 // should NOT warn.
71 //
72 const Expr *LHS = B->getLHS()->IgnoreParenImpCasts();
73 const Expr *RHS = B->getRHS()->IgnoreParenImpCasts();
74
75 const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
76 const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
77 const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
78 const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
79 if ((DeclRef1) && (DeclRef2)) {
80 if ((DeclRef1->getType()->hasFloatingRepresentation()) &&
81 (DeclRef2->getType()->hasFloatingRepresentation())) {
82 if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
83 if ((Op == BO_EQ) || (Op == BO_NE)) {
84 return true;
85 }
86 }
87 }
88 } else if ((FloatLit1) && (FloatLit2)) {
89 if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
90 if ((Op == BO_EQ) || (Op == BO_NE)) {
91 return true;
92 }
93 }
94 } else if (LHS->getType()->hasFloatingRepresentation()) {
95 // If any side of comparison operator still has floating-point
96 // representation, then it's an expression. Don't warn.
97 // Here only LHS is checked since RHS will be implicit casted to float.
98 return true;
99 } else {
100 // No special case with floating-point representation, report as usual.
101 }
102
103 if (isIdenticalExpr(AC->getASTContext(), B->getLHS(), B->getRHS())) {
104 PathDiagnosticLocation ELoc =
105 PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
106 StringRef Message;
107 if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
108 Message = "comparison of identical expressions always evaluates to true";
109 else
110 Message = "comparison of identical expressions always evaluates to false";
111 BR.EmitBasicReport(AC->getDecl(), "Compare of identical expressions",
112 categories::LogicError, Message, ELoc);
113 }
114 // We want to visit ALL nodes (subexpressions of binary comparison
115 // expressions too) that contains comparison operators.
116 // True is always returned to traverse ALL nodes.
117 return true;
118}
119/// \brief Determines whether two expression trees are identical regarding
120/// operators and symbols.
121///
122/// Exceptions: expressions containing macros or functions with possible side
123/// effects are never considered identical.
124/// Limitations: (t + u) and (u + t) are not considered identical.
125/// t*(u + t) and t*u + t*t are not considered identical.
126///
127static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
128 const Expr *Expr2) {
129 // If Expr1 & Expr2 are of different class then they are not
130 // identical expression.
131 if (Expr1->getStmtClass() != Expr2->getStmtClass())
132 return false;
133 // If Expr1 has side effects then don't warn even if expressions
134 // are identical.
135 if (Expr1->HasSideEffects(Ctx))
136 return false;
137 // Is expression is based on macro then don't warn even if
138 // the expressions are identical.
139 if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
140 return false;
141 // If all children of two expressions are identical, return true.
142 Expr::const_child_iterator I1 = Expr1->child_begin();
143 Expr::const_child_iterator I2 = Expr2->child_begin();
144 while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
145 const Expr *Child1 = dyn_cast<Expr>(*I1);
146 const Expr *Child2 = dyn_cast<Expr>(*I2);
147 if (!Child1 || !Child2 || !isIdenticalExpr(Ctx, Child1, Child2))
148 return false;
149 ++I1;
150 ++I2;
151 }
152 // If there are different number of children in the expressions, return false.
153 // (TODO: check if this is a redundant condition.)
154 if (I1 != Expr1->child_end())
155 return false;
156 if (I2 != Expr2->child_end())
157 return false;
158
159 switch (Expr1->getStmtClass()) {
160 default:
161 return false;
162 case Stmt::ArraySubscriptExprClass:
163 case Stmt::CStyleCastExprClass:
164 case Stmt::ImplicitCastExprClass:
165 case Stmt::ParenExprClass:
166 return true;
167 case Stmt::BinaryOperatorClass: {
168 const BinaryOperator *BinOp1 = dyn_cast<BinaryOperator>(Expr1);
169 const BinaryOperator *BinOp2 = dyn_cast<BinaryOperator>(Expr2);
170 return BinOp1->getOpcode() == BinOp2->getOpcode();
171 }
172 case Stmt::CharacterLiteralClass: {
173 const CharacterLiteral *CharLit1 = dyn_cast<CharacterLiteral>(Expr1);
174 const CharacterLiteral *CharLit2 = dyn_cast<CharacterLiteral>(Expr2);
175 return CharLit1->getValue() == CharLit2->getValue();
176 }
177 case Stmt::DeclRefExprClass: {
178 const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(Expr1);
179 const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(Expr2);
180 return DeclRef1->getDecl() == DeclRef2->getDecl();
181 }
182 case Stmt::IntegerLiteralClass: {
183 const IntegerLiteral *IntLit1 = dyn_cast<IntegerLiteral>(Expr1);
184 const IntegerLiteral *IntLit2 = dyn_cast<IntegerLiteral>(Expr2);
185 return IntLit1->getValue() == IntLit2->getValue();
186 }
187 case Stmt::FloatingLiteralClass: {
188 const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(Expr1);
189 const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(Expr2);
190 return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
191 }
192 case Stmt::MemberExprClass: {
193 const MemberExpr *MemberExpr1 = dyn_cast<MemberExpr>(Expr1);
194 const MemberExpr *MemberExpr2 = dyn_cast<MemberExpr>(Expr2);
195 return MemberExpr1->getMemberDecl() == MemberExpr2->getMemberDecl();
196 }
197 case Stmt::UnaryOperatorClass: {
198 const UnaryOperator *UnaryOp1 = dyn_cast<UnaryOperator>(Expr1);
199 const UnaryOperator *UnaryOp2 = dyn_cast<UnaryOperator>(Expr2);
200 if (UnaryOp1->getOpcode() != UnaryOp2->getOpcode())
201 return false;
202 return !UnaryOp1->isIncrementDecrementOp();
203 }
204 }
205}
206
207//===----------------------------------------------------------------------===//
208// FindIdenticalExprChecker
209//===----------------------------------------------------------------------===//
210
211class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {
212public:
213 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
214 BugReporter &BR) const {
215 FindIdenticalExprVisitor Visitor(BR, Mgr.getAnalysisDeclContext(D));
216 Visitor.TraverseDecl(const_cast<Decl *>(D));
217 }
218};
219
220void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
221 Mgr.registerChecker<FindIdenticalExprChecker>();
222}