[clang-doc] crashes when generating HTML without --repository flag · Issue #131697 · llvm/llvm-project (original) (raw)

Issue Description

After merging PR #122566, the clang-doc tool crashes with an assertion failure when attempting to generate HTML documentation without explicitly specifying the --repository flag. The tool fails with:

/usr/include/c++/14.2.1/optional:475: constexpr _Tp& std::_Optional_base_impl<_Tp, _Dp>::_M_get() [with _Tp = llvm::StringRef; _Dp = std::_Optional_base<llvm::StringRef, true, true>]: Assertion 'this->_M_is_engaged()' failed.

Steps to Reproduce

  1. Build clang-doc from the latest source after merging PR [clang-doc] Make --repository change the HTML output #122566
  2. Run the tool on a C++ file with HTML output format but without the --repository flag:
clang-doc src/Calculator.cpp --format=html -- -I./include  

Stack Trace

/usr/include/c++/14.2.1/optional:475: constexpr _Tp& std::_Optional_base_impl<_Tp, _Dp>::_M_get() [with _Tp = llvm::StringRef; _Dp = std::_Optional_base<llvm::StringRef, true, true>]: Assertion 'this->_M_is_engaged()' failed.
 #0 0x00005ff1c4b6d1de llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/mohamed/dev/contrib/llvm/llvm-project/llvm/lib/Support/Unix/Signals.inc:804:22
 #1 0x00005ff1c4b6d5f1 PrintStackTraceSignalHandler(void*) /home/mohamed/dev/contrib/llvm/llvm-project/llvm/lib/Support/Unix/Signals.inc:880:1
 #2 0x00005ff1c4b6aa83 llvm::sys::RunSignalHandlers() /home/mohamed/dev/contrib/llvm/llvm-project/llvm/lib/Support/Signals.cpp:105:20
 #3 0x00005ff1c4b6ca92 SignalHandler(int, siginfo_t*, void*) /home/mohamed/dev/contrib/llvm/llvm-project/llvm/lib/Support/Unix/Signals.inc:418:13
 #4 0x000074807044bcd0 (/usr/lib/libc.so.6+0x3dcd0)
 #5 0x00007480704a5624 __pthread_kill_implementation /usr/src/debug/glibc/glibc/nptl/pthread_kill.c:44:76
 #6 0x000074807044bba0 raise /usr/src/debug/glibc/glibc/signal/../sysdeps/posix/raise.c:27:6
 #7 0x0000748070433582 abort /usr/src/debug/glibc/glibc/stdlib/abort.c:81:3
 #8 0x00007480706d3f70 std::chrono::_V2::system_clock::now() /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/chrono.cc:52:5
 #9 0x00005ff1c4b20874 std::_Optional_base_impl<llvm::StringRef, std::_Optional_base<llvm::StringRef, true, true>>::_M_get() /usr/include/c++/14.2.1/optional:476:51
#10 0x00005ff1c4b1aba4 std::optional<llvm::StringRef>::operator*() & /usr/include/c++/14.2.1/optional:972:32
#11 0x00005ff1c588885f clang::doc::writeFileDefinition(clang::doc::Location const&, std::optional<llvm::StringRef>) /home/mohamed/dev/contrib/llvm/llvm-project/clang-tools-extra/clang-doc/HTMLGenerator.cpp:501:42
#12 0x00005ff1c588add0 clang::doc::genHTML(clang::doc::FunctionInfo const&, clang::doc::ClangDocContext const&, llvm::StringRef) /home/mohamed/dev/contrib/llvm/llvm-project/clang-tools-extra/clang-doc/HTMLGenerator.cpp:803:43
#13 0x00005ff1c5887dab clang::doc::genFunctionsBlock(std::vector<clang::doc::FunctionInfo, std::allocator<clang::doc::FunctionInfo>> const&, clang::doc::ClangDocContext const&, llvm::StringRef) /home/mohamed/dev/contrib/llvm/llvm-project/clang-tools-extra/clang-doc/HTMLGenerator.cpp:442:43
#14 0x00005ff1c588bcf1 clang::doc::genHTML(clang::doc::RecordInfo const&, clang::doc::Index&, clang::doc::ClangDocContext const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /home/mohamed/dev/contrib/llvm/llvm-project/clang-tools-extra/clang-doc/HTMLGenerator.cpp:908:25
#15 0x00005ff1c588c893 clang::doc::HTMLGenerator::generateDocForInfo(clang::doc::Info*, llvm::raw_ostream&, clang::doc::ClangDocContext const&) /home/mohamed/dev/contrib/llvm/llvm-project/clang-tools-extra/clang-doc/HTMLGenerator.cpp:1014:59
#16 0x00005ff1c588c622 clang::doc::HTMLGenerator::generateDocs(llvm::StringRef, llvm::StringMap<std::unique_ptr<clang::doc::Info, std::default_delete<clang::doc::Info>>, llvm::MallocAllocator>, clang::doc::ClangDocContext const&) /home/mohamed/dev/contrib/llvm/llvm-project/clang-tools-extra/clang-doc/HTMLGenerator.cpp:993:68
#17 0x00005ff1c4a2d2ac main /home/mohamed/dev/contrib/llvm/llvm-project/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp:371:33
#18 0x0000748070435488 __libc_start_call_main /usr/src/debug/glibc/glibc/csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#19 0x000074807043554c call_init /usr/src/debug/glibc/glibc/csu/../csu/libc-start.c:128:20
#20 0x000074807043554c __libc_start_main /usr/src/debug/glibc/glibc/csu/../csu/libc-start.c:347:5
#21 0x00005ff1c4a2b225 _start (/home/mohamed/dev/contrib/llvm/llvm-project/build/bin/clang-doc+0x2e7225)
[1]    910707 IOT instruction (core dumped)  ~/dev/contrib/llvm/llvm-project/build/bin/clang-doc src/Calculator.cpp  --

Root Cause

The issue stems from a logical condition change in PR #122566. In the writeFileDefinition function, the condition was changed from:

if (!L.IsFileInRootDir || !RepositoryUrl)

to:

if (!L.IsFileInRootDir && !RepositoryUrl)

This change means that when RepositoryUrl is not provided (which happens when --repository flag is omitted), the function proceeds to code paths that attempt to dereference the optional without first checking if it contains a value.

Solution

The fix is straightforward - revert the condition back to its original state:

static std::unique_ptr writeFileDefinition(const Location &L, std::optional RepositoryUrl = std::nullopt) {

This change ensures that if RepositoryUrl is not provided, the function takes a safe path that doesn't attempt to dereference the empty optional.