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 @@ -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 @@ return HI; } +/// The standard defines __func__ as a "predefined variable". +llvm::Optional +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 @@ // For `this` expr we currently generate hover with pointee type. if (const CXXThisExpr *CTE = dyn_cast(E)) return getThisExprHoverContents(CTE, AST.getASTContext(), PP); + if (const PredefinedExpr *PE = dyn_cast(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())) { diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -720,6 +720,14 @@ 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; 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 @@ -139,6 +139,33 @@ 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 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 { diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -527,6 +527,10 @@ /*error-ok*/ void func() [[{^]])cpp", "CompoundStmt"}, + {R"cpp( + void func() { [[__^func__]]; } + )cpp", + "PredefinedExpr"}, }; for (const Case &C : Cases) {