diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -71,6 +71,7 @@ std::string Documentation; /// Source code containing the definition of the symbol. std::string Definition; + std::string MacroExpansion; const char *DefinitionLanguage = "cpp"; /// Access specifier for declarations inside class/struct/unions, empty for /// others. diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -641,7 +641,8 @@ } /// Generate a \p Hover object given the macro \p MacroDecl. -HoverInfo getHoverContents(const DefinedMacro &Macro, ParsedAST &AST) { +HoverInfo getHoverContents(const syntax::Token &Tok, const DefinedMacro &Macro, + ParsedAST &AST) { HoverInfo HI; SourceManager &SM = AST.getSourceManager(); HI.Name = std::string(Macro.Name); @@ -670,6 +671,29 @@ HI.Definition = ("#define " + Buffer.substr(StartOffset, EndOffset - StartOffset)) .str(); + + auto Expansion = AST.getTokens().expansionStartingAt(&Tok); + if (Expansion) { + // Use a fixed size buffer for better performance and presentation. + // For extremely long expansion text, it's not readable from hover card + // anyway. + std::string ExpansionTextBuffer; + ExpansionTextBuffer.reserve(2048); + + for (const auto &ExpandedTok : Expansion->Expanded) { + StringRef ExpandedTokText = ExpandedTok.text(SM); + if (ExpansionTextBuffer.size() + ExpandedTokText.size() + 1 < + ExpansionTextBuffer.capacity()) { + ExpansionTextBuffer += ExpandedTokText; + ExpansionTextBuffer += " "; + } else { + ExpansionTextBuffer.clear(); + break; + } + } + + HI.MacroExpansion = std::move(ExpansionTextBuffer); + } } } return HI; @@ -1004,7 +1028,7 @@ // Prefer the identifier token as a fallback highlighting range. HighlightRange = Tok.range(SM).toCharRange(SM); if (auto M = locateMacroAt(Tok, AST.getPreprocessor())) { - HI = getHoverContents(*M, AST); + HI = getHoverContents(Tok, *M, AST); break; } } else if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) { @@ -1055,11 +1079,25 @@ if (!HI) return llvm::None; - auto Replacements = format::reformat( - Style, HI->Definition, tooling::Range(0, HI->Definition.size())); - if (auto Formatted = - tooling::applyAllReplacements(HI->Definition, Replacements)) - HI->Definition = *Formatted; + // Reformat Definition + if (!HI->Definition.empty()) { + auto Replacements = format::reformat( + Style, HI->Definition, tooling::Range(0, HI->Definition.size())); + if (auto Formatted = + tooling::applyAllReplacements(HI->Definition, Replacements)) + HI->Definition = *Formatted; + } + + // Reformat Macro Expansion + if (!HI->MacroExpansion.empty()) { + auto Replacements = + format::reformat(Style, HI->MacroExpansion, + tooling::Range(0, HI->MacroExpansion.size())); + if (auto Formatted = + tooling::applyAllReplacements(HI->MacroExpansion, Replacements)) + HI->MacroExpansion = *Formatted; + } + HI->DefinitionLanguage = getMarkdownLanguage(AST.getASTContext()); HI->SymRange = halfOpenToRange(SM, HighlightRange); @@ -1175,6 +1213,12 @@ DefinitionLanguage); } + if (!MacroExpansion.empty()) { + Output.addRuler(); + Output.addParagraph().appendText("Macro Expansion:"); + Output.addCodeBlock(MacroExpansion, DefinitionLanguage); + } + return Output; } diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -478,15 +478,46 @@ HI.Definition = "Foo"; }}, - // macro + // variable-like macro + {R"cpp( + #define MACRO 41 + int x = [[MAC^RO]]; + )cpp", + [](HoverInfo &HI) { + HI.Name = "MACRO"; + HI.Kind = index::SymbolKind::Macro; + HI.Definition = "#define MACRO 41"; + HI.MacroExpansion = "41"; + }}, + + // function-like macro {R"cpp( // Best MACRO ever. - #define MACRO(x,y,z) void foo(x, y, z); + #define MACRO(x,y,z) void foo(x, y, z) [[MAC^RO]](int, double d, bool z = false); )cpp", [](HoverInfo &HI) { - HI.Name = "MACRO", HI.Kind = index::SymbolKind::Macro, - HI.Definition = "#define MACRO(x, y, z) void foo(x, y, z);"; + HI.Name = "MACRO"; + HI.Kind = index::SymbolKind::Macro; + HI.Definition = "#define MACRO(x, y, z) void foo(x, y, z)"; + HI.MacroExpansion = "void foo(int, double d, bool z = false)"; + }}, + + // nested macro + {R"cpp( + #define STRINGIFY_AUX(s) #s + #define STRINGIFY(s) STRINGIFY_AUX(s) + #define DECL_STR(NAME, VALUE) const char *v_##NAME = STRINGIFY(VALUE) + #define FOO 41 + + [[DECL^_STR]](foo, FOO); + )cpp", + [](HoverInfo &HI) { + HI.Name = "DECL_STR"; + HI.Kind = index::SymbolKind::Macro; + HI.Definition = "#define DECL_STR(NAME, VALUE) const char *v_##NAME = " + "STRINGIFY(VALUE)"; + HI.MacroExpansion = "const char *v_foo = \"41\""; }}, // constexprs @@ -1070,6 +1101,7 @@ EXPECT_EQ(H->Kind, Expected.Kind); EXPECT_EQ(H->Documentation, Expected.Documentation); EXPECT_EQ(H->Definition, Expected.Definition); + EXPECT_EQ(H->MacroExpansion, Expected.MacroExpansion); EXPECT_EQ(H->Type, Expected.Type); EXPECT_EQ(H->ReturnType, Expected.ReturnType); EXPECT_EQ(H->Parameters, Expected.Parameters); @@ -1567,6 +1599,7 @@ HI.Name = "MACRO"; HI.Kind = index::SymbolKind::Macro; HI.Definition = "#define MACRO 0"; + HI.MacroExpansion = "0"; }}, { R"cpp(// Macro @@ -1577,6 +1610,8 @@ HI.Name = "MACRO"; HI.Kind = index::SymbolKind::Macro; HI.Definition = "#define MACRO 0"; + // FIXME: expansion of MACRO isn't available in macro + // definition/arguments }}, { R"cpp(// Macro @@ -1591,6 +1626,7 @@ HI.Definition = R"cpp(#define MACRO \ { return 0; })cpp"; + HI.MacroExpansion = "{ return 0; }"; }}, { R"cpp(// Forward class declaration @@ -2625,6 +2661,7 @@ EXPECT_EQ(H->Kind, Expected.Kind); EXPECT_EQ(H->Documentation, Expected.Documentation); EXPECT_EQ(H->Definition, Expected.Definition); + EXPECT_EQ(H->MacroExpansion, Expected.MacroExpansion); EXPECT_EQ(H->Type, Expected.Type); EXPECT_EQ(H->ReturnType, Expected.ReturnType); EXPECT_EQ(H->Parameters, Expected.Parameters);