clang-tools  14.0.0git
RedundantBranchConditionCheck.cpp
Go to the documentation of this file.
1 //===--- RedundantBranchConditionCheck.cpp - clang-tidy -------------------------===//
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 
10 #include "../utils/Aliasing.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
14 #include "clang/Lex/Lexer.h"
15 
16 using namespace clang::ast_matchers;
18 
19 namespace clang {
20 namespace tidy {
21 namespace bugprone {
22 
23 static const char CondVarStr[] = "cond_var";
24 static const char OuterIfStr[] = "outer_if";
25 static const char InnerIfStr[] = "inner_if";
26 static const char OuterIfVar1Str[] = "outer_if_var1";
27 static const char OuterIfVar2Str[] = "outer_if_var2";
28 static const char InnerIfVar1Str[] = "inner_if_var1";
29 static const char InnerIfVar2Str[] = "inner_if_var2";
30 static const char FuncStr[] = "func";
31 
32 /// Returns whether `Var` is changed in range (`PrevS`..`NextS`).
33 static bool isChangedBefore(const Stmt *S, const Stmt *NextS, const Stmt *PrevS,
34  const VarDecl *Var, ASTContext *Context) {
35  ExprMutationAnalyzer MutAn(*S, *Context);
36  const auto &SM = Context->getSourceManager();
37  const Stmt *MutS = MutAn.findMutation(Var);
38  return MutS &&
39  SM.isBeforeInTranslationUnit(PrevS->getEndLoc(),
40  MutS->getBeginLoc()) &&
41  SM.isBeforeInTranslationUnit(MutS->getEndLoc(), NextS->getBeginLoc());
42 }
43 
44 void RedundantBranchConditionCheck::registerMatchers(MatchFinder *Finder) {
45  const auto ImmutableVar =
46  varDecl(anyOf(parmVarDecl(), hasLocalStorage()), hasType(isInteger()),
47  unless(hasType(isVolatileQualified())))
48  .bind(CondVarStr);
49  Finder->addMatcher(
50  ifStmt(
51  hasCondition(anyOf(
52  declRefExpr(hasDeclaration(ImmutableVar)).bind(OuterIfVar1Str),
53  binaryOperator(
54  hasOperatorName("&&"),
55  hasEitherOperand(declRefExpr(hasDeclaration(ImmutableVar))
56  .bind(OuterIfVar2Str))))),
57  hasThen(hasDescendant(
58  ifStmt(hasCondition(anyOf(
59  declRefExpr(hasDeclaration(
60  varDecl(equalsBoundNode(CondVarStr))))
61  .bind(InnerIfVar1Str),
62  binaryOperator(
63  hasAnyOperatorName("&&", "||"),
64  hasEitherOperand(
65  declRefExpr(hasDeclaration(varDecl(
66  equalsBoundNode(CondVarStr))))
67  .bind(InnerIfVar2Str))))))
68  .bind(InnerIfStr))),
69  forFunction(functionDecl().bind(FuncStr)))
70  .bind(OuterIfStr),
71  this);
72  // FIXME: Handle longer conjunctive and disjunctive clauses.
73 }
74 
75 void RedundantBranchConditionCheck::check(const MatchFinder::MatchResult &Result) {
76  const auto *OuterIf = Result.Nodes.getNodeAs<IfStmt>(OuterIfStr);
77  const auto *InnerIf = Result.Nodes.getNodeAs<IfStmt>(InnerIfStr);
78  const auto *CondVar = Result.Nodes.getNodeAs<VarDecl>(CondVarStr);
79  const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>(FuncStr);
80 
81  const DeclRefExpr *OuterIfVar, *InnerIfVar;
82  if (const auto *Inner = Result.Nodes.getNodeAs<DeclRefExpr>(InnerIfVar1Str))
83  InnerIfVar = Inner;
84  else
85  InnerIfVar = Result.Nodes.getNodeAs<DeclRefExpr>(InnerIfVar2Str);
86  if (const auto *Outer = Result.Nodes.getNodeAs<DeclRefExpr>(OuterIfVar1Str))
87  OuterIfVar = Outer;
88  else
89  OuterIfVar = Result.Nodes.getNodeAs<DeclRefExpr>(OuterIfVar2Str);
90 
91  if (OuterIfVar && InnerIfVar) {
92  if (isChangedBefore(OuterIf->getThen(), InnerIfVar, OuterIfVar, CondVar,
93  Result.Context))
94  return;
95 
96  if (isChangedBefore(OuterIf->getCond(), InnerIfVar, OuterIfVar, CondVar,
97  Result.Context))
98  return;
99  }
100 
101  // If the variable has an alias then it can be changed by that alias as well.
102  // FIXME: could potentially support tracking pointers and references in the
103  // future to improve catching true positives through aliases.
104  if (hasPtrOrReferenceInFunc(Func, CondVar))
105  return;
106 
107  auto Diag = diag(InnerIf->getBeginLoc(), "redundant condition %0") << CondVar;
108 
109  // For standalone condition variables and for "or" binary operations we simply
110  // remove the inner `if`.
111  const auto *BinOpCond =
112  dyn_cast<BinaryOperator>(InnerIf->getCond()->IgnoreParenImpCasts());
113 
114  if (isa<DeclRefExpr>(InnerIf->getCond()->IgnoreParenImpCasts()) ||
115  (BinOpCond && BinOpCond->getOpcode() == BO_LOr)) {
116  SourceLocation IfBegin = InnerIf->getBeginLoc();
117  const Stmt *Body = InnerIf->getThen();
118  const Expr *OtherSide = nullptr;
119  if (BinOpCond) {
120  const auto *LeftDRE =
121  dyn_cast<DeclRefExpr>(BinOpCond->getLHS()->IgnoreParenImpCasts());
122  if (LeftDRE && LeftDRE->getDecl() == CondVar)
123  OtherSide = BinOpCond->getRHS();
124  else
125  OtherSide = BinOpCond->getLHS();
126  }
127 
128  SourceLocation IfEnd = Body->getBeginLoc().getLocWithOffset(-1);
129 
130  // For compound statements also remove the left brace.
131  if (isa<CompoundStmt>(Body))
132  IfEnd = Body->getBeginLoc();
133 
134  // If the other side has side effects then keep it.
135  if (OtherSide && OtherSide->HasSideEffects(*Result.Context)) {
136  SourceLocation BeforeOtherSide =
137  OtherSide->getBeginLoc().getLocWithOffset(-1);
138  SourceLocation AfterOtherSide =
139  Lexer::findNextToken(OtherSide->getEndLoc(), *Result.SourceManager,
140  getLangOpts())
141  ->getLocation();
142  Diag << FixItHint::CreateRemoval(
143  CharSourceRange::getTokenRange(IfBegin, BeforeOtherSide))
144  << FixItHint::CreateInsertion(AfterOtherSide, ";")
145  << FixItHint::CreateRemoval(
146  CharSourceRange::getTokenRange(AfterOtherSide, IfEnd));
147  } else {
148  Diag << FixItHint::CreateRemoval(
149  CharSourceRange::getTokenRange(IfBegin, IfEnd));
150  }
151 
152  // For compound statements also remove the right brace at the end.
153  if (isa<CompoundStmt>(Body))
154  Diag << FixItHint::CreateRemoval(
155  CharSourceRange::getTokenRange(Body->getEndLoc(), Body->getEndLoc()));
156 
157  // For "and" binary operations we remove the "and" operation with the
158  // condition variable from the inner if.
159  } else {
160  const auto *CondOp =
161  cast<BinaryOperator>(InnerIf->getCond()->IgnoreParenImpCasts());
162  const auto *LeftDRE =
163  dyn_cast<DeclRefExpr>(CondOp->getLHS()->IgnoreParenImpCasts());
164  if (LeftDRE && LeftDRE->getDecl() == CondVar) {
165  SourceLocation BeforeRHS =
166  CondOp->getRHS()->getBeginLoc().getLocWithOffset(-1);
167  Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
168  CondOp->getLHS()->getBeginLoc(), BeforeRHS));
169  } else {
170  SourceLocation AfterLHS =
171  Lexer::findNextToken(CondOp->getLHS()->getEndLoc(),
172  *Result.SourceManager, getLangOpts())
173  ->getLocation();
174  Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
175  AfterLHS, CondOp->getRHS()->getEndLoc()));
176  }
177  }
178 }
179 
180 } // namespace bugprone
181 } // namespace tidy
182 } // namespace clang
Outer
std::pair< Context, Canceler > Outer
Definition: CancellationTests.cpp:49
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::bugprone::isChangedBefore
static bool isChangedBefore(const Stmt *S, const Stmt *NextS, const Stmt *PrevS, const VarDecl *Var, ASTContext *Context)
Returns whether Var is changed in range (PrevS..NextS).
Definition: RedundantBranchConditionCheck.cpp:33
clang::tidy::bugprone::InnerIfVar2Str
static const char InnerIfVar2Str[]
Definition: RedundantBranchConditionCheck.cpp:29
clang::tidy::bugprone::FuncStr
static const char FuncStr[]
Definition: RedundantBranchConditionCheck.cpp:30
Inner
std::pair< Context, Canceler > Inner
Definition: CancellationTests.cpp:49
clang::tidy::utils::hasPtrOrReferenceInFunc
bool hasPtrOrReferenceInFunc(const Decl *Func, const VarDecl *Var)
Returns whether Var has a pointer or reference in Func.
Definition: Aliasing.cpp:94
clang::tidy::bugprone::OuterIfVar2Str
static const char OuterIfVar2Str[]
Definition: RedundantBranchConditionCheck.cpp:27
clang::clangd::check
bool check(llvm::StringRef File, llvm::function_ref< bool(const Position &)> ShouldCheckLine, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:259
clang::tidy::bugprone::InnerIfStr
static const char InnerIfStr[]
Definition: RedundantBranchConditionCheck.cpp:25
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::bugprone::InnerIfVar1Str
static const char InnerIfVar1Str[]
Definition: RedundantBranchConditionCheck.cpp:28
clang::tidy::bugprone::OuterIfStr
static const char OuterIfStr[]
Definition: RedundantBranchConditionCheck.cpp:24
RedundantBranchConditionCheck.h
clang::tidy::bugprone::CondVarStr
static const char CondVarStr[]
Definition: RedundantBranchConditionCheck.cpp:23
clang::tidy::bugprone::OuterIfVar1Str
static const char OuterIfVar1Str[]
Definition: RedundantBranchConditionCheck.cpp:26