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