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