Skip to content

[clang][ExtractAPI] Correctly generate declaration fragments for non-type template parameters #91958

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 189 additions & 37 deletions clang/lib/ExtractAPI/DeclarationFragments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@
//===----------------------------------------------------------------------===//

#include "clang/ExtractAPI/DeclarationFragments.h"
#include "clang/AST/ASTFwd.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>

using namespace clang::extractapi;
using namespace llvm;
Expand Down Expand Up @@ -386,6 +392,25 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
getFragmentsForType(AT->getElementType(), Context, After));
}

if (const TemplateSpecializationType *TemplSpecTy =
dyn_cast<TemplateSpecializationType>(T)) {
const auto TemplName = TemplSpecTy->getTemplateName();
std::string Str;
raw_string_ostream Stream(Str);
TemplName.print(Stream, Context.getPrintingPolicy(),
TemplateName::Qualified::AsWritten);
SmallString<64> USR("");
if (const auto *TemplDecl = TemplName.getAsTemplateDecl())
index::generateUSRForDecl(TemplDecl, USR);

return Fragments
.append(Str, DeclarationFragments::FragmentKind::TypeIdentifier, USR)
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
TemplSpecTy->template_arguments(), Context, std::nullopt))
.append(">", DeclarationFragments::FragmentKind::Text);
}

