xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/NonZeroEnumToBoolConversionCheck.cpp (revision 75adb1226905693ac57e563f675e282e78ac491e)
1 //===--- NonZeroEnumToBoolConversionCheck.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 
9 #include "NonZeroEnumToBoolConversionCheck.h"
10 #include "../utils/Matchers.h"
11 #include "../utils/OptionsUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include <algorithm>
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang::tidy::bugprone {
19 
20 namespace {
21 
AST_MATCHER(EnumDecl,isCompleteAndHasNoZeroValue)22 AST_MATCHER(EnumDecl, isCompleteAndHasNoZeroValue) {
23   const EnumDecl *Definition = Node.getDefinition();
24   return Definition && Node.isComplete() &&
25          std::none_of(Definition->enumerator_begin(),
26                       Definition->enumerator_end(),
27                       [](const EnumConstantDecl *Value) {
28                         return Value->getInitVal().isZero();
29                       });
30 }
31 
32 } // namespace
33 
NonZeroEnumToBoolConversionCheck(StringRef Name,ClangTidyContext * Context)34 NonZeroEnumToBoolConversionCheck::NonZeroEnumToBoolConversionCheck(
35     StringRef Name, ClangTidyContext *Context)
36     : ClangTidyCheck(Name, Context),
37       EnumIgnoreList(
38           utils::options::parseStringList(Options.get("EnumIgnoreList", ""))) {}
39 
storeOptions(ClangTidyOptions::OptionMap & Opts)40 void NonZeroEnumToBoolConversionCheck::storeOptions(
41     ClangTidyOptions::OptionMap &Opts) {
42   Options.store(Opts, "EnumIgnoreList",
43                 utils::options::serializeStringList(EnumIgnoreList));
44 }
45 
isLanguageVersionSupported(const LangOptions & LangOpts) const46 bool NonZeroEnumToBoolConversionCheck::isLanguageVersionSupported(
47     const LangOptions &LangOpts) const {
48   return LangOpts.CPlusPlus;
49 }
50 
registerMatchers(MatchFinder * Finder)51 void NonZeroEnumToBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
52   // Excluding bitwise operators (binary and overload) to avoid false-positives
53   // in code like this 'if (e & SUCCESS) {'.
54   auto ExcludedOperators = binaryOperation(hasAnyOperatorName(
55       "|", "&", "^", "<<", ">>", "~", "|=", "&=", "^=", "<<=", ">>="));
56 
57   Finder->addMatcher(
58       castExpr(hasCastKind(CK_IntegralToBoolean),
59                unless(isExpansionInSystemHeader()), hasType(booleanType()),
60                hasSourceExpression(
61                    expr(hasType(qualType(hasCanonicalType(hasDeclaration(
62                             enumDecl(isCompleteAndHasNoZeroValue(),
63                                      unless(matchers::matchesAnyListedName(
64                                          EnumIgnoreList)))
65                                 .bind("enum"))))),
66                         unless(declRefExpr(to(enumConstantDecl()))),
67                         unless(ignoringParenImpCasts(ExcludedOperators)))),
68                unless(hasAncestor(staticAssertDecl())))
69           .bind("cast"),
70       this);
71 }
72 
check(const MatchFinder::MatchResult & Result)73 void NonZeroEnumToBoolConversionCheck::check(
74     const MatchFinder::MatchResult &Result) {
75   const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("cast");
76   const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("enum");
77 
78   diag(Cast->getExprLoc(), "conversion of %0 into 'bool' will always return "
79                            "'true', enum doesn't have a zero-value enumerator")
80       << Enum;
81   diag(Enum->getLocation(), "enum is defined here", DiagnosticIDs::Note);
82 }
83 
84 } // namespace clang::tidy::bugprone
85