@@ -26,16 +26,13 @@ use std::fmt::Write;
2626use std:: mem;
2727use std:: ops:: Range ;
2828
29- use crate :: clean:: { self , utils:: find_nearest_parent_module} ;
29+ use crate :: { clean:: { self , utils:: find_nearest_parent_module} , html :: { format :: HrefError , markdown :: { MarkdownLink , markdown_links } } , lint :: { PRIVATE_INTRA_DOC_LINKS , BROKEN_INTRA_DOC_LINKS } , visit :: DocVisitor } ;
3030use crate :: clean:: { Crate , Item , ItemId , ItemLink , PrimitiveType } ;
3131use crate :: core:: DocContext ;
32- use crate :: html:: markdown:: { markdown_links, MarkdownLink } ;
33- use crate :: lint:: { BROKEN_INTRA_DOC_LINKS , PRIVATE_INTRA_DOC_LINKS } ;
34- use crate :: passes:: Pass ;
35- use crate :: visit:: DocVisitor ;
3632
3733mod early;
3834crate use early:: early_resolve_intra_doc_links;
35+ use super :: Pass ;
3936
4037crate const COLLECT_INTRA_DOC_LINKS : Pass = Pass {
4138 name : "collect-intra-doc-links" ,
@@ -1413,6 +1410,7 @@ impl LinkCollector<'_, '_> {
14131410 }
14141411 }
14151412
1413+ let mut had_error = false ;
14161414 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
14171415 if let Some ( ( src_id, dst_id) ) = id
14181416 . as_local ( )
@@ -1426,9 +1424,21 @@ impl LinkCollector<'_, '_> {
14261424 && !self . cx . tcx . privacy_access_levels ( ( ) ) . is_exported ( dst_id)
14271425 {
14281426 privacy_error ( self . cx , diag_info, path_str) ;
1427+ had_error = true ;
14291428 }
14301429 }
14311430
1431+ // Even if the item exists and is public, it may not have documentation generated
1432+ // (e.g. if it's marked with `doc(hidden)`, or in a dependency with `--no-deps`).
1433+ // Give a warning if so.
1434+ if had_error {
1435+ return Some ( ( ) ) ;
1436+ }
1437+ // `relative_to` is only used for the actual path of the link, not whether it's resolved or not.
1438+ if let Err ( missing) = crate :: html:: format:: href_inner ( id, self . cx . tcx , & self . cx . cache , None , & [ ] ) {
1439+ missing_docs_for_link ( self . cx , & item, id, missing, & path_str, & diag_info) ;
1440+ }
1441+
14321442 Some ( ( ) )
14331443 }
14341444
@@ -2294,6 +2304,81 @@ fn suggest_disambiguator(
22942304 }
22952305}
22962306
2307+ /// Report a link to an item that will not have documentation generated.
2308+ fn missing_docs_for_link (
2309+ cx : & DocContext < ' _ > ,
2310+ item : & Item ,
2311+ destination_id : DefId ,
2312+ why : HrefError ,
2313+ path_str : & str ,
2314+ diag_info : & DiagnosticInfo < ' _ > ,
2315+ ) {
2316+ // FIXME: this is a bug, rustdoc should load all items into the cache before doing this check.
2317+ // Otherwise, there will be false negatives where rustdoc doesn't warn but should.
2318+ if why == HrefError :: NotInExternalCache {
2319+ return ;
2320+ }
2321+
2322+ let item_name = match item. name {
2323+ Some ( name) => name. to_string ( ) ,
2324+ None => "<unknown>" . into ( ) ,
2325+ } ;
2326+ let msg = format ! (
2327+ "documentation for `{}` links to item `{}` which will not have documentation generated" ,
2328+ item_name, path_str
2329+ ) ;
2330+
2331+ report_diagnostic ( cx. tcx , PRIVATE_INTRA_DOC_LINKS , & msg, diag_info, |diag, sp| {
2332+ use crate :: clean:: types:: { AttributesExt , NestedAttributesExt } ;
2333+
2334+ if let Some ( sp) = sp {
2335+ diag. span_label ( sp, "this item is will not be documented" ) ;
2336+ }
2337+
2338+ if why == HrefError :: DocumentationNotBuilt {
2339+ diag. note ( & format ! (
2340+ "`{}` is in the crate `{}`, which will not be documented" ,
2341+ path_str,
2342+ cx. tcx. crate_name( destination_id. krate)
2343+ ) ) ;
2344+ return ;
2345+ }
2346+
2347+ // shouldn't be possible to resolve private items
2348+ assert_ne ! ( why, HrefError :: Private , "{:?}" , destination_id) ;
2349+
2350+ if let Some ( attr) =
2351+ cx. tcx . get_attrs ( destination_id) . lists ( sym:: doc) . get_word_attr ( sym:: hidden)
2352+ {
2353+ diag. span_label ( attr. span ( ) , & format ! ( "`{}` is marked as `#[doc(hidden)]`" , path_str) ) ;
2354+ return ;
2355+ }
2356+
2357+ let mut current = destination_id;
2358+ while let Some ( parent) = cx. tcx . parent ( current) {
2359+ if cx. tcx . get_attrs ( parent) . lists ( sym:: doc) . has_word ( sym:: hidden) {
2360+ let name = cx. tcx . item_name ( parent) ;
2361+ let ( _, description) = cx. tcx . article_and_description ( parent) ;
2362+ let span = cx. tcx . def_span ( parent) ;
2363+ diag. span_label (
2364+ span,
2365+ & format ! (
2366+ "{} is in the {} `{}`, which is marked as `#[doc(hidden)]`" ,
2367+ path_str, description, name
2368+ ) ,
2369+ ) ;
2370+ return ;
2371+ }
2372+ current = parent;
2373+ }
2374+
2375+ diag. note ( & format ! (
2376+ "`{}` may be in a private module with all re-exports marked as `#[doc(no_inline)]`" ,
2377+ path_str
2378+ ) ) ;
2379+ } ) ;
2380+ }
2381+
22972382/// Report a link from a public item to a private one.
22982383fn privacy_error ( cx : & DocContext < ' _ > , diag_info : & DiagnosticInfo < ' _ > , path_str : & str ) {
22992384 let sym;
0 commit comments