diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp | 518 | 
1 files changed, 518 insertions, 0 deletions
| diff --git a/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp new file mode 100644 index 000000000000..cc2cfb774227 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp @@ -0,0 +1,518 @@ +//== IdenticalExprChecker.cpp - Identical expression checker----------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This defines IdenticalExprChecker, a check that warns about +/// unintended use of identical expressions. +/// +/// It checks for use of identical expressions with comparison operators and +/// inside conditional expressions. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, +                            const Stmt *Stmt2, bool IgnoreSideEffects = false); +//===----------------------------------------------------------------------===// +// FindIdenticalExprVisitor - Identify nodes using identical expressions. +//===----------------------------------------------------------------------===// + +namespace { +class FindIdenticalExprVisitor +    : public RecursiveASTVisitor<FindIdenticalExprVisitor> { +  BugReporter &BR; +  const CheckerBase *Checker; +  AnalysisDeclContext *AC; +public: +  explicit FindIdenticalExprVisitor(BugReporter &B, +                                    const CheckerBase *Checker, +                                    AnalysisDeclContext *A) +      : BR(B), Checker(Checker), AC(A) {} +  // FindIdenticalExprVisitor only visits nodes +  // that are binary operators, if statements or +  // conditional operators. +  bool VisitBinaryOperator(const BinaryOperator *B); +  bool VisitIfStmt(const IfStmt *I); +  bool VisitConditionalOperator(const ConditionalOperator *C); + +private: +  void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise, +                           ArrayRef<SourceRange> Sr); +  void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise); +  void checkComparisonOp(const BinaryOperator *B); +}; +} // end anonymous namespace + +void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B, +                                                   bool CheckBitwise, +                                                   ArrayRef<SourceRange> Sr) { +  StringRef Message; +  if (CheckBitwise) +    Message = "identical expressions on both sides of bitwise operator"; +  else +    Message = "identical expressions on both sides of logical operator"; + +  PathDiagnosticLocation ELoc = +      PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager()); +  BR.EmitBasicReport(AC->getDecl(), Checker, +                     "Use of identical expressions", +                     categories::LogicError, +                     Message, ELoc, Sr); +} + +void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B, +                                                       bool CheckBitwise) { +  SourceRange Sr[2]; + +  const Expr *LHS = B->getLHS(); +  const Expr *RHS = B->getRHS(); + +  // Split operators as long as we still have operators to split on. We will +  // get called for every binary operator in an expression so there is no need +  // to check every one against each other here, just the right most one with +  // the others. +  while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) { +    if (B->getOpcode() != B2->getOpcode()) +      break; +    if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) { +      Sr[0] = RHS->getSourceRange(); +      Sr[1] = B2->getRHS()->getSourceRange(); +      reportIdenticalExpr(B, CheckBitwise, Sr); +    } +    LHS = B2->getLHS(); +  } + +  if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) { +    Sr[0] = RHS->getSourceRange(); +    Sr[1] = LHS->getSourceRange(); +    reportIdenticalExpr(B, CheckBitwise, Sr); +  } +} + +bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) { +  const Stmt *Stmt1 = I->getThen(); +  const Stmt *Stmt2 = I->getElse(); + +  // Check for identical inner condition: +  // +  // if (x<10) { +  //   if (x<10) { +  //   .. +  if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Stmt1)) { +    if (!CS->body_empty()) { +      const IfStmt *InnerIf = dyn_cast<IfStmt>(*CS->body_begin()); +      if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*IgnoreSideEffects=*/ false)) { +        PathDiagnosticLocation ELoc(InnerIf->getCond(), BR.getSourceManager(), AC); +        BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions", +          categories::LogicError, +          "conditions of the inner and outer statements are identical", +          ELoc); +      } +    } +  } + +  // Check for identical conditions: +  // +  // if (b) { +  //   foo1(); +  // } else if (b) { +  //   foo2(); +  // } +  if (Stmt1 && Stmt2) { +    const Expr *Cond1 = I->getCond(); +    const Stmt *Else = Stmt2; +    while (const IfStmt *I2 = dyn_cast_or_null<IfStmt>(Else)) { +      const Expr *Cond2 = I2->getCond(); +      if (isIdenticalStmt(AC->getASTContext(), Cond1, Cond2, false)) { +        SourceRange Sr = Cond1->getSourceRange(); +        PathDiagnosticLocation ELoc(Cond2, BR.getSourceManager(), AC); +        BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions", +                           categories::LogicError, +                           "expression is identical to previous condition", +                           ELoc, Sr); +      } +      Else = I2->getElse(); +    } +  } + +  if (!Stmt1 || !Stmt2) +    return true; + +  // Special handling for code like: +  // +  // if (b) { +  //   i = 1; +  // } else +  //   i = 1; +  if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) { +    if (CompStmt->size() == 1) +      Stmt1 = CompStmt->body_back(); +  } +  if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) { +    if (CompStmt->size() == 1) +      Stmt2 = CompStmt->body_back(); +  } + +  if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) { +      PathDiagnosticLocation ELoc = +          PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC); +      BR.EmitBasicReport(AC->getDecl(), Checker, +                         "Identical branches", +                         categories::LogicError, +                         "true and false branches are identical", ELoc); +  } +  return true; +} + +bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) { +  BinaryOperator::Opcode Op = B->getOpcode(); + +  if (BinaryOperator::isBitwiseOp(Op)) +    checkBitwiseOrLogicalOp(B, true); + +  if (BinaryOperator::isLogicalOp(Op)) +    checkBitwiseOrLogicalOp(B, false); + +  if (BinaryOperator::isComparisonOp(Op)) +    checkComparisonOp(B); + +  // We want to visit ALL nodes (subexpressions of binary comparison +  // expressions too) that contains comparison operators. +  // True is always returned to traverse ALL nodes. +  return true; +} + +void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) { +  BinaryOperator::Opcode Op = B->getOpcode(); + +  // +  // Special case for floating-point representation. +  // +  // If expressions on both sides of comparison operator are of type float, +  // then for some comparison operators no warning shall be +  // reported even if the expressions are identical from a symbolic point of +  // view. Comparison between expressions, declared variables and literals +  // are treated differently. +  // +  // != and == between float literals that have the same value should NOT warn. +  // < > between float literals that have the same value SHOULD warn. +  // +  // != and == between the same float declaration should NOT warn. +  // < > between the same float declaration SHOULD warn. +  // +  // != and == between eq. expressions that evaluates into float +  //           should NOT warn. +  // < >       between eq. expressions that evaluates into float +  //           should NOT warn. +  // +  const Expr *LHS = B->getLHS()->IgnoreParenImpCasts(); +  const Expr *RHS = B->getRHS()->IgnoreParenImpCasts(); + +  const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS); +  const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS); +  const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS); +  const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS); +  if ((DeclRef1) && (DeclRef2)) { +    if ((DeclRef1->getType()->hasFloatingRepresentation()) && +        (DeclRef2->getType()->hasFloatingRepresentation())) { +      if (DeclRef1->getDecl() == DeclRef2->getDecl()) { +        if ((Op == BO_EQ) || (Op == BO_NE)) { +          return; +        } +      } +    } +  } else if ((FloatLit1) && (FloatLit2)) { +    if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) { +      if ((Op == BO_EQ) || (Op == BO_NE)) { +        return; +      } +    } +  } else if (LHS->getType()->hasFloatingRepresentation()) { +    // If any side of comparison operator still has floating-point +    // representation, then it's an expression. Don't warn. +    // Here only LHS is checked since RHS will be implicit casted to float. +    return; +  } else { +    // No special case with floating-point representation, report as usual. +  } + +  if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) { +    PathDiagnosticLocation ELoc = +        PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager()); +    StringRef Message; +    if (Op == BO_Cmp) +      Message = "comparison of identical expressions always evaluates to " +                "'equal'"; +    else if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE))) +      Message = "comparison of identical expressions always evaluates to true"; +    else +      Message = "comparison of identical expressions always evaluates to false"; +    BR.EmitBasicReport(AC->getDecl(), Checker, +                       "Compare of identical expressions", +                       categories::LogicError, Message, ELoc); +  } +} + +bool FindIdenticalExprVisitor::VisitConditionalOperator( +    const ConditionalOperator *C) { + +  // Check if expressions in conditional expression are identical +  // from a symbolic point of view. + +  if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(), +                      C->getFalseExpr(), true)) { +    PathDiagnosticLocation ELoc = +        PathDiagnosticLocation::createConditionalColonLoc( +            C, BR.getSourceManager()); + +    SourceRange Sr[2]; +    Sr[0] = C->getTrueExpr()->getSourceRange(); +    Sr[1] = C->getFalseExpr()->getSourceRange(); +    BR.EmitBasicReport( +        AC->getDecl(), Checker, +        "Identical expressions in conditional expression", +        categories::LogicError, +        "identical expressions on both sides of ':' in conditional expression", +        ELoc, Sr); +  } +  // We want to visit ALL nodes (expressions in conditional +  // expressions too) that contains conditional operators, +  // thus always return true to traverse ALL nodes. +  return true; +} + +/// Determines whether two statement trees are identical regarding +/// operators and symbols. +/// +/// Exceptions: expressions containing macros or functions with possible side +/// effects are never considered identical. +/// Limitations: (t + u) and (u + t) are not considered identical. +/// t*(u + t) and t*u + t*t are not considered identical. +/// +static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, +                            const Stmt *Stmt2, bool IgnoreSideEffects) { + +  if (!Stmt1 || !Stmt2) { +    return !Stmt1 && !Stmt2; +  } + +  // If Stmt1 & Stmt2 are of different class then they are not +  // identical statements. +  if (Stmt1->getStmtClass() != Stmt2->getStmtClass()) +    return false; + +  const Expr *Expr1 = dyn_cast<Expr>(Stmt1); +  const Expr *Expr2 = dyn_cast<Expr>(Stmt2); + +  if (Expr1 && Expr2) { +    // If Stmt1 has side effects then don't warn even if expressions +    // are identical. +    if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx)) +      return false; +    // If either expression comes from a macro then don't warn even if +    // the expressions are identical. +    if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID())) +      return false; + +    // If all children of two expressions are identical, return true. +    Expr::const_child_iterator I1 = Expr1->child_begin(); +    Expr::const_child_iterator I2 = Expr2->child_begin(); +    while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) { +      if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects)) +        return false; +      ++I1; +      ++I2; +    } +    // If there are different number of children in the statements, return +    // false. +    if (I1 != Expr1->child_end()) +      return false; +    if (I2 != Expr2->child_end()) +      return false; +  } + +  switch (Stmt1->getStmtClass()) { +  default: +    return false; +  case Stmt::CallExprClass: +  case Stmt::ArraySubscriptExprClass: +  case Stmt::OMPArraySectionExprClass: +  case Stmt::ImplicitCastExprClass: +  case Stmt::ParenExprClass: +  case Stmt::BreakStmtClass: +  case Stmt::ContinueStmtClass: +  case Stmt::NullStmtClass: +    return true; +  case Stmt::CStyleCastExprClass: { +    const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1); +    const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2); + +    return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten(); +  } +  case Stmt::ReturnStmtClass: { +    const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1); +    const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2); + +    return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(), +                           ReturnStmt2->getRetValue(), IgnoreSideEffects); +  } +  case Stmt::ForStmtClass: { +    const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1); +    const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2); + +    if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(), +                         IgnoreSideEffects)) +      return false; +    if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(), +                         IgnoreSideEffects)) +      return false; +    if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(), +                         IgnoreSideEffects)) +      return false; +    if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(), +                         IgnoreSideEffects)) +      return false; +    return true; +  } +  case Stmt::DoStmtClass: { +    const DoStmt *DStmt1 = cast<DoStmt>(Stmt1); +    const DoStmt *DStmt2 = cast<DoStmt>(Stmt2); + +    if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(), +                         IgnoreSideEffects)) +      return false; +    if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(), +                         IgnoreSideEffects)) +      return false; +    return true; +  } +  case Stmt::WhileStmtClass: { +    const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1); +    const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2); + +    if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(), +                         IgnoreSideEffects)) +      return false; +    if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(), +                         IgnoreSideEffects)) +      return false; +    return true; +  } +  case Stmt::IfStmtClass: { +    const IfStmt *IStmt1 = cast<IfStmt>(Stmt1); +    const IfStmt *IStmt2 = cast<IfStmt>(Stmt2); + +    if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(), +                         IgnoreSideEffects)) +      return false; +    if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(), +                         IgnoreSideEffects)) +      return false; +    if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(), +                         IgnoreSideEffects)) +      return false; +    return true; +  } +  case Stmt::CompoundStmtClass: { +    const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1); +    const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2); + +    if (CompStmt1->size() != CompStmt2->size()) +      return false; + +    CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin(); +    CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin(); +    while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) { +      if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects)) +        return false; +      ++I1; +      ++I2; +    } + +    return true; +  } +  case Stmt::CompoundAssignOperatorClass: +  case Stmt::BinaryOperatorClass: { +    const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1); +    const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2); +    return BinOp1->getOpcode() == BinOp2->getOpcode(); +  } +  case Stmt::CharacterLiteralClass: { +    const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1); +    const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2); +    return CharLit1->getValue() == CharLit2->getValue(); +  } +  case Stmt::DeclRefExprClass: { +    const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1); +    const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2); +    return DeclRef1->getDecl() == DeclRef2->getDecl(); +  } +  case Stmt::IntegerLiteralClass: { +    const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1); +    const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2); + +    llvm::APInt I1 = IntLit1->getValue(); +    llvm::APInt I2 = IntLit2->getValue(); +    if (I1.getBitWidth() != I2.getBitWidth()) +      return false; +    return  I1 == I2; +  } +  case Stmt::FloatingLiteralClass: { +    const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1); +    const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2); +    return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue()); +  } +  case Stmt::StringLiteralClass: { +    const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1); +    const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2); +    return StringLit1->getBytes() == StringLit2->getBytes(); +  } +  case Stmt::MemberExprClass: { +    const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1); +    const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2); +    return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl(); +  } +  case Stmt::UnaryOperatorClass: { +    const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1); +    const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2); +    return UnaryOp1->getOpcode() == UnaryOp2->getOpcode(); +  } +  } +} + +//===----------------------------------------------------------------------===// +// FindIdenticalExprChecker +//===----------------------------------------------------------------------===// + +namespace { +class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> { +public: +  void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, +                        BugReporter &BR) const { +    FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); +    Visitor.TraverseDecl(const_cast<Decl *>(D)); +  } +}; +} // end anonymous namespace + +void ento::registerIdenticalExprChecker(CheckerManager &Mgr) { +  Mgr.registerChecker<FindIdenticalExprChecker>(); +} + +bool ento::shouldRegisterIdenticalExprChecker(const LangOptions &LO) { +  return true; +} | 
