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