Details
- Reviewers
- None
Diff Detail
Event Timeline
clang-tidy/ClangTidy.h | ||
---|---|---|
45–46 | If there's a reason not to use StringRef instead of const char*, it should be in ALL CAPS, RED AND BOLD in the comment above ;) | |
62–74 | Why do we want unknown values to give Default? If we do, we should put it into the comments; can we rather make them given an error while parsing, and then do: | |
69–74 | Will is_integral trigger for 128 bit integral values, and then narrow? |
We can generalize go a step further get() and store() to all types that specialize llvm::yaml traits.
This could remove the code duplication and simplify the creation of clang-tidy checks having options of enumeration type.
clang-tidy/ClangTidy.h | ||
---|---|---|
45–46 | What's the reason for changing std::string Default to const char* Default? | |
67 | That's essentially the same code as in llvm/lib/Support/YAMLTraits.cpp::651. | |
67–74 | Couldn't we just generalize this approach to all the types that specialize llvm::yaml::XYZTraits (XYZ being one of Scalar/ScalarEnumeration/...) ? An ugly hack (working at least for enums specializing ScalarEnumerationTraits) would be: template <typename T> typename std::enable_if<!std::is_integral<T>::value && !std::is_convertible<T, std::string>::value, T>::type get(StringRef LocalName, T Default) const { std::string Value = get(LocalName, ""); T Result = Default; if (!Value.empty()) { std::stringstream Content; Content << LocalName.str() << ": " << Value; llvm::yaml::Input Input(Content.str()); Input.setCurrentDocument(); Input.mapOptional(LocalName.data(), Result); if (Input.error()) Result = Default; } return Result; } The hack is ugly since it uses Input::setCurrentDocument() (which should probably be private) and Input::mapOptional() and not Input::operator>>. That is so, because otherwise we would need creating a type specializing llvm::yaml::MappingTraits which would essentially do an operation equivalent to Input.mapOptional(LocalName.str(), Result);. | |
78–79 | Similarly to above get() method, one can generalize the approach. template <typename T> typename std::enable_if<!std::is_integral<T>::value && !std::is_convertible<T, std::string>::value, void>::type store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, T Value) const { std::string Text; llvm::raw_string_ostream Content(Text); llvm::yaml::Output Output(Content); Output.beginMapping(); if (Output.preflightDocument(0)) Output.mapOptional(LocalName.data(), Value); Output.endMapping(); auto T = Content.str(); // Skip past ": " between key and value. auto Off = T.find(':', LocalName.size()); assert(Off != std::string::npos); T = T.substr(Off + 2, std::string::npos); // FIXME(mkurdej): Strip enclosing apostrophes "'" if present. Options[NamePrefix + LocalName.str()] = T; } |
clang-tidy/ClangTidy.h | ||
---|---|---|
67–74 |
That sounds interesting. I'll try something along these lines. |
Use llvm::yaml::ScalarTraits to convert all built-it and string types. No
support for enumerations though, as it is bound tighter to llvm::yaml::IO, and
no error handling (not sure if we need it).
clang-tidy/ClangTidy.h | ||
---|---|---|
62–74 | We can't error out early enough as check options are parsed after the configuration is read. So I don't currently see a better way than to fall back to default. |
clang-tidy/ClangTidy.h | ||
---|---|---|
62–74 | Fall back to false? |
clang-tidy/ClangTidy.h | ||
---|---|---|
62–74 | Could you explain why this would be better? |
clang-tidy/ClangTidy.h | ||
---|---|---|
62–74 | If we catch the error in the config later on, and give a nice error, it doesn't matter, and the code gets simpler. If we cannot catch the error, I would like an explicitly given config to not lead to different behavior depending on the default value; I find that unexpected. Plus, I think it would make the code simpler. |
clang-tidy/ClangTidy.h | ||
---|---|---|
62–74 |
Not sure yet if this can be done nicely.
It doesn't make the code simpler, as we still need to handle missing value and return Default.
Why? On the opposite, I think that the case of invalid value is close to the case of missing value, so using the same default is more intuitive.
Not really. The code is now common for all types having ScalarTraits<>. It also falls back to Default in case of error, and there I don't see another reasonable value to fall back to in case the parsing fails. |
clang-tidy/ClangTidy.h | ||
---|---|---|
62–74 | Ok, I'm convinced: |
clang-tidy/ClangTidy.h | ||
---|---|---|
62–74 | Crashing doesn't seem like a the best solution. We could (mis)use diagnostic output to report configuration errors. WDYT? For now, I've changed OptionsView::get<> to propagate errors to the caller. And the caller can decide whether to crash in case of an error or handle it in a different way. I've changed two current users of this method to use ErrorOr<>::operator* which asserts that there's no value. Do we want to add some kind of a getValueOrCrash method to ErrorOr<> which would use report_fatal_error instead? |
clang-tidy/ClangTidy.h | ||
---|---|---|
45–54 | Won't it be counterintuitive for the user of this code that get(..., string) returns a string and that get(..., T) returns ErrorOr<T>? |
clang-tidy/ClangTidy.h | ||
---|---|---|
45–54 | The difference is that the string version doesn't do any parsing, so it can't encounter any errors. So maybe it should be named differently from the template version to avoid confusion. |
clang-tidy/ClangTidy.h | ||
---|---|---|
45–54 | Yes, I know that, but still it's a bit weird in my eyes to have functions with same name behaving as differently as this. What are your thoughts about it? |
clang-tidy/ClangTidy.h | ||
---|---|---|
45–54 | ScalarTraits::input returns a StringRef describing the error. We can also use StringRef to avoid impedance mismatch and make use of these strings in our error messages. Or maybe ScalarTraits<> should be changed to use std::error_code with custom errors. I'll ask the authors' opinion. |
clang-tidy/ClangTidy.h | ||
---|---|---|
62–74 | We can add OptionsView::get overloads that return Optional<Something> (though not sure why would callers bother using it instead of the versions with default). |
clang-tidy/ClangTidy.h | ||
---|---|---|
62–74 | I meant as opposed to an error. Otherwise I don't understand your last comment above: |
Complete rewrite of the options access and validation code. Now modules can
provide typed default values for the options defined by module's checks.
Validation happens during option parsing and results in the same kind of error
reporting as other configuration format errors.
Actually, the actual command-line workflow is broken now, so I'm working on fixing it and adding a test. But I believe, the fix won't require much change, so it shouldn't postpone the review.
clang-tidy/ClangTidyOptions.h | ||
---|---|---|
79–114 | So, if we want to roll with having the scalar transformations on the class, I'd just make Value private and overload get() and store() for string types - that would also make the outer code symmetrical. Second, I'm slightly confused about storing the Validate function with the option - it seems to be completely unrelated (it's about the option type, not about the option value). |
What's the reason for changing std::string Default to const char* Default?
It "breaks" somehow the symmetry of T get(StringRef, T), i.e. taking T type as Default and returning T.