r374271 - [libTooling] Move Transformer files to their own directory/library.
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 9 19:34:47 PDT 2019
Author: ymandel
Date: Wed Oct 9 19:34:47 2019
New Revision: 374271
URL: http://llvm.org/viewvc/llvm-project?rev=374271&view=rev
Log:
[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
Added:
cfe/trunk/include/clang/Tooling/Transformer/
cfe/trunk/include/clang/Tooling/Transformer/MatchConsumer.h
cfe/trunk/include/clang/Tooling/Transformer/RangeSelector.h
cfe/trunk/include/clang/Tooling/Transformer/SourceCode.h
cfe/trunk/include/clang/Tooling/Transformer/SourceCodeBuilders.h
cfe/trunk/include/clang/Tooling/Transformer/Stencil.h
cfe/trunk/include/clang/Tooling/Transformer/Transformer.h
cfe/trunk/lib/Tooling/Transformer/
cfe/trunk/lib/Tooling/Transformer/CMakeLists.txt
cfe/trunk/lib/Tooling/Transformer/RangeSelector.cpp
cfe/trunk/lib/Tooling/Transformer/SourceCode.cpp
cfe/trunk/lib/Tooling/Transformer/SourceCodeBuilders.cpp
cfe/trunk/lib/Tooling/Transformer/Stencil.cpp
cfe/trunk/lib/Tooling/Transformer/Transformer.cpp
Removed:
cfe/trunk/include/clang/Tooling/Refactoring/MatchConsumer.h
cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h
cfe/trunk/include/clang/Tooling/Refactoring/SourceCode.h
cfe/trunk/include/clang/Tooling/Refactoring/SourceCodeBuilders.h
cfe/trunk/include/clang/Tooling/Refactoring/Stencil.h
cfe/trunk/include/clang/Tooling/Refactoring/Transformer.h
cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp
cfe/trunk/lib/Tooling/Refactoring/SourceCode.cpp
cfe/trunk/lib/Tooling/Refactoring/SourceCodeBuilders.cpp
cfe/trunk/lib/Tooling/Refactoring/Stencil.cpp
cfe/trunk/lib/Tooling/Refactoring/Transformer.cpp
Modified:
cfe/trunk/lib/Tooling/CMakeLists.txt
cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt
cfe/trunk/unittests/Tooling/CMakeLists.txt
cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp
cfe/trunk/unittests/Tooling/SourceCodeBuildersTest.cpp
cfe/trunk/unittests/Tooling/SourceCodeTest.cpp
cfe/trunk/unittests/Tooling/StencilTest.cpp
cfe/trunk/unittests/Tooling/TransformerTest.cpp
Removed: cfe/trunk/include/clang/Tooling/Refactoring/MatchConsumer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/MatchConsumer.h?rev=374270&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/MatchConsumer.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/MatchConsumer.h (removed)
@@ -1,58 +0,0 @@
-//===--- MatchConsumer.h - MatchConsumer abstraction ------------*- 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file This file defines the *MatchConsumer* abstraction: a computation over
-/// match results, specifically the `ast_matchers::MatchFinder::MatchResult`
-/// class.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLING_REFACTOR_MATCH_CONSUMER_H_
-#define LLVM_CLANG_TOOLING_REFACTOR_MATCH_CONSUMER_H_
-
-#include "clang/AST/ASTTypeTraits.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Errc.h"
-#include "llvm/Support/Error.h"
-
-namespace clang {
-namespace tooling {
-
-/// A failable computation over nodes bound by AST matchers.
-///
-/// The computation should report any errors though its return value (rather
-/// than terminating the program) to enable usage in interactive scenarios like
-/// clang-query.
-///
-/// This is a central abstraction of the Transformer framework.
-template <typename T>
-using MatchConsumer =
- std::function<Expected<T>(const ast_matchers::MatchFinder::MatchResult &)>;
-
-/// Creates an error that signals that a `MatchConsumer` expected a certain node
-/// to be bound by AST matchers, but it was not actually bound.
-inline llvm::Error notBoundError(llvm::StringRef Id) {
- return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
- "Id not bound: " + Id);
-}
-
-/// Chooses between the two consumers, based on whether \p ID is bound in the
-/// match.
-template <typename T>
-MatchConsumer<T> ifBound(std::string ID, MatchConsumer<T> TrueC,
- MatchConsumer<T> FalseC) {
- return [=](const ast_matchers::MatchFinder::MatchResult &Result) {
- auto &Map = Result.Nodes.getMap();
- return (Map.find(ID) != Map.end() ? TrueC : FalseC)(Result);
- };
-}
-
-} // namespace tooling
-} // namespace clang
-#endif // LLVM_CLANG_TOOLING_REFACTOR_MATCH_CONSUMER_H_
Removed: cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h?rev=374270&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h (removed)
@@ -1,93 +0,0 @@
-//===--- RangeSelector.h - Source-selection library ---------*- 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Defines a combinator library supporting the definition of _selectors_,
-/// which select source ranges based on (bound) AST nodes.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_
-#define LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_
-
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/Basic/SourceLocation.h"
-#include "clang/Tooling/Refactoring/MatchConsumer.h"
-#include "llvm/Support/Error.h"
-#include <functional>
-#include <string>
-
-namespace clang {
-namespace tooling {
-using RangeSelector = MatchConsumer<CharSourceRange>;
-
-inline RangeSelector charRange(CharSourceRange R) {
- return [R](const ast_matchers::MatchFinder::MatchResult &)
- -> Expected<CharSourceRange> { return R; };
-}
-
-/// Selects from the start of \p Begin and to the end of \p End.
-RangeSelector range(RangeSelector Begin, RangeSelector End);
-
-/// Convenience version of \c range where end-points are bound nodes.
-RangeSelector range(std::string BeginID, std::string EndID);
-
-/// Selects the (empty) range [B,B) when \p Selector selects the range [B,E).
-RangeSelector before(RangeSelector Selector);
-
-/// Selects the the point immediately following \p Selector. That is, the
-/// (empty) range [E,E), when \p Selector selects either
-/// * the CharRange [B,E) or
-/// * the TokenRange [B,E'] where the token at E' spans the range [E,E').
-RangeSelector after(RangeSelector Selector);
-
-/// Selects a node, including trailing semicolon (for non-expression
-/// statements). \p ID is the node's binding in the match result.
-RangeSelector node(std::string ID);
-
-/// Selects a node, including trailing semicolon (always). Useful for selecting
-/// expression statements. \p ID is the node's binding in the match result.
-RangeSelector statement(std::string ID);
-
-/// Given a \c MemberExpr, selects the member token. \p ID is the node's
-/// binding in the match result.
-RangeSelector member(std::string ID);
-
-/// Given a node with a "name", (like \c NamedDecl, \c DeclRefExpr or \c
-/// CxxCtorInitializer) selects the name's token. Only selects the final
-/// identifier of a qualified name, but not any qualifiers or template
-/// arguments. For example, for `::foo::bar::baz` and `::foo::bar::baz<int>`,
-/// it selects only `baz`.
-///
-/// \param ID is the node's binding in the match result.
-RangeSelector name(std::string ID);
-
-// Given a \c CallExpr (bound to \p ID), selects the arguments' source text (all
-// source between the call's parentheses).
-RangeSelector callArgs(std::string ID);
-
-// Given a \c CompoundStmt (bound to \p ID), selects the source of the
-// statements (all source between the braces).
-RangeSelector statements(std::string ID);
-
-// Given a \c InitListExpr (bound to \p ID), selects the range of the elements
-// (all source between the braces).
-RangeSelector initListElements(std::string ID);
-
-/// Given an \IfStmt (bound to \p ID), selects the range of the else branch,
-/// starting from the \c else keyword.
-RangeSelector elseBranch(std::string ID);
-
-/// Selects the range from which `S` was expanded (possibly along with other
-/// source), if `S` is an expansion, and `S` itself, otherwise. Corresponds to
-/// `SourceManager::getExpansionRange`.
-RangeSelector expansion(RangeSelector S);
-} // namespace tooling
-} // namespace clang
-
-#endif // LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_
Removed: cfe/trunk/include/clang/Tooling/Refactoring/SourceCode.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/SourceCode.h?rev=374270&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/SourceCode.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/SourceCode.h (removed)
@@ -1,90 +0,0 @@
-//===--- SourceCode.h - 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.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H
-#define LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H
-
-#include "clang/AST/ASTContext.h"
-#include "clang/Basic/SourceLocation.h"
-#include "clang/Basic/TokenKinds.h"
-
-namespace clang {
-namespace tooling {
-
-/// Extends \p Range to include the token \p Next, if it immediately follows the
-/// end of the range. Otherwise, returns \p Range unchanged.
-CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next,
- ASTContext &Context);
-
-/// Returns the source range spanning the node, extended to include \p Next, if
-/// it immediately follows \p Node. Otherwise, returns the normal range of \p
-/// Node. See comments on `getExtendedText()` for examples.
-template <typename T>
-CharSourceRange getExtendedRange(const T &Node, tok::TokenKind Next,
- ASTContext &Context) {
- return maybeExtendRange(CharSourceRange::getTokenRange(Node.getSourceRange()),
- Next, Context);
-}
-
-/// Returns the source-code text in the specified range.
-StringRef getText(CharSourceRange Range, const ASTContext &Context);
-
-/// Returns the source-code text corresponding to \p Node.
-template <typename T>
-StringRef getText(const T &Node, const ASTContext &Context) {
- return getText(CharSourceRange::getTokenRange(Node.getSourceRange()),
- Context);
-}
-
-/// Returns the source text of the node, extended to include \p Next, if it
-/// immediately follows the node. Otherwise, returns the text of just \p Node.
-///
-/// For example, given statements S1 and S2 below:
-/// \code
-/// {
-/// // S1:
-/// if (!x) return foo();
-/// // S2:
-/// if (!x) { return 3; }
-/// }
-/// \endcode
-/// then
-/// \code
-/// getText(S1, Context) = "if (!x) return foo()"
-/// getExtendedText(S1, tok::TokenKind::semi, Context)
-/// = "if (!x) return foo();"
-/// getExtendedText(*S1.getThen(), tok::TokenKind::semi, Context)
-/// = "return foo();"
-/// getExtendedText(*S2.getThen(), tok::TokenKind::semi, Context)
-/// = getText(S2, Context) = "{ return 3; }"
-/// \endcode
-template <typename T>
-StringRef getExtendedText(const T &Node, tok::TokenKind Next,
- ASTContext &Context) {
- return getText(getExtendedRange(Node, Next, Context), Context);
-}
-
-// Attempts to resolve the given range to one that can be edited by a rewrite;
-// generally, one that starts and ends within a particular file. It supports
-// a limited set of cases involving source locations in macro expansions.
-llvm::Optional<CharSourceRange>
-getRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM,
- const LangOptions &LangOpts);
-
-inline llvm::Optional<CharSourceRange>
-getRangeForEdit(const CharSourceRange &EditRange, const ASTContext &Context) {
- return getRangeForEdit(EditRange, Context.getSourceManager(),
- Context.getLangOpts());
-}
-} // namespace tooling
-} // namespace clang
-#endif // LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H
Removed: cfe/trunk/include/clang/Tooling/Refactoring/SourceCodeBuilders.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/SourceCodeBuilders.h?rev=374270&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/SourceCodeBuilders.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/SourceCodeBuilders.h (removed)
@@ -1,86 +0,0 @@
-//===--- SourceCodeBuilders.h - Source-code building facilities -*- 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file collects facilities for generating source code strings.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_BUILDERS_H_
-#define LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_BUILDERS_H_
-
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Expr.h"
-#include <string>
-
-namespace clang {
-namespace tooling {
-
-/// \name Code analysis utilities.
-/// @{
-/// Ignores implicit object-construction expressions in addition to the normal
-/// implicit expressions that are ignored.
-const Expr *reallyIgnoreImplicit(const Expr &E);
-
-/// Determines whether printing this expression in *any* expression requires
-/// parentheses to preserve its meaning. This analyses is necessarily
-/// conservative because it lacks information about the target context.
-bool mayEverNeedParens(const Expr &E);
-
-/// Determines whether printing this expression to the left of a dot or arrow
-/// operator requires a parentheses to preserve its meaning. Given that
-/// dot/arrow are (effectively) the highest precedence, this is equivalent to
-/// asking whether it ever needs parens.
-inline bool needParensBeforeDotOrArrow(const Expr &E) {
- return mayEverNeedParens(E);
-}
-
-/// Determines whether printing this expression to the right of a unary operator
-/// requires a parentheses to preserve its meaning.
-bool needParensAfterUnaryOperator(const Expr &E);
-/// @}
-
-/// \name Basic code-string generation utilities.
-/// @{
-
-/// Builds source for an expression, adding parens if needed for unambiguous
-/// parsing.
-llvm::Optional<std::string> buildParens(const Expr &E,
- const ASTContext &Context);
-
-/// Builds idiomatic source for the dereferencing of `E`: prefix with `*` but
-/// simplify when it already begins with `&`. \returns empty string on failure.
-llvm::Optional<std::string> buildDereference(const Expr &E,
- const ASTContext &Context);
-
-/// Builds idiomatic source for taking the address of `E`: prefix with `&` but
-/// simplify when it already begins with `*`. \returns empty string on failure.
-llvm::Optional<std::string> buildAddressOf(const Expr &E,
- const ASTContext &Context);
-
-/// Adds a dot to the end of the given expression, but adds parentheses when
-/// needed by the syntax, and simplifies to `->` when possible, e.g.:
-///
-/// `x` becomes `x.`
-/// `*a` becomes `a->`
-/// `a+b` becomes `(a+b).`
-llvm::Optional<std::string> buildDot(const Expr &E, const ASTContext &Context);
-
-/// Adds an arrow to the end of the given expression, but adds parentheses
-/// when needed by the syntax, and simplifies to `.` when possible, e.g.:
-///
-/// `x` becomes `x->`
-/// `&a` becomes `a.`
-/// `a+b` becomes `(a+b)->`
-llvm::Optional<std::string> buildArrow(const Expr &E,
- const ASTContext &Context);
-/// @}
-
-} // namespace tooling
-} // namespace clang
-#endif // LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_BUILDERS_H_
Removed: cfe/trunk/include/clang/Tooling/Refactoring/Stencil.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/Stencil.h?rev=374270&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/Stencil.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/Stencil.h (removed)
@@ -1,226 +0,0 @@
-//===--- Stencil.h - Stencil class ------------------------------*- 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file defines the *Stencil* abstraction: a code-generating object,
-/// parameterized by named references to (bound) AST nodes. Given a match
-/// result, a stencil can be evaluated to a string of source code.
-///
-/// A stencil is similar in spirit to a format string: it is composed of a
-/// series of raw text strings, references to nodes (the parameters) and helper
-/// code-generation operations.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLING_REFACTOR_STENCIL_H_
-#define LLVM_CLANG_TOOLING_REFACTOR_STENCIL_H_
-
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/ASTTypeTraits.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/Tooling/Refactoring/MatchConsumer.h"
-#include "clang/Tooling/Refactoring/RangeSelector.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Error.h"
-#include <string>
-#include <vector>
-
-namespace clang {
-namespace tooling {
-
-/// A stencil is represented as a sequence of "parts" that can each individually
-/// generate a code string based on a match result. The different kinds of
-/// parts include (raw) text, references to bound nodes and assorted operations
-/// on bound nodes.
-///
-/// Users can create custom Stencil operations by implementing this interface.
-class StencilPartInterface {
-public:
- virtual ~StencilPartInterface() = default;
-
- /// Evaluates this part to a string and appends it to \c Result. \c Result is
- /// undefined in the case of an error.
- virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
- std::string *Result) const = 0;
-
- virtual bool isEqual(const StencilPartInterface &other) const = 0;
-
- /// Constructs a string representation of the StencilPart. StencilParts
- /// generated by the `selection` and `run` functions do not have a unique
- /// string representation.
- virtual std::string toString() const = 0;
-
- const void *typeId() const { return TypeId; }
-
-protected:
- StencilPartInterface(const void *DerivedId) : TypeId(DerivedId) {}
-
- // Since this is an abstract class, copying/assigning only make sense for
- // derived classes implementing `clone()`.
- StencilPartInterface(const StencilPartInterface &) = default;
- StencilPartInterface &operator=(const StencilPartInterface &) = default;
-
- /// Unique identifier of the concrete type of this instance. Supports safe
- /// downcasting.
- const void *TypeId;
-};
-
-/// A copyable facade for a std::unique_ptr<StencilPartInterface>. Copies result
-/// in a copy of the underlying pointee object.
-class StencilPart {
-public:
- explicit StencilPart(std::shared_ptr<StencilPartInterface> Impl)
- : Impl(std::move(Impl)) {}
-
- /// See `StencilPartInterface::eval()`.
- llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
- std::string *Result) const {
- return Impl->eval(Match, Result);
- }
-
- bool operator==(const StencilPart &Other) const {
- if (Impl == Other.Impl)
- return true;
- if (Impl == nullptr || Other.Impl == nullptr)
- return false;
- return Impl->isEqual(*Other.Impl);
- }
-
- std::string toString() const {
- if (Impl == nullptr)
- return "";
- return Impl->toString();
- }
-
-private:
- std::shared_ptr<StencilPartInterface> Impl;
-};
-
-/// A sequence of code fragments, references to parameters and code-generation
-/// operations that together can be evaluated to (a fragment of) source code,
-/// given a match result.
-class Stencil {
-public:
- Stencil() = default;
-
- /// Composes a stencil from a series of parts.
- template <typename... Ts> static Stencil cat(Ts &&... Parts) {
- Stencil S;
- S.Parts = {wrap(std::forward<Ts>(Parts))...};
- return S;
- }
-
- /// Appends data from a \p OtherStencil to this stencil.
- void append(Stencil OtherStencil);
-
- // Evaluates the stencil given a match result. Requires that the nodes in the
- // result includes any ids referenced in the stencil. References to missing
- // nodes will result in an invalid_argument error.
- llvm::Expected<std::string>
- eval(const ast_matchers::MatchFinder::MatchResult &Match) const;
-
- // Allow Stencils to operate as std::function, for compatibility with
- // Transformer's TextGenerator.
- llvm::Expected<std::string>
- operator()(const ast_matchers::MatchFinder::MatchResult &Result) const {
- return eval(Result);
- }
-
- /// Constructs a string representation of the Stencil. The string is not
- /// guaranteed to be unique.
- std::string toString() const {
- std::vector<std::string> PartStrings;
- PartStrings.reserve(Parts.size());
- for (const auto &Part : Parts)
- PartStrings.push_back(Part.toString());
- return llvm::join(PartStrings, ", ");
- }
-
-private:
- friend bool operator==(const Stencil &A, const Stencil &B);
- static StencilPart wrap(llvm::StringRef Text);
- static StencilPart wrap(RangeSelector Selector);
- static StencilPart wrap(StencilPart Part) { return Part; }
-
- std::vector<StencilPart> Parts;
-};
-
-inline bool operator==(const Stencil &A, const Stencil &B) {
- return A.Parts == B.Parts;
-}
-
-inline bool operator!=(const Stencil &A, const Stencil &B) { return !(A == B); }
-
-// Functions for conveniently building stencils.
-namespace stencil {
-/// Convenience wrapper for Stencil::cat that can be imported with a using decl.
-template <typename... Ts> Stencil cat(Ts &&... Parts) {
- return Stencil::cat(std::forward<Ts>(Parts)...);
-}
-
-/// \returns exactly the text provided.
-StencilPart text(llvm::StringRef Text);
-
-/// \returns the source corresponding to the selected range.
-StencilPart selection(RangeSelector Selector);
-
-/// \returns the source corresponding to the identified node.
-/// FIXME: Deprecated. Write `selection(node(Id))` instead.
-inline StencilPart node(llvm::StringRef Id) {
- return selection(tooling::node(Id));
-}
-
-/// Generates the source of the expression bound to \p Id, wrapping it in
-/// parentheses if it may parse differently depending on context. For example, a
-/// binary operation is always wrapped, while a variable reference is never
-/// wrapped.
-StencilPart expression(llvm::StringRef Id);
-
-/// Constructs an idiomatic dereferencing of the expression bound to \p ExprId.
-/// \p ExprId is wrapped in parentheses, if needed.
-StencilPart deref(llvm::StringRef ExprId);
-
-/// Constructs an expression that idiomatically takes the address of the
-/// expression bound to \p ExprId. \p ExprId is wrapped in parentheses, if
-/// needed.
-StencilPart addressOf(llvm::StringRef ExprId);
-
-/// Constructs a `MemberExpr` that accesses the named member (\p Member) of the
-/// object bound to \p BaseId. The access is constructed idiomatically: if \p
-/// BaseId is bound to `e` and \p Member identifies member `m`, then returns
-/// `e->m`, when e is a pointer, `e2->m` when e = `*e2` and `e.m` otherwise.
-/// Additionally, `e` is wrapped in parentheses, if needed.
-StencilPart access(llvm::StringRef BaseId, StencilPart Member);
-inline StencilPart access(llvm::StringRef BaseId, llvm::StringRef Member) {
- return access(BaseId, text(Member));
-}
-
-/// Chooses between the two stencil parts, based on whether \p ID is bound in
-/// the match.
-StencilPart ifBound(llvm::StringRef Id, StencilPart TruePart,
- StencilPart FalsePart);
-
-/// Chooses between the two strings, based on whether \p ID is bound in the
-/// match.
-inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText,
- llvm::StringRef FalseText) {
- return ifBound(Id, text(TrueText), text(FalseText));
-}
-
-/// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil.
-/// This supports user-defined extensions to the Stencil language.
-StencilPart run(MatchConsumer<std::string> C);
-
-/// For debug use only; semantics are not guaranteed.
-///
-/// \returns the string resulting from calling the node's print() method.
-StencilPart dPrint(llvm::StringRef Id);
-} // namespace stencil
-} // namespace tooling
-} // namespace clang
-#endif // LLVM_CLANG_TOOLING_REFACTOR_STENCIL_H_
Removed: cfe/trunk/include/clang/Tooling/Refactoring/Transformer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/Transformer.h?rev=374270&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/Transformer.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/Transformer.h (removed)
@@ -1,320 +0,0 @@
-//===--- Transformer.h - Clang source-rewriting library ---------*- 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Defines a library supporting the concise specification of clang-based
-/// source-to-source transformations.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_
-#define LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_
-
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/ASTMatchers/ASTMatchersInternal.h"
-#include "clang/Tooling/Refactoring/AtomicChange.h"
-#include "clang/Tooling/Refactoring/MatchConsumer.h"
-#include "clang/Tooling/Refactoring/RangeSelector.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/Support/Error.h"
-#include <deque>
-#include <functional>
-#include <string>
-#include <type_traits>
-#include <utility>
-
-namespace clang {
-namespace tooling {
-
-using TextGenerator = MatchConsumer<std::string>;
-
-/// Wraps a string as a TextGenerator.
-inline TextGenerator text(std::string M) {
- return [M](const ast_matchers::MatchFinder::MatchResult &)
- -> Expected<std::string> { return M; };
-}
-
-// Description of a source-code edit, expressed in terms of an AST node.
-// Includes: an ID for the (bound) node, a selector for source related to the
-// node, a replacement and, optionally, an explanation for the edit.
-//
-// * Target: the source code impacted by the rule. This identifies an AST node,
-// or part thereof (\c Part), whose source range indicates the extent of the
-// replacement applied by the replacement term. By default, the extent is the
-// node matched by the pattern term (\c NodePart::Node). Target's are typed
-// (\c Kind), which guides the determination of the node extent.
-//
-// * Replacement: a function that produces a replacement string for the target,
-// based on the match result.
-//
-// * Note: (optional) a note specifically for this edit, potentially referencing
-// elements of the match. This will be displayed to the user, where possible;
-// for example, in clang-tidy diagnostics. Use of notes should be rare --
-// explanations of the entire rewrite should be set in the rule
-// (`RewriteRule::Explanation`) instead. Notes serve the rare cases wherein
-// edit-specific diagnostics are required.
-//
-// `ASTEdit` should be built using the `change` convenience functions. For
-// example,
-// \code
-// change(name(fun), text("Frodo"))
-// \endcode
-// Or, if we use Stencil for the TextGenerator:
-// \code
-// using stencil::cat;
-// change(statement(thenNode), cat("{", thenNode, "}"))
-// change(callArgs(call), cat(x, ",", y))
-// \endcode
-// Or, if you are changing the node corresponding to the rule's matcher, you can
-// use the single-argument override of \c change:
-// \code
-// change(cat("different_expr"))
-// \endcode
-struct ASTEdit {
- RangeSelector TargetRange;
- TextGenerator Replacement;
- TextGenerator Note;
-};
-
-/// Format of the path in an include directive -- angle brackets or quotes.
-enum class IncludeFormat {
- Quoted,
- Angled,
-};
-
-/// Description of a source-code transformation.
-//
-// A *rewrite rule* describes a transformation of source code. A simple rule
-// contains each of the following components:
-//
-// * Matcher: the pattern term, expressed as clang matchers (with Transformer
-// extensions).
-//
-// * Edits: a set of Edits to the source code, described with ASTEdits.
-//
-// * Explanation: explanation of the rewrite. This will be displayed to the
-// user, where possible; for example, in clang-tidy diagnostics.
-//
-// However, rules can also consist of (sub)rules, where the first that matches
-// is applied and the rest are ignored. So, the above components are gathered
-// as a `Case` and a rule is a list of cases.
-//
-// Rule cases have an additional, implicit, component: the parameters. These are
-// portions of the pattern which are left unspecified, yet bound in the pattern
-// so that we can reference them in the edits.
-//
-// The \c Transformer class can be used to apply the rewrite rule and obtain the
-// corresponding replacements.
-struct RewriteRule {
- struct Case {
- ast_matchers::internal::DynTypedMatcher Matcher;
- SmallVector<ASTEdit, 1> Edits;
- TextGenerator Explanation;
- // Include paths to add to the file affected by this case. These are
- // bundled with the `Case`, rather than the `RewriteRule`, because each case
- // might have different associated changes to the includes.
- std::vector<std::pair<std::string, IncludeFormat>> AddedIncludes;
- };
- // We expect RewriteRules will most commonly include only one case.
- SmallVector<Case, 1> Cases;
-
- // ID used as the default target of each match. The node described by the
- // matcher is should always be bound to this id.
- static constexpr llvm::StringLiteral RootID = "___root___";
-};
-
-/// Convenience function for constructing a simple \c RewriteRule.
-RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M,
- SmallVector<ASTEdit, 1> Edits,
- TextGenerator Explanation = nullptr);
-
-/// Convenience overload of \c makeRule for common case of only one edit.
-inline RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M,
- ASTEdit Edit,
- TextGenerator Explanation = nullptr) {
- SmallVector<ASTEdit, 1> Edits;
- Edits.emplace_back(std::move(Edit));
- return makeRule(std::move(M), std::move(Edits), std::move(Explanation));
-}
-
-/// For every case in Rule, adds an include directive for the given header. The
-/// common use is assumed to be a rule with only one case. For example, to
-/// replace a function call and add headers corresponding to the new code, one
-/// could write:
-/// \code
-/// auto R = makeRule(callExpr(callee(functionDecl(hasName("foo")))),
-/// change(text("bar()")));
-/// AddInclude(R, "path/to/bar_header.h");
-/// AddInclude(R, "vector", IncludeFormat::Angled);
-/// \endcode
-void addInclude(RewriteRule &Rule, llvm::StringRef Header,
- IncludeFormat Format = IncludeFormat::Quoted);
-
-/// Applies the first rule whose pattern matches; other rules are ignored. If
-/// the matchers are independent then order doesn't matter. In that case,
-/// `applyFirst` is simply joining the set of rules into one.
-//
-// `applyFirst` is like an `anyOf` matcher with an edit action attached to each
-// of its cases. Anywhere you'd use `anyOf(m1.bind("id1"), m2.bind("id2"))` and
-// then dispatch on those ids in your code for control flow, `applyFirst` lifts
-// that behavior to the rule level. So, you can write `applyFirst({makeRule(m1,
-// action1), makeRule(m2, action2), ...});`
-//
-// For example, consider a type `T` with a deterministic serialization function,
-// `serialize()`. For performance reasons, we would like to make it
-// non-deterministic. Therefore, we want to drop the expectation that
-// `a.serialize() = b.serialize() iff a = b` (although we'll maintain
-// `deserialize(a.serialize()) = a`).
-//
-// We have three cases to consider (for some equality function, `eq`):
-// ```
-// eq(a.serialize(), b.serialize()) --> eq(a,b)
-// eq(a, b.serialize()) --> eq(deserialize(a), b)
-// eq(a.serialize(), b) --> eq(a, deserialize(b))
-// ```
-//
-// `applyFirst` allows us to specify each independently:
-// ```
-// auto eq_fun = functionDecl(...);
-// auto method_call = cxxMemberCallExpr(...);
-//
-// auto two_calls = callExpr(callee(eq_fun), hasArgument(0, method_call),
-// hasArgument(1, method_call));
-// auto left_call =
-// callExpr(callee(eq_fun), callExpr(hasArgument(0, method_call)));
-// auto right_call =
-// callExpr(callee(eq_fun), callExpr(hasArgument(1, method_call)));
-//
-// RewriteRule R = applyFirst({makeRule(two_calls, two_calls_action),
-// makeRule(left_call, left_call_action),
-// makeRule(right_call, right_call_action)});
-// ```
-RewriteRule applyFirst(ArrayRef<RewriteRule> Rules);
-
-/// Replaces a portion of the source text with \p Replacement.
-ASTEdit change(RangeSelector Target, TextGenerator Replacement);
-
-/// Replaces the entirety of a RewriteRule's match with \p Replacement. For
-/// example, to replace a function call, one could write:
-/// \code
-/// makeRule(callExpr(callee(functionDecl(hasName("foo")))),
-/// change(text("bar()")))
-/// \endcode
-inline ASTEdit change(TextGenerator Replacement) {
- return change(node(RewriteRule::RootID), std::move(Replacement));
-}
-
-/// Inserts \p Replacement before \p S, leaving the source selected by \S
-/// unchanged.
-inline ASTEdit insertBefore(RangeSelector S, TextGenerator Replacement) {
- return change(before(std::move(S)), std::move(Replacement));
-}
-
-/// Inserts \p Replacement after \p S, leaving the source selected by \S
-/// unchanged.
-inline ASTEdit insertAfter(RangeSelector S, TextGenerator Replacement) {
- return change(after(std::move(S)), std::move(Replacement));
-}
-
-/// Removes the source selected by \p S.
-inline ASTEdit remove(RangeSelector S) {
- return change(std::move(S), text(""));
-}
-
-/// The following three functions are a low-level part of the RewriteRule
-/// API. We expose them for use in implementing the fixtures that interpret
-/// RewriteRule, like Transformer and TransfomerTidy, or for more advanced
-/// users.
-//
-// FIXME: These functions are really public, if advanced, elements of the
-// RewriteRule API. Recast them as such. Or, just declare these functions
-// public and well-supported and move them out of `detail`.
-namespace detail {
-/// Builds a single matcher for the rule, covering all of the rule's cases.
-/// Only supports Rules whose cases' matchers share the same base "kind"
-/// (`Stmt`, `Decl`, etc.) Deprecated: use `buildMatchers` instead, which
-/// supports mixing matchers of different kinds.
-ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule);
-
-/// Builds a set of matchers that cover the rule (one for each distinct node
-/// matcher base kind: Stmt, Decl, etc.). Node-matchers for `QualType` and
-/// `Type` are not permitted, since such nodes carry no source location
-/// information and are therefore not relevant for rewriting. If any such
-/// matchers are included, will return an empty vector.
-std::vector<ast_matchers::internal::DynTypedMatcher>
-buildMatchers(const RewriteRule &Rule);
-
-/// Gets the beginning location of the source matched by a rewrite rule. If the
-/// match occurs within a macro expansion, returns the beginning of the
-/// expansion point. `Result` must come from the matching of a rewrite rule.
-SourceLocation
-getRuleMatchLoc(const ast_matchers::MatchFinder::MatchResult &Result);
-
-/// Returns the \c Case of \c Rule that was selected in the match result.
-/// Assumes a matcher built with \c buildMatcher.
-const RewriteRule::Case &
-findSelectedCase(const ast_matchers::MatchFinder::MatchResult &Result,
- const RewriteRule &Rule);
-
-/// A source "transformation," represented by a character range in the source to
-/// be replaced and a corresponding replacement string.
-struct Transformation {
- CharSourceRange Range;
- std::string Replacement;
-};
-
-/// Attempts to translate `Edits`, which are in terms of AST nodes bound in the
-/// match `Result`, into Transformations, which are in terms of the source code
-/// text.
-///
-/// Returns an empty vector if any of the edits apply to portions of the source
-/// that are ineligible for rewriting (certain interactions with macros, for
-/// example). Fails if any invariants are violated relating to bound nodes in
-/// the match. However, it does not fail in the case of conflicting edits --
-/// conflict handling is left to clients. We recommend use of the \c
-/// AtomicChange or \c Replacements classes for assistance in detecting such
-/// conflicts.
-Expected<SmallVector<Transformation, 1>>
-translateEdits(const ast_matchers::MatchFinder::MatchResult &Result,
- llvm::ArrayRef<ASTEdit> Edits);
-} // namespace detail
-
-/// Handles the matcher and callback registration for a single rewrite rule, as
-/// defined by the arguments of the constructor.
-class Transformer : public ast_matchers::MatchFinder::MatchCallback {
-public:
- using ChangeConsumer =
- std::function<void(Expected<clang::tooling::AtomicChange> Change)>;
-
- /// \param Consumer Receives each rewrite or error. Will not necessarily be
- /// called for each match; for example, if the rewrite is not applicable
- /// because of macros, but doesn't fail. Note that clients are responsible
- /// for handling the case that independent \c AtomicChanges conflict with each
- /// other.
- Transformer(RewriteRule Rule, ChangeConsumer Consumer)
- : Rule(std::move(Rule)), Consumer(std::move(Consumer)) {}
-
- /// N.B. Passes `this` pointer to `MatchFinder`. So, this object should not
- /// be moved after this call.
- void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
-
- /// Not called directly by users -- called by the framework, via base class
- /// pointer.
- void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
-
-private:
- RewriteRule Rule;
- /// Receives each successful rewrites as an \c AtomicChange.
- ChangeConsumer Consumer;
-};
-} // namespace tooling
-} // namespace clang
-
-#endif // LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_
Added: cfe/trunk/include/clang/Tooling/Transformer/MatchConsumer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Transformer/MatchConsumer.h?rev=374271&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Transformer/MatchConsumer.h (added)
+++ cfe/trunk/include/clang/Tooling/Transformer/MatchConsumer.h Wed Oct 9 19:34:47 2019
@@ -0,0 +1,58 @@
+//===--- MatchConsumer.h - MatchConsumer abstraction ------------*- 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file defines the *MatchConsumer* abstraction: a computation over
+/// match results, specifically the `ast_matchers::MatchFinder::MatchResult`
+/// class.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_MATCH_CONSUMER_H_
+#define LLVM_CLANG_TOOLING_TRANSFORMER_MATCH_CONSUMER_H_
+
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace tooling {
+
+/// A failable computation over nodes bound by AST matchers.
+///
+/// The computation should report any errors though its return value (rather
+/// than terminating the program) to enable usage in interactive scenarios like
+/// clang-query.
+///
+/// This is a central abstraction of the Transformer framework.
+template <typename T>
+using MatchConsumer =
+ std::function<Expected<T>(const ast_matchers::MatchFinder::MatchResult &)>;
+
+/// Creates an error that signals that a `MatchConsumer` expected a certain node
+/// to be bound by AST matchers, but it was not actually bound.
+inline llvm::Error notBoundError(llvm::StringRef Id) {
+ return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
+ "Id not bound: " + Id);
+}
+
+/// Chooses between the two consumers, based on whether \p ID is bound in the
+/// match.
+template <typename T>
+MatchConsumer<T> ifBound(std::string ID, MatchConsumer<T> TrueC,
+ MatchConsumer<T> FalseC) {
+ return [=](const ast_matchers::MatchFinder::MatchResult &Result) {
+ auto &Map = Result.Nodes.getMap();
+ return (Map.find(ID) != Map.end() ? TrueC : FalseC)(Result);
+ };
+}
+
+} // namespace tooling
+} // namespace clang
+#endif // LLVM_CLANG_TOOLING_TRANSFORMER_MATCH_CONSUMER_H_
Added: cfe/trunk/include/clang/Tooling/Transformer/RangeSelector.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Transformer/RangeSelector.h?rev=374271&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Transformer/RangeSelector.h (added)
+++ cfe/trunk/include/clang/Tooling/Transformer/RangeSelector.h Wed Oct 9 19:34:47 2019
@@ -0,0 +1,93 @@
+//===--- RangeSelector.h - Source-selection library ---------*- 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines a combinator library supporting the definition of _selectors_,
+/// which select source ranges based on (bound) AST nodes.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_
+#define LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Transformer/MatchConsumer.h"
+#include "llvm/Support/Error.h"
+#include <functional>
+#include <string>
+
+namespace clang {
+namespace tooling {
+using RangeSelector = MatchConsumer<CharSourceRange>;
+
+inline RangeSelector charRange(CharSourceRange R) {
+ return [R](const ast_matchers::MatchFinder::MatchResult &)
+ -> Expected<CharSourceRange> { return R; };
+}
+
+/// Selects from the start of \p Begin and to the end of \p End.
+RangeSelector range(RangeSelector Begin, RangeSelector End);
+
+/// Convenience version of \c range where end-points are bound nodes.
+RangeSelector range(std::string BeginID, std::string EndID);
+
+/// Selects the (empty) range [B,B) when \p Selector selects the range [B,E).
+RangeSelector before(RangeSelector Selector);
+
+/// Selects the the point immediately following \p Selector. That is, the
+/// (empty) range [E,E), when \p Selector selects either
+/// * the CharRange [B,E) or
+/// * the TokenRange [B,E'] where the token at E' spans the range [E,E').
+RangeSelector after(RangeSelector Selector);
+
+/// Selects a node, including trailing semicolon (for non-expression
+/// statements). \p ID is the node's binding in the match result.
+RangeSelector node(std::string ID);
+
+/// Selects a node, including trailing semicolon (always). Useful for selecting
+/// expression statements. \p ID is the node's binding in the match result.
+RangeSelector statement(std::string ID);
+
+/// Given a \c MemberExpr, selects the member token. \p ID is the node's
+/// binding in the match result.
+RangeSelector member(std::string ID);
+
+/// Given a node with a "name", (like \c NamedDecl, \c DeclRefExpr or \c
+/// CxxCtorInitializer) selects the name's token. Only selects the final
+/// identifier of a qualified name, but not any qualifiers or template
+/// arguments. For example, for `::foo::bar::baz` and `::foo::bar::baz<int>`,
+/// it selects only `baz`.
+///
+/// \param ID is the node's binding in the match result.
+RangeSelector name(std::string ID);
+
+// Given a \c CallExpr (bound to \p ID), selects the arguments' source text (all
+// source between the call's parentheses).
+RangeSelector callArgs(std::string ID);
+
+// Given a \c CompoundStmt (bound to \p ID), selects the source of the
+// statements (all source between the braces).
+RangeSelector statements(std::string ID);
+
+// Given a \c InitListExpr (bound to \p ID), selects the range of the elements
+// (all source between the braces).
+RangeSelector initListElements(std::string ID);
+
+/// Given an \IfStmt (bound to \p ID), selects the range of the else branch,
+/// starting from the \c else keyword.
+RangeSelector elseBranch(std::string ID);
+
+/// Selects the range from which `S` was expanded (possibly along with other
+/// source), if `S` is an expansion, and `S` itself, otherwise. Corresponds to
+/// `SourceManager::getExpansionRange`.
+RangeSelector expansion(RangeSelector S);
+} // namespace tooling
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_
Added: cfe/trunk/include/clang/Tooling/Transformer/SourceCode.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Transformer/SourceCode.h?rev=374271&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Transformer/SourceCode.h (added)
+++ cfe/trunk/include/clang/Tooling/Transformer/SourceCode.h Wed Oct 9 19:34:47 2019
@@ -0,0 +1,90 @@
+//===--- SourceCode.h - 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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_H
+#define LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
+
+namespace clang {
+namespace tooling {
+
+/// Extends \p Range to include the token \p Next, if it immediately follows the
+/// end of the range. Otherwise, returns \p Range unchanged.
+CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next,
+ ASTContext &Context);
+
+/// Returns the source range spanning the node, extended to include \p Next, if
+/// it immediately follows \p Node. Otherwise, returns the normal range of \p
+/// Node. See comments on `getExtendedText()` for examples.
+template <typename T>
+CharSourceRange getExtendedRange(const T &Node, tok::TokenKind Next,
+ ASTContext &Context) {
+ return maybeExtendRange(CharSourceRange::getTokenRange(Node.getSourceRange()),
+ Next, Context);
+}
+
+/// Returns the source-code text in the specified range.
+StringRef getText(CharSourceRange Range, const ASTContext &Context);
+
+/// Returns the source-code text corresponding to \p Node.
+template <typename T>
+StringRef getText(const T &Node, const ASTContext &Context) {
+ return getText(CharSourceRange::getTokenRange(Node.getSourceRange()),
+ Context);
+}
+
+/// Returns the source text of the node, extended to include \p Next, if it
+/// immediately follows the node. Otherwise, returns the text of just \p Node.
+///
+/// For example, given statements S1 and S2 below:
+/// \code
+/// {
+/// // S1:
+/// if (!x) return foo();
+/// // S2:
+/// if (!x) { return 3; }
+/// }
+/// \endcode
+/// then
+/// \code
+/// getText(S1, Context) = "if (!x) return foo()"
+/// getExtendedText(S1, tok::TokenKind::semi, Context)
+/// = "if (!x) return foo();"
+/// getExtendedText(*S1.getThen(), tok::TokenKind::semi, Context)
+/// = "return foo();"
+/// getExtendedText(*S2.getThen(), tok::TokenKind::semi, Context)
+/// = getText(S2, Context) = "{ return 3; }"
+/// \endcode
+template <typename T>
+StringRef getExtendedText(const T &Node, tok::TokenKind Next,
+ ASTContext &Context) {
+ return getText(getExtendedRange(Node, Next, Context), Context);
+}
+
+// Attempts to resolve the given range to one that can be edited by a rewrite;
+// generally, one that starts and ends within a particular file. It supports
+// a limited set of cases involving source locations in macro expansions.
+llvm::Optional<CharSourceRange>
+getRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+inline llvm::Optional<CharSourceRange>
+getRangeForEdit(const CharSourceRange &EditRange, const ASTContext &Context) {
+ return getRangeForEdit(EditRange, Context.getSourceManager(),
+ Context.getLangOpts());
+}
+} // namespace tooling
+} // namespace clang
+#endif // LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_H
Added: cfe/trunk/include/clang/Tooling/Transformer/SourceCodeBuilders.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Transformer/SourceCodeBuilders.h?rev=374271&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Transformer/SourceCodeBuilders.h (added)
+++ cfe/trunk/include/clang/Tooling/Transformer/SourceCodeBuilders.h Wed Oct 9 19:34:47 2019
@@ -0,0 +1,86 @@
+//===--- SourceCodeBuilders.h - Source-code building facilities -*- 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file collects facilities for generating source code strings.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_BUILDERS_H_
+#define LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_BUILDERS_H_
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include <string>
+
+namespace clang {
+namespace tooling {
+
+/// \name Code analysis utilities.
+/// @{
+/// Ignores implicit object-construction expressions in addition to the normal
+/// implicit expressions that are ignored.
+const Expr *reallyIgnoreImplicit(const Expr &E);
+
+/// Determines whether printing this expression in *any* expression requires
+/// parentheses to preserve its meaning. This analyses is necessarily
+/// conservative because it lacks information about the target context.
+bool mayEverNeedParens(const Expr &E);
+
+/// Determines whether printing this expression to the left of a dot or arrow
+/// operator requires a parentheses to preserve its meaning. Given that
+/// dot/arrow are (effectively) the highest precedence, this is equivalent to
+/// asking whether it ever needs parens.
+inline bool needParensBeforeDotOrArrow(const Expr &E) {
+ return mayEverNeedParens(E);
+}
+
+/// Determines whether printing this expression to the right of a unary operator
+/// requires a parentheses to preserve its meaning.
+bool needParensAfterUnaryOperator(const Expr &E);
+/// @}
+
+/// \name Basic code-string generation utilities.
+/// @{
+
+/// Builds source for an expression, adding parens if needed for unambiguous
+/// parsing.
+llvm::Optional<std::string> buildParens(const Expr &E,
+ const ASTContext &Context);
+
+/// Builds idiomatic source for the dereferencing of `E`: prefix with `*` but
+/// simplify when it already begins with `&`. \returns empty string on failure.
+llvm::Optional<std::string> buildDereference(const Expr &E,
+ const ASTContext &Context);
+
+/// Builds idiomatic source for taking the address of `E`: prefix with `&` but
+/// simplify when it already begins with `*`. \returns empty string on failure.
+llvm::Optional<std::string> buildAddressOf(const Expr &E,
+ const ASTContext &Context);
+
+/// Adds a dot to the end of the given expression, but adds parentheses when
+/// needed by the syntax, and simplifies to `->` when possible, e.g.:
+///
+/// `x` becomes `x.`
+/// `*a` becomes `a->`
+/// `a+b` becomes `(a+b).`
+llvm::Optional<std::string> buildDot(const Expr &E, const ASTContext &Context);
+
+/// Adds an arrow to the end of the given expression, but adds parentheses
+/// when needed by the syntax, and simplifies to `.` when possible, e.g.:
+///
+/// `x` becomes `x->`
+/// `&a` becomes `a.`
+/// `a+b` becomes `(a+b)->`
+llvm::Optional<std::string> buildArrow(const Expr &E,
+ const ASTContext &Context);
+/// @}
+
+} // namespace tooling
+} // namespace clang
+#endif // LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_BUILDERS_H_
Added: cfe/trunk/include/clang/Tooling/Transformer/Stencil.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Transformer/Stencil.h?rev=374271&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Transformer/Stencil.h (added)
+++ cfe/trunk/include/clang/Tooling/Transformer/Stencil.h Wed Oct 9 19:34:47 2019
@@ -0,0 +1,226 @@
+//===--- Stencil.h - Stencil class ------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines the *Stencil* abstraction: a code-generating object,
+/// parameterized by named references to (bound) AST nodes. Given a match
+/// result, a stencil can be evaluated to a string of source code.
+///
+/// A stencil is similar in spirit to a format string: it is composed of a
+/// series of raw text strings, references to nodes (the parameters) and helper
+/// code-generation operations.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_
+#define LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Transformer/MatchConsumer.h"
+#include "clang/Tooling/Transformer/RangeSelector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tooling {
+
+/// A stencil is represented as a sequence of "parts" that can each individually
+/// generate a code string based on a match result. The different kinds of
+/// parts include (raw) text, references to bound nodes and assorted operations
+/// on bound nodes.
+///
+/// Users can create custom Stencil operations by implementing this interface.
+class StencilPartInterface {
+public:
+ virtual ~StencilPartInterface() = default;
+
+ /// Evaluates this part to a string and appends it to \c Result. \c Result is
+ /// undefined in the case of an error.
+ virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
+ std::string *Result) const = 0;
+
+ virtual bool isEqual(const StencilPartInterface &other) const = 0;
+
+ /// Constructs a string representation of the StencilPart. StencilParts
+ /// generated by the `selection` and `run` functions do not have a unique
+ /// string representation.
+ virtual std::string toString() const = 0;
+
+ const void *typeId() const { return TypeId; }
+
+protected:
+ StencilPartInterface(const void *DerivedId) : TypeId(DerivedId) {}
+
+ // Since this is an abstract class, copying/assigning only make sense for
+ // derived classes implementing `clone()`.
+ StencilPartInterface(const StencilPartInterface &) = default;
+ StencilPartInterface &operator=(const StencilPartInterface &) = default;
+
+ /// Unique identifier of the concrete type of this instance. Supports safe
+ /// downcasting.
+ const void *TypeId;
+};
+
+/// A copyable facade for a std::unique_ptr<StencilPartInterface>. Copies result
+/// in a copy of the underlying pointee object.
+class StencilPart {
+public:
+ explicit StencilPart(std::shared_ptr<StencilPartInterface> Impl)
+ : Impl(std::move(Impl)) {}
+
+ /// See `StencilPartInterface::eval()`.
+ llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
+ std::string *Result) const {
+ return Impl->eval(Match, Result);
+ }
+
+ bool operator==(const StencilPart &Other) const {
+ if (Impl == Other.Impl)
+ return true;
+ if (Impl == nullptr || Other.Impl == nullptr)
+ return false;
+ return Impl->isEqual(*Other.Impl);
+ }
+
+ std::string toString() const {
+ if (Impl == nullptr)
+ return "";
+ return Impl->toString();
+ }
+
+private:
+ std::shared_ptr<StencilPartInterface> Impl;
+};
+
+/// A sequence of code fragments, references to parameters and code-generation
+/// operations that together can be evaluated to (a fragment of) source code,
+/// given a match result.
+class Stencil {
+public:
+ Stencil() = default;
+
+ /// Composes a stencil from a series of parts.
+ template <typename... Ts> static Stencil cat(Ts &&... Parts) {
+ Stencil S;
+ S.Parts = {wrap(std::forward<Ts>(Parts))...};
+ return S;
+ }
+
+ /// Appends data from a \p OtherStencil to this stencil.
+ void append(Stencil OtherStencil);
+
+ // Evaluates the stencil given a match result. Requires that the nodes in the
+ // result includes any ids referenced in the stencil. References to missing
+ // nodes will result in an invalid_argument error.
+ llvm::Expected<std::string>
+ eval(const ast_matchers::MatchFinder::MatchResult &Match) const;
+
+ // Allow Stencils to operate as std::function, for compatibility with
+ // Transformer's TextGenerator.
+ llvm::Expected<std::string>
+ operator()(const ast_matchers::MatchFinder::MatchResult &Result) const {
+ return eval(Result);
+ }
+
+ /// Constructs a string representation of the Stencil. The string is not
+ /// guaranteed to be unique.
+ std::string toString() const {
+ std::vector<std::string> PartStrings;
+ PartStrings.reserve(Parts.size());
+ for (const auto &Part : Parts)
+ PartStrings.push_back(Part.toString());
+ return llvm::join(PartStrings, ", ");
+ }
+
+private:
+ friend bool operator==(const Stencil &A, const Stencil &B);
+ static StencilPart wrap(llvm::StringRef Text);
+ static StencilPart wrap(RangeSelector Selector);
+ static StencilPart wrap(StencilPart Part) { return Part; }
+
+ std::vector<StencilPart> Parts;
+};
+
+inline bool operator==(const Stencil &A, const Stencil &B) {
+ return A.Parts == B.Parts;
+}
+
+inline bool operator!=(const Stencil &A, const Stencil &B) { return !(A == B); }
+
+// Functions for conveniently building stencils.
+namespace stencil {
+/// Convenience wrapper for Stencil::cat that can be imported with a using decl.
+template <typename... Ts> Stencil cat(Ts &&... Parts) {
+ return Stencil::cat(std::forward<Ts>(Parts)...);
+}
+
+/// \returns exactly the text provided.
+StencilPart text(llvm::StringRef Text);
+
+/// \returns the source corresponding to the selected range.
+StencilPart selection(RangeSelector Selector);
+
+/// \returns the source corresponding to the identified node.
+/// FIXME: Deprecated. Write `selection(node(Id))` instead.
+inline StencilPart node(llvm::StringRef Id) {
+ return selection(tooling::node(Id));
+}
+
+/// Generates the source of the expression bound to \p Id, wrapping it in
+/// parentheses if it may parse differently depending on context. For example, a
+/// binary operation is always wrapped, while a variable reference is never
+/// wrapped.
+StencilPart expression(llvm::StringRef Id);
+
+/// Constructs an idiomatic dereferencing of the expression bound to \p ExprId.
+/// \p ExprId is wrapped in parentheses, if needed.
+StencilPart deref(llvm::StringRef ExprId);
+
+/// Constructs an expression that idiomatically takes the address of the
+/// expression bound to \p ExprId. \p ExprId is wrapped in parentheses, if
+/// needed.
+StencilPart addressOf(llvm::StringRef ExprId);
+
+/// Constructs a `MemberExpr` that accesses the named member (\p Member) of the
+/// object bound to \p BaseId. The access is constructed idiomatically: if \p
+/// BaseId is bound to `e` and \p Member identifies member `m`, then returns
+/// `e->m`, when e is a pointer, `e2->m` when e = `*e2` and `e.m` otherwise.
+/// Additionally, `e` is wrapped in parentheses, if needed.
+StencilPart access(llvm::StringRef BaseId, StencilPart Member);
+inline StencilPart access(llvm::StringRef BaseId, llvm::StringRef Member) {
+ return access(BaseId, text(Member));
+}
+
+/// Chooses between the two stencil parts, based on whether \p ID is bound in
+/// the match.
+StencilPart ifBound(llvm::StringRef Id, StencilPart TruePart,
+ StencilPart FalsePart);
+
+/// Chooses between the two strings, based on whether \p ID is bound in the
+/// match.
+inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText,
+ llvm::StringRef FalseText) {
+ return ifBound(Id, text(TrueText), text(FalseText));
+}
+
+/// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil.
+/// This supports user-defined extensions to the Stencil language.
+StencilPart run(MatchConsumer<std::string> C);
+
+/// For debug use only; semantics are not guaranteed.
+///
+/// \returns the string resulting from calling the node's print() method.
+StencilPart dPrint(llvm::StringRef Id);
+} // namespace stencil
+} // namespace tooling
+} // namespace clang
+#endif // LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_
Added: cfe/trunk/include/clang/Tooling/Transformer/Transformer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Transformer/Transformer.h?rev=374271&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Transformer/Transformer.h (added)
+++ cfe/trunk/include/clang/Tooling/Transformer/Transformer.h Wed Oct 9 19:34:47 2019
@@ -0,0 +1,320 @@
+//===--- Transformer.h - Clang source-rewriting library ---------*- 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines a library supporting the concise specification of clang-based
+/// source-to-source transformations.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
+#define LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "clang/Tooling/Transformer/MatchConsumer.h"
+#include "clang/Tooling/Transformer/RangeSelector.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Error.h"
+#include <deque>
+#include <functional>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+namespace clang {
+namespace tooling {
+
+using TextGenerator = MatchConsumer<std::string>;
+
+/// Wraps a string as a TextGenerator.
+inline TextGenerator text(std::string M) {
+ return [M](const ast_matchers::MatchFinder::MatchResult &)
+ -> Expected<std::string> { return M; };
+}
+
+// Description of a source-code edit, expressed in terms of an AST node.
+// Includes: an ID for the (bound) node, a selector for source related to the
+// node, a replacement and, optionally, an explanation for the edit.
+//
+// * Target: the source code impacted by the rule. This identifies an AST node,
+// or part thereof (\c Part), whose source range indicates the extent of the
+// replacement applied by the replacement term. By default, the extent is the
+// node matched by the pattern term (\c NodePart::Node). Target's are typed
+// (\c Kind), which guides the determination of the node extent.
+//
+// * Replacement: a function that produces a replacement string for the target,
+// based on the match result.
+//
+// * Note: (optional) a note specifically for this edit, potentially referencing
+// elements of the match. This will be displayed to the user, where possible;
+// for example, in clang-tidy diagnostics. Use of notes should be rare --
+// explanations of the entire rewrite should be set in the rule
+// (`RewriteRule::Explanation`) instead. Notes serve the rare cases wherein
+// edit-specific diagnostics are required.
+//
+// `ASTEdit` should be built using the `change` convenience functions. For
+// example,
+// \code
+// change(name(fun), text("Frodo"))
+// \endcode
+// Or, if we use Stencil for the TextGenerator:
+// \code
+// using stencil::cat;
+// change(statement(thenNode), cat("{", thenNode, "}"))
+// change(callArgs(call), cat(x, ",", y))
+// \endcode
+// Or, if you are changing the node corresponding to the rule's matcher, you can
+// use the single-argument override of \c change:
+// \code
+// change(cat("different_expr"))
+// \endcode
+struct ASTEdit {
+ RangeSelector TargetRange;
+ TextGenerator Replacement;
+ TextGenerator Note;
+};
+
+/// Format of the path in an include directive -- angle brackets or quotes.
+enum class IncludeFormat {
+ Quoted,
+ Angled,
+};
+
+/// Description of a source-code transformation.
+//
+// A *rewrite rule* describes a transformation of source code. A simple rule
+// contains each of the following components:
+//
+// * Matcher: the pattern term, expressed as clang matchers (with Transformer
+// extensions).
+//
+// * Edits: a set of Edits to the source code, described with ASTEdits.
+//
+// * Explanation: explanation of the rewrite. This will be displayed to the
+// user, where possible; for example, in clang-tidy diagnostics.
+//
+// However, rules can also consist of (sub)rules, where the first that matches
+// is applied and the rest are ignored. So, the above components are gathered
+// as a `Case` and a rule is a list of cases.
+//
+// Rule cases have an additional, implicit, component: the parameters. These are
+// portions of the pattern which are left unspecified, yet bound in the pattern
+// so that we can reference them in the edits.
+//
+// The \c Transformer class can be used to apply the rewrite rule and obtain the
+// corresponding replacements.
+struct RewriteRule {
+ struct Case {
+ ast_matchers::internal::DynTypedMatcher Matcher;
+ SmallVector<ASTEdit, 1> Edits;
+ TextGenerator Explanation;
+ // Include paths to add to the file affected by this case. These are
+ // bundled with the `Case`, rather than the `RewriteRule`, because each case
+ // might have different associated changes to the includes.
+ std::vector<std::pair<std::string, IncludeFormat>> AddedIncludes;
+ };
+ // We expect RewriteRules will most commonly include only one case.
+ SmallVector<Case, 1> Cases;
+
+ // ID used as the default target of each match. The node described by the
+ // matcher is should always be bound to this id.
+ static constexpr llvm::StringLiteral RootID = "___root___";
+};
+
+/// Convenience function for constructing a simple \c RewriteRule.
+RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M,
+ SmallVector<ASTEdit, 1> Edits,
+ TextGenerator Explanation = nullptr);
+
+/// Convenience overload of \c makeRule for common case of only one edit.
+inline RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M,
+ ASTEdit Edit,
+ TextGenerator Explanation = nullptr) {
+ SmallVector<ASTEdit, 1> Edits;
+ Edits.emplace_back(std::move(Edit));
+ return makeRule(std::move(M), std::move(Edits), std::move(Explanation));
+}
+
+/// For every case in Rule, adds an include directive for the given header. The
+/// common use is assumed to be a rule with only one case. For example, to
+/// replace a function call and add headers corresponding to the new code, one
+/// could write:
+/// \code
+/// auto R = makeRule(callExpr(callee(functionDecl(hasName("foo")))),
+/// change(text("bar()")));
+/// AddInclude(R, "path/to/bar_header.h");
+/// AddInclude(R, "vector", IncludeFormat::Angled);
+/// \endcode
+void addInclude(RewriteRule &Rule, llvm::StringRef Header,
+ IncludeFormat Format = IncludeFormat::Quoted);
+
+/// Applies the first rule whose pattern matches; other rules are ignored. If
+/// the matchers are independent then order doesn't matter. In that case,
+/// `applyFirst` is simply joining the set of rules into one.
+//
+// `applyFirst` is like an `anyOf` matcher with an edit action attached to each
+// of its cases. Anywhere you'd use `anyOf(m1.bind("id1"), m2.bind("id2"))` and
+// then dispatch on those ids in your code for control flow, `applyFirst` lifts
+// that behavior to the rule level. So, you can write `applyFirst({makeRule(m1,
+// action1), makeRule(m2, action2), ...});`
+//
+// For example, consider a type `T` with a deterministic serialization function,
+// `serialize()`. For performance reasons, we would like to make it
+// non-deterministic. Therefore, we want to drop the expectation that
+// `a.serialize() = b.serialize() iff a = b` (although we'll maintain
+// `deserialize(a.serialize()) = a`).
+//
+// We have three cases to consider (for some equality function, `eq`):
+// ```
+// eq(a.serialize(), b.serialize()) --> eq(a,b)
+// eq(a, b.serialize()) --> eq(deserialize(a), b)
+// eq(a.serialize(), b) --> eq(a, deserialize(b))
+// ```
+//
+// `applyFirst` allows us to specify each independently:
+// ```
+// auto eq_fun = functionDecl(...);
+// auto method_call = cxxMemberCallExpr(...);
+//
+// auto two_calls = callExpr(callee(eq_fun), hasArgument(0, method_call),
+// hasArgument(1, method_call));
+// auto left_call =
+// callExpr(callee(eq_fun), callExpr(hasArgument(0, method_call)));
+// auto right_call =
+// callExpr(callee(eq_fun), callExpr(hasArgument(1, method_call)));
+//
+// RewriteRule R = applyFirst({makeRule(two_calls, two_calls_action),
+// makeRule(left_call, left_call_action),
+// makeRule(right_call, right_call_action)});
+// ```
+RewriteRule applyFirst(ArrayRef<RewriteRule> Rules);
+
+/// Replaces a portion of the source text with \p Replacement.
+ASTEdit change(RangeSelector Target, TextGenerator Replacement);
+
+/// Replaces the entirety of a RewriteRule's match with \p Replacement. For
+/// example, to replace a function call, one could write:
+/// \code
+/// makeRule(callExpr(callee(functionDecl(hasName("foo")))),
+/// change(text("bar()")))
+/// \endcode
+inline ASTEdit change(TextGenerator Replacement) {
+ return change(node(RewriteRule::RootID), std::move(Replacement));
+}
+
+/// Inserts \p Replacement before \p S, leaving the source selected by \S
+/// unchanged.
+inline ASTEdit insertBefore(RangeSelector S, TextGenerator Replacement) {
+ return change(before(std::move(S)), std::move(Replacement));
+}
+
+/// Inserts \p Replacement after \p S, leaving the source selected by \S
+/// unchanged.
+inline ASTEdit insertAfter(RangeSelector S, TextGenerator Replacement) {
+ return change(after(std::move(S)), std::move(Replacement));
+}
+
+/// Removes the source selected by \p S.
+inline ASTEdit remove(RangeSelector S) {
+ return change(std::move(S), text(""));
+}
+
+/// The following three functions are a low-level part of the RewriteRule
+/// API. We expose them for use in implementing the fixtures that interpret
+/// RewriteRule, like Transformer and TransfomerTidy, or for more advanced
+/// users.
+//
+// FIXME: These functions are really public, if advanced, elements of the
+// RewriteRule API. Recast them as such. Or, just declare these functions
+// public and well-supported and move them out of `detail`.
+namespace detail {
+/// Builds a single matcher for the rule, covering all of the rule's cases.
+/// Only supports Rules whose cases' matchers share the same base "kind"
+/// (`Stmt`, `Decl`, etc.) Deprecated: use `buildMatchers` instead, which
+/// supports mixing matchers of different kinds.
+ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule);
+
+/// Builds a set of matchers that cover the rule (one for each distinct node
+/// matcher base kind: Stmt, Decl, etc.). Node-matchers for `QualType` and
+/// `Type` are not permitted, since such nodes carry no source location
+/// information and are therefore not relevant for rewriting. If any such
+/// matchers are included, will return an empty vector.
+std::vector<ast_matchers::internal::DynTypedMatcher>
+buildMatchers(const RewriteRule &Rule);
+
+/// Gets the beginning location of the source matched by a rewrite rule. If the
+/// match occurs within a macro expansion, returns the beginning of the
+/// expansion point. `Result` must come from the matching of a rewrite rule.
+SourceLocation
+getRuleMatchLoc(const ast_matchers::MatchFinder::MatchResult &Result);
+
+/// Returns the \c Case of \c Rule that was selected in the match result.
+/// Assumes a matcher built with \c buildMatcher.
+const RewriteRule::Case &
+findSelectedCase(const ast_matchers::MatchFinder::MatchResult &Result,
+ const RewriteRule &Rule);
+
+/// A source "transformation," represented by a character range in the source to
+/// be replaced and a corresponding replacement string.
+struct Transformation {
+ CharSourceRange Range;
+ std::string Replacement;
+};
+
+/// Attempts to translate `Edits`, which are in terms of AST nodes bound in the
+/// match `Result`, into Transformations, which are in terms of the source code
+/// text.
+///
+/// Returns an empty vector if any of the edits apply to portions of the source
+/// that are ineligible for rewriting (certain interactions with macros, for
+/// example). Fails if any invariants are violated relating to bound nodes in
+/// the match. However, it does not fail in the case of conflicting edits --
+/// conflict handling is left to clients. We recommend use of the \c
+/// AtomicChange or \c Replacements classes for assistance in detecting such
+/// conflicts.
+Expected<SmallVector<Transformation, 1>>
+translateEdits(const ast_matchers::MatchFinder::MatchResult &Result,
+ llvm::ArrayRef<ASTEdit> Edits);
+} // namespace detail
+
+/// Handles the matcher and callback registration for a single rewrite rule, as
+/// defined by the arguments of the constructor.
+class Transformer : public ast_matchers::MatchFinder::MatchCallback {
+public:
+ using ChangeConsumer =
+ std::function<void(Expected<clang::tooling::AtomicChange> Change)>;
+
+ /// \param Consumer Receives each rewrite or error. Will not necessarily be
+ /// called for each match; for example, if the rewrite is not applicable
+ /// because of macros, but doesn't fail. Note that clients are responsible
+ /// for handling the case that independent \c AtomicChanges conflict with each
+ /// other.
+ Transformer(RewriteRule Rule, ChangeConsumer Consumer)
+ : Rule(std::move(Rule)), Consumer(std::move(Consumer)) {}
+
+ /// N.B. Passes `this` pointer to `MatchFinder`. So, this object should not
+ /// be moved after this call.
+ void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
+
+ /// Not called directly by users -- called by the framework, via base class
+ /// pointer.
+ void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ RewriteRule Rule;
+ /// Receives each successful rewrites as an \c AtomicChange.
+ ChangeConsumer Consumer;
+};
+} // namespace tooling
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
Modified: cfe/trunk/lib/Tooling/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/CMakeLists.txt?rev=374271&r1=374270&r2=374271&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/CMakeLists.txt (original)
+++ cfe/trunk/lib/Tooling/CMakeLists.txt Wed Oct 9 19:34:47 2019
@@ -9,6 +9,7 @@ add_subdirectory(Refactoring)
add_subdirectory(ASTDiff)
add_subdirectory(Syntax)
add_subdirectory(DependencyScanning)
+add_subdirectory(Transformer)
add_clang_library(clangTooling
AllTUsExecution.cpp
Modified: cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt?rev=374271&r1=374270&r2=374271&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt (original)
+++ cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt Wed Oct 9 19:34:47 2019
@@ -6,17 +6,12 @@ add_clang_library(clangToolingRefactorin
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
Removed: cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp?rev=374270&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp (removed)
@@ -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);
- };
-}
Removed: cfe/trunk/lib/Tooling/Refactoring/SourceCode.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/SourceCode.cpp?rev=374270&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/SourceCode.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/SourceCode.cpp (removed)
@@ -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;
-}
Removed: cfe/trunk/lib/Tooling/Refactoring/SourceCodeBuilders.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/SourceCodeBuilders.cpp?rev=374270&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/SourceCodeBuilders.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/SourceCodeBuilders.cpp (removed)
@@ -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();
-}
Removed: cfe/trunk/lib/Tooling/Refactoring/Stencil.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/Stencil.cpp?rev=374270&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/Stencil.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/Stencil.cpp (removed)
@@ -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)));
-}
Removed: cfe/trunk/lib/Tooling/Refactoring/Transformer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/Transformer.cpp?rev=374270&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/Transformer.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/Transformer.cpp (removed)
@@ -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));
-}
Added: cfe/trunk/lib/Tooling/Transformer/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Transformer/CMakeLists.txt?rev=374271&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Transformer/CMakeLists.txt (added)
+++ cfe/trunk/lib/Tooling/Transformer/CMakeLists.txt Wed Oct 9 19:34:47 2019
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+add_clang_library(clangTransformer
+ RangeSelector.cpp
+ SourceCode.cpp
+ SourceCodeBuilders.cpp
+ Stencil.cpp
+ Transformer.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangToolingCore
+ clangToolingRefactoring
+ )
Added: cfe/trunk/lib/Tooling/Transformer/RangeSelector.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Transformer/RangeSelector.cpp?rev=374271&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Transformer/RangeSelector.cpp (added)
+++ cfe/trunk/lib/Tooling/Transformer/RangeSelector.cpp Wed Oct 9 19:34:47 2019
@@ -0,0 +1,312 @@
+//===--- 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/Transformer/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/Transformer/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);
+ };
+}
Added: cfe/trunk/lib/Tooling/Transformer/SourceCode.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Transformer/SourceCode.cpp?rev=374271&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Transformer/SourceCode.cpp (added)
+++ cfe/trunk/lib/Tooling/Transformer/SourceCode.cpp Wed Oct 9 19:34:47 2019
@@ -0,0 +1,65 @@
+//===--- 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/Transformer/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;
+}
Added: cfe/trunk/lib/Tooling/Transformer/SourceCodeBuilders.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Transformer/SourceCodeBuilders.cpp?rev=374271&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Transformer/SourceCodeBuilders.cpp (added)
+++ cfe/trunk/lib/Tooling/Transformer/SourceCodeBuilders.cpp Wed Oct 9 19:34:47 2019
@@ -0,0 +1,160 @@
+//===--- 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/Transformer/SourceCodeBuilders.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Tooling/Transformer/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();
+}
Added: cfe/trunk/lib/Tooling/Transformer/Stencil.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Transformer/Stencil.cpp?rev=374271&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Transformer/Stencil.cpp (added)
+++ cfe/trunk/lib/Tooling/Transformer/Stencil.cpp Wed Oct 9 19:34:47 2019
@@ -0,0 +1,369 @@
+//===--- 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/Transformer/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/Transformer/SourceCode.h"
+#include "clang/Tooling/Transformer/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)));
+}
Added: cfe/trunk/lib/Tooling/Transformer/Transformer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Transformer/Transformer.cpp?rev=374271&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Transformer/Transformer.cpp (added)
+++ cfe/trunk/lib/Tooling/Transformer/Transformer.cpp Wed Oct 9 19:34:47 2019
@@ -0,0 +1,235 @@
+//===--- 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/Transformer/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/Transformer/SourceCode.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+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));
+}
Modified: cfe/trunk/unittests/Tooling/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/CMakeLists.txt?rev=374271&r1=374270&r2=374271&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/CMakeLists.txt (original)
+++ cfe/trunk/unittests/Tooling/CMakeLists.txt Wed Oct 9 19:34:47 2019
@@ -75,6 +75,7 @@ clang_target_link_libraries(ToolingTests
clangToolingCore
clangToolingInclusions
clangToolingRefactoring
+ clangTransformer
)
target_link_libraries(ToolingTests
Modified: cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp?rev=374271&r1=374270&r2=374271&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp Wed Oct 9 19:34:47 2019
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/Refactoring/RangeSelector.h"
+#include "clang/Tooling/Transformer/RangeSelector.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Tooling/FixIt.h"
Modified: cfe/trunk/unittests/Tooling/SourceCodeBuildersTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/SourceCodeBuildersTest.cpp?rev=374271&r1=374270&r2=374271&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/SourceCodeBuildersTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/SourceCodeBuildersTest.cpp Wed Oct 9 19:34:47 2019
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/Refactoring/SourceCodeBuilders.h"
+#include "clang/Tooling/Transformer/SourceCodeBuilders.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
Modified: cfe/trunk/unittests/Tooling/SourceCodeTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/SourceCodeTest.cpp?rev=374271&r1=374270&r2=374271&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/SourceCodeTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/SourceCodeTest.cpp Wed Oct 9 19:34:47 2019
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/Refactoring/SourceCode.h"
+#include "clang/Tooling/Transformer/SourceCode.h"
#include "TestVisitor.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/Testing/Support/Annotations.h"
Modified: cfe/trunk/unittests/Tooling/StencilTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/StencilTest.cpp?rev=374271&r1=374270&r2=374271&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/StencilTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/StencilTest.cpp Wed Oct 9 19:34:47 2019
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/Refactoring/Stencil.h"
+#include "clang/Tooling/Transformer/Stencil.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/FixIt.h"
#include "clang/Tooling/Tooling.h"
Modified: cfe/trunk/unittests/Tooling/TransformerTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/TransformerTest.cpp?rev=374271&r1=374270&r2=374271&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/TransformerTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/TransformerTest.cpp Wed Oct 9 19:34:47 2019
@@ -6,9 +6,9 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/Refactoring/Transformer.h"
+#include "clang/Tooling/Transformer/Transformer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Tooling/Refactoring/RangeSelector.h"
+#include "clang/Tooling/Transformer/RangeSelector.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
More information about the cfe-commits
mailing list