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