[clangd] Support hover on __func__ etc (PredefinedExpr)

Expose these as variables as that's what the standard calls them (and D131175).

To make this work, we also fix a bug in SelectionTree: PredefinedExpr has
an implicit/invisible StringLiteral, and SelectionTree should not traverse
implicit things.

Reviewed By: ckandeler

Differential Revision: https://reviews.llvm.org/D132135
This commit is contained in:
Sam McCall 2022-08-19 14:51:36 +02:00
parent 3d2ea3c77f
commit 13b2a0c69b
4 changed files with 65 additions and 0 deletions

View File

@ -32,6 +32,7 @@
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TokenKinds.h"
@ -640,6 +641,29 @@ HoverInfo getHoverContents(const NamedDecl *D, const PrintingPolicy &PP,
return HI;
}
/// The standard defines __func__ as a "predefined variable".
llvm::Optional<HoverInfo>
getPredefinedExprHoverContents(const PredefinedExpr &PE, ASTContext &Ctx,
const PrintingPolicy &PP) {
HoverInfo HI;
HI.Name = PE.getIdentKindName();
HI.Kind = index::SymbolKind::Variable;
HI.Documentation = "Name of the current function (predefined variable)";
if (const StringLiteral *Name = PE.getFunctionName()) {
HI.Value.emplace();
llvm::raw_string_ostream OS(*HI.Value);
Name->outputString(OS);
HI.Type = printType(Name->getType(), Ctx, PP);
} else {
// Inside templates, the approximate type `const char[]` is still useful.
QualType StringType = Ctx.getIncompleteArrayType(
Ctx.CharTy.withConst(), ArrayType::ArraySizeModifier::Normal,
/*IndexTypeQuals=*/0);
HI.Type = printType(StringType, Ctx, PP);
}
return HI;
}
/// Generate a \p Hover object given the macro \p MacroDecl.
HoverInfo getHoverContents(const DefinedMacro &Macro, ParsedAST &AST) {
HoverInfo HI;
@ -764,6 +788,8 @@ llvm::Optional<HoverInfo> getHoverContents(const Expr *E, ParsedAST &AST,
// For `this` expr we currently generate hover with pointee type.
if (const CXXThisExpr *CTE = dyn_cast<CXXThisExpr>(E))
return getThisExprHoverContents(CTE, AST.getASTContext(), PP);
if (const PredefinedExpr *PE = dyn_cast<PredefinedExpr>(E))
return getPredefinedExprHoverContents(*PE, AST.getASTContext(), PP);
// For expressions we currently print the type and the value, iff it is
// evaluatable.
if (auto Val = printExprValue(E, AST.getASTContext())) {

View File

@ -720,6 +720,14 @@ public:
return Base::TraverseTypeConstraint(C);
}
// Override child traversal for certain node types.
using RecursiveASTVisitor::getStmtChildren;
// PredefinedExpr like __func__ has a StringLiteral child for its value.
// It's not written, so don't traverse it.
Stmt::child_range getStmtChildren(PredefinedExpr *) {
return {StmtIterator{}, StmtIterator{}};
}
private:
using Base = RecursiveASTVisitor<SelectionVisitor>;

View File

@ -139,6 +139,33 @@ TEST(Hover, Structured) {
HI.Definition = "int bar";
HI.Type = "int";
}},
// Predefined variable
{R"cpp(
void foo() {
[[__f^unc__]];
}
)cpp",
[](HoverInfo &HI) {
HI.Name = "__func__";
HI.Kind = index::SymbolKind::Variable;
HI.Documentation =
"Name of the current function (predefined variable)";
HI.Value = "\"foo\"";
HI.Type = "const char[4]";
}},
// Predefined variable (dependent)
{R"cpp(
template<int> void foo() {
[[__f^unc__]];
}
)cpp",
[](HoverInfo &HI) {
HI.Name = "__func__";
HI.Kind = index::SymbolKind::Variable;
HI.Documentation =
"Name of the current function (predefined variable)";
HI.Type = "const char[]";
}},
// Anon namespace and local scope.
{R"cpp(
namespace ns1 { namespace {

View File

@ -527,6 +527,10 @@ TEST(SelectionTest, CommonAncestor) {
/*error-ok*/
void func() [[{^]])cpp",
"CompoundStmt"},
{R"cpp(
void func() { [[__^func__]]; }
)cpp",
"PredefinedExpr"},
};
for (const Case &C : Cases) {