brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.4 KiB · 039d1ed Raw
499 lines · cpp
1//===--- Stencil.cpp - Stencil implementation -------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Tooling/Transformer/Stencil.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/ASTTypeTraits.h"12#include "clang/AST/Expr.h"13#include "clang/ASTMatchers/ASTMatchFinder.h"14#include "clang/Basic/SourceLocation.h"15#include "clang/Lex/Lexer.h"16#include "clang/Tooling/Transformer/SourceCode.h"17#include "clang/Tooling/Transformer/SourceCodeBuilders.h"18#include "llvm/ADT/SmallVector.h"19#include "llvm/ADT/Twine.h"20#include "llvm/Support/Errc.h"21#include "llvm/Support/Error.h"22#include <memory>23#include <string>24 25using namespace clang;26using namespace transformer;27 28using ast_matchers::BoundNodes;29using ast_matchers::MatchFinder;30using llvm::errc;31using llvm::Error;32using llvm::Expected;33using llvm::StringError;34 35static llvm::Expected<DynTypedNode> getNode(const BoundNodes &Nodes,36                                            StringRef Id) {37  auto &NodesMap = Nodes.getMap();38  auto It = NodesMap.find(Id);39  if (It == NodesMap.end())40    return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,41                                               "Id not bound: " + Id);42  return It->second;43}44 45static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match,46                       std::string *Result) {47  std::string Output;48  llvm::raw_string_ostream Os(Output);49  auto NodeOrErr = getNode(Match.Nodes, Id);50  if (auto Err = NodeOrErr.takeError())51    return Err;52  const PrintingPolicy PP(Match.Context->getLangOpts());53  if (const auto *ND = NodeOrErr->get<NamedDecl>()) {54    // For NamedDecls, we can do a better job than printing the whole thing.55    ND->getNameForDiagnostic(Os, PP, false);56  } else {57    NodeOrErr->print(Os, PP);58  }59  *Result += Output;60  return Error::success();61}62 63namespace {64// An arbitrary fragment of code within a stencil.65class RawTextStencil : public StencilInterface {66  std::string Text;67 68public:69  explicit RawTextStencil(std::string T) : Text(std::move(T)) {}70 71  std::string toString() const override {72    std::string Result;73    llvm::raw_string_ostream OS(Result);74    OS << "\"";75    OS.write_escaped(Text);76    OS << "\"";77    return Result;78  }79 80  Error eval(const MatchFinder::MatchResult &Match,81             std::string *Result) const override {82    Result->append(Text);83    return Error::success();84  }85};86 87// A debugging operation to dump the AST for a particular (bound) AST node.88class DebugPrintNodeStencil : public StencilInterface {89  std::string Id;90 91public:92  explicit DebugPrintNodeStencil(std::string S) : Id(std::move(S)) {}93 94  std::string toString() const override {95    return (llvm::Twine("dPrint(\"") + Id + "\")").str();96  }97 98  Error eval(const MatchFinder::MatchResult &Match,99             std::string *Result) const override {100    return printNode(Id, Match, Result);101  }102};103 104// Operators that take a single node Id as an argument.105enum class UnaryNodeOperator {106  Parens,107  Deref,108  MaybeDeref,109  AddressOf,110  MaybeAddressOf,111  Describe,112};113 114// Generic container for stencil operations with a (single) node-id argument.115class UnaryOperationStencil : public StencilInterface {116  UnaryNodeOperator Op;117  std::string Id;118 119public:120  UnaryOperationStencil(UnaryNodeOperator Op, std::string Id)121      : Op(Op), Id(std::move(Id)) {}122 123  std::string toString() const override {124    StringRef OpName;125    switch (Op) {126    case UnaryNodeOperator::Parens:127      OpName = "expression";128      break;129    case UnaryNodeOperator::Deref:130      OpName = "deref";131      break;132    case UnaryNodeOperator::MaybeDeref:133      OpName = "maybeDeref";134      break;135    case UnaryNodeOperator::AddressOf:136      OpName = "addressOf";137      break;138    case UnaryNodeOperator::MaybeAddressOf:139      OpName = "maybeAddressOf";140      break;141    case UnaryNodeOperator::Describe:142      OpName = "describe";143      break;144    }145    return (OpName + "(\"" + Id + "\")").str();146  }147 148  Error eval(const MatchFinder::MatchResult &Match,149             std::string *Result) const override {150    // The `Describe` operation can be applied to any node, not just151    // expressions, so it is handled here, separately.152    if (Op == UnaryNodeOperator::Describe)153      return printNode(Id, Match, Result);154 155    const auto *E = Match.Nodes.getNodeAs<Expr>(Id);156    if (E == nullptr)157      return llvm::make_error<StringError>(errc::invalid_argument,158                                           "Id not bound or not Expr: " + Id);159    std::optional<std::string> Source;160    switch (Op) {161    case UnaryNodeOperator::Parens:162      Source = tooling::buildParens(*E, *Match.Context);163      break;164    case UnaryNodeOperator::Deref:165      Source = tooling::buildDereference(*E, *Match.Context);166      break;167    case UnaryNodeOperator::MaybeDeref:168      if (E->getType()->isAnyPointerType() ||169          tooling::isKnownPointerLikeType(E->getType(), *Match.Context)) {170        // Strip off any operator->. This can only occur inside an actual arrow171        // member access, so we treat it as equivalent to an actual object172        // expression.173        if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {174          if (OpCall->getOperator() == clang::OO_Arrow &&175              OpCall->getNumArgs() == 1) {176            E = OpCall->getArg(0);177          }178        }179        Source = tooling::buildDereference(*E, *Match.Context);180        break;181      }182      *Result += tooling::getText(*E, *Match.Context);183      return Error::success();184    case UnaryNodeOperator::AddressOf:185      Source = tooling::buildAddressOf(*E, *Match.Context);186      break;187    case UnaryNodeOperator::MaybeAddressOf:188      if (E->getType()->isAnyPointerType() ||189          tooling::isKnownPointerLikeType(E->getType(), *Match.Context)) {190        // Strip off any operator->. This can only occur inside an actual arrow191        // member access, so we treat it as equivalent to an actual object192        // expression.193        if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {194          if (OpCall->getOperator() == clang::OO_Arrow &&195              OpCall->getNumArgs() == 1) {196            E = OpCall->getArg(0);197          }198        }199        *Result += tooling::getText(*E, *Match.Context);200        return Error::success();201      }202      Source = tooling::buildAddressOf(*E, *Match.Context);203      break;204    case UnaryNodeOperator::Describe:205      llvm_unreachable("This case is handled at the start of the function");206    }207    if (!Source)208      return llvm::make_error<StringError>(209          errc::invalid_argument,210          "Could not construct expression source from ID: " + Id);211    *Result += *Source;212    return Error::success();213  }214};215 216// The fragment of code corresponding to the selected range.217class SelectorStencil : public StencilInterface {218  RangeSelector Selector;219 220public:221  explicit SelectorStencil(RangeSelector S) : Selector(std::move(S)) {}222 223  std::string toString() const override { return "selection(...)"; }224 225  Error eval(const MatchFinder::MatchResult &Match,226             std::string *Result) const override {227    auto RawRange = Selector(Match);228    if (!RawRange)229      return RawRange.takeError();230    CharSourceRange Range = Lexer::makeFileCharRange(231        *RawRange, *Match.SourceManager, Match.Context->getLangOpts());232    if (Range.isInvalid()) {233      // Validate the original range to attempt to get a meaningful error234      // message. If it's valid, then something else is the cause and we just235      // return the generic failure message.236      if (auto Err = tooling::validateRange(*RawRange, *Match.SourceManager,237                                            /*AllowSystemHeaders=*/true))238        return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) {239          assert(E->convertToErrorCode() ==240                     llvm::make_error_code(errc::invalid_argument) &&241                 "Validation errors must carry the invalid_argument code");242          return llvm::createStringError(243              errc::invalid_argument,244              "selected range could not be resolved to a valid source range; " +245                  E->getMessage());246        });247      return llvm::createStringError(248          errc::invalid_argument,249          "selected range could not be resolved to a valid source range");250    }251    // Validate `Range`, because `makeFileCharRange` accepts some ranges that252    // `validateRange` rejects.253    if (auto Err = tooling::validateRange(Range, *Match.SourceManager,254                                          /*AllowSystemHeaders=*/true))255      return joinErrors(256          llvm::createStringError(errc::invalid_argument,257                                  "selected range is not valid for editing"),258          std::move(Err));259    *Result += tooling::getText(Range, *Match.Context);260    return Error::success();261  }262};263 264// A stencil operation to build a member access `e.m` or `e->m`, as appropriate.265class AccessStencil : public StencilInterface {266  std::string BaseId;267  Stencil Member;268 269public:270  AccessStencil(StringRef BaseId, Stencil Member)271      : BaseId(std::string(BaseId)), Member(std::move(Member)) {}272 273  std::string toString() const override {274    return (llvm::Twine("access(\"") + BaseId + "\", " + Member->toString() +275            ")")276        .str();277  }278 279  Error eval(const MatchFinder::MatchResult &Match,280             std::string *Result) const override {281    const auto *E = Match.Nodes.getNodeAs<Expr>(BaseId);282    if (E == nullptr)283      return llvm::make_error<StringError>(errc::invalid_argument,284                                           "Id not bound: " + BaseId);285    std::optional<std::string> S = tooling::buildAccess(*E, *Match.Context);286    if (!S)287      return llvm::make_error<StringError>(288          errc::invalid_argument,289          "Could not construct object text from ID: " + BaseId);290    *Result += *S;291    return Member->eval(Match, Result);292  }293};294 295class IfBoundStencil : public StencilInterface {296  std::string Id;297  Stencil TrueStencil;298  Stencil FalseStencil;299 300public:301  IfBoundStencil(StringRef Id, Stencil TrueStencil, Stencil FalseStencil)302      : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)),303        FalseStencil(std::move(FalseStencil)) {}304 305  std::string toString() const override {306    return (llvm::Twine("ifBound(\"") + Id + "\", " + TrueStencil->toString() +307            ", " + FalseStencil->toString() + ")")308        .str();309  }310 311  Error eval(const MatchFinder::MatchResult &Match,312             std::string *Result) const override {313    auto &M = Match.Nodes.getMap();314    return (M.find(Id) != M.end() ? TrueStencil : FalseStencil)315        ->eval(Match, Result);316  }317};318 319class SelectBoundStencil : public clang::transformer::StencilInterface {320  static bool containsNoNullStencils(321      const std::vector<std::pair<std::string, Stencil>> &Cases) {322    for (const auto &S : Cases)323      if (S.second == nullptr)324        return false;325    return true;326  }327 328public:329  SelectBoundStencil(std::vector<std::pair<std::string, Stencil>> Cases,330                     Stencil Default)331      : CaseStencils(std::move(Cases)), DefaultStencil(std::move(Default)) {332    assert(containsNoNullStencils(CaseStencils) &&333           "cases of selectBound may not be null");334  }335  ~SelectBoundStencil() override {}336 337  llvm::Error eval(const MatchFinder::MatchResult &match,338                   std::string *result) const override {339    const BoundNodes::IDToNodeMap &NodeMap = match.Nodes.getMap();340    for (const auto &S : CaseStencils) {341      if (NodeMap.count(S.first) > 0) {342        return S.second->eval(match, result);343      }344    }345 346    if (DefaultStencil != nullptr) {347      return DefaultStencil->eval(match, result);348    }349 350    llvm::SmallVector<llvm::StringRef, 2> CaseIDs;351    CaseIDs.reserve(CaseStencils.size());352    for (const auto &S : CaseStencils)353      CaseIDs.emplace_back(S.first);354 355    return llvm::createStringError(356        errc::result_out_of_range,357        llvm::Twine("selectBound failed: no cases bound and no default: {") +358            llvm::join(CaseIDs, ", ") + "}");359  }360 361  std::string toString() const override {362    std::string Buffer;363    llvm::raw_string_ostream Stream(Buffer);364    Stream << "selectBound({";365    bool First = true;366    for (const auto &S : CaseStencils) {367      if (First)368        First = false;369      else370        Stream << "}, ";371      Stream << "{\"" << S.first << "\", " << S.second->toString();372    }373    Stream << "}}";374    if (DefaultStencil != nullptr) {375      Stream << ", " << DefaultStencil->toString();376    }377    Stream << ")";378    return Buffer;379  }380 381private:382  std::vector<std::pair<std::string, Stencil>> CaseStencils;383  Stencil DefaultStencil;384};385 386class SequenceStencil : public StencilInterface {387  std::vector<Stencil> Stencils;388 389public:390  SequenceStencil(std::vector<Stencil> Stencils)391      : Stencils(std::move(Stencils)) {}392 393  std::string toString() const override {394    llvm::SmallVector<std::string, 2> Parts;395    Parts.reserve(Stencils.size());396    for (const auto &S : Stencils)397      Parts.push_back(S->toString());398    return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str();399  }400 401  Error eval(const MatchFinder::MatchResult &Match,402             std::string *Result) const override {403    for (const auto &S : Stencils)404      if (auto Err = S->eval(Match, Result))405        return Err;406    return Error::success();407  }408};409 410class RunStencil : public StencilInterface {411  MatchConsumer<std::string> Consumer;412 413public:414  explicit RunStencil(MatchConsumer<std::string> C) : Consumer(std::move(C)) {}415 416  std::string toString() const override { return "run(...)"; }417 418  Error eval(const MatchFinder::MatchResult &Match,419             std::string *Result) const override {420 421    Expected<std::string> Value = Consumer(Match);422    if (!Value)423      return Value.takeError();424    *Result += *Value;425    return Error::success();426  }427};428} // namespace429 430Stencil transformer::detail::makeStencil(StringRef Text) {431  return std::make_shared<RawTextStencil>(std::string(Text));432}433 434Stencil transformer::detail::makeStencil(RangeSelector Selector) {435  return std::make_shared<SelectorStencil>(std::move(Selector));436}437 438Stencil transformer::dPrint(StringRef Id) {439  return std::make_shared<DebugPrintNodeStencil>(std::string(Id));440}441 442Stencil transformer::expression(llvm::StringRef Id) {443  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Parens,444                                                 std::string(Id));445}446 447Stencil transformer::deref(llvm::StringRef ExprId) {448  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Deref,449                                                 std::string(ExprId));450}451 452Stencil transformer::maybeDeref(llvm::StringRef ExprId) {453  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::MaybeDeref,454                                                 std::string(ExprId));455}456 457Stencil transformer::addressOf(llvm::StringRef ExprId) {458  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::AddressOf,459                                                 std::string(ExprId));460}461 462Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) {463  return std::make_shared<UnaryOperationStencil>(464      UnaryNodeOperator::MaybeAddressOf, std::string(ExprId));465}466 467Stencil transformer::describe(StringRef Id) {468  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Describe,469                                                 std::string(Id));470}471 472Stencil transformer::access(StringRef BaseId, Stencil Member) {473  return std::make_shared<AccessStencil>(BaseId, std::move(Member));474}475 476Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil,477                             Stencil FalseStencil) {478  return std::make_shared<IfBoundStencil>(Id, std::move(TrueStencil),479                                          std::move(FalseStencil));480}481 482Stencil transformer::selectBound(483    std::vector<std::pair<std::string, Stencil>> CaseStencils,484    Stencil DefaultStencil) {485  return std::make_shared<SelectBoundStencil>(std::move(CaseStencils),486                                              std::move(DefaultStencil));487}488 489Stencil transformer::run(MatchConsumer<std::string> Fn) {490  return std::make_shared<RunStencil>(std::move(Fn));491}492 493Stencil transformer::catVector(std::vector<Stencil> Parts) {494  // Only one argument, so don't wrap in sequence.495  if (Parts.size() == 1)496    return std::move(Parts[0]);497  return std::make_shared<SequenceStencil>(std::move(Parts));498}499