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