diff options
Diffstat (limited to 'lib/Tooling/Refactoring')
-rw-r--r-- | lib/Tooling/Refactoring/ASTSelectionRequirements.cpp | 2 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Extract/Extract.cpp | 2 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Extract/SourceExtraction.cpp | 8 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Extract/SourceExtraction.h | 51 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/RangeSelector.cpp | 296 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/RefactoringActions.cpp | 4 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Rename/RenamingAction.cpp | 4 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp | 2 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Rename/USRFindingAction.cpp | 6 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/SourceCode.cpp | 31 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Stencil.cpp | 175 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Transformer.cpp | 263 |
12 files changed, 18 insertions, 826 deletions
diff --git a/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp b/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp index 14fc66a979ae5..7dfd3988d9041 100644 --- a/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp +++ b/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp @@ -35,7 +35,7 @@ Expected<CodeRangeASTSelection> CodeRangeASTSelectionRequirement::evaluate( if (!ASTSelection) return ASTSelection.takeError(); std::unique_ptr<SelectedASTNode> StoredSelection = - llvm::make_unique<SelectedASTNode>(std::move(*ASTSelection)); + std::make_unique<SelectedASTNode>(std::move(*ASTSelection)); Optional<CodeRangeASTSelection> CodeRange = CodeRangeASTSelection::create( Context.getSelectionRange(), *StoredSelection); if (!CodeRange) diff --git a/lib/Tooling/Refactoring/Extract/Extract.cpp b/lib/Tooling/Refactoring/Extract/Extract.cpp index f5b94a462103f..402b561090524 100644 --- a/lib/Tooling/Refactoring/Extract/Extract.cpp +++ b/lib/Tooling/Refactoring/Extract/Extract.cpp @@ -13,12 +13,12 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/Refactoring/Extract/Extract.h" -#include "SourceExtraction.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h" namespace clang { namespace tooling { diff --git a/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp b/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp index 533c373e35c45..5d57ecf90a96d 100644 --- a/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp +++ b/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "SourceExtraction.h" +#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" @@ -40,8 +40,12 @@ bool isSemicolonRequiredAfter(const Stmt *S) { return isSemicolonRequiredAfter(CXXFor->getBody()); if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S)) return isSemicolonRequiredAfter(ObjCFor->getBody()); + if(const auto *Switch = dyn_cast<SwitchStmt>(S)) + return isSemicolonRequiredAfter(Switch->getBody()); + if(const auto *Case = dyn_cast<SwitchCase>(S)) + return isSemicolonRequiredAfter(Case->getSubStmt()); switch (S->getStmtClass()) { - case Stmt::SwitchStmtClass: + case Stmt::DeclStmtClass: case Stmt::CXXTryStmtClass: case Stmt::ObjCAtSynchronizedStmtClass: case Stmt::ObjCAutoreleasePoolStmtClass: diff --git a/lib/Tooling/Refactoring/Extract/SourceExtraction.h b/lib/Tooling/Refactoring/Extract/SourceExtraction.h deleted file mode 100644 index 545eb6c1a11c2..0000000000000 --- a/lib/Tooling/Refactoring/Extract/SourceExtraction.h +++ /dev/null @@ -1,51 +0,0 @@ -//===--- 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_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H -#define LLVM_CLANG_LIB_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_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H diff --git a/lib/Tooling/Refactoring/RangeSelector.cpp b/lib/Tooling/Refactoring/RangeSelector.cpp deleted file mode 100644 index 768c02e2277b3..0000000000000 --- a/lib/Tooling/Refactoring/RangeSelector.cpp +++ /dev/null @@ -1,296 +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 { -// 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)); -} - -RangeSelector tooling::expansion(RangeSelector S) { - return [S](const MatchResult &Result) -> Expected<CharSourceRange> { - Expected<CharSourceRange> SRange = S(Result); - if (!SRange) - return SRange.takeError(); - return Result.SourceManager->getExpansionRange(*SRange); - }; -} diff --git a/lib/Tooling/Refactoring/RefactoringActions.cpp b/lib/Tooling/Refactoring/RefactoringActions.cpp index 1a3833243ab4c..7ac723f67c047 100644 --- a/lib/Tooling/Refactoring/RefactoringActions.cpp +++ b/lib/Tooling/Refactoring/RefactoringActions.cpp @@ -98,8 +98,8 @@ public: std::vector<std::unique_ptr<RefactoringAction>> createRefactoringActions() { std::vector<std::unique_ptr<RefactoringAction>> Actions; - Actions.push_back(llvm::make_unique<LocalRename>()); - Actions.push_back(llvm::make_unique<ExtractRefactoring>()); + Actions.push_back(std::make_unique<LocalRename>()); + Actions.push_back(std::make_unique<ExtractRefactoring>()); return Actions; } diff --git a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp index 1649513a077a2..b0634912e3fc3 100644 --- a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp +++ b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp @@ -266,12 +266,12 @@ private: }; std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() { - return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList, + return std::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList, FileToReplaces, PrintLocations); } std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() { - return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces); + return std::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces); } } // end namespace tooling diff --git a/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp b/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp index 8cc1ffaf44820..9e69d37e81add 100644 --- a/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp +++ b/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp @@ -25,7 +25,7 @@ SymbolOccurrence::SymbolOccurrence(const SymbolName &Name, OccurrenceKind Kind, Locations[0], Locations[0].getLocWithOffset(NamePieces[0].size())); return; } - MultipleRanges = llvm::make_unique<SourceRange[]>(Locations.size()); + MultipleRanges = std::make_unique<SourceRange[]>(Locations.size()); RangeOrNumRanges.setBegin( SourceLocation::getFromRawEncoding(Locations.size())); for (const auto &Loc : llvm::enumerate(Locations)) { diff --git a/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp b/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp index 54c6f3e734b15..e26248f50c298 100644 --- a/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp +++ b/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp @@ -102,6 +102,10 @@ public: private: void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) { + if (!RecordDecl->getDefinition()) { + USRSet.insert(getUSRForDecl(RecordDecl)); + return; + } RecordDecl = RecordDecl->getDefinition(); if (const auto *ClassTemplateSpecDecl = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl)) @@ -264,7 +268,7 @@ private: }; std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() { - return llvm::make_unique<NamedDeclFindingConsumer>( + return std::make_unique<NamedDeclFindingConsumer>( SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force, ErrorOccurred); } diff --git a/lib/Tooling/Refactoring/SourceCode.cpp b/lib/Tooling/Refactoring/SourceCode.cpp deleted file mode 100644 index 3a97e178bbd4b..0000000000000 --- a/lib/Tooling/Refactoring/SourceCode.cpp +++ /dev/null @@ -1,31 +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()); -} diff --git a/lib/Tooling/Refactoring/Stencil.cpp b/lib/Tooling/Refactoring/Stencil.cpp deleted file mode 100644 index 09eca21c3cef1..0000000000000 --- a/lib/Tooling/Refactoring/Stencil.cpp +++ /dev/null @@ -1,175 +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 "llvm/Support/Errc.h" -#include <atomic> -#include <memory> -#include <string> - -using namespace clang; -using namespace tooling; - -using ast_matchers::MatchFinder; -using llvm::Error; - -// 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<ast_type_traits::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 DebugPrintNodeOpData { - explicit DebugPrintNodeOpData(std::string S) : Id(std::move(S)) {} - std::string Id; -}; - -// The fragment of code corresponding to the selected range. -struct SelectorOpData { - explicit SelectorOpData(RangeSelector S) : Selector(std::move(S)) {} - RangeSelector Selector; -}; -} // namespace - -bool isEqualData(const RawTextData &A, const RawTextData &B) { - return A.Text == B.Text; -} - -bool isEqualData(const DebugPrintNodeOpData &A, const DebugPrintNodeOpData &B) { - return A.Id == B.Id; -} - -// Equality is not (yet) defined for \c RangeSelector. -bool isEqualData(const SelectorOpData &, const SelectorOpData &) { return false; } - -// 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 DebugPrintNodeOpData &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 SelectorOpData &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(); -} - -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; - } -}; - -namespace { -using RawText = StencilPartImpl<RawTextData>; -using DebugPrintNodeOp = StencilPartImpl<DebugPrintNodeOpData>; -using SelectorOp = StencilPartImpl<SelectorOpData>; -} // 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<RawText>(Text)); -} - -StencilPart stencil::selection(RangeSelector Selector) { - return StencilPart(std::make_shared<SelectorOp>(std::move(Selector))); -} - -StencilPart stencil::dPrint(StringRef Id) { - return StencilPart(std::make_shared<DebugPrintNodeOp>(Id)); -} diff --git a/lib/Tooling/Refactoring/Transformer.cpp b/lib/Tooling/Refactoring/Transformer.cpp deleted file mode 100644 index 8e6fe6c7a940d..0000000000000 --- a/lib/Tooling/Refactoring/Transformer.cpp +++ /dev/null @@ -1,263 +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 <deque> -#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; - -// Did the text at this location originate in a macro definition (aka. body)? -// For example, -// -// #define NESTED(x) x -// #define MACRO(y) { int y = NESTED(3); } -// if (true) MACRO(foo) -// -// The if statement expands to -// -// if (true) { int foo = 3; } -// ^ ^ -// Loc1 Loc2 -// -// For SourceManager SM, SM.isMacroArgExpansion(Loc1) and -// SM.isMacroArgExpansion(Loc2) are both true, but isOriginMacroBody(sm, Loc1) -// is false, because "foo" originated in the source file (as an argument to a -// macro), whereas isOriginMacroBody(SM, Loc2) is true, because "3" originated -// in the definition of MACRO. -static bool isOriginMacroBody(const clang::SourceManager &SM, - clang::SourceLocation Loc) { - while (Loc.isMacroID()) { - if (SM.isMacroBodyExpansion(Loc)) - return true; - // Otherwise, it must be in an argument, so we continue searching up the - // invocation stack. getImmediateMacroCallerLoc() gives the location of the - // argument text, inside the call text. - Loc = SM.getImmediateMacroCallerLoc(Loc); - } - return false; -} - -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(); - if (Range->isInvalid() || - isOriginMacroBody(*Result.SourceManager, Range->getBegin())) - return SmallVector<Transformation, 0>(); - auto Replacement = Edit.Replacement(Result); - if (!Replacement) - return Replacement.takeError(); - tooling::detail::Transformation T; - T.Range = *Range; - 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); -} - -// Determines whether A is a base type of B in the class hierarchy, including -// the implicit relationship of Type and QualType. -static bool isBaseOf(ASTNodeKind A, ASTNodeKind B) { - static auto TypeKind = ASTNodeKind::getFromNodeKind<Type>(); - static auto QualKind = ASTNodeKind::getFromNodeKind<QualType>(); - /// Mimic the implicit conversions of Matcher<>. - /// - From Matcher<Type> to Matcher<QualType> - /// - From Matcher<Base> to Matcher<Derived> - return (A.isSame(TypeKind) && B.isSame(QualKind)) || A.isBaseOf(B); -} - -// Try to find a common kind to which all of the rule's matchers can be -// converted. -static ASTNodeKind -findCommonKind(const SmallVectorImpl<RewriteRule::Case> &Cases) { - assert(!Cases.empty() && "Rule must have at least one case."); - ASTNodeKind JoinKind = Cases[0].Matcher.getSupportedKind(); - // Find a (least) Kind K, for which M.canConvertTo(K) holds, for all matchers - // M in Rules. - for (const auto &Case : Cases) { - auto K = Case.Matcher.getSupportedKind(); - if (isBaseOf(JoinKind, K)) { - JoinKind = K; - continue; - } - if (K.isSame(JoinKind) || isBaseOf(K, JoinKind)) - // JoinKind is already the lowest. - continue; - // K and JoinKind are unrelated -- there is no least common kind. - return ASTNodeKind(); - } - return JoinKind; -} - -// Binds each rule's matcher to a unique (and deterministic) tag based on -// `TagBase`. -static std::vector<DynTypedMatcher> -taggedMatchers(StringRef TagBase, - const SmallVectorImpl<RewriteRule::Case> &Cases) { - std::vector<DynTypedMatcher> Matchers; - Matchers.reserve(Cases.size()); - size_t count = 0; - for (const auto &Case : Cases) { - std::string Tag = (TagBase + Twine(count)).str(); - ++count; - auto M = Case.Matcher.tryBind(Tag); - assert(M && "RewriteRule matchers should be bindable."); - 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; -} - -static DynTypedMatcher joinCaseMatchers(const RewriteRule &Rule) { - assert(!Rule.Cases.empty() && "Rule must have at least one case."); - if (Rule.Cases.size() == 1) - return Rule.Cases[0].Matcher; - - auto CommonKind = findCommonKind(Rule.Cases); - assert(!CommonKind.isNone() && "Cases must have compatible matchers."); - return DynTypedMatcher::constructVariadic( - DynTypedMatcher::VO_AnyOf, CommonKind, taggedMatchers("Tag", Rule.Cases)); -} - -DynTypedMatcher tooling::detail::buildMatcher(const RewriteRule &Rule) { - DynTypedMatcher M = joinCaseMatchers(Rule); - M.setAllowBind(true); - // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true. - return *M.tryBind(RewriteRule::RootID); -} - -// 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) { - MatchFinder->addDynamicMatcher(tooling::detail::buildMatcher(Rule), this); -} - -void Transformer::run(const MatchResult &Result) { - if (Result.Context->getDiagnostics().hasErrorOccurred()) - return; - - // Verify the existence and validity of the AST node that roots this rule. - auto &NodesMap = Result.Nodes.getMap(); - auto Root = NodesMap.find(RewriteRule::RootID); - assert(Root != NodesMap.end() && "Transformation failed: missing root node."); - SourceLocation RootLoc = Result.SourceManager->getExpansionLoc( - Root->second.getSourceRange().getBegin()); - assert(RootLoc.isValid() && "Invalid location for Root node of match."); - - RewriteRule::Case Case = tooling::detail::findSelectedCase(Result, Rule); - 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). - RootLoc.print(llvm::errs() << "note: skipping match at loc ", - *Result.SourceManager); - llvm::errs() << "\n"; - return; - } - - // Record the results in the AtomicChange. - AtomicChange AC(*Result.SourceManager, RootLoc); - 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)); -} |