Skip to content

[clang-doc] Add start and end line numbers #137732

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions clang-tools-extra/clang-doc/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ static llvm::Error decodeRecord(const Record &R, std::optional<Location> &Field,
if (R[0] > INT_MAX)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"integer too large to parse");
Field.emplace(static_cast<int>(R[0]), Blob, static_cast<bool>(R[1]));
Field.emplace(static_cast<int>(R[0]), static_cast<int>(R[1]), Blob,
static_cast<bool>(R[2]));
return llvm::Error::success();
}

Expand Down Expand Up @@ -130,7 +131,8 @@ static llvm::Error decodeRecord(const Record &R,
if (R[0] > INT_MAX)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"integer too large to parse");
Field.emplace_back(static_cast<int>(R[0]), Blob, static_cast<bool>(R[1]));
Field.emplace_back(static_cast<int>(R[0]), static_cast<int>(R[1]), Blob,
static_cast<bool>(R[2]));
return llvm::Error::success();
}

Expand Down
12 changes: 8 additions & 4 deletions clang-tools-extra/clang-doc/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,16 @@ static void genLocationAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &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)});
}

Expand Down Expand Up @@ -357,7 +360,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());
Expand Down
11 changes: 6 additions & 5 deletions clang-tools-extra/clang-doc/HTMLGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ static std::unique_ptr<TagNode> writeSourceFileRef(const ClangDocContext &CDCtx,

if (!L.IsFileInRootDir && !CDCtx.RepositoryUrl)
return std::make_unique<TagNode>(
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(""));
Expand All @@ -472,13 +472,14 @@ static std::unique_ptr<TagNode> writeSourceFileRef(const ClangDocContext &CDCtx,
llvm::sys::path::Style::windows));
auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
auto LocNumberNode =
std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
auto LocNumberNode = std::make_unique<TagNode>(
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<TextNode>(" of file "));
auto LocFileNode = std::make_unique<TagNode>(
Expand Down
6 changes: 3 additions & 3 deletions clang-tools-extra/clang-doc/MDGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
17 changes: 14 additions & 3 deletions clang-tools-extra/clang-doc/Mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ template <typename T> static 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);
SourceManager &SM = D->getASTContext().getSourceManager();
int Start = SM.getPresumedLoc(D->getBeginLoc()).getLine();
int End = SM.getPresumedLoc(D->getEndLoc()).getLine();

return Location(Start, End, File, IsFileInRootDir);
}

void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
TraverseDecl(Context.getTranslationUnitDecl());
}
Expand Down Expand Up @@ -59,9 +70,9 @@ bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
bool IsFileInRootDir;
llvm::SmallString<128> File =
getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
auto [Child, Parent] = serialize::emitInfo(
D, getComment(D, D->getASTContext()), getLine(D, D->getASTContext()),
File, IsFileInRootDir, CDCtx.PublicOnly);
auto [Child, Parent] =
serialize::emitInfo(D, getComment(D, D->getASTContext()),
getDeclLocation(D), CDCtx.PublicOnly);

// A null in place of a valid Info indicates that the serializer is skipping
// this decl for some reason (e.g. we're only reporting public decls).
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-doc/Mapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
template <typename T> 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;
Expand Down
24 changes: 11 additions & 13 deletions clang-tools-extra/clang-doc/Representation.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,31 +241,29 @@ 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);
}
bool operator!=(const Location &Other) const { return !(*this == Other); }

// This operator is used to sort a vector of Locations.
// No specific order (attributes more important than others) is required. Any
// 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, EndLineNumber, Filename) <
std::tie(Other.StartLineNumber, Other.EndLineNumber, 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
};
Expand Down
67 changes: 29 additions & 38 deletions clang-tools-extra/clang-doc/Serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,22 +535,18 @@ static void populateInfo(Info &I, const T *D, const FullComment *C,

template <typename T>
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);
Expand Down Expand Up @@ -579,7 +575,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *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);
Expand Down Expand Up @@ -643,8 +639,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());
Expand All @@ -662,8 +657,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
}

std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
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 NSI = std::make_unique<NamespaceInfo>();
bool IsInAnonymousNamespace = false;
populateInfo(*NSI, D, FC, IsInAnonymousNamespace);
Expand All @@ -683,12 +678,11 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
}

std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
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 RI = std::make_unique<RecordInfo>();
bool IsInAnonymousNamespace = false;
populateSymbolInfo(*RI, D, FC, LineNumber, File, IsFileInRootDir,
IsInAnonymousNamespace);
populateSymbolInfo(*RI, D, FC, Loc, IsInAnonymousNamespace);
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};

Expand All @@ -701,7 +695,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
}
// TODO: remove first call to parseBases, that function should be deleted
parseBases(*RI, C);
parseBases(*RI, C, IsFileInRootDir, PublicOnly, true);
parseBases(*RI, C, /*IsFileInRootDir=*/true, PublicOnly, /*IsParent=*/true);
}
RI->Path = getInfoRelativePath(RI->Namespace);

Expand Down Expand Up @@ -750,12 +744,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
}

std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
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 {};
Expand All @@ -765,12 +758,11 @@ emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
}

std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
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 {};

Expand All @@ -795,16 +787,15 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
}

std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
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) {
TypedefInfo Info;

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()) {
Expand All @@ -822,16 +813,16 @@ 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<Info>, std::unique_ptr<Info>>
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;
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);
Info.IsUsing = true;
Expand All @@ -841,12 +832,12 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
}

std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
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 {};

Expand Down
Loading
Loading