diff options
author | Yitzhak Mandelbaum <yitzhakm@google.com> | 2019-10-10 02:34:47 +0000 |
---|---|---|
committer | Yitzhak Mandelbaum <yitzhakm@google.com> | 2019-10-10 02:34:47 +0000 |
commit | fbdf83521b17c4683e4f819587000bbce71d928b (patch) | |
tree | 729317d14b678ab28d26557e4d46478783435cc4 /clang/lib/Tooling/Refactoring | |
parent | 79a8476d4363912553d5165a055601bcd417e8ff (diff) | |
download | bcm5719-llvm-fbdf83521b17c4683e4f819587000bbce71d928b.tar.gz bcm5719-llvm-fbdf83521b17c4683e4f819587000bbce71d928b.zip |
[libTooling] Move Transformer files to their own directory/library.
Summary:
The Transformer library has been growing inside of
lib/Tooling/Refactoring. However, it's not really related to anything else in
that directory. This revision moves all Transformer-related files into their own
include & lib directories. A followup revision will (temporarily) add
forwarding headers to help any users migrate their code to the new location.
Reviewers: gribozavr
Subscribers: mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D68637
llvm-svn: 374271
Diffstat (limited to 'clang/lib/Tooling/Refactoring')
-rw-r--r-- | clang/lib/Tooling/Refactoring/CMakeLists.txt | 5 | ||||
-rw-r--r-- | clang/lib/Tooling/Refactoring/RangeSelector.cpp | 312 | ||||
-rw-r--r-- | clang/lib/Tooling/Refactoring/SourceCode.cpp | 65 | ||||
-rw-r--r-- | clang/lib/Tooling/Refactoring/SourceCodeBuilders.cpp | 160 | ||||
-rw-r--r-- | clang/lib/Tooling/Refactoring/Stencil.cpp | 369 | ||||
-rw-r--r-- | clang/lib/Tooling/Refactoring/Transformer.cpp | 235 |
6 files changed, 0 insertions, 1146 deletions
diff --git a/clang/lib/Tooling/Refactoring/CMakeLists.txt b/clang/lib/Tooling/Refactoring/CMakeLists.txt index e3961db2841..db889d2a06b 100644 --- a/clang/lib/Tooling/Refactoring/CMakeLists.txt +++ b/clang/lib/Tooling/Refactoring/CMakeLists.txt @@ -6,17 +6,12 @@ add_clang_library(clangToolingRefactoring AtomicChange.cpp Extract/Extract.cpp Extract/SourceExtraction.cpp - RangeSelector.cpp RefactoringActions.cpp Rename/RenamingAction.cpp Rename/SymbolOccurrences.cpp Rename/USRFinder.cpp Rename/USRFindingAction.cpp Rename/USRLocFinder.cpp - SourceCode.cpp - SourceCodeBuilders.cpp - Stencil.cpp - Transformer.cpp LINK_LIBS clangAST diff --git a/clang/lib/Tooling/Refactoring/RangeSelector.cpp b/clang/lib/Tooling/Refactoring/RangeSelector.cpp deleted file mode 100644 index 972c7e65540..00000000000 --- a/clang/lib/Tooling/Refactoring/RangeSelector.cpp +++ /dev/null @@ -1,312 +0,0 @@ -//===--- RangeSelector.cpp - RangeSelector implementations ------*- C++ -*-===// -// -// 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/Tooling/Refactoring/RangeSelector.h" -#include "clang/AST/Expr.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Lex/Lexer.h" -#include "clang/Tooling/Refactoring/SourceCode.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Error.h" -#include <string> -#include <utility> -#include <vector> - -using namespace clang; -using namespace tooling; - -using ast_matchers::MatchFinder; -using ast_type_traits::ASTNodeKind; -using ast_type_traits::DynTypedNode; -using llvm::Error; -using llvm::StringError; - -using MatchResult = MatchFinder::MatchResult; - -static Error invalidArgumentError(Twine Message) { - return llvm::make_error<StringError>(llvm::errc::invalid_argument, Message); -} - -static Error typeError(StringRef ID, const ASTNodeKind &Kind) { - return invalidArgumentError("mismatched type (node id=" + ID + - " kind=" + Kind.asStringRef() + ")"); -} - -static Error typeError(StringRef ID, const ASTNodeKind &Kind, - Twine ExpectedType) { - return invalidArgumentError("mismatched type: expected one of " + - ExpectedType + " (node id=" + ID + - " kind=" + Kind.asStringRef() + ")"); -} - -static Error missingPropertyError(StringRef ID, Twine Description, - StringRef Property) { - return invalidArgumentError(Description + " requires property '" + Property + - "' (node id=" + ID + ")"); -} - -static Expected<DynTypedNode> getNode(const ast_matchers::BoundNodes &Nodes, - StringRef ID) { - auto &NodesMap = Nodes.getMap(); - auto It = NodesMap.find(ID); - if (It == NodesMap.end()) - return invalidArgumentError("ID not bound: " + ID); - return It->second; -} - -// FIXME: handling of macros should be configurable. -static SourceLocation findPreviousTokenStart(SourceLocation Start, - const SourceManager &SM, - const LangOptions &LangOpts) { - if (Start.isInvalid() || Start.isMacroID()) - return SourceLocation(); - - SourceLocation BeforeStart = Start.getLocWithOffset(-1); - if (BeforeStart.isInvalid() || BeforeStart.isMacroID()) - return SourceLocation(); - - return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts); -} - -// Finds the start location of the previous token of kind \p TK. -// FIXME: handling of macros should be configurable. -static SourceLocation findPreviousTokenKind(SourceLocation Start, - const SourceManager &SM, - const LangOptions &LangOpts, - tok::TokenKind TK) { - while (true) { - SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts); - if (L.isInvalid() || L.isMacroID()) - return SourceLocation(); - - Token T; - if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true)) - return SourceLocation(); - - if (T.is(TK)) - return T.getLocation(); - - Start = L; - } -} - -static SourceLocation findOpenParen(const CallExpr &E, const SourceManager &SM, - const LangOptions &LangOpts) { - SourceLocation EndLoc = - E.getNumArgs() == 0 ? E.getRParenLoc() : E.getArg(0)->getBeginLoc(); - return findPreviousTokenKind(EndLoc, SM, LangOpts, tok::TokenKind::l_paren); -} - -RangeSelector tooling::before(RangeSelector Selector) { - return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> { - Expected<CharSourceRange> SelectedRange = Selector(Result); - if (!SelectedRange) - return SelectedRange.takeError(); - return CharSourceRange::getCharRange(SelectedRange->getBegin()); - }; -} - -RangeSelector tooling::after(RangeSelector Selector) { - return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> { - Expected<CharSourceRange> SelectedRange = Selector(Result); - if (!SelectedRange) - return SelectedRange.takeError(); - if (SelectedRange->isCharRange()) - return CharSourceRange::getCharRange(SelectedRange->getEnd()); - return CharSourceRange::getCharRange(Lexer::getLocForEndOfToken( - SelectedRange->getEnd(), 0, Result.Context->getSourceManager(), - Result.Context->getLangOpts())); - }; -} - -RangeSelector tooling::node(std::string ID) { - return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { - Expected<DynTypedNode> Node = getNode(Result.Nodes, ID); - if (!Node) - return Node.takeError(); - return Node->get<Stmt>() != nullptr && Node->get<Expr>() == nullptr - ? getExtendedRange(*Node, tok::TokenKind::semi, *Result.Context) - : CharSourceRange::getTokenRange(Node->getSourceRange()); - }; -} - -RangeSelector tooling::statement(std::string ID) { - return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { - Expected<DynTypedNode> Node = getNode(Result.Nodes, ID); - if (!Node) - return Node.takeError(); - return getExtendedRange(*Node, tok::TokenKind::semi, *Result.Context); - }; -} - -RangeSelector tooling::range(RangeSelector Begin, RangeSelector End) { - return [Begin, End](const MatchResult &Result) -> Expected<CharSourceRange> { - Expected<CharSourceRange> BeginRange = Begin(Result); - if (!BeginRange) - return BeginRange.takeError(); - Expected<CharSourceRange> EndRange = End(Result); - if (!EndRange) - return EndRange.takeError(); - SourceLocation B = BeginRange->getBegin(); - SourceLocation E = EndRange->getEnd(); - // Note: we are precluding the possibility of sub-token ranges in the case - // that EndRange is a token range. - if (Result.SourceManager->isBeforeInTranslationUnit(E, B)) { - return invalidArgumentError("Bad range: out of order"); - } - return CharSourceRange(SourceRange(B, E), EndRange->isTokenRange()); - }; -} - -RangeSelector tooling::range(std::string BeginID, std::string EndID) { - return tooling::range(node(std::move(BeginID)), node(std::move(EndID))); -} - -RangeSelector tooling::member(std::string ID) { - return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { - Expected<DynTypedNode> Node = getNode(Result.Nodes, ID); - if (!Node) - return Node.takeError(); - if (auto *M = Node->get<clang::MemberExpr>()) - return CharSourceRange::getTokenRange( - M->getMemberNameInfo().getSourceRange()); - return typeError(ID, Node->getNodeKind(), "MemberExpr"); - }; -} - -RangeSelector tooling::name(std::string ID) { - return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { - Expected<DynTypedNode> N = getNode(Result.Nodes, ID); - if (!N) - return N.takeError(); - auto &Node = *N; - if (const auto *D = Node.get<NamedDecl>()) { - if (!D->getDeclName().isIdentifier()) - return missingPropertyError(ID, "name", "identifier"); - SourceLocation L = D->getLocation(); - auto R = CharSourceRange::getTokenRange(L, L); - // Verify that the range covers exactly the name. - // FIXME: extend this code to support cases like `operator +` or - // `foo<int>` for which this range will be too short. Doing so will - // require subcasing `NamedDecl`, because it doesn't provide virtual - // access to the \c DeclarationNameInfo. - if (getText(R, *Result.Context) != D->getName()) - return CharSourceRange(); - return R; - } - if (const auto *E = Node.get<DeclRefExpr>()) { - if (!E->getNameInfo().getName().isIdentifier()) - return missingPropertyError(ID, "name", "identifier"); - SourceLocation L = E->getLocation(); - return CharSourceRange::getTokenRange(L, L); - } - if (const auto *I = Node.get<CXXCtorInitializer>()) { - if (!I->isMemberInitializer() && I->isWritten()) - return missingPropertyError(ID, "name", "explicit member initializer"); - SourceLocation L = I->getMemberLocation(); - return CharSourceRange::getTokenRange(L, L); - } - return typeError(ID, Node.getNodeKind(), - "DeclRefExpr, NamedDecl, CXXCtorInitializer"); - }; -} - -namespace { -// FIXME: make this available in the public API for users to easily create their -// own selectors. - -// Creates a selector from a range-selection function \p Func, which selects a -// range that is relative to a bound node id. \c T is the node type expected by -// \p Func. -template <typename T, CharSourceRange (*Func)(const MatchResult &, const T &)> -class RelativeSelector { - std::string ID; - -public: - RelativeSelector(std::string ID) : ID(std::move(ID)) {} - - Expected<CharSourceRange> operator()(const MatchResult &Result) { - Expected<DynTypedNode> N = getNode(Result.Nodes, ID); - if (!N) - return N.takeError(); - if (const auto *Arg = N->get<T>()) - return Func(Result, *Arg); - return typeError(ID, N->getNodeKind()); - } -}; -} // namespace - -// FIXME: Change the following functions from being in an anonymous namespace -// to static functions, after the minimum Visual C++ has _MSC_VER >= 1915 -// (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous -// namespace works around a bug in earlier versions. -namespace { -// Returns the range of the statements (all source between the braces). -CharSourceRange getStatementsRange(const MatchResult &, - const CompoundStmt &CS) { - return CharSourceRange::getCharRange(CS.getLBracLoc().getLocWithOffset(1), - CS.getRBracLoc()); -} -} // namespace - -RangeSelector tooling::statements(std::string ID) { - return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID)); -} - -namespace { -// Returns the range of the source between the call's parentheses. -CharSourceRange getCallArgumentsRange(const MatchResult &Result, - const CallExpr &CE) { - return CharSourceRange::getCharRange( - findOpenParen(CE, *Result.SourceManager, Result.Context->getLangOpts()) - .getLocWithOffset(1), - CE.getRParenLoc()); -} -} // namespace - -RangeSelector tooling::callArgs(std::string ID) { - return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID)); -} - -namespace { -// Returns the range of the elements of the initializer list. Includes all -// source between the braces. -CharSourceRange getElementsRange(const MatchResult &, - const InitListExpr &E) { - return CharSourceRange::getCharRange(E.getLBraceLoc().getLocWithOffset(1), - E.getRBraceLoc()); -} -} // namespace - -RangeSelector tooling::initListElements(std::string ID) { - return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID)); -} - -namespace { -// Returns the range of the else branch, including the `else` keyword. -CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) { - return maybeExtendRange( - CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()), - tok::TokenKind::semi, *Result.Context); -} -} // namespace - -RangeSelector tooling::elseBranch(std::string ID) { - return RelativeSelector<IfStmt, getElseRange>(std::move(ID)); -} - -RangeSelector tooling::expansion(RangeSelector S) { - return [S](const MatchResult &Result) -> Expected<CharSourceRange> { - Expected<CharSourceRange> SRange = S(Result); - if (!SRange) - return SRange.takeError(); - return Result.SourceManager->getExpansionRange(*SRange); - }; -} diff --git a/clang/lib/Tooling/Refactoring/SourceCode.cpp b/clang/lib/Tooling/Refactoring/SourceCode.cpp deleted file mode 100644 index cee8f43f3e6..00000000000 --- a/clang/lib/Tooling/Refactoring/SourceCode.cpp +++ /dev/null @@ -1,65 +0,0 @@ -//===--- SourceCode.cpp - Source code manipulation routines -----*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// This file provides functions that simplify extraction of source code. -// -//===----------------------------------------------------------------------===// -#include "clang/Tooling/Refactoring/SourceCode.h" -#include "clang/Lex/Lexer.h" - -using namespace clang; - -StringRef clang::tooling::getText(CharSourceRange Range, - const ASTContext &Context) { - return Lexer::getSourceText(Range, Context.getSourceManager(), - Context.getLangOpts()); -} - -CharSourceRange clang::tooling::maybeExtendRange(CharSourceRange Range, - tok::TokenKind Next, - ASTContext &Context) { - Optional<Token> Tok = Lexer::findNextToken( - Range.getEnd(), Context.getSourceManager(), Context.getLangOpts()); - if (!Tok || !Tok->is(Next)) - return Range; - return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation()); -} - -llvm::Optional<CharSourceRange> -clang::tooling::getRangeForEdit(const CharSourceRange &EditRange, - const SourceManager &SM, - const LangOptions &LangOpts) { - // FIXME: makeFileCharRange() has the disadvantage of stripping off "identity" - // macros. For example, if we're looking to rewrite the int literal 3 to 6, - // and we have the following definition: - // #define DO_NOTHING(x) x - // then - // foo(DO_NOTHING(3)) - // will be rewritten to - // foo(6) - // rather than the arguably better - // foo(DO_NOTHING(6)) - // Decide whether the current behavior is desirable and modify if not. - CharSourceRange Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts); - if (Range.isInvalid()) - return None; - - if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID()) - return None; - if (SM.isInSystemHeader(Range.getBegin()) || - SM.isInSystemHeader(Range.getEnd())) - return None; - - std::pair<FileID, unsigned> BeginInfo = SM.getDecomposedLoc(Range.getBegin()); - std::pair<FileID, unsigned> EndInfo = SM.getDecomposedLoc(Range.getEnd()); - if (BeginInfo.first != EndInfo.first || - BeginInfo.second > EndInfo.second) - return None; - - return Range; -} diff --git a/clang/lib/Tooling/Refactoring/SourceCodeBuilders.cpp b/clang/lib/Tooling/Refactoring/SourceCodeBuilders.cpp deleted file mode 100644 index 2499c0f1eb3..00000000000 --- a/clang/lib/Tooling/Refactoring/SourceCodeBuilders.cpp +++ /dev/null @@ -1,160 +0,0 @@ -//===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===// -// -// 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/Tooling/Refactoring/SourceCodeBuilders.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprCXX.h" -#include "clang/Tooling/Refactoring/SourceCode.h" -#include "llvm/ADT/Twine.h" -#include <string> - -using namespace clang; -using namespace tooling; - -const Expr *tooling::reallyIgnoreImplicit(const Expr &E) { - const Expr *Expr = E.IgnoreImplicit(); - if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) { - if (CE->getNumArgs() > 0 && - CE->getArg(0)->getSourceRange() == Expr->getSourceRange()) - return CE->getArg(0)->IgnoreImplicit(); - } - return Expr; -} - -bool tooling::mayEverNeedParens(const Expr &E) { - const Expr *Expr = reallyIgnoreImplicit(E); - // We always want parens around unary, binary, and ternary operators, because - // they are lower precedence. - if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) || - isa<AbstractConditionalOperator>(Expr)) - return true; - - // We need parens around calls to all overloaded operators except: function - // calls, subscripts, and expressions that are already part of an (implicit) - // call to operator->. These latter are all in the same precedence level as - // dot/arrow and that level is left associative, so they don't need parens - // when appearing on the left. - if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr)) - return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript && - Op->getOperator() != OO_Arrow; - - return false; -} - -bool tooling::needParensAfterUnaryOperator(const Expr &E) { - const Expr *Expr = reallyIgnoreImplicit(E); - if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr)) - return true; - - if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr)) - return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus && - Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call && - Op->getOperator() != OO_Subscript; - - return false; -} - -llvm::Optional<std::string> tooling::buildParens(const Expr &E, - const ASTContext &Context) { - StringRef Text = getText(E, Context); - if (Text.empty()) - return llvm::None; - if (mayEverNeedParens(E)) - return ("(" + Text + ")").str(); - return Text.str(); -} - -llvm::Optional<std::string> -tooling::buildDereference(const Expr &E, const ASTContext &Context) { - if (const auto *Op = dyn_cast<UnaryOperator>(&E)) - if (Op->getOpcode() == UO_AddrOf) { - // Strip leading '&'. - StringRef Text = - getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); - if (Text.empty()) - return llvm::None; - return Text.str(); - } - - StringRef Text = getText(E, Context); - if (Text.empty()) - return llvm::None; - // Add leading '*'. - if (needParensAfterUnaryOperator(E)) - return ("*(" + Text + ")").str(); - return ("*" + Text).str(); -} - -llvm::Optional<std::string> tooling::buildAddressOf(const Expr &E, - const ASTContext &Context) { - if (const auto *Op = dyn_cast<UnaryOperator>(&E)) - if (Op->getOpcode() == UO_Deref) { - // Strip leading '*'. - StringRef Text = - getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); - if (Text.empty()) - return llvm::None; - return Text.str(); - } - // Add leading '&'. - StringRef Text = getText(E, Context); - if (Text.empty()) - return llvm::None; - if (needParensAfterUnaryOperator(E)) { - return ("&(" + Text + ")").str(); - } - return ("&" + Text).str(); -} - -llvm::Optional<std::string> tooling::buildDot(const Expr &E, - const ASTContext &Context) { - if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E)) - if (Op->getOpcode() == UO_Deref) { - // Strip leading '*', add following '->'. - const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); - StringRef DerefText = getText(*SubExpr, Context); - if (DerefText.empty()) - return llvm::None; - if (needParensBeforeDotOrArrow(*SubExpr)) - return ("(" + DerefText + ")->").str(); - return (DerefText + "->").str(); - } - - // Add following '.'. - StringRef Text = getText(E, Context); - if (Text.empty()) - return llvm::None; - if (needParensBeforeDotOrArrow(E)) { - return ("(" + Text + ").").str(); - } - return (Text + ".").str(); -} - -llvm::Optional<std::string> tooling::buildArrow(const Expr &E, - const ASTContext &Context) { - if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E)) - if (Op->getOpcode() == UO_AddrOf) { - // Strip leading '&', add following '.'. - const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); - StringRef DerefText = getText(*SubExpr, Context); - if (DerefText.empty()) - return llvm::None; - if (needParensBeforeDotOrArrow(*SubExpr)) - return ("(" + DerefText + ").").str(); - return (DerefText + ".").str(); - } - - // Add following '->'. - StringRef Text = getText(E, Context); - if (Text.empty()) - return llvm::None; - if (needParensBeforeDotOrArrow(E)) - return ("(" + Text + ")->").str(); - return (Text + "->").str(); -} diff --git a/clang/lib/Tooling/Refactoring/Stencil.cpp b/clang/lib/Tooling/Refactoring/Stencil.cpp deleted file mode 100644 index ebfe78099db..00000000000 --- a/clang/lib/Tooling/Refactoring/Stencil.cpp +++ /dev/null @@ -1,369 +0,0 @@ -//===--- Stencil.cpp - Stencil implementation -------------------*- C++ -*-===// -// -// 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/Tooling/Refactoring/Stencil.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/ASTTypeTraits.h" -#include "clang/AST/Expr.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Lex/Lexer.h" -#include "clang/Tooling/Refactoring/SourceCode.h" -#include "clang/Tooling/Refactoring/SourceCodeBuilders.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/Errc.h" -#include <atomic> -#include <memory> -#include <string> - -using namespace clang; -using namespace tooling; - -using ast_matchers::MatchFinder; -using ast_type_traits::DynTypedNode; -using llvm::errc; -using llvm::Error; -using llvm::Expected; -using llvm::StringError; - -// A down_cast function to safely down cast a StencilPartInterface to a subclass -// D. Returns nullptr if P is not an instance of D. -template <typename D> const D *down_cast(const StencilPartInterface *P) { - if (P == nullptr || D::typeId() != P->typeId()) - return nullptr; - return static_cast<const D *>(P); -} - -static llvm::Expected<DynTypedNode> -getNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) { - auto &NodesMap = Nodes.getMap(); - auto It = NodesMap.find(Id); - if (It == NodesMap.end()) - return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument, - "Id not bound: " + Id); - return It->second; -} - -namespace { -// An arbitrary fragment of code within a stencil. -struct RawTextData { - explicit RawTextData(std::string T) : Text(std::move(T)) {} - std::string Text; -}; - -// A debugging operation to dump the AST for a particular (bound) AST node. -struct DebugPrintNodeData { - explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {} - std::string Id; -}; - -// Operators that take a single node Id as an argument. -enum class UnaryNodeOperator { - Parens, - Deref, - Address, -}; - -// Generic container for stencil operations with a (single) node-id argument. -struct UnaryOperationData { - UnaryOperationData(UnaryNodeOperator Op, std::string Id) - : Op(Op), Id(std::move(Id)) {} - UnaryNodeOperator Op; - std::string Id; -}; - -// The fragment of code corresponding to the selected range. -struct SelectorData { - explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {} - RangeSelector Selector; -}; - -// A stencil operation to build a member access `e.m` or `e->m`, as appropriate. -struct AccessData { - AccessData(StringRef BaseId, StencilPart Member) - : BaseId(BaseId), Member(std::move(Member)) {} - std::string BaseId; - StencilPart Member; -}; - -struct IfBoundData { - IfBoundData(StringRef Id, StencilPart TruePart, StencilPart FalsePart) - : Id(Id), TruePart(std::move(TruePart)), FalsePart(std::move(FalsePart)) { - } - std::string Id; - StencilPart TruePart; - StencilPart FalsePart; -}; - -bool isEqualData(const RawTextData &A, const RawTextData &B) { - return A.Text == B.Text; -} - -bool isEqualData(const DebugPrintNodeData &A, const DebugPrintNodeData &B) { - return A.Id == B.Id; -} - -bool isEqualData(const UnaryOperationData &A, const UnaryOperationData &B) { - return A.Op == B.Op && A.Id == B.Id; -} - -// Equality is not (yet) defined for \c RangeSelector. -bool isEqualData(const SelectorData &, const SelectorData &) { return false; } - -bool isEqualData(const AccessData &A, const AccessData &B) { - return A.BaseId == B.BaseId && A.Member == B.Member; -} - -bool isEqualData(const IfBoundData &A, const IfBoundData &B) { - return A.Id == B.Id && A.TruePart == B.TruePart && A.FalsePart == B.FalsePart; -} - -// Equality is not defined over MatchConsumers, which are opaque. -bool isEqualData(const MatchConsumer<std::string> &A, - const MatchConsumer<std::string> &B) { - return false; -} - -std::string toStringData(const RawTextData &Data) { - std::string Result; - llvm::raw_string_ostream OS(Result); - OS << "\""; - OS.write_escaped(Data.Text); - OS << "\""; - OS.flush(); - return Result; -} - -std::string toStringData(const DebugPrintNodeData &Data) { - return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str(); -} - -std::string toStringData(const UnaryOperationData &Data) { - StringRef OpName; - switch (Data.Op) { - case UnaryNodeOperator::Parens: - OpName = "expression"; - break; - case UnaryNodeOperator::Deref: - OpName = "deref"; - break; - case UnaryNodeOperator::Address: - OpName = "addressOf"; - break; - } - return (OpName + "(\"" + Data.Id + "\")").str(); -} - -std::string toStringData(const SelectorData &) { return "SelectorData()"; } - -std::string toStringData(const AccessData &Data) { - return (llvm::Twine("access(\"") + Data.BaseId + "\", " + - Data.Member.toString() + ")") - .str(); -} - -std::string toStringData(const IfBoundData &Data) { - return (llvm::Twine("ifBound(\"") + Data.Id + "\", " + - Data.TruePart.toString() + ", " + Data.FalsePart.toString() + ")") - .str(); -} - -std::string toStringData(const MatchConsumer<std::string> &) { - return "MatchConsumer<std::string>()"; -} - -// The `evalData()` overloads evaluate the given stencil data to a string, given -// the match result, and append it to `Result`. We define an overload for each -// type of stencil data. - -Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &, - std::string *Result) { - Result->append(Data.Text); - return Error::success(); -} - -Error evalData(const DebugPrintNodeData &Data, - const MatchFinder::MatchResult &Match, std::string *Result) { - std::string Output; - llvm::raw_string_ostream Os(Output); - auto NodeOrErr = getNode(Match.Nodes, Data.Id); - if (auto Err = NodeOrErr.takeError()) - return Err; - NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts())); - *Result += Os.str(); - return Error::success(); -} - -Error evalData(const UnaryOperationData &Data, - const MatchFinder::MatchResult &Match, std::string *Result) { - const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id); - if (E == nullptr) - return llvm::make_error<StringError>( - errc::invalid_argument, "Id not bound or not Expr: " + Data.Id); - llvm::Optional<std::string> Source; - switch (Data.Op) { - case UnaryNodeOperator::Parens: - Source = buildParens(*E, *Match.Context); - break; - case UnaryNodeOperator::Deref: - Source = buildDereference(*E, *Match.Context); - break; - case UnaryNodeOperator::Address: - Source = buildAddressOf(*E, *Match.Context); - break; - } - if (!Source) - return llvm::make_error<StringError>( - errc::invalid_argument, - "Could not construct expression source from ID: " + Data.Id); - *Result += *Source; - return Error::success(); -} - -Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match, - std::string *Result) { - auto Range = Data.Selector(Match); - if (!Range) - return Range.takeError(); - *Result += getText(*Range, *Match.Context); - return Error::success(); -} - -Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match, - std::string *Result) { - const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId); - if (E == nullptr) - return llvm::make_error<StringError>(errc::invalid_argument, - "Id not bound: " + Data.BaseId); - if (!E->isImplicitCXXThis()) { - if (llvm::Optional<std::string> S = E->getType()->isAnyPointerType() - ? buildArrow(*E, *Match.Context) - : buildDot(*E, *Match.Context)) - *Result += *S; - else - return llvm::make_error<StringError>( - errc::invalid_argument, - "Could not construct object text from ID: " + Data.BaseId); - } - return Data.Member.eval(Match, Result); -} - -Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, - std::string *Result) { - auto &M = Match.Nodes.getMap(); - return (M.find(Data.Id) != M.end() ? Data.TruePart : Data.FalsePart) - .eval(Match, Result); -} - -Error evalData(const MatchConsumer<std::string> &Fn, - const MatchFinder::MatchResult &Match, std::string *Result) { - Expected<std::string> Value = Fn(Match); - if (!Value) - return Value.takeError(); - *Result += *Value; - return Error::success(); -} - -template <typename T> -class StencilPartImpl : public StencilPartInterface { - T Data; - -public: - template <typename... Ps> - explicit StencilPartImpl(Ps &&... Args) - : StencilPartInterface(StencilPartImpl::typeId()), - Data(std::forward<Ps>(Args)...) {} - - // Generates a unique identifier for this class (specifically, one per - // instantiation of the template). - static const void* typeId() { - static bool b; - return &b; - } - - Error eval(const MatchFinder::MatchResult &Match, - std::string *Result) const override { - return evalData(Data, Match, Result); - } - - bool isEqual(const StencilPartInterface &Other) const override { - if (const auto *OtherPtr = down_cast<StencilPartImpl>(&Other)) - return isEqualData(Data, OtherPtr->Data); - return false; - } - - std::string toString() const override { return toStringData(Data); } -}; -} // namespace - -StencilPart Stencil::wrap(StringRef Text) { - return stencil::text(Text); -} - -StencilPart Stencil::wrap(RangeSelector Selector) { - return stencil::selection(std::move(Selector)); -} - -void Stencil::append(Stencil OtherStencil) { - for (auto &Part : OtherStencil.Parts) - Parts.push_back(std::move(Part)); -} - -llvm::Expected<std::string> -Stencil::eval(const MatchFinder::MatchResult &Match) const { - std::string Result; - for (const auto &Part : Parts) - if (auto Err = Part.eval(Match, &Result)) - return std::move(Err); - return Result; -} - -StencilPart stencil::text(StringRef Text) { - return StencilPart(std::make_shared<StencilPartImpl<RawTextData>>(Text)); -} - -StencilPart stencil::selection(RangeSelector Selector) { - return StencilPart( - std::make_shared<StencilPartImpl<SelectorData>>(std::move(Selector))); -} - -StencilPart stencil::dPrint(StringRef Id) { - return StencilPart(std::make_shared<StencilPartImpl<DebugPrintNodeData>>(Id)); -} - -StencilPart stencil::expression(llvm::StringRef Id) { - return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( - UnaryNodeOperator::Parens, Id)); -} - -StencilPart stencil::deref(llvm::StringRef ExprId) { - return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( - UnaryNodeOperator::Deref, ExprId)); -} - -StencilPart stencil::addressOf(llvm::StringRef ExprId) { - return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( - UnaryNodeOperator::Address, ExprId)); -} - -StencilPart stencil::access(StringRef BaseId, StencilPart Member) { - return StencilPart( - std::make_shared<StencilPartImpl<AccessData>>(BaseId, std::move(Member))); -} - -StencilPart stencil::ifBound(StringRef Id, StencilPart TruePart, - StencilPart FalsePart) { - return StencilPart(std::make_shared<StencilPartImpl<IfBoundData>>( - Id, std::move(TruePart), std::move(FalsePart))); -} - -StencilPart stencil::run(MatchConsumer<std::string> Fn) { - return StencilPart( - std::make_shared<StencilPartImpl<MatchConsumer<std::string>>>( - std::move(Fn))); -} diff --git a/clang/lib/Tooling/Refactoring/Transformer.cpp b/clang/lib/Tooling/Refactoring/Transformer.cpp deleted file mode 100644 index 905d5944449..00000000000 --- a/clang/lib/Tooling/Refactoring/Transformer.cpp +++ /dev/null @@ -1,235 +0,0 @@ -//===--- Transformer.cpp - Transformer library implementation ---*- C++ -*-===// -// -// 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/Tooling/Refactoring/Transformer.h" -#include "clang/AST/Expr.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Rewrite/Core/Rewriter.h" -#include "clang/Tooling/Refactoring/AtomicChange.h" -#include "clang/Tooling/Refactoring/SourceCode.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Error.h" -#include <string> -#include <utility> -#include <vector> -#include <map> - -using namespace clang; -using namespace tooling; - -using ast_matchers::MatchFinder; -using ast_matchers::internal::DynTypedMatcher; -using ast_type_traits::ASTNodeKind; -using ast_type_traits::DynTypedNode; -using llvm::Error; -using llvm::StringError; - -using MatchResult = MatchFinder::MatchResult; - -Expected<SmallVector<tooling::detail::Transformation, 1>> -tooling::detail::translateEdits(const MatchResult &Result, - llvm::ArrayRef<ASTEdit> Edits) { - SmallVector<tooling::detail::Transformation, 1> Transformations; - for (const auto &Edit : Edits) { - Expected<CharSourceRange> Range = Edit.TargetRange(Result); - if (!Range) - return Range.takeError(); - llvm::Optional<CharSourceRange> EditRange = - getRangeForEdit(*Range, *Result.Context); - // FIXME: let user specify whether to treat this case as an error or ignore - // it as is currently done. - if (!EditRange) - return SmallVector<Transformation, 0>(); - auto Replacement = Edit.Replacement(Result); - if (!Replacement) - return Replacement.takeError(); - tooling::detail::Transformation T; - T.Range = *EditRange; - T.Replacement = std::move(*Replacement); - Transformations.push_back(std::move(T)); - } - return Transformations; -} - -ASTEdit tooling::change(RangeSelector S, TextGenerator Replacement) { - ASTEdit E; - E.TargetRange = std::move(S); - E.Replacement = std::move(Replacement); - return E; -} - -RewriteRule tooling::makeRule(DynTypedMatcher M, SmallVector<ASTEdit, 1> Edits, - TextGenerator Explanation) { - return RewriteRule{{RewriteRule::Case{ - std::move(M), std::move(Edits), std::move(Explanation), {}}}}; -} - -void tooling::addInclude(RewriteRule &Rule, StringRef Header, - IncludeFormat Format) { - for (auto &Case : Rule.Cases) - Case.AddedIncludes.emplace_back(Header.str(), Format); -} - -#ifndef NDEBUG -// Filters for supported matcher kinds. FIXME: Explicitly list the allowed kinds -// (all node matcher types except for `QualType` and `Type`), rather than just -// banning `QualType` and `Type`. -static bool hasValidKind(const DynTypedMatcher &M) { - return !M.canConvertTo<QualType>(); -} -#endif - -// Binds each rule's matcher to a unique (and deterministic) tag based on -// `TagBase` and the id paired with the case. -static std::vector<DynTypedMatcher> taggedMatchers( - StringRef TagBase, - const SmallVectorImpl<std::pair<size_t, RewriteRule::Case>> &Cases) { - std::vector<DynTypedMatcher> Matchers; - Matchers.reserve(Cases.size()); - for (const auto &Case : Cases) { - std::string Tag = (TagBase + Twine(Case.first)).str(); - // HACK: Many matchers are not bindable, so ensure that tryBind will work. - DynTypedMatcher BoundMatcher(Case.second.Matcher); - BoundMatcher.setAllowBind(true); - auto M = BoundMatcher.tryBind(Tag); - Matchers.push_back(*std::move(M)); - } - return Matchers; -} - -// Simply gathers the contents of the various rules into a single rule. The -// actual work to combine these into an ordered choice is deferred to matcher -// registration. -RewriteRule tooling::applyFirst(ArrayRef<RewriteRule> Rules) { - RewriteRule R; - for (auto &Rule : Rules) - R.Cases.append(Rule.Cases.begin(), Rule.Cases.end()); - return R; -} - -std::vector<DynTypedMatcher> -tooling::detail::buildMatchers(const RewriteRule &Rule) { - // Map the cases into buckets of matchers -- one for each "root" AST kind, - // which guarantees that they can be combined in a single anyOf matcher. Each - // case is paired with an identifying number that is converted to a string id - // in `taggedMatchers`. - std::map<ASTNodeKind, SmallVector<std::pair<size_t, RewriteRule::Case>, 1>> - Buckets; - const SmallVectorImpl<RewriteRule::Case> &Cases = Rule.Cases; - for (int I = 0, N = Cases.size(); I < N; ++I) { - assert(hasValidKind(Cases[I].Matcher) && - "Matcher must be non-(Qual)Type node matcher"); - Buckets[Cases[I].Matcher.getSupportedKind()].emplace_back(I, Cases[I]); - } - - std::vector<DynTypedMatcher> Matchers; - for (const auto &Bucket : Buckets) { - DynTypedMatcher M = DynTypedMatcher::constructVariadic( - DynTypedMatcher::VO_AnyOf, Bucket.first, - taggedMatchers("Tag", Bucket.second)); - M.setAllowBind(true); - // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true. - Matchers.push_back(*M.tryBind(RewriteRule::RootID)); - } - return Matchers; -} - -DynTypedMatcher tooling::detail::buildMatcher(const RewriteRule &Rule) { - std::vector<DynTypedMatcher> Ms = buildMatchers(Rule); - assert(Ms.size() == 1 && "Cases must have compatible matchers."); - return Ms[0]; -} - -SourceLocation tooling::detail::getRuleMatchLoc(const MatchResult &Result) { - auto &NodesMap = Result.Nodes.getMap(); - auto Root = NodesMap.find(RewriteRule::RootID); - assert(Root != NodesMap.end() && "Transformation failed: missing root node."); - llvm::Optional<CharSourceRange> RootRange = getRangeForEdit( - CharSourceRange::getTokenRange(Root->second.getSourceRange()), - *Result.Context); - if (RootRange) - return RootRange->getBegin(); - // The match doesn't have a coherent range, so fall back to the expansion - // location as the "beginning" of the match. - return Result.SourceManager->getExpansionLoc( - Root->second.getSourceRange().getBegin()); -} - -// Finds the case that was "selected" -- that is, whose matcher triggered the -// `MatchResult`. -const RewriteRule::Case & -tooling::detail::findSelectedCase(const MatchResult &Result, - const RewriteRule &Rule) { - if (Rule.Cases.size() == 1) - return Rule.Cases[0]; - - auto &NodesMap = Result.Nodes.getMap(); - for (size_t i = 0, N = Rule.Cases.size(); i < N; ++i) { - std::string Tag = ("Tag" + Twine(i)).str(); - if (NodesMap.find(Tag) != NodesMap.end()) - return Rule.Cases[i]; - } - llvm_unreachable("No tag found for this rule."); -} - -constexpr llvm::StringLiteral RewriteRule::RootID; - -void Transformer::registerMatchers(MatchFinder *MatchFinder) { - for (auto &Matcher : tooling::detail::buildMatchers(Rule)) - MatchFinder->addDynamicMatcher(Matcher, this); -} - -void Transformer::run(const MatchResult &Result) { - if (Result.Context->getDiagnostics().hasErrorOccurred()) - return; - - RewriteRule::Case Case = tooling::detail::findSelectedCase(Result, Rule); - auto Transformations = tooling::detail::translateEdits(Result, Case.Edits); - if (!Transformations) { - Consumer(Transformations.takeError()); - return; - } - - if (Transformations->empty()) { - // No rewrite applied (but no error encountered either). - detail::getRuleMatchLoc(Result).print( - llvm::errs() << "note: skipping match at loc ", *Result.SourceManager); - llvm::errs() << "\n"; - return; - } - - // Record the results in the AtomicChange, anchored at the location of the - // first change. - AtomicChange AC(*Result.SourceManager, - (*Transformations)[0].Range.getBegin()); - for (const auto &T : *Transformations) { - if (auto Err = AC.replace(*Result.SourceManager, T.Range, T.Replacement)) { - Consumer(std::move(Err)); - return; - } - } - - for (const auto &I : Case.AddedIncludes) { - auto &Header = I.first; - switch (I.second) { - case IncludeFormat::Quoted: - AC.addHeader(Header); - break; - case IncludeFormat::Angled: - AC.addHeader((llvm::Twine("<") + Header + ">").str()); - break; - } - } - - Consumer(std::move(AC)); -} |