From 950dfd47c7dea8bd8fba59d81155340c9a494b0c Mon Sep 17 00:00:00 2001 From: marcvernet31 Date: Wed, 29 Oct 2025 19:41:47 +0100 Subject: [PATCH 1/2] fix(forge doc): use relative path instead of full path --- crates/doc/src/builder.rs | 7 +-- crates/doc/src/document.rs | 4 +- .../src/preprocessor/contract_inheritance.rs | 2 +- crates/doc/src/writer/as_doc.rs | 16 +------ crates/forge/tests/cli/doc.rs | 48 +++++++++++++++++++ 5 files changed, 57 insertions(+), 20 deletions(-) diff --git a/crates/doc/src/builder.rs b/crates/doc/src/builder.rs index a4019db4a6a3a..3788df8e7edfa 100644 --- a/crates/doc/src/builder.rs +++ b/crates/doc/src/builder.rs @@ -124,6 +124,7 @@ impl DocBuilder { .collect::>(); let out_dir = self.out_dir()?; + let out_target_dir = out_dir.clone(); let documents = compiler.enter_mut(|compiler| -> eyre::Result>> { let gcx = compiler.gcx(); let documents = combined_sources @@ -197,7 +198,7 @@ impl DocBuilder { path.clone(), target_path, from_library, - self.config.out.clone(), + out_target_dir.clone(), ) .with_content(DocumentContent::Single(item), ident)) }) @@ -231,7 +232,7 @@ impl DocBuilder { path.clone(), target_path, from_library, - self.config.out.clone(), + out_target_dir.clone(), ) .with_content(DocumentContent::Constants(consts), identity), ) @@ -250,7 +251,7 @@ impl DocBuilder { path.clone(), target_path, from_library, - self.config.out.clone(), + out_target_dir.clone(), ) .with_content( DocumentContent::OverloadedFunctions(funcs), diff --git a/crates/doc/src/document.rs b/crates/doc/src/document.rs index cabc230e13b14..067ee86473c75 100644 --- a/crates/doc/src/document.rs +++ b/crates/doc/src/document.rs @@ -1,4 +1,4 @@ -use crate::{DocBuilder, ParseItem, PreprocessorId, PreprocessorOutput}; +use crate::{ParseItem, PreprocessorId, PreprocessorOutput}; use alloy_primitives::map::HashMap; use std::{ path::{Path, PathBuf}, @@ -69,7 +69,7 @@ impl Document { } fn try_relative_output_path(&self) -> Option<&Path> { - self.target_path.strip_prefix(&self.out_target_dir).ok()?.strip_prefix(DocBuilder::SRC).ok() + self.target_path.strip_prefix(&self.out_target_dir).ok() } /// Returns the relative path of the document output. diff --git a/crates/doc/src/preprocessor/contract_inheritance.rs b/crates/doc/src/preprocessor/contract_inheritance.rs index 6d4195700bcc4..1c2159131b01a 100644 --- a/crates/doc/src/preprocessor/contract_inheritance.rs +++ b/crates/doc/src/preprocessor/contract_inheritance.rs @@ -62,7 +62,7 @@ impl ContractInheritance { && let ParseSource::Contract(ref contract) = item.source && base == contract.name.safe_unwrap().name { - return Some(candidate.target_path.clone()); + return Some(candidate.relative_output_path().to_path_buf()); } } None diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index ef6bf3875fcbb..b7d1490c1ccd2 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -9,7 +9,7 @@ use crate::{ }; use itertools::Itertools; use solang_parser::pt::{Base, FunctionDefinition}; -use std::path::{Path, PathBuf}; +use std::path::Path; /// The result of [`AsDoc::as_doc`]. pub type AsDocResult = Result; @@ -141,9 +141,6 @@ impl AsDoc for Document { if !contract.base.is_empty() { writer.write_bold("Inherits:")?; - // we need this to find the _relative_ paths - let src_target_dir = self.target_src_dir(); - let mut bases = vec![]; let linked = read_context!(self, CONTRACT_INHERITANCE_ID, ContractInheritance); @@ -155,11 +152,7 @@ impl AsDoc for Document { .as_ref() .and_then(|link| { link.get(base_ident).map(|path| { - let path = Path::new("/").join( - path.strip_prefix(&src_target_dir) - .ok() - .unwrap_or(path), - ); + let path = Path::new("/").join(path); Markdown::Link(&base_doc, &path.display().to_string()) .as_doc() }) @@ -287,11 +280,6 @@ impl AsDoc for Document { } impl Document { - /// Where all the source files are written to - fn target_src_dir(&self) -> PathBuf { - self.out_target_dir.join("src") - } - /// Writes a function to the buffer. fn write_function( &self, diff --git a/crates/forge/tests/cli/doc.rs b/crates/forge/tests/cli/doc.rs index fad1294b8fe09..97c829cade1d1 100644 --- a/crates/forge/tests/cli/doc.rs +++ b/crates/forge/tests/cli/doc.rs @@ -69,3 +69,51 @@ contract Example is IExample { assert!(content.contains("Process multiple addresses")); assert!(content.contains("Process an address with a value")); }); + +// Test that hyperlinks use relative paths, not absolute paths +// fixes +forgetest_init!(hyperlinks_use_relative_paths, |prj, cmd| { + prj.add_source( + "IBase.sol", + r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IBase { + function baseFunction() external; +} +"#, + ); + + prj.add_source( + "Derived.sol", + r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./IBase.sol"; + +/// @dev Inherits: {IBase} +contract Derived is IBase { + function baseFunction() external override {} +} +"#, + ); + + cmd.args(["doc", "--build"]).assert_success(); + + let doc_path = prj.root().join("docs/src/src/Derived.sol/contract.Derived.md"); + let content = std::fs::read_to_string(&doc_path).unwrap(); + + assert!( + content.contains("[IBase](/src/"), + "Hyperlink should use relative path starting with /src/, but found: {:?}", + content.lines().find(|line| line.contains("[IBase]")).unwrap_or("not found") + ); + assert!(!content.contains("/Users/"), "Hyperlink should not contain absolute path /Users/"); + assert!(!content.contains("/home/"), "Hyperlink should not contain absolute path /home/"); + assert!( + content.contains("IBase.sol/interface.IBase.md"), + "Hyperlink should point to IBase.sol/interface.IBase.md" + ); +}); From 313f2ace3bd6e37770ffbda16490f68038ed114d Mon Sep 17 00:00:00 2001 From: marcvernet31 Date: Wed, 5 Nov 2025 21:03:41 +0100 Subject: [PATCH 2/2] Revert strip_prefix back on try_relative_output_path --- crates/doc/src/document.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/doc/src/document.rs b/crates/doc/src/document.rs index 067ee86473c75..cabc230e13b14 100644 --- a/crates/doc/src/document.rs +++ b/crates/doc/src/document.rs @@ -1,4 +1,4 @@ -use crate::{ParseItem, PreprocessorId, PreprocessorOutput}; +use crate::{DocBuilder, ParseItem, PreprocessorId, PreprocessorOutput}; use alloy_primitives::map::HashMap; use std::{ path::{Path, PathBuf}, @@ -69,7 +69,7 @@ impl Document { } fn try_relative_output_path(&self) -> Option<&Path> { - self.target_path.strip_prefix(&self.out_target_dir).ok() + self.target_path.strip_prefix(&self.out_target_dir).ok()?.strip_prefix(DocBuilder::SRC).ok() } /// Returns the relative path of the document output.