1 //===--- DumpAST.cpp ---------------------------------------------*- 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 // Defines a few tweaks that expose AST and related information. 9 // Some of these are fairly clang-specific and hidden (e.g. textual AST dumps). 10 // Others are more generally useful (class layout) and are exposed by default. 11 //===----------------------------------------------------------------------===// 12 #include "XRefs.h" 13 #include "refactor/Tweak.h" 14 #include "clang/AST/ASTTypeTraits.h" 15 #include "clang/AST/Type.h" 16 #include "llvm/Support/FormatVariadic.h" 17 #include "llvm/Support/ScopedPrinter.h" 18 #include "llvm/Support/raw_ostream.h" 19 20 namespace clang { 21 namespace clangd { 22 namespace { 23 24 /// Dumps the AST of the selected node. 25 /// Input: 26 /// fcall("foo"); 27 /// ^^^^^ 28 /// Message: 29 /// CallExpr 30 /// |-DeclRefExpr fcall 31 /// `-StringLiteral "foo" 32 class DumpAST : public Tweak { 33 public: 34 const char *id() const override final; 35 36 bool prepare(const Selection &Inputs) override { 37 for (auto *N = Inputs.ASTSelection.commonAncestor(); N && !Node; 38 N = N->Parent) 39 if (dumpable(N->ASTNode)) 40 Node = N->ASTNode; 41 return Node.has_value(); 42 } 43 Expected<Effect> apply(const Selection &Inputs) override; 44 std::string title() const override { 45 return std::string( 46 llvm::formatv("Dump {0} AST", Node->getNodeKind().asStringRef())); 47 } 48 llvm::StringLiteral kind() const override { return CodeAction::INFO_KIND; } 49 bool hidden() const override { return true; } 50 51 private: 52 static bool dumpable(const DynTypedNode &N) { 53 // Sadly not all node types can be dumped, and there's no API to check. 54 // See DynTypedNode::dump(). 55 return N.get<Decl>() || N.get<Stmt>() || N.get<Type>(); 56 } 57 58 llvm::Optional<DynTypedNode> Node; 59 }; 60 REGISTER_TWEAK(DumpAST) 61 62 llvm::Expected<Tweak::Effect> DumpAST::apply(const Selection &Inputs) { 63 std::string Str; 64 llvm::raw_string_ostream OS(Str); 65 Node->dump(OS, Inputs.AST->getASTContext()); 66 return Effect::showMessage(std::move(OS.str())); 67 } 68 69 /// Dumps the SelectionTree. 70 /// Input: 71 /// int fcall(int); 72 /// void foo() { 73 /// fcall(2 + 2); 74 /// ^^^^^ 75 /// } 76 /// Message: 77 /// TranslationUnitDecl 78 /// FunctionDecl void foo() 79 /// CompoundStmt {} 80 /// .CallExpr fcall(2 + 2) 81 /// ImplicitCastExpr fcall 82 /// .DeclRefExpr fcall 83 /// BinaryOperator 2 + 2 84 /// *IntegerLiteral 2 85 class ShowSelectionTree : public Tweak { 86 public: 87 const char *id() const override final; 88 89 bool prepare(const Selection &Inputs) override { return true; } 90 Expected<Effect> apply(const Selection &Inputs) override { 91 return Effect::showMessage(llvm::to_string(Inputs.ASTSelection)); 92 } 93 std::string title() const override { return "Show selection tree"; } 94 llvm::StringLiteral kind() const override { return CodeAction::INFO_KIND; } 95 bool hidden() const override { return true; } 96 }; 97 REGISTER_TWEAK(ShowSelectionTree) 98 99 /// Dumps the symbol under the cursor. 100 /// Inputs: 101 /// void foo(); 102 /// ^^^ 103 /// Message: 104 /// foo - 105 /// {"containerName":null,"id":"CA2EBE44A1D76D2A","name":"foo","usr":"c:@F@foo#"} 106 class DumpSymbol : public Tweak { 107 const char *id() const override final; 108 bool prepare(const Selection &Inputs) override { return true; } 109 Expected<Effect> apply(const Selection &Inputs) override { 110 std::string Storage; 111 llvm::raw_string_ostream Out(Storage); 112 113 for (auto &Sym : getSymbolInfo( 114 *Inputs.AST, sourceLocToPosition(Inputs.AST->getSourceManager(), 115 Inputs.Cursor))) 116 Out << Sym; 117 return Effect::showMessage(Out.str()); 118 } 119 std::string title() const override { return "Dump symbol under the cursor"; } 120 llvm::StringLiteral kind() const override { return CodeAction::INFO_KIND; } 121 bool hidden() const override { return true; } 122 }; 123 REGISTER_TWEAK(DumpSymbol) 124 125 /// Shows the layout of the RecordDecl under the cursor. 126 /// Input: 127 /// struct X { int foo; }; 128 /// ^^^^^^^^ 129 /// Message: 130 /// 0 | struct S 131 /// 0 | int foo 132 /// | [sizeof=4, dsize=4, align=4, 133 /// | nvsize=4, nvalign=4] 134 class DumpRecordLayout : public Tweak { 135 public: 136 const char *id() const override final; 137 138 bool prepare(const Selection &Inputs) override { 139 if (auto *Node = Inputs.ASTSelection.commonAncestor()) 140 if (auto *D = Node->ASTNode.get<Decl>()) 141 Record = dyn_cast<RecordDecl>(D); 142 return Record && Record->isThisDeclarationADefinition() && 143 !Record->isDependentType(); 144 } 145 Expected<Effect> apply(const Selection &Inputs) override { 146 std::string Str; 147 llvm::raw_string_ostream OS(Str); 148 Inputs.AST->getASTContext().DumpRecordLayout(Record, OS); 149 return Effect::showMessage(std::move(OS.str())); 150 } 151 std::string title() const override { 152 return std::string(llvm::formatv( 153 "Show {0} layout", 154 TypeWithKeyword::getTagTypeKindName(Record->getTagKind()))); 155 } 156 llvm::StringLiteral kind() const override { return CodeAction::INFO_KIND; } 157 // FIXME: this is interesting to most users. However: 158 // - triggering is too broad (e.g. triggers on comments within a class) 159 // - showMessage has inconsistent UX (e.g. newlines are stripped in VSCode) 160 // - the output itself is a bit hard to decipher. 161 bool hidden() const override { return true; } 162 163 private: 164 const RecordDecl *Record = nullptr; 165 }; 166 REGISTER_TWEAK(DumpRecordLayout) 167 168 } // namespace 169 } // namespace clangd 170 } // namespace clang 171