1 //== 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 and 15 /// inside conditional expressions. 16 /// 17 //===----------------------------------------------------------------------===// 18 19 #include "ClangSACheckers.h" 20 #include "clang/AST/RecursiveASTVisitor.h" 21 #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" 25 26 using namespace clang; 27 using namespace ento; 28 29 static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1, 30 const Expr *Expr2, bool IgnoreSideEffects = false); 31 //===----------------------------------------------------------------------===// 32 // FindIdenticalExprVisitor - Identify nodes using identical expressions. 33 //===----------------------------------------------------------------------===// 34 35 namespace { 36 class FindIdenticalExprVisitor 37 : public RecursiveASTVisitor<FindIdenticalExprVisitor> { 38 public: 39 explicit FindIdenticalExprVisitor(BugReporter &B, 40 const CheckerBase *Checker, 41 AnalysisDeclContext *A) 42 : BR(B), Checker(Checker), AC(A) {} 43 // FindIdenticalExprVisitor only visits nodes 44 // that are binary operators or conditional operators. 45 bool VisitBinaryOperator(const BinaryOperator *B); 46 bool VisitConditionalOperator(const ConditionalOperator *C); 47 48 private: 49 BugReporter &BR; 50 const CheckerBase *Checker; 51 AnalysisDeclContext *AC; 52 }; 53 } // end anonymous namespace 54 55 bool 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"; 118 BR.EmitBasicReport(AC->getDecl(), Checker, 119 "Compare of identical expressions", 120 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 } 127 128 bool 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( 144 AC->getDecl(), Checker, 145 "Identical expressions in conditional expression", 146 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 156 /// \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 /// 164 static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1, 165 const Expr *Expr2, bool IgnoreSideEffects) { 166 // 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. 172 if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx)) 173 return false; 174 // If either expression comes from a macro then don't warn even if 175 // 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); 184 if (!Child1 || !Child2 || !isIdenticalExpr(Ctx, Child1, Child2, 185 IgnoreSideEffects)) 186 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; 200 case Stmt::CallExprClass: 201 case Stmt::ArraySubscriptExprClass: 202 case Stmt::CStyleCastExprClass: 203 case Stmt::ImplicitCastExprClass: 204 case Stmt::ParenExprClass: 205 return true; 206 case Stmt::BinaryOperatorClass: { 207 const BinaryOperator *BinOp1 = cast<BinaryOperator>(Expr1); 208 const BinaryOperator *BinOp2 = cast<BinaryOperator>(Expr2); 209 return BinOp1->getOpcode() == BinOp2->getOpcode(); 210 } 211 case Stmt::CharacterLiteralClass: { 212 const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Expr1); 213 const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Expr2); 214 return CharLit1->getValue() == CharLit2->getValue(); 215 } 216 case Stmt::DeclRefExprClass: { 217 const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Expr1); 218 const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Expr2); 219 return DeclRef1->getDecl() == DeclRef2->getDecl(); 220 } 221 case Stmt::IntegerLiteralClass: { 222 const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Expr1); 223 const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Expr2); 224 return IntLit1->getValue() == IntLit2->getValue(); 225 } 226 case Stmt::FloatingLiteralClass: { 227 const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Expr1); 228 const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Expr2); 229 return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue()); 230 } 231 case Stmt::MemberExprClass: { 232 const MemberExpr *MemberExpr1 = cast<MemberExpr>(Expr1); 233 const MemberExpr *MemberExpr2 = cast<MemberExpr>(Expr2); 234 return MemberExpr1->getMemberDecl() == MemberExpr2->getMemberDecl(); 235 } 236 case Stmt::UnaryOperatorClass: { 237 const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Expr1); 238 const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Expr2); 239 return UnaryOp1->getOpcode() == UnaryOp2->getOpcode(); 240 } 241 } 242 } 243 244 //===----------------------------------------------------------------------===// 245 // FindIdenticalExprChecker 246 //===----------------------------------------------------------------------===// 247 248 namespace { 249 class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> { 250 public: 251 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, 252 BugReporter &BR) const { 253 FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); 254 Visitor.TraverseDecl(const_cast<Decl *>(D)); 255 } 256 }; 257 } // end anonymous namespace 258 259 void ento::registerIdenticalExprChecker(CheckerManager &Mgr) { 260 Mgr.registerChecker<FindIdenticalExprChecker>(); 261 } 262