Skip to content

Commit b9306fd

Browse files
committed
[clang-tidy] Reworked enum options handling(again)
Following on from D77085, I was never happy with the passing a mapping to the option get/store functions. This patch addresses this by using explicit specializations to handle the serializing and deserializing of enum options. Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D82188
1 parent abafb65 commit b9306fd

19 files changed

+164
-139
lines changed

clang-tools-extra/clang-tidy/ClangTidyCheck.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ void ClangTidyCheck::OptionsView::store(ClangTidyOptions::OptionMap &Options,
158158
}
159159

160160
llvm::Expected<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
161-
StringRef LocalName, ArrayRef<std::pair<StringRef, int64_t>> Mapping,
161+
StringRef LocalName, ArrayRef<std::pair<int64_t, StringRef>> Mapping,
162162
bool CheckGlobal, bool IgnoreCase) {
163163
auto Iter = CheckOptions.find((NamePrefix + LocalName).str());
164164
if (CheckGlobal && Iter == CheckOptions.end())
@@ -171,19 +171,19 @@ llvm::Expected<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
171171
unsigned EditDistance = -1;
172172
for (const auto &NameAndEnum : Mapping) {
173173
if (IgnoreCase) {
174-
if (Value.equals_lower(NameAndEnum.first))
175-
return NameAndEnum.second;
176-
} else if (Value.equals(NameAndEnum.first)) {
177-
return NameAndEnum.second;
178-
} else if (Value.equals_lower(NameAndEnum.first)) {
179-
Closest = NameAndEnum.first;
174+
if (Value.equals_lower(NameAndEnum.second))
175+
return NameAndEnum.first;
176+
} else if (Value.equals(NameAndEnum.second)) {
177+
return NameAndEnum.first;
178+
} else if (Value.equals_lower(NameAndEnum.second)) {
179+
Closest = NameAndEnum.second;
180180
EditDistance = 0;
181181
continue;
182182
}
183-
unsigned Distance = Value.edit_distance(NameAndEnum.first);
183+
unsigned Distance = Value.edit_distance(NameAndEnum.second);
184184
if (Distance < EditDistance) {
185185
EditDistance = Distance;
186-
Closest = NameAndEnum.first;
186+
Closest = NameAndEnum.second;
187187
}
188188
}
189189
if (EditDistance < 3)

clang-tools-extra/clang-tidy/ClangTidyCheck.h

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ class CompilerInstance;
2727

2828
namespace tidy {
2929

30+
/// This class should be specialized by any enum type that needs to be converted
31+
/// to and from an \ref llvm::StringRef.
32+
template <class T> struct OptionEnumMapping {
33+
// Specializations of this struct must implement this function.
34+
static ArrayRef<std::pair<T, StringRef>> getEnumMapping() = delete;
35+
};
36+
3037
template <typename T> class OptionError : public llvm::ErrorInfo<T> {
3138
std::error_code convertToErrorCode() const override {
3239
return llvm::inconvertibleErrorCode();
@@ -313,77 +320,80 @@ class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
313320
}
314321

315322
/// Read a named option from the ``Context`` and parse it as an
316-
/// enum type ``T`` using the \p Mapping provided. If \p IgnoreCase is set,
317-
/// it will search the mapping ignoring the case.
323+
/// enum type ``T``.
318324
///
319325
/// Reads the option with the check-local name \p LocalName from the
320326
/// ``CheckOptions``. If the corresponding key is not present, returns a
321327
/// ``MissingOptionError``. If the key can't be parsed as a ``T`` returns a
322328
/// ``UnparseableEnumOptionError``.
329+
///
330+
/// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
331+
/// supply the mapping required to convert between ``T`` and a string.
323332
template <typename T>
324333
std::enable_if_t<std::is_enum<T>::value, llvm::Expected<T>>
325-
get(StringRef LocalName, ArrayRef<std::pair<StringRef, T>> Mapping,
326-
bool IgnoreCase = false) {
327-
if (llvm::Expected<int64_t> ValueOr = getEnumInt(
328-
LocalName, typeEraseMapping(Mapping), false, IgnoreCase))
334+
get(StringRef LocalName, bool IgnoreCase = false) {
335+
if (llvm::Expected<int64_t> ValueOr =
336+
getEnumInt(LocalName, typeEraseMapping<T>(), false, IgnoreCase))
329337
return static_cast<T>(*ValueOr);
330338
else
331339
return std::move(ValueOr.takeError());
332340
}
333341

334342
/// Read a named option from the ``Context`` and parse it as an
335-
/// enum type ``T`` using the \p Mapping provided. If \p IgnoreCase is set,
336-
/// it will search the mapping ignoring the case.
343+
/// enum type ``T``.
337344
///
338345
/// Reads the option with the check-local name \p LocalName from the
339346
/// ``CheckOptions``. If the corresponding key is not present or it can't be
340347
/// parsed as a ``T``, returns \p Default.
348+
///
349+
/// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
350+
/// supply the mapping required to convert between ``T`` and a string.
341351
template <typename T>
342352
std::enable_if_t<std::is_enum<T>::value, T>
343-
get(StringRef LocalName, ArrayRef<std::pair<StringRef, T>> Mapping,
344-
T Default, bool IgnoreCase = false) {
345-
if (auto ValueOr = get(LocalName, Mapping, IgnoreCase))
353+
get(StringRef LocalName, T Default, bool IgnoreCase = false) {
354+
if (auto ValueOr = get<T>(LocalName, IgnoreCase))
346355
return *ValueOr;
347356
else
348357
logErrToStdErr(ValueOr.takeError());
349358
return Default;
350359
}
351360

352361
/// Read a named option from the ``Context`` and parse it as an
353-
/// enum type ``T`` using the \p Mapping provided. If \p IgnoreCase is set,
354-
/// it will search the mapping ignoring the case.
362+
/// enum type ``T``.
355363
///
356364
/// Reads the option with the check-local name \p LocalName from local or
357365
/// global ``CheckOptions``. Gets local option first. If local is not
358366
/// present, falls back to get global option. If global option is not
359367
/// present either, returns a ``MissingOptionError``. If the key can't be
360368
/// parsed as a ``T`` returns a ``UnparseableEnumOptionError``.
369+
///
370+
/// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
371+
/// supply the mapping required to convert between ``T`` and a string.
361372
template <typename T>
362373
std::enable_if_t<std::is_enum<T>::value, llvm::Expected<T>>
363374
getLocalOrGlobal(StringRef LocalName,
364-
ArrayRef<std::pair<StringRef, T>> Mapping,
365375
bool IgnoreCase = false) {
366-
if (llvm::Expected<int64_t> ValueOr = getEnumInt(
367-
LocalName, typeEraseMapping(Mapping), true, IgnoreCase))
376+
if (llvm::Expected<int64_t> ValueOr =
377+
getEnumInt(LocalName, typeEraseMapping<T>(), true, IgnoreCase))
368378
return static_cast<T>(*ValueOr);
369379
else
370380
return std::move(ValueOr.takeError());
371381
}
372382

373383
/// Read a named option from the ``Context`` and parse it as an
374-
/// enum type ``T`` using the \p Mapping provided. If \p IgnoreCase is set,
375-
/// it will search the mapping ignoring the case.
384+
/// enum type ``T``.
376385
///
377386
/// Reads the option with the check-local name \p LocalName from local or
378387
/// global ``CheckOptions``. Gets local option first. If local is not
379388
/// present, falls back to get global option. If global option is not
380389
/// present either or it can't be parsed as a ``T``, returns \p Default.
390+
///
391+
/// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
392+
/// supply the mapping required to convert between ``T`` and a string.
381393
template <typename T>
382394
std::enable_if_t<std::is_enum<T>::value, T>
383-
getLocalOrGlobal(StringRef LocalName,
384-
ArrayRef<std::pair<StringRef, T>> Mapping, T Default,
385-
bool IgnoreCase = false) {
386-
if (auto ValueOr = getLocalOrGlobal(LocalName, Mapping, IgnoreCase))
395+
getLocalOrGlobal(StringRef LocalName, T Default, bool IgnoreCase = false) {
396+
if (auto ValueOr = getLocalOrGlobal<T>(LocalName, IgnoreCase))
387397
return *ValueOr;
388398
else
389399
logErrToStdErr(ValueOr.takeError());
@@ -401,34 +411,40 @@ class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
401411
int64_t Value) const;
402412

403413
/// Stores an option with the check-local name \p LocalName as the string
404-
/// representation of the Enum \p Value using the \p Mapping to \p Options.
414+
/// representation of the Enum \p Value to \p Options.
415+
///
416+
/// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
417+
/// supply the mapping required to convert between ``T`` and a string.
405418
template <typename T>
406419
std::enable_if_t<std::is_enum<T>::value>
407-
store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, T Value,
408-
ArrayRef<std::pair<StringRef, T>> Mapping) {
420+
store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, T Value) {
421+
ArrayRef<std::pair<T, StringRef>> Mapping =
422+
OptionEnumMapping<T>::getEnumMapping();
409423
auto Iter = llvm::find_if(
410-
Mapping, [&](const std::pair<StringRef, T> &NameAndEnum) {
411-
return NameAndEnum.second == Value;
424+
Mapping, [&](const std::pair<T, StringRef> &NameAndEnum) {
425+
return NameAndEnum.first == Value;
412426
});
413427
assert(Iter != Mapping.end() && "Unknown Case Value");
414-
store(Options, LocalName, Iter->first);
428+
store(Options, LocalName, Iter->second);
415429
}
416430

417431
private:
418-
using NameAndValue = std::pair<StringRef, int64_t>;
432+
using NameAndValue = std::pair<int64_t, StringRef>;
419433

420434
llvm::Expected<int64_t> getEnumInt(StringRef LocalName,
421435
ArrayRef<NameAndValue> Mapping,
422436
bool CheckGlobal, bool IgnoreCase);
423437

424438
template <typename T>
425439
std::enable_if_t<std::is_enum<T>::value, std::vector<NameAndValue>>
426-
typeEraseMapping(ArrayRef<std::pair<StringRef, T>> Mapping) {
440+
typeEraseMapping() {
441+
ArrayRef<std::pair<T, StringRef>> Mapping =
442+
OptionEnumMapping<T>::getEnumMapping();
427443
std::vector<NameAndValue> Result;
428444
Result.reserve(Mapping.size());
429445
for (auto &MappedItem : Mapping) {
430-
Result.emplace_back(MappedItem.first,
431-
static_cast<int64_t>(MappedItem.second));
446+
Result.emplace_back(static_cast<int64_t>(MappedItem.first),
447+
MappedItem.second);
432448
}
433449
return Result;
434450
}

clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ StringFindStartswithCheck::StringFindStartswithCheck(StringRef Name,
2727
StringLikeClasses(utils::options::parseStringList(
2828
Options.get("StringLikeClasses", "::std::basic_string"))),
2929
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
30-
utils::IncludeSorter::getMapping(),
3130
utils::IncludeSorter::IS_LLVM)),
3231
AbseilStringsMatchHeader(
3332
Options.get("AbseilStringsMatchHeader", "absl/strings/match.h")) {}
@@ -122,8 +121,7 @@ void StringFindStartswithCheck::storeOptions(
122121
ClangTidyOptions::OptionMap &Opts) {
123122
Options.store(Opts, "StringLikeClasses",
124123
utils::options::serializeStringList(StringLikeClasses));
125-
Options.store(Opts, "IncludeStyle", IncludeStyle,
126-
utils::IncludeSorter::getMapping());
124+
Options.store(Opts, "IncludeStyle", IncludeStyle);
127125
Options.store(Opts, "AbseilStringsMatchHeader", AbseilStringsMatchHeader);
128126
}
129127

clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,11 @@ InitVariablesCheck::InitVariablesCheck(StringRef Name,
2525
ClangTidyContext *Context)
2626
: ClangTidyCheck(Name, Context),
2727
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
28-
utils::IncludeSorter::getMapping(),
2928
utils::IncludeSorter::IS_LLVM)),
3029
MathHeader(Options.get("MathHeader", "math.h")) {}
3130

3231
void InitVariablesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
33-
Options.store(Opts, "IncludeStyle", IncludeStyle,
34-
utils::IncludeSorter::getMapping());
32+
Options.store(Opts, "IncludeStyle", IncludeStyle);
3533
Options.store(Opts, "MathHeader", MathHeader);
3634
}
3735

clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ ProBoundsConstantArrayIndexCheck::ProBoundsConstantArrayIndexCheck(
2222
StringRef Name, ClangTidyContext *Context)
2323
: ClangTidyCheck(Name, Context), GslHeader(Options.get("GslHeader", "")),
2424
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
25-
utils::IncludeSorter::getMapping(),
2625
utils::IncludeSorter::IS_LLVM)) {}
2726

2827
void ProBoundsConstantArrayIndexCheck::storeOptions(

clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,31 @@ using namespace llvm;
2828

2929
namespace clang {
3030
namespace tidy {
31+
32+
template <> struct OptionEnumMapping<modernize::Confidence::Level> {
33+
static llvm::ArrayRef<std::pair<modernize::Confidence::Level, StringRef>>
34+
getEnumMapping() {
35+
static constexpr std::pair<modernize::Confidence::Level, StringRef>
36+
Mapping[] = {{modernize::Confidence::CL_Reasonable, "reasonable"},
37+
{modernize::Confidence::CL_Safe, "safe"},
38+
{modernize::Confidence::CL_Risky, "risky"}};
39+
return makeArrayRef(Mapping);
40+
}
41+
};
42+
43+
template <> struct OptionEnumMapping<modernize::VariableNamer::NamingStyle> {
44+
static llvm::ArrayRef<
45+
std::pair<modernize::VariableNamer::NamingStyle, StringRef>>
46+
getEnumMapping() {
47+
static constexpr std::pair<modernize::VariableNamer::NamingStyle, StringRef>
48+
Mapping[] = {{modernize::VariableNamer::NS_CamelCase, "CamelCase"},
49+
{modernize::VariableNamer::NS_CamelBack, "camelBack"},
50+
{modernize::VariableNamer::NS_LowerCase, "lower_case"},
51+
{modernize::VariableNamer::NS_UpperCase, "UPPER_CASE"}};
52+
return makeArrayRef(Mapping);
53+
}
54+
};
55+
3156
namespace modernize {
3257

3358
static const char LoopNameArray[] = "forLoopArray";
@@ -44,25 +69,6 @@ static const char EndVarName[] = "endVar";
4469
static const char DerefByValueResultName[] = "derefByValueResult";
4570
static const char DerefByRefResultName[] = "derefByRefResult";
4671

47-
static ArrayRef<std::pair<StringRef, Confidence::Level>>
48-
getConfidenceMapping() {
49-
static constexpr std::pair<StringRef, Confidence::Level> Mapping[] = {
50-
{"reasonable", Confidence::CL_Reasonable},
51-
{"safe", Confidence::CL_Safe},
52-
{"risky", Confidence::CL_Risky}};
53-
return makeArrayRef(Mapping);
54-
}
55-
56-
static ArrayRef<std::pair<StringRef, VariableNamer::NamingStyle>>
57-
getStyleMapping() {
58-
static constexpr std::pair<StringRef, VariableNamer::NamingStyle> Mapping[] =
59-
{{"CamelCase", VariableNamer::NS_CamelCase},
60-
{"camelBack", VariableNamer::NS_CamelBack},
61-
{"lower_case", VariableNamer::NS_LowerCase},
62-
{"UPPER_CASE", VariableNamer::NS_UpperCase}};
63-
return makeArrayRef(Mapping);
64-
}
65-
6672
// shared matchers
6773
static const TypeMatcher AnyType() { return anything(); }
6874

@@ -477,15 +483,13 @@ LoopConvertCheck::RangeDescriptor::RangeDescriptor()
477483
LoopConvertCheck::LoopConvertCheck(StringRef Name, ClangTidyContext *Context)
478484
: ClangTidyCheck(Name, Context), TUInfo(new TUTrackingInfo),
479485
MaxCopySize(Options.get("MaxCopySize", 16ULL)),
480-
MinConfidence(Options.get("MinConfidence", getConfidenceMapping(),
481-
Confidence::CL_Reasonable)),
482-
NamingStyle(Options.get("NamingStyle", getStyleMapping(),
483-
VariableNamer::NS_CamelCase)) {}
486+
MinConfidence(Options.get("MinConfidence", Confidence::CL_Reasonable)),
487+
NamingStyle(Options.get("NamingStyle", VariableNamer::NS_CamelCase)) {}
484488

485489
void LoopConvertCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
486490
Options.store(Opts, "MaxCopySize", std::to_string(MaxCopySize));
487-
Options.store(Opts, "MinConfidence", MinConfidence, getConfidenceMapping());
488-
Options.store(Opts, "NamingStyle", NamingStyle, getStyleMapping());
491+
Options.store(Opts, "MinConfidence", MinConfidence);
492+
Options.store(Opts, "NamingStyle", NamingStyle);
489493
}
490494

491495
void LoopConvertCheck::registerMatchers(MatchFinder *Finder) {

clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
4545
StringRef MakeSmartPtrFunctionName)
4646
: ClangTidyCheck(Name, Context),
4747
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
48-
utils::IncludeSorter::getMapping(),
4948
utils::IncludeSorter::IS_LLVM)),
5049
MakeSmartPtrFunctionHeader(
5150
Options.get("MakeSmartPtrFunctionHeader", StdMemoryHeader)),
@@ -54,8 +53,7 @@ MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
5453
IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
5554

5655
void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
57-
Options.store(Opts, "IncludeStyle", IncludeStyle,
58-
utils::IncludeSorter::getMapping());
56+
Options.store(Opts, "IncludeStyle", IncludeStyle);
5957
Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
6058
Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
6159
Options.store(Opts, "IgnoreMacros", IgnoreMacros);

clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,11 @@ collectParamDecls(const CXXConstructorDecl *Ctor,
121121
PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context)
122122
: ClangTidyCheck(Name, Context),
123123
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
124-
utils::IncludeSorter::getMapping(),
125124
utils::IncludeSorter::IS_LLVM)),
126125
ValuesOnly(Options.get("ValuesOnly", false)) {}
127126

