Hi djasper, bkramer,
This allows using configurable strings in fixits, such as user name or
email in TODO() comments in certain styles.
http://reviews.llvm.org/D5408
Files:
clang-tidy/ClangTidyDiagnosticConsumer.cpp
clang-tidy/ClangTidyDiagnosticConsumer.h
clang-tidy/ClangTidyOptions.cpp
clang-tidy/ClangTidyOptions.h
clang-tidy/google/TodoCommentCheck.cpp
clang-tidy/google/TodoCommentCheck.h
clang-tidy/tool/ClangTidyMain.cpp
unittests/clang-tidy/ClangTidyDiagnosticConsumerTest.cpp
unittests/clang-tidy/ClangTidyTest.h
Index: clang-tidy/ClangTidyDiagnosticConsumer.cpp
===================================================================
--- clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -229,6 +229,36 @@
/// \brief Store a \c ClangTidyError.
void ClangTidyContext::storeError(const ClangTidyError &Error) {
Errors.push_back(Error);
+ applySubstitutions(Errors.back());
+}
+
+void ClangTidyContext::applySubstitutions(ClangTidyError &Error) const {
+ std::vector<std::pair<const tooling::Replacement &, tooling::Replacement>>
+ ChangedReplacements;
+ for (const auto &Replacement : Error.Fix) {
+ std::string ReplacementText = Replacement.getReplacementText().str();
+ for (const auto &Substitution : getOptions().Substitutions) {
+ const std::string &From = Substitution.first;
+ const std::string &To = Substitution.second;
+ std::string::size_type Pos = 0;
+ while ((Pos = ReplacementText.find(From, Pos)) != std::string::npos) {
+ ReplacementText = ReplacementText.substr(0, Pos) + To +
+ ReplacementText.substr(Pos + From.size());
+ Pos += To.size();
+ }
+ }
+ if (Replacement.getReplacementText() != ReplacementText) {
+ ChangedReplacements.emplace_back(
+ Replacement, tooling::Replacement(
+ Replacement.getFilePath(), Replacement.getOffset(),
+ Replacement.getLength(), ReplacementText));
+ }
+ }
+
+ for (const auto &ChangedReplacement : ChangedReplacements) {
+ Error.Fix.erase(ChangedReplacement.first);
+ Error.Fix.insert(ChangedReplacement.second);
+ }
}
StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
Index: clang-tidy/ClangTidyDiagnosticConsumer.h
===================================================================
--- clang-tidy/ClangTidyDiagnosticConsumer.h
+++ clang-tidy/ClangTidyDiagnosticConsumer.h
@@ -173,6 +173,10 @@
/// \brief Store an \p Error.
void storeError(const ClangTidyError &Error);
+ /// \brief Applies \c ClangTidyOptions::Substitutions to each
+ /// \c ClangTidyError::Fix's replacement text.
+ void applySubstitutions(ClangTidyError &Error) const;
+
std::vector<ClangTidyError> Errors;
DiagnosticsEngine *DiagEngine;
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
Index: clang-tidy/ClangTidyOptions.cpp
===================================================================
--- clang-tidy/ClangTidyOptions.cpp
+++ clang-tidy/ClangTidyOptions.cpp
@@ -82,10 +82,21 @@
static void mapping(IO &IO, ClangTidyOptions &Options) {
MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
IO, Options.CheckOptions);
+ MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NSubsts(
+ IO, Options.Substitutions);
IO.mapOptional("Checks", Options.Checks);
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
IO.mapOptional("CheckOptions", NOpts->Options);
+ IO.mapOptional("Substitutions", NSubsts->Options);
+ }
+ static StringRef validate(IO &io, ClangTidyOptions &Options) {
+ for (const auto &S : Options.Substitutions) {
+ StringRef Key = S.first;
+ if (!Key.startswith("${") || !Key.endswith("}"))
+ return "Substitution name has to be enclosed in ${}";
+ }
+ return StringRef();
}
};
@@ -95,6 +106,8 @@
namespace clang {
namespace tidy {
+const char ClangTidyOptions::Placeholders::User[] = "${ClangTidy.User}";
+
ClangTidyOptions
ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
ClangTidyOptions Result = *this;
@@ -113,6 +126,9 @@
for (const auto &KeyValue : Other.CheckOptions)
Result.CheckOptions[KeyValue.first] = KeyValue.second;
+ for (const auto &KeyValue : Other.Substitutions)
+ Result.Substitutions[KeyValue.first] = KeyValue.second;
+
return Result;
}
Index: clang-tidy/ClangTidyOptions.h
===================================================================
--- clang-tidy/ClangTidyOptions.h
+++ clang-tidy/ClangTidyOptions.h
@@ -77,6 +77,20 @@
/// \brief Key-value mapping used to store check-specific options.
OptionMap CheckOptions;
+
+ /// \brief Maps placeholders to the content that they should be replaced with.
+ ///
+ /// ClangTidy supports substitiution of placeholders in replacement strings of
+ /// the \c FixItHints. \c Substitutions maps placeholders to the text inserted
+ /// instead.
+ OptionMap Substitutions;
+
+ /// \brief Placeholders available globally.
+ struct Placeholders {
+ /// \brief This placeholder can be used whenever the user's name/email is
+ /// needed (e.g. in TODO() comments in certain styles).
+ static const char User[];
+ };
};
/// \brief Abstract interface for retrieving various ClangTidy options.
Index: clang-tidy/google/TodoCommentCheck.cpp
===================================================================
--- clang-tidy/google/TodoCommentCheck.cpp
+++ clang-tidy/google/TodoCommentCheck.cpp
@@ -35,13 +35,9 @@
if (!Username.empty())
return false;
- // If the username is missing put in the current user's name. Not ideal but
- // works for running tidy locally.
- // FIXME: Can we get this from a more reliable source?
- const char *User = std::getenv("USER");
- if (!User)
- User = "unknown";
- std::string NewText = ("// TODO(" + Twine(User) + "): " + Comment).str();
+ std::string NewText =
+ ("// TODO(" + Twine(ClangTidyOptions::Placeholders::User) + "): " +
+ Comment).str();
Check.diag(Range.getBegin(), "missing username/bug in TODO")
<< FixItHint::CreateReplacement(CharSourceRange::getCharRange(Range),
@@ -58,8 +54,6 @@
: ClangTidyCheck(Name, Context),
Handler(llvm::make_unique<TodoCommentHandler>(*this)) {}
-TodoCommentCheck::~TodoCommentCheck() {}
-
void TodoCommentCheck::registerPPCallbacks(CompilerInstance &Compiler) {
Compiler.getPreprocessor().addCommentHandler(Handler.get());
}
Index: clang-tidy/google/TodoCommentCheck.h
===================================================================
--- clang-tidy/google/TodoCommentCheck.h
+++ clang-tidy/google/TodoCommentCheck.h
@@ -22,7 +22,6 @@
class TodoCommentCheck : public ClangTidyCheck {
public:
TodoCommentCheck(StringRef Name, ClangTidyContext *Context);
- ~TodoCommentCheck();
void registerPPCallbacks(CompilerInstance &Compiler) override;
private:
Index: clang-tidy/tool/ClangTidyMain.cpp
===================================================================
--- clang-tidy/tool/ClangTidyMain.cpp
+++ clang-tidy/tool/ClangTidyMain.cpp
@@ -17,6 +17,7 @@
#include "../ClangTidy.h"
#include "clang/Tooling/CommonOptionsParser.h"
+#include "llvm/Support/Process.h"
using namespace clang::ast_matchers;
using namespace clang::driver;
@@ -156,6 +157,10 @@
if (AnalyzeTemporaryDtors.getNumOccurrences() > 0)
OverrideOptions.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
+ // FIXME: Allow this to be overridden from a config file.
+ if (llvm::Optional<std::string> User = llvm::sys::Process::GetEnv("USER"))
+ OverrideOptions.Substitutions[ClangTidyOptions::Placeholders::User] = *User;
+
auto OptionsProvider = llvm::make_unique<FileOptionsProvider>(
GlobalOptions, FallbackOptions, OverrideOptions);
Index: unittests/clang-tidy/ClangTidyDiagnosticConsumerTest.cpp
===================================================================
--- unittests/clang-tidy/ClangTidyDiagnosticConsumerTest.cpp
+++ unittests/clang-tidy/ClangTidyDiagnosticConsumerTest.cpp
@@ -1,5 +1,6 @@
#include "ClangTidy.h"
#include "ClangTidyTest.h"
+#include "clang/Lex/Lexer.h"
#include "gtest/gtest.h"
namespace clang {
@@ -29,6 +30,49 @@
EXPECT_EQ("variable", Errors[1].Message.Message);
}
+TEST(ClangTidyDiagnosticConsumer, HandlesSubstitutions) {
+ class SubstitutionsTestCheck : public TestCheck {
+ public:
+ SubstitutionsTestCheck(StringRef Name, ClangTidyContext *Context)
+ : TestCheck(Name, Context) {}
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+ const VarDecl *Var = Result.Nodes.getNodeAs<VarDecl>("var");
+ diag(Var->getTypeSpecStartLoc(), "around type")
+ << FixItHint::CreateInsertion(Var->getTypeSpecStartLoc(),
+ ClangTidyOptions::Placeholders::User)
+ << FixItHint::CreateInsertion(
+ Lexer::getLocForEndOfToken(Var->getTypeSpecStartLoc(), 0,
+ *Result.SourceManager,
+ Result.Context->getLangOpts()),
+ ClangTidyOptions::Placeholders::User);
+ diag(Var->getLocation(), "instead of name")
+ << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(Var->getLocation(),
+ Var->getLocation()),
+ "\\${aaa}/${aaa}${aaa}${}}}{{");
+ }
+ };
+
+ std::vector<ClangTidyError> Errors;
+ ClangTidyOptions Options;
+ std::string ActualUser = "<<<user \\ / $ ^ \\\\ \n\t>>>";
+ Options.Substitutions[ClangTidyOptions::Placeholders::User] = ActualUser;
+ Options.Substitutions["${aaa}"] = "123";
+ std::string Replacement2 = "\\123/123123${}}}{{";
+ std::string Result = runCheckOnCode<SubstitutionsTestCheck>(
+ "int abc;", &Errors, "input.cc", None, Options);
+ EXPECT_EQ(ActualUser + "int" + ActualUser + " " + Replacement2 + ";", Result);
+ EXPECT_EQ(2ul, Errors.size());
+ EXPECT_EQ("around type", Errors[0].Message.Message);
+ EXPECT_EQ(2ul, Errors[0].Fix.size());
+ for (const auto &Fix : Errors[0].Fix)
+ EXPECT_EQ(ActualUser, Fix.getReplacementText());
+
+ EXPECT_EQ("instead of name", Errors[1].Message.Message);
+ EXPECT_EQ(1ul, Errors[1].Fix.size());
+ EXPECT_EQ(Replacement2, Errors[1].Fix.begin()->getReplacementText());
+}
+
TEST(GlobList, Empty) {
GlobList Filter("");
Index: unittests/clang-tidy/ClangTidyTest.h
===================================================================
--- unittests/clang-tidy/ClangTidyTest.h
+++ unittests/clang-tidy/ClangTidyTest.h
@@ -40,12 +40,15 @@
};
template <typename T>
-std::string runCheckOnCode(StringRef Code,
- std::vector<ClangTidyError> *Errors = nullptr,
- const Twine &Filename = "input.cc",
- ArrayRef<std::string> ExtraArgs = None) {
+std::string
+runCheckOnCode(StringRef Code, std::vector<ClangTidyError> *Errors = nullptr,
+ const Twine &Filename = "input.cc",
+ ArrayRef<std::string> ExtraArgs = None,
+ llvm::Optional<ClangTidyOptions> OverrideOptions = None) {
ClangTidyOptions Options;
Options.Checks = "*";
+ if (OverrideOptions)
+ Options = Options.mergeWith(*OverrideOptions);
ClangTidyContext Context(llvm::make_unique<DefaultOptionsProvider>(
ClangTidyGlobalOptions(), Options));
ClangTidyDiagnosticConsumer DiagConsumer(Context);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits