diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -58,7 +58,8 @@ /// Prints unqualified name of the decl for the purpose of displaying it to the /// user. Anonymous decls return names of the form "(anonymous {kind})", e.g. /// "(anonymous struct)" or "(anonymous namespace)". -std::string printName(const ASTContext &Ctx, const NamedDecl &ND); +std::string printName(const ASTContext &Ctx, const NamedDecl &ND, + const PrintingPolicy * = nullptr); /// Prints template arguments of a decl as written in the source code, including /// enclosing '<' and '>', e.g for a partial specialization like: template diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -210,10 +210,12 @@ return Out.str(); } -std::string printName(const ASTContext &Ctx, const NamedDecl &ND) { +std::string printName(const ASTContext &Ctx, const NamedDecl &ND, + const PrintingPolicy *BasePolicy) { std::string Name; llvm::raw_string_ostream Out(Name); - PrintingPolicy PP(Ctx.getLangOpts()); + PrintingPolicy PP = + BasePolicy ? *BasePolicy : PrintingPolicy(Ctx.getLangOpts()); // We don't consider a class template's args part of the constructor name. PP.SuppressTemplateArgsInCXXConstructors = true; @@ -244,7 +246,10 @@ if (auto *Qualifier = getQualifierLoc(ND).getNestedNameSpecifier()) Qualifier->print(Out, PP); // Print the name itself. - ND.getDeclName().print(Out, PP); + if (PP.CleanUglifiedParameters && ND.getIdentifier()) + Out << ND.getIdentifier()->deuglifiedName(); + else + ND.getDeclName().print(Out, PP); // Print template arguments. Out << printTemplateSpecializationArgs(ND); 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 @@ -62,13 +62,14 @@ Base.PolishForDeclaration = true; Base.ConstantsAsWritten = true; Base.SuppressTemplateArgsInCXXConstructors = true; + Base.CleanUglifiedParameters = true; return Base; } /// Given a declaration \p D, return a human-readable string representing the /// local scope in which it is declared, i.e. class(es) and method name. Returns /// an empty string if it is not local. -std::string getLocalScope(const Decl *D) { +std::string getLocalScope(const Decl *D, const PrintingPolicy &PP) { std::vector Scopes; const DeclContext *DC = D->getDeclContext(); @@ -80,9 +81,9 @@ if (const ObjCContainerDecl *CD = dyn_cast(DC)) return printObjCContainer(*CD); - auto GetName = [](const TypeDecl *D) { + auto GetName = [&](const TypeDecl *D) { if (!D->getDeclName().isEmpty()) { - PrintingPolicy Policy = D->getASTContext().getPrintingPolicy(); + PrintingPolicy Policy = PP; Policy.SuppressScope = true; return declaredType(D).getAsString(Policy); } @@ -224,8 +225,8 @@ if (const auto *TTP = dyn_cast(Param)) { P.Type = printType(TTP); - if (!TTP->getName().empty()) - P.Name = TTP->getNameAsString(); + if (IdentifierInfo *II = TTP->getIdentifier()) + P.Name = II->deuglifiedName().str(); if (TTP->hasDefaultArgument()) P.Default = TTP->getDefaultArgument().getAsString(PP); @@ -233,7 +234,7 @@ P.Type = printType(NTTP, PP); if (IdentifierInfo *II = NTTP->getIdentifier()) - P.Name = II->getName().str(); + P.Name = II->deuglifiedName().str(); if (NTTP->hasDefaultArgument()) { P.Default.emplace(); @@ -243,8 +244,8 @@ } else if (const auto *TTPD = dyn_cast(Param)) { P.Type = printType(TTPD, PP); - if (!TTPD->getName().empty()) - P.Name = TTPD->getNameAsString(); + if (IdentifierInfo *II = TTPD->getIdentifier()) + P.Name = II->deuglifiedName().str(); if (TTPD->hasDefaultArgument()) { P.Default.emplace(); @@ -347,8 +348,8 @@ const PrintingPolicy &PP) { HoverInfo::Param Out; Out.Type = printType(PVD->getType(), PVD->getASTContext(), PP); - if (!PVD->getName().empty()) - Out.Name = PVD->getNameAsString(); + if (const IdentifierInfo *II = PVD->getIdentifier()) + Out.Name = II->deuglifiedName().str(); if (const Expr *DefArg = getDefaultArg(PVD)) { Out.Default.emplace(); llvm::raw_string_ostream OS(*Out.Default); @@ -576,11 +577,11 @@ HI.NamespaceScope = getNamespaceScope(D); if (!HI.NamespaceScope->empty()) HI.NamespaceScope->append("::"); - HI.LocalScope = getLocalScope(D); + HI.LocalScope = getLocalScope(D, PP); if (!HI.LocalScope.empty()) HI.LocalScope.append("::"); - HI.Name = printName(Ctx, *D); + HI.Name = printName(Ctx, *D, &PP); const auto *CommentD = getDeclForComment(D); HI.Documentation = getDeclComment(Ctx, *CommentD); enhanceFromIndex(HI, *CommentD, Index); 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 @@ -1044,6 +1044,42 @@ HI.Kind = index::SymbolKind::Field; HI.Definition = "m_int arr[Size]"; HI.Type = {"m_int[Size]", "int[Size]"}; + }}, + {// Uglified standard library parameter names. + R"cpp( + template class Tmpl { + template + typename _Tp::iterator ^[[put]](_Tp __value); + }; + )cpp", + [](HoverInfo &HI) { + HI.Name = "put"; + HI.NamespaceScope = ""; + HI.LocalScope = "Tmpl::"; + HI.AccessSpecifier = "private"; + HI.Kind = index::SymbolKind::InstanceMethod; + HI.Definition = "template typename Tp::iterator put(Tp value)"; + HI.ReturnType = "typename Tp::iterator"; + HI.Type = "typename Tp::iterator (Tp)"; + HI.Parameters = { + {{"Tp"}, {"value"}, llvm::None}, + }; + HI.TemplateParameters = { + {{"int"}, {"I"}, llvm::None}, + }; + }}, + {// Uglified type name. + R"cpp( + template class X; + )cpp", + [](HoverInfo &HI) { + HI.Name = "Tp"; + HI.Type = "typename"; + HI.LocalScope = "X::"; + HI.NamespaceScope = ""; + HI.AccessSpecifier = "public"; + HI.Definition = "typename Tp"; + HI.Kind = index::SymbolKind::TemplateTypeParm; }}}; for (const auto &Case : Cases) { SCOPED_TRACE(Case.Code); diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp --- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp +++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp @@ -1458,8 +1458,8 @@ namespace std { class string {}; // Move overloads have special handling. - template T&& move(T&&); - template O move(I, I, O); + template T&& move(_T&& __value); + template _O move(_I, _I, _O); } )cpp", /*Main=*/""); @@ -1469,7 +1469,8 @@ QName("std"), AllOf(QName("std::string"), DeclURI(TestHeaderURI), IncludeHeader("")), - AllOf(Labeled("move(T &&)"), IncludeHeader("")), + // Parameter names are demangled. + AllOf(Labeled("move(T &&value)"), IncludeHeader("")), AllOf(Labeled("move(I, I, O)"), IncludeHeader("")))); } diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -75,7 +75,8 @@ MSVCFormatting(false), ConstantsAsWritten(false), SuppressImplicitBase(false), FullyQualifiedName(false), PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true), - UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false) {} + UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false), + CleanUglifiedParameters(false) {} /// Adjust this printing policy for cases where it's known that we're /// printing C++ code (for instance, if AST dumping reaches a C++-only @@ -282,6 +283,11 @@ /// parameters. unsigned AlwaysIncludeTypeForTemplateArgument : 1; + /// Whether to strip underscores when printing reserved parameter names. + /// e.g. std::vector becomes std::vector. + /// This only affects parameter names, and so describes a compatible API. + unsigned CleanUglifiedParameters : 1; + /// Callbacks to use to allow the behavior of printing to be customized. const PrintingCallbacks *Callbacks = nullptr; }; diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -458,6 +458,10 @@ /// 7.1.3, C++ [lib.global.names]). ReservedIdentifierStatus isReserved(const LangOptions &LangOpts) const; + /// If the identifier is an "uglified" reserved name, return a cleaned form. + /// e.g. _Foo => Foo. Otherwise, just returns the name. + StringRef deuglifiedName() const; + /// Provide less than operator for lexicographical sorting. bool operator<(const IdentifierInfo &RHS) const { return getName() < RHS.getName(); diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -885,7 +885,10 @@ } } - printDeclType(T, D->getName()); + printDeclType(T, (isa(D) && Policy.CleanUglifiedParameters && + D->getIdentifier()) + ? D->getIdentifier()->deuglifiedName() + : D->getName()); Expr *Init = D->getInit(); if (!Policy.SuppressInitializers && Init) { bool ImplicitInit = false; @@ -1131,8 +1134,13 @@ else if (TTP->getDeclName()) Out << ' '; - if (TTP->getDeclName()) - Out << TTP->getDeclName(); + if (TTP->getDeclName()) { + if (Policy.CleanUglifiedParameters && isa(D) && + TTP->getIdentifier()) + Out << TTP->getIdentifier()->deuglifiedName(); + else + Out << TTP->getDeclName(); + } } else if (auto *TD = D->getTemplatedDecl()) Visit(TD); else if (const auto *Concept = dyn_cast(D)) { @@ -1742,8 +1750,12 @@ else if (TTP->getDeclName()) Out << ' '; - if (TTP->getDeclName()) - Out << TTP->getDeclName(); + if (TTP->getDeclName()) { + if (Policy.CleanUglifiedParameters && TTP->getIdentifier()) + Out << TTP->getIdentifier()->deuglifiedName(); + else + Out << TTP->getDeclName(); + } if (TTP->hasDefaultArgument()) { Out << " = "; @@ -1755,7 +1767,8 @@ const NonTypeTemplateParmDecl *NTTP) { StringRef Name; if (IdentifierInfo *II = NTTP->getIdentifier()) - Name = II->getName(); + Name = + Policy.CleanUglifiedParameters ? II->deuglifiedName() : II->getName(); printDeclType(NTTP->getType(), Name, NTTP->isParameterPack()); if (NTTP->hasDefaultArgument()) { diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1030,7 +1030,12 @@ Qualifier->print(OS, Policy); if (Node->hasTemplateKeyword()) OS << "template "; - OS << Node->getNameInfo(); + if (Policy.CleanUglifiedParameters && + isa(Node->getDecl()) && + Node->getDecl()->getIdentifier()) + OS << Node->getDecl()->getIdentifier()->deuglifiedName(); + else + Node->getNameInfo().printName(OS, Policy); if (Node->hasExplicitTemplateArgs()) { const TemplateParameterList *TPL = nullptr; if (!Node->hadMultipleCandidates()) @@ -2069,7 +2074,10 @@ } else { NeedComma = true; } - std::string ParamStr = P->getNameAsString(); + std::string ParamStr = + (Policy.CleanUglifiedParameters && P->getIdentifier()) + ? P->getIdentifier()->deuglifiedName().str() + : P->getNameAsString(); P->getOriginalType().print(OS, Policy, ParamStr); } if (Method->isVariadic()) { diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp --- a/clang/lib/AST/TemplateName.cpp +++ b/clang/lib/AST/TemplateName.cpp @@ -223,8 +223,12 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, Qualified Qual) const { if (TemplateDecl *Template = Storage.dyn_cast()) - if (Qual == Qualified::Fully && - getDependence() != TemplateNameDependenceScope::DependentInstantiation) + if (Policy.CleanUglifiedParameters && + isa(Template) && Template->getIdentifier()) + OS << Template->getIdentifier()->deuglifiedName(); + else if (Qual == Qualified::Fully && + getDependence() != + TemplateNameDependenceScope::DependentInstantiation) Template->printQualifiedName(OS, Policy); else OS << *Template; diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1418,7 +1418,8 @@ } OS << "auto"; } else if (IdentifierInfo *Id = T->getIdentifier()) - OS << Id->getName(); + OS << (Policy.CleanUglifiedParameters ? Id->deuglifiedName() + : Id->getName()); else OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -309,6 +309,14 @@ return ReservedIdentifierStatus::NotReserved; } +StringRef IdentifierInfo::deuglifiedName() const { + StringRef Name = getName(); + if (Name.size() >= 2 && Name.front() == '_' && + (Name[1] == '_' || (Name[1] >= 'A' && Name[1] <= 'Z'))) + return Name.ltrim('_'); + return Name; +} + tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const { // We use a perfect hash function here involving the length of the keyword, // the first and third character. For preprocessor ID's there are no diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -1895,6 +1895,7 @@ Policy.SuppressStrongLifetime = true; Policy.SuppressUnwrittenScope = true; Policy.SuppressScope = true; + Policy.CleanUglifiedParameters = true; return Policy; } @@ -2832,7 +2833,7 @@ std::string Result; if (Param->getIdentifier() && !ObjCMethodParam && !SuppressName) - Result = std::string(Param->getIdentifier()->getName()); + Result = std::string(Param->getIdentifier()->deuglifiedName()); QualType Type = Param->getType(); if (ObjCSubsts) @@ -2843,7 +2844,7 @@ "(" + formatObjCParamQualifiers(Param->getObjCDeclQualifier(), Type); Result += Type.getAsString(Policy) + ")"; if (Param->getIdentifier() && !SuppressName) - Result += Param->getIdentifier()->getName(); + Result += Param->getIdentifier()->deuglifiedName(); } else { Type.getAsStringInternal(Result, Policy); } @@ -2871,7 +2872,7 @@ // for the block; just use the parameter type as a placeholder. std::string Result; if (!ObjCMethodParam && Param->getIdentifier()) - Result = std::string(Param->getIdentifier()->getName()); + Result = std::string(Param->getIdentifier()->deuglifiedName()); QualType Type = Param->getType().getUnqualifiedType(); @@ -2884,7 +2885,7 @@ if (Result.back() != ')') Result += " "; if (Param->getIdentifier()) - Result += Param->getIdentifier()->getName(); + Result += Param->getIdentifier()->deuglifiedName(); } else { Type.getAsStringInternal(Result, Policy); } @@ -3079,14 +3080,14 @@ if (TTP->getIdentifier()) { PlaceholderStr += ' '; - PlaceholderStr += TTP->getIdentifier()->getName(); + PlaceholderStr += TTP->getIdentifier()->deuglifiedName(); } HasDefaultArg = TTP->hasDefaultArgument(); } else if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*P)) { if (NTTP->getIdentifier()) - PlaceholderStr = std::string(NTTP->getIdentifier()->getName()); + PlaceholderStr = std::string(NTTP->getIdentifier()->deuglifiedName()); NTTP->getType().getAsStringInternal(PlaceholderStr, Policy); HasDefaultArg = NTTP->hasDefaultArgument(); } else { @@ -3098,7 +3099,7 @@ PlaceholderStr = "template<...> class"; if (TTP->getIdentifier()) { PlaceholderStr += ' '; - PlaceholderStr += TTP->getIdentifier()->getName(); + PlaceholderStr += TTP->getIdentifier()->deuglifiedName(); } HasDefaultArg = TTP->hasDefaultArgument(); diff --git a/clang/test/CodeCompletion/deuglify.cpp b/clang/test/CodeCompletion/deuglify.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeCompletion/deuglify.cpp @@ -0,0 +1,25 @@ +// Fake standard library with uglified names. +// Parameters (including template params) get ugliness stripped. +namespace std { + +template +class __vector_base {}; + +template +class vector : private __vector_base<_Tp> { +public: + _Tp &at(unsigned __index) const; + int __stays_ugly(); +}; + +} // namespace std + +int x = std::vector{}.at(42); +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:17:14 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: COMPLETION: __vector_base : __vector_base<<#typename Tp#>> +// CHECK-CC1: COMPLETION: vector : vector<<#typename Tp#>> +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:17:28 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: COMPLETION: __stays_ugly : [#int#]__stays_ugly() +// CHECK-CC2: COMPLETION: at : [#int &#]at(<#unsigned int index#>)[# const#] +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:17:31 %s -o - | FileCheck -check-prefix=CHECK-CC3 %s +// CHECK-CC3: OVERLOAD: [#int &#]at(<#unsigned int index#>) diff --git a/clang/unittests/AST/DeclPrinterTest.cpp b/clang/unittests/AST/DeclPrinterTest.cpp --- a/clang/unittests/AST/DeclPrinterTest.cpp +++ b/clang/unittests/AST/DeclPrinterTest.cpp @@ -1336,6 +1336,41 @@ ASSERT_TRUE(PrintedDeclCXX11Matches(Code, "NT2", "int NT2 = 5")); } +TEST(DeclPrinter, TestFunctionParamUglified) { + llvm::StringLiteral Code = R"cpp( + class __c; + void _A(__c *__param); + )cpp"; + auto Clean = [](PrintingPolicy &Policy) { + Policy.CleanUglifiedParameters = true; + }; + + ASSERT_TRUE(PrintedDeclCXX17Matches(Code, namedDecl(hasName("_A")).bind("id"), + "void _A(__c *__param)")); + ASSERT_TRUE(PrintedDeclCXX17Matches(Code, namedDecl(hasName("_A")).bind("id"), + "void _A(__c *param)", Clean)); +} + +TEST(DeclPrinter, TestTemplateParamUglified) { + llvm::StringLiteral Code = R"cpp( + template class _Container> + struct _A{}; + )cpp"; + auto Clean = [](PrintingPolicy &Policy) { + Policy.CleanUglifiedParameters = true; + }; + + ASSERT_TRUE(PrintedDeclCXX17Matches( + Code, classTemplateDecl(hasName("_A")).bind("id"), + "template class _Container> " + "struct _A {}")); + ASSERT_TRUE(PrintedDeclCXX17Matches( + Code, classTemplateDecl(hasName("_A")).bind("id"), + "template class Container> " + "struct _A {}", + Clean)); +} + TEST(DeclPrinter, TestStaticAssert1) { ASSERT_TRUE(PrintedDeclCXX17Matches("static_assert(true);", staticAssertDecl().bind("id"), diff --git a/clang/unittests/AST/StmtPrinterTest.cpp b/clang/unittests/AST/StmtPrinterTest.cpp --- a/clang/unittests/AST/StmtPrinterTest.cpp +++ b/clang/unittests/AST/StmtPrinterTest.cpp @@ -263,3 +263,22 @@ [](PrintingPolicy &PP) { PP.TerseOutput = true; })); } + +TEST(StmtPrinter, ParamsUglified) { + llvm::StringLiteral Code = R"cpp( + template class _C> + auto foo(int __j) { + return typename _C<_T>::_F(_I, __j); + } + )cpp"; + auto Clean = [](PrintingPolicy &Policy) { + Policy.CleanUglifiedParameters = true; + }; + + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX14, Code, + returnStmt().bind("id"), + "return typename _C<_T>::_F(_I, __j);\n")); + ASSERT_TRUE( + PrintedStmtCXXMatches(StdVer::CXX14, Code, returnStmt().bind("id"), + "return typename C::_F(I, j);\n", Clean)); +} diff --git a/clang/unittests/AST/TypePrinterTest.cpp b/clang/unittests/AST/TypePrinterTest.cpp --- a/clang/unittests/AST/TypePrinterTest.cpp +++ b/clang/unittests/AST/TypePrinterTest.cpp @@ -62,4 +62,21 @@ ASSERT_TRUE(PrintedTypeMatches( Code, {}, Matcher, "const N::Type &", [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = true; })); -} \ No newline at end of file +} + +TEST(TypePrinter, ParamsUglified) { + llvm::StringLiteral Code = R"cpp( + template class __f> + const __f<_Tp&> *A = nullptr; + )cpp"; + auto Clean = [](PrintingPolicy &Policy) { + Policy.CleanUglifiedParameters = true; + }; + + ASSERT_TRUE(PrintedTypeMatches(Code, {}, + varDecl(hasType(qualType().bind("id"))), + "const __f<_Tp &> *", nullptr)); + ASSERT_TRUE(PrintedTypeMatches(Code, {}, + varDecl(hasType(qualType().bind("id"))), + "const f *", Clean)); +}