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 Error evalData(const UnaryOperationData &Data, 199 const MatchFinder::MatchResult &Match, std::string *Result) { 200 // The `Describe` operation can be applied to any node, not just expressions, 201 // so it is handled here, separately. 202 if (Data.Op == UnaryNodeOperator::Describe) 203 return printNode(Data.Id, Match, Result); 204 205 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id); 206 if (E == nullptr) 207 return llvm::make_error<StringError>( 208 errc::invalid_argument, "Id not bound or not Expr: " + Data.Id); 209 llvm::Optional<std::string> Source; 210 switch (Data.Op) { 211 case UnaryNodeOperator::Parens: 212 Source = tooling::buildParens(*E, *Match.Context); 213 break; 214 case UnaryNodeOperator::Deref: 215 Source = tooling::buildDereference(*E, *Match.Context); 216 break; 217 case UnaryNodeOperator::MaybeDeref: 218 if (!E->getType()->isAnyPointerType()) { 219 *Result += tooling::getText(*E, *Match.Context); 220 return Error::success(); 221 } 222 Source = tooling::buildDereference(*E, *Match.Context); 223 break; 224 case UnaryNodeOperator::AddressOf: 225 Source = tooling::buildAddressOf(*E, *Match.Context); 226 break; 227 case UnaryNodeOperator::MaybeAddressOf: 228 if (E->getType()->isAnyPointerType()) { 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: " + Data.Id); 241 *Result += *Source; 242 return Error::success(); 243 } 244 245 Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match, 246 std::string *Result) { 247 auto RawRange = Data.Selector(Match); 248 if (!RawRange) 249 return RawRange.takeError(); 250 CharSourceRange Range = Lexer::makeFileCharRange( 251 *RawRange, *Match.SourceManager, Match.Context->getLangOpts()); 252 if (Range.isInvalid()) { 253 // Validate the original range to attempt to get a meaningful error message. 254 // If it's valid, then something else is the cause and we just return the 255 // generic failure message. 256 if (auto Err = tooling::validateEditRange(*RawRange, *Match.SourceManager)) 257 return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) { 258 assert(E->convertToErrorCode() == 259 llvm::make_error_code(errc::invalid_argument) && 260 "Validation errors must carry the invalid_argument code"); 261 return llvm::createStringError( 262 errc::invalid_argument, 263 "selected range could not be resolved to a valid source range; " + 264 E->getMessage()); 265 }); 266 return llvm::createStringError( 267 errc::invalid_argument, 268 "selected range could not be resolved to a valid source range"); 269 } 270 // Validate `Range`, because `makeFileCharRange` accepts some ranges that 271 // `validateEditRange` rejects. 272 if (auto Err = tooling::validateEditRange(Range, *Match.SourceManager)) 273 return joinErrors( 274 llvm::createStringError(errc::invalid_argument, 275 "selected range is not valid for editing"), 276 std::move(Err)); 277 *Result += tooling::getText(Range, *Match.Context); 278 return Error::success(); 279 } 280 281 Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match, 282 std::string *Result) { 283 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId); 284 if (E == nullptr) 285 return llvm::make_error<StringError>(errc::invalid_argument, 286 "Id not bound: " + Data.BaseId); 287 if (!E->isImplicitCXXThis()) { 288 if (llvm::Optional<std::string> S = 289 E->getType()->isAnyPointerType() 290 ? tooling::buildArrow(*E, *Match.Context) 291 : tooling::buildDot(*E, *Match.Context)) 292 *Result += *S; 293 else 294 return llvm::make_error<StringError>( 295 errc::invalid_argument, 296 "Could not construct object text from ID: " + Data.BaseId); 297 } 298 return Data.Member->eval(Match, Result); 299 } 300 301 Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, 302 std::string *Result) { 303 auto &M = Match.Nodes.getMap(); 304 return (M.find(Data.Id) != M.end() ? Data.TrueStencil : Data.FalseStencil) 305 ->eval(Match, Result); 306 } 307 308 Error evalData(const MatchConsumer<std::string> &Fn, 309 const MatchFinder::MatchResult &Match, std::string *Result) { 310 Expected<std::string> Value = Fn(Match); 311 if (!Value) 312 return Value.takeError(); 313 *Result += *Value; 314 return Error::success(); 315 } 316 317 Error evalData(const SequenceData &Data, const MatchFinder::MatchResult &Match, 318 std::string *Result) { 319 for (const auto &S : Data.Stencils) 320 if (auto Err = S->eval(Match, Result)) 321 return Err; 322 return Error::success(); 323 } 324 325 template <typename T> class StencilImpl : public StencilInterface { 326 T Data; 327 328 public: 329 template <typename... Ps> 330 explicit StencilImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {} 331 332 Error eval(const MatchFinder::MatchResult &Match, 333 std::string *Result) const override { 334 return evalData(Data, Match, Result); 335 } 336 337 std::string toString() const override { return toStringData(Data); } 338 }; 339 } // namespace 340 341 Stencil transformer::detail::makeStencil(StringRef Text) { 342 return std::make_shared<StencilImpl<RawTextData>>(std::string(Text)); 343 } 344 345 Stencil transformer::detail::makeStencil(RangeSelector Selector) { 346 return std::make_shared<StencilImpl<SelectorData>>(std::move(Selector)); 347 } 348 349 Stencil transformer::dPrint(StringRef Id) { 350 return std::make_shared<StencilImpl<DebugPrintNodeData>>(std::string(Id)); 351 } 352 353 Stencil transformer::expression(llvm::StringRef Id) { 354 return std::make_shared<StencilImpl<UnaryOperationData>>( 355 UnaryNodeOperator::Parens, std::string(Id)); 356 } 357 358 Stencil transformer::deref(llvm::StringRef ExprId) { 359 return std::make_shared<StencilImpl<UnaryOperationData>>( 360 UnaryNodeOperator::Deref, std::string(ExprId)); 361 } 362 363 Stencil transformer::maybeDeref(llvm::StringRef ExprId) { 364 return std::make_shared<StencilImpl<UnaryOperationData>>( 365 UnaryNodeOperator::MaybeDeref, std::string(ExprId)); 366 } 367 368 Stencil transformer::addressOf(llvm::StringRef ExprId) { 369 return std::make_shared<StencilImpl<UnaryOperationData>>( 370 UnaryNodeOperator::AddressOf, std::string(ExprId)); 371 } 372 373 Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) { 374 return std::make_shared<StencilImpl<UnaryOperationData>>( 375 UnaryNodeOperator::MaybeAddressOf, std::string(ExprId)); 376 } 377 378 Stencil transformer::describe(StringRef Id) { 379 return std::make_shared<StencilImpl<UnaryOperationData>>( 380 UnaryNodeOperator::Describe, std::string(Id)); 381 } 382 383 Stencil transformer::access(StringRef BaseId, Stencil Member) { 384 return std::make_shared<StencilImpl<AccessData>>(BaseId, std::move(Member)); 385 } 386 387 Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil, 388 Stencil FalseStencil) { 389 return std::make_shared<StencilImpl<IfBoundData>>(Id, std::move(TrueStencil), 390 std::move(FalseStencil)); 391 } 392 393 Stencil transformer::run(MatchConsumer<std::string> Fn) { 394 return std::make_shared<StencilImpl<MatchConsumer<std::string>>>( 395 std::move(Fn)); 396 } 397 398 Stencil transformer::catVector(std::vector<Stencil> Parts) { 399 // Only one argument, so don't wrap in sequence. 400 if (Parts.size() == 1) 401 return std::move(Parts[0]); 402 return std::make_shared<StencilImpl<SequenceData>>(std::move(Parts)); 403 } 404