diff --git a/clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp b/clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp --- a/clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp +++ b/clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp @@ -90,6 +90,32 @@ EXPECT_EQ(Errors[0].Message.FileOffset, 10U); } +transformer::ASTEdit noReplacementEdit(transformer::RangeSelector Target) { + transformer::ASTEdit E; + E.TargetRange = std::move(Target); + return E; +} + +TEST(TransformerClangTidyCheckTest, EmptyReplacement) { + class DiagOnlyCheck : public TransformerClangTidyCheck { + public: + DiagOnlyCheck(StringRef Name, ClangTidyContext *Context) + : TransformerClangTidyCheck( + makeRule(returnStmt(), edit(noReplacementEdit(node(RootID))), + cat("message")), + Name, Context) {} + }; + std::string Input = "int h() { return 5; }"; + std::vector Errors; + EXPECT_EQ("int h() { }", test::runCheckOnCode(Input, &Errors)); + EXPECT_EQ(Errors.size(), 1U); + EXPECT_EQ(Errors[0].Message.Message, "message"); + EXPECT_THAT(Errors[0].Message.Ranges, testing::IsEmpty()); + + // The diagnostic is anchored to the match, "return 5". + EXPECT_EQ(Errors[0].Message.FileOffset, 10U); +} + TEST(TransformerClangTidyCheckTest, DiagnosticMessageEscaped) { class GiveDiagWithPercentSymbol : public TransformerClangTidyCheck { public: diff --git a/clang/lib/Tooling/Transformer/RewriteRule.cpp b/clang/lib/Tooling/Transformer/RewriteRule.cpp --- a/clang/lib/Tooling/Transformer/RewriteRule.cpp +++ b/clang/lib/Tooling/Transformer/RewriteRule.cpp @@ -50,17 +50,21 @@ // produces a bad range, whereas the latter will simply ignore A. if (!EditRange) return SmallVector(); - auto Replacement = E.Replacement->eval(Result); - if (!Replacement) - return Replacement.takeError(); - auto Metadata = E.Metadata(Result); - if (!Metadata) - return Metadata.takeError(); transformer::Edit T; T.Kind = E.Kind; T.Range = *EditRange; - T.Replacement = std::move(*Replacement); - T.Metadata = std::move(*Metadata); + if (E.Replacement) { + auto Replacement = E.Replacement->eval(Result); + if (!Replacement) + return Replacement.takeError(); + T.Replacement = std::move(*Replacement); + } + if (E.Metadata) { + auto Metadata = E.Metadata(Result); + if (!Metadata) + return Metadata.takeError(); + T.Metadata = std::move(*Metadata); + } Edits.push_back(std::move(T)); } return Edits;