From 2600533a66748717f84ffc71945dcedf8baefe0a Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Thu, 2 Apr 2026 16:25:21 -0700 Subject: [PATCH] [clang-doc] Switch to string internment (#190044) This is the first step in migrating all the Info types to be POD. We introduced a shared string saver that can be used safely across threads, and updated the internal represntation of various data types to use these over owned strings, like SmallString or std::string. This also required changes to YAMLGenerator to keep the single quoted string formatting and to update the YAML traits. This change gives an almost 50% reduction in peak memory when building documentation for clang, at about a 10% performance loss. Future patches can mitigate the performance penalties, and further reduce memory use. | Metric | Baseline | Prev | This | Culm% | Seq% | | :--- | :--- | :--- | :--- | :--- | :--- | | Time | 920.5s | 920.5s | 1011.0s | +9.8% | +9.8% | | Memory | 86.0G | 86.0G | 44.9G | -47.8% | -47.8% | | Benchmark | Baseline | Prev | This | Culm% | Seq% | | :--- | :--- | :--- | :--- | :--- | :--- | | BM_BitcodeReader_Scale/10 | 67.9us | 67.9us | 70.0us | +3.0% | +3.0% | | BM_BitcodeReader_Scale/10000 | 70.5ms | 70.5ms | 21.3ms | -69.8% | -69.8% | | BM_BitcodeReader_Scale/4096 | 23.2ms | 23.2ms | 4.5ms | -80.7% | -80.7% | | BM_BitcodeReader_Scale/512 | 509.4us | 509.4us | 538.8us | +5.8% | +5.8% | | BM_BitcodeReader_Scale/64 | 114.8us | 114.8us | 118.0us | +2.8% | +2.8% | | BM_Index_Insertion/10 | 2.3us | 2.3us | 4.0us | +71.6% | +71.6% | | BM_Index_Insertion/10000 | 3.1ms | 3.1ms | 5.0ms | +60.6% | +60.6% | | BM_Index_Insertion/4096 | 1.3ms | 1.3ms | 2.0ms | +57.1% | +57.1% | | BM_Index_Insertion/512 | 153.6us | 153.6us | 245.0us | +59.6% | +59.6% | | BM_Index_Insertion/64 | 18.1us | 18.1us | 28.9us | +60.0% | +60.0% | | BM_JSONGenerator_Scale/10 | 36.8us | 36.8us | 36.4us | -1.3% | -1.3% | | BM_Mapper_Scale/10000 | 104.3ms | 104.3ms | 105.4ms | +1.0% | +1.0% | | BM_Mapper_Scale/512 | 7.6ms | 7.6ms | 7.7ms | +1.9% | +1.9% | | BM_MergeInfos_Scale/10000 | 12.2ms | 12.2ms | 1.4ms | -88.2% | -88.2% | | BM_MergeInfos_Scale/2 | 1.9us | 1.9us | 1.7us | -10.3% | -10.3% | | BM_MergeInfos_Scale/4096 | 2.8ms | 2.8ms | 495.6us | -82.2% | -82.2% | | BM_MergeInfos_Scale/512 | 68.9us | 68.9us | 34.6us | -49.7% | -49.7% | | BM_MergeInfos_Scale/64 | 10.3us | 10.3us | 6.0us | -41.6% | -41.6% | | BM_MergeInfos_Scale/8 | 2.8us | 2.8us | 2.1us | -24.4% | -24.4% | | BM_SerializeFunctionInfo | 25.5us | 25.5us | 26.8us | +4.9% | +4.9% | note: I used an LLM to help generate the test code adjustments and the YAML traits. --- clang-tools-extra/clang-doc/BitcodeReader.cpp | 15 +- clang-tools-extra/clang-doc/JSONGenerator.cpp | 6 +- .../clang-doc/Representation.cpp | 56 +++-- clang-tools-extra/clang-doc/Representation.h | 135 ++++++++---- clang-tools-extra/clang-doc/Serialize.cpp | 102 ++++----- clang-tools-extra/clang-doc/Serialize.h | 10 +- clang-tools-extra/clang-doc/YAMLGenerator.cpp | 199 ++++++++++++++---- .../benchmarks/ClangDocBenchmark.cpp | 7 +- .../unittests/clang-doc/GeneratorTest.cpp | 6 +- 9 files changed, 353 insertions(+), 183 deletions(-) diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp index 1589e2006488..469a6e73eb42 100644 --- a/clang-tools-extra/clang-doc/BitcodeReader.cpp +++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp @@ -28,6 +28,12 @@ static llvm::Error decodeRecord(const Record &R, return llvm::Error::success(); } +static llvm::Error decodeRecord(const Record &R, llvm::StringRef &Field, + llvm::StringRef Blob) { + Field = internString(Blob); + return llvm::Error::success(); +} + static llvm::Error decodeRecord(const Record &R, SymbolID &Field, llvm::StringRef Blob) { if (R[0] != BitCodeConstants::USRHashSize) @@ -123,11 +129,10 @@ static llvm::Error decodeRecord(const Record &R, FieldId &Field, "invalid value for FieldId"); } -static llvm::Error -decodeRecord(const Record &R, - llvm::SmallVectorImpl> &Field, - llvm::StringRef Blob) { - Field.push_back(Blob); +static llvm::Error decodeRecord(const Record &R, + llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { + Field.push_back(internString(Blob)); return llvm::Error::success(); } diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp index d7d7e99d0ba4..980dafc2a4ed 100644 --- a/clang-tools-extra/clang-doc/JSONGenerator.cpp +++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp @@ -837,11 +837,11 @@ static OwningVec preprocessCDCtxIndex(Index CDCtxIndex) { Processed.reserve(CDCtxIndex.Children.size()); for (const auto *Idx : CDCtxIndex.getSortedChildren()) { Index NewIdx = *Idx; - auto NewPath = NewIdx.getRelativeFilePath(""); + SmallString<128> NewPath(NewIdx.getRelativeFilePath("")); sys::path::native(NewPath, sys::path::Style::posix); sys::path::append(NewPath, sys::path::Style::posix, NewIdx.getFileBaseName() + ".md"); - NewIdx.Path = NewPath; + NewIdx.Path = internString(NewPath); Processed.push_back(NewIdx); } @@ -968,7 +968,7 @@ Error JSONGenerator::generateDocumentation( if (FileToInfos.contains(Path)) continue; FileToInfos[Path].push_back(Info); - Info->DocumentationFileName = FileName; + Info->DocumentationFileName = internString(FileName); } if (CDCtx.Format == OutputFormatTy::md_mustache) { diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp index 503cefc7ec04..7a8be5a06e99 100644 --- a/clang-tools-extra/clang-doc/Representation.cpp +++ b/clang-tools-extra/clang-doc/Representation.cpp @@ -27,6 +27,11 @@ namespace clang { namespace doc { +ConcurrentStringPool &getGlobalStringPool() { + static ConcurrentStringPool GlobalPool; + return GlobalPool; +} + CommentKind stringToCommentKind(llvm::StringRef KindStr) { static const llvm::StringMap KindMap = { {"FullComment", CommentKind::CK_FullComment}, @@ -207,26 +212,26 @@ calculateRelativeFilePath(const InfoType &Type, const StringRef &Path, return llvm::sys::path::relative_path(FilePath); } -llvm::SmallString<64> -Reference::getRelativeFilePath(const StringRef &CurrentPath) const { - return calculateRelativeFilePath(RefType, Path, Name, CurrentPath); +StringRef Reference::getRelativeFilePath(const StringRef &CurrentPath) const { + return internString( + calculateRelativeFilePath(RefType, Path, Name, CurrentPath)); } -llvm::SmallString<16> Reference::getFileBaseName() const { +StringRef Reference::getFileBaseName() const { if (RefType == InfoType::IT_namespace) - return llvm::SmallString<16>("index"); + return "index"; return Name; } -llvm::SmallString<64> -Info::getRelativeFilePath(const StringRef &CurrentPath) const { - return calculateRelativeFilePath(IT, Path, extractName(), CurrentPath); +StringRef Info::getRelativeFilePath(const StringRef &CurrentPath) const { + return internString( + calculateRelativeFilePath(IT, Path, extractName(), CurrentPath)); } -llvm::SmallString<16> Info::getFileBaseName() const { +StringRef Info::getFileBaseName() const { if (IT == InfoType::IT_namespace) - return llvm::SmallString<16>("index"); + return "index"; return extractName(); } @@ -406,7 +411,7 @@ BaseRecordInfo::BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path, : RecordInfo(USR, Name, Path), Access(Access), IsVirtual(IsVirtual), IsParent(IsParent) {} -llvm::SmallString<16> Info::extractName() const { +StringRef Info::extractName() const { if (!Name.empty()) return Name; @@ -417,37 +422,30 @@ llvm::SmallString<16> Info::extractName() const { // namespace, which would conflict with the hard-coded global namespace name // below.) if (Name == "GlobalNamespace" && Namespace.empty()) - return llvm::SmallString<16>("@GlobalNamespace"); + return "@GlobalNamespace"; // The case of anonymous namespaces is taken care of in serialization, // so here we can safely assume an unnamed namespace is the global // one. - return llvm::SmallString<16>("GlobalNamespace"); + return "GlobalNamespace"; case InfoType::IT_record: - return llvm::SmallString<16>("@nonymous_record_" + - toHex(llvm::toStringRef(USR))); + return internString("@nonymous_record_" + toHex(llvm::toStringRef(USR))); case InfoType::IT_enum: - return llvm::SmallString<16>("@nonymous_enum_" + - toHex(llvm::toStringRef(USR))); + return internString("@nonymous_enum_" + toHex(llvm::toStringRef(USR))); case InfoType::IT_typedef: - return llvm::SmallString<16>("@nonymous_typedef_" + - toHex(llvm::toStringRef(USR))); + return internString("@nonymous_typedef_" + toHex(llvm::toStringRef(USR))); case InfoType::IT_function: - return llvm::SmallString<16>("@nonymous_function_" + - toHex(llvm::toStringRef(USR))); + return internString("@nonymous_function_" + toHex(llvm::toStringRef(USR))); case InfoType::IT_concept: - return llvm::SmallString<16>("@nonymous_concept_" + - toHex(llvm::toStringRef(USR))); + return internString("@nonymous_concept_" + toHex(llvm::toStringRef(USR))); case InfoType::IT_variable: - return llvm::SmallString<16>("@nonymous_variable_" + - toHex(llvm::toStringRef(USR))); + return internString("@nonymous_variable_" + toHex(llvm::toStringRef(USR))); case InfoType::IT_friend: - return llvm::SmallString<16>("@nonymous_friend_" + - toHex(llvm::toStringRef(USR))); + return internString("@nonymous_friend_" + toHex(llvm::toStringRef(USR))); case InfoType::IT_default: - return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR))); + return internString("@nonymous_" + toHex(llvm::toStringRef(USR))); } llvm_unreachable("Invalid InfoType."); - return llvm::SmallString<16>(""); + return ""; } // Order is based on the Name attribute: case insensitive order diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index e692579ae95a..63af0abe564f 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -20,6 +20,9 @@ #include "clang/Tooling/Execution.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/StringSaver.h" #include #include #include @@ -28,6 +31,42 @@ namespace clang { namespace doc { +class ConcurrentStringPool { +public: + StringRef intern(StringRef Name) { + if (Name.empty()) + return StringRef(); + + llvm::sys::SmartScopedLock Lock(PoolMutex); + return Saver.save(Name); + } + +private: + llvm::sys::SmartMutex PoolMutex; + llvm::BumpPtrAllocator Alloc; + llvm::UniqueStringSaver Saver{Alloc}; +}; + +ConcurrentStringPool &getGlobalStringPool(); + +inline StringRef internString(const Twine &T) { + if (T.isTriviallyEmpty()) + return StringRef(); + + if (T.isSingleStringRef()) { + StringRef S = T.getSingleStringRef(); + if (S.empty()) + return StringRef(); + return getGlobalStringPool().intern(S); + } + + SmallString<128> Buffer; + StringRef S = T.toStringRef(Buffer); + if (S.empty()) + return StringRef(); + return getGlobalStringPool().intern(S); +} + // An abstraction for owned pointers. Initially mapped to OwnedPtr, // to be eventually transitioned to bare pointers in an arena. template using OwnedPtr = std::unique_ptr; @@ -123,16 +162,16 @@ struct CommentInfo { OwningPtrVec Children; // List of child comments for this CommentInfo. - SmallString<8> Direction; // Parameter direction (for (T)ParamCommand). - SmallString<16> Name; // Name of the comment (for Verbatim and HTML). - SmallString<16> ParamName; // Parameter name (for (T)ParamCommand). - SmallString<16> CloseName; // Closing tag name (for VerbatimBlock). - SmallString<64> Text; // Text of the comment. - llvm::SmallVector, 4> + StringRef Direction; // Parameter direction (for (T)ParamCommand). + StringRef Name; // Name of the comment (for Verbatim and HTML). + StringRef ParamName; // Parameter name (for (T)ParamCommand). + StringRef CloseName; // Closing tag name (for VerbatimBlock). + StringRef Text; // Text of the comment. + llvm::SmallVector AttrKeys; // List of attribute keys (for HTML). - llvm::SmallVector, 4> + llvm::SmallVector AttrValues; // List of attribute values for each key (for HTML). - llvm::SmallVector, 4> + llvm::SmallVector Args; // List of arguments to commands (for InlineCommand). CommentKind Kind = CommentKind:: CK_Unknown; // Kind of comment (FullComment, ParagraphComment, @@ -154,14 +193,17 @@ struct Reference { // "GlobalNamespace" as the name, but an empty QualName). Reference(SymbolID USR = SymbolID(), StringRef Name = StringRef(), InfoType IT = InfoType::IT_default) - : USR(USR), RefType(IT), Name(Name), QualName(Name) {} + : USR(USR), RefType(IT), Name(internString(Name)), + QualName(internString(Name)) {} Reference(SymbolID USR, StringRef Name, InfoType IT, StringRef QualName, StringRef Path = StringRef()) - : USR(USR), RefType(IT), Name(Name), QualName(QualName), Path(Path) {} + : USR(USR), RefType(IT), Name(internString(Name)), + QualName(internString(QualName)), Path(internString(Path)) {} Reference(SymbolID USR, StringRef Name, InfoType IT, StringRef QualName, - StringRef Path, SmallString<16> DocumentationFileName) - : USR(USR), RefType(IT), Name(Name), QualName(QualName), Path(Path), - DocumentationFileName(DocumentationFileName) {} + StringRef Path, StringRef DocumentationFileName) + : USR(USR), RefType(IT), Name(internString(Name)), + QualName(internString(QualName)), Path(internString(Path)), + DocumentationFileName(internString(DocumentationFileName)) {} bool operator==(const Reference &Other) const { return std::tie(USR, Name, QualName, RefType) == @@ -173,10 +215,10 @@ struct Reference { bool operator<(const Reference &Other) const { return Name < Other.Name; } /// Returns the path for this Reference relative to CurrentPath. - llvm::SmallString<64> getRelativeFilePath(const StringRef &CurrentPath) const; + StringRef getRelativeFilePath(const StringRef &CurrentPath) const; /// Returns the basename that should be used for this Reference. - llvm::SmallString<16> getFileBaseName() const; + StringRef getFileBaseName() const; SymbolID USR = SymbolID(); // Unique identifier for referenced decl @@ -187,27 +229,27 @@ struct Reference { // 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; + StringRef 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; + StringRef QualName; // Path of directory where the clang-doc generated file will be saved // (possibly unresolved) - llvm::SmallString<128> Path; - SmallString<16> DocumentationFileName; + StringRef Path; + StringRef DocumentationFileName; }; // A Context is a reference that holds a relative path from a certain Info's // location. struct Context : public Reference { Context(SymbolID USR, StringRef Name, InfoType IT, StringRef QualName, - StringRef Path, SmallString<16> DocumentationFileName) + StringRef Path, StringRef DocumentationFileName) : Reference(USR, Name, IT, QualName, Path, DocumentationFileName) {} explicit Context(const Info &I); - SmallString<128> RelativePath; + StringRef RelativePath; }; // Holds the children of a record or namespace. @@ -255,12 +297,13 @@ struct TypeInfo { // name and default values in the future if needed. struct TemplateParamInfo { TemplateParamInfo() = default; - explicit TemplateParamInfo(StringRef Contents) : Contents(Contents) {} + explicit TemplateParamInfo(StringRef Contents) + : Contents(internString(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; + StringRef Contents; }; struct TemplateSpecializationInfo { @@ -277,7 +320,7 @@ struct ConstraintInfo { : ConceptRef(USR, Name, InfoType::IT_concept) {} Reference ConceptRef; - SmallString<16> ConstraintExpr; + StringRef ConstraintExpr; }; // Records the template information for a struct or function that is a template @@ -296,18 +339,19 @@ struct FieldTypeInfo : public TypeInfo { FieldTypeInfo() = default; FieldTypeInfo(const TypeInfo &TI, StringRef Name = StringRef(), StringRef DefaultValue = StringRef()) - : TypeInfo(TI), Name(Name), DefaultValue(DefaultValue) {} + : TypeInfo(TI), Name(internString(Name)), + DefaultValue(internString(DefaultValue)) {} bool operator==(const FieldTypeInfo &Other) const { return std::tie(Type, Name, DefaultValue) == std::tie(Other.Type, Other.Name, Other.DefaultValue); } - SmallString<16> Name; // Name associated with this info. + StringRef Name; // Name associated with this info. // When used for function parameters, contains the string representing the // expression of the default value, if any. - SmallString<16> DefaultValue; + StringRef DefaultValue; }; // Info for member types. @@ -336,7 +380,7 @@ struct MemberTypeInfo : public FieldTypeInfo { struct Location { Location(int StartLineNumber = 0, int EndLineNumber = 0, StringRef Filename = StringRef(), bool IsFileInRootDir = false) - : Filename(Filename), StartLineNumber(StartLineNumber), + : Filename(internString(Filename)), StartLineNumber(StartLineNumber), EndLineNumber(EndLineNumber), IsFileInRootDir(IsFileInRootDir) {} bool operator==(const Location &Other) const { @@ -355,7 +399,7 @@ struct Location { std::tie(Other.StartLineNumber, Other.EndLineNumber, Other.Filename); } - SmallString<32> Filename; + StringRef Filename; int StartLineNumber = 0; int EndLineNumber = 0; bool IsFileInRootDir = false; @@ -365,7 +409,7 @@ struct Location { struct Info { Info(InfoType IT = InfoType::IT_default, SymbolID USR = SymbolID(), StringRef Name = StringRef(), StringRef Path = StringRef()) - : Path(Path), Name(Name), USR(USR), IT(IT) {} + : Path(internString(Path)), Name(internString(Name)), USR(USR), IT(IT) {} Info(const Info &Other) = delete; Info(Info &&Other) = default; @@ -376,24 +420,24 @@ struct Info { void mergeBase(Info &&I); bool mergeable(const Info &Other); - llvm::SmallString<16> extractName() const; + StringRef extractName() const; /// Returns the file path for this Info relative to CurrentPath. - llvm::SmallString<64> getRelativeFilePath(const StringRef &CurrentPath) const; + StringRef getRelativeFilePath(const StringRef &CurrentPath) const; /// Returns the basename that should be used for this Info. - llvm::SmallString<16> getFileBaseName() const; + StringRef getFileBaseName() const; // Path of directory where the clang-doc generated file will be saved. - llvm::SmallString<128> Path; + StringRef Path; // Unqualified name of the decl. - SmallString<16> Name; + StringRef Name; // The name used for the file that this info is documented in. // In the JSON generator, infos are documented in files with mangled names. // Thus, we keep track of the physical filename for linking purposes. - SmallString<16> DocumentationFileName; + StringRef DocumentationFileName; // List of parent namespaces for this decl. llvm::SmallVector Namespace; @@ -450,7 +494,7 @@ struct SymbolInfo : public Info { std::optional DefLoc; // Location where this decl is defined. llvm::SmallVector Loc; // Locations where this decl is declared. - SmallString<16> MangledName; + StringRef MangledName; bool IsStatic = false; }; @@ -490,7 +534,7 @@ struct FunctionInfo : public SymbolInfo { Reference Parent; TypeInfo ReturnType; llvm::SmallVector Params; - SmallString<256> Prototype; + StringRef Prototype; // When present, this function is a template or specialization. std::optional Template; @@ -554,7 +598,7 @@ struct TypedefInfo : public SymbolInfo { std::optional Template; // Underlying type declaration - SmallString<16> TypeDeclaration; + StringRef TypeDeclaration; // Indicates if this is a new C++ "using"-style typedef: // using MyVector = std::vector @@ -581,23 +625,24 @@ struct EnumValueInfo { explicit EnumValueInfo(StringRef Name = StringRef(), StringRef Value = StringRef("0"), StringRef ValueExpr = StringRef()) - : Name(Name), Value(Value), ValueExpr(ValueExpr) {} + : Name(internString(Name)), Value(internString(Value)), + ValueExpr(internString(ValueExpr)) {} bool operator==(const EnumValueInfo &Other) const { return std::tie(Name, Value, ValueExpr) == std::tie(Other.Name, Other.Value, Other.ValueExpr); } - SmallString<16> Name; + StringRef Name; // The computed value of the enumeration constant. This could be the result of // evaluating the ValueExpr, or it could be automatically generated according // to C rules. - SmallString<16> Value; + StringRef Value; // Stores the user-supplied initialization expression for this enumeration // constant. This will be empty for implicit enumeration values. - SmallString<16> ValueExpr; + StringRef ValueExpr; /// Comment description of this field. OwningVec Description; @@ -630,7 +675,7 @@ struct ConceptInfo : public SymbolInfo { bool IsType; TemplateInfo Template; - SmallString<16> ConstraintExpression; + StringRef ConstraintExpression; }; struct Index : public Reference { @@ -644,7 +689,7 @@ struct Index : public Reference { bool operator==(const SymbolID &Other) const { return USR == Other; } bool operator<(const Index &Other) const; - std::optional> JumpToSection; + std::optional JumpToSection; llvm::StringMap Children; OwningVec getSortedChildren() const; diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 14c8fcca25ab..cf6d5325178d 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -27,13 +27,13 @@ namespace doc { namespace serialize { namespace { -static SmallString<16> exprToString(const clang::Expr *E) { +static StringRef exprToString(const clang::Expr *E) { clang::LangOptions Opts; clang::PrintingPolicy Policy(Opts); SmallString<16> Result; llvm::raw_svector_ostream OS(Result); E->printPretty(OS, nullptr, Policy); - return Result; + return internString(Result); } } // namespace @@ -86,8 +86,7 @@ void Serializer::getTemplateParameters( // Extract the full function prototype from a FunctionDecl including // Full Decl -llvm::SmallString<256> -Serializer::getFunctionPrototype(const FunctionDecl *FuncDecl) { +StringRef Serializer::getFunctionPrototype(const FunctionDecl *FuncDecl) { llvm::SmallString<256> Result; llvm::raw_svector_ostream Stream(Result); const ASTContext &Ctx = FuncDecl->getASTContext(); @@ -154,10 +153,10 @@ Serializer::getFunctionPrototype(const FunctionDecl *FuncDecl) { if (auto ExceptionSpecType = FuncDecl->getExceptionSpecType()) Stream << " " << ExceptionSpecType; - return Result; // Convert SmallString to std::string for return + return internString(Result); } -llvm::SmallString<16> Serializer::getTypeAlias(const TypeAliasDecl *Alias) { +StringRef Serializer::getTypeAlias(const TypeAliasDecl *Alias) { llvm::SmallString<16> Result; llvm::raw_svector_ostream Stream(Result); const ASTContext &Ctx = Alias->getASTContext(); @@ -167,7 +166,7 @@ llvm::SmallString<16> Serializer::getTypeAlias(const TypeAliasDecl *Alias) { QualType Q = Alias->getUnderlyingType(); Q.print(Stream, Ctx.getPrintingPolicy()); - return Result; + return internString(Result); } // A function to extract the appropriate relative path for a given info's @@ -183,15 +182,15 @@ llvm::SmallString<16> Serializer::getTypeAlias(const TypeAliasDecl *Alias) { // // } // } -llvm::SmallString<128> Serializer::getInfoRelativePath( +StringRef Serializer::getInfoRelativePath( const llvm::SmallVectorImpl &Namespaces) { llvm::SmallString<128> Path; for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R) llvm::sys::path::append(Path, R->Name); - return Path; + return internString(Path); } -llvm::SmallString<128> Serializer::getInfoRelativePath(const Decl *D) { +StringRef Serializer::getInfoRelativePath(const Decl *D) { llvm::SmallVector Namespaces; // The third arg in populateParentNamespaces is a boolean passed by reference, // its value is not relevant in here so it's not used anywhere besides the @@ -220,7 +219,7 @@ public: void visitVerbatimLineComment(const VerbatimLineComment *C); private: - std::string getCommandName(unsigned CommandID) const; + StringRef getCommandName(unsigned CommandID) const; bool isWhitespaceOnly(StringRef S) const; CommentInfo &CurrentCI; @@ -244,87 +243,86 @@ void ClangDocCommentVisitor::visitTextComment(const TextComment *C) { void ClangDocCommentVisitor::visitInlineCommandComment( const InlineCommandComment *C) { - CurrentCI.Name = getCommandName(C->getCommandID()); + CurrentCI.Name = internString(getCommandName(C->getCommandID())); for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I) - CurrentCI.Args.push_back(C->getArgText(I).trim()); + CurrentCI.Args.push_back(internString(C->getArgText(I).trim())); } void ClangDocCommentVisitor::visitHTMLStartTagComment( const HTMLStartTagComment *C) { - CurrentCI.Name = C->getTagName(); + CurrentCI.Name = internString(C->getTagName()); CurrentCI.SelfClosing = C->isSelfClosing(); for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) { const HTMLStartTagComment::Attribute &Attr = C->getAttr(I); - CurrentCI.AttrKeys.push_back(Attr.Name); - CurrentCI.AttrValues.push_back(Attr.Value); + CurrentCI.AttrKeys.push_back(internString(Attr.Name)); + CurrentCI.AttrValues.push_back(internString(Attr.Value)); } } void ClangDocCommentVisitor::visitHTMLEndTagComment( const HTMLEndTagComment *C) { - CurrentCI.Name = C->getTagName(); + CurrentCI.Name = internString(C->getTagName()); CurrentCI.SelfClosing = true; } void ClangDocCommentVisitor::visitBlockCommandComment( const BlockCommandComment *C) { - CurrentCI.Name = getCommandName(C->getCommandID()); + CurrentCI.Name = internString(getCommandName(C->getCommandID())); for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I) - CurrentCI.Args.push_back(C->getArgText(I).trim()); + CurrentCI.Args.push_back(internString(C->getArgText(I).trim())); } void ClangDocCommentVisitor::visitParamCommandComment( const ParamCommandComment *C) { - CurrentCI.Direction = - ParamCommandComment::getDirectionAsString(C->getDirection()); + CurrentCI.Direction = internString( + ParamCommandComment::getDirectionAsString(C->getDirection())); CurrentCI.Explicit = C->isDirectionExplicit(); if (C->hasParamName()) - CurrentCI.ParamName = C->getParamNameAsWritten(); + CurrentCI.ParamName = internString(C->getParamNameAsWritten()); } void ClangDocCommentVisitor::visitTParamCommandComment( const TParamCommandComment *C) { if (C->hasParamName()) - CurrentCI.ParamName = C->getParamNameAsWritten(); + CurrentCI.ParamName = internString(C->getParamNameAsWritten()); } void ClangDocCommentVisitor::visitVerbatimBlockComment( const VerbatimBlockComment *C) { - CurrentCI.Name = getCommandName(C->getCommandID()); - CurrentCI.CloseName = C->getCloseName(); + CurrentCI.Name = internString(getCommandName(C->getCommandID())); + CurrentCI.CloseName = internString(C->getCloseName()); } void ClangDocCommentVisitor::visitVerbatimBlockLineComment( const VerbatimBlockLineComment *C) { if (!isWhitespaceOnly(C->getText())) - CurrentCI.Text = C->getText(); + CurrentCI.Text = internString(C->getText()); } void ClangDocCommentVisitor::visitVerbatimLineComment( const VerbatimLineComment *C) { if (!isWhitespaceOnly(C->getText())) - CurrentCI.Text = C->getText(); + CurrentCI.Text = internString(C->getText()); } bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const { return llvm::all_of(S, isspace); } -std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const { +StringRef ClangDocCommentVisitor::getCommandName(unsigned CommandID) const { const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID); if (Info) - return Info->Name; + return internString(Info->Name); // TODO: Add parsing for \file command. return ""; } // Serializing functions. -std::string Serializer::getSourceCode(const Decl *D, const SourceRange &R) { - return Lexer::getSourceText(CharSourceRange::getTokenRange(R), - D->getASTContext().getSourceManager(), - D->getASTContext().getLangOpts()) - .str(); +StringRef Serializer::getSourceCode(const Decl *D, const SourceRange &R) { + return internString(Lexer::getSourceText( + CharSourceRange::getTokenRange(R), D->getASTContext().getSourceManager(), + D->getASTContext().getLangOpts())); } template @@ -590,7 +588,9 @@ void Serializer::parseParameters(FunctionInfo &I, const FunctionDecl *D) { for (const ParmVarDecl *P : D->parameters()) { FieldTypeInfo &FieldInfo = I.Params.emplace_back( getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString()); - FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange()); + if (std::optional DefaultValue = + getSourceCode(D, P->getDefaultArgRange())) + FieldInfo.DefaultValue = *DefaultValue; } } @@ -611,7 +611,7 @@ void Serializer::parseBases(RecordInfo &I, const CXXRecordDecl *D) { } else if (const RecordDecl *P = getRecordDeclForType(B.getType())) I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record, P->getQualifiedNameAsString(), - getInfoRelativePath(P)); + internString(getInfoRelativePath(P))); else I.Parents.emplace_back(SymbolID(), B.getType().getAsString()); } @@ -619,7 +619,7 @@ void Serializer::parseBases(RecordInfo &I, const CXXRecordDecl *D) { if (const RecordDecl *P = getRecordDeclForType(B.getType())) I.VirtualParents.emplace_back( getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record, - P->getQualifiedNameAsString(), getInfoRelativePath(P)); + P->getQualifiedNameAsString(), internString(getInfoRelativePath(P))); else I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString()); } @@ -732,9 +732,10 @@ void Serializer::populateInfo(Info &I, const T *D, const FullComment *C, ConversionDecl && ConversionDecl->getConversionType() .getTypePtr() ->isTemplateTypeParmType()) - I.Name = "operator " + ConversionDecl->getConversionType().getAsString(); + I.Name = internString("operator " + + ConversionDecl->getConversionType().getAsString()); else - I.Name = D->getNameAsString(); + I.Name = internString(D->getNameAsString()); populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace); if (C) { I.Description.emplace_back(); @@ -764,9 +765,10 @@ void Serializer::populateSymbolInfo(SymbolInfo &I, const T *D, // different filesystems, with a 5 character buffer for file extensions. if (MangledName.size() > 250) { auto SymbolID = llvm::toStringRef(llvm::toHex(I.USR)).str(); - I.MangledName = MangledName.substr(0, 250 - SymbolID.size()) + SymbolID; + I.MangledName = + internString(MangledName.substr(0, 250 - SymbolID.size()) + SymbolID); } else - I.MangledName = MangledName; + I.MangledName = internString(MangledName); delete Mangler; } @@ -784,7 +786,7 @@ void Serializer::handleCompoundConstraints( auto *Concept = dyn_cast(Constraint); ConstraintInfo CI(getUSRForDecl(Concept->getNamedConcept()), Concept->getNamedConcept()->getNameAsString()); - CI.ConstraintExpr = exprToString(Concept); + CI.ConstraintExpr = internString(exprToString(Concept)); ConstraintInfos.push_back(CI); } } @@ -805,7 +807,7 @@ void Serializer::populateConstraints(TemplateInfo &I, const TemplateDecl *D) { Constraint.ConstraintExpr)) { ConstraintInfo CI(getUSRForDecl(ConstraintExpr->getNamedConcept()), ConstraintExpr->getNamedConcept()->getNameAsString()); - CI.ConstraintExpr = exprToString(ConstraintExpr); + CI.ConstraintExpr = internString(exprToString(ConstraintExpr)); I.Constraints.push_back(std::move(CI)); } else { handleCompoundConstraints(Constraint.ConstraintExpr, I.Constraints); @@ -890,16 +892,16 @@ void Serializer::parseBases(RecordInfo &I, const CXXRecordDecl *D, // Initialized without USR and name, this will be set in the following // if-else stmt. BaseRecordInfo BI( - {}, "", getInfoRelativePath(Base), B.isVirtual(), + {}, "", internString(getInfoRelativePath(Base)), B.isVirtual(), getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()), IsParent); if (const auto *Ty = B.getType()->getAs()) { const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl(); BI.USR = getUSRForDecl(D); - BI.Name = B.getType().getAsString(); + BI.Name = internString(B.getType().getAsString()); } else { BI.USR = getUSRForDecl(Base); - BI.Name = Base->getNameAsString(); + BI.Name = internString(Base->getNameAsString()); } parseFields(BI, Base, PublicOnly, BI.Access); for (const auto &Decl : Base->decls()) @@ -941,9 +943,7 @@ Serializer::emitInfo(const NamespaceDecl *D, const FullComment *FC, if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) return {}; - NSI->Name = D->isAnonymousNamespace() - ? llvm::SmallString<16>("@nonymous_namespace") - : NSI->Name; + NSI->Name = D->isAnonymousNamespace() ? "@nonymous_namespace" : NSI->Name; NSI->Path = getInfoRelativePath(NSI->Namespace); if (NSI->Namespace.empty() && NSI->USR == SymbolID()) return {OwnedPtr{std::move(NSI)}, nullptr}; @@ -1019,7 +1019,7 @@ Serializer::emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc, if (const auto *C = dyn_cast(D)) { if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) { - RI->Name = TD->getNameAsString(); + RI->Name = internString(TD->getNameAsString()); RI->IsTypeDef = true; } // TODO: remove first call to parseBases, that function should be deleted @@ -1027,7 +1027,7 @@ Serializer::emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc, parseBases(*RI, C, /*IsFileInRootDir=*/true, PublicOnly, /*IsParent=*/true); parseFriends(*RI, C); } - RI->Path = getInfoRelativePath(RI->Namespace); + RI->Path = internString(getInfoRelativePath(RI->Namespace)); populateTemplateParameters(RI->Template, D); if (RI->Template) diff --git a/clang-tools-extra/clang-doc/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h index e6b1e6954279..04891bea3322 100644 --- a/clang-tools-extra/clang-doc/Serialize.h +++ b/clang-tools-extra/clang-doc/Serialize.h @@ -90,16 +90,16 @@ private: void getTemplateParameters(const TemplateParameterList *TemplateParams, llvm::raw_ostream &Stream); - llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl); + StringRef getFunctionPrototype(const FunctionDecl *FuncDecl); - llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias); + StringRef getTypeAlias(const TypeAliasDecl *Alias); - llvm::SmallString<128> + StringRef getInfoRelativePath(const llvm::SmallVectorImpl &Namespaces); - llvm::SmallString<128> getInfoRelativePath(const Decl *D); + StringRef getInfoRelativePath(const Decl *D); - std::string getSourceCode(const Decl *D, const SourceRange &R); + llvm::StringRef getSourceCode(const Decl *D, const SourceRange &R); void parseFullComment(const FullComment *C, CommentInfo &CI); diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp index 17b55fa1e124..1a9d144d499b 100644 --- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp @@ -29,7 +29,6 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(TemplateParamInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(TypedefInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(OwnedPtr) -LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>) namespace llvm { namespace yaml { @@ -94,20 +93,6 @@ template <> struct ScalarEnumerationTraits { }; // Scalars to YAML output. -template struct ScalarTraits> { - - static void output(const SmallString &S, void *, llvm::raw_ostream &OS) { - for (const auto &C : S) - OS << C; - } - - static StringRef input(StringRef Scalar, void *, SmallString &Value) { - Value.assign(Scalar.begin(), Scalar.end()); - return StringRef(); - } - - static QuotingType mustQuote(StringRef) { return QuotingType::Single; } -}; template <> struct ScalarTraits { @@ -132,6 +117,33 @@ template <> struct ScalarTraits { static QuotingType mustQuote(StringRef) { return QuotingType::Single; } }; +/// A wrapper for StringRef to force YAML traits to single-quote the string. +struct QuotedString { + StringRef Ref; + QuotedString() = default; + QuotedString(StringRef R) : Ref(R) {} + operator StringRef() const { return Ref; } + bool operator==(const QuotedString &Other) const { return Ref == Other.Ref; } +}; + +template <> struct ScalarTraits { + static void output(const QuotedString &S, void *, llvm::raw_ostream &OS) { + OS << S.Ref; + } + static StringRef input(StringRef Scalar, void *, QuotedString &Value) { + Value.Ref = Scalar; + return StringRef(); + } + static QuotingType mustQuote(StringRef) { return QuotingType::Single; } +}; +} // end namespace yaml +} // end namespace llvm + +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::QuotedString) + +namespace llvm { +namespace yaml { + // Helper functions to map infos to YAML. static void typeInfoMapping(IO &IO, TypeInfo &I) { @@ -140,14 +152,31 @@ static void typeInfoMapping(IO &IO, TypeInfo &I) { static void fieldTypeInfoMapping(IO &IO, FieldTypeInfo &I) { typeInfoMapping(IO, I); - IO.mapOptional("Name", I.Name, SmallString<16>()); - IO.mapOptional("DefaultValue", I.DefaultValue, SmallString<16>()); + + QuotedString QName(I.Name); + IO.mapOptional("Name", QName, QuotedString(StringRef())); + if (!IO.outputting()) + I.Name = QName.Ref; + + QuotedString QDefault(I.DefaultValue); + IO.mapOptional("DefaultValue", QDefault, QuotedString(StringRef())); + if (!IO.outputting()) + I.DefaultValue = QDefault.Ref; } static void infoMapping(IO &IO, Info &I) { IO.mapRequired("USR", I.USR); - IO.mapOptional("Name", I.Name, SmallString<16>()); - IO.mapOptional("Path", I.Path, SmallString<128>()); + + QuotedString QName(I.Name); + IO.mapOptional("Name", QName, QuotedString(StringRef())); + if (!IO.outputting()) + I.Name = QName.Ref; + + QuotedString QPath(I.Path); + IO.mapOptional("Path", QPath, QuotedString(StringRef())); + if (!IO.outputting()) + I.Path = QPath.Ref; + IO.mapOptional("Namespace", I.Namespace, llvm::SmallVector()); IO.mapOptional("Description", I.Description); } @@ -176,18 +205,71 @@ static void recordInfoMapping(IO &IO, RecordInfo &I) { static void commentInfoMapping(IO &IO, CommentInfo &I) { IO.mapOptional("Kind", I.Kind, CommentKind::CK_Unknown); - IO.mapOptional("Text", I.Text, SmallString<64>()); - IO.mapOptional("Name", I.Name, SmallString<16>()); - IO.mapOptional("Direction", I.Direction, SmallString<8>()); - IO.mapOptional("ParamName", I.ParamName, SmallString<16>()); - IO.mapOptional("CloseName", I.CloseName, SmallString<16>()); + + QuotedString QText(I.Text); + IO.mapOptional("Text", QText, QuotedString(StringRef())); + if (!IO.outputting()) + I.Text = QText.Ref; + + QuotedString QName(I.Name); + IO.mapOptional("Name", QName, QuotedString(StringRef())); + if (!IO.outputting()) + I.Name = QName.Ref; + + QuotedString QDirection(I.Direction); + IO.mapOptional("Direction", QDirection, QuotedString(StringRef())); + if (!IO.outputting()) + I.Direction = QDirection.Ref; + + QuotedString QParamName(I.ParamName); + IO.mapOptional("ParamName", QParamName, QuotedString(StringRef())); + if (!IO.outputting()) + I.ParamName = QParamName.Ref; + + QuotedString QCloseName(I.CloseName); + IO.mapOptional("CloseName", QCloseName, QuotedString(StringRef())); + if (!IO.outputting()) + I.CloseName = QCloseName.Ref; + IO.mapOptional("SelfClosing", I.SelfClosing, false); IO.mapOptional("Explicit", I.Explicit, false); - IO.mapOptional("Args", I.Args, llvm::SmallVector, 4>()); - IO.mapOptional("AttrKeys", I.AttrKeys, - llvm::SmallVector, 4>()); - IO.mapOptional("AttrValues", I.AttrValues, - llvm::SmallVector, 4>()); + + std::vector QArgs; + if (IO.outputting()) { + for (auto &S : I.Args) + QArgs.push_back(QuotedString(S)); + } + IO.mapOptional("Args", QArgs, std::vector()); + if (!IO.outputting()) { + I.Args.clear(); + for (auto &Q : QArgs) + I.Args.push_back(Q.Ref); + } + + std::vector QAttrKeys; + if (IO.outputting()) { + for (auto &S : I.AttrKeys) + QAttrKeys.push_back(QuotedString(S)); + } + IO.mapOptional("AttrKeys", QAttrKeys, std::vector()); + if (!IO.outputting()) { + I.AttrKeys.clear(); + for (auto &Q : QAttrKeys) + I.AttrKeys.push_back(Q.Ref); + } + + std::vector QAttrValues; + if (IO.outputting()) { + for (auto &S : I.AttrValues) + QAttrValues.push_back(QuotedString(S)); + } + IO.mapOptional("AttrValues", QAttrValues, std::vector()); + if (!IO.outputting()) { + I.AttrValues.clear(); + for (auto &Q : QAttrValues) + I.AttrValues.push_back(Q.Ref); + } + IO.mapOptional("Children", I.Children); } @@ -196,17 +278,34 @@ static void commentInfoMapping(IO &IO, CommentInfo &I) { template <> struct MappingTraits { static void mapping(IO &IO, Location &Loc) { IO.mapOptional("LineNumber", Loc.StartLineNumber, 0); - IO.mapOptional("Filename", Loc.Filename, SmallString<32>()); + + QuotedString QFilename(Loc.Filename); + IO.mapOptional("Filename", QFilename, QuotedString(StringRef())); + if (!IO.outputting()) + Loc.Filename = QFilename.Ref; } }; template <> struct MappingTraits { 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>()); + + QuotedString QName(Ref.Name); + IO.mapOptional("Name", QName, QuotedString(StringRef())); + if (!IO.outputting()) + Ref.Name = QName.Ref; + + QuotedString QQualName(Ref.QualName); + IO.mapOptional("QualName", QQualName, QuotedString(StringRef())); + if (!IO.outputting()) + Ref.QualName = QQualName.Ref; + IO.mapOptional("USR", Ref.USR, SymbolID()); - IO.mapOptional("Path", Ref.Path, SmallString<128>()); + + QuotedString QPath(Ref.Path); + IO.mapOptional("Path", QPath, QuotedString(StringRef())); + if (!IO.outputting()) + Ref.Path = QPath.Ref; } }; @@ -217,8 +316,16 @@ template <> struct MappingTraits { template <> struct MappingTraits { static void mapping(IO &IO, FieldTypeInfo &I) { typeInfoMapping(IO, I); - IO.mapOptional("Name", I.Name, SmallString<16>()); - IO.mapOptional("DefaultValue", I.DefaultValue, SmallString<16>()); + + QuotedString QName(I.Name); + IO.mapOptional("Name", QName, QuotedString(StringRef())); + if (!IO.outputting()) + I.Name = QName.Ref; + + QuotedString QDefault(I.DefaultValue); + IO.mapOptional("DefaultValue", QDefault, QuotedString(StringRef())); + if (!IO.outputting()) + I.DefaultValue = QDefault.Ref; } }; @@ -263,9 +370,20 @@ template <> struct MappingTraits { template <> struct MappingTraits { static void mapping(IO &IO, EnumValueInfo &I) { - IO.mapOptional("Name", I.Name); - IO.mapOptional("Value", I.Value); - IO.mapOptional("Expr", I.ValueExpr, SmallString<16>()); + QuotedString QName(I.Name); + IO.mapOptional("Name", QName, QuotedString(StringRef())); + if (!IO.outputting()) + I.Name = QName.Ref; + + QuotedString QValue(I.Value); + IO.mapOptional("Value", QValue, QuotedString(StringRef())); + if (!IO.outputting()) + I.Value = QValue.Ref; + + QuotedString QExpr(I.ValueExpr); + IO.mapOptional("Expr", QExpr, QuotedString(StringRef())); + if (!IO.outputting()) + I.ValueExpr = QExpr.Ref; } }; @@ -303,7 +421,10 @@ template <> struct MappingTraits { template <> struct MappingTraits { static void mapping(IO &IO, TemplateParamInfo &I) { - IO.mapOptional("Contents", I.Contents); + QuotedString QContents(I.Contents); + IO.mapOptional("Contents", QContents, QuotedString(StringRef())); + if (!IO.outputting()) + I.Contents = QContents.Ref; } }; diff --git a/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp b/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp index c8628bdeb0bc..4da314920fa6 100644 --- a/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp +++ b/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp @@ -150,7 +150,7 @@ static void BM_BitcodeReader_Scale(benchmark::State &State) { ClangDocBitcodeWriter Writer(Stream, Diags); for (int i = 0; i < NumRecords; ++i) { RecordInfo RI; - RI.Name = "Record" + std::to_string(i); + RI.Name = internString("Record" + std::to_string(i)); RI.USR = {(uint8_t)(i & 0xFF)}; Writer.emitBlock(RI); } @@ -180,7 +180,6 @@ static void BM_JSONGenerator_Scale(benchmark::State &State) { llvm::consumeError(G.takeError()); return; } - int NumRecords = State.range(0); auto NI = allocatePtr(); NI->Name = "GlobalNamespace"; @@ -218,10 +217,10 @@ static void BM_Index_Insertion(benchmark::State &State) { Index Idx; for (int i = 0; i < State.range(0); ++i) { RecordInfo I; - I.Name = "Record" + std::to_string(i); + I.Name = internString("Record" + std::to_string(i)); // Vary USR to ensure unique entries I.USR = {(uint8_t)(i & 0xFF), (uint8_t)((i >> 8) & 0xFF)}; - I.Path = "path/to/record"; + I.Path = internString("path/to/record"); Generator::addInfoToIndex(Idx, &I); } benchmark::DoNotOptimize(Idx); diff --git a/clang-tools-extra/unittests/clang-doc/GeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/GeneratorTest.cpp index b6c4793aef3d..38f37bd53523 100644 --- a/clang-tools-extra/unittests/clang-doc/GeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/GeneratorTest.cpp @@ -15,7 +15,9 @@ namespace clang { namespace doc { -TEST(GeneratorTest, emitIndex) { +class GeneratorTest : public ClangDocContextTest {}; + +TEST_F(GeneratorTest, emitIndex) { Index Idx; auto InfoA = std::make_unique(); InfoA->Name = "A"; @@ -80,7 +82,7 @@ TEST(GeneratorTest, emitIndex) { CheckIndex(ExpectedIdx, Idx); } -TEST(GeneratorTest, sortIndex) { +TEST_F(GeneratorTest, sortIndex) { Index Idx; Index IndexA; IndexA.Name = "a";