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