Index: clang-tools-extra/clang-doc/BitcodeReader.cpp
===================================================================
--- clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -350,6 +350,8 @@
return decodeRecord(R, I->USR, Blob);
case REFERENCE_NAME:
return decodeRecord(R, I->Name, Blob);
+ case REFERENCE_QUAL_NAME:
+ return decodeRecord(R, I->QualName, Blob);
case REFERENCE_TYPE:
return decodeRecord(R, I->RefType, Blob);
case REFERENCE_PATH:
@@ -362,6 +364,35 @@
}
}
+llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
+ TemplateInfo *I) {
+ // Currently there are no child records of TemplateInfo (only child blocks).
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "invalid field for TemplateParamInfo");
+}
+
+llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
+ TemplateSpecializationInfo *I) {
+ switch (ID) {
+ case TEMPLATE_SPECIALIZATION_OF:
+ return decodeRecord(R, I->SpecializationOf, Blob);
+ default:
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "invalid field for TemplateParamInfo");
+ }
+}
+
+llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
+ TemplateParamInfo *I) {
+ switch (ID) {
+ case TEMPLATE_PARAM_CONTENTS:
+ return decodeRecord(R, I->Contents, Blob);
+ default:
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "invalid field for TemplateParamInfo");
+ }
+}
+
template llvm::Expected getCommentInfo(T I) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain CommentInfo");
@@ -594,6 +625,45 @@
I->Children.Functions.emplace_back(std::move(R));
}
+// TemplateParam children. These go into either a TemplateInfo (for template
+// parameters) or TemplateSpecializationInfo (for the specialization's
+// parameters).
+template void addTemplateParam(T I, TemplateParamInfo &&P) {
+ llvm::errs() << "invalid container for template parameter";
+ exit(1);
+}
+template <> void addTemplateParam(TemplateInfo *I, TemplateParamInfo &&P) {
+ I->Params.emplace_back(std::move(P));
+}
+template <>
+void addTemplateParam(TemplateSpecializationInfo *I, TemplateParamInfo &&P) {
+ I->Params.emplace_back(std::move(P));
+}
+
+// Template info. These apply to either records or functions.
+template void addTemplate(T I, TemplateInfo &&P) {
+ llvm::errs() << "invalid container for template info";
+ exit(1);
+}
+template <> void addTemplate(RecordInfo *I, TemplateInfo &&P) {
+ I->Template.emplace(std::move(P));
+}
+template <> void addTemplate(FunctionInfo *I, TemplateInfo &&P) {
+ I->Template.emplace(std::move(P));
+}
+
+// Template specializations go only into template records.
+template
+void addTemplateSpecialization(T I, TemplateSpecializationInfo &&TSI) {
+ llvm::errs() << "invalid container for template specialization info";
+ exit(1);
+}
+template <>
+void addTemplateSpecialization(TemplateInfo *I,
+ TemplateSpecializationInfo &&TSI) {
+ I->Specialization.emplace(std::move(TSI));
+}
+
// Read records from bitcode into a given info.
template
llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, T I) {
@@ -718,6 +788,27 @@
addChild(I, std::move(EV));
return llvm::Error::success();
}
+ case BI_TEMPLATE_BLOCK_ID: {
+ TemplateInfo TI;
+ if (auto Err = readBlock(ID, &TI))
+ return Err;
+ addTemplate(I, std::move(TI));
+ return llvm::Error::success();
+ }
+ case BI_TEMPLATE_SPECIALIZATION_BLOCK_ID: {
+ TemplateSpecializationInfo TSI;
+ if (auto Err = readBlock(ID, &TSI))
+ return Err;
+ addTemplateSpecialization(I, std::move(TSI));
+ return llvm::Error::success();
+ }
+ case BI_TEMPLATE_PARAM_BLOCK_ID: {
+ TemplateParamInfo TPI;
+ if (auto Err = readBlock(ID, &TPI))
+ return Err;
+ addTemplateParam(I, std::move(TPI));
+ return llvm::Error::success();
+ }
case BI_TYPEDEF_BLOCK_ID: {
TypedefInfo TI;
if (auto Err = readBlock(ID, &TI))
Index: clang-tools-extra/clang-doc/BitcodeWriter.h
===================================================================
--- clang-tools-extra/clang-doc/BitcodeWriter.h
+++ clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -17,7 +17,6 @@
#include "Representation.h"
#include "clang/AST/AST.h"
-#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
@@ -64,6 +63,9 @@
BI_FUNCTION_BLOCK_ID,
BI_COMMENT_BLOCK_ID,
BI_REFERENCE_BLOCK_ID,
+ BI_TEMPLATE_BLOCK_ID,
+ BI_TEMPLATE_SPECIALIZATION_BLOCK_ID,
+ BI_TEMPLATE_PARAM_BLOCK_ID,
BI_TYPEDEF_BLOCK_ID,
BI_LAST,
BI_FIRST = BI_VERSION_BLOCK_ID
@@ -121,9 +123,12 @@
BASE_RECORD_IS_PARENT,
REFERENCE_USR,
REFERENCE_NAME,
+ REFERENCE_QUAL_NAME,
REFERENCE_TYPE,
REFERENCE_PATH,
REFERENCE_FIELD,
+ TEMPLATE_PARAM_CONTENTS,
+ TEMPLATE_SPECIALIZATION_OF,
TYPEDEF_USR,
TYPEDEF_NAME,
TYPEDEF_DEFLOCATION,
@@ -169,6 +174,9 @@
void emitBlock(const FieldTypeInfo &B);
void emitBlock(const MemberTypeInfo &T);
void emitBlock(const CommentInfo &B);
+ void emitBlock(const TemplateInfo &T);
+ void emitBlock(const TemplateSpecializationInfo &T);
+ void emitBlock(const TemplateParamInfo &T);
void emitBlock(const Reference &B, FieldId F);
private:
@@ -215,7 +223,7 @@
void emitRecord(bool Value, RecordId ID);
void emitRecord(int Value, RecordId ID);
void emitRecord(unsigned Value, RecordId ID);
- void emitRecord(llvm::APSInt Value, RecordId ID);
+ void emitRecord(const TemplateInfo &Templ);
bool prepRecordData(RecordId ID, bool ShouldEmit = true);
// Emission of appropriate abbreviation type.
Index: clang-tools-extra/clang-doc/BitcodeWriter.cpp
===================================================================
--- clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -121,7 +121,10 @@
{BI_BASE_RECORD_BLOCK_ID, "BaseRecordBlock"},
{BI_FUNCTION_BLOCK_ID, "FunctionBlock"},
{BI_COMMENT_BLOCK_ID, "CommentBlock"},
- {BI_REFERENCE_BLOCK_ID, "ReferenceBlock"}};
+ {BI_REFERENCE_BLOCK_ID, "ReferenceBlock"},
+ {BI_TEMPLATE_BLOCK_ID, "TemplateBlock"},
+ {BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, "TemplateSpecializationBlock"},
+ {BI_TEMPLATE_PARAM_BLOCK_ID, "TemplateParamBlock"}};
assert(Inits.size() == BlockIdCount);
for (const auto &Init : Inits)
BlockIdNameMap[Init.first] = Init.second;
@@ -186,9 +189,12 @@
{FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}},
{REFERENCE_USR, {"USR", &SymbolIDAbbrev}},
{REFERENCE_NAME, {"Name", &StringAbbrev}},
+ {REFERENCE_QUAL_NAME, {"QualName", &StringAbbrev}},
{REFERENCE_TYPE, {"RefType", &IntAbbrev}},
{REFERENCE_PATH, {"Path", &StringAbbrev}},
{REFERENCE_FIELD, {"Field", &IntAbbrev}},
+ {TEMPLATE_PARAM_CONTENTS, {"Contents", &StringAbbrev}},
+ {TEMPLATE_SPECIALIZATION_OF, {"SpecializationOf", &SymbolIDAbbrev}},
{TYPEDEF_USR, {"USR", &SymbolIDAbbrev}},
{TYPEDEF_NAME, {"Name", &StringAbbrev}},
{TYPEDEF_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
@@ -244,8 +250,12 @@
FUNCTION_ACCESS, FUNCTION_IS_METHOD}},
// Reference Block
{BI_REFERENCE_BLOCK_ID,
- {REFERENCE_USR, REFERENCE_NAME, REFERENCE_TYPE, REFERENCE_PATH,
- REFERENCE_FIELD}}};
+ {REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE,
+ REFERENCE_PATH, REFERENCE_FIELD}},
+ // Template Blocks.
+ {BI_TEMPLATE_BLOCK_ID, {}},
+ {BI_TEMPLATE_PARAM_BLOCK_ID, {TEMPLATE_PARAM_CONTENTS}},
+ {BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, {TEMPLATE_SPECIALIZATION_OF}}};
// AbbreviationMap
@@ -378,6 +388,8 @@
Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record);
}
+void ClangDocBitcodeWriter::emitRecord(const TemplateInfo &Templ) {}
+
bool ClangDocBitcodeWriter::prepRecordData(RecordId ID, bool ShouldEmit) {
assert(RecordIdNameMap[ID] && "Unknown RecordId.");
if (!ShouldEmit)
@@ -416,6 +428,7 @@
StreamSubBlockGuard Block(Stream, BI_REFERENCE_BLOCK_ID);
emitRecord(R.USR, REFERENCE_USR);
emitRecord(R.Name, REFERENCE_NAME);
+ emitRecord(R.QualName, REFERENCE_QUAL_NAME);
emitRecord((unsigned)R.RefType, REFERENCE_TYPE);
emitRecord(R.Path, REFERENCE_PATH);
emitRecord((unsigned)Field, REFERENCE_FIELD);
@@ -556,6 +569,8 @@
emitBlock(C);
for (const auto &C : I.Children.Typedefs)
emitBlock(C);
+ if (I.Template)
+ emitBlock(*I.Template);
}
void ClangDocBitcodeWriter::emitBlock(const BaseRecordInfo &I) {
@@ -591,6 +606,28 @@
emitBlock(I.ReturnType);
for (const auto &N : I.Params)
emitBlock(N);
+ if (I.Template)
+ emitBlock(*I.Template);
+}
+
+void ClangDocBitcodeWriter::emitBlock(const TemplateInfo &T) {
+ StreamSubBlockGuard Block(Stream, BI_TEMPLATE_BLOCK_ID);
+ for (const auto &P : T.Params)
+ emitBlock(P);
+ if (T.Specialization)
+ emitBlock(*T.Specialization);
+}
+
+void ClangDocBitcodeWriter::emitBlock(const TemplateSpecializationInfo &T) {
+ StreamSubBlockGuard Block(Stream, BI_TEMPLATE_SPECIALIZATION_BLOCK_ID);
+ emitRecord(T.SpecializationOf, TEMPLATE_SPECIALIZATION_OF);
+ for (const auto &P : T.Params)
+ emitBlock(P);
+}
+
+void ClangDocBitcodeWriter::emitBlock(const TemplateParamInfo &T) {
+ StreamSubBlockGuard Block(Stream, BI_TEMPLATE_PARAM_BLOCK_ID);
+ emitRecord(T.Contents, TEMPLATE_PARAM_CONTENTS);
}
bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) {
Index: clang-tools-extra/clang-doc/Representation.h
===================================================================
--- clang-tools-extra/clang-doc/Representation.h
+++ clang-tools-extra/clang-doc/Representation.h
@@ -116,13 +116,21 @@
};
struct Reference {
+ // This variant (that takes no qualified name parameter) uses the Name as the
+ // QualName (very useful in unit tests to reduce verbosity). This can't use
+ // the empty string as a default because the global namespace uses
+ // "GlobalNamespace" as the name, but should have an empty QualName
+ // (corresponding to the name in code).
Reference(SymbolID USR = SymbolID(), StringRef Name = StringRef(),
- InfoType IT = InfoType::IT_default, StringRef Path = StringRef())
- : USR(USR), Name(Name), RefType(IT), Path(Path) {}
+ InfoType IT = InfoType::IT_default)
+ : USR(USR), Name(Name), QualName(Name), RefType(IT) {}
+ Reference(SymbolID USR, StringRef Name, InfoType IT, StringRef QualName,
+ StringRef Path = StringRef())
+ : USR(USR), Name(Name), QualName(QualName), RefType(IT), Path(Path) {}
bool operator==(const Reference &Other) const {
- return std::tie(USR, Name, RefType) ==
- std::tie(Other.USR, Other.Name, Other.RefType);
+ return std::tie(USR, Name, QualName, RefType) ==
+ std::tie(Other.USR, Other.Name, QualName, Other.RefType);
}
bool mergeable(const Reference &Other);
@@ -135,7 +143,17 @@
llvm::SmallString<16> getFileBaseName() const;
SymbolID USR = SymbolID(); // Unique identifier for referenced decl
- SmallString<16> Name; // Name of type (possibly unresolved).
+
+ // Name of type (possibly unresolved). Not including namespaces or template
+ // parameters (so for a std::vector this would be "vector"). See also
+ // QualName.
+ SmallString<16> Name;
+
+ // Full qualified name of this type, including namespaces and template
+ // parameter (for example this could be "std::vector"). Contrast to
+ // Name.
+ SmallString<16> QualName;
+
InfoType RefType = InfoType::IT_default; // Indicates the type of this
// Reference (namespace, record,
// function, enum, default).
@@ -168,13 +186,51 @@
// Convenience constructor for when there is no symbol ID or info type
// (normally used for built-in types in tests).
TypeInfo(StringRef Name, StringRef Path = StringRef())
- : Type(SymbolID(), Name, InfoType::IT_default, Path) {}
+ : Type(SymbolID(), Name, InfoType::IT_default, Name, Path) {}
bool operator==(const TypeInfo &Other) const { return Type == Other.Type; }
Reference Type; // Referenced type in this info.
};
+// Represents one template parameter.
+//
+// This is a very simple serialization of the text of the source code of the
+// template parameter. Representing the full range of template parameter kinds
+// is quite involved. Currently the documentation uses of this data just involve
+// echoing it back to reconstruct the declaration of the class or function, so
+// the full contents is sufficient.
+//
+// It is separated into this separate class so that other attributes like the
+// name and default value can be easily added as needed.
+struct TemplateParamInfo {
+ TemplateParamInfo() = default;
+ explicit TemplateParamInfo(StringRef Contents) : Contents(Contents) {}
+
+ // The literal contents of the code for everything that specifies this
+ // template parameter. This will include all components, so for a template
+ // declaration it could be "typename T = int".
+ SmallString<16> Contents;
+};
+
+struct TemplateSpecializationInfo {
+ // Indicates the declaration that this specializes.
+ SymbolID SpecializationOf;
+
+ // Template parameters applying to the specialized record/function.
+ std::vector Params;
+};
+
+// This struct will be a member of the record/function that is the template or
+// specialization.
+struct TemplateInfo {
+ // May be empty for non-partial specializations.
+ std::vector Params;
+
+ // Set when this is a specialization of another record/function.
+ llvm::Optional Specialization;
+};
+
// Info for field types.
struct FieldTypeInfo : public TypeInfo {
FieldTypeInfo() = default;
@@ -316,6 +372,13 @@
// with value 0 to be used as the default.
// (AS_public = 0, AS_protected = 1, AS_private = 2, AS_none = 3)
AccessSpecifier Access = AccessSpecifier::AS_public;
+
+ // Full qualified name of this function, including namespaces and template
+ // specializations.
+ SmallString<16> FullName;
+
+ // When present, this function is a template or specialization.
+ llvm::Optional Template;
};
// TODO: Expand to allow for documenting templating, inheritance access,
@@ -331,6 +394,13 @@
// Type of this record (struct, class, union, interface).
TagTypeKind TagType = TagTypeKind::TTK_Struct;
+ // Full qualified name of this record, including namespaces and template
+ // specializations.
+ SmallString<16> FullName;
+
+ // When present, this record is a template or specialization.
+ llvm::Optional Template;
+
// Indicates if the record was declared using a typedef. Things like anonymous
// structs in a typedef:
// typedef struct { ... } foo_t;
@@ -432,7 +502,7 @@
Index(StringRef Name, StringRef JumpToSection)
: Reference(SymbolID(), Name), JumpToSection(JumpToSection) {}
Index(SymbolID USR, StringRef Name, InfoType IT, StringRef Path)
- : Reference(USR, Name, IT, Path) {}
+ : Reference(USR, Name, IT, Name, Path) {}
// This is used to look for a USR in a vector of Indexes using std::find
bool operator==(const SymbolID &Other) const { return USR == Other; }
bool operator<(const Index &Other) const;
Index: clang-tools-extra/clang-doc/Representation.cpp
===================================================================
--- clang-tools-extra/clang-doc/Representation.cpp
+++ clang-tools-extra/clang-doc/Representation.cpp
@@ -250,6 +250,8 @@
reduceChildren(Children.Enums, std::move(Other.Children.Enums));
reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
SymbolInfo::merge(std::move(Other));
+ if (!Template)
+ Template = Other.Template;
}
void EnumInfo::merge(EnumInfo &&Other) {
@@ -274,6 +276,8 @@
if (Params.empty())
Params = std::move(Other.Params);
SymbolInfo::merge(std::move(Other));
+ if (!Template)
+ Template = Other.Template;
}
void TypedefInfo::merge(TypedefInfo &&Other) {
Index: clang-tools-extra/clang-doc/Serialize.cpp
===================================================================
--- clang-tools-extra/clang-doc/Serialize.cpp
+++ clang-tools-extra/clang-doc/Serialize.cpp
@@ -250,7 +250,7 @@
IT = InfoType::IT_default;
}
return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
- getInfoRelativePath(TD)));
+ T.getAsString(), getInfoRelativePath(TD)));
}
static bool isPublic(const clang::AccessSpecifier AS,
@@ -281,12 +281,12 @@
// See MakeAndInsertIntoParent().
static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
- getInfoRelativePath(Info.Namespace));
+ Info.Name, getInfoRelativePath(Info.Namespace));
}
static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
- getInfoRelativePath(Info.Namespace));
+ Info.Name, getInfoRelativePath(Info.Namespace));
}
static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
@@ -405,10 +405,7 @@
for (const ParmVarDecl *P : D->parameters()) {
FieldTypeInfo &FieldInfo = I.Params.emplace_back(
getTypeInfoForType(P->getOriginalType()), P->getNameAsString());
-
- if (const Expr *DefaultArg = P->getDefaultArg()) {
- FieldInfo.DefaultValue = getSourceCode(D, DefaultArg->getSourceRange());
- }
+ FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
}
}
@@ -424,18 +421,19 @@
if (const auto *Ty = B.getType()->getAs()) {
const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
- InfoType::IT_record);
+ InfoType::IT_record, B.getType().getAsString());
} else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
- InfoType::IT_record, getInfoRelativePath(P));
+ InfoType::IT_record, P->getQualifiedNameAsString(),
+ getInfoRelativePath(P));
else
I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
}
for (const CXXBaseSpecifier &B : D->vbases()) {
if (const RecordDecl *P = getRecordDeclForType(B.getType()))
- I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
- InfoType::IT_record,
- getInfoRelativePath(P));
+ I.VirtualParents.emplace_back(
+ getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
+ P->getQualifiedNameAsString(), getInfoRelativePath(P));
else
I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
}
@@ -455,16 +453,19 @@
} else
Namespace = N->getNameAsString();
Namespaces.emplace_back(getUSRForDecl(N), Namespace,
- InfoType::IT_namespace);
+ InfoType::IT_namespace,
+ N->getQualifiedNameAsString());
} else if (const auto *N = dyn_cast(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
- InfoType::IT_record);
+ InfoType::IT_record,
+ N->getQualifiedNameAsString());
else if (const auto *N = dyn_cast(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
- InfoType::IT_function);
+ InfoType::IT_function,
+ N->getQualifiedNameAsString());
else if (const auto *N = dyn_cast(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
- InfoType::IT_enum);
+ InfoType::IT_enum, N->getQualifiedNameAsString());
} while ((DC = DC->getParent()));
// The global namespace should be added to the list of namespaces if the decl
// corresponds to a Record and if it doesn't have any namespace (because this
@@ -476,6 +477,30 @@
InfoType::IT_namespace);
}
+void PopulateTemplateParameters(llvm::Optional &TemplateInfo,
+ const clang::Decl *D) {
+ if (const TemplateParameterList *ParamList =
+ D->getDescribedTemplateParams()) {
+ if (!TemplateInfo) {
+ TemplateInfo.emplace();
+ }
+ for (const NamedDecl *ND : *ParamList) {
+ TemplateInfo->Params.emplace_back(
+ getSourceCode(ND, ND->getSourceRange()));
+ }
+ }
+}
+
+TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D,
+ const TemplateArgument &Arg) {
+ // The TemplateArgument's pretty printing handles all the normal cases
+ // well enough for our requirements.
+ std::string Str;
+ llvm::raw_string_ostream Stream(Str);
+ Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
+ return TemplateParamInfo(Str);
+}
+
template
static void populateInfo(Info &I, const T *D, const FullComment *C,
bool &IsInAnonymousNamespace) {
@@ -508,6 +533,26 @@
IsInAnonymousNamespace);
I.ReturnType = getTypeInfoForType(D->getReturnType());
parseParameters(I, D);
+
+ PopulateTemplateParameters(I.Template, D);
+
+ // Handle function template specializations.
+ if (const FunctionTemplateSpecializationInfo *FTSI =
+ D->getTemplateSpecializationInfo()) {
+ if (!I.Template)
+ I.Template.emplace();
+ I.Template->Specialization.emplace();
+ auto &Specialization = *I.Template->Specialization;
+
+ Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
+
+ // Template parameters to the specialization.
+ if (FTSI->TemplateArguments) {
+ for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
+ Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
+ }
+ }
+ }
}
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
@@ -627,6 +672,46 @@
}
I->Path = getInfoRelativePath(I->Namespace);
+ PopulateTemplateParameters(I->Template, D);
+
+ // Full and partial specializations.
+ if (auto *CTSD = dyn_cast(D)) {
+ if (!I->Template)
+ I->Template.emplace();
+ I->Template->Specialization.emplace();
+ auto &Specialization = *I->Template->Specialization;
+
+ // What this is a specialization of.
+ auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
+ if (SpecOf.is()) {
+ Specialization.SpecializationOf =
+ getUSRForDecl(SpecOf.get());
+ } else if (SpecOf.is()) {
+ Specialization.SpecializationOf =
+ getUSRForDecl(SpecOf.get());
+ }
+
+ // Parameters to the specilization. For partial specializations, get the
+ // parameters "as written" from the ClassTemplatePartialSpecializationDecl
+ // because the non-explicit template parameters will have generated internal
+ // placeholder names rather than the names the user typed that match the
+ // template parameters.
+ if (const ClassTemplatePartialSpecializationDecl *CTPSD =
+ dyn_cast(D)) {
+ if (const ASTTemplateArgumentListInfo *AsWritten =
+ CTPSD->getTemplateArgsAsWritten()) {
+ for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) {
+ Specialization.Params.emplace_back(
+ getSourceCode(D, (*AsWritten)[i].getSourceRange()));
+ }
+ }
+ } else {
+ for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
+ Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
+ }
+ }
+ }
+
// Records are inserted into the parent by reference, so we need to return
// both the parent and the record itself.
auto Parent = MakeAndInsertIntoParent(*I);
@@ -669,7 +754,8 @@
SymbolID ParentUSR = getUSRForDecl(Parent);
Func.Parent =
- Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record};
+ Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
+ Parent->getQualifiedNameAsString()};
Func.Access = D->getAccess();
// Info is wrapped in its parent scope so is returned in the second position.
@@ -731,8 +817,10 @@
return {};
Enum.Scoped = D->isScoped();
- if (D->isFixed())
- Enum.BaseType = TypeInfo(D->getIntegerType().getAsString());
+ if (D->isFixed()) {
+ auto Name = D->getIntegerType().getAsString();
+ Enum.BaseType = TypeInfo(Name, Name);
+ }
parseEnumerators(Enum, D);
// Info is wrapped in its parent scope so is returned in the second position.
Index: clang-tools-extra/clang-doc/YAMLGenerator.cpp
===================================================================
--- clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ clang-tools-extra/clang-doc/YAMLGenerator.cpp
@@ -9,6 +9,7 @@
//===----------------------------------------------------------------------===//
#include "Generators.h"
+#include "Representation.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
@@ -23,6 +24,7 @@
LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(EnumValueInfo)
+LLVM_YAML_IS_SEQUENCE_VECTOR(TemplateParamInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(TypedefInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr)
@@ -142,6 +144,7 @@
IO.mapOptional("ChildFunctions", I.Children.Functions);
IO.mapOptional("ChildEnums", I.Children.Enums);
IO.mapOptional("ChildTypedefs", I.Children.Typedefs);
+ IO.mapOptional("Template", I.Template);
}
static void CommentInfoMapping(IO &IO, CommentInfo &I) {
@@ -174,6 +177,7 @@
static void mapping(IO &IO, Reference &Ref) {
IO.mapOptional("Type", Ref.RefType, InfoType::IT_default);
IO.mapOptional("Name", Ref.Name, SmallString<16>());
+ IO.mapOptional("QualName", Ref.QualName, SmallString<16>());
IO.mapOptional("USR", Ref.USR, SymbolID());
IO.mapOptional("Path", Ref.Path, SmallString<128>());
}
@@ -267,6 +271,28 @@
// the AS that shouldn't be part of the output. Even though AS_public is the
// default in the struct, it should be displayed in the YAML output.
IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none);
+ IO.mapOptional("Template", I.Template);
+ }
+};
+
+template <> struct MappingTraits {
+ static void mapping(IO &IO, TemplateParamInfo &I) {
+ IO.mapOptional("Contents", I.Contents);
+ }
+};
+
+template <> struct MappingTraits {
+ static void mapping(IO &IO, TemplateSpecializationInfo &I) {
+ IO.mapOptional("SpecializationOf", I.SpecializationOf);
+ IO.mapOptional("Params", I.Params);
+ }
+};
+
+template <> struct MappingTraits {
+ static void mapping(IO &IO, TemplateInfo &I) {
+ IO.mapOptional("Params", I.Params);
+ IO.mapOptional("Specialization", I.Specialization,
+ Optional());
}
};
Index: clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
===================================================================
--- clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
+++ clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -44,9 +44,10 @@
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
- InfoType::IT_namespace, "Namespace");
+ InfoType::IT_namespace,
+ "Namespace::ChildNamespace", "Namespace");
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
- "Namespace");
+ "Namespace::ChildStruct", "Namespace");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Access = AccessSpecifier::AS_none;
I.Children.Functions.back().Name = "OneFunction";
@@ -152,14 +153,13 @@
SmallString<16> PathTo;
llvm::sys::path::native("path/to", PathTo);
- I.Members.emplace_back(TypeInfo("int", "X/Y"), "X",
- AccessSpecifier::AS_private);
+ I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
I.TagType = TagTypeKind::TTK_Class;
- I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, PathTo);
+ I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "F", PathTo);
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
- "X/Y/Z/r");
+ "X::Y::Z::r::ChildStruct", "X/Y/Z/r");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Enums.emplace_back();
@@ -195,11 +195,7 @@
Members
- -
- private
- int
- X
-
+ - private int X
Records
@@ -277,8 +273,8 @@
SmallString<16> PathTo;
llvm::sys::path::native("path/to", PathTo);
- I.ReturnType =
- TypeInfo(Reference(EmptySID, "float", InfoType::IT_default, PathTo));
+ I.ReturnType = TypeInfo(
+ Reference(EmptySID, "float", InfoType::IT_default, "float", PathTo));
I.Params.emplace_back(TypeInfo("int", PathTo), "P");
I.IsMethod = true;
I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
Index: clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
===================================================================
--- clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
+++ clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
@@ -390,7 +390,7 @@
RecordInfo *F = InfoAsRecord(Infos[0].get());
RecordInfo ExpectedF(EmptySID, /*Name=*/"F", /*Path=*/"GlobalNamespace");
ExpectedF.Namespace.emplace_back(EmptySID, "GlobalNamespace",
- InfoType::IT_namespace);
+ InfoType::IT_namespace, "");
ExpectedF.TagType = TagTypeKind::TTK_Class;
ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
CheckRecordInfo(&ExpectedF, F);
@@ -410,9 +410,10 @@
ExpectedE.Namespace.emplace_back(EmptySID, "GlobalNamespace",
InfoType::IT_namespace);
ExpectedE.Parents.emplace_back(EmptySID, /*Name=*/"F", InfoType::IT_record,
- /*Path*=*/"GlobalNamespace");
- ExpectedE.VirtualParents.emplace_back(
- EmptySID, /*Name=*/"G", InfoType::IT_record, /*Path*=*/"GlobalNamespace");
+ /*QualName=*/"", /*Path*=*/"GlobalNamespace");
+ ExpectedE.VirtualParents.emplace_back(EmptySID, /*Name=*/"G",
+ InfoType::IT_record, /*QualName=*/"G",
+ /*Path*=*/"GlobalNamespace");
ExpectedE.Bases.emplace_back(EmptySID, /*Name=*/"F",
/*Path=*/"GlobalNamespace", false,
AccessSpecifier::AS_public, true);
@@ -455,9 +456,10 @@
ExpectedH.TagType = TagTypeKind::TTK_Class;
ExpectedH.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
ExpectedH.Parents.emplace_back(EmptySID, /*Name=*/"E", InfoType::IT_record,
- /*Path=*/"GlobalNamespace");
- ExpectedH.VirtualParents.emplace_back(
- EmptySID, /*Name=*/"G", InfoType::IT_record, /*Path=*/"GlobalNamespace");
+ /*QualName=*/"E", /*Path=*/"GlobalNamespace");
+ ExpectedH.VirtualParents.emplace_back(EmptySID, /*Name=*/"G",
+ InfoType::IT_record, /*QualName=*/"G",
+ /*Path=*/"GlobalNamespace");
ExpectedH.Bases.emplace_back(EmptySID, /*Name=*/"E",
/*Path=*/"GlobalNamespace", false,
AccessSpecifier::AS_private, true);
@@ -562,7 +564,7 @@
NamespaceInfo *ParentA = InfoAsNamespace(Infos[1].get());
NamespaceInfo ExpectedParentA(EmptySID);
ExpectedParentA.Children.Records.emplace_back(
- EmptySID, "A", InfoType::IT_record, "GlobalNamespace");
+ EmptySID, "A", InfoType::IT_record, "A", "GlobalNamespace");
CheckNamespaceInfo(&ExpectedParentA, ParentA);
RecordInfo *ParentB = InfoAsRecord(Infos[3].get());
@@ -570,13 +572,13 @@
llvm::SmallString<128> ExpectedParentBPath("GlobalNamespace/A");
llvm::sys::path::native(ExpectedParentBPath);
ExpectedParentB.Children.Records.emplace_back(
- EmptySID, "B", InfoType::IT_record, ExpectedParentBPath);
+ EmptySID, "B", InfoType::IT_record, "A::B", ExpectedParentBPath);
CheckRecordInfo(&ExpectedParentB, ParentB);
NamespaceInfo *ParentC = InfoAsNamespace(Infos[7].get());
NamespaceInfo ExpectedParentC(EmptySID);
ExpectedParentC.Children.Records.emplace_back(
- EmptySID, "C", InfoType::IT_record, "@nonymous_namespace");
+ EmptySID, "C", InfoType::IT_record, "C", "@nonymous_namespace");
CheckNamespaceInfo(&ExpectedParentC, ParentC);
}
@@ -594,8 +596,8 @@
NamespaceInfo *ParentB = InfoAsNamespace(Infos[3].get());
NamespaceInfo ExpectedParentB(EmptySID);
- ExpectedParentB.Children.Namespaces.emplace_back(EmptySID, "B",
- InfoType::IT_namespace, "A");
+ ExpectedParentB.Children.Namespaces.emplace_back(
+ EmptySID, "B", InfoType::IT_namespace, "A::B", "A");
CheckNamespaceInfo(&ExpectedParentB, ParentB);
}
@@ -626,5 +628,77 @@
EXPECT_EQ("double", SecondTD.Underlying.Type.Name);
}
+TEST(SerializeTests, emitFunctionTemplate) {
+ EmittedInfoList Infos;
+ // A template and a specialization.
+ ExtractInfosFromCode("template void GetFoo(T);\n"
+ "template<> void GetFoo(bool);",
+ 2,
+ /*Public=*/false, Infos);
+
+ // First info will be the global namespace.
+ NamespaceInfo *GlobalNS1 = InfoAsNamespace(Infos[0].get());
+ ASSERT_EQ(1u, GlobalNS1->Children.Functions.size());
+
+ const FunctionInfo &Func1 = GlobalNS1->Children.Functions[0];
+ EXPECT_EQ("GetFoo", Func1.Name);
+ ASSERT_TRUE(Func1.Template);
+ EXPECT_FALSE(Func1.Template->Specialization); // Not a specialization.
+
+ // Template parameter.
+ ASSERT_EQ(1u, Func1.Template->Params.size());
+ EXPECT_EQ("typename T = int", Func1.Template->Params[0].Contents);
+
+ // The second will be another global namespace with the function in it (the
+ // global namespace is duplicated because the items haven't been merged at the
+ // serialization phase of processing).
+ NamespaceInfo *GlobalNS2 = InfoAsNamespace(Infos[1].get());
+ ASSERT_EQ(1u, GlobalNS2->Children.Functions.size());
+
+ // This one is a template specialization.
+ const FunctionInfo &Func2 = GlobalNS2->Children.Functions[0];
+ EXPECT_EQ("GetFoo", Func2.Name);
+ ASSERT_TRUE(Func2.Template);
+ EXPECT_TRUE(Func2.Template->Params.empty()); // No template params.
+ ASSERT_TRUE(Func2.Template->Specialization);
+
+ // Specialization values.
+ ASSERT_EQ(1u, Func2.Template->Specialization->Params.size());
+ EXPECT_EQ("bool", Func2.Template->Specialization->Params[0].Contents);
+ EXPECT_EQ(Func1.USR, Func2.Template->Specialization->SpecializationOf);
+}
+
+TEST(SerializeTests, emitClassTemplate) {
+ EmittedInfoList Infos;
+ // A template and a specialization. This will generate 4 infos: the two
+ // records, each followed by a copy of the global namespace (the merge hasn't
+ // happened yet).
+ ExtractInfosFromCode("template class MyTemplate { int i[I]; };\n"
+ "template<> class MyTemplate<0> {};",
+ 4,
+ /*Public=*/false, Infos);
+
+ const RecordInfo *Rec1 = InfoAsRecord(Infos[0].get());
+ EXPECT_EQ("MyTemplate", Rec1->Name);
+ ASSERT_TRUE(Rec1->Template);
+ EXPECT_FALSE(Rec1->Template->Specialization); // Not a specialization.
+
+ // Template parameter.
+ ASSERT_EQ(1u, Rec1->Template->Params.size());
+ EXPECT_EQ("int I", Rec1->Template->Params[0].Contents);
+
+ // Second record.
+ const RecordInfo *Rec2 = InfoAsRecord(Infos[2].get());
+ EXPECT_EQ("MyTemplate", Rec2->Name);
+ ASSERT_TRUE(Rec2->Template);
+ EXPECT_TRUE(Rec2->Template->Params.empty()); // No template params.
+ ASSERT_TRUE(Rec2->Template->Specialization);
+
+ // Specialization values.
+ ASSERT_EQ(1u, Rec2->Template->Specialization->Params.size());
+ EXPECT_EQ("0", Rec2->Template->Specialization->Params[0].Contents);
+ EXPECT_EQ(Rec1->USR, Rec2->Template->Specialization->SpecializationOf);
+}
+
} // namespace doc
} // end namespace clang
Index: clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
===================================================================
--- clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
+++ clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
@@ -28,10 +28,11 @@
I.Path = "path/to/A";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
- I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
- InfoType::IT_namespace,
- "path/to/A/Namespace");
+ I.Children.Namespaces.emplace_back(
+ EmptySID, "ChildNamespace", InfoType::IT_namespace,
+ "path::to::A::Namespace::ChildNamespace", "path/to/A/Namespace");
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+ "path::to::A::Namespace::ChildStruct",
"path/to/A/Namespace");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
@@ -53,13 +54,16 @@
Namespace:
- Type: Namespace
Name: 'A'
+ QualName: 'A'
ChildNamespaces:
- Type: Namespace
Name: 'ChildNamespace'
+ QualName: 'path::to::A::Namespace::ChildNamespace'
Path: 'path/to/A/Namespace'
ChildRecords:
- Type: Record
Name: 'ChildStruct'
+ QualName: 'path::to::A::Namespace::ChildStruct'
Path: 'path/to/A/Namespace'
ChildFunctions:
- USR: '0000000000000000000000000000000000000000'
@@ -83,8 +87,7 @@
I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
- I.Members.emplace_back(TypeInfo("int", "path/to/int"), "X",
- AccessSpecifier::AS_private);
+ I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
// Member documentation.
CommentInfo TopComment;
@@ -103,15 +106,15 @@
AccessSpecifier::AS_public, true);
I.Bases.back().Children.Functions.emplace_back();
I.Bases.back().Children.Functions.back().Name = "InheritedFunctionOne";
- I.Bases.back().Members.emplace_back(TypeInfo("int", "path/to/int"), "N",
+ I.Bases.back().Members.emplace_back(TypeInfo("int"), "N",
AccessSpecifier::AS_private);
// F is in the global namespace
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "");
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
- "path/to/G");
+ "path::to::G::G", "path/to/G");
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
- "path/to/A/r");
+ "path::to::A::r::ChildStruct", "path/to/A/r");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Enums.emplace_back();
@@ -131,6 +134,7 @@
Namespace:
- Type: Namespace
Name: 'A'
+ QualName: 'A'
DefLocation:
LineNumber: 10
Filename: 'test.cpp'
@@ -142,7 +146,7 @@
Members:
- Type:
Name: 'int'
- Path: 'path/to/int'
+ QualName: 'int'
Name: 'X'
Access: Private
Description:
@@ -161,7 +165,7 @@
Members:
- Type:
Name: 'int'
- Path: 'path/to/int'
+ QualName: 'int'
Name: 'N'
Access: Private
ChildFunctions:
@@ -178,10 +182,12 @@
VirtualParents:
- Type: Record
Name: 'G'
+ QualName: 'path::to::G::G'
Path: 'path/to/G'
ChildRecords:
- Type: Record
Name: 'ChildStruct'
+ QualName: 'path::to::A::r::ChildStruct'
Path: 'path/to/A/r'
ChildFunctions:
- USR: '0000000000000000000000000000000000000000'
@@ -206,10 +212,9 @@
I.Access = AccessSpecifier::AS_none;
- I.ReturnType = TypeInfo(
- Reference(EmptySID, "void", InfoType::IT_default, "path/to/void"));
- I.Params.emplace_back(TypeInfo("int", "path/to/int"), "P");
- I.Params.emplace_back(TypeInfo("double", "path/to/double"), "D");
+ I.ReturnType = TypeInfo(Reference(EmptySID, "void", InfoType::IT_default));
+ I.Params.emplace_back(TypeInfo("int"), "P");
+ I.Params.emplace_back(TypeInfo("double"), "D");
I.Params.back().DefaultValue = "2.0 * M_PI";
I.IsMethod = true;
I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
@@ -227,6 +232,7 @@
Namespace:
- Type: Namespace
Name: 'A'
+ QualName: 'A'
DefLocation:
LineNumber: 10
Filename: 'test.cpp'
@@ -237,20 +243,21 @@
Parent:
Type: Record
Name: 'Parent'
+ QualName: 'Parent'
Params:
- Type:
Name: 'int'
- Path: 'path/to/int'
+ QualName: 'int'
Name: 'P'
- Type:
Name: 'double'
- Path: 'path/to/double'
+ QualName: 'double'
Name: 'D'
DefaultValue: '2.0 * M_PI'
ReturnType:
Type:
Name: 'void'
- Path: 'path/to/void'
+ QualName: 'void'
...
)raw";
EXPECT_EQ(Expected, Actual.str());
@@ -284,6 +291,7 @@
Namespace:
- Type: Namespace
Name: 'A'
+ QualName: 'A'
DefLocation:
LineNumber: 10
Filename: 'test.cpp'
@@ -322,6 +330,7 @@
BaseType:
Type:
Name: 'short'
+ QualName: 'short'
Members:
- Name: 'X'
Value: '-9876'
@@ -349,6 +358,7 @@
Name: 'MyUsing'
Underlying:
Name: 'int'
+ QualName: 'int'
IsUsing: true
...
)raw";
@@ -548,13 +558,16 @@
Params:
- Type:
Name: 'int'
+ QualName: 'int'
Name: 'I'
- Type:
Name: 'int'
+ QualName: 'int'
Name: 'J'
ReturnType:
Type:
Name: 'void'
+ QualName: 'void'
...
)raw";