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