xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1  //== IdenticalExprChecker.cpp - Identical expression checker----------------==//
2  //
3  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4  // See https://llvm.org/LICENSE.txt for license information.
5  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6  //
7  //===----------------------------------------------------------------------===//
8  ///
9  /// \file
10  /// This defines IdenticalExprChecker, a check that warns about
11  /// unintended use of identical expressions.
12  ///
13  /// It checks for use of identical expressions with comparison operators and
14  /// inside conditional expressions.
15  ///
16  //===----------------------------------------------------------------------===//
17  
18  #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19  #include "clang/AST/RecursiveASTVisitor.h"
20  #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21  #include "clang/StaticAnalyzer/Core/Checker.h"
22  #include "clang/StaticAnalyzer/Core/CheckerManager.h"
23  #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24  
25  using namespace clang;
26  using namespace ento;
27  
28  static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
29                              const Stmt *Stmt2, bool IgnoreSideEffects = false);
30  //===----------------------------------------------------------------------===//
31  // FindIdenticalExprVisitor - Identify nodes using identical expressions.
32  //===----------------------------------------------------------------------===//
33  
34  namespace {
35  class FindIdenticalExprVisitor
36      : public RecursiveASTVisitor<FindIdenticalExprVisitor> {
37    BugReporter &BR;
38    const CheckerBase *Checker;
39    AnalysisDeclContext *AC;
40  public:
FindIdenticalExprVisitor(BugReporter & B,const CheckerBase * Checker,AnalysisDeclContext * A)41    explicit FindIdenticalExprVisitor(BugReporter &B,
42                                      const CheckerBase *Checker,
43                                      AnalysisDeclContext *A)
44        : BR(B), Checker(Checker), AC(A) {}
45    // FindIdenticalExprVisitor only visits nodes
46    // that are binary operators, if statements or
47    // conditional operators.
48    bool VisitBinaryOperator(const BinaryOperator *B);
49    bool VisitIfStmt(const IfStmt *I);
50    bool VisitConditionalOperator(const ConditionalOperator *C);
51  
52  private:
53    void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise,
54                             ArrayRef<SourceRange> Sr);
55    void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise);
56    void checkComparisonOp(const BinaryOperator *B);
57  };
58  } // end anonymous namespace
59  
reportIdenticalExpr(const BinaryOperator * B,bool CheckBitwise,ArrayRef<SourceRange> Sr)60  void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B,
61                                                     bool CheckBitwise,
62                                                     ArrayRef<SourceRange> Sr) {
63    StringRef Message;
64    if (CheckBitwise)
65      Message = "identical expressions on both sides of bitwise operator";
66    else
67      Message = "identical expressions on both sides of logical operator";
68  
69    PathDiagnosticLocation ELoc =
70        PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
71    BR.EmitBasicReport(AC->getDecl(), Checker,
72                       "Use of identical expressions",
73                       categories::LogicError,
74                       Message, ELoc, Sr);
75  }
76  
checkBitwiseOrLogicalOp(const BinaryOperator * B,bool CheckBitwise)77  void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B,
78                                                         bool CheckBitwise) {
79    SourceRange Sr[2];
80  
81    const Expr *LHS = B->getLHS();
82    const Expr *RHS = B->getRHS();
83  
84    // Split operators as long as we still have operators to split on. We will
85    // get called for every binary operator in an expression so there is no need
86    // to check every one against each other here, just the right most one with
87    // the others.
88    while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) {
89      if (B->getOpcode() != B2->getOpcode())
90        break;
91      if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) {
92        Sr[0] = RHS->getSourceRange();
93        Sr[1] = B2->getRHS()->getSourceRange();
94        reportIdenticalExpr(B, CheckBitwise, Sr);
95      }
96      LHS = B2->getLHS();
97    }
98  
99    if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) {
100      Sr[0] = RHS->getSourceRange();
101      Sr[1] = LHS->getSourceRange();
102      reportIdenticalExpr(B, CheckBitwise, Sr);
103    }
104  }
105  
VisitIfStmt(const IfStmt * I)106  bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) {
107    const Stmt *Stmt1 = I->getThen();
108    const Stmt *Stmt2 = I->getElse();
109  
110    // Check for identical inner condition:
111    //
112    // if (x<10) {
113    //   if (x<10) {
114    //   ..
115    if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Stmt1)) {
116      if (!CS->body_empty()) {
117        const IfStmt *InnerIf = dyn_cast<IfStmt>(*CS->body_begin());
118        if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*IgnoreSideEffects=*/ false)) {
119          PathDiagnosticLocation ELoc(InnerIf->getCond(), BR.getSourceManager(), AC);
120          BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",
121            categories::LogicError,
122            "conditions of the inner and outer statements are identical",
123            ELoc);
124        }
125      }
126    }
127  
128    // Check for identical conditions:
129    //
130    // if (b) {
131    //   foo1();
132    // } else if (b) {
133    //   foo2();
134    // }
135    if (Stmt1 && Stmt2) {
136      const Expr *Cond1 = I->getCond();
137      const Stmt *Else = Stmt2;
138      while (const IfStmt *I2 = dyn_cast_or_null<IfStmt>(Else)) {
139        const Expr *Cond2 = I2->getCond();
140        if (isIdenticalStmt(AC->getASTContext(), Cond1, Cond2, false)) {
141          SourceRange Sr = Cond1->getSourceRange();
142          PathDiagnosticLocation ELoc(Cond2, BR.getSourceManager(), AC);
143          BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",
144                             categories::LogicError,
145                             "expression is identical to previous condition",
146                             ELoc, Sr);
147        }
148        Else = I2->getElse();
149      }
150    }
151  
152    if (!Stmt1 || !Stmt2)
153      return true;
154  
155    // Special handling for code like:
156    //
157    // if (b) {
158    //   i = 1;
159    // } else
160    //   i = 1;
161    if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) {
162      if (CompStmt->size() == 1)
163        Stmt1 = CompStmt->body_back();
164    }
165    if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) {
166      if (CompStmt->size() == 1)
167        Stmt2 = CompStmt->body_back();
168    }
169  
170    if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) {
171        PathDiagnosticLocation ELoc =
172            PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC);
173        BR.EmitBasicReport(AC->getDecl(), Checker,
174                           "Identical branches",
175                           categories::LogicError,
176                           "true and false branches are identical", ELoc);
177    }
178    return true;
179  }
180  
VisitBinaryOperator(const BinaryOperator * B)181  bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
182    BinaryOperator::Opcode Op = B->getOpcode();
183  
184    if (BinaryOperator::isBitwiseOp(Op))
185      checkBitwiseOrLogicalOp(B, true);
186  
187    if (BinaryOperator::isLogicalOp(Op))
188      checkBitwiseOrLogicalOp(B, false);
189  
190    if (BinaryOperator::isComparisonOp(Op))
191      checkComparisonOp(B);
192  
193    // We want to visit ALL nodes (subexpressions of binary comparison
194    // expressions too) that contains comparison operators.
195    // True is always returned to traverse ALL nodes.
196    return true;
197  }
198  
checkComparisonOp(const BinaryOperator * B)199  void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) {
200    BinaryOperator::Opcode Op = B->getOpcode();
201  
202    //
203    // Special case for floating-point representation.
204    //
205    // If expressions on both sides of comparison operator are of type float,
206    // then for some comparison operators no warning shall be
207    // reported even if the expressions are identical from a symbolic point of
208    // view. Comparison between expressions, declared variables and literals
209    // are treated differently.
210    //
211    // != and == between float literals that have the same value should NOT warn.
212    // < > between float literals that have the same value SHOULD warn.
213    //
214    // != and == between the same float declaration should NOT warn.
215    // < > between the same float declaration SHOULD warn.
216    //
217    // != and == between eq. expressions that evaluates into float
218    //           should NOT warn.
219    // < >       between eq. expressions that evaluates into float
220    //           should NOT warn.
221    //
222    const Expr *LHS = B->getLHS()->IgnoreParenImpCasts();
223    const Expr *RHS = B->getRHS()->IgnoreParenImpCasts();
224  
225    const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
226    const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
227    const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
228    const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
229    if ((DeclRef1) && (DeclRef2)) {
230      if ((DeclRef1->getType()->hasFloatingRepresentation()) &&
231          (DeclRef2->getType()->hasFloatingRepresentation())) {
232        if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
233          if ((Op == BO_EQ) || (Op == BO_NE)) {
234            return;
235          }
236        }
237      }
238    } else if ((FloatLit1) && (FloatLit2)) {
239      if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
240        if ((Op == BO_EQ) || (Op == BO_NE)) {
241          return;
242        }
243      }
244    } else if (LHS->getType()->hasFloatingRepresentation()) {
245      // If any side of comparison operator still has floating-point
246      // representation, then it's an expression. Don't warn.
247      // Here only LHS is checked since RHS will be implicit casted to float.
248      return;
249    } else {
250      // No special case with floating-point representation, report as usual.
251    }
252  
253    if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) {
254      PathDiagnosticLocation ELoc =
255          PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
256      StringRef Message;
257      if (Op == BO_Cmp)
258        Message = "comparison of identical expressions always evaluates to "
259                  "'equal'";
260      else if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
261        Message = "comparison of identical expressions always evaluates to true";
262      else
263        Message = "comparison of identical expressions always evaluates to false";
264      BR.EmitBasicReport(AC->getDecl(), Checker,
265                         "Compare of identical expressions",
266                         categories::LogicError, Message, ELoc);
267    }
268  }
269  
VisitConditionalOperator(const ConditionalOperator * C)270  bool FindIdenticalExprVisitor::VisitConditionalOperator(
271      const ConditionalOperator *C) {
272  
273    // Check if expressions in conditional expression are identical
274    // from a symbolic point of view.
275  
276    if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(),
277                        C->getFalseExpr(), true)) {
278      PathDiagnosticLocation ELoc =
279          PathDiagnosticLocation::createConditionalColonLoc(
280              C, BR.getSourceManager());
281  
282      SourceRange Sr[2];
283      Sr[0] = C->getTrueExpr()->getSourceRange();
284      Sr[1] = C->getFalseExpr()->getSourceRange();
285      BR.EmitBasicReport(
286          AC->getDecl(), Checker,
287          "Identical expressions in conditional expression",
288          categories::LogicError,
289          "identical expressions on both sides of ':' in conditional expression",
290          ELoc, Sr);
291    }
292    // We want to visit ALL nodes (expressions in conditional
293    // expressions too) that contains conditional operators,
294    // thus always return true to traverse ALL nodes.
295    return true;
296  }
297  
298  /// Determines whether two statement trees are identical regarding
299  /// operators and symbols.
300  ///
301  /// Exceptions: expressions containing macros or functions with possible side
302  /// effects are never considered identical.
303  /// Limitations: (t + u) and (u + t) are not considered identical.
304  /// t*(u + t) and t*u + t*t are not considered identical.
305  ///
isIdenticalStmt(const ASTContext & Ctx,const Stmt * Stmt1,const Stmt * Stmt2,bool IgnoreSideEffects)306  static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
307                              const Stmt *Stmt2, bool IgnoreSideEffects) {
308  
309    if (!Stmt1 || !Stmt2) {
310      return !Stmt1 && !Stmt2;
311    }
312  
313    // If Stmt1 & Stmt2 are of different class then they are not
314    // identical statements.
315    if (Stmt1->getStmtClass() != Stmt2->getStmtClass())
316      return false;
317  
318    const Expr *Expr1 = dyn_cast<Expr>(Stmt1);
319    const Expr *Expr2 = dyn_cast<Expr>(Stmt2);
320  
321    if (Expr1 && Expr2) {
322      // If Stmt1 has side effects then don't warn even if expressions
323      // are identical.
324      if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))
325        return false;
326      // If either expression comes from a macro then don't warn even if
327      // the expressions are identical.
328      if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
329        return false;
330  
331      // If all children of two expressions are identical, return true.
332      Expr::const_child_iterator I1 = Expr1->child_begin();
333      Expr::const_child_iterator I2 = Expr2->child_begin();
334      while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
335        if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
336          return false;
337        ++I1;
338        ++I2;
339      }
340      // If there are different number of children in the statements, return
341      // false.
342      if (I1 != Expr1->child_end())
343        return false;
344      if (I2 != Expr2->child_end())
345        return false;
346    }
347  
348    switch (Stmt1->getStmtClass()) {
349    default:
350      return false;
351    case Stmt::CallExprClass:
352    case Stmt::ArraySubscriptExprClass:
353    case Stmt::ArraySectionExprClass:
354    case Stmt::OMPArrayShapingExprClass:
355    case Stmt::OMPIteratorExprClass:
356    case Stmt::ImplicitCastExprClass:
357    case Stmt::ParenExprClass:
358    case Stmt::BreakStmtClass:
359    case Stmt::ContinueStmtClass:
360    case Stmt::NullStmtClass:
361      return true;
362    case Stmt::CStyleCastExprClass: {
363      const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1);
364      const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2);
365  
366      return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
367    }
368    case Stmt::ReturnStmtClass: {
369      const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
370      const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
371  
372      return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
373                             ReturnStmt2->getRetValue(), IgnoreSideEffects);
374    }
375    case Stmt::ForStmtClass: {
376      const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);
377      const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2);
378  
379      if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),
380                           IgnoreSideEffects))
381        return false;
382      if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),
383                           IgnoreSideEffects))
384        return false;
385      if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),
386                           IgnoreSideEffects))
387        return false;
388      if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),
389                           IgnoreSideEffects))
390        return false;
391      return true;
392    }
393    case Stmt::DoStmtClass: {
394      const DoStmt *DStmt1 = cast<DoStmt>(Stmt1);
395      const DoStmt *DStmt2 = cast<DoStmt>(Stmt2);
396  
397      if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),
398                           IgnoreSideEffects))
399        return false;
400      if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),
401                           IgnoreSideEffects))
402        return false;
403      return true;
404    }
405    case Stmt::WhileStmtClass: {
406      const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);
407      const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2);
408  
409      if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),
410                           IgnoreSideEffects))
411        return false;
412      if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(),
413                           IgnoreSideEffects))
414        return false;
415      return true;
416    }
417    case Stmt::IfStmtClass: {
418      const IfStmt *IStmt1 = cast<IfStmt>(Stmt1);
419      const IfStmt *IStmt2 = cast<IfStmt>(Stmt2);
420  
421      if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),
422                           IgnoreSideEffects))
423        return false;
424      if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),
425                           IgnoreSideEffects))
426        return false;
427      if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),
428                           IgnoreSideEffects))
429        return false;
430      return true;
431    }
432    case Stmt::CompoundStmtClass: {
433      const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);
434      const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2);
435  
436      if (CompStmt1->size() != CompStmt2->size())
437        return false;
438  
439      CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin();
440      CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin();
441      while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) {
442        if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
443          return false;
444        ++I1;
445        ++I2;
446      }
447  
448      return true;
449    }
450    case Stmt::CompoundAssignOperatorClass:
451    case Stmt::BinaryOperatorClass: {
452      const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);
453      const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2);
454      return BinOp1->getOpcode() == BinOp2->getOpcode();
455    }
456    case Stmt::CharacterLiteralClass: {
457      const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);
458      const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2);
459      return CharLit1->getValue() == CharLit2->getValue();
460    }
461    case Stmt::DeclRefExprClass: {
462      const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);
463      const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2);
464      return DeclRef1->getDecl() == DeclRef2->getDecl();
465    }
466    case Stmt::IntegerLiteralClass: {
467      const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);
468      const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2);
469  
470      llvm::APInt I1 = IntLit1->getValue();
471      llvm::APInt I2 = IntLit2->getValue();
472      if (I1.getBitWidth() != I2.getBitWidth())
473        return false;
474      return  I1 == I2;
475    }
476    case Stmt::FloatingLiteralClass: {
477      const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1);
478      const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2);
479      return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
480    }
481    case Stmt::StringLiteralClass: {
482      const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1);
483      const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2);
484      return StringLit1->getBytes() == StringLit2->getBytes();
485    }
486    case Stmt::MemberExprClass: {
487      const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);
488      const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2);
489      return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
490    }
491    case Stmt::UnaryOperatorClass: {
492      const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);
493      const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2);
494      return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
495    }
496    }
497  }
498  
499  //===----------------------------------------------------------------------===//
500  // FindIdenticalExprChecker
501  //===----------------------------------------------------------------------===//
502  
503  namespace {
504  class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {
505  public:
checkASTCodeBody(const Decl * D,AnalysisManager & Mgr,BugReporter & BR) const506    void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
507                          BugReporter &BR) const {
508      FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
509      Visitor.TraverseDecl(const_cast<Decl *>(D));
510    }
511  };
512  } // end anonymous namespace
513  
registerIdenticalExprChecker(CheckerManager & Mgr)514  void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
515    Mgr.registerChecker<FindIdenticalExprChecker>();
516  }
517  
shouldRegisterIdenticalExprChecker(const CheckerManager & mgr)518  bool ento::shouldRegisterIdenticalExprChecker(const CheckerManager &mgr) {
519    return true;
520  }
521