[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / clang-tools-extra / clang-tidy / performance / EnumSizeCheck.cpp
blob0d44b8c7706c3c49b215809639bfc34f5f2f1d99
1 //===--- EnumSizeCheck.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 //===----------------------------------------------------------------------===//
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>
20 using namespace clang::ast_matchers;
22 namespace clang::tidy::performance {
24 namespace {
26 const std::uint64_t Min8 =
27 std::imaxabs(std::numeric_limits<std::int8_t>::min());
28 const std::uint64_t Max8 = std::numeric_limits<std::int8_t>::max();
29 const std::uint64_t Min16 =
30 std::imaxabs(std::numeric_limits<std::int16_t>::min());
31 const std::uint64_t Max16 = std::numeric_limits<std::int16_t>::max();
32 const std::uint64_t Min32 =
33 std::imaxabs(std::numeric_limits<std::int32_t>::min());
34 const std::uint64_t Max32 = std::numeric_limits<std::int32_t>::max();
36 std::pair<const char *, std::uint32_t>
37 getNewType(std::size_t Size, std::uint64_t Min, std::uint64_t Max) noexcept {
38 if (Min) {
39 if (Min <= Min8 && Max <= Max8) {
40 return {"std::int8_t", sizeof(std::int8_t)};
43 if (Min <= Min16 && Max <= Max16 && Size > sizeof(std::int16_t)) {
44 return {"std::int16_t", sizeof(std::int16_t)};
47 if (Min <= Min32 && Max <= Max32 && Size > sizeof(std::int32_t)) {
48 return {"std::int32_t", sizeof(std::int32_t)};
51 return {};
54 if (Max) {
55 if (Max <= std::numeric_limits<std::uint8_t>::max()) {
56 return {"std::uint8_t", sizeof(std::uint8_t)};
59 if (Max <= std::numeric_limits<std::uint16_t>::max() &&
60 Size > sizeof(std::uint16_t)) {
61 return {"std::uint16_t", sizeof(std::uint16_t)};
64 if (Max <= std::numeric_limits<std::uint32_t>::max() &&
65 Size > sizeof(std::uint32_t)) {
66 return {"std::uint32_t", sizeof(std::uint32_t)};
69 return {};
72 // Zero case
73 return {"std::uint8_t", sizeof(std::uint8_t)};
76 } // namespace
78 EnumSizeCheck::EnumSizeCheck(StringRef Name, ClangTidyContext *Context)
79 : ClangTidyCheck(Name, Context),
80 EnumIgnoreList(
81 utils::options::parseStringList(Options.get("EnumIgnoreList", ""))) {}
83 void EnumSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
84 Options.store(Opts, "EnumIgnoreList",
85 utils::options::serializeStringList(EnumIgnoreList));
88 bool EnumSizeCheck::isLanguageVersionSupported(
89 const LangOptions &LangOpts) const {
90 return LangOpts.CPlusPlus11;
93 void EnumSizeCheck::registerMatchers(MatchFinder *Finder) {
94 Finder->addMatcher(
95 enumDecl(unless(isExpansionInSystemHeader()), isDefinition(),
96 unless(matchers::matchesAnyListedName(EnumIgnoreList)))
97 .bind("e"),
98 this);
101 void EnumSizeCheck::check(const MatchFinder::MatchResult &Result) {
102 const auto *MatchedDecl = Result.Nodes.getNodeAs<EnumDecl>("e");
103 const QualType BaseType = MatchedDecl->getIntegerType().getCanonicalType();
104 if (!BaseType->isIntegerType())
105 return;
107 const std::uint32_t Size = Result.Context->getTypeSize(BaseType) / 8U;
108 if (1U == Size)
109 return;
111 std::uint64_t MinV = 0U;
112 std::uint64_t MaxV = 0U;
114 for (const auto &It : MatchedDecl->enumerators()) {
115 const llvm::APSInt &InitVal = It->getInitVal();
116 if ((InitVal.isUnsigned() || InitVal.isNonNegative())) {
117 MaxV = std::max<std::uint64_t>(MaxV, InitVal.getZExtValue());
118 } else {
119 MinV = std::max<std::uint64_t>(MinV, InitVal.abs().getZExtValue());
123 auto NewType = getNewType(Size, MinV, MaxV);
124 if (!NewType.first || Size <= NewType.second)
125 return;
127 diag(MatchedDecl->getLocation(),
128 "enum %0 uses a larger base type (%1, size: %2 %select{byte|bytes}5) "
129 "than necessary for its value set, consider using '%3' (%4 "
130 "%select{byte|bytes}6) as the base type to reduce its size")
131 << MatchedDecl << MatchedDecl->getIntegerType() << Size << NewType.first
132 << NewType.second << (Size > 1U) << (NewType.second > 1U);
135 } // namespace clang::tidy::performance