brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.9 KiB · 049ad75 Raw
210 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 "EnumInitialValueCheck.h"10#include "../utils/LexerUtils.h"11#include "clang/AST/Decl.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/ASTMatchers/ASTMatchers.h"14#include "clang/Basic/Diagnostic.h"15#include "clang/Basic/SourceLocation.h"16#include "llvm/ADT/STLExtras.h"17#include "llvm/ADT/SmallString.h"18 19using namespace clang::ast_matchers;20 21namespace clang::tidy::readability {22 23static bool isNoneEnumeratorsInitialized(const EnumDecl &Node) {24  return llvm::all_of(Node.enumerators(), [](const EnumConstantDecl *ECD) {25    return ECD->getInitExpr() == nullptr;26  });27}28 29static bool isOnlyFirstEnumeratorInitialized(const EnumDecl &Node) {30  bool IsFirst = true;31  for (const EnumConstantDecl *ECD : Node.enumerators()) {32    if ((IsFirst && ECD->getInitExpr() == nullptr) ||33        (!IsFirst && ECD->getInitExpr() != nullptr))34      return false;35    IsFirst = false;36  }37  return !IsFirst;38}39 40static bool areAllEnumeratorsInitialized(const EnumDecl &Node) {41  return llvm::all_of(Node.enumerators(), [](const EnumConstantDecl *ECD) {42    return ECD->getInitExpr() != nullptr;43  });44}45 46/// Check if \p Enumerator is initialized with a (potentially negated) \c47/// IntegerLiteral.48static bool isInitializedByLiteral(const EnumConstantDecl *Enumerator) {49  const Expr *const Init = Enumerator->getInitExpr();50  if (!Init)51    return false;52  return Init->isIntegerConstantExpr(Enumerator->getASTContext());53}54 55static void cleanInitialValue(DiagnosticBuilder &Diag,56                              const EnumConstantDecl *ECD,57                              const SourceManager &SM,58                              const LangOptions &LangOpts) {59  const SourceRange InitExprRange = ECD->getInitExpr()->getSourceRange();60  if (InitExprRange.isInvalid() || InitExprRange.getBegin().isMacroID() ||61      InitExprRange.getEnd().isMacroID())62    return;63  std::optional<Token> EqualToken = utils::lexer::findNextTokenSkippingComments(64      ECD->getLocation(), SM, LangOpts);65  if (!EqualToken.has_value() ||66      EqualToken.value().getKind() != tok::TokenKind::equal)67    return;68  const SourceLocation EqualLoc{EqualToken->getLocation()};69  if (EqualLoc.isInvalid() || EqualLoc.isMacroID())70    return;71  Diag << FixItHint::CreateRemoval(EqualLoc)72       << FixItHint::CreateRemoval(InitExprRange);73}74 75namespace {76 77AST_MATCHER(EnumDecl, isMacro) {78  const SourceLocation Loc = Node.getBeginLoc();79  return Loc.isMacroID();80}81 82AST_MATCHER(EnumDecl, hasConsistentInitialValues) {83  return isNoneEnumeratorsInitialized(Node) ||84         isOnlyFirstEnumeratorInitialized(Node) ||85         areAllEnumeratorsInitialized(Node);86}87 88AST_MATCHER(EnumDecl, hasZeroInitialValueForFirstEnumerator) {89  const EnumDecl::enumerator_range Enumerators = Node.enumerators();90  if (Enumerators.empty())91    return false;92  const EnumConstantDecl *ECD = *Enumerators.begin();93  return isOnlyFirstEnumeratorInitialized(Node) &&94         isInitializedByLiteral(ECD) && ECD->getInitVal().isZero();95}96 97/// Excludes bitfields because enumerators initialized with the result of a98/// bitwise operator on enumeration values or any other expr that is not a99/// potentially negative integer literal.100/// Enumerations where it is not directly clear if they are used with101/// bitmask, evident when enumerators are only initialized with (potentially102/// negative) integer literals, are ignored. This is also the case when all103/// enumerators are powers of two (e.g., 0, 1, 2).104AST_MATCHER(EnumDecl, hasSequentialInitialValues) {105  const EnumDecl::enumerator_range Enumerators = Node.enumerators();106  if (Enumerators.empty())107    return false;108  const EnumConstantDecl *const FirstEnumerator = *Node.enumerator_begin();109  llvm::APSInt PrevValue = FirstEnumerator->getInitVal();110  if (!isInitializedByLiteral(FirstEnumerator))111    return false;112  bool AllEnumeratorsArePowersOfTwo = true;113  for (const EnumConstantDecl *Enumerator : llvm::drop_begin(Enumerators)) {114    const llvm::APSInt NewValue = Enumerator->getInitVal();115    if (NewValue != ++PrevValue)116      return false;117    if (!isInitializedByLiteral(Enumerator))118      return false;119    PrevValue = NewValue;120    AllEnumeratorsArePowersOfTwo &= NewValue.isPowerOf2();121  }122  return !AllEnumeratorsArePowersOfTwo;123}124 125} // namespace126 127static std::string getName(const EnumDecl *Decl) {128  if (!Decl->getDeclName())129    return "<unnamed>";130 131  return Decl->getQualifiedNameAsString();132}133 134EnumInitialValueCheck::EnumInitialValueCheck(StringRef Name,135                                             ClangTidyContext *Context)136    : ClangTidyCheck(Name, Context),137      AllowExplicitZeroFirstInitialValue(138          Options.get("AllowExplicitZeroFirstInitialValue", true)),139      AllowExplicitSequentialInitialValues(140          Options.get("AllowExplicitSequentialInitialValues", true)) {}141 142void EnumInitialValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {143  Options.store(Opts, "AllowExplicitZeroFirstInitialValue",144                AllowExplicitZeroFirstInitialValue);145  Options.store(Opts, "AllowExplicitSequentialInitialValues",146                AllowExplicitSequentialInitialValues);147}148 149void EnumInitialValueCheck::registerMatchers(MatchFinder *Finder) {150  Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),151                              unless(hasConsistentInitialValues()))152                         .bind("inconsistent"),153                     this);154  if (!AllowExplicitZeroFirstInitialValue)155    Finder->addMatcher(156        enumDecl(isDefinition(), hasZeroInitialValueForFirstEnumerator())157            .bind("zero_first"),158        this);159  if (!AllowExplicitSequentialInitialValues)160    Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),161                                hasSequentialInitialValues())162                           .bind("sequential"),163                       this);164}165 166void EnumInitialValueCheck::check(const MatchFinder::MatchResult &Result) {167  if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("inconsistent")) {168    const DiagnosticBuilder Diag =169        diag(170            Enum->getBeginLoc(),171            "initial values in enum '%0' are not consistent, consider explicit "172            "initialization of all, none or only the first enumerator")173        << getName(Enum);174    for (const EnumConstantDecl *ECD : Enum->enumerators())175      if (ECD->getInitExpr() == nullptr) {176        const SourceLocation EndLoc = Lexer::getLocForEndOfToken(177            ECD->getLocation(), 0, *Result.SourceManager, getLangOpts());178        if (EndLoc.isMacroID())179          continue;180        llvm::SmallString<8> Str{" = "};181        ECD->getInitVal().toString(Str);182        Diag << FixItHint::CreateInsertion(EndLoc, Str);183      }184    return;185  }186 187  if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("zero_first")) {188    const EnumConstantDecl *ECD = *Enum->enumerator_begin();189    const SourceLocation Loc = ECD->getLocation();190    if (Loc.isInvalid() || Loc.isMacroID())191      return;192    DiagnosticBuilder Diag = diag(Loc, "zero initial value for the first "193                                       "enumerator in '%0' can be disregarded")194                             << getName(Enum);195    cleanInitialValue(Diag, ECD, *Result.SourceManager, getLangOpts());196    return;197  }198  if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("sequential")) {199    DiagnosticBuilder Diag =200        diag(Enum->getBeginLoc(),201             "sequential initial value in '%0' can be ignored")202        << getName(Enum);203    for (const EnumConstantDecl *ECD : llvm::drop_begin(Enum->enumerators()))204      cleanInitialValue(Diag, ECD, *Result.SourceManager, getLangOpts());205    return;206  }207}208 209} // namespace clang::tidy::readability210