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