123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- //== 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::OMPArrayShapingExprClass:
- case Stmt::OMPIteratorExprClass:
- 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 CheckerManager &mgr) {
- return true;
- }
|