1353942Sdim//===--- Stencil.cpp - Stencil implementation -------------------*- C++ -*-===// 2353942Sdim// 3353942Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353942Sdim// See https://llvm.org/LICENSE.txt for license information. 5353942Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6353942Sdim// 7353942Sdim//===----------------------------------------------------------------------===// 8353942Sdim 9353942Sdim#include "clang/Tooling/Transformer/Stencil.h" 10353942Sdim#include "clang/AST/ASTContext.h" 11353942Sdim#include "clang/AST/ASTTypeTraits.h" 12353942Sdim#include "clang/AST/Expr.h" 13353942Sdim#include "clang/ASTMatchers/ASTMatchFinder.h" 14353942Sdim#include "clang/ASTMatchers/ASTMatchers.h" 15353942Sdim#include "clang/Lex/Lexer.h" 16353942Sdim#include "clang/Tooling/Transformer/SourceCode.h" 17353942Sdim#include "clang/Tooling/Transformer/SourceCodeBuilders.h" 18357095Sdim#include "llvm/ADT/SmallVector.h" 19353942Sdim#include "llvm/ADT/Twine.h" 20353942Sdim#include "llvm/Support/Errc.h" 21353942Sdim#include <atomic> 22353942Sdim#include <memory> 23353942Sdim#include <string> 24353942Sdim 25353942Sdimusing namespace clang; 26353942Sdimusing namespace transformer; 27353942Sdim 28353942Sdimusing ast_matchers::MatchFinder; 29353942Sdimusing ast_type_traits::DynTypedNode; 30353942Sdimusing llvm::errc; 31353942Sdimusing llvm::Error; 32353942Sdimusing llvm::Expected; 33353942Sdimusing llvm::StringError; 34353942Sdim 35353942Sdimstatic llvm::Expected<DynTypedNode> 36353942SdimgetNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) { 37353942Sdim auto &NodesMap = Nodes.getMap(); 38353942Sdim auto It = NodesMap.find(Id); 39353942Sdim if (It == NodesMap.end()) 40353942Sdim return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument, 41353942Sdim "Id not bound: " + Id); 42353942Sdim return It->second; 43353942Sdim} 44353942Sdim 45353942Sdimnamespace { 46353942Sdim// An arbitrary fragment of code within a stencil. 47353942Sdimstruct RawTextData { 48353942Sdim explicit RawTextData(std::string T) : Text(std::move(T)) {} 49353942Sdim std::string Text; 50353942Sdim}; 51353942Sdim 52353942Sdim// A debugging operation to dump the AST for a particular (bound) AST node. 53353942Sdimstruct DebugPrintNodeData { 54353942Sdim explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {} 55353942Sdim std::string Id; 56353942Sdim}; 57353942Sdim 58353942Sdim// Operators that take a single node Id as an argument. 59353942Sdimenum class UnaryNodeOperator { 60353942Sdim Parens, 61353942Sdim Deref, 62357095Sdim MaybeDeref, 63357095Sdim AddressOf, 64357095Sdim MaybeAddressOf, 65353942Sdim}; 66353942Sdim 67353942Sdim// Generic container for stencil operations with a (single) node-id argument. 68353942Sdimstruct UnaryOperationData { 69353942Sdim UnaryOperationData(UnaryNodeOperator Op, std::string Id) 70353942Sdim : Op(Op), Id(std::move(Id)) {} 71353942Sdim UnaryNodeOperator Op; 72353942Sdim std::string Id; 73353942Sdim}; 74353942Sdim 75353942Sdim// The fragment of code corresponding to the selected range. 76353942Sdimstruct SelectorData { 77353942Sdim explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {} 78353942Sdim RangeSelector Selector; 79353942Sdim}; 80353942Sdim 81353942Sdim// A stencil operation to build a member access `e.m` or `e->m`, as appropriate. 82353942Sdimstruct AccessData { 83357095Sdim AccessData(StringRef BaseId, Stencil Member) 84353942Sdim : BaseId(BaseId), Member(std::move(Member)) {} 85353942Sdim std::string BaseId; 86357095Sdim Stencil Member; 87353942Sdim}; 88353942Sdim 89353942Sdimstruct IfBoundData { 90357095Sdim IfBoundData(StringRef Id, Stencil TrueStencil, Stencil FalseStencil) 91357095Sdim : Id(Id), TrueStencil(std::move(TrueStencil)), 92357095Sdim FalseStencil(std::move(FalseStencil)) {} 93353942Sdim std::string Id; 94357095Sdim Stencil TrueStencil; 95357095Sdim Stencil FalseStencil; 96353942Sdim}; 97353942Sdim 98357095Sdimstruct SequenceData { 99357095Sdim SequenceData(std::vector<Stencil> Stencils) : Stencils(std::move(Stencils)) {} 100357095Sdim std::vector<Stencil> Stencils; 101357095Sdim}; 102357095Sdim 103353942Sdimstd::string toStringData(const RawTextData &Data) { 104353942Sdim std::string Result; 105353942Sdim llvm::raw_string_ostream OS(Result); 106353942Sdim OS << "\""; 107353942Sdim OS.write_escaped(Data.Text); 108353942Sdim OS << "\""; 109353942Sdim OS.flush(); 110353942Sdim return Result; 111353942Sdim} 112353942Sdim 113353942Sdimstd::string toStringData(const DebugPrintNodeData &Data) { 114353942Sdim return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str(); 115353942Sdim} 116353942Sdim 117353942Sdimstd::string toStringData(const UnaryOperationData &Data) { 118353942Sdim StringRef OpName; 119353942Sdim switch (Data.Op) { 120353942Sdim case UnaryNodeOperator::Parens: 121353942Sdim OpName = "expression"; 122353942Sdim break; 123353942Sdim case UnaryNodeOperator::Deref: 124353942Sdim OpName = "deref"; 125353942Sdim break; 126357095Sdim case UnaryNodeOperator::MaybeDeref: 127357095Sdim OpName = "maybeDeref"; 128357095Sdim break; 129357095Sdim case UnaryNodeOperator::AddressOf: 130353942Sdim OpName = "addressOf"; 131353942Sdim break; 132357095Sdim case UnaryNodeOperator::MaybeAddressOf: 133357095Sdim OpName = "maybeAddressOf"; 134357095Sdim break; 135353942Sdim } 136353942Sdim return (OpName + "(\"" + Data.Id + "\")").str(); 137353942Sdim} 138353942Sdim 139353942Sdimstd::string toStringData(const SelectorData &) { return "selection(...)"; } 140353942Sdim 141353942Sdimstd::string toStringData(const AccessData &Data) { 142353942Sdim return (llvm::Twine("access(\"") + Data.BaseId + "\", " + 143357095Sdim Data.Member->toString() + ")") 144353942Sdim .str(); 145353942Sdim} 146353942Sdim 147353942Sdimstd::string toStringData(const IfBoundData &Data) { 148353942Sdim return (llvm::Twine("ifBound(\"") + Data.Id + "\", " + 149357095Sdim Data.TrueStencil->toString() + ", " + Data.FalseStencil->toString() + 150357095Sdim ")") 151353942Sdim .str(); 152353942Sdim} 153353942Sdim 154353942Sdimstd::string toStringData(const MatchConsumer<std::string> &) { 155353942Sdim return "run(...)"; 156353942Sdim} 157353942Sdim 158357095Sdimstd::string toStringData(const SequenceData &Data) { 159357095Sdim llvm::SmallVector<std::string, 2> Parts; 160357095Sdim Parts.reserve(Data.Stencils.size()); 161357095Sdim for (const auto &S : Data.Stencils) 162357095Sdim Parts.push_back(S->toString()); 163357095Sdim return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str(); 164357095Sdim} 165357095Sdim 166353942Sdim// The `evalData()` overloads evaluate the given stencil data to a string, given 167353942Sdim// the match result, and append it to `Result`. We define an overload for each 168353942Sdim// type of stencil data. 169353942Sdim 170353942SdimError evalData(const RawTextData &Data, const MatchFinder::MatchResult &, 171353942Sdim std::string *Result) { 172353942Sdim Result->append(Data.Text); 173353942Sdim return Error::success(); 174353942Sdim} 175353942Sdim 176353942SdimError evalData(const DebugPrintNodeData &Data, 177353942Sdim const MatchFinder::MatchResult &Match, std::string *Result) { 178353942Sdim std::string Output; 179353942Sdim llvm::raw_string_ostream Os(Output); 180353942Sdim auto NodeOrErr = getNode(Match.Nodes, Data.Id); 181353942Sdim if (auto Err = NodeOrErr.takeError()) 182353942Sdim return Err; 183353942Sdim NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts())); 184353942Sdim *Result += Os.str(); 185353942Sdim return Error::success(); 186353942Sdim} 187353942Sdim 188353942SdimError evalData(const UnaryOperationData &Data, 189353942Sdim const MatchFinder::MatchResult &Match, std::string *Result) { 190353942Sdim const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id); 191353942Sdim if (E == nullptr) 192353942Sdim return llvm::make_error<StringError>( 193353942Sdim errc::invalid_argument, "Id not bound or not Expr: " + Data.Id); 194353942Sdim llvm::Optional<std::string> Source; 195353942Sdim switch (Data.Op) { 196353942Sdim case UnaryNodeOperator::Parens: 197353942Sdim Source = tooling::buildParens(*E, *Match.Context); 198353942Sdim break; 199353942Sdim case UnaryNodeOperator::Deref: 200353942Sdim Source = tooling::buildDereference(*E, *Match.Context); 201353942Sdim break; 202357095Sdim case UnaryNodeOperator::MaybeDeref: 203357095Sdim if (!E->getType()->isAnyPointerType()) { 204357095Sdim *Result += tooling::getText(*E, *Match.Context); 205357095Sdim return Error::success(); 206357095Sdim } 207357095Sdim Source = tooling::buildDereference(*E, *Match.Context); 208357095Sdim break; 209357095Sdim case UnaryNodeOperator::AddressOf: 210353942Sdim Source = tooling::buildAddressOf(*E, *Match.Context); 211353942Sdim break; 212357095Sdim case UnaryNodeOperator::MaybeAddressOf: 213357095Sdim if (E->getType()->isAnyPointerType()) { 214357095Sdim *Result += tooling::getText(*E, *Match.Context); 215357095Sdim return Error::success(); 216357095Sdim } 217357095Sdim Source = tooling::buildAddressOf(*E, *Match.Context); 218357095Sdim break; 219353942Sdim } 220353942Sdim if (!Source) 221353942Sdim return llvm::make_error<StringError>( 222353942Sdim errc::invalid_argument, 223353942Sdim "Could not construct expression source from ID: " + Data.Id); 224353942Sdim *Result += *Source; 225353942Sdim return Error::success(); 226353942Sdim} 227353942Sdim 228353942SdimError evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match, 229353942Sdim std::string *Result) { 230353942Sdim auto Range = Data.Selector(Match); 231353942Sdim if (!Range) 232353942Sdim return Range.takeError(); 233353942Sdim *Result += tooling::getText(*Range, *Match.Context); 234353942Sdim return Error::success(); 235353942Sdim} 236353942Sdim 237353942SdimError evalData(const AccessData &Data, const MatchFinder::MatchResult &Match, 238353942Sdim std::string *Result) { 239353942Sdim const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId); 240353942Sdim if (E == nullptr) 241353942Sdim return llvm::make_error<StringError>(errc::invalid_argument, 242353942Sdim "Id not bound: " + Data.BaseId); 243353942Sdim if (!E->isImplicitCXXThis()) { 244353942Sdim if (llvm::Optional<std::string> S = 245353942Sdim E->getType()->isAnyPointerType() 246353942Sdim ? tooling::buildArrow(*E, *Match.Context) 247353942Sdim : tooling::buildDot(*E, *Match.Context)) 248353942Sdim *Result += *S; 249353942Sdim else 250353942Sdim return llvm::make_error<StringError>( 251353942Sdim errc::invalid_argument, 252353942Sdim "Could not construct object text from ID: " + Data.BaseId); 253353942Sdim } 254357095Sdim return Data.Member->eval(Match, Result); 255353942Sdim} 256353942Sdim 257353942SdimError evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, 258353942Sdim std::string *Result) { 259353942Sdim auto &M = Match.Nodes.getMap(); 260357095Sdim return (M.find(Data.Id) != M.end() ? Data.TrueStencil : Data.FalseStencil) 261357095Sdim ->eval(Match, Result); 262353942Sdim} 263353942Sdim 264353942SdimError evalData(const MatchConsumer<std::string> &Fn, 265353942Sdim const MatchFinder::MatchResult &Match, std::string *Result) { 266353942Sdim Expected<std::string> Value = Fn(Match); 267353942Sdim if (!Value) 268353942Sdim return Value.takeError(); 269353942Sdim *Result += *Value; 270353942Sdim return Error::success(); 271353942Sdim} 272353942Sdim 273357095SdimError evalData(const SequenceData &Data, const MatchFinder::MatchResult &Match, 274357095Sdim std::string *Result) { 275357095Sdim for (const auto &S : Data.Stencils) 276357095Sdim if (auto Err = S->eval(Match, Result)) 277357095Sdim return Err; 278357095Sdim return Error::success(); 279357095Sdim} 280357095Sdim 281357095Sdimtemplate <typename T> class StencilImpl : public StencilInterface { 282353942Sdim T Data; 283353942Sdim 284353942Sdimpublic: 285353942Sdim template <typename... Ps> 286357095Sdim explicit StencilImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {} 287353942Sdim 288353942Sdim Error eval(const MatchFinder::MatchResult &Match, 289353942Sdim std::string *Result) const override { 290353942Sdim return evalData(Data, Match, Result); 291353942Sdim } 292353942Sdim 293353942Sdim std::string toString() const override { return toStringData(Data); } 294353942Sdim}; 295353942Sdim} // namespace 296353942Sdim 297357095SdimStencil transformer::detail::makeStencil(StringRef Text) { return text(Text); } 298357095Sdim 299357095SdimStencil transformer::detail::makeStencil(RangeSelector Selector) { 300357095Sdim return selection(std::move(Selector)); 301353942Sdim} 302353942Sdim 303357095SdimStencil transformer::text(StringRef Text) { 304357095Sdim return std::make_shared<StencilImpl<RawTextData>>(Text); 305353942Sdim} 306353942Sdim 307357095SdimStencil transformer::selection(RangeSelector Selector) { 308357095Sdim return std::make_shared<StencilImpl<SelectorData>>(std::move(Selector)); 309353942Sdim} 310353942Sdim 311357095SdimStencil transformer::dPrint(StringRef Id) { 312357095Sdim return std::make_shared<StencilImpl<DebugPrintNodeData>>(Id); 313353942Sdim} 314353942Sdim 315357095SdimStencil transformer::expression(llvm::StringRef Id) { 316357095Sdim return std::make_shared<StencilImpl<UnaryOperationData>>( 317357095Sdim UnaryNodeOperator::Parens, Id); 318353942Sdim} 319353942Sdim 320357095SdimStencil transformer::deref(llvm::StringRef ExprId) { 321357095Sdim return std::make_shared<StencilImpl<UnaryOperationData>>( 322357095Sdim UnaryNodeOperator::Deref, ExprId); 323353942Sdim} 324353942Sdim 325357095SdimStencil transformer::maybeDeref(llvm::StringRef ExprId) { 326357095Sdim return std::make_shared<StencilImpl<UnaryOperationData>>( 327357095Sdim UnaryNodeOperator::MaybeDeref, ExprId); 328353942Sdim} 329353942Sdim 330357095SdimStencil transformer::addressOf(llvm::StringRef ExprId) { 331357095Sdim return std::make_shared<StencilImpl<UnaryOperationData>>( 332357095Sdim UnaryNodeOperator::AddressOf, ExprId); 333353942Sdim} 334353942Sdim 335357095SdimStencil transformer::maybeAddressOf(llvm::StringRef ExprId) { 336357095Sdim return std::make_shared<StencilImpl<UnaryOperationData>>( 337357095Sdim UnaryNodeOperator::MaybeAddressOf, ExprId); 338353942Sdim} 339353942Sdim 340357095SdimStencil transformer::access(StringRef BaseId, Stencil Member) { 341357095Sdim return std::make_shared<StencilImpl<AccessData>>(BaseId, std::move(Member)); 342353942Sdim} 343353942Sdim 344357095SdimStencil transformer::ifBound(StringRef Id, Stencil TrueStencil, 345357095Sdim Stencil FalseStencil) { 346357095Sdim return std::make_shared<StencilImpl<IfBoundData>>(Id, std::move(TrueStencil), 347357095Sdim std::move(FalseStencil)); 348353942Sdim} 349353942Sdim 350357095SdimStencil transformer::run(MatchConsumer<std::string> Fn) { 351357095Sdim return std::make_shared<StencilImpl<MatchConsumer<std::string>>>( 352357095Sdim std::move(Fn)); 353353942Sdim} 354353942Sdim 355357095SdimStencil transformer::catVector(std::vector<Stencil> Parts) { 356357095Sdim // Only one argument, so don't wrap in sequence. 357357095Sdim if (Parts.size() == 1) 358357095Sdim return std::move(Parts[0]); 359357095Sdim return std::make_shared<StencilImpl<SequenceData>>(std::move(Parts)); 360353942Sdim} 361