clang-tools  7.0.0
MultiwayPathsCoveredCheck.cpp
Go to the documentation of this file.
1 //===--- MultiwayPathsCoveredCheck.cpp - clang-tidy------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
11 #include "clang/AST/ASTContext.h"
12 
13 #include <limits>
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace hicpp {
20 
21 void MultiwayPathsCoveredCheck::storeOptions(
23  Options.store(Opts, "WarnOnMissingElse", WarnOnMissingElse);
24 }
25 
26 void MultiwayPathsCoveredCheck::registerMatchers(MatchFinder *Finder) {
27  Finder->addMatcher(
28  switchStmt(
29  hasCondition(allOf(
30  // Match on switch statements that have either a bit-field or
31  // an integer condition. The ordering in 'anyOf()' is
32  // important because the last condition is the most general.
33  anyOf(ignoringImpCasts(memberExpr(hasDeclaration(
34  fieldDecl(isBitField()).bind("bitfield")))),
35  ignoringImpCasts(declRefExpr().bind("non-enum-condition"))),
36  // 'unless()' must be the last match here and must be bound,
37  // otherwise the matcher does not work correctly, because it
38  // will not explicitly ignore enum conditions.
39  unless(ignoringImpCasts(
40  declRefExpr(hasType(enumType())).bind("enum-condition"))))))
41  .bind("switch"),
42  this);
43 
44  // This option is noisy, therefore matching is configurable.
45  if (WarnOnMissingElse) {
46  Finder->addMatcher(
47  ifStmt(allOf(hasParent(ifStmt()), unless(hasElse(anything()))))
48  .bind("else-if"),
49  this);
50  }
51 }
52 
53 static std::pair<std::size_t, bool> countCaseLabels(const SwitchStmt *Switch) {
54  std::size_t CaseCount = 0;
55  bool HasDefault = false;
56 
57  const SwitchCase *CurrentCase = Switch->getSwitchCaseList();
58  while (CurrentCase) {
59  ++CaseCount;
60  if (isa<DefaultStmt>(CurrentCase))
61  HasDefault = true;
62 
63  CurrentCase = CurrentCase->getNextSwitchCase();
64  }
65 
66  return std::make_pair(CaseCount, HasDefault);
67 }
68 
69 /// This function calculate 2 ** Bits and returns
70 /// numeric_limits<std::size_t>::max() if an overflow occured.
71 static std::size_t twoPow(std::size_t Bits) {
72  return Bits >= std::numeric_limits<std::size_t>::digits
73  ? std::numeric_limits<std::size_t>::max()
74  : static_cast<size_t>(1) << Bits;
75 }
76 
77 /// Get the number of possible values that can be switched on for the type T.
78 ///
79 /// \return - 0 if bitcount could not be determined
80 /// - numeric_limits<std::size_t>::max() when overflow appeared due to
81 /// more than 64 bits type size.
82 static std::size_t getNumberOfPossibleValues(QualType T,
83  const ASTContext &Context) {
84  // `isBooleanType` must come first because `bool` is an integral type as well
85  // and would not return 2 as result.
86  if (T->isBooleanType())
87  return 2;
88  else if (T->isIntegralType(Context))
89  return twoPow(Context.getTypeSize(T));
90  else
91  return 1;
92 }
93 
94 void MultiwayPathsCoveredCheck::check(const MatchFinder::MatchResult &Result) {
95  if (const auto *ElseIfWithoutElse =
96  Result.Nodes.getNodeAs<IfStmt>("else-if")) {
97  diag(ElseIfWithoutElse->getLocStart(),
98  "potentially uncovered codepath; add an ending else statement");
99  return;
100  }
101  const auto *Switch = Result.Nodes.getNodeAs<SwitchStmt>("switch");
102  std::size_t SwitchCaseCount;
103  bool SwitchHasDefault;
104  std::tie(SwitchCaseCount, SwitchHasDefault) = countCaseLabels(Switch);
105 
106  // Checks the sanity of 'switch' statements that actually do define
107  // a default branch but might be degenerated by having no or only one case.
108  if (SwitchHasDefault) {
109  handleSwitchWithDefault(Switch, SwitchCaseCount);
110  return;
111  }
112  // Checks all 'switch' statements that do not define a default label.
113  // Here the heavy lifting happens.
114  if (!SwitchHasDefault && SwitchCaseCount > 0) {
115  handleSwitchWithoutDefault(Switch, SwitchCaseCount, Result);
116  return;
117  }
118  // Warns for degenerated 'switch' statements that neither define a case nor
119  // a default label.
120  // FIXME: Evaluate, if emitting a fix-it to simplify that statement is
121  // reasonable.
122  if (!SwitchHasDefault && SwitchCaseCount == 0) {
123  diag(Switch->getLocStart(),
124  "switch statement without labels has no effect");
125  return;
126  }
127  llvm_unreachable("matched a case, that was not explicitly handled");
128 }
129 
130 void MultiwayPathsCoveredCheck::handleSwitchWithDefault(
131  const SwitchStmt *Switch, std::size_t CaseCount) {
132  assert(CaseCount > 0 && "Switch statement with supposedly one default "
133  "branch did not contain any case labels");
134  if (CaseCount == 1 || CaseCount == 2)
135  diag(Switch->getLocStart(),
136  CaseCount == 1
137  ? "degenerated switch with default label only"
138  : "switch could be better written as an if/else statement");
139 }
140 
141 void MultiwayPathsCoveredCheck::handleSwitchWithoutDefault(
142  const SwitchStmt *Switch, std::size_t CaseCount,
143  const MatchFinder::MatchResult &Result) {
144  // The matcher only works because some nodes are explicitly matched and
145  // bound but ignored. This is necessary to build the excluding logic for
146  // enums and 'switch' statements without a 'default' branch.
147  assert(!Result.Nodes.getNodeAs<DeclRefExpr>("enum-condition") &&
148  "switch over enum is handled by warnings already, explicitly ignoring "
149  "them");
150  // Determine the number of case labels. Because 'default' is not present
151  // and duplicating case labels is not allowed this number represents
152  // the number of codepaths. It can be directly compared to 'MaxPathsPossible'
153  // to see if some cases are missing.
154  // CaseCount == 0 is caught in DegenerateSwitch. Necessary because the
155  // matcher used for here does not match on degenerate 'switch'.
156  assert(CaseCount > 0 && "Switch statement without any case found. This case "
157  "should be excluded by the matcher and is handled "
158  "separatly.");
159  std::size_t MaxPathsPossible = [&]() {
160  if (const auto *GeneralCondition =
161  Result.Nodes.getNodeAs<DeclRefExpr>("non-enum-condition")) {
162  return getNumberOfPossibleValues(GeneralCondition->getType(),
163  *Result.Context);
164  }
165  if (const auto *BitfieldDecl =
166  Result.Nodes.getNodeAs<FieldDecl>("bitfield")) {
167  return twoPow(BitfieldDecl->getBitWidthValue(*Result.Context));
168  }
169 
170  return static_cast<std::size_t>(0);
171  }();
172 
173  // FIXME: Transform the 'switch' into an 'if' for CaseCount == 1.
174  if (CaseCount < MaxPathsPossible)
175  diag(Switch->getLocStart(),
176  CaseCount == 1 ? "switch with only one case; use an if statement"
177  : "potential uncovered code path; add a default label");
178 }
179 } // namespace hicpp
180 } // namespace tidy
181 } // namespace clang
static std::pair< std::size_t, bool > countCaseLabels(const SwitchStmt *Switch)
std::map< std::string, std::string > OptionMap
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static std::size_t getNumberOfPossibleValues(QualType T, const ASTContext &Context)
Get the number of possible values that can be switched on for the type T.
static std::size_t twoPow(std::size_t Bits)
This function calculate 2 ** Bits and returns numeric_limits<std::size_t>::max() if an overflow occur...