Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
35269 views
//== IdenticalExprChecker.cpp - Identical expression checker----------------==//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This defines IdenticalExprChecker, a check that warns about10/// unintended use of identical expressions.11///12/// It checks for use of identical expressions with comparison operators and13/// inside conditional expressions.14///15//===----------------------------------------------------------------------===//1617#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"18#include "clang/AST/RecursiveASTVisitor.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"2324using namespace clang;25using namespace ento;2627static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,28const Stmt *Stmt2, bool IgnoreSideEffects = false);29//===----------------------------------------------------------------------===//30// FindIdenticalExprVisitor - Identify nodes using identical expressions.31//===----------------------------------------------------------------------===//3233namespace {34class FindIdenticalExprVisitor35: public RecursiveASTVisitor<FindIdenticalExprVisitor> {36BugReporter &BR;37const CheckerBase *Checker;38AnalysisDeclContext *AC;39public:40explicit FindIdenticalExprVisitor(BugReporter &B,41const CheckerBase *Checker,42AnalysisDeclContext *A)43: BR(B), Checker(Checker), AC(A) {}44// FindIdenticalExprVisitor only visits nodes45// that are binary operators, if statements or46// conditional operators.47bool VisitBinaryOperator(const BinaryOperator *B);48bool VisitIfStmt(const IfStmt *I);49bool VisitConditionalOperator(const ConditionalOperator *C);5051private:52void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise,53ArrayRef<SourceRange> Sr);54void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise);55void checkComparisonOp(const BinaryOperator *B);56};57} // end anonymous namespace5859void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B,60bool CheckBitwise,61ArrayRef<SourceRange> Sr) {62StringRef Message;63if (CheckBitwise)64Message = "identical expressions on both sides of bitwise operator";65else66Message = "identical expressions on both sides of logical operator";6768PathDiagnosticLocation ELoc =69PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());70BR.EmitBasicReport(AC->getDecl(), Checker,71"Use of identical expressions",72categories::LogicError,73Message, ELoc, Sr);74}7576void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B,77bool CheckBitwise) {78SourceRange Sr[2];7980const Expr *LHS = B->getLHS();81const Expr *RHS = B->getRHS();8283// Split operators as long as we still have operators to split on. We will84// get called for every binary operator in an expression so there is no need85// to check every one against each other here, just the right most one with86// the others.87while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) {88if (B->getOpcode() != B2->getOpcode())89break;90if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) {91Sr[0] = RHS->getSourceRange();92Sr[1] = B2->getRHS()->getSourceRange();93reportIdenticalExpr(B, CheckBitwise, Sr);94}95LHS = B2->getLHS();96}9798if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) {99Sr[0] = RHS->getSourceRange();100Sr[1] = LHS->getSourceRange();101reportIdenticalExpr(B, CheckBitwise, Sr);102}103}104105bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) {106const Stmt *Stmt1 = I->getThen();107const Stmt *Stmt2 = I->getElse();108109// Check for identical inner condition:110//111// if (x<10) {112// if (x<10) {113// ..114if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Stmt1)) {115if (!CS->body_empty()) {116const IfStmt *InnerIf = dyn_cast<IfStmt>(*CS->body_begin());117if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*IgnoreSideEffects=*/ false)) {118PathDiagnosticLocation ELoc(InnerIf->getCond(), BR.getSourceManager(), AC);119BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",120categories::LogicError,121"conditions of the inner and outer statements are identical",122ELoc);123}124}125}126127// Check for identical conditions:128//129// if (b) {130// foo1();131// } else if (b) {132// foo2();133// }134if (Stmt1 && Stmt2) {135const Expr *Cond1 = I->getCond();136const Stmt *Else = Stmt2;137while (const IfStmt *I2 = dyn_cast_or_null<IfStmt>(Else)) {138const Expr *Cond2 = I2->getCond();139if (isIdenticalStmt(AC->getASTContext(), Cond1, Cond2, false)) {140SourceRange Sr = Cond1->getSourceRange();141PathDiagnosticLocation ELoc(Cond2, BR.getSourceManager(), AC);142BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",143categories::LogicError,144"expression is identical to previous condition",145ELoc, Sr);146}147Else = I2->getElse();148}149}150151if (!Stmt1 || !Stmt2)152return true;153154// Special handling for code like:155//156// if (b) {157// i = 1;158// } else159// i = 1;160if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) {161if (CompStmt->size() == 1)162Stmt1 = CompStmt->body_back();163}164if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) {165if (CompStmt->size() == 1)166Stmt2 = CompStmt->body_back();167}168169if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) {170PathDiagnosticLocation ELoc =171PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC);172BR.EmitBasicReport(AC->getDecl(), Checker,173"Identical branches",174categories::LogicError,175"true and false branches are identical", ELoc);176}177return true;178}179180bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {181BinaryOperator::Opcode Op = B->getOpcode();182183if (BinaryOperator::isBitwiseOp(Op))184checkBitwiseOrLogicalOp(B, true);185186if (BinaryOperator::isLogicalOp(Op))187checkBitwiseOrLogicalOp(B, false);188189if (BinaryOperator::isComparisonOp(Op))190checkComparisonOp(B);191192// We want to visit ALL nodes (subexpressions of binary comparison193// expressions too) that contains comparison operators.194// True is always returned to traverse ALL nodes.195return true;196}197198void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) {199BinaryOperator::Opcode Op = B->getOpcode();200201//202// Special case for floating-point representation.203//204// If expressions on both sides of comparison operator are of type float,205// then for some comparison operators no warning shall be206// reported even if the expressions are identical from a symbolic point of207// view. Comparison between expressions, declared variables and literals208// are treated differently.209//210// != and == between float literals that have the same value should NOT warn.211// < > between float literals that have the same value SHOULD warn.212//213// != and == between the same float declaration should NOT warn.214// < > between the same float declaration SHOULD warn.215//216// != and == between eq. expressions that evaluates into float217// should NOT warn.218// < > between eq. expressions that evaluates into float219// should NOT warn.220//221const Expr *LHS = B->getLHS()->IgnoreParenImpCasts();222const Expr *RHS = B->getRHS()->IgnoreParenImpCasts();223224const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);225const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);226const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);227const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);228if ((DeclRef1) && (DeclRef2)) {229if ((DeclRef1->getType()->hasFloatingRepresentation()) &&230(DeclRef2->getType()->hasFloatingRepresentation())) {231if (DeclRef1->getDecl() == DeclRef2->getDecl()) {232if ((Op == BO_EQ) || (Op == BO_NE)) {233return;234}235}236}237} else if ((FloatLit1) && (FloatLit2)) {238if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {239if ((Op == BO_EQ) || (Op == BO_NE)) {240return;241}242}243} else if (LHS->getType()->hasFloatingRepresentation()) {244// If any side of comparison operator still has floating-point245// representation, then it's an expression. Don't warn.246// Here only LHS is checked since RHS will be implicit casted to float.247return;248} else {249// No special case with floating-point representation, report as usual.250}251252if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) {253PathDiagnosticLocation ELoc =254PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());255StringRef Message;256if (Op == BO_Cmp)257Message = "comparison of identical expressions always evaluates to "258"'equal'";259else if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))260Message = "comparison of identical expressions always evaluates to true";261else262Message = "comparison of identical expressions always evaluates to false";263BR.EmitBasicReport(AC->getDecl(), Checker,264"Compare of identical expressions",265categories::LogicError, Message, ELoc);266}267}268269bool FindIdenticalExprVisitor::VisitConditionalOperator(270const ConditionalOperator *C) {271272// Check if expressions in conditional expression are identical273// from a symbolic point of view.274275if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(),276C->getFalseExpr(), true)) {277PathDiagnosticLocation ELoc =278PathDiagnosticLocation::createConditionalColonLoc(279C, BR.getSourceManager());280281SourceRange Sr[2];282Sr[0] = C->getTrueExpr()->getSourceRange();283Sr[1] = C->getFalseExpr()->getSourceRange();284BR.EmitBasicReport(285AC->getDecl(), Checker,286"Identical expressions in conditional expression",287categories::LogicError,288"identical expressions on both sides of ':' in conditional expression",289ELoc, Sr);290}291// We want to visit ALL nodes (expressions in conditional292// expressions too) that contains conditional operators,293// thus always return true to traverse ALL nodes.294return true;295}296297/// Determines whether two statement trees are identical regarding298/// operators and symbols.299///300/// Exceptions: expressions containing macros or functions with possible side301/// effects are never considered identical.302/// Limitations: (t + u) and (u + t) are not considered identical.303/// t*(u + t) and t*u + t*t are not considered identical.304///305static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,306const Stmt *Stmt2, bool IgnoreSideEffects) {307308if (!Stmt1 || !Stmt2) {309return !Stmt1 && !Stmt2;310}311312// If Stmt1 & Stmt2 are of different class then they are not313// identical statements.314if (Stmt1->getStmtClass() != Stmt2->getStmtClass())315return false;316317const Expr *Expr1 = dyn_cast<Expr>(Stmt1);318const Expr *Expr2 = dyn_cast<Expr>(Stmt2);319320if (Expr1 && Expr2) {321// If Stmt1 has side effects then don't warn even if expressions322// are identical.323if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))324return false;325// If either expression comes from a macro then don't warn even if326// the expressions are identical.327if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))328return false;329330// If all children of two expressions are identical, return true.331Expr::const_child_iterator I1 = Expr1->child_begin();332Expr::const_child_iterator I2 = Expr2->child_begin();333while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {334if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))335return false;336++I1;337++I2;338}339// If there are different number of children in the statements, return340// false.341if (I1 != Expr1->child_end())342return false;343if (I2 != Expr2->child_end())344return false;345}346347switch (Stmt1->getStmtClass()) {348default:349return false;350case Stmt::CallExprClass:351case Stmt::ArraySubscriptExprClass:352case Stmt::ArraySectionExprClass:353case Stmt::OMPArrayShapingExprClass:354case Stmt::OMPIteratorExprClass:355case Stmt::ImplicitCastExprClass:356case Stmt::ParenExprClass:357case Stmt::BreakStmtClass:358case Stmt::ContinueStmtClass:359case Stmt::NullStmtClass:360return true;361case Stmt::CStyleCastExprClass: {362const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1);363const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2);364365return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();366}367case Stmt::ReturnStmtClass: {368const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);369const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2);370371return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),372ReturnStmt2->getRetValue(), IgnoreSideEffects);373}374case Stmt::ForStmtClass: {375const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);376const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2);377378if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),379IgnoreSideEffects))380return false;381if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),382IgnoreSideEffects))383return false;384if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),385IgnoreSideEffects))386return false;387if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),388IgnoreSideEffects))389return false;390return true;391}392case Stmt::DoStmtClass: {393const DoStmt *DStmt1 = cast<DoStmt>(Stmt1);394const DoStmt *DStmt2 = cast<DoStmt>(Stmt2);395396if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),397IgnoreSideEffects))398return false;399if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),400IgnoreSideEffects))401return false;402return true;403}404case Stmt::WhileStmtClass: {405const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);406const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2);407408if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),409IgnoreSideEffects))410return false;411if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(),412IgnoreSideEffects))413return false;414return true;415}416case Stmt::IfStmtClass: {417const IfStmt *IStmt1 = cast<IfStmt>(Stmt1);418const IfStmt *IStmt2 = cast<IfStmt>(Stmt2);419420if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),421IgnoreSideEffects))422return false;423if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),424IgnoreSideEffects))425return false;426if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),427IgnoreSideEffects))428return false;429return true;430}431case Stmt::CompoundStmtClass: {432const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);433const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2);434435if (CompStmt1->size() != CompStmt2->size())436return false;437438CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin();439CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin();440while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) {441if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))442return false;443++I1;444++I2;445}446447return true;448}449case Stmt::CompoundAssignOperatorClass:450case Stmt::BinaryOperatorClass: {451const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);452const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2);453return BinOp1->getOpcode() == BinOp2->getOpcode();454}455case Stmt::CharacterLiteralClass: {456const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);457const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2);458return CharLit1->getValue() == CharLit2->getValue();459}460case Stmt::DeclRefExprClass: {461const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);462const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2);463return DeclRef1->getDecl() == DeclRef2->getDecl();464}465case Stmt::IntegerLiteralClass: {466const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);467const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2);468469llvm::APInt I1 = IntLit1->getValue();470llvm::APInt I2 = IntLit2->getValue();471if (I1.getBitWidth() != I2.getBitWidth())472return false;473return I1 == I2;474}475case Stmt::FloatingLiteralClass: {476const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1);477const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2);478return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());479}480case Stmt::StringLiteralClass: {481const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1);482const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2);483return StringLit1->getBytes() == StringLit2->getBytes();484}485case Stmt::MemberExprClass: {486const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);487const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2);488return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();489}490case Stmt::UnaryOperatorClass: {491const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);492const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2);493return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();494}495}496}497498//===----------------------------------------------------------------------===//499// FindIdenticalExprChecker500//===----------------------------------------------------------------------===//501502namespace {503class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {504public:505void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,506BugReporter &BR) const {507FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));508Visitor.TraverseDecl(const_cast<Decl *>(D));509}510};511} // end anonymous namespace512513void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {514Mgr.registerChecker<FindIdenticalExprChecker>();515}516517bool ento::shouldRegisterIdenticalExprChecker(const CheckerManager &mgr) {518return true;519}520521522