// Everything we care about has been handled now, reduce to the canonical
// unqualified base type.
QualType Base = T->getCanonicalTypeUnqualified();
Expand Down Expand Up @@ -650,7 +675,6 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForBlock(
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
DeclarationFragments Fragments;
// FIXME: Handle template specialization
switch (Func->getStorageClass()) {
case SC_None:
case SC_PrivateExtern:
Expand Down Expand Up @@ -952,27 +976,84 @@ DeclarationFragmentsBuilder::getFragmentsForTemplateParameters(
Fragments.append(",", DeclarationFragments::FragmentKind::Text)
.appendSpace();

const auto *TemplateParam =
dyn_cast<TemplateTypeParmDecl>(ParameterArray[i]);
if (!TemplateParam)
continue;
if (TemplateParam->hasTypeConstraint())
Fragments.append(TemplateParam->getTypeConstraint()
->getNamedConcept()
->getName()
.str(),
DeclarationFragments::FragmentKind::TypeIdentifier);
else if (TemplateParam->wasDeclaredWithTypename())
Fragments.append("typename", DeclarationFragments::FragmentKind::Keyword);
else
Fragments.append("class", DeclarationFragments::FragmentKind::Keyword);

if (TemplateParam->isParameterPack())
Fragments.append("...", DeclarationFragments::FragmentKind::Text);

Fragments.appendSpace().append(
TemplateParam->getName(),
DeclarationFragments::FragmentKind::GenericParameter);
if (const auto *TemplateParam =
dyn_cast<TemplateTypeParmDecl>(ParameterArray[i])) {
if (TemplateParam->hasTypeConstraint())
Fragments.append(TemplateParam->getTypeConstraint()
->getNamedConcept()
->getName()
.str(),
DeclarationFragments::FragmentKind::TypeIdentifier);
else if (TemplateParam->wasDeclaredWithTypename())
Fragments.append("typename",
DeclarationFragments::FragmentKind::Keyword);
else
Fragments.append("class", DeclarationFragments::FragmentKind::Keyword);

if (TemplateParam->isParameterPack())
Fragments.append("...", DeclarationFragments::FragmentKind::Text);

if (!TemplateParam->getName().empty())
Fragments.appendSpace().append(
TemplateParam->getName(),
DeclarationFragments::FragmentKind::GenericParameter);

if (TemplateParam->hasDefaultArgument()) {
DeclarationFragments After;
Fragments.append(" = ", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForType(TemplateParam->getDefaultArgument(),
TemplateParam->getASTContext(), After));
Fragments.append(std::move(After));
}
} else if (const auto *NTP =
dyn_cast<NonTypeTemplateParmDecl>(ParameterArray[i])) {
DeclarationFragments After;
const auto TyFragments =
getFragmentsForType(NTP->getType(), NTP->getASTContext(), After);
Fragments.append(std::move(TyFragments)).append(std::move(After));

if (NTP->isParameterPack())
Fragments.append("...", DeclarationFragments::FragmentKind::Text);

if (!NTP->getName().empty())
Fragments.appendSpace().append(
NTP->getName(),
DeclarationFragments::FragmentKind::GenericParameter);

if (NTP->hasDefaultArgument()) {
SmallString<8> ExprStr;
raw_svector_ostream Output(ExprStr);
NTP->getDefaultArgument()->printPretty(
Output, nullptr, NTP->getASTContext().getPrintingPolicy());
Fragments.append(" = ", DeclarationFragments::FragmentKind::Text)
.append(ExprStr, DeclarationFragments::FragmentKind::Text);
}
} else if (const auto *TTP =
dyn_cast<TemplateTemplateParmDecl>(ParameterArray[i])) {
Fragments.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateParameters(
TTP->getTemplateParameters()->asArray()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(TTP->wasDeclaredWithTypename() ? "typename" : "class",
DeclarationFragments::FragmentKind::Keyword);

if (TTP->isParameterPack())
Fragments.append("...", DeclarationFragments::FragmentKind::Text);

if (!TTP->getName().empty())
Fragments.appendSpace().append(
TTP->getName(),
DeclarationFragments::FragmentKind::GenericParameter);
if (TTP->hasDefaultArgument()) {
const auto Default = TTP->getDefaultArgument();
Fragments.append(" = ", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
{Default.getArgument()}, TTP->getASTContext(), {Default}));
}
}
}
return Fragments;
}
Expand All @@ -993,23 +1074,85 @@ DeclarationFragmentsBuilder::getFragmentsForTemplateArguments(
Fragments.append(",", DeclarationFragments::FragmentKind::Text)
.appendSpace();

std::string Type = TemplateArguments[i].getAsType().getAsString();
DeclarationFragments After;
DeclarationFragments ArgumentFragment =
getFragmentsForType(TemplateArguments[i].getAsType(), Context, After);

if (StringRef(ArgumentFragment.begin()->Spelling)
.starts_with("type-parameter")) {
std::string ProperArgName = TemplateArgumentLocs.value()[i]
.getTypeSourceInfo()
->getType()
.getAsString();
ArgumentFragment.begin()->Spelling.swap(ProperArgName);
const auto &CTA = TemplateArguments[i];
switch (CTA.getKind()) {
case TemplateArgument::Type: {
DeclarationFragments After;
DeclarationFragments ArgumentFragment =
getFragmentsForType(CTA.getAsType(), Context, After);

if (StringRef(ArgumentFragment.begin()->Spelling)
.starts_with("type-parameter")) {
std::string ProperArgName = TemplateArgumentLocs.value()[i]
.getTypeSourceInfo()
->getType()
.getAsString();
ArgumentFragment.begin()->Spelling.swap(ProperArgName);
}
Fragments.append(std::move(ArgumentFragment));
break;
}
Fragments.append(std::move(ArgumentFragment));
case TemplateArgument::Declaration: {
const auto *VD = CTA.getAsDecl();
SmallString<128> USR;
index::generateUSRForDecl(VD, USR);
Fragments.append(VD->getNameAsString(),
DeclarationFragments::FragmentKind::Identifier, USR);
break;
}
case TemplateArgument::NullPtr:
Fragments.append("nullptr", DeclarationFragments::FragmentKind::Keyword);
break;

if (TemplateArguments[i].isPackExpansion())
Fragments.append("...", DeclarationFragments::FragmentKind::Text);
case TemplateArgument::Integral: {
SmallString<4> Str;
CTA.getAsIntegral().toString(Str);
Fragments.append(Str, DeclarationFragments::FragmentKind::Text);
break;
}

case TemplateArgument::StructuralValue: {
const auto SVTy = CTA.getStructuralValueType();
Fragments.append(CTA.getAsStructuralValue().getAsString(Context, SVTy),
DeclarationFragments::FragmentKind::Text);
break;
}

case TemplateArgument::TemplateExpansion:
case TemplateArgument::Template: {
std::string Str;
raw_string_ostream Stream(Str);
CTA.getAsTemplate().print(Stream, Context.getPrintingPolicy());
SmallString<64> USR("");
if (const auto *TemplDecl =
CTA.getAsTemplateOrTemplatePattern().getAsTemplateDecl())
index::generateUSRForDecl(TemplDecl, USR);
Fragments.append(Str, DeclarationFragments::FragmentKind::TypeIdentifier,
USR);
if (CTA.getKind() == TemplateArgument::TemplateExpansion)
Fragments.append("...", DeclarationFragments::FragmentKind::Text);
break;
}

case TemplateArgument::Pack:
Fragments.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(CTA.pack_elements(), Context,
{}))
.append(">", DeclarationFragments::FragmentKind::Text);
break;

case TemplateArgument::Expression: {
SmallString<8> ExprStr;
raw_svector_ostream Output(ExprStr);
CTA.getAsExpr()->printPretty(Output, nullptr,
Context.getPrintingPolicy());
Fragments.append(ExprStr, DeclarationFragments::FragmentKind::Text);
break;
}

case TemplateArgument::Null:
break;
}
}
return Fragments;
}
Expand All @@ -1019,10 +1162,12 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForConcept(
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateParameters(
Concept->getTemplateParameters()->asArray()))
.append("> ", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append("concept", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append(Concept->getName().str(),
Expand All @@ -1035,6 +1180,7 @@ DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(
const RedeclarableTemplateDecl *RedeclarableTemplate) {
DeclarationFragments Fragments;
Fragments.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateParameters(
RedeclarableTemplate->getTemplateParameters()->asArray()))
Expand All @@ -1057,6 +1203,7 @@ DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
Expand All @@ -1077,6 +1224,7 @@ DeclarationFragmentsBuilder::getFragmentsForClassTemplatePartialSpecialization(
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateParameters(
Decl->getTemplateParameters()->asArray()))
Expand All @@ -1099,6 +1247,7 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplateSpecialization(
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
Expand All @@ -1118,6 +1267,7 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplatePartialSpecialization(
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
// Partial specs may have new params.
.append(getFragmentsForTemplateParameters(
Expand All @@ -1140,6 +1290,7 @@ DeclarationFragmentsBuilder::getFragmentsForFunctionTemplate(
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
// Partial specs may have new params.
.append(getFragmentsForTemplateParameters(
Expand All @@ -1156,6 +1307,7 @@ DeclarationFragmentsBuilder::getFragmentsForFunctionTemplateSpecialization(
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<>", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(DeclarationFragmentsBuilder::getFragmentsForFunction(Decl));
Expand Down
2 changes: 1 addition & 1 deletion clang/test/ExtractAPI/class_template.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ template<typename T> class Foo {};
},
{
"kind": "text",
"spelling": "<"
"spelling": " <"
},
{
"kind": "keyword",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ template<typename T> class Foo : public T {};
},
{
"kind": "text",
"spelling": "<"
"spelling": " <"
},
{
"kind": "keyword",
Expand Down
4 changes: 2 additions & 2 deletions clang/test/ExtractAPI/class_template_partial_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ template<typename Z> class Foo<Z, int> {};
},
{
"kind": "text",
"spelling": "<"
"spelling": " <"
},
{
"kind": "keyword",
Expand Down Expand Up @@ -161,7 +161,7 @@ template<typename Z> class Foo<Z, int> {};
},
{
"kind": "text",
"spelling": "<"
"spelling": " <"
},
{
"kind": "keyword",
Expand Down
4 changes: 2 additions & 2 deletions clang/test/ExtractAPI/class_template_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ template<> class Foo<int> {};
},
{
"kind": "text",
"spelling": "<"
"spelling": " <"
},
{
"kind": "keyword",
Expand Down Expand Up @@ -140,7 +140,7 @@ template<> class Foo<int> {};
},
{
"kind": "text",
"spelling": "<> "
"spelling": " <> "
},
{
"kind": "keyword",
Expand Down
2 changes: 1 addition & 1 deletion clang/test/ExtractAPI/concept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ template<typename T> concept Foo = true;
},
{
"kind": "text",
"spelling": "<"
"spelling": " <"
},
{
"kind": "keyword",
Expand Down
Loading
Loading