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::MatchFinder; 31 using llvm::errc; 32 using llvm::Error; 33 using llvm::Expected; 34 using llvm::StringError; 35 36 static llvm::Expected<DynTypedNode> 37 getNode(const ast_matchers::BoundNodes &Nodes, 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 namespace { 47 // An arbitrary fragment of code within a stencil. 48 struct RawTextData { 49 explicit RawTextData(std::string T) : Text(std::move(T)) {} 50 std::string Text; 51 }; 52 53 // A debugging operation to dump the AST for a particular (bound) AST node. 54 struct DebugPrintNodeData { 55 explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {} 56 std::string Id; 57 }; 58 59 // Operators that take a single node Id as an argument. 60 enum class UnaryNodeOperator { 61 Parens, 62 Deref, 63 MaybeDeref, 64 AddressOf, 65 MaybeAddressOf, 66 Describe, 67 }; 68 69 // Generic container for stencil operations with a (single) node-id argument. 70 struct UnaryOperationData { 71 UnaryOperationData(UnaryNodeOperator Op, std::string Id) 72 : Op(Op), Id(std::move(Id)) {} 73 UnaryNodeOperator Op; 74 std::string Id; 75 }; 76 77 // The fragment of code corresponding to the selected range. 78 struct SelectorData { 79 explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {} 80 RangeSelector Selector; 81 }; 82 83 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate. 84 struct AccessData { 85 AccessData(StringRef BaseId, Stencil Member) 86 : BaseId(std::string(BaseId)), Member(std::move(Member)) {} 87 std::string BaseId; 88 Stencil Member; 89 }; 90 91 struct IfBoundData { 92 IfBoundData(StringRef Id, Stencil TrueStencil, Stencil FalseStencil) 93 : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)), 94 FalseStencil(std::move(FalseStencil)) {} 95 std::string Id; 96 Stencil TrueStencil; 97 Stencil FalseStencil; 98 }; 99 100 struct SequenceData { 101 SequenceData(std::vector<Stencil> Stencils) : Stencils(std::move(Stencils)) {} 102 std::vector<Stencil> Stencils; 103 }; 104 105 std::string toStringData(const RawTextData &Data) { 106 std::string Result; 107 llvm::raw_string_ostream OS(Result); 108 OS << "\""; 109 OS.write_escaped(Data.Text); 110 OS << "\""; 111 OS.flush(); 112 return Result; 113 } 114 115 std::string toStringData(const DebugPrintNodeData &Data) { 116 return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str(); 117 } 118 119 std::string toStringData(const UnaryOperationData &Data) { 120 StringRef OpName; 121 switch (Data.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 + "(\"" + Data.Id + "\")").str(); 142 } 143 144 std::string toStringData(const SelectorData &) { return "selection(...)"; } 145 146 std::string toStringData(const AccessData &Data) { 147 return (llvm::Twine("access(\"") + Data.BaseId + "\", " + 148 Data.Member->toString() + ")") 149 .str(); 150 } 151 152 std::string toStringData(const IfBoundData &Data) { 153 return (llvm::Twine("ifBound(\"") + Data.Id + "\", " + 154 Data.TrueStencil->toString() + ", " + Data.FalseStencil->toString() + 155 ")") 156 .str(); 157 } 158 159 std::string toStringData(const MatchConsumer<std::string> &) { 160 return "run(...)"; 161 } 162 163 std::string toStringData(const SequenceData &Data) { 164 llvm::SmallVector<std::string, 2> Parts; 165 Parts.reserve(Data.Stencils.size()); 166 for (const auto &S : Data.Stencils) 167 Parts.push_back(S->toString()); 168 return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str(); 169 } 170 171 // The `evalData()` overloads evaluate the given stencil data to a string, given 172 // the match result, and append it to `Result`. We define an overload for each 173 // type of stencil data. 174 175 Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &, 176 std::string *Result) { 177 Result->append(Data.Text); 178 return Error::success(); 179 } 180 181 static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match, 182 std::string *Result) { 183 std::string Output; 184 llvm::raw_string_ostream Os(Output); 185 auto NodeOrErr = getNode(Match.Nodes, Id); 186 if (auto Err = NodeOrErr.takeError()) 187 return Err; 188 NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts())); 189 *Result += Os.str(); 190 return Error::success(); 191 } 192 193 Error evalData(const DebugPrintNodeData &Data, 194 const MatchFinder::MatchResult &Match, std::string *Result) { 195 return printNode(Data.Id, Match, Result); 196 } 197 198 // FIXME: Consider memoizing this function using the `ASTContext`. 199 static bool isSmartPointerType(QualType Ty, ASTContext &Context) { 200 using namespace ::clang::ast_matchers; 201 202 // Optimization: hard-code common smart-pointer types. This can/should be 203 // removed if we start caching the results of this function. 204 auto KnownSmartPointer = 205 cxxRecordDecl(hasAnyName("::std::unique_ptr", "::std::shared_ptr")); 206 const auto QuacksLikeASmartPointer = cxxRecordDecl( 207 hasMethod(cxxMethodDecl(hasOverloadedOperatorName("->"), 208 returns(qualType(pointsTo(type()))))), 209 hasMethod(cxxMethodDecl(hasOverloadedOperatorName("*"), 210 returns(qualType(references(type())))))); 211 const auto SmartPointer = qualType(hasDeclaration( 212 cxxRecordDecl(anyOf(KnownSmartPointer, QuacksLikeASmartPointer)))); 213 return match(SmartPointer, Ty, Context).size() > 0; 214 } 215 216 Error evalData(const UnaryOperationData &Data, 217 const MatchFinder::MatchResult &Match, std::string *Result) { 218 // The `Describe` operation can be applied to any node, not just expressions, 219 // so it is handled here, separately. 220 if (Data.Op == UnaryNodeOperator::Describe) 221 return printNode(Data.Id, Match, Result); 222 223 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id); 224 if (E == nullptr) 225 return llvm::make_error<StringError>( 226 errc::invalid_argument, "Id not bound or not Expr: " + Data.Id); 227 llvm::Optional<std::string> Source; 228 switch (Data.Op) { 229 case UnaryNodeOperator::Parens: 230 Source = tooling::buildParens(*E, *Match.Context); 231 break; 232 case UnaryNodeOperator::Deref: 233 Source = tooling::buildDereference(*E, *Match.Context); 234 break; 235 case UnaryNodeOperator::MaybeDeref: 236 if (E->getType()->isAnyPointerType() || 237 isSmartPointerType(E->getType(), *Match.Context)) { 238 // Strip off any operator->. This can only occur inside an actual arrow 239 // member access, so we treat it as equivalent to an actual object 240 // expression. 241 if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) { 242 if (OpCall->getOperator() == clang::OO_Arrow && 243 OpCall->getNumArgs() == 1) { 244 E = OpCall->getArg(0); 245 } 246 } 247 Source = tooling::buildDereference(*E, *Match.Context); 248 break; 249 } 250 *Result += tooling::getText(*E, *Match.Context); 251 return Error::success(); 252 case UnaryNodeOperator::AddressOf: 253 Source = tooling::buildAddressOf(*E, *Match.Context); 254 break; 255 case UnaryNodeOperator::MaybeAddressOf: 256 if (E->getType()->isAnyPointerType() || 257 isSmartPointerType(E->getType(), *Match.Context)) { 258 // Strip off any operator->. This can only occur inside an actual arrow 259 // member access, so we treat it as equivalent to an actual object 260 // expression. 261 if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) { 262 if (OpCall->getOperator() == clang::OO_Arrow && 263 OpCall->getNumArgs() == 1) { 264 E = OpCall->getArg(0); 265 } 266 } 267 *Result += tooling::getText(*E, *Match.Context); 268 return Error::success(); 269 } 270 Source = tooling::buildAddressOf(*E, *Match.Context); 271 break; 272 case UnaryNodeOperator::Describe: 273 llvm_unreachable("This case is handled at the start of the function"); 274 } 275 if (!Source) 276 return llvm::make_error<StringError>( 277 errc::invalid_argument, 278 "Could not construct expression source from ID: " + Data.Id); 279 *Result += *Source; 280 return Error::success(); 281 } 282 283 Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match, 284 std::string *Result) { 285 auto RawRange = Data.Selector(Match); 286 if (!RawRange) 287 return RawRange.takeError(); 288 CharSourceRange Range = Lexer::makeFileCharRange( 289 *RawRange, *Match.SourceManager, Match.Context->getLangOpts()); 290 if (Range.isInvalid()) { 291 // Validate the original range to attempt to get a meaningful error message. 292 // If it's valid, then something else is the cause and we just return the 293 // generic failure message. 294 if (auto Err = tooling::validateEditRange(*RawRange, *Match.SourceManager)) 295 return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) { 296 assert(E->convertToErrorCode() == 297 llvm::make_error_code(errc::invalid_argument) && 298 "Validation errors must carry the invalid_argument code"); 299 return llvm::createStringError( 300 errc::invalid_argument, 301 "selected range could not be resolved to a valid source range; " + 302 E->getMessage()); 303 }); 304 return llvm::createStringError( 305 errc::invalid_argument, 306 "selected range could not be resolved to a valid source range"); 307 } 308 // Validate `Range`, because `makeFileCharRange` accepts some ranges that 309 // `validateEditRange` rejects. 310 if (auto Err = tooling::validateEditRange(Range, *Match.SourceManager)) 311 return joinErrors( 312 llvm::createStringError(errc::invalid_argument, 313 "selected range is not valid for editing"), 314 std::move(Err)); 315 *Result += tooling::getText(Range, *Match.Context); 316 return Error::success(); 317 } 318 319 Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match, 320 std::string *Result) { 321 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId); 322 if (E == nullptr) 323 return llvm::make_error<StringError>(errc::invalid_argument, 324 "Id not bound: " + Data.BaseId); 325 if (!E->isImplicitCXXThis()) { 326 if (llvm::Optional<std::string> S = 327 E->getType()->isAnyPointerType() 328 ? tooling::buildArrow(*E, *Match.Context) 329 : tooling::buildDot(*E, *Match.Context)) 330 *Result += *S; 331 else 332 return llvm::make_error<StringError>( 333 errc::invalid_argument, 334 "Could not construct object text from ID: " + Data.BaseId); 335 } 336 return Data.Member->eval(Match, Result); 337 } 338 339 Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, 340 std::string *Result) { 341 auto &M = Match.Nodes.getMap(); 342 return (M.find(Data.Id) != M.end() ? Data.TrueStencil : Data.FalseStencil) 343 ->eval(Match, Result); 344 } 345 346 Error evalData(const MatchConsumer<std::string> &Fn, 347 const MatchFinder::MatchResult &Match, std::string *Result) { 348 Expected<std::string> Value = Fn(Match); 349 if (!Value) 350 return Value.takeError(); 351 *Result += *Value; 352 return Error::success(); 353 } 354 355 Error evalData(const SequenceData &Data, const MatchFinder::MatchResult &Match, 356 std::string *Result) { 357 for (const auto &S : Data.Stencils) 358 if (auto Err = S->eval(Match, Result)) 359 return Err; 360 return Error::success(); 361 } 362 363 template <typename T> class StencilImpl : public StencilInterface { 364 T Data; 365 366 public: 367 template <typename... Ps> 368 explicit StencilImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {} 369 370 Error eval(const MatchFinder::MatchResult &Match, 371 std::string *Result) const override { 372 return evalData(Data, Match, Result); 373 } 374 375 std::string toString() const override { return toStringData(Data); } 376 }; 377 } // namespace 378 379 Stencil transformer::detail::makeStencil(StringRef Text) { 380 return std::make_shared<StencilImpl<RawTextData>>(std::string(Text)); 381 } 382 383 Stencil transformer::detail::makeStencil(RangeSelector Selector) { 384 return std::make_shared<StencilImpl<SelectorData>>(std::move(Selector)); 385 } 386 387 Stencil transformer::dPrint(StringRef Id) { 388 return std::make_shared<StencilImpl<DebugPrintNodeData>>(std::string(Id)); 389 } 390 391 Stencil transformer::expression(llvm::StringRef Id) { 392 return std::make_shared<StencilImpl<UnaryOperationData>>( 393 UnaryNodeOperator::Parens, std::string(Id)); 394 } 395 396 Stencil transformer::deref(llvm::StringRef ExprId) { 397 return std::make_shared<StencilImpl<UnaryOperationData>>( 398 UnaryNodeOperator::Deref, std::string(ExprId)); 399 } 400 401 Stencil transformer::maybeDeref(llvm::StringRef ExprId) { 402 return std::make_shared<StencilImpl<UnaryOperationData>>( 403 UnaryNodeOperator::MaybeDeref, std::string(ExprId)); 404 } 405 406 Stencil transformer::addressOf(llvm::StringRef ExprId) { 407 return std::make_shared<StencilImpl<UnaryOperationData>>( 408 UnaryNodeOperator::AddressOf, std::string(ExprId)); 409 } 410 411 Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) { 412 return std::make_shared<StencilImpl<UnaryOperationData>>( 413 UnaryNodeOperator::MaybeAddressOf, std::string(ExprId)); 414 } 415 416 Stencil transformer::describe(StringRef Id) { 417 return std::make_shared<StencilImpl<UnaryOperationData>>( 418 UnaryNodeOperator::Describe, std::string(Id)); 419 } 420 421 Stencil transformer::access(StringRef BaseId, Stencil Member) { 422 return std::make_shared<StencilImpl<AccessData>>(BaseId, std::move(Member)); 423 } 424 425 Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil, 426 Stencil FalseStencil) { 427 return std::make_shared<StencilImpl<IfBoundData>>(Id, std::move(TrueStencil), 428 std::move(FalseStencil)); 429 } 430 431 Stencil transformer::run(MatchConsumer<std::string> Fn) { 432 return std::make_shared<StencilImpl<MatchConsumer<std::string>>>( 433 std::move(Fn)); 434 } 435 436 Stencil transformer::catVector(std::vector<Stencil> Parts) { 437 // Only one argument, so don't wrap in sequence. 438 if (Parts.size() == 1) 439 return std::move(Parts[0]); 440 return std::make_shared<StencilImpl<SequenceData>>(std::move(Parts)); 441 } 442