1 //===--- EnumSizeCheck.cpp - clang-tidy -----------------------------------===//
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
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"
20 using namespace clang::ast_matchers
;
22 namespace clang::tidy::performance
{
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
{
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)};
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)};
73 return {"std::uint8_t", sizeof(std::uint8_t)};
78 EnumSizeCheck::EnumSizeCheck(StringRef Name
, ClangTidyContext
*Context
)
79 : ClangTidyCheck(Name
, Context
),
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
) {
95 enumDecl(unless(isExpansionInSystemHeader()), isDefinition(),
96 unless(matchers::matchesAnyListedName(EnumIgnoreList
)))
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())
107 const std::uint32_t Size
= Result
.Context
->getTypeSize(BaseType
) / 8U;
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());
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
)
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