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