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
16using namespace clang::ast_matchers;
17
18namespace clang::tidy::bugprone {
19
20namespace {
21
22AST_MATCHER(EnumDecl, isCompleteAndHasNoZeroValue) {
23 const EnumDecl *Definition = Node.getDefinition();
24 return Definition && Node.isComplete() &&
25 std::none_of(first: Definition->enumerator_begin(),
26 last: Definition->enumerator_end(),
27 pred: [](const EnumConstantDecl *Value) {
28 return Value->getInitVal().isZero();
29 });
30}
31
32} // namespace
33
34NonZeroEnumToBoolConversionCheck::NonZeroEnumToBoolConversionCheck(
35 StringRef Name, ClangTidyContext *Context)
36 : ClangTidyCheck(Name, Context),
37 EnumIgnoreList(
38 utils::options::parseStringList(Option: Options.get(LocalName: "EnumIgnoreList", Default: ""))) {}
39
40void NonZeroEnumToBoolConversionCheck::storeOptions(
41 ClangTidyOptions::OptionMap &Opts) {
42 Options.store(Options&: Opts, LocalName: "EnumIgnoreList",
43 Value: utils::options::serializeStringList(Strings: EnumIgnoreList));
44}
45
46bool NonZeroEnumToBoolConversionCheck::isLanguageVersionSupported(
47 const LangOptions &LangOpts) const {
48 return LangOpts.CPlusPlus;
49}
50
51void 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 NodeMatch: castExpr(hasCastKind(Kind: CK_IntegralToBoolean),
59 unless(isExpansionInSystemHeader()), hasType(InnerMatcher: booleanType()),
60 hasSourceExpression(
61 InnerMatcher: expr(hasType(InnerMatcher: qualType(hasCanonicalType(InnerMatcher: hasDeclaration(
62 InnerMatcher: enumDecl(isCompleteAndHasNoZeroValue(),
63 unless(matchers::matchesAnyListedName(
64 NameList: EnumIgnoreList)))
65 .bind(ID: "enum"))))),
66 unless(declRefExpr(to(InnerMatcher: enumConstantDecl()))),
67 unless(ignoringParenImpCasts(InnerMatcher: ExcludedOperators)))),
68 unless(hasAncestor(staticAssertDecl())))
69 .bind(ID: "cast"),
70 Action: this);
71}
72
73void NonZeroEnumToBoolConversionCheck::check(
74 const MatchFinder::MatchResult &Result) {
75 const auto *Cast = Result.Nodes.getNodeAs<CastExpr>(ID: "cast");
76 const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(ID: "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

source code of clang-tools-extra/clang-tidy/bugprone/NonZeroEnumToBoolConversionCheck.cpp