brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.4 KiB · 03981c1 Raw
138 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 "EnumSizeCheck.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#include <cinttypes>16#include <cstdint>17#include <limits>18#include <utility>19 20using namespace clang::ast_matchers;21 22namespace clang::tidy::performance {23 24namespace {25 26AST_MATCHER(EnumDecl, hasEnumerators) { return !Node.enumerators().empty(); }27 28const std::uint64_t Min8 =29    std::imaxabs(std::numeric_limits<std::int8_t>::min());30const std::uint64_t Max8 = std::numeric_limits<std::int8_t>::max();31const std::uint64_t Min16 =32    std::imaxabs(std::numeric_limits<std::int16_t>::min());33const std::uint64_t Max16 = std::numeric_limits<std::int16_t>::max();34const std::uint64_t Min32 =35    std::imaxabs(std::numeric_limits<std::int32_t>::min());36const std::uint64_t Max32 = std::numeric_limits<std::int32_t>::max();37 38} // namespace39static std::pair<const char *, std::uint32_t>40getNewType(std::size_t Size, std::uint64_t Min, std::uint64_t Max) noexcept {41  if (Min) {42    if (Min <= Min8 && Max <= Max8) {43      return {"std::int8_t", sizeof(std::int8_t)};44    }45 46    if (Min <= Min16 && Max <= Max16 && Size > sizeof(std::int16_t)) {47      return {"std::int16_t", sizeof(std::int16_t)};48    }49 50    if (Min <= Min32 && Max <= Max32 && Size > sizeof(std::int32_t)) {51      return {"std::int32_t", sizeof(std::int32_t)};52    }53 54    return {};55  }56 57  if (Max) {58    if (Max <= std::numeric_limits<std::uint8_t>::max()) {59      return {"std::uint8_t", sizeof(std::uint8_t)};60    }61 62    if (Max <= std::numeric_limits<std::uint16_t>::max() &&63        Size > sizeof(std::uint16_t)) {64      return {"std::uint16_t", sizeof(std::uint16_t)};65    }66 67    if (Max <= std::numeric_limits<std::uint32_t>::max() &&68        Size > sizeof(std::uint32_t)) {69      return {"std::uint32_t", sizeof(std::uint32_t)};70    }71 72    return {};73  }74 75  // Zero case76  return {"std::uint8_t", sizeof(std::uint8_t)};77}78 79EnumSizeCheck::EnumSizeCheck(StringRef Name, ClangTidyContext *Context)80    : ClangTidyCheck(Name, Context),81      EnumIgnoreList(82          utils::options::parseStringList(Options.get("EnumIgnoreList", ""))) {}83 84void EnumSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {85  Options.store(Opts, "EnumIgnoreList",86                utils::options::serializeStringList(EnumIgnoreList));87}88 89bool EnumSizeCheck::isLanguageVersionSupported(90    const LangOptions &LangOpts) const {91  return LangOpts.CPlusPlus11;92}93 94void EnumSizeCheck::registerMatchers(MatchFinder *Finder) {95  Finder->addMatcher(96      enumDecl(unless(isExpansionInSystemHeader()), isDefinition(),97               hasEnumerators(),98               unless(matchers::matchesAnyListedName(EnumIgnoreList)))99          .bind("e"),100      this);101}102 103void EnumSizeCheck::check(const MatchFinder::MatchResult &Result) {104  const auto *MatchedDecl = Result.Nodes.getNodeAs<EnumDecl>("e");105  const QualType BaseType = MatchedDecl->getIntegerType().getCanonicalType();106  if (!BaseType->isIntegerType())107    return;108 109  const std::uint32_t Size = Result.Context->getTypeSize(BaseType) / 8U;110  if (1U == Size)111    return;112 113  std::uint64_t MinV = 0U;114  std::uint64_t MaxV = 0U;115 116  for (const auto &It : MatchedDecl->enumerators()) {117    const llvm::APSInt &InitVal = It->getInitVal();118    if ((InitVal.isUnsigned() || InitVal.isNonNegative())) {119      MaxV = std::max<std::uint64_t>(MaxV, InitVal.getZExtValue());120    } else {121      MinV = std::max<std::uint64_t>(MinV, InitVal.abs().getZExtValue());122    }123  }124 125  auto NewType = getNewType(Size, MinV, MaxV);126  if (!NewType.first || Size <= NewType.second)127    return;128 129  diag(MatchedDecl->getLocation(),130       "enum %0 uses a larger base type (%1, size: %2 %select{byte|bytes}5) "131       "than necessary for its value set, consider using '%3' (%4 "132       "%select{byte|bytes}6) as the base type to reduce its size")133      << MatchedDecl << MatchedDecl->getIntegerType() << Size << NewType.first134      << NewType.second << (Size > 1U) << (NewType.second > 1U);135}136 137} // namespace clang::tidy::performance138