diff options
Diffstat (limited to 'include/clang/Tooling/Refactoring')
7 files changed, 68 insertions, 655 deletions
diff --git a/include/clang/Tooling/Refactoring/Extract/SourceExtraction.h b/include/clang/Tooling/Refactoring/Extract/SourceExtraction.h new file mode 100755 index 000000000000..034a0aaaf6db --- /dev/null +++ b/include/clang/Tooling/Refactoring/Extract/SourceExtraction.h @@ -0,0 +1,51 @@ +//===--- SourceExtraction.cpp - Clang refactoring library -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H +#define LLVM_CLANG_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H + +#include "clang/Basic/LLVM.h" + +namespace clang { + +class LangOptions; +class SourceManager; +class SourceRange; +class Stmt; + +namespace tooling { + +/// Determines which semicolons should be inserted during extraction. +class ExtractionSemicolonPolicy { +public: + bool isNeededInExtractedFunction() const { + return IsNeededInExtractedFunction; + } + + bool isNeededInOriginalFunction() const { return IsNeededInOriginalFunction; } + + /// Returns the semicolon insertion policy that is needed for extraction of + /// the given statement from the given source range. + static ExtractionSemicolonPolicy compute(const Stmt *S, + SourceRange &ExtractedRange, + const SourceManager &SM, + const LangOptions &LangOpts); + +private: + ExtractionSemicolonPolicy(bool IsNeededInExtractedFunction, + bool IsNeededInOriginalFunction) + : IsNeededInExtractedFunction(IsNeededInExtractedFunction), + IsNeededInOriginalFunction(IsNeededInOriginalFunction) {} + bool IsNeededInExtractedFunction; + bool IsNeededInOriginalFunction; +}; + +} // end namespace tooling +} // end namespace clang + +#endif //LLVM_CLANG_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H diff --git a/include/clang/Tooling/Refactoring/RangeSelector.h b/include/clang/Tooling/Refactoring/RangeSelector.h deleted file mode 100644 index b117e4d82ad4..000000000000 --- a/include/clang/Tooling/Refactoring/RangeSelector.h +++ /dev/null @@ -1,89 +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 "llvm/Support/Error.h" -#include <functional> -#include <string> - -namespace clang { -namespace tooling { -using RangeSelector = std::function<Expected<CharSourceRange>( - const ast_matchers::MatchFinder::MatchResult &)>; - -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); - -/// 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_ diff --git a/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h b/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h index 41a448f035a4..c0f995d85c14 100644 --- a/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h +++ b/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h @@ -98,7 +98,17 @@ public: TypeBeginLoc, TypeEndLoc)) return false; } - return visit(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc, TypeEndLoc); + if (const Type *TP = Loc.getTypePtr()) { + if (TP->getTypeClass() == clang::Type::Record) + return visit(TP->getAsCXXRecordDecl(), TypeBeginLoc, TypeEndLoc); + } + return true; + } + + bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { + const SourceLocation TypeEndLoc = + Lexer::getLocForEndOfToken(TL.getBeginLoc(), 0, SM, LangOpts); + return visit(TL.getTypedefNameDecl(), TL.getBeginLoc(), TypeEndLoc); } bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { @@ -122,8 +132,7 @@ private: ND, SourceRange(BeginLoc, EndLoc)); } bool visit(const NamedDecl *ND, SourceLocation Loc) { - return visit(ND, Loc, - Loc.getLocWithOffset(ND->getNameAsString().length() - 1)); + return visit(ND, Loc, Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts)); } }; diff --git a/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h b/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h index cc6ae83202f1..fb373fcf5029 100644 --- a/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h +++ b/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h @@ -47,7 +47,7 @@ template <typename RuleType, typename... RequirementTypes, size_t... Is> void invokeRuleAfterValidatingRequirements( RefactoringResultConsumer &Consumer, RefactoringRuleContext &Context, const std::tuple<RequirementTypes...> &Requirements, - llvm::index_sequence<Is...>) { + std::index_sequence<Is...>) { // Check if the requirements we're interested in can be evaluated. auto Values = std::make_tuple(std::get<Is>(Requirements).evaluate(Context)...); @@ -87,7 +87,7 @@ template <typename... RequirementTypes, size_t... Is> void visitRefactoringOptions( RefactoringOptionVisitor &Visitor, const std::tuple<RequirementTypes...> &Requirements, - llvm::index_sequence<Is...>) { + std::index_sequence<Is...>) { visitRefactoringOptionsImpl(Visitor, std::get<Is>(Requirements)...); } @@ -131,7 +131,7 @@ createRefactoringActionRule(const RequirementTypes &... Requirements) { RefactoringRuleContext &Context) override { internal::invokeRuleAfterValidatingRequirements<RuleType>( Consumer, Context, Requirements, - llvm::index_sequence_for<RequirementTypes...>()); + std::index_sequence_for<RequirementTypes...>()); } bool hasSelectionRequirement() override { @@ -142,13 +142,13 @@ createRefactoringActionRule(const RequirementTypes &... Requirements) { void visitRefactoringOptions(RefactoringOptionVisitor &Visitor) override { internal::visitRefactoringOptions( Visitor, Requirements, - llvm::index_sequence_for<RequirementTypes...>()); + std::index_sequence_for<RequirementTypes...>()); } private: std::tuple<RequirementTypes...> Requirements; }; - return llvm::make_unique<Rule>(std::make_tuple(Requirements...)); + return std::make_unique<Rule>(std::make_tuple(Requirements...)); } } // end namespace tooling diff --git a/include/clang/Tooling/Refactoring/SourceCode.h b/include/clang/Tooling/Refactoring/SourceCode.h deleted file mode 100644 index 498dbea96c70..000000000000 --- a/include/clang/Tooling/Refactoring/SourceCode.h +++ /dev/null @@ -1,77 +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); -} -} // namespace tooling -} // namespace clang -#endif // LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H diff --git a/include/clang/Tooling/Refactoring/Stencil.h b/include/clang/Tooling/Refactoring/Stencil.h deleted file mode 100644 index e57a576e5575..000000000000 --- a/include/clang/Tooling/Refactoring/Stencil.h +++ /dev/null @@ -1,173 +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/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; - - 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); - } - -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); - } - -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)); -} - -/// Variant of \c node() that identifies the node as a statement, for purposes -/// of deciding whether to include any trailing semicolon. Only relevant for -/// Expr nodes, which, by default, are *not* considered as statements. -/// \returns the source corresponding to the identified node, considered as a -/// statement. -/// FIXME: Deprecated. Write `selection(statement(Id))` instead. -inline StencilPart sNode(llvm::StringRef Id) { - return selection(tooling::statement(Id)); -} - -/// 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_ diff --git a/include/clang/Tooling/Refactoring/Transformer.h b/include/clang/Tooling/Refactoring/Transformer.h deleted file mode 100644 index 6d9c5a37cc18..000000000000 --- a/include/clang/Tooling/Refactoring/Transformer.h +++ /dev/null @@ -1,308 +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/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 { - -// Note that \p TextGenerator is allowed to fail, e.g. when trying to access a -// matched node that was not bound. Allowing this to fail simplifies error -// handling for interactive tools like clang-query. -using TextGenerator = std::function<Expected<std::string>( - const ast_matchers::MatchFinder::MatchResult &)>; - -/// 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. -/// -/// N.B. All of the rules must use the same kind of matcher (that is, share a -/// base class in the AST hierarchy). However, this constraint is caused by an -/// implementation detail and should be lifted in the future. -// -// `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. -ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule); - -/// 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_ |