brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.2 KiB · f3e94b6 Raw
196 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "InvalidEnumDefaultInitializationCheck.h"10#include "../utils/Matchers.h"11#include "../utils/OptionsUtils.h"12#include "clang/AST/ASTContext.h"13#include "clang/AST/TypeVisitor.h"14#include "clang/ASTMatchers/ASTMatchFinder.h"15#include <algorithm>16 17using namespace clang::ast_matchers;18 19namespace clang::tidy::bugprone {20 21namespace {22 23// Preserve same name as AST_MATCHER(isCompleteAndHasNoZeroValue)24// NOLINTNEXTLINE(llvm-prefer-static-over-anonymous-namespace)25bool isCompleteAndHasNoZeroValue(const EnumDecl *D) {26  const EnumDecl *Definition = D->getDefinition();27  return Definition && Definition->isComplete() &&28         !Definition->enumerators().empty() &&29         llvm::none_of(Definition->enumerators(),30                       [](const EnumConstantDecl *Value) {31                         return Value->getInitVal().isZero();32                       });33}34 35AST_MATCHER(EnumDecl, isCompleteAndHasNoZeroValue) {36  return isCompleteAndHasNoZeroValue(&Node);37}38 39// Find an initialization which initializes the value (if it has enum type) to a40// default zero value.41AST_MATCHER(Expr, isEmptyInit) {42  if (isa<CXXScalarValueInitExpr, ImplicitValueInitExpr>(&Node))43    return true;44  if (const auto *Init = dyn_cast<InitListExpr>(&Node)) {45    if (Init->getNumInits() == 0)46      return true;47  }48  return false;49}50 51AST_MATCHER(InitListExpr, hasArrayFiller) { return Node.hasArrayFiller(); }52 53// Check if any type has a "child" type that is an enum without zero value.54// The "child" type can be an array element type or member type of a record55// type (or a recursive combination of these). In this case, if the "root" type56// is statically initialized, the enum component is initialized to zero.57class FindEnumMember : public TypeVisitor<FindEnumMember, bool> {58public:59  const EnumType *FoundEnum = nullptr;60 61  bool VisitType(const Type *T) {62    const Type *DesT = T->getUnqualifiedDesugaredType();63    if (DesT != T)64      return Visit(DesT);65    return false;66  }67  bool VisitArrayType(const ArrayType *T) {68    return Visit(T->getElementType().getTypePtr());69  }70  bool VisitConstantArrayType(const ConstantArrayType *T) {71    return Visit(T->getElementType().getTypePtr());72  }73  bool VisitEnumType(const EnumType *T) {74    if (isCompleteAndHasNoZeroValue(T->getDecl())) {75      FoundEnum = T;76      return true;77    }78    return false;79  }80  bool VisitRecordType(const RecordType *T) {81    const RecordDecl *RD = T->getDecl()->getDefinition();82    if (!RD || RD->isUnion())83      return false;84    auto VisitField = [this](const FieldDecl *F) {85      return Visit(F->getType().getTypePtr());86    };87    return llvm::any_of(RD->fields(), VisitField);88  }89};90 91} // namespace92 93InvalidEnumDefaultInitializationCheck::InvalidEnumDefaultInitializationCheck(94    StringRef Name, ClangTidyContext *Context)95    : ClangTidyCheck(Name, Context),96      IgnoredEnums(97          utils::options::parseStringList(Options.get("IgnoredEnums", ""))) {98  IgnoredEnums.emplace_back("::std::errc");99}100 101void InvalidEnumDefaultInitializationCheck::storeOptions(102    ClangTidyOptions::OptionMap &Opts) {103  Options.store(Opts, "IgnoredEnums",104                utils::options::serializeStringList(IgnoredEnums));105}106 107void InvalidEnumDefaultInitializationCheck::registerMatchers(108    MatchFinder *Finder) {109  auto EnumWithoutZeroValue = enumType(hasDeclaration(110      enumDecl(isCompleteAndHasNoZeroValue(),111               unless(matchers::matchesAnyListedName(IgnoredEnums)))112          .bind("enum")));113  auto EnumOrArrayOfEnum = qualType(hasUnqualifiedDesugaredType(114      anyOf(EnumWithoutZeroValue,115            arrayType(hasElementType(qualType(116                hasUnqualifiedDesugaredType(EnumWithoutZeroValue)))))));117  Finder->addMatcher(118      expr(isEmptyInit(), hasType(EnumOrArrayOfEnum)).bind("expr"), this);119 120  // Array initialization can contain an "array filler" for the (syntactically)121  // unspecified elements. This expression is not found by AST matchers and can122  // have any type (the array's element type). This is an implicitly generated123  // initialization, so if the type contains somewhere an enum without zero124  // enumerator, the zero initialization applies here. We search this array125  // element type for the specific enum type manually when this matcher matches.126  Finder->addMatcher(initListExpr(hasArrayFiller()).bind("array_filler_expr"),127                     this);128}129 130void InvalidEnumDefaultInitializationCheck::check(131    const MatchFinder::MatchResult &Result) {132  const auto *InitExpr = Result.Nodes.getNodeAs<Expr>("expr");133  const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("enum");134  if (!InitExpr) {135    const auto *InitList =136        Result.Nodes.getNodeAs<InitListExpr>("array_filler_expr");137    // Initialization of omitted array elements with array filler was found.138    // Check the type for enum without zero value.139    // FIXME: In this way only one enum-typed value is found, not all of these.140    FindEnumMember Finder;141    if (!Finder.Visit(InitList->getArrayFiller()->getType().getTypePtr()))142      return;143    InitExpr = InitList;144    Enum = Finder.FoundEnum->getDecl();145  }146 147  if (!InitExpr || !Enum)148    return;149 150  ASTContext &ACtx = Enum->getASTContext();151  SourceLocation Loc = InitExpr->getExprLoc();152  if (Loc.isInvalid()) {153    if (isa<ImplicitValueInitExpr, InitListExpr>(InitExpr)) {154      const DynTypedNodeList Parents = ACtx.getParents(*InitExpr);155      if (Parents.empty())156        return;157 158      if (const auto *Ctor = Parents[0].get<CXXConstructorDecl>()) {159        // Try to find member initializer with the found expression and get the160        // source location from it.161        CXXCtorInitializer *const *CtorInit = std::find_if(162            Ctor->init_begin(), Ctor->init_end(),163            [InitExpr](const CXXCtorInitializer *Init) {164              return Init->isMemberInitializer() && Init->getInit() == InitExpr;165            });166        if (!CtorInit)167          return;168        Loc = (*CtorInit)->getLParenLoc();169      } else if (const auto *InitList = Parents[0].get<InitListExpr>()) {170        // The expression may be implicitly generated for an initialization.171        // Search for a parent initialization list with valid source location.172        while (InitList->getExprLoc().isInvalid()) {173          const DynTypedNodeList Parents = ACtx.getParents(*InitList);174          if (Parents.empty())175            return;176          InitList = Parents[0].get<InitListExpr>();177          if (!InitList)178            return;179        }180        Loc = InitList->getExprLoc();181      }182    }183    // If still not found a source location, omit the warning.184    // Ideally all such cases (if they exist) should be handled to make the185    // check more precise.186    if (Loc.isInvalid())187      return;188  }189  diag(Loc, "enum value of type %0 initialized with invalid value of 0, "190            "enum doesn't have a zero-value enumerator")191      << Enum;192  diag(Enum->getLocation(), "enum is defined here", DiagnosticIDs::Note);193}194 195} // namespace clang::tidy::bugprone196