From 519fc96c475680de2cc49e7811dbbfadb912cbcc Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Wed, 23 Oct 2019 17:52:09 +0000 Subject: Vendor import of stripped clang trunk r375505, the last commit before the upstream Subversion repository was made read-only, and the LLVM project migrated to GitHub: https://llvm.org/svn/llvm-project/cfe/trunk@375505 --- lib/Tooling/Transformer/RewriteRule.cpp | 178 ++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 lib/Tooling/Transformer/RewriteRule.cpp (limited to 'lib/Tooling/Transformer/RewriteRule.cpp') diff --git a/lib/Tooling/Transformer/RewriteRule.cpp b/lib/Tooling/Transformer/RewriteRule.cpp new file mode 100644 index 0000000000000..6fa558f7b2ee7 --- /dev/null +++ b/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 +#include +#include +#include + +using namespace clang; +using namespace transformer; + +using ast_matchers::MatchFinder; +using ast_matchers::internal::DynTypedMatcher; +using ast_type_traits::ASTNodeKind; + +using MatchResult = MatchFinder::MatchResult; + +Expected> +transformer::detail::translateEdits(const MatchResult &Result, + llvm::ArrayRef Edits) { + SmallVector Transformations; + for (const auto &Edit : Edits) { + Expected Range = Edit.TargetRange(Result); + if (!Range) + return Range.takeError(); + llvm::Optional EditRange = + tooling::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(); + auto Replacement = Edit.Replacement(Result); + if (!Replacement) + return Replacement.takeError(); + transformer::detail::Transformation T; + T.Range = *EditRange; + T.Replacement = std::move(*Replacement); + Transformations.push_back(std::move(T)); + } + return Transformations; +} + +ASTEdit transformer::change(RangeSelector S, TextGenerator Replacement) { + ASTEdit E; + E.TargetRange = std::move(S); + E.Replacement = std::move(Replacement); + return E; +} + +RewriteRule transformer::makeRule(DynTypedMatcher M, SmallVector Edits, + TextGenerator Explanation) { + return RewriteRule{{RewriteRule::Case{ + std::move(M), std::move(Edits), std::move(Explanation), {}}}}; +} + +void transformer::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(); +} +#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 taggedMatchers( + StringRef TagBase, + const SmallVectorImpl> &Cases) { + std::vector 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 transformer::applyFirst(ArrayRef Rules) { + RewriteRule R; + for (auto &Rule : Rules) + R.Cases.append(Rule.Cases.begin(), Rule.Cases.end()); + return R; +} + +std::vector +transformer::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, 1>> + Buckets; + const SmallVectorImpl &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 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 transformer::detail::buildMatcher(const RewriteRule &Rule) { + std::vector Ms = buildMatchers(Rule); + assert(Ms.size() == 1 && "Cases must have compatible matchers."); + return Ms[0]; +} + +SourceLocation transformer::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 RootRange = tooling::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 & +transformer::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; -- cgit v1.2.3