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 tooling; 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 = buildParens(*E, *Match.Context); 175 break; 176 case UnaryNodeOperator::Deref: 177 Source = buildDereference(*E, *Match.Context); 178 break; 179 case UnaryNodeOperator::Address: 180 Source = 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 += 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 = E->getType()->isAnyPointerType() 208 ? buildArrow(*E, *Match.Context) 209 : buildDot(*E, *Match.Context)) 210 *Result += *S; 211 else 212 return llvm::make_error<StringError>( 213 errc::invalid_argument, 214 "Could not construct object text from ID: " + Data.BaseId); 215 } 216 return Data.Member.eval(Match, Result); 217 } 218 219 Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, 220 std::string *Result) { 221 auto &M = Match.Nodes.getMap(); 222 return (M.find(Data.Id) != M.end() ? Data.TruePart : Data.FalsePart) 223 .eval(Match, Result); 224 } 225 226 Error evalData(const MatchConsumer<std::string> &Fn, 227 const MatchFinder::MatchResult &Match, std::string *Result) { 228 Expected<std::string> Value = Fn(Match); 229 if (!Value) 230 return Value.takeError(); 231 *Result += *Value; 232 return Error::success(); 233 } 234 235 template <typename T> 236 class StencilPartImpl : public StencilPartInterface { 237 T Data; 238 239 public: 240 template <typename... Ps> 241 explicit StencilPartImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {} 242 243 Error eval(const MatchFinder::MatchResult &Match, 244 std::string *Result) const override { 245 return evalData(Data, Match, Result); 246 } 247 248 std::string toString() const override { return toStringData(Data); } 249 }; 250 } // namespace 251 252 StencilPart Stencil::wrap(StringRef Text) { 253 return stencil::text(Text); 254 } 255 256 StencilPart Stencil::wrap(RangeSelector Selector) { 257 return stencil::selection(std::move(Selector)); 258 } 259 260 void Stencil::append(Stencil OtherStencil) { 261 for (auto &Part : OtherStencil.Parts) 262 Parts.push_back(std::move(Part)); 263 } 264 265 llvm::Expected<std::string> 266 Stencil::eval(const MatchFinder::MatchResult &Match) const { 267 std::string Result; 268 for (const auto &Part : Parts) 269 if (auto Err = Part.eval(Match, &Result)) 270 return std::move(Err); 271 return Result; 272 } 273 274 StencilPart stencil::text(StringRef Text) { 275 return StencilPart(std::make_shared<StencilPartImpl<RawTextData>>(Text)); 276 } 277 278 StencilPart stencil::selection(RangeSelector Selector) { 279 return StencilPart( 280 std::make_shared<StencilPartImpl<SelectorData>>(std::move(Selector))); 281 } 282 283 StencilPart stencil::dPrint(StringRef Id) { 284 return StencilPart(std::make_shared<StencilPartImpl<DebugPrintNodeData>>(Id)); 285 } 286 287 StencilPart stencil::expression(llvm::StringRef Id) { 288 return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( 289 UnaryNodeOperator::Parens, Id)); 290 } 291 292 StencilPart stencil::deref(llvm::StringRef ExprId) { 293 return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( 294 UnaryNodeOperator::Deref, ExprId)); 295 } 296 297 StencilPart stencil::addressOf(llvm::StringRef ExprId) { 298 return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>( 299 UnaryNodeOperator::Address, ExprId)); 300 } 301 302 StencilPart stencil::access(StringRef BaseId, StencilPart Member) { 303 return StencilPart( 304 std::make_shared<StencilPartImpl<AccessData>>(BaseId, std::move(Member))); 305 } 306 307 StencilPart stencil::ifBound(StringRef Id, StencilPart TruePart, 308 StencilPart FalsePart) { 309 return StencilPart(std::make_shared<StencilPartImpl<IfBoundData>>( 310 Id, std::move(TruePart), std::move(FalsePart))); 311 } 312 313 StencilPart stencil::run(MatchConsumer<std::string> Fn) { 314 return StencilPart( 315 std::make_shared<StencilPartImpl<MatchConsumer<std::string>>>( 316 std::move(Fn))); 317 } 318