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/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" + ); +});