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 Address, 63 }; 64 65 // Generic container for stencil operations with a (single) node-id argument. 66 struct UnaryOperationData { 67 UnaryOperationData(UnaryNodeOperator Op, std::string Id) 68 : Op(Op), Id(std::move(Id)) {} 69 UnaryNodeOperator Op; 70 std::string Id; 71 }; 72 73 // The fragment of code corresponding to the selected range. 74 struct SelectorData { 75 explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {} 76 RangeSelector Selector; 77 }; 78 79 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate. 80 struct AccessData { 81 AccessData(StringRef BaseId, Stencil Member) 82 : BaseId(BaseId), Member(std::move(Member)) {} 83 std::string BaseId; 84 Stencil Member; 85 }; 86 87 struct IfBoundData { 88 IfBoundData(StringRef Id, Stencil TrueStencil, Stencil FalseStencil) 89 : Id(Id), TrueStencil(std::move(TrueStencil)), 90 FalseStencil(std::move(FalseStencil)) {} 91 std::string Id; 92 Stencil TrueStencil; 93 Stencil FalseStencil; 94 }; 95 96 struct SequenceData { 97 SequenceData(std::vector<Stencil> Stencils) : Stencils(std::move(Stencils)) {} 98 std::vector<Stencil> Stencils; 99 }; 100 101 std::string toStringData(const RawTextData &Data) { 102 std::string Result; 103 llvm::raw_string_ostream OS(Result); 104 OS << "\""; 105 OS.write_escaped(Data.Text); 106 OS << "\""; 107 OS.flush(); 108 return Result; 109 } 110 111 std::string toStringData(const DebugPrintNodeData &Data) { 112 return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str(); 113 } 114 115 std::string toStringData(const UnaryOperationData &Data) { 116 StringRef OpName; 117 switch (Data.Op) { 118 case UnaryNodeOperator::Parens: 119 OpName = "expression"; 120 break; 121 case UnaryNodeOperator::Deref: 122 OpName = "deref"; 123 break; 124 case UnaryNodeOperator::Address: 125 OpName = "addressOf"; 126 break; 127 } 128 return (OpName + "(\"" + Data.Id + "\")").str(); 129 } 130 131 std::string toStringData(const SelectorData &) { return "selection(...)"; } 132 133 std::string toStringData(const AccessData &Data) { 134 return (llvm::Twine("access(\"") + Data.BaseId + "\", " + 135 Data.Member->toString() + ")") 136 .str(); 137 } 138 139 std::string toStringData(const IfBoundData &Data) { 140 return (llvm::Twine("ifBound(\"") + Data.Id + "\", " + 141 Data.TrueStencil->toString() + ", " + Data.FalseStencil->toString() + 142 ")") 143 .str(); 144 } 145 146 std::string toStringData(const MatchConsumer<std::string> &) { 147 return "run(...)"; 148 } 149 150 std::string toStringData(const SequenceData &Data) { 151 llvm::SmallVector<std::string, 2> Parts; 152 Parts.reserve(Data.Stencils.size()); 153 for (const auto &S : Data.Stencils) 154 Parts.push_back(S->toString()); 155 return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str(); 156 } 157 158 // The `evalData()` overloads evaluate the given stencil data to a string, given 159 // the match result, and append it to `Result`. We define an overload for each 160 // type of stencil data. 161 162 Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &, 163 std::string *Result) { 164 Result->append(Data.Text); 165 return Error::success(); 166 } 167 168 Error evalData(const DebugPrintNodeData &Data, 169 const MatchFinder::MatchResult &Match, std::string *Result) { 170 std::string Output; 171 llvm::raw_string_ostream Os(Output); 172 auto NodeOrErr = getNode(Match.Nodes, Data.Id); 173 if (auto Err = NodeOrErr.takeError()) 174 return Err; 175 NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts())); 176 *Result += Os.str(); 177 return Error::success(); 178 } 179 180 Error evalData(const UnaryOperationData &Data, 181 const MatchFinder::MatchResult &Match, std::string *Result) { 182 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id); 183 if (E == nullptr) 184 return llvm::make_error<StringError>( 185 errc::invalid_argument, "Id not bound or not Expr: " + Data.Id); 186 llvm::Optional<std::string> Source; 187 switch (Data.Op) { 188 case UnaryNodeOperator::Parens: 189 Source = tooling::buildParens(*E, *Match.Context); 190 break; 191 case UnaryNodeOperator::Deref: 192 Source = tooling::buildDereference(*E, *Match.Context); 193 break; 194 case UnaryNodeOperator::Address: 195 Source = tooling::buildAddressOf(*E, *Match.Context); 196 break; 197 } 198 if (!Source) 199 return llvm::make_error<StringError>( 200 errc::invalid_argument, 201 "Could not construct expression source from ID: " + Data.Id); 202 *Result += *Source; 203 return Error::success(); 204 } 205 206 Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match, 207 std::string *Result) { 208 auto Range = Data.Selector(Match); 209 if (!Range) 210 return Range.takeError(); 211 *Result += tooling::getText(*Range, *Match.Context); 212 return Error::success(); 213 } 214 215 Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match, 216 std::string *Result) { 217 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId); 218 if (E == nullptr) 219 return llvm::make_error<StringError>(errc::invalid_argument, 220 "Id not bound: " + Data.BaseId); 221 if (!E->isImplicitCXXThis()) { 222 if (llvm::Optional<std::string> S = 223 E->getType()->isAnyPointerType() 224 ? tooling::buildArrow(*E, *Match.Context) 225 : tooling::buildDot(*E, *Match.Context)) 226 *Result += *S; 227 else 228 return llvm::make_error<StringError>( 229 errc::invalid_argument, 230 "Could not construct object text from ID: " + Data.BaseId); 231 } 232 return Data.Member->eval(Match, Result); 233 } 234 235 Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, 236 std::string *Result) { 237 auto &M = Match.Nodes.getMap(); 238 return (M.find(Data.Id) != M.end() ? Data.TrueStencil : Data.FalseStencil) 239 ->eval(Match, Result); 240 } 241 242 Error evalData(const MatchConsumer<std::string> &Fn, 243 const MatchFinder::MatchResult &Match, std::string *Result) { 244 Expected<std::string> Value = Fn(Match); 245 if (!Value) 246 return Value.takeError(); 247 *Result += *Value; 248 return Error::success(); 249 } 250 251 Error evalData(const SequenceData &Data, const MatchFinder::MatchResult &Match, 252 std::string *Result) { 253 for (const auto &S : Data.Stencils) 254 if (auto Err = S->eval(Match, Result)) 255 return Err; 256 return Error::success(); 257 } 258 259 template <typename T> class StencilImpl : public StencilInterface { 260 T Data; 261 262 public: 263 template <typename... Ps> 264 explicit StencilImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {} 265 266 Error eval(const MatchFinder::MatchResult &Match, 267 std::string *Result) const override { 268 return evalData(Data, Match, Result); 269 } 270 271 std::string toString() const override { return toStringData(Data); } 272 }; 273 } // namespace 274 275 llvm::Expected<std::string> 276 StencilInterface::eval(const MatchFinder::MatchResult &R) const { 277 std::string Output; 278 if (auto Err = eval(R, &Output)) 279 return std::move(Err); 280 return Output; 281 } 282 283 Stencil transformer::detail::makeStencil(StringRef Text) { return text(Text); } 284 285 Stencil transformer::detail::makeStencil(RangeSelector Selector) { 286 return selection(std::move(Selector)); 287 } 288 289 Stencil transformer::text(StringRef Text) { 290 return Stencil(std::make_shared<StencilImpl<RawTextData>>(Text)); 291 } 292 293 Stencil transformer::selection(RangeSelector Selector) { 294 return Stencil( 295 std::make_shared<StencilImpl<SelectorData>>(std::move(Selector))); 296 } 297 298 Stencil transformer::dPrint(StringRef Id) { 299 return Stencil(std::make_shared<StencilImpl<DebugPrintNodeData>>(Id)); 300 } 301 302 Stencil transformer::expression(llvm::StringRef Id) { 303 return Stencil(std::make_shared<StencilImpl<UnaryOperationData>>( 304 UnaryNodeOperator::Parens, Id)); 305 } 306 307 Stencil transformer::deref(llvm::StringRef ExprId) { 308 return Stencil(std::make_shared<StencilImpl<UnaryOperationData>>( 309 UnaryNodeOperator::Deref, ExprId)); 310 } 311 312 Stencil transformer::addressOf(llvm::StringRef ExprId) { 313 return Stencil(std::make_shared<StencilImpl<UnaryOperationData>>( 314 UnaryNodeOperator::Address, ExprId)); 315 } 316 317 Stencil transformer::access(StringRef BaseId, Stencil Member) { 318 return Stencil( 319 std::make_shared<StencilImpl<AccessData>>(BaseId, std::move(Member))); 320 } 321 322 Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil, 323 Stencil FalseStencil) { 324 return Stencil(std::make_shared<StencilImpl<IfBoundData>>( 325 Id, std::move(TrueStencil), std::move(FalseStencil))); 326 } 327 328 Stencil transformer::run(MatchConsumer<std::string> Fn) { 329 return Stencil( 330 std::make_shared<StencilImpl<MatchConsumer<std::string>>>(std::move(Fn))); 331 } 332 333 Stencil transformer::catVector(std::vector<Stencil> Parts) { 334 // Only one argument, so don't wrap in sequence. 335 if (Parts.size() == 1) 336 return std::move(Parts[0]); 337 return Stencil(std::make_shared<StencilImpl<SequenceData>>(std::move(Parts))); 338 } 339