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 }; 67 68 // Generic container for stencil operations with a (single) node-id argument. 69 struct UnaryOperationData { 70 UnaryOperationData(UnaryNodeOperator Op, std::string Id) 71 : Op(Op), Id(std::move(Id)) {} 72 UnaryNodeOperator Op; 73 std::string Id; 74 }; 75 76 // The fragment of code corresponding to the selected range. 77 struct SelectorData { 78 explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {} 79 RangeSelector Selector; 80 }; 81 82 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate. 83 struct AccessData { 84 AccessData(StringRef BaseId, Stencil Member) 85 : BaseId(std::string(BaseId)), Member(std::move(Member)) {} 86 std::string BaseId; 87 Stencil Member; 88 }; 89 90 struct IfBoundData { 91 IfBoundData(StringRef Id, Stencil TrueStencil, Stencil FalseStencil) 92 : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)), 93 FalseStencil(std::move(FalseStencil)) {} 94 std::string Id; 95 Stencil TrueStencil; 96 Stencil FalseStencil; 97 }; 98 99 struct SequenceData { 100 SequenceData(std::vector<Stencil> Stencils) : Stencils(std::move(Stencils)) {} 101 std::vector<Stencil> Stencils; 102 }; 103 104 std::string toStringData(const RawTextData &Data) { 105 std::string Result; 106 llvm::raw_string_ostream OS(Result); 107 OS << "\""; 108 OS.write_escaped(Data.Text); 109 OS << "\""; 110 OS.flush(); 111 return Result; 112 } 113 114 std::string toStringData(const DebugPrintNodeData &Data) { 115 return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str(); 116 } 117 118 std::string toStringData(const UnaryOperationData &Data) { 119 StringRef OpName; 120 switch (Data.Op) { 121 case UnaryNodeOperator::Parens: 122 OpName = "expression"; 123 break; 124 case UnaryNodeOperator::Deref: 125 OpName = "deref"; 126 break; 127 case UnaryNodeOperator::MaybeDeref: 128 OpName = "maybeDeref"; 129 break; 130 case UnaryNodeOperator::AddressOf: 131 OpName = "addressOf"; 132 break; 133 case UnaryNodeOperator::MaybeAddressOf: 134 OpName = "maybeAddressOf"; 135 break; 136 } 137 return (OpName + "(\"" + Data.Id + "\")").str(); 138 } 139 140 std::string toStringData(const SelectorData &) { return "selection(...)"; } 141 142 std::string toStringData(const AccessData &Data) { 143 return (llvm::Twine("access(\"") + Data.BaseId + "\", " + 144 Data.Member->toString() + ")") 145 .str(); 146 } 147 148 std::string toStringData(const IfBoundData &Data) { 149 return (llvm::Twine("ifBound(\"") + Data.Id + "\", " + 150 Data.TrueStencil->toString() + ", " + Data.FalseStencil->toString() + 151 ")") 152 .str(); 153 } 154 155 std::string toStringData(const MatchConsumer<std::string> &) { 156 return "run(...)"; 157 } 158 159 std::string toStringData(const SequenceData &Data) { 160 llvm::SmallVector<std::string, 2> Parts; 161 Parts.reserve(Data.Stencils.size()); 162 for (const auto &S : Data.Stencils) 163 Parts.push_back(S->toString()); 164 return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str(); 165 } 166 167 // The `evalData()` overloads evaluate the given stencil data to a string, given 168 // the match result, and append it to `Result`. We define an overload for each 169 // type of stencil data. 170 171 Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &, 172 std::string *Result) { 173 Result->append(Data.Text); 174 return Error::success(); 175 } 176 177 Error evalData(const DebugPrintNodeData &Data, 178 const MatchFinder::MatchResult &Match, std::string *Result) { 179 std::string Output; 180 llvm::raw_string_ostream Os(Output); 181 auto NodeOrErr = getNode(Match.Nodes, Data.Id); 182 if (auto Err = NodeOrErr.takeError()) 183 return Err; 184 NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts())); 185 *Result += Os.str(); 186 return Error::success(); 187 } 188 189 Error evalData(const UnaryOperationData &Data, 190 const MatchFinder::MatchResult &Match, std::string *Result) { 191 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id); 192 if (E == nullptr) 193 return llvm::make_error<StringError>( 194 errc::invalid_argument, "Id not bound or not Expr: " + Data.Id); 195 llvm::Optional<std::string> Source; 196 switch (Data.Op) { 197 case UnaryNodeOperator::Parens: 198 Source = tooling::buildParens(*E, *Match.Context); 199 break; 200 case UnaryNodeOperator::Deref: 201 Source = tooling::buildDereference(*E, *Match.Context); 202 break; 203 case UnaryNodeOperator::MaybeDeref: 204 if (!E->getType()->isAnyPointerType()) { 205 *Result += tooling::getText(*E, *Match.Context); 206 return Error::success(); 207 } 208 Source = tooling::buildDereference(*E, *Match.Context); 209 break; 210 case UnaryNodeOperator::AddressOf: 211 Source = tooling::buildAddressOf(*E, *Match.Context); 212 break; 213 case UnaryNodeOperator::MaybeAddressOf: 214 if (E->getType()->isAnyPointerType()) { 215 *Result += tooling::getText(*E, *Match.Context); 216 return Error::success(); 217 } 218 Source = tooling::buildAddressOf(*E, *Match.Context); 219 break; 220 } 221 if (!Source) 222 return llvm::make_error<StringError>( 223 errc::invalid_argument, 224 "Could not construct expression source from ID: " + Data.Id); 225 *Result += *Source; 226 return Error::success(); 227 } 228 229 Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match, 230 std::string *Result) { 231 auto RawRange = Data.Selector(Match); 232 if (!RawRange) 233 return RawRange.takeError(); 234 CharSourceRange Range = Lexer::makeFileCharRange( 235 *RawRange, *Match.SourceManager, Match.Context->getLangOpts()); 236 if (Range.isInvalid()) { 237 // Validate the original range to attempt to get a meaningful error message. 238 // If it's valid, then something else is the cause and we just return the 239 // generic failure message. 240 if (auto Err = tooling::validateEditRange(*RawRange, *Match.SourceManager)) 241 return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) { 242 assert(E->convertToErrorCode() == 243 llvm::make_error_code(errc::invalid_argument) && 244 "Validation errors must carry the invalid_argument code"); 245 return llvm::createStringError( 246 errc::invalid_argument, 247 "selected range could not be resolved to a valid source range; " + 248 E->getMessage()); 249 }); 250 return llvm::createStringError( 251 errc::invalid_argument, 252 "selected range could not be resolved to a valid source range"); 253 } 254 // Validate `Range`, because `makeFileCharRange` accepts some ranges that 255 // `validateEditRange` rejects. 256 if (auto Err = tooling::validateEditRange(Range, *Match.SourceManager)) 257 return joinErrors( 258 llvm::createStringError(errc::invalid_argument, 259 "selected range is not valid for editing"), 260 std::move(Err)); 261 *Result += tooling::getText(Range, *Match.Context); 262 return Error::success(); 263 } 264 265 Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match, 266 std::string *Result) { 267 const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId); 268 if (E == nullptr) 269 return llvm::make_error<StringError>(errc::invalid_argument, 270 "Id not bound: " + Data.BaseId); 271 if (!E->isImplicitCXXThis()) { 272 if (llvm::Optional<std::string> S = 273 E->getType()->isAnyPointerType() 274 ? tooling::buildArrow(*E, *Match.Context) 275 : tooling::buildDot(*E, *Match.Context)) 276 *Result += *S; 277 else 278 return llvm::make_error<StringError>( 279 errc::invalid_argument, 280 "Could not construct object text from ID: " + Data.BaseId); 281 } 282 return Data.Member->eval(Match, Result); 283 } 284 285 Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, 286 std::string *Result) { 287 auto &M = Match.Nodes.getMap(); 288 return (M.find(Data.Id) != M.end() ? Data.TrueStencil : Data.FalseStencil) 289 ->eval(Match, Result); 290 } 291 292 Error evalData(const MatchConsumer<std::string> &Fn, 293 const MatchFinder::MatchResult &Match, std::string *Result) { 294 Expected<std::string> Value = Fn(Match); 295 if (!Value) 296 return Value.takeError(); 297 *Result += *Value; 298 return Error::success(); 299 } 300 301 Error evalData(const SequenceData &Data, const MatchFinder::MatchResult &Match, 302 std::string *Result) { 303 for (const auto &S : Data.Stencils) 304 if (auto Err = S->eval(Match, Result)) 305 return Err; 306 return Error::success(); 307 } 308 309 template <typename T> class StencilImpl : public StencilInterface { 310 T Data; 311 312 public: 313 template <typename... Ps> 314 explicit StencilImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {} 315 316 Error eval(const MatchFinder::MatchResult &Match, 317 std::string *Result) const override { 318 return evalData(Data, Match, Result); 319 } 320 321 std::string toString() const override { return toStringData(Data); } 322 }; 323 } // namespace 324 325 Stencil transformer::detail::makeStencil(StringRef Text) { 326 return std::make_shared<StencilImpl<RawTextData>>(std::string(Text)); 327 } 328 329 Stencil transformer::detail::makeStencil(RangeSelector Selector) { 330 return std::make_shared<StencilImpl<SelectorData>>(std::move(Selector)); 331 } 332 333 Stencil transformer::dPrint(StringRef Id) { 334 return std::make_shared<StencilImpl<DebugPrintNodeData>>(std::string(Id)); 335 } 336 337 Stencil transformer::expression(llvm::StringRef Id) { 338 return std::make_shared<StencilImpl<UnaryOperationData>>( 339 UnaryNodeOperator::Parens, std::string(Id)); 340 } 341 342 Stencil transformer::deref(llvm::StringRef ExprId) { 343 return std::make_shared<StencilImpl<UnaryOperationData>>( 344 UnaryNodeOperator::Deref, std::string(ExprId)); 345 } 346 347 Stencil transformer::maybeDeref(llvm::StringRef ExprId) { 348 return std::make_shared<StencilImpl<UnaryOperationData>>( 349 UnaryNodeOperator::MaybeDeref, std::string(ExprId)); 350 } 351 352 Stencil transformer::addressOf(llvm::StringRef ExprId) { 353 return std::make_shared<StencilImpl<UnaryOperationData>>( 354 UnaryNodeOperator::AddressOf, std::string(ExprId)); 355 } 356 357 Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) { 358 return std::make_shared<StencilImpl<UnaryOperationData>>( 359 UnaryNodeOperator::MaybeAddressOf, std::string(ExprId)); 360 } 361 362 Stencil transformer::access(StringRef BaseId, Stencil Member) { 363 return std::make_shared<StencilImpl<AccessData>>(BaseId, std::move(Member)); 364 } 365 366 Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil, 367 Stencil FalseStencil) { 368 return std::make_shared<StencilImpl<IfBoundData>>(Id, std::move(TrueStencil), 369 std::move(FalseStencil)); 370 } 371 372 Stencil transformer::run(MatchConsumer<std::string> Fn) { 373 return std::make_shared<StencilImpl<MatchConsumer<std::string>>>( 374 std::move(Fn)); 375 } 376 377 Stencil transformer::catVector(std::vector<Stencil> Parts) { 378 // Only one argument, so don't wrap in sequence. 379 if (Parts.size() == 1) 380 return std::move(Parts[0]); 381 return std::make_shared<StencilImpl<SequenceData>>(std::move(Parts)); 382 } 383