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); @@ -672,6 +673,29 @@ .str(); } } + + if (auto Expansion = AST.getTokens().expansionStartingAt(&Tok)) { + // We drop expansion that's longer than the threshold. + // For extremely long expansion text, it's not readable from hover card + // anyway. + std::string ExpansionText; + for (const auto &ExpandedTok : Expansion->Expanded) { + ExpansionText += ExpandedTok.text(SM); + ExpansionText += " "; + if (ExpansionText.size() > 2048) { + ExpansionText.clear(); + break; + } + } + + if (!ExpansionText.empty()) { + if (!HI.Definition.empty()) { + HI.Definition += "\n\n"; + } + HI.Definition += "// Expands to\n"; + HI.Definition += ExpansionText; + } + } 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,15 @@ 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; + } + HI->DefinitionLanguage = getMarkdownLanguage(AST.getASTContext()); HI->SymRange = halfOpenToRange(SM, HighlightRange); @@ -1154,25 +1182,31 @@ if (!Definition.empty()) { Output.addRuler(); - std::string ScopeComment; - // Drop trailing "::". - if (!LocalScope.empty()) { - // Container name, e.g. class, method, function. - // We might want to propagate some info about container type to print - // function foo, class X, method X::bar, etc. - ScopeComment = - "// In " + llvm::StringRef(LocalScope).rtrim(':').str() + '\n'; - } else if (NamespaceScope && !NamespaceScope->empty()) { - ScopeComment = "// In namespace " + - llvm::StringRef(*NamespaceScope).rtrim(':').str() + '\n'; + std::string Buffer; + + if (!Definition.empty()) { + // Append scope comment, dropping trailing "::". + // Note that we don't print anything for global namespace, to not annoy + // non-c++ projects or projects that are not making use of namespaces. + if (!LocalScope.empty()) { + // Container name, e.g. class, method, function. + // We might want to propagate some info about container type to print + // function foo, class X, method X::bar, etc. + Buffer += + "// In " + llvm::StringRef(LocalScope).rtrim(':').str() + '\n'; + } else if (NamespaceScope && !NamespaceScope->empty()) { + Buffer += "// In namespace " + + llvm::StringRef(*NamespaceScope).rtrim(':').str() + '\n'; + } + + if (!AccessSpecifier.empty()) { + Buffer += AccessSpecifier + ": "; + } + + Buffer += Definition; } - std::string DefinitionWithAccess = !AccessSpecifier.empty() - ? AccessSpecifier + ": " + Definition - : Definition; - // Note that we don't print anything for global namespace, to not annoy - // non-c++ projects or projects that are not making use of namespaces. - Output.addCodeBlock(ScopeComment + DefinitionWithAccess, - DefinitionLanguage); + + Output.addCodeBlock(Buffer, 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,60 @@ HI.Definition = "Foo"; }}, - // macro + // empty macro + {R"cpp( + #define MACRO + [[MAC^RO]] + )cpp", + [](HoverInfo &HI) { + HI.Name = "MACRO"; + HI.Kind = index::SymbolKind::Macro; + HI.Definition = "#define MACRO"; + }}, + + // object-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\n\n" + "// Expands to\n" + "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)\n\n" + "// Expands to\n" + "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)\n\n" + "// Expands to\n" + "const char *v_foo = \"41\""; }}, // constexprs @@ -1566,7 +1611,9 @@ [](HoverInfo &HI) { HI.Name = "MACRO"; HI.Kind = index::SymbolKind::Macro; - HI.Definition = "#define MACRO 0"; + HI.Definition = "#define MACRO 0\n\n" + "// Expands to\n" + "0"; }}, { R"cpp(// Macro @@ -1577,6 +1624,8 @@ HI.Name = "MACRO"; HI.Kind = index::SymbolKind::Macro; HI.Definition = "#define MACRO 0"; + // NOTE MACRO doesn't have expansion since it technically isn't + // expanded here }}, { R"cpp(// Macro @@ -1590,7 +1639,10 @@ HI.Kind = index::SymbolKind::Macro; HI.Definition = R"cpp(#define MACRO \ - { return 0; })cpp"; + { return 0; } + +// Expands to +{ return 0; })cpp"; }}, { R"cpp(// Forward class declaration @@ -2986,6 +3038,21 @@ // In test::Bar int foo = 3)", + }, + { + [](HoverInfo &HI) { + HI.Kind = index::SymbolKind::Macro; + HI.Name = "PLUS_ONE"; + HI.Definition = "#define PLUS_ONE(X) (X+1)\n\n" + "// Expands to\n" + "(1 + 1)"; + }, + R"(macro PLUS_ONE + +#define PLUS_ONE(X) (X+1) + +// Expands to +(1 + 1))", }, { [](HoverInfo &HI) {