diff options
author | Yitzhak Mandelbaum <yitzhakm@google.com> | 2019-10-11 14:43:46 +0000 |
---|---|---|
committer | Yitzhak Mandelbaum <yitzhakm@google.com> | 2019-10-11 14:43:46 +0000 |
commit | e38c36b7b0ab5c84122025032533a6fbbfd69778 (patch) | |
tree | 2d14ffe7a9f75296aa5f41e255ddd56ec0d5be6f /clang/lib/Tooling/Transformer | |
parent | 472c6b0aa02238abeadeeae1c77c4a0587575b0b (diff) | |
download | bcm5719-llvm-e38c36b7b0ab5c84122025032533a6fbbfd69778.tar.gz bcm5719-llvm-e38c36b7b0ab5c84122025032533a6fbbfd69778.zip |
[libTooling] Move `RewriteRule` abstraction into its own header and impl.
Summary: Move the `RewriteRule` class and related declarations into its own set
of files (header, implementation). Only the `Transformer` class is left in the
Transformer-named files. This change clarifies the distinction between the
`RewriteRule` class, which is essential to the Transformer library, and the
`Transformer` class, which is only one possible `RewriteRule` interpreter
(compare to `TransformerClangTidyCheck`, a clang-tidy based interpreter).
Reviewers: gribozavr
Subscribers: jfb, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D68795
llvm-svn: 374558
Diffstat (limited to 'clang/lib/Tooling/Transformer')
-rw-r--r-- | clang/lib/Tooling/Transformer/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/lib/Tooling/Transformer/RewriteRule.cpp | 178 | ||||
-rw-r--r-- | clang/lib/Tooling/Transformer/Transformer.cpp | 170 |
3 files changed, 182 insertions, 167 deletions
diff --git a/clang/lib/Tooling/Transformer/CMakeLists.txt b/clang/lib/Tooling/Transformer/CMakeLists.txt index 2e9ba58862c..68f0cfeee8f 100644 --- a/clang/lib/Tooling/Transformer/CMakeLists.txt +++ b/clang/lib/Tooling/Transformer/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS Support) add_clang_library(clangTransformer RangeSelector.cpp + RewriteRule.cpp SourceCode.cpp SourceCodeBuilders.cpp Stencil.cpp diff --git a/clang/lib/Tooling/Transformer/RewriteRule.cpp b/clang/lib/Tooling/Transformer/RewriteRule.cpp new file mode 100644 index 00000000000..605b5031ca3 --- /dev/null +++ b/clang/lib/Tooling/Transformer/RewriteRule.cpp @@ -0,0 +1,178 @@ +//===--- 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/RewriteRule.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/SourceLocation.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 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; diff --git a/clang/lib/Tooling/Transformer/Transformer.cpp b/clang/lib/Tooling/Transformer/Transformer.cpp index 1aecf6ab8e1..3baa1b9ff27 100644 --- a/clang/lib/Tooling/Transformer/Transformer.cpp +++ b/clang/lib/Tooling/Transformer/Transformer.cpp @@ -7,20 +7,11 @@ //===----------------------------------------------------------------------===// #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/ASTMatchers/ASTMatchersInternal.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> @@ -28,168 +19,13 @@ 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) { +void Transformer::run(const MatchFinder::MatchResult &Result) { if (Result.Context->getDiagnostics().hasErrorOccurred()) return; @@ -202,7 +38,7 @@ void Transformer::run(const MatchResult &Result) { if (Transformations->empty()) { // No rewrite applied (but no error encountered either). - detail::getRuleMatchLoc(Result).print( + tooling::detail::getRuleMatchLoc(Result).print( llvm::errs() << "note: skipping match at loc ", *Result.SourceManager); llvm::errs() << "\n"; return; |