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-exception 6 // 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/ASTMatchers/ASTMatchers.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/Twine.h" 19 #include "llvm/Support/Errc.h" 20 #include <atomic> 21 #include <memory> 22 #include <string> 23 24 using namespace clang; 25 using namespace transformer; 26 27 using ast_matchers::MatchFinder; 28 using ast_type_traits::DynTypedNode; 29 using llvm::errc; 30 using llvm::Error; 31 using llvm::Expected; 32 using llvm::StringError; 33 34 static llvm::Expected<DynTypedNode> 35 getNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) { 36 auto &NodesMap = Nodes.getMap(); 37 auto It = NodesMap.find(Id); 38 if (It == NodesMap.end()) 39 return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument, 40 "Id not bound: " + Id); 41 return It->second; 42 } 43 44 namespace { 45 // An arbitrary fragment of code within a stencil. 46 struct RawTextData { 47 explicit RawTextData(std::string T) : Text(std::move(T)) {} 48 std::string Text; 49 }; 50 51 // A debugging operation to dump the AST for a particular (bound) AST node. 52 struct DebugPrintNodeData { 53 explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {} 54 std::string Id; 55 }; 56 57 // Operators that take a single node Id as an argument. 58 enum class UnaryNodeOperator { 59 Parens, 60 Deref, 61 Address, 62 }; 63 64 // Generic container for stencil operations with a (single) node-id argument. 65 struct UnaryOperationData { 66 UnaryOperationData(UnaryNodeOperator Op, std::string Id) 67 : Op(Op), Id(std::move(Id)) {} 68 UnaryNodeOperator Op; 69 std::string Id; 70 }; 71 72 // The fragment of code corresponding to the selected range. 73 struct SelectorData { 74 explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {} 75 RangeSelector Selector; 76 }; 77 78 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate. 79 struct AccessData { 80 AccessData(StringRef BaseId, StencilPart Member) 81 : BaseId(BaseId), Member(std::move(Member)) {} 82 std::string BaseId; 83 StencilPart Member; 84 }; 85 86 struct IfBoundData { 87 IfBoundData(StringRef Id, StencilPart TruePart, StencilPart FalsePart) 88 : Id(Id), TruePart(std::move(TruePart)), FalsePart(std::move(FalsePart)) { 89 } 90 std::string Id; 91 StencilPart TruePart; 92 StencilPart FalsePart; 93 }; 94 95 std::string toStringData(const RawTextData &Data) { 96 std::string Result; 97 llvm::raw_string_ostream OS(Result); 98 OS << "\""; 99 OS.write_escaped(Data.Text); 100 OS << "\""; 101 OS.flush(); 102 return Result; 103 } 104 105 std::string toStringData(const DebugPrintNodeData &Data) { 106 return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str(); 107 } 108 109 std::string toStringData(const UnaryOperationData &Data) { 110 StringRef OpName; 111 switch (Data.Op) { 112 case UnaryNodeOperator::Parens: 113 OpName = "expression"; 114 break; 115 case UnaryNodeOperator::Deref: 116 OpName = "deref"; 117 break; 118 case UnaryNodeOperator::Address: 119 OpName = "addressOf"; 120 break; 121 } 122 return (OpName + "(\"" + Data.Id + "\")").str(); 123 } 124 125 std::string toStringData(const SelectorData &) { return "selection(...)"; } 126 127 std::string toStringData(const AccessData &Data) { 128 return (llvm::Twine("access(\"") + Data.BaseId + "\", " + 129 Data.Member.toString() + ")") 130 .str(); 131 } 132 133 std::string toStringData(const IfBoundData &Data) { 134 return (llvm::Twine("ifBound(\"") + Data.Id + "\", " + 135 Data.TruePart.toString() + ", " + Data.FalsePart.toString() + ")") 136 .str(); 137 } 138 139 std::string toStringData(const MatchConsumer<std::string> &) { 140 return "run(...)"; 141 } 142 143 // The `evalData()` overloads evaluate the given stencil data to a string, given 144 // the match result, and append it to `Result`. We define an overload for each 145 // type of stencil data. 146 147 Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &, 148 std::string *Result) { 149 Result->append(Data.Text); 150 return Error::success(); 151 } 152 153 Error evalData(const DebugPrintNodeData &Data, 154 const MatchFinder::MatchResult &Match, std::string *Result) { 155 std::string Output; 156 llvm::raw_string_ostream Os(Output); 157 auto NodeOrErr = getNode(Match.Nodes, Data.Id); 158 if (auto Err = NodeOrErr.takeError()) 159 return Err; 160 NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts())); 161 *Result += Os.str(); 162 return Error::success(); 163 } 164 165 Error evalData(const UnaryOperationData &Data, 166 const MatchFinder::MatchResult &Match, std::string *Result) { 167 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id); 168 if (E == nullptr) 169 return llvm::make_error<StringError>( 170 errc::invalid_argument, "Id not bound or not Expr: " + Data.Id); 171 llvm::Optional<std::string> Source; 172 switch (Data.Op) { 173 case UnaryNodeOperator::Parens: 174 Source = tooling::buildParens(*E, *Match.Context); 175 break; 176 case UnaryNodeOperator::Deref: 177 Source = tooling::buildDereference(*E, *Match.Context); 178 break; 179 case UnaryNodeOperator::Address: 180 Source = tooling::buildAddressOf(*E, *Match.Context); 181 break; 182 } 183 if (!Source) 184 return llvm::make_error<StringError>( 185 errc::invalid_argument, 186 "Could not construct expression source from ID: " + Data.Id); 187 *Result += *Source; 188 return Error::success(); 189 } 190 191 Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match, 192 std::string *Result) { 193 auto Range = Data.Selector(Match); 194 if (!Range) 195 return Range.takeError(); 196 *Result += tooling::getText(*Range, *Match.Context); 197 return Error::success(); 198 } 199 200 Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match, 201 std::string *Result) { 202 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId); 203 if (E == nullptr) 204 return llvm::make_error<StringError>(errc::invalid_argument, 205 "Id not bound: " + Data.BaseId); 206 if (!E->isImplicitCXXThis()) { 207 if (llvm::Optional<std::string> S = 208 E->getType()->isAnyPointerType() 209 ? tooling::buildArrow(*E, *Match.Context) 210 : tooling::buildDot(*E, *Match.Context)) 211 *Result += *S; 212 else 213 return llvm::make_error<StringError>( 214 errc::invalid_argument, 215 "Could not construct object text from ID: " + Data.BaseId); 216 } 217 return Data.Member.eval(Match, Result); 218 } 219 220 Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, 221 std::string *Result) { 222 auto &M = Match.Nodes.getMap(); 223 return (M.find(Data.Id) != M.end() ? Data.TruePart : Data.FalsePart) 224 .eval(Match, Result); 225 } 226 227 Error evalData(const MatchConsumer<std::string> &Fn, 228 const MatchFinder::MatchResult &Match, std::string *Result) { 229 Expected<std::string> Value = Fn(Match); 230 if (!Value) 231 return Value.takeError(); 232 *Result += *Value; 233 return Error::success(); 234 } 235 236 template <typename T> 237 class StencilPartImpl : public StencilPartInterface { 238 T Data; 239 240 public: 241 template <typename... Ps> 242 explicit StencilPartImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {} 243 244 Error eval(const MatchFinder::MatchResult &Match, 245 std::string *Result) const override { 246 return evalData(Data, Match, Result); 247 } 248 249 std::string toString() const override { return toStringData(Data); } 250 }; 251 } // namespace 252 253 StencilPart Stencil::wrap(StringRef Text) { 254 return transformer::text(Text); 255 } 256 257 StencilPart Stencil::wrap(RangeSelector Selector) { 258 return transformer::selection(std::move(Selector)); 259 } 260 261 void Stencil::append(Stencil OtherStencil) { 262 for (auto &Part : OtherStencil.Parts) 263 Parts.push_back(std::move(Part)); 264 } 265 266 llvm::Expected<std::string> 267 Stencil::eval(const MatchFinder::MatchResult &Match) const { 268 std::string Result; 269 for (const auto &Part : Parts) 270 if (auto Err = Part.eval(Match, &Result)) 271 return std::move(Err); 272 return Result; 273 } 274 275 StencilPart transformer::text(StringRef Text) { 276 return StencilPart(std::make_shared<StencilPartImpl<RawTextData>>(Text)); 277 } 278 279 StencilPart transformer::selection(RangeSelector Selector) { 280 return StencilPart( 281 std::make_shared<StencilPartImpl<SelectorData>>(std::move(Selector))); 282 } 283 284 StencilPart transformer::dPrint(StringRef Id) { 285 return StencilPart(std::make_shared<StencilPartImpl<DebugPrintNodeData>>(Id)); 286 } 287 288 StencilPart transformer::expression(llvm::StringRef Id) { 289 return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( 290 UnaryNodeOperator::Parens, Id)); 291 } 292 293 StencilPart transformer::deref(llvm::StringRef ExprId) { 294 return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( 295 UnaryNodeOperator::Deref, ExprId)); 296 } 297 298 StencilPart transformer::addressOf(llvm::StringRef ExprId) { 299 return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( 300 UnaryNodeOperator::Address, ExprId)); 301 } 302 303 StencilPart transformer::access(StringRef BaseId, StencilPart Member) { 304 return StencilPart( 305 std::make_shared<StencilPartImpl<AccessData>>(BaseId, std::move(Member))); 306 } 307 308 StencilPart transformer::ifBound(StringRef Id, StencilPart TruePart, 309 StencilPart FalsePart) { 310 return StencilPart(std::make_shared<StencilPartImpl<IfBoundData>>( 311 Id, std::move(TruePart), std::move(FalsePart))); 312 } 313 314 StencilPart transformer::run(MatchConsumer<std::string> Fn) { 315 return StencilPart( 316 std::make_shared<StencilPartImpl<MatchConsumer<std::string>>>( 317 std::move(Fn))); 318 } 319