//===--- NonZeroEnumToBoolConversionCheck.cpp - clang-tidy ----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "NonZeroEnumToBoolConversionCheck.h" #include "../utils/Matchers.h" #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include using namespace clang::ast_matchers; namespace clang::tidy::bugprone { namespace { AST_MATCHER(EnumDecl, isCompleteAndHasNoZeroValue) { const EnumDecl *Definition = Node.getDefinition(); return Definition && Node.isComplete() && std::none_of(Definition->enumerator_begin(), Definition->enumerator_end(), [](const EnumConstantDecl *Value) { return Value->getInitVal().isZero(); }); } } // namespace NonZeroEnumToBoolConversionCheck::NonZeroEnumToBoolConversionCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), EnumIgnoreList( utils::options::parseStringList(Options.get("EnumIgnoreList", ""))) {} void NonZeroEnumToBoolConversionCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "EnumIgnoreList", utils::options::serializeStringList(EnumIgnoreList)); } bool NonZeroEnumToBoolConversionCheck::isLanguageVersionSupported( const LangOptions &LangOpts) const { return LangOpts.CPlusPlus; } void NonZeroEnumToBoolConversionCheck::registerMatchers(MatchFinder *Finder) { // Excluding bitwise operators (binary and overload) to avoid false-positives // in code like this 'if (e & SUCCESS) {'. auto ExcludedOperators = binaryOperation(hasAnyOperatorName( "|", "&", "^", "<<", ">>", "~", "|=", "&=", "^=", "<<=", ">>=")); Finder->addMatcher( castExpr(hasCastKind(CK_IntegralToBoolean), unless(isExpansionInSystemHeader()), hasType(booleanType()), hasSourceExpression( expr(hasType(qualType(hasCanonicalType(hasDeclaration( enumDecl(isCompleteAndHasNoZeroValue(), unless(matchers::matchesAnyListedName( EnumIgnoreList))) .bind("enum"))))), unless(declRefExpr(to(enumConstantDecl()))), unless(ignoringImplicit(ExcludedOperators)))), unless(hasAncestor(staticAssertDecl()))) .bind("cast"), this); } void NonZeroEnumToBoolConversionCheck::check( const MatchFinder::MatchResult &Result) { const auto *Cast = Result.Nodes.getNodeAs("cast"); const auto *Enum = Result.Nodes.getNodeAs("enum"); diag(Cast->getExprLoc(), "conversion of %0 into 'bool' will always return " "'true', enum doesn't have a zero-value enumerator") << Enum; diag(Enum->getLocation(), "enum is defined here", DiagnosticIDs::Note); } } // namespace clang::tidy::bugprone