128127
void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
129-
Options.store(Opts, "IncludeStyle", IncludeStyle,
130-
utils::IncludeSorter::getMapping());
128+
Options.store(Opts, "IncludeStyle", IncludeStyle);
131129
Options.store(Opts, "ValuesOnly", ValuesOnly);
132130
}
133131

clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,10 @@ ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name,
7575
ClangTidyContext *Context)
7676
: ClangTidyCheck(Name, Context),
7777
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
78-
utils::IncludeSorter::getMapping(),
7978
utils::IncludeSorter::IS_LLVM)) {}
8079

8180
void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
82-
Options.store(Opts, "IncludeStyle", IncludeStyle,
83-
utils::IncludeSorter::getMapping());
81+
Options.store(Opts, "IncludeStyle", IncludeStyle);
8482
}
8583

8684
void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {

clang-tools-extra/clang-tidy/modernize/ReplaceRandomShuffleCheck.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ ReplaceRandomShuffleCheck::ReplaceRandomShuffleCheck(StringRef Name,
2424
ClangTidyContext *Context)
2525
: ClangTidyCheck(Name, Context),
2626
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
27-
utils::IncludeSorter::getMapping(),
2827
utils::IncludeSorter::IS_LLVM)) {}
2928

3029
void ReplaceRandomShuffleCheck::registerMatchers(MatchFinder *Finder) {
@@ -52,8 +51,7 @@ void ReplaceRandomShuffleCheck::registerPPCallbacks(
5251

5352
void ReplaceRandomShuffleCheck::storeOptions(
5453
ClangTidyOptions::OptionMap &Opts) {
55-
Options.store(Opts, "IncludeStyle", IncludeStyle,
56-
utils::IncludeSorter::getMapping());
54+
Options.store(Opts, "IncludeStyle", IncludeStyle);
5755
}
5856

5957
void ReplaceRandomShuffleCheck::check(const MatchFinder::MatchResult &Result) {

0 commit comments

Comments
 (0)