summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-doc
diff options
context:
space:
mode:
authorBrett Wilson <brettw@gmail.com>2022-11-07 15:07:56 -0800
committerBrett Wilson <brettw@gmail.com>2022-12-07 09:48:13 -0800
commit0f6dbb5f164662c3e6a167a89e7a89f07c60e32b (patch)
tree0fcd10c93ef2f7a12a9302a580e5fc9f94a40618 /clang-tools-extra/clang-doc
parent538f69f69c767a576e63fad0a31f25ab2242c616 (diff)
downloadllvm-0f6dbb5f164662c3e6a167a89e7a89f07c60e32b.tar.gz
[clang-doc] Add template support.
Reads template information from the AST and adds template parameters and specialization information to the corresponding clang-doc structures. Add a "QualName" to the Reference struct which includes the full qualified type name. The Reference object represents a link in the HTML/MD generators so is based on the unqualified name. But this does not encode C-V qualifiers or template information that decorate the name. The new QualName member encodes all of this information and also makes it easier for the generators or downsteam YAML consumers to generate the full name (before they had to process the "Path"). In test code that was changed, remove made-up paths to built-in types like "int". In addition to slightnly cleaning up the code, these types do not have paths in real execution, and generating incorrect references to nonexistant data may complicate future changes in the generators. Differential Revision: https://reviews.llvm.org/D139154
Diffstat (limited to 'clang-tools-extra/clang-doc')
-rw-r--r--clang-tools-extra/clang-doc/BitcodeReader.cpp85
-rw-r--r--clang-tools-extra/clang-doc/BitcodeWriter.cpp43
-rw-r--r--clang-tools-extra/clang-doc/BitcodeWriter.h12
-rw-r--r--clang-tools-extra/clang-doc/Representation.cpp4
-rw-r--r--clang-tools-extra/clang-doc/Representation.h79
-rw-r--r--clang-tools-extra/clang-doc/Serialize.cpp126
-rw-r--r--clang-tools-extra/clang-doc/YAMLGenerator.cpp26
7 files changed, 344 insertions, 31 deletions
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 072011448330..3fb85056cee8 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -351,6 +351,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
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:
@@ -363,6 +365,29 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
}
}
+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) {
+ if (ID == TEMPLATE_SPECIALIZATION_OF)
+ return decodeRecord(R, I->SpecializationOf, Blob);
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "invalid field for TemplateParamInfo");
+}
+
+llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
+ TemplateParamInfo *I) {
+ if (ID == TEMPLATE_PARAM_CONTENTS)
+ return decodeRecord(R, I->Contents, Blob);
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "invalid field for TemplateParamInfo");
+}
+
template <typename T> llvm::Expected<CommentInfo *> getCommentInfo(T I) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain CommentInfo");
@@ -595,6 +620,45 @@ template <> void addChild(BaseRecordInfo *I, FunctionInfo &&R) {
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 <typename T> 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 <typename T> 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 <typename T>
+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 <typename T>
llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, T I) {
@@ -719,6 +783,27 @@ llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
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))
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index bb0698a5a402..8a5647cb6a75 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -121,7 +121,10 @@ static const llvm::IndexedMap<llvm::StringRef, BlockIdToIndexFunctor>
{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 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{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 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
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 @@ void ClangDocBitcodeWriter::emitRecord(unsigned Val, RecordId ID) {
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 @@ void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
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 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
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 @@ void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) {
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) {
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index 5a2514a19b30..9a572e40e352 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/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 @@ enum BlockId {
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 @@ enum RecordId {
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 @@ public:
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 @@ private:
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.
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index 27b83d67513d..c1279997813a 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -250,6 +250,8 @@ void RecordInfo::merge(RecordInfo &&Other) {
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 @@ void FunctionInfo::merge(FunctionInfo &&Other) {
if (Params.empty())
Params = std::move(Other.Params);
SymbolInfo::merge(std::move(Other));
+ if (!Template)
+ Template = Other.Template;
}
void TypedefInfo::merge(TypedefInfo &&Other) {
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 2a690883b33e..32272cdde4ff 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -117,13 +117,21 @@ struct CommentInfo {
};
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 an
+ // empty string to indicate the default because we need to accept the empty
+ // string as a valid input for the global namespace (it will have
+ // "GlobalNamespace" as the name, but an empty QualName).
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);
@@ -136,7 +144,17 @@ struct Reference {
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<int> 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<int>"). Contrast to
+ // Name.
+ SmallString<16> QualName;
+
InfoType RefType = InfoType::IT_default; // Indicates the type of this
// Reference (namespace, record,
// function, enum, default).
@@ -169,13 +187,46 @@ struct TypeInfo {
// 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. It is saved in a struct so there is a place to add the
+// name and default values in the future if needed.
+struct TemplateParamInfo {
+ TemplateParamInfo() = default;
+ explicit TemplateParamInfo(StringRef Contents) : Contents(Contents) {}
+
+ // The literal contents of the code for that specifies this template parameter
+ // for this declaration. Typical values will be "class T" and
+ // "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<TemplateParamInfo> Params;
+};
+
+// Records the template information for a struct or function that is a template
+// or an explicit template specialization.
+struct TemplateInfo {
+ // May be empty for non-partial specializations.
+ std::vector<TemplateParamInfo> Params;
+
+ // Set when this is a specialization of another record/function.
+ llvm::Optional<TemplateSpecializationInfo> Specialization;
+};
+
// Info for field types.
struct FieldTypeInfo : public TypeInfo {
FieldTypeInfo() = default;
@@ -317,6 +368,13 @@ struct FunctionInfo : public SymbolInfo {
// 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<TemplateInfo> Template;
};
// TODO: Expand to allow for documenting templating, inheritance access,
@@ -332,6 +390,13 @@ struct RecordInfo : public SymbolInfo {
// 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<TemplateInfo> Template;
+
// Indicates if the record was declared using a typedef. Things like anonymous
// structs in a typedef:
// typedef struct { ... } foo_t;
@@ -433,7 +498,7 @@ struct Index : public Reference {
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;
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 66a938d488e1..50cda2cb6fd3 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -250,7 +250,7 @@ TypeInfo getTypeInfoForType(const QualType &T) {
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 @@ static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
// 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 @@ static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
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 @@ static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
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 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
} 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<RecordDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
- InfoType::IT_record);
+ InfoType::IT_record,
+ N->getQualifiedNameAsString());
else if (const auto *N = dyn_cast<FunctionDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
- InfoType::IT_function);
+ InfoType::IT_function,
+ N->getQualifiedNameAsString());
else if (const auto *N = dyn_cast<EnumDecl>(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 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
InfoType::IT_namespace);
}
+void PopulateTemplateParameters(llvm::Optional<TemplateInfo> &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 <typename T>
static void populateInfo(Info &I, const T *D, const FullComment *C,
bool &IsInAnonymousNamespace) {
@@ -508,6 +533,26 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
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 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
}
I->Path = getInfoRelativePath(I->Namespace);
+ PopulateTemplateParameters(I->Template, D);
+
+ // Full and partial specializations.
+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(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<ClassTemplateDecl *>()) {
+ Specialization.SpecializationOf =
+ getUSRForDecl(SpecOf.get<ClassTemplateDecl *>());
+ } else if (SpecOf.is<ClassTemplatePartialSpecializationDecl *>()) {
+ Specialization.SpecializationOf =
+ getUSRForDecl(SpecOf.get<ClassTemplatePartialSpecializationDecl *>());
+ }
+
+ // 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<ClassTemplatePartialSpecializationDecl>(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<const RecordInfo &>(*I);
@@ -669,7 +754,8 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
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 @@ emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
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.
diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
index 0e662c0d9006..047a9a1e99eb 100644
--- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ b/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"
#include <optional>
@@ -24,6 +25,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo)
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<CommentInfo>)
@@ -143,6 +145,7 @@ static void RecordInfoMapping(IO &IO, RecordInfo &I) {
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) {
@@ -175,6 +178,7 @@ template <> struct MappingTraits<Reference> {
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>());
}
@@ -268,6 +272,28 @@ template <> struct MappingTraits<FunctionInfo> {
// 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<TemplateParamInfo> {
+ static void mapping(IO &IO, TemplateParamInfo &I) {
+ IO.mapOptional("Contents", I.Contents);
+ }
+};
+
+template <> struct MappingTraits<TemplateSpecializationInfo> {
+ static void mapping(IO &IO, TemplateSpecializationInfo &I) {
+ IO.mapOptional("SpecializationOf", I.SpecializationOf);
+ IO.mapOptional("Params", I.Params);
+ }
+};
+
+template <> struct MappingTraits<TemplateInfo> {
+ static void mapping(IO &IO, TemplateInfo &I) {
+ IO.mapOptional("Params", I.Params);
+ IO.mapOptional("Specialization", I.Specialization,
+ Optional<TemplateSpecializationInfo>());
}
};