[Lldb-commits] [PATCH] D116377: [libTooling] Adds more support for constructing object access expressions.
ymandel updated this revision to Diff 402984. ymandel added a comment. Herald added a subscriber: JDevlieghere. fix bad previous snapshot Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D116377/new/ https://reviews.llvm.org/D116377 Files: clang/include/clang/Tooling/Transformer/SourceCodeBuilders.h clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp clang/lib/Tooling/Transformer/Stencil.cpp clang/unittests/Tooling/SourceCodeBuildersTest.cpp clang/unittests/Tooling/StencilTest.cpp Index: clang/unittests/Tooling/StencilTest.cpp === --- clang/unittests/Tooling/StencilTest.cpp +++ clang/unittests/Tooling/StencilTest.cpp @@ -36,10 +36,13 @@ namespace N { class C {}; } namespace { class AnonC {}; } struct S { int Field; }; -struct Smart { - S* operator->() const; - S& operator*() const; +namespace std { +template +struct unique_ptr { + T* operator->() const; + T& operator*() const; }; +} )cc"; return (Preface + ExtraPreface + "auto stencil_test_snippet = []{" + StatementCode + "};") @@ -326,32 +329,15 @@ TEST_F(StencilTest, MaybeDerefSmartPointer) { StringRef Id = "id"; std::string Snippet = R"cc( -Smart x; +std::unique_ptr x; x; )cc"; testExpr(Id, Snippet, maybeDeref(Id), "*x"); } -// Tests that unique_ptr specifically is handled. -TEST_F(StencilTest, MaybeDerefSmartPointerUniquePtr) { - StringRef Id = "id"; - // We deliberately specify `unique_ptr` as empty to verify that it matches - // because of its name, rather than its contents. - StringRef ExtraPreface = - "namespace std { template class unique_ptr {}; }\n"; - StringRef Snippet = R"cc( -std::unique_ptr x; -x; - )cc"; - auto StmtMatch = matchStmt(Snippet, expr().bind(Id), ExtraPreface); - ASSERT_TRUE(StmtMatch); - EXPECT_THAT_EXPECTED(maybeDeref(Id)->eval(StmtMatch->Result), - HasValue(std::string("*x"))); -} - TEST_F(StencilTest, MaybeDerefSmartPointerFromMemberExpr) { StringRef Id = "id"; - std::string Snippet = "Smart x; x->Field;"; + std::string Snippet = "std::unique_ptr x; x->Field;"; auto StmtMatch = matchStmt(Snippet, memberExpr(hasObjectExpression(expr().bind(Id; ASSERT_TRUE(StmtMatch); @@ -381,12 +367,12 @@ TEST_F(StencilTest, MaybeAddressOfSmartPointer) { StringRef Id = "id"; - testExpr(Id, "Smart x; x;", maybeAddressOf(Id), "x"); + testExpr(Id, "std::unique_ptr x; x;", maybeAddressOf(Id), "x"); } TEST_F(StencilTest, MaybeAddressOfSmartPointerFromMemberCall) { StringRef Id = "id"; - std::string Snippet = "Smart x; x->Field;"; + std::string Snippet = "std::unique_ptr x; x->Field;"; auto StmtMatch = matchStmt(Snippet, memberExpr(hasObjectExpression(expr().bind(Id; ASSERT_TRUE(StmtMatch); @@ -396,7 +382,7 @@ TEST_F(StencilTest, MaybeAddressOfSmartPointerDerefNoCancel) { StringRef Id = "id"; - testExpr(Id, "Smart x; *x;", maybeAddressOf(Id), "&*x"); + testExpr(Id, "std::unique_ptr x; *x;", maybeAddressOf(Id), "&*x"); } TEST_F(StencilTest, AccessOpValue) { @@ -446,7 +432,7 @@ TEST_F(StencilTest, AccessOpSmartPointer) { StringRef Snippet = R"cc( -Smart x; +std::unique_ptr x; x; )cc"; StringRef Id = "id"; @@ -455,7 +441,7 @@ TEST_F(StencilTest, AccessOpSmartPointerDereference) { StringRef Snippet = R"cc( -Smart x; +std::unique_ptr x; *x; )cc"; StringRef Id = "id"; @@ -464,7 +450,7 @@ TEST_F(StencilTest, AccessOpSmartPointerMemberCall) { StringRef Snippet = R"cc( -Smart x; +std::unique_ptr x; x->Field; )cc"; StringRef Id = "id"; Index: clang/unittests/Tooling/SourceCodeBuildersTest.cpp === --- clang/unittests/Tooling/SourceCodeBuildersTest.cpp +++ clang/unittests/Tooling/SourceCodeBuildersTest.cpp @@ -7,6 +7,7 @@ //===--===// #include "clang/Tooling/Transformer/SourceCodeBuilders.h" +#include "clang/AST/Type.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/Tooling.h" @@ -24,8 +25,23 @@ // Create a valid translation unit from a statement. static std::string wrapSnippet(StringRef StatementCode) { - return ("struct S { S(); S(int); int field; };\n" + return ("namespace std {\n" + "template struct unique_ptr {\n" + " T* operator->() const;\n" + " T& operator*() const;\n" + "};\n" + "template struct shared_ptr {\n" + " T* operator->() const;\n" + " T& operator*() const;\n" + "};\n" + "}\n" + "struct A { void super(); };\n" + "struct S : public A { S(); S(int); int Field; };\n" "S operator+(const S &a, const S &b);\n" +
[Lldb-commits] [PATCH] D87031: [libTooling] Provide overloads of `rewriteDescendants` that operate directly on an AST node.
ymandel updated this revision to Diff 289623. ymandel added a comment. Herald added a subscriber: JDevlieghere. fix diff base; make small clang-tidy suggested change Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D87031/new/ https://reviews.llvm.org/D87031 Files: clang/include/clang/Tooling/Transformer/RewriteRule.h clang/lib/Tooling/Transformer/RewriteRule.cpp clang/unittests/Tooling/TransformerTest.cpp Index: clang/unittests/Tooling/TransformerTest.cpp === --- clang/unittests/Tooling/TransformerTest.cpp +++ clang/unittests/Tooling/TransformerTest.cpp @@ -25,6 +25,7 @@ using ::testing::IsEmpty; using transformer::cat; using transformer::changeTo; +using transformer::rewriteDescendants; using transformer::RewriteRule; constexpr char KHeaderContents[] = R"cc( @@ -568,6 +569,88 @@ EXPECT_EQ(ErrorCount, 1); } +// +// We include one test per typed overload. We don't test extensively since that +// is already covered by the tests above. +// + +TEST_F(TransformerTest, RewriteDescendantsTypedStmt) { + // Add an unrelated definition to the header that also has a variable named + // "x", to test that the rewrite is limited to the scope we intend. + appendToHeader(R"cc(int g(int x) { return x; })cc"); + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + std::string Expected = + "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x", changeTo(cat("3"))); + testRule(makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))), +[&InlineX](const MatchFinder::MatchResult &R) { + const auto *Node = R.Nodes.getNodeAs("body"); + assert(Node != nullptr && "body must be bound"); + return transformer::detail::rewriteDescendants( + *Node, InlineX, R); +}), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsTypedDecl) { + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + std::string Expected = + "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x", changeTo(cat("3"))); + testRule(makeRule(functionDecl(hasName("f")).bind("fun"), +[&InlineX](const MatchFinder::MatchResult &R) { + const auto *Node = R.Nodes.getNodeAs("fun"); + assert(Node != nullptr && "fun must be bound"); + return transformer::detail::rewriteDescendants( + *Node, InlineX, R); +}), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsTypedTypeLoc) { + std::string Input = "int f(int *x) { return *x; }"; + std::string Expected = "int f(char *x) { return *x; }"; + auto IntToChar = + makeRule(typeLoc(loc(qualType(isInteger(), builtinType(.bind("loc"), + changeTo(cat("char"))); + testRule( + makeRule( + functionDecl( + hasName("f"), + hasParameter(0, varDecl(hasTypeLoc(typeLoc().bind("parmType"), + [&IntToChar](const MatchFinder::MatchResult &R) { +const auto *Node = R.Nodes.getNodeAs("parmType"); +assert(Node != nullptr && "parmType must be bound"); +return transformer::detail::rewriteDescendants(*Node, IntToChar, R); + }), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsTypedDynTyped) { + // Add an unrelated definition to the header that also has a variable named + // "x", to test that the rewrite is limited to the scope we intend. + appendToHeader(R"cc(int g(int x) { return x; })cc"); + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + std::string Expected = + "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x", changeTo(cat("3"))); + testRule( + makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))), + [&InlineX](const MatchFinder::MatchResult &R) { + auto It = R.Nodes.getMap().find("body"); + assert(It != R.Nodes.getMap().end() && "body must be bound"); + return transformer::detail::rewriteDescendants(It->second, +InlineX, R); + }), + Input, Expected); +} + TEST_F(TransformerTest, InsertBeforeEdit) { std::string Input = R"cc( int f() { Index: clang/lib/Tooling/Transformer/RewriteRule.cpp === --- clang/lib/Tooling/Transformer/RewriteRule.cpp +++ clang/lib/Tooling/Transfor
[Lldb-commits] [PATCH] D61386: [clang-tidy] Add support writing a check as a Transformer rewrite rule.
ymandel marked 2 inline comments as done. ymandel added inline comments. Comment at: clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp:46 + + StringRef Message = "no explanation"; + if (Case.Explanation) { ilya-biryukov wrote: > ymandel wrote: > > ilya-biryukov wrote: > > > ymandel wrote: > > > > ilya-biryukov wrote: > > > > > The users will see this for every case without explanation, right? > > > > > I'd expect the rules without explanation to be somewhat common, maybe > > > > > don't show any message at all in that case? > > > > There's no option to call `diag()` without a message. We could pass an > > > > empty string , but that may be confusing given the way the message is > > > > concatenated here: > > > > https://github.com/llvm/llvm-project/blob/master/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp#L204 > > > > > > > > So, no matter what, there will be some message to go w/ the diagnostic. > > > > I figure that being explicit about the lack of explanation is better > > > > than an empty string, but don't feel strongly about this. > > > Ah, so all the options are bad. I can see why you had this design in > > > transformers in the first place. > > > I wonder if we should check the explanations are always set for rewrite > > > rules inside the clang-tidy transformation? > > > Ah, so all the options are bad. I can see why you had this design in > > > transformers in the first place. > > Heh. indeed. > > > > > I wonder if we should check the explanations are always set for rewrite > > > rules inside the clang-tidy transformation?> Quoted Text > > > > I would have thought so, but AFAIK, most folks who write one-off > > transformations use clang-tidy, rather than writing a standalone tool. They > > just ignore the diagnostics, i gather. Transformer may shift that somewhat > > if we improve the experience of writing a (throwaway) standalone tool, but > > for the time being I think we can't assume that. > > > We should focus on minimizing maintenance cost for long-term fixes rather > than one-off transformations. The cost of passing an empty string to a > required explanation field for one-off transformations is rather small and > falls into the hands of a person writing the check, the cost of constantly > finding and fixing the long-lived upstream checks that don't have explanation > (leading to bad user experience) is high and will probably fall into the > hands of `clang-tidy` maintainers in addition to people writing the checks. > > FWIW, having a new API of top of plain transformers should be better than > `clang-tidy` for those writing one-off transformations in the long run. I > don't think `clang-tidy` was ever designed to cover to cover those use-cases, > even though today it seems to be the fastest way to do those. Agreed on all points. I'll send a followup that enforces that constraint. Repository: rL LLVM CHANGES SINCE LAST ACTION https://reviews.llvm.org/D61386/new/ https://reviews.llvm.org/D61386 ___ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
[Lldb-commits] [PATCH] D61386: [clang-tidy] Add support writing a check as a Transformer rewrite rule.
ymandel updated this revision to Diff 200802. ymandel removed a subscriber: dexonsmith. ymandel added a comment. Replace previous diff which had the wrong base. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D61386/new/ https://reviews.llvm.org/D61386 Files: clang-tools-extra/clang-tidy/utils/CMakeLists.txt clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.h clang-tools-extra/unittests/clang-tidy/CMakeLists.txt clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp Index: clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp === --- /dev/null +++ clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp @@ -0,0 +1,68 @@ +//=== TransformerClangTidyCheckTest.cpp - clang-tidy --===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#include "../clang-tidy/utils/TransformerClangTidyCheck.h" +#include "ClangTidyTest.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Refactoring/RangeSelector.h" +#include "clang/Tooling/Refactoring/Stencil.h" +#include "clang/Tooling/Refactoring/Transformer.h" +#include "gtest/gtest.h" + +namespace clang { +namespace tidy { +namespace utils { +namespace { +using tooling::RewriteRule; + +// Invert the code of an if-statement, while maintaining its semantics. +RewriteRule invertIf() { + using namespace ::clang::ast_matchers; + using tooling::change; + using tooling::node; + using tooling::statement; + using tooling::stencil::cat; + + StringRef C = "C", T = "T", E = "E"; + return tooling::makeRule(ifStmt(hasCondition(expr().bind(C)), + hasThen(stmt().bind(T)), + hasElse(stmt().bind(E))), + change(statement(RewriteRule::RootID), + cat("if(!(", node(C), ")) ", statement(E), + " else ", statement(T; +} + +class IfInverterCheck : public TransformerClangTidyCheck { +public: + IfInverterCheck(StringRef Name, ClangTidyContext *Context) + : TransformerClangTidyCheck(invertIf(), Name, Context) {} +}; + +// Basic test of using a rewrite rule as a ClangTidy. +TEST(TransformerClangTidyCheckTest, Basic) { + const std::string Input = R"cc( +void log(const char* msg); +void foo() { + if (10 > 1.0) +log("oh no!"); + else +log("ok"); +} + )cc"; + const std::string Expected = R"( +void log(const char* msg); +void foo() { + if(!(10 > 1.0)) log("ok"); else log("oh no!"); +} + )"; + EXPECT_EQ(Expected, test::runCheckOnCode(Input)); +} +} // namespace +} // namespace utils +} // namespace tidy +} // namespace clang Index: clang-tools-extra/unittests/clang-tidy/CMakeLists.txt === --- clang-tools-extra/unittests/clang-tidy/CMakeLists.txt +++ clang-tools-extra/unittests/clang-tidy/CMakeLists.txt @@ -17,6 +17,7 @@ OverlappingReplacementsTest.cpp UsingInserterTest.cpp ReadabilityModuleTest.cpp + TransformerClangTidyCheckTest.cpp ) target_link_libraries(ClangTidyTests @@ -36,4 +37,5 @@ clangTidyUtils clangTooling clangToolingCore + clangToolingRefactor ) Index: clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.h === --- /dev/null +++ clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.h @@ -0,0 +1,49 @@ +//===-- TransformerClangTidyCheck.h - clang-tidy --===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_TRANSFORMER_CLANG_TIDY_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_TRANSFORMER_CLANG_TIDY_CHECK_H + +#include "../ClangTidy.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Refactoring/Transformer.h" +#include +#include + +namespace clang { +namespace tidy { +namespace utils { + +// A base class for defining a ClangTidy check based on a `RewriteRule`. +// +// For example, given a rule `MyCheckAsRewriteRule`, one can define a tidy check +// as follows: +// +// class MyCheck : public TransformerClangTidyCheck { +// public: +// MyCheck(StringRef Name, ClangTidyContext *Context) +// : TransformerClangTidyCheck(MyCheckA
[Lldb-commits] [PATCH] D61386: [clang-tidy] Add support writing a check as a Transformer rewrite rule.
This revision was automatically updated to reflect the committed changes. Closed by commit rL361418: [clang-tidy] Add support for writing a check as a Transformer rewrite rule. (authored by ymandel, committed by ). Changed prior to commit: https://reviews.llvm.org/D61386?vs=200802&id=200804#toc Repository: rL LLVM CHANGES SINCE LAST ACTION https://reviews.llvm.org/D61386/new/ https://reviews.llvm.org/D61386 Files: clang-tools-extra/trunk/clang-tidy/utils/CMakeLists.txt clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.cpp clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.h clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt clang-tools-extra/trunk/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp Index: clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.h === --- clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.h +++ clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.h @@ -0,0 +1,49 @@ +//===-- TransformerClangTidyCheck.h - clang-tidy --===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_TRANSFORMER_CLANG_TIDY_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_TRANSFORMER_CLANG_TIDY_CHECK_H + +#include "../ClangTidy.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Refactoring/Transformer.h" +#include +#include + +namespace clang { +namespace tidy { +namespace utils { + +// A base class for defining a ClangTidy check based on a `RewriteRule`. +// +// For example, given a rule `MyCheckAsRewriteRule`, one can define a tidy check +// as follows: +// +// class MyCheck : public TransformerClangTidyCheck { +// public: +// MyCheck(StringRef Name, ClangTidyContext *Context) +// : TransformerClangTidyCheck(MyCheckAsRewriteRule, Name, Context) {} +// }; +class TransformerClangTidyCheck : public ClangTidyCheck { +public: + TransformerClangTidyCheck(tooling::RewriteRule R, StringRef Name, +ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), Rule(std::move(R)) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) final; + void check(const ast_matchers::MatchFinder::MatchResult &Result) final; + +private: + tooling::RewriteRule Rule; +}; + +} // namespace utils +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_TRANSFORMER_CLANG_TIDY_CHECK_H Index: clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.cpp === --- clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.cpp @@ -0,0 +1,63 @@ +//===-- TransformerClangTidyCheck.cpp - clang-tidy ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#include "TransformerClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace utils { +using tooling::RewriteRule; + +void TransformerClangTidyCheck::registerMatchers( +ast_matchers::MatchFinder *Finder) { + Finder->addDynamicMatcher(tooling::detail::buildMatcher(Rule), this); +} + +void TransformerClangTidyCheck::check( +const ast_matchers::MatchFinder::MatchResult &Result) { + if (Result.Context->getDiagnostics().hasErrorOccurred()) +return; + + // Verify the existence and validity of the AST node that roots this rule. + const ast_matchers::BoundNodes::IDToNodeMap &NodesMap = Result.Nodes.getMap(); + auto Root = NodesMap.find(RewriteRule::RootID); + assert(Root != NodesMap.end() && "Transformation failed: missing root node."); + SourceLocation RootLoc = Result.SourceManager->getExpansionLoc( + Root->second.getSourceRange().getBegin()); + assert(RootLoc.isValid() && "Invalid location for Root node of match."); + + RewriteRule::Case Case = tooling::detail::findSelectedCase(Result, Rule); + Expected> Transformations = + tooling::detail::translateEdits(Result, Case.Edits); + if (!Transformations) { +llvm::errs() << "Rewrite failed: " + << llvm::toString(Transformations.takeError()) << "\n"; +return; + } + + // No rewrite applied, but no error encountered either. + if (Transformations->empty()) +return; + + StringRef Message = "no explanation"; + if (Case.Explanation) { +if (Expected E = Case.Explanation(Re