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