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/Basic/SourceLocation.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 "llvm/Support/Error.h" 22 #include <atomic> 23 #include <memory> 24 #include <string> 25 26 using namespace clang; 27 using namespace transformer; 28 29 using ast_matchers::BoundNodes; 30 using ast_matchers::MatchFinder; 31 using llvm::errc; 32 using llvm::Error; 33 using llvm::Expected; 34 using llvm::StringError; 35 36 static llvm::Expected<DynTypedNode> getNode(const BoundNodes &Nodes, 37 StringRef Id) { 38 auto &NodesMap = Nodes.getMap(); 39 auto It = NodesMap.find(Id); 40 if (It == NodesMap.end()) 41 return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument, 42 "Id not bound: " + Id); 43 return It->second; 44 } 45 46 static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match, 47 std::string *Result) { 48 std::string Output; 49 llvm::raw_string_ostream Os(Output); 50 auto NodeOrErr = getNode(Match.Nodes, Id); 51 if (auto Err = NodeOrErr.takeError()) 52 return Err; 53 NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts())); 54 *Result += Output; 55 return Error::success(); 56 } 57 58 namespace { 59 // An arbitrary fragment of code within a stencil. 60 class RawTextStencil : public StencilInterface { 61 std::string Text; 62 63 public: 64 explicit RawTextStencil(std::string T) : Text(std::move(T)) {} 65 66 std::string toString() const override { 67 std::string Result; 68 llvm::raw_string_ostream OS(Result); 69 OS << "\""; 70 OS.write_escaped(Text); 71 OS << "\""; 72 OS.flush(); 73 return Result; 74 } 75 76 Error eval(const MatchFinder::MatchResult &Match, 77 std::string *Result) const override { 78 Result->append(Text); 79 return Error::success(); 80 } 81 }; 82 83 // A debugging operation to dump the AST for a particular (bound) AST node. 84 class DebugPrintNodeStencil : public StencilInterface { 85 std::string Id; 86 87 public: 88 explicit DebugPrintNodeStencil(std::string S) : Id(std::move(S)) {} 89 90 std::string toString() const override { 91 return (llvm::Twine("dPrint(\"") + Id + "\")").str(); 92 } 93 94 Error eval(const MatchFinder::MatchResult &Match, 95 std::string *Result) const override { 96 return printNode(Id, Match, Result); 97 } 98 }; 99 100 // Operators that take a single node Id as an argument. 101 enum class UnaryNodeOperator { 102 Parens, 103 Deref, 104 MaybeDeref, 105 AddressOf, 106 MaybeAddressOf, 107 Describe, 108 }; 109 110 // Generic container for stencil operations with a (single) node-id argument. 111 class UnaryOperationStencil : public StencilInterface { 112 UnaryNodeOperator Op; 113 std::string Id; 114 115 public: 116 UnaryOperationStencil(UnaryNodeOperator Op, std::string Id) 117 : Op(Op), Id(std::move(Id)) {} 118 119 std::string toString() const override { 120 StringRef OpName; 121 switch (Op) { 122 case UnaryNodeOperator::Parens: 123 OpName = "expression"; 124 break; 125 case UnaryNodeOperator::Deref: 126 OpName = "deref"; 127 break; 128 case UnaryNodeOperator::MaybeDeref: 129 OpName = "maybeDeref"; 130 break; 131 case UnaryNodeOperator::AddressOf: 132 OpName = "addressOf"; 133 break; 134 case UnaryNodeOperator::MaybeAddressOf: 135 OpName = "maybeAddressOf"; 136 break; 137 case UnaryNodeOperator::Describe: 138 OpName = "describe"; 139 break; 140 } 141 return (OpName + "(\"" + Id + "\")").str(); 142 } 143 144 Error eval(const MatchFinder::MatchResult &Match, 145 std::string *Result) const override { 146 // The `Describe` operation can be applied to any node, not just 147 // expressions, so it is handled here, separately. 148 if (Op == UnaryNodeOperator::Describe) 149 return printNode(Id, Match, Result); 150 151 const auto *E = Match.Nodes.getNodeAs<Expr>(Id); 152 if (E == nullptr) 153 return llvm::make_error<StringError>(errc::invalid_argument, 154 "Id not bound or not Expr: " + Id); 155 std::optional<std::string> Source; 156 switch (Op) { 157 case UnaryNodeOperator::Parens: 158 Source = tooling::buildParens(*E, *Match.Context); 159 break; 160 case UnaryNodeOperator::Deref: 161 Source = tooling::buildDereference(*E, *Match.Context); 162 break; 163 case UnaryNodeOperator::MaybeDeref: 164 if (E->getType()->isAnyPointerType() || 165 tooling::isKnownPointerLikeType(E->getType(), *Match.Context)) { 166 // Strip off any operator->. This can only occur inside an actual arrow 167 // member access, so we treat it as equivalent to an actual object 168 // expression. 169 if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) { 170 if (OpCall->getOperator() == clang::OO_Arrow && 171 OpCall->getNumArgs() == 1) { 172 E = OpCall->getArg(0); 173 } 174 } 175 Source = tooling::buildDereference(*E, *Match.Context); 176 break; 177 } 178 *Result += tooling::getText(*E, *Match.Context); 179 return Error::success(); 180 case UnaryNodeOperator::AddressOf: 181 Source = tooling::buildAddressOf(*E, *Match.Context); 182 break; 183 case UnaryNodeOperator::MaybeAddressOf: 184 if (E->getType()->isAnyPointerType() || 185 tooling::isKnownPointerLikeType(E->getType(), *Match.Context)) { 186 // Strip off any operator->. This can only occur inside an actual arrow 187 // member access, so we treat it as equivalent to an actual object 188 // expression. 189 if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) { 190 if (OpCall->getOperator() == clang::OO_Arrow && 191 OpCall->getNumArgs() == 1) { 192 E = OpCall->getArg(0); 193 } 194 } 195 *Result += tooling::getText(*E, *Match.Context); 196 return Error::success(); 197 } 198 Source = tooling::buildAddressOf(*E, *Match.Context); 199 break; 200 case UnaryNodeOperator::Describe: 201 llvm_unreachable("This case is handled at the start of the function"); 202 } 203 if (!Source) 204 return llvm::make_error<StringError>( 205 errc::invalid_argument, 206 "Could not construct expression source from ID: " + Id); 207 *Result += *Source; 208 return Error::success(); 209 } 210 }; 211 212 // The fragment of code corresponding to the selected range. 213 class SelectorStencil : public StencilInterface { 214 RangeSelector Selector; 215 216 public: 217 explicit SelectorStencil(RangeSelector S) : Selector(std::move(S)) {} 218 219 std::string toString() const override { return "selection(...)"; } 220 221 Error eval(const MatchFinder::MatchResult &Match, 222 std::string *Result) const override { 223 auto RawRange = Selector(Match); 224 if (!RawRange) 225 return RawRange.takeError(); 226 CharSourceRange Range = Lexer::makeFileCharRange( 227 *RawRange, *Match.SourceManager, Match.Context->getLangOpts()); 228 if (Range.isInvalid()) { 229 // Validate the original range to attempt to get a meaningful error 230 // message. If it's valid, then something else is the cause and we just 231 // return the generic failure message. 232 if (auto Err = tooling::validateRange(*RawRange, *Match.SourceManager, 233 /*AllowSystemHeaders=*/true)) 234 return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) { 235 assert(E->convertToErrorCode() == 236 llvm::make_error_code(errc::invalid_argument) && 237 "Validation errors must carry the invalid_argument code"); 238 return llvm::createStringError( 239 errc::invalid_argument, 240 "selected range could not be resolved to a valid source range; " + 241 E->getMessage()); 242 }); 243 return llvm::createStringError( 244 errc::invalid_argument, 245 "selected range could not be resolved to a valid source range"); 246 } 247 // Validate `Range`, because `makeFileCharRange` accepts some ranges that 248 // `validateRange` rejects. 249 if (auto Err = tooling::validateRange(Range, *Match.SourceManager, 250 /*AllowSystemHeaders=*/true)) 251 return joinErrors( 252 llvm::createStringError(errc::invalid_argument, 253 "selected range is not valid for editing"), 254 std::move(Err)); 255 *Result += tooling::getText(Range, *Match.Context); 256 return Error::success(); 257 } 258 }; 259 260 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate. 261 class AccessStencil : public StencilInterface { 262 std::string BaseId; 263 Stencil Member; 264 265 public: 266 AccessStencil(StringRef BaseId, Stencil Member) 267 : BaseId(std::string(BaseId)), Member(std::move(Member)) {} 268 269 std::string toString() const override { 270 return (llvm::Twine("access(\"") + BaseId + "\", " + Member->toString() + 271 ")") 272 .str(); 273 } 274 275 Error eval(const MatchFinder::MatchResult &Match, 276 std::string *Result) const override { 277 const auto *E = Match.Nodes.getNodeAs<Expr>(BaseId); 278 if (E == nullptr) 279 return llvm::make_error<StringError>(errc::invalid_argument, 280 "Id not bound: " + BaseId); 281 std::optional<std::string> S = tooling::buildAccess(*E, *Match.Context); 282 if (!S) 283 return llvm::make_error<StringError>( 284 errc::invalid_argument, 285 "Could not construct object text from ID: " + BaseId); 286 *Result += *S; 287 return Member->eval(Match, Result); 288 } 289 }; 290 291 class IfBoundStencil : public StencilInterface { 292 std::string Id; 293 Stencil TrueStencil; 294 Stencil FalseStencil; 295 296 public: 297 IfBoundStencil(StringRef Id, Stencil TrueStencil, Stencil FalseStencil) 298 : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)), 299 FalseStencil(std::move(FalseStencil)) {} 300 301 std::string toString() const override { 302 return (llvm::Twine("ifBound(\"") + Id + "\", " + TrueStencil->toString() + 303 ", " + FalseStencil->toString() + ")") 304 .str(); 305 } 306 307 Error eval(const MatchFinder::MatchResult &Match, 308 std::string *Result) const override { 309 auto &M = Match.Nodes.getMap(); 310 return (M.find(Id) != M.end() ? TrueStencil : FalseStencil) 311 ->eval(Match, Result); 312 } 313 }; 314 315 class SelectBoundStencil : public clang::transformer::StencilInterface { 316 static bool containsNoNullStencils( 317 const std::vector<std::pair<std::string, Stencil>> &Cases) { 318 for (const auto &S : Cases) 319 if (S.second == nullptr) 320 return false; 321 return true; 322 } 323 324 public: 325 SelectBoundStencil(std::vector<std::pair<std::string, Stencil>> Cases, 326 Stencil Default) 327 : CaseStencils(std::move(Cases)), DefaultStencil(std::move(Default)) { 328 assert(containsNoNullStencils(CaseStencils) && 329 "cases of selectBound may not be null"); 330 } 331 ~SelectBoundStencil() override {} 332 333 llvm::Error eval(const MatchFinder::MatchResult &match, 334 std::string *result) const override { 335 const BoundNodes::IDToNodeMap &NodeMap = match.Nodes.getMap(); 336 for (const auto &S : CaseStencils) { 337 if (NodeMap.count(S.first) > 0) { 338 return S.second->eval(match, result); 339 } 340 } 341 342 if (DefaultStencil != nullptr) { 343 return DefaultStencil->eval(match, result); 344 } 345 346 llvm::SmallVector<llvm::StringRef, 2> CaseIDs; 347 CaseIDs.reserve(CaseStencils.size()); 348 for (const auto &S : CaseStencils) 349 CaseIDs.emplace_back(S.first); 350 351 return llvm::createStringError( 352 errc::result_out_of_range, 353 llvm::Twine("selectBound failed: no cases bound and no default: {") + 354 llvm::join(CaseIDs, ", ") + "}"); 355 } 356 357 std::string toString() const override { 358 std::string Buffer; 359 llvm::raw_string_ostream Stream(Buffer); 360 Stream << "selectBound({"; 361 bool First = true; 362 for (const auto &S : CaseStencils) { 363 if (First) 364 First = false; 365 else 366 Stream << "}, "; 367 Stream << "{\"" << S.first << "\", " << S.second->toString(); 368 } 369 Stream << "}}"; 370 if (DefaultStencil != nullptr) { 371 Stream << ", " << DefaultStencil->toString(); 372 } 373 Stream << ")"; 374 return Buffer; 375 } 376 377 private: 378 std::vector<std::pair<std::string, Stencil>> CaseStencils; 379 Stencil DefaultStencil; 380 }; 381 382 class SequenceStencil : public StencilInterface { 383 std::vector<Stencil> Stencils; 384 385 public: 386 SequenceStencil(std::vector<Stencil> Stencils) 387 : Stencils(std::move(Stencils)) {} 388 389 std::string toString() const override { 390 llvm::SmallVector<std::string, 2> Parts; 391 Parts.reserve(Stencils.size()); 392 for (const auto &S : Stencils) 393 Parts.push_back(S->toString()); 394 return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str(); 395 } 396 397 Error eval(const MatchFinder::MatchResult &Match, 398 std::string *Result) const override { 399 for (const auto &S : Stencils) 400 if (auto Err = S->eval(Match, Result)) 401 return Err; 402 return Error::success(); 403 } 404 }; 405 406 class RunStencil : public StencilInterface { 407 MatchConsumer<std::string> Consumer; 408 409 public: 410 explicit RunStencil(MatchConsumer<std::string> C) : Consumer(std::move(C)) {} 411 412 std::string toString() const override { return "run(...)"; } 413 414 Error eval(const MatchFinder::MatchResult &Match, 415 std::string *Result) const override { 416 417 Expected<std::string> Value = Consumer(Match); 418 if (!Value) 419 return Value.takeError(); 420 *Result += *Value; 421 return Error::success(); 422 } 423 }; 424 } // namespace 425 426 Stencil transformer::detail::makeStencil(StringRef Text) { 427 return std::make_shared<RawTextStencil>(std::string(Text)); 428 } 429 430 Stencil transformer::detail::makeStencil(RangeSelector Selector) { 431 return std::make_shared<SelectorStencil>(std::move(Selector)); 432 } 433 434 Stencil transformer::dPrint(StringRef Id) { 435 return std::make_shared<DebugPrintNodeStencil>(std::string(Id)); 436 } 437 438 Stencil transformer::expression(llvm::StringRef Id) { 439 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Parens, 440 std::string(Id)); 441 } 442 443 Stencil transformer::deref(llvm::StringRef ExprId) { 444 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Deref, 445 std::string(ExprId)); 446 } 447 448 Stencil transformer::maybeDeref(llvm::StringRef ExprId) { 449 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::MaybeDeref, 450 std::string(ExprId)); 451 } 452 453 Stencil transformer::addressOf(llvm::StringRef ExprId) { 454 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::AddressOf, 455 std::string(ExprId)); 456 } 457 458 Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) { 459 return std::make_shared<UnaryOperationStencil>( 460 UnaryNodeOperator::MaybeAddressOf, std::string(ExprId)); 461 } 462 463 Stencil transformer::describe(StringRef Id) { 464 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Describe, 465 std::string(Id)); 466 } 467 468 Stencil transformer::access(StringRef BaseId, Stencil Member) { 469 return std::make_shared<AccessStencil>(BaseId, std::move(Member)); 470 } 471 472 Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil, 473 Stencil FalseStencil) { 474 return std::make_shared<IfBoundStencil>(Id, std::move(TrueStencil), 475 std::move(FalseStencil)); 476 } 477 478 Stencil transformer::selectBound( 479 std::vector<std::pair<std::string, Stencil>> CaseStencils, 480 Stencil DefaultStencil) { 481 return std::make_shared<SelectBoundStencil>(std::move(CaseStencils), 482 std::move(DefaultStencil)); 483 } 484 485 Stencil transformer::run(MatchConsumer<std::string> Fn) { 486 return std::make_shared<RunStencil>(std::move(Fn)); 487 } 488 489 Stencil transformer::catVector(std::vector<Stencil> Parts) { 490 // Only one argument, so don't wrap in sequence. 491 if (Parts.size() == 1) 492 return std::move(Parts[0]); 493 return std::make_shared<SequenceStencil>(std::move(Parts)); 494 } 495