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