Stencil.cpp revision 353942
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" 18353942Sdim#include "llvm/ADT/Twine.h" 19353942Sdim#include "llvm/Support/Errc.h" 20353942Sdim#include <atomic> 21353942Sdim#include <memory> 22353942Sdim#include <string> 23353942Sdim 24353942Sdimusing namespace clang; 25353942Sdimusing namespace transformer; 26353942Sdim 27353942Sdimusing ast_matchers::MatchFinder; 28353942Sdimusing ast_type_traits::DynTypedNode; 29353942Sdimusing llvm::errc; 30353942Sdimusing llvm::Error; 31353942Sdimusing llvm::Expected; 32353942Sdimusing llvm::StringError; 33353942Sdim 34353942Sdimstatic llvm::Expected<DynTypedNode> 35353942SdimgetNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) { 36353942Sdim auto &NodesMap = Nodes.getMap(); 37353942Sdim auto It = NodesMap.find(Id); 38353942Sdim if (It == NodesMap.end()) 39353942Sdim return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument, 40353942Sdim "Id not bound: " + Id); 41353942Sdim return It->second; 42353942Sdim} 43353942Sdim 44353942Sdimnamespace { 45353942Sdim// An arbitrary fragment of code within a stencil. 46353942Sdimstruct RawTextData { 47353942Sdim explicit RawTextData(std::string T) : Text(std::move(T)) {} 48353942Sdim std::string Text; 49353942Sdim}; 50353942Sdim 51353942Sdim// A debugging operation to dump the AST for a particular (bound) AST node. 52353942Sdimstruct DebugPrintNodeData { 53353942Sdim explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {} 54353942Sdim std::string Id; 55353942Sdim}; 56353942Sdim 57353942Sdim// Operators that take a single node Id as an argument. 58353942Sdimenum class UnaryNodeOperator { 59353942Sdim Parens, 60353942Sdim Deref, 61353942Sdim Address, 62353942Sdim}; 63353942Sdim 64353942Sdim// Generic container for stencil operations with a (single) node-id argument. 65353942Sdimstruct UnaryOperationData { 66353942Sdim UnaryOperationData(UnaryNodeOperator Op, std::string Id) 67353942Sdim : Op(Op), Id(std::move(Id)) {} 68353942Sdim UnaryNodeOperator Op; 69353942Sdim std::string Id; 70353942Sdim}; 71353942Sdim 72353942Sdim// The fragment of code corresponding to the selected range. 73353942Sdimstruct SelectorData { 74353942Sdim explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {} 75353942Sdim RangeSelector Selector; 76353942Sdim}; 77353942Sdim 78353942Sdim// A stencil operation to build a member access `e.m` or `e->m`, as appropriate. 79353942Sdimstruct AccessData { 80353942Sdim AccessData(StringRef BaseId, StencilPart Member) 81353942Sdim : BaseId(BaseId), Member(std::move(Member)) {} 82353942Sdim std::string BaseId; 83353942Sdim StencilPart Member; 84353942Sdim}; 85353942Sdim 86353942Sdimstruct IfBoundData { 87353942Sdim IfBoundData(StringRef Id, StencilPart TruePart, StencilPart FalsePart) 88353942Sdim : Id(Id), TruePart(std::move(TruePart)), FalsePart(std::move(FalsePart)) { 89353942Sdim } 90353942Sdim std::string Id; 91353942Sdim StencilPart TruePart; 92353942Sdim StencilPart FalsePart; 93353942Sdim}; 94353942Sdim 95353942Sdimstd::string toStringData(const RawTextData &Data) { 96353942Sdim std::string Result; 97353942Sdim llvm::raw_string_ostream OS(Result); 98353942Sdim OS << "\""; 99353942Sdim OS.write_escaped(Data.Text); 100353942Sdim OS << "\""; 101353942Sdim OS.flush(); 102353942Sdim return Result; 103353942Sdim} 104353942Sdim 105353942Sdimstd::string toStringData(const DebugPrintNodeData &Data) { 106353942Sdim return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str(); 107353942Sdim} 108353942Sdim 109353942Sdimstd::string toStringData(const UnaryOperationData &Data) { 110353942Sdim StringRef OpName; 111353942Sdim switch (Data.Op) { 112353942Sdim case UnaryNodeOperator::Parens: 113353942Sdim OpName = "expression"; 114353942Sdim break; 115353942Sdim case UnaryNodeOperator::Deref: 116353942Sdim OpName = "deref"; 117353942Sdim break; 118353942Sdim case UnaryNodeOperator::Address: 119353942Sdim OpName = "addressOf"; 120353942Sdim break; 121353942Sdim } 122353942Sdim return (OpName + "(\"" + Data.Id + "\")").str(); 123353942Sdim} 124353942Sdim 125353942Sdimstd::string toStringData(const SelectorData &) { return "selection(...)"; } 126353942Sdim 127353942Sdimstd::string toStringData(const AccessData &Data) { 128353942Sdim return (llvm::Twine("access(\"") + Data.BaseId + "\", " + 129353942Sdim Data.Member.toString() + ")") 130353942Sdim .str(); 131353942Sdim} 132353942Sdim 133353942Sdimstd::string toStringData(const IfBoundData &Data) { 134353942Sdim return (llvm::Twine("ifBound(\"") + Data.Id + "\", " + 135353942Sdim Data.TruePart.toString() + ", " + Data.FalsePart.toString() + ")") 136353942Sdim .str(); 137353942Sdim} 138353942Sdim 139353942Sdimstd::string toStringData(const MatchConsumer<std::string> &) { 140353942Sdim return "run(...)"; 141353942Sdim} 142353942Sdim 143353942Sdim// The `evalData()` overloads evaluate the given stencil data to a string, given 144353942Sdim// the match result, and append it to `Result`. We define an overload for each 145353942Sdim// type of stencil data. 146353942Sdim 147353942SdimError evalData(const RawTextData &Data, const MatchFinder::MatchResult &, 148353942Sdim std::string *Result) { 149353942Sdim Result->append(Data.Text); 150353942Sdim return Error::success(); 151353942Sdim} 152353942Sdim 153353942SdimError evalData(const DebugPrintNodeData &Data, 154353942Sdim const MatchFinder::MatchResult &Match, std::string *Result) { 155353942Sdim std::string Output; 156353942Sdim llvm::raw_string_ostream Os(Output); 157353942Sdim auto NodeOrErr = getNode(Match.Nodes, Data.Id); 158353942Sdim if (auto Err = NodeOrErr.takeError()) 159353942Sdim return Err; 160353942Sdim NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts())); 161353942Sdim *Result += Os.str(); 162353942Sdim return Error::success(); 163353942Sdim} 164353942Sdim 165353942SdimError evalData(const UnaryOperationData &Data, 166353942Sdim const MatchFinder::MatchResult &Match, std::string *Result) { 167353942Sdim const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id); 168353942Sdim if (E == nullptr) 169353942Sdim return llvm::make_error<StringError>( 170353942Sdim errc::invalid_argument, "Id not bound or not Expr: " + Data.Id); 171353942Sdim llvm::Optional<std::string> Source; 172353942Sdim switch (Data.Op) { 173353942Sdim case UnaryNodeOperator::Parens: 174353942Sdim Source = tooling::buildParens(*E, *Match.Context); 175353942Sdim break; 176353942Sdim case UnaryNodeOperator::Deref: 177353942Sdim Source = tooling::buildDereference(*E, *Match.Context); 178353942Sdim break; 179353942Sdim case UnaryNodeOperator::Address: 180353942Sdim Source = tooling::buildAddressOf(*E, *Match.Context); 181353942Sdim break; 182353942Sdim } 183353942Sdim if (!Source) 184353942Sdim return llvm::make_error<StringError>( 185353942Sdim errc::invalid_argument, 186353942Sdim "Could not construct expression source from ID: " + Data.Id); 187353942Sdim *Result += *Source; 188353942Sdim return Error::success(); 189353942Sdim} 190353942Sdim 191353942SdimError evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match, 192353942Sdim std::string *Result) { 193353942Sdim auto Range = Data.Selector(Match); 194353942Sdim if (!Range) 195353942Sdim return Range.takeError(); 196353942Sdim *Result += tooling::getText(*Range, *Match.Context); 197353942Sdim return Error::success(); 198353942Sdim} 199353942Sdim 200353942SdimError evalData(const AccessData &Data, const MatchFinder::MatchResult &Match, 201353942Sdim std::string *Result) { 202353942Sdim const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId); 203353942Sdim if (E == nullptr) 204353942Sdim return llvm::make_error<StringError>(errc::invalid_argument, 205353942Sdim "Id not bound: " + Data.BaseId); 206353942Sdim if (!E->isImplicitCXXThis()) { 207353942Sdim if (llvm::Optional<std::string> S = 208353942Sdim E->getType()->isAnyPointerType() 209353942Sdim ? tooling::buildArrow(*E, *Match.Context) 210353942Sdim : tooling::buildDot(*E, *Match.Context)) 211353942Sdim *Result += *S; 212353942Sdim else 213353942Sdim return llvm::make_error<StringError>( 214353942Sdim errc::invalid_argument, 215353942Sdim "Could not construct object text from ID: " + Data.BaseId); 216353942Sdim } 217353942Sdim return Data.Member.eval(Match, Result); 218353942Sdim} 219353942Sdim 220353942SdimError evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, 221353942Sdim std::string *Result) { 222353942Sdim auto &M = Match.Nodes.getMap(); 223353942Sdim return (M.find(Data.Id) != M.end() ? Data.TruePart : Data.FalsePart) 224353942Sdim .eval(Match, Result); 225353942Sdim} 226353942Sdim 227353942SdimError evalData(const MatchConsumer<std::string> &Fn, 228353942Sdim const MatchFinder::MatchResult &Match, std::string *Result) { 229353942Sdim Expected<std::string> Value = Fn(Match); 230353942Sdim if (!Value) 231353942Sdim return Value.takeError(); 232353942Sdim *Result += *Value; 233353942Sdim return Error::success(); 234353942Sdim} 235353942Sdim 236353942Sdimtemplate <typename T> 237353942Sdimclass StencilPartImpl : public StencilPartInterface { 238353942Sdim T Data; 239353942Sdim 240353942Sdimpublic: 241353942Sdim template <typename... Ps> 242353942Sdim explicit StencilPartImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {} 243353942Sdim 244353942Sdim Error eval(const MatchFinder::MatchResult &Match, 245353942Sdim std::string *Result) const override { 246353942Sdim return evalData(Data, Match, Result); 247353942Sdim } 248353942Sdim 249353942Sdim std::string toString() const override { return toStringData(Data); } 250353942Sdim}; 251353942Sdim} // namespace 252353942Sdim 253353942SdimStencilPart Stencil::wrap(StringRef Text) { 254353942Sdim return transformer::text(Text); 255353942Sdim} 256353942Sdim 257353942SdimStencilPart Stencil::wrap(RangeSelector Selector) { 258353942Sdim return transformer::selection(std::move(Selector)); 259353942Sdim} 260353942Sdim 261353942Sdimvoid Stencil::append(Stencil OtherStencil) { 262353942Sdim for (auto &Part : OtherStencil.Parts) 263353942Sdim Parts.push_back(std::move(Part)); 264353942Sdim} 265353942Sdim 266353942Sdimllvm::Expected<std::string> 267353942SdimStencil::eval(const MatchFinder::MatchResult &Match) const { 268353942Sdim std::string Result; 269353942Sdim for (const auto &Part : Parts) 270353942Sdim if (auto Err = Part.eval(Match, &Result)) 271353942Sdim return std::move(Err); 272353942Sdim return Result; 273353942Sdim} 274353942Sdim 275353942SdimStencilPart transformer::text(StringRef Text) { 276353942Sdim return StencilPart(std::make_shared<StencilPartImpl<RawTextData>>(Text)); 277353942Sdim} 278353942Sdim 279353942SdimStencilPart transformer::selection(RangeSelector Selector) { 280353942Sdim return StencilPart( 281353942Sdim std::make_shared<StencilPartImpl<SelectorData>>(std::move(Selector))); 282353942Sdim} 283353942Sdim 284353942SdimStencilPart transformer::dPrint(StringRef Id) { 285353942Sdim return StencilPart(std::make_shared<StencilPartImpl<DebugPrintNodeData>>(Id)); 286353942Sdim} 287353942Sdim 288353942SdimStencilPart transformer::expression(llvm::StringRef Id) { 289353942Sdim return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( 290353942Sdim UnaryNodeOperator::Parens, Id)); 291353942Sdim} 292353942Sdim 293353942SdimStencilPart transformer::deref(llvm::StringRef ExprId) { 294353942Sdim return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( 295353942Sdim UnaryNodeOperator::Deref, ExprId)); 296353942Sdim} 297353942Sdim 298353942SdimStencilPart transformer::addressOf(llvm::StringRef ExprId) { 299353942Sdim return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( 300353942Sdim UnaryNodeOperator::Address, ExprId)); 301353942Sdim} 302353942Sdim 303353942SdimStencilPart transformer::access(StringRef BaseId, StencilPart Member) { 304353942Sdim return StencilPart( 305353942Sdim std::make_shared<StencilPartImpl<AccessData>>(BaseId, std::move(Member))); 306353942Sdim} 307353942Sdim 308353942SdimStencilPart transformer::ifBound(StringRef Id, StencilPart TruePart, 309353942Sdim StencilPart FalsePart) { 310353942Sdim return StencilPart(std::make_shared<StencilPartImpl<IfBoundData>>( 311353942Sdim Id, std::move(TruePart), std::move(FalsePart))); 312353942Sdim} 313353942Sdim 314353942SdimStencilPart transformer::run(MatchConsumer<std::string> Fn) { 315353942Sdim return StencilPart( 316353942Sdim std::make_shared<StencilPartImpl<MatchConsumer<std::string>>>( 317353942Sdim std::move(Fn))); 318353942Sdim} 319