diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp index 1f2fb0a8b2b85..d7233459bdd61 100644 --- a/clang-tools-extra/clang-doc/BitcodeReader.cpp +++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp @@ -85,7 +85,7 @@ llvm::Error decodeRecord(const Record &R, std::optional &Field, if (R[0] > INT_MAX) return llvm::createStringError(llvm::inconvertibleErrorCode(), "integer too large to parse"); - Field.emplace((int)R[0], Blob, (bool)R[1]); + Field.emplace((int)R[0], (int)R[1], Blob, (bool)R[2]); return llvm::Error::success(); } @@ -135,7 +135,7 @@ llvm::Error decodeRecord(const Record &R, if (R[0] > INT_MAX) return llvm::createStringError(llvm::inconvertibleErrorCode(), "integer too large to parse"); - Field.emplace_back((int)R[0], Blob, (bool)R[1]); + Field.emplace_back((int)R[0], (int)R[1], Blob, (bool)R[2]); return llvm::Error::success(); } diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp index 06f30f76e33d8..402386443dbc0 100644 --- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp +++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp @@ -77,13 +77,16 @@ static void LocationAbbrev(std::shared_ptr &Abbrev) { {// 0. Fixed-size integer (line number) llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, BitCodeConstants::LineNumberSize), - // 1. Boolean (IsFileInRootDir) + // 1. Fixed-size integer (start line number) + llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, + BitCodeConstants::LineNumberSize), + // 2. Boolean (IsFileInRootDir) llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, BitCodeConstants::BoolSize), - // 2. Fixed-size integer (length of the following string (filename)) + // 3. Fixed-size integer (length of the following string (filename)) llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, BitCodeConstants::StringLengthSize), - // 3. The string blob + // 4. The string blob llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)}); } @@ -352,7 +355,8 @@ void ClangDocBitcodeWriter::emitRecord(const Location &Loc, RecordId ID) { if (!prepRecordData(ID, true)) return; // FIXME: Assert that the line number is of the appropriate size. - Record.push_back(Loc.LineNumber); + Record.push_back(Loc.StartLineNumber); + Record.push_back(Loc.EndLineNumber); assert(Loc.Filename.size() < (1U << BitCodeConstants::StringLengthSize)); Record.push_back(Loc.IsFileInRootDir); Record.push_back(Loc.Filename.size()); diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp index f559933fc2283..b9be8ceb3f66c 100644 --- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -495,7 +495,7 @@ static std::unique_ptr writeSourceFileRef(const ClangDocContext &CDCtx, if (!L.IsFileInRootDir && !CDCtx.RepositoryUrl) return std::make_unique( - HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) + + HTMLTag::TAG_P, "Defined at line " + std::to_string(L.StartLineNumber) + " of file " + L.Filename); SmallString<128> FileURL(CDCtx.RepositoryUrl.value_or("")); @@ -512,13 +512,14 @@ static std::unique_ptr writeSourceFileRef(const ClangDocContext &CDCtx, llvm::sys::path::Style::windows)); auto Node = std::make_unique(HTMLTag::TAG_P); Node->Children.emplace_back(std::make_unique("Defined at line ")); - auto LocNumberNode = - std::make_unique(HTMLTag::TAG_A, std::to_string(L.LineNumber)); + auto LocNumberNode = std::make_unique( + HTMLTag::TAG_A, std::to_string(L.StartLineNumber)); // The links to a specific line in the source code use the github / // googlesource notation so it won't work for all hosting pages. LocNumberNode->Attributes.emplace_back( - "href", formatv("{0}#{1}{2}", FileURL, - CDCtx.RepositoryLinePrefix.value_or(""), L.LineNumber)); + "href", + formatv("{0}#{1}{2}", FileURL, CDCtx.RepositoryLinePrefix.value_or(""), + L.StartLineNumber)); Node->Children.emplace_back(std::move(LocNumberNode)); Node->Children.emplace_back(std::make_unique(" of file ")); auto LocFileNode = std::make_unique( diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp index 5c782fcc10da5..ce147de5e54d8 100644 --- a/clang-tools-extra/clang-doc/MDGenerator.cpp +++ b/clang-tools-extra/clang-doc/MDGenerator.cpp @@ -56,12 +56,12 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L, raw_ostream &OS) { if (!CDCtx.RepositoryUrl) { - OS << "*Defined at " << L.Filename << "#" << std::to_string(L.LineNumber) - << "*"; + OS << "*Defined at " << L.Filename << "#" + << std::to_string(L.StartLineNumber) << "*"; } else { OS << formatv("*Defined at [#{0}{1}{2}](#{0}{1}{3})*", - CDCtx.RepositoryLinePrefix.value_or(""), L.LineNumber, + CDCtx.RepositoryLinePrefix.value_or(""), L.StartLineNumber, L.Filename, *CDCtx.RepositoryUrl); } OS << "\n\n"; diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp index 6c90db03424c6..c210338f7863f 100644 --- a/clang-tools-extra/clang-doc/Mapper.cpp +++ b/clang-tools-extra/clang-doc/Mapper.cpp @@ -28,6 +28,18 @@ template bool isTypedefAnonRecord(const T *D) { return false; } +Location MapASTVisitor::getDeclLocation(const NamedDecl *D) const { + bool IsFileInRootDir; + llvm::SmallString<128> File = + getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir); + ASTContext &Context = D->getASTContext(); + int Start = + Context.getSourceManager().getPresumedLoc(D->getBeginLoc()).getLine(); + int End = Context.getSourceManager().getPresumedLoc(D->getEndLoc()).getLine(); + + return Location(Start, End, File, IsFileInRootDir); +} + void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) { TraverseDecl(Context.getTranslationUnitDecl()); } @@ -60,8 +72,7 @@ bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) { llvm::SmallString<128> File = getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir); auto I = serialize::emitInfo(D, getComment(D, D->getASTContext()), - getLine(D, D->getASTContext()), File, - IsFileInRootDir, CDCtx.PublicOnly); + getDeclLocation(D), CDCtx.PublicOnly); // A null in place of I indicates that the serializer is skipping this decl // for some reason (e.g. we're only reporting public decls). diff --git a/clang-tools-extra/clang-doc/Mapper.h b/clang-tools-extra/clang-doc/Mapper.h index 75c8e947c8f90..36322ea2bfb77 100644 --- a/clang-tools-extra/clang-doc/Mapper.h +++ b/clang-tools-extra/clang-doc/Mapper.h @@ -46,6 +46,9 @@ class MapASTVisitor : public clang::RecursiveASTVisitor, template bool mapDecl(const T *D, bool IsDefinition); int getLine(const NamedDecl *D, const ASTContext &Context) const; + + Location getDeclLocation(const NamedDecl *D) const; + llvm::SmallString<128> getFile(const NamedDecl *D, const ASTContext &Context, StringRef RootDir, bool &IsFileInRootDir) const; diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index 2153b62864ee3..eebf0c799c1f0 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -238,19 +238,19 @@ struct MemberTypeInfo : public FieldTypeInfo { }; struct Location { - Location(int LineNumber = 0, StringRef Filename = StringRef(), - bool IsFileInRootDir = false) - : LineNumber(LineNumber), Filename(Filename), - IsFileInRootDir(IsFileInRootDir) {} + Location(int StartLineNumber = 0, int EndLineNumber = 0, + StringRef Filename = StringRef(), bool IsFileInRootDir = false) + : StartLineNumber(StartLineNumber), EndLineNumber(EndLineNumber), + Filename(Filename), IsFileInRootDir(IsFileInRootDir) {} bool operator==(const Location &Other) const { - return std::tie(LineNumber, Filename) == - std::tie(Other.LineNumber, Other.Filename); + return std::tie(StartLineNumber, EndLineNumber, Filename) == + std::tie(Other.StartLineNumber, Other.EndLineNumber, Other.Filename); } bool operator!=(const Location &Other) const { - return std::tie(LineNumber, Filename) != - std::tie(Other.LineNumber, Other.Filename); + return std::tie(StartLineNumber, Filename) != + std::tie(Other.StartLineNumber, Other.Filename); } // This operator is used to sort a vector of Locations. @@ -258,11 +258,12 @@ struct Location { // sort is enough, the order is only needed to call std::unique after sorting // the vector. bool operator<(const Location &Other) const { - return std::tie(LineNumber, Filename) < - std::tie(Other.LineNumber, Other.Filename); + return std::tie(StartLineNumber, Filename) < + std::tie(Other.StartLineNumber, Other.Filename); } - int LineNumber = 0; // Line number of this Location. + int StartLineNumber = 0; // Line number of this Location. + int EndLineNumber = 0; SmallString<32> Filename; // File for this Location. bool IsFileInRootDir = false; // Indicates if file is inside root directory }; diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index f737fc75135a1..ce21a9bc8c109 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -527,22 +527,18 @@ static void populateInfo(Info &I, const T *D, const FullComment *C, template static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, - int LineNumber, StringRef Filename, - bool IsFileInRootDir, - bool &IsInAnonymousNamespace) { + Location Loc, bool &IsInAnonymousNamespace) { populateInfo(I, D, C, IsInAnonymousNamespace); if (D->isThisDeclarationADefinition()) - I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir); + I.DefLoc = Loc; else - I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir); + I.Loc.emplace_back(Loc); } static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, - const FullComment *FC, int LineNumber, - StringRef Filename, bool IsFileInRootDir, + const FullComment *FC, Location Loc, bool &IsInAnonymousNamespace) { - populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir, - IsInAnonymousNamespace); + populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace); auto &LO = D->getLangOpts(); I.ReturnType = getTypeInfoForType(D->getReturnType(), LO); parseParameters(I, D); @@ -571,7 +567,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) { assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo"); - ASTContext& Context = D->getASTContext(); + ASTContext &Context = D->getASTContext(); // TODO investigate whether we can use ASTContext::getCommentForDecl instead // of this logic. See also similar code in Mapper.cpp. RawComment *Comment = Context.getRawCommentForDeclNoCache(D); @@ -623,8 +619,7 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, // reference, its value is not relevant in here so it's not used // anywhere besides the function call. bool IsInAnonymousNamespace; - populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{}, - /*FileName=*/{}, IsFileInRootDir, + populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{}, IsInAnonymousNamespace); FI.Access = getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe()); @@ -642,8 +637,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, } std::pair, std::unique_ptr> -emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { +emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly) { auto I = std::make_unique(); bool IsInAnonymousNamespace = false; populateInfo(*I, D, FC, IsInAnonymousNamespace); @@ -663,12 +658,11 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, } std::pair, std::unique_ptr> -emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { +emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly) { auto I = std::make_unique(); bool IsInAnonymousNamespace = false; - populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir, - IsInAnonymousNamespace); + populateSymbolInfo(*I, D, FC, Loc, IsInAnonymousNamespace); if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) return {}; @@ -681,7 +675,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, } // TODO: remove first call to parseBases, that function should be deleted parseBases(*I, C); - parseBases(*I, C, IsFileInRootDir, PublicOnly, true); + parseBases(*I, C, true, PublicOnly, true); } I->Path = getInfoRelativePath(I->Namespace); @@ -730,12 +724,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, } std::pair, std::unique_ptr> -emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { +emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly) { FunctionInfo Func; bool IsInAnonymousNamespace = false; - populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir, - IsInAnonymousNamespace); + populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace); Func.Access = clang::AccessSpecifier::AS_none; if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) return {}; @@ -745,12 +738,11 @@ emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, } std::pair, std::unique_ptr> -emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { +emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly) { FunctionInfo Func; bool IsInAnonymousNamespace = false; - populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir, - IsInAnonymousNamespace); + populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace); if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) return {}; @@ -774,18 +766,21 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, } std::pair, std::unique_ptr> -emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool IsFileInRootDir, bool PublicOnly) { - TypedefInfo Info; +emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly) { + TypedefInfo Info; + ASTContext &Context = D->getASTContext(); bool IsInAnonymousNamespace = false; populateInfo(Info, D, FC, IsInAnonymousNamespace); + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) return {}; - Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir); + Info.DefLoc = Loc; auto &LO = D->getLangOpts(); Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO); + if (Info.Underlying.Type.Name.empty()) { // Typedef for an unnamed type. This is like "typedef struct { } Foo;" // The record serializer explicitly checks for this syntax and constructs @@ -793,7 +788,13 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber, return {}; } Info.IsUsing = false; - + if (RawComment *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) { + Comment->setAttached(); + if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) { + Info.Description.emplace_back(); + parseFullComment(Fc, Info.Description.back()); + } + } // Info is wrapped in its parent scope so is returned in the second position. return {nullptr, MakeAndInsertIntoParent(std::move(Info))}; } @@ -801,8 +802,8 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber, // A type alias is a C++ "using" declaration for a type. It gets mapped to a // TypedefInfo with the IsUsing flag set. std::pair, std::unique_ptr> -emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool IsFileInRootDir, bool PublicOnly) { +emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly) { TypedefInfo Info; bool IsInAnonymousNamespace = false; @@ -810,7 +811,7 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber, if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) return {}; - Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir); + Info.DefLoc = Loc; auto &LO = D->getLangOpts(); Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO); Info.IsUsing = true; @@ -820,12 +821,12 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber, } std::pair, std::unique_ptr> -emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { +emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly) { EnumInfo Enum; bool IsInAnonymousNamespace = false; - populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir, - IsInAnonymousNamespace); + populateSymbolInfo(Enum, D, FC, Loc, IsInAnonymousNamespace); + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) return {}; diff --git a/clang-tools-extra/clang-doc/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h index 4e203ca7891ac..bdf969aa56b97 100644 --- a/clang-tools-extra/clang-doc/Serialize.h +++ b/clang-tools-extra/clang-doc/Serialize.h @@ -37,32 +37,32 @@ namespace serialize { // its parent scope. For NamespaceDecl and RecordDecl both elements are not // nullptr. std::pair, std::unique_ptr> -emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool IsFileInRootDir, bool PublicOnly); +emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly); std::pair, std::unique_ptr> -emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool IsFileInRootDir, bool PublicOnly); +emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly); std::pair, std::unique_ptr> -emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool IsFileInRootDir, bool PublicOnly); +emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly); std::pair, std::unique_ptr> -emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool IsFileInRootDir, bool PublicOnly); +emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly); std::pair, std::unique_ptr> -emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool IsFileInRootDir, bool PublicOnly); +emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly); std::pair, std::unique_ptr> -emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool IsFileInRootDir, bool PublicOnly); +emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly); std::pair, std::unique_ptr> -emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool IsFileInRootDir, bool PublicOnly); +emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc, + bool PublicOnly); // Function to hash a given USR value for storage. // As USRs (Unified Symbol Resolution) could be large, especially for functions diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp index ffabd2fd82229..d990dd4b647e8 100644 --- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp @@ -169,7 +169,7 @@ static void CommentInfoMapping(IO &IO, CommentInfo &I) { template <> struct MappingTraits { static void mapping(IO &IO, Location &Loc) { - IO.mapOptional("LineNumber", Loc.LineNumber, 0); + IO.mapOptional("LineNumber", Loc.StartLineNumber, 0); IO.mapOptional("Filename", Loc.Filename, SmallString<32>()); } };