summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-27 08:12:29 +0000
committerbors <bors@rust-lang.org>2020-09-27 08:12:29 +0000
commit71bdb8481760f7c6b8540d3dd176ed54a92c0f31 (patch)
treeff759d0a968f0e6f310c88eaf27d618764b021be
parentAuto merge of #77154 - fusion-engineering-forks:lazy-stdio, r=dtolnay (diff)
parentUnify primitive errors with other intra-link errors (diff)
downloadrust-71bdb8481760f7c6b8540d3dd176ed54a92c0f31.tar.gz
rust-71bdb8481760f7c6b8540d3dd176ed54a92c0f31.tar.bz2
rust-71bdb8481760f7c6b8540d3dd176ed54a92c0f31.tar.xz
Auto merge of #76955 - jyn514:refactor-diagnostics, r=euclio
Refactor and fix intra-doc link diagnostics, and fix links to primitives Closes https://github.com/rust-lang/rust/issues/76925, closes https://github.com/rust-lang/rust/issues/76693, closes https://github.com/rust-lang/rust/issues/76692. Originally I only meant to fix #76925. But the hack with `has_primitive` was so bad it was easier to fix the primitive issues than to try and work around it. Note that this still has one bug: `std::primitive::i32::MAX` does not resolve. However, this fixes the ICE so I'm fine with fixing the link in a later PR. This is part of a series of refactors to make #76467 possible. This is best reviewed commit-by-commit; it has detailed commit messages. r? `@euclio`
-rw-r--r--compiler/rustc_hir/src/hir.rs24
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs435
-rw-r--r--src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr2
-rw-r--r--src/test/rustdoc-ui/intra-link-errors.rs27
-rw-r--r--src/test/rustdoc-ui/intra-link-errors.stderr63
-rw-r--r--src/test/rustdoc-ui/intra-link-span-ice-55723.stderr2
-rw-r--r--src/test/rustdoc-ui/intra-links-warning-crlf.stderr8
-rw-r--r--src/test/rustdoc-ui/intra-links-warning.stderr36
-rw-r--r--src/test/rustdoc-ui/lint-group.stderr2
-rw-r--r--src/test/rustdoc/intra-link-associated-items.rs2
-rw-r--r--src/test/rustdoc/primitive-link.rs7
11 files changed, 329 insertions, 279 deletions
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index cd41852..636f67a 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2004,6 +2004,30 @@ pub enum PrimTy {
2004 Char, 2004 Char,
2005} 2005}
2006 2006
2007impl PrimTy {
2008 pub fn name_str(self) -> &'static str {
2009 match self {
2010 PrimTy::Int(i) => i.name_str(),
2011 PrimTy::Uint(u) => u.name_str(),
2012 PrimTy::Float(f) => f.name_str(),
2013 PrimTy::Str => "str",
2014 PrimTy::Bool => "bool",
2015 PrimTy::Char => "char",
2016 }
2017 }
2018
2019 pub fn name(self) -> Symbol {
2020 match self {
2021 PrimTy::Int(i) => i.name(),
2022 PrimTy::Uint(u) => u.name(),
2023 PrimTy::Float(f) => f.name(),
2024 PrimTy::Str => sym::str,
2025 PrimTy::Bool => sym::bool,
2026 PrimTy::Char => sym::char,
2027 }
2028 }
2029}
2030
2007#[derive(Debug, HashStable_Generic)] 2031#[derive(Debug, HashStable_Generic)]
2008pub struct BareFnTy<'hir> { 2032pub struct BareFnTy<'hir> {
2009 pub unsafety: Unsafety, 2033 pub unsafety: Unsafety,
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 93016c4..5d74a3d 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -57,45 +57,16 @@ enum ResolutionFailure<'a> {
57 /// This resolved, but with the wrong namespace. 57 /// This resolved, but with the wrong namespace.
58 /// `Namespace` is the expected namespace (as opposed to the actual). 58 /// `Namespace` is the expected namespace (as opposed to the actual).
59 WrongNamespace(Res, Namespace), 59 WrongNamespace(Res, Namespace),
60 /// This has a partial resolution, but is not in the TypeNS and so cannot 60 /// The link failed to resolve. `resolution_failure` should look to see if there's
61 /// have associated items or fields. 61 /// a more helpful error that can be given.
62 CannotHaveAssociatedItems(Res, Namespace), 62 NotResolved { module_id: DefId, partial_res: Option<Res>, unresolved: Cow<'a, str> },
63 /// `name` is the base name of the path (not necessarily the whole link)
64 NotInScope { module_id: DefId, name: Cow<'a, str> },
65 /// this is a primitive type without an impls (no associated methods)
66 /// when will this actually happen?
67 /// the `Res` is the primitive it resolved to
68 NoPrimitiveImpl(Res, String),
69 /// `[u8::not_found]`
70 /// the `Res` is the primitive it resolved to
71 NoPrimitiveAssocItem { res: Res, prim_name: &'a str, assoc_item: Symbol },
72 /// `[S::not_found]`
73 /// the `String` is the associated item that wasn't found
74 NoAssocItem(Res, Symbol),
75 /// should not ever happen 63 /// should not ever happen
76 NoParentItem, 64 NoParentItem,
77 /// this could be an enum variant, but the last path fragment wasn't resolved.
78 /// the `String` is the variant that didn't exist
79 NotAVariant(Res, Symbol),
80 /// used to communicate that this should be ignored, but shouldn't be reported to the user 65 /// used to communicate that this should be ignored, but shouldn't be reported to the user
81 Dummy, 66 Dummy,
82} 67}
83 68
84impl ResolutionFailure<'a> { 69impl ResolutionFailure<'a> {
85 // A partial or full resolution
86 fn res(&self) -> Option<Res> {
87 use ResolutionFailure::*;
88 match self {
89 NoPrimitiveAssocItem { res, .. }
90 | NoAssocItem(res, _)
91 | NoPrimitiveImpl(res, _)
92 | NotAVariant(res, _)
93 | WrongNamespace(res, _)
94 | CannotHaveAssociatedItems(res, _) => Some(*res),
95 NotInScope { .. } | NoParentItem | Dummy => None,
96 }
97 }
98
99 // This resolved fully (not just partially) but is erroneous for some other reason 70 // This resolved fully (not just partially) but is erroneous for some other reason
100 fn full_res(&self) -> Option<Res> { 71 fn full_res(&self) -> Option<Res> {
101 match self { 72 match self {
@@ -130,22 +101,25 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
130 path_str: &'path str, 101 path_str: &'path str,
131 current_item: &Option<String>, 102 current_item: &Option<String>,
132 module_id: DefId, 103 module_id: DefId,
133 extra_fragment: &Option<String>,
134 ) -> Result<(Res, Option<String>), ErrorKind<'path>> { 104 ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
135 let cx = self.cx; 105 let cx = self.cx;
106 let no_res = || ResolutionFailure::NotResolved {
107 module_id,
108 partial_res: None,
109 unresolved: path_str.into(),
110 };
136 111
137 debug!("looking for enum variant {}", path_str); 112 debug!("looking for enum variant {}", path_str);
138 let mut split = path_str.rsplitn(3, "::"); 113 let mut split = path_str.rsplitn(3, "::");
139 let variant_field_name = split 114 let (variant_field_str, variant_field_name) = split
140 .next() 115 .next()
141 .map(|f| Symbol::intern(f)) 116 .map(|f| (f, Symbol::intern(f)))
142 .expect("fold_item should ensure link is non-empty"); 117 .expect("fold_item should ensure link is non-empty");
143 let variant_name = 118 let (variant_str, variant_name) =
144 // we're not sure this is a variant at all, so use the full string 119 // we're not sure this is a variant at all, so use the full string
145 split.next().map(|f| Symbol::intern(f)).ok_or_else(|| ResolutionFailure::NotInScope { 120 // If there's no second component, the link looks like `[path]`.
146 module_id, 121 // So there's no partial res and we should say the whole link failed to resolve.
147 name: path_str.into(), 122 split.next().map(|f| (f, Symbol::intern(f))).ok_or_else(no_res)?;
148 })?;
149 let path = split 123 let path = split
150 .next() 124 .next()
151 .map(|f| { 125 .map(|f| {
@@ -156,10 +130,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
156 } 130 }
157 f.to_owned() 131 f.to_owned()
158 }) 132 })
159 .ok_or_else(|| ResolutionFailure::NotInScope { 133 // If there's no third component, we saw `[a::b]` before and it failed to resolve.
160 module_id, 134 // So there's no partial res.
161 name: variant_name.to_string().into(), 135 .ok_or_else(no_res)?;
162 })?;
163 let ty_res = cx 136 let ty_res = cx
164 .enter_resolver(|resolver| { 137 .enter_resolver(|resolver| {
165 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) 138 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
@@ -167,7 +140,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
167 .map(|(_, res)| res) 140 .map(|(_, res)| res)
168 .unwrap_or(Res::Err); 141 .unwrap_or(Res::Err);
169 if let Res::Err = ty_res { 142 if let Res::Err = ty_res {
170 return Err(ResolutionFailure::NotInScope { module_id, name: path.into() }.into()); 143 return Err(no_res().into());
171 } 144 }
172 let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); 145 let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
173 match ty_res { 146 match ty_res {
@@ -190,38 +163,27 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
190 ty_res, 163 ty_res,
191 Some(format!( 164 Some(format!(
192 "variant.{}.field.{}", 165 "variant.{}.field.{}",
193 variant_name, variant_field_name 166 variant_str, variant_field_name
194 )), 167 )),
195 )) 168 ))
196 } else { 169 } else {
197 Err(ResolutionFailure::NotAVariant(ty_res, variant_field_name).into()) 170 Err(ResolutionFailure::NotResolved {
171 module_id,
172 partial_res: Some(Res::Def(DefKind::Enum, def.did)),
173 unresolved: variant_field_str.into(),
174 }
175 .into())
198 } 176 }
199 } 177 }
200 _ => unreachable!(), 178 _ => unreachable!(),
201 } 179 }
202 } 180 }
203 // `variant_field` looks at 3 different path segments in a row. 181 _ => Err(ResolutionFailure::NotResolved {
204 // But `NoAssocItem` assumes there are only 2. Check to see if there's 182 module_id,
205 // an intermediate segment that resolves. 183 partial_res: Some(ty_res),
206 _ => { 184 unresolved: variant_str.into(),
207 let intermediate_path = format!("{}::{}", path, variant_name);
208 // NOTE: we have to be careful here, because we're already in `resolve`.
209 // We know this doesn't recurse forever because we use a shorter path each time.
210 // NOTE: this uses `TypeNS` because nothing else has a valid path segment after
211 let kind = if let Some(intermediate) = self.check_full_res(
212 TypeNS,
213 &intermediate_path,
214 module_id,
215 current_item,
216 extra_fragment,
217 ) {
218 ResolutionFailure::NoAssocItem(intermediate, variant_field_name)
219 } else {
220 // Even with the shorter path, it didn't resolve, so say that.
221 ResolutionFailure::NoAssocItem(ty_res, variant_name)
222 };
223 Err(kind.into())
224 } 185 }
186 .into()),
225 } 187 }
226 } 188 }
227 189
@@ -242,11 +204,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
242 false, 204 false,
243 ) { 205 ) {
244 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind { 206 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
245 return Some(Ok(res.map_id(|_| panic!("unexpected id")))); 207 return Ok(res.map_id(|_| panic!("unexpected id")));
246 } 208 }
247 } 209 }
248 if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) { 210 if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
249 return Some(Ok(res.map_id(|_| panic!("unexpected id")))); 211 return Ok(res.map_id(|_| panic!("unexpected id")));
250 } 212 }
251 debug!("resolving {} as a macro in the module {:?}", path_str, module_id); 213 debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
252 if let Ok((_, res)) = 214 if let Ok((_, res)) =
@@ -255,28 +217,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
255 // don't resolve builtins like `#[derive]` 217 // don't resolve builtins like `#[derive]`
256 if let Res::Def(..) = res { 218 if let Res::Def(..) = res {
257 let res = res.map_id(|_| panic!("unexpected node_id")); 219 let res = res.map_id(|_| panic!("unexpected node_id"));
258 return Some(Ok(res)); 220 return Ok(res);
259 }
260 }
261 None
262 })
263 // This weird control flow is so we don't borrow the resolver more than once at a time
264 .unwrap_or_else(|| {
265 let mut split = path_str.rsplitn(2, "::");
266 if let Some((parent, base)) = split.next().and_then(|x| Some((split.next()?, x))) {
267 if let Some(res) = self.check_full_res(TypeNS, parent, module_id, &None, &None) {
268 return Err(if matches!(res, Res::PrimTy(_)) {
269 ResolutionFailure::NoPrimitiveAssocItem {
270 res,
271 prim_name: parent,
272 assoc_item: Symbol::intern(base),
273 }
274 } else {
275 ResolutionFailure::NoAssocItem(res, Symbol::intern(base))
276 });
277 } 221 }
278 } 222 }
279 Err(ResolutionFailure::NotInScope { module_id, name: path_str.into() }) 223 Err(ResolutionFailure::NotResolved {
224 module_id,
225 partial_res: None,
226 unresolved: path_str.into(),
227 })
280 }) 228 })
281 } 229 }
282 230
@@ -312,13 +260,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
312 return handle_variant(cx, res, extra_fragment); 260 return handle_variant(cx, res, extra_fragment);
313 } 261 }
314 // Not a trait item; just return what we found. 262 // Not a trait item; just return what we found.
315 Res::PrimTy(..) => { 263 Res::PrimTy(ty) => {
316 if extra_fragment.is_some() { 264 if extra_fragment.is_some() {
317 return Err(ErrorKind::AnchorFailure( 265 return Err(ErrorKind::AnchorFailure(
318 AnchorFailure::RustdocAnchorConflict(res), 266 AnchorFailure::RustdocAnchorConflict(res),
319 )); 267 ));
320 } 268 }
321 return Ok((res, Some(path_str.to_owned()))); 269 return Ok((res, Some(ty.name_str().to_owned())));
322 } 270 }
323 Res::Def(DefKind::Mod, _) => { 271 Res::Def(DefKind::Mod, _) => {
324 return Ok((res, extra_fragment.clone())); 272 return Ok((res, extra_fragment.clone()));
@@ -331,6 +279,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
331 if value != (ns == ValueNS) { 279 if value != (ns == ValueNS) {
332 return Err(ResolutionFailure::WrongNamespace(res, ns).into()); 280 return Err(ResolutionFailure::WrongNamespace(res, ns).into());
333 } 281 }
282 // FIXME: why is this necessary?
334 } else if let Some((path, prim)) = is_primitive(path_str, ns) { 283 } else if let Some((path, prim)) = is_primitive(path_str, ns) {
335 if extra_fragment.is_some() { 284 if extra_fragment.is_some() {
336 return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(prim))); 285 return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(prim)));
@@ -341,7 +290,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
341 // Try looking for methods and associated items. 290 // Try looking for methods and associated items.
342 let mut split = path_str.rsplitn(2, "::"); 291 let mut split = path_str.rsplitn(2, "::");
343 // this can be an `unwrap()` because we ensure the link is never empty 292 // this can be an `unwrap()` because we ensure the link is never empty
344 let item_name = Symbol::intern(split.next().unwrap()); 293 let (item_str, item_name) = split.next().map(|i| (i, Symbol::intern(i))).unwrap();
345 let path_root = split 294 let path_root = split
346 .next() 295 .next()
347 .map(|f| { 296 .map(|f| {
@@ -356,12 +305,20 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
356 // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. 305 // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
357 .ok_or_else(|| { 306 .ok_or_else(|| {
358 debug!("found no `::`, assumming {} was correctly not in scope", item_name); 307 debug!("found no `::`, assumming {} was correctly not in scope", item_name);
359 ResolutionFailure::NotInScope { module_id, name: item_name.to_string().into() } 308 ResolutionFailure::NotResolved {
309 module_id,
310 partial_res: None,
311 unresolved: item_str.into(),
312 }
360 })?; 313 })?;
361 314
362 if let Some((path, prim)) = is_primitive(&path_root, TypeNS) { 315 if let Some((path, prim)) = is_primitive(&path_root, TypeNS) {
363 let impls = primitive_impl(cx, &path) 316 let impls =
364 .ok_or_else(|| ResolutionFailure::NoPrimitiveImpl(prim, path_root.into()))?; 317 primitive_impl(cx, &path).ok_or_else(|| ResolutionFailure::NotResolved {
318 module_id,
319 partial_res: Some(prim),
320 unresolved: item_str.into(),
321 })?;
365 for &impl_ in impls { 322 for &impl_ in impls {
366 let link = cx 323 let link = cx
367 .tcx 324 .tcx
@@ -377,7 +334,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
377 ty::AssocKind::Const => "associatedconstant", 334 ty::AssocKind::Const => "associatedconstant",
378 ty::AssocKind::Type => "associatedtype", 335 ty::AssocKind::Type => "associatedtype",
379 }) 336 })
380 .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name)))); 337 .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_str))));
381 if let Some(link) = link { 338 if let Some(link) = link {
382 return Ok(link); 339 return Ok(link);
383 } 340 }
@@ -388,10 +345,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
388 item_name, 345 item_name,
389 ns.descr() 346 ns.descr()
390 ); 347 );
391 return Err(ResolutionFailure::NoPrimitiveAssocItem { 348 return Err(ResolutionFailure::NotResolved {
392 res: prim, 349 module_id,
393 prim_name: path, 350 partial_res: Some(prim),
394 assoc_item: item_name, 351 unresolved: item_str.into(),
395 } 352 }
396 .into()); 353 .into());
397 } 354 }
@@ -405,25 +362,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
405 let ty_res = match ty_res { 362 let ty_res = match ty_res {
406 Err(()) | Ok(Res::Err) => { 363 Err(()) | Ok(Res::Err) => {
407 return if ns == Namespace::ValueNS { 364 return if ns == Namespace::ValueNS {
408 self.variant_field(path_str, current_item, module_id, extra_fragment) 365 self.variant_field(path_str, current_item, module_id)
409 } else { 366 } else {
410 // See if it only broke because of the namespace. 367 Err(ResolutionFailure::NotResolved {
411 let kind = cx.enter_resolver(|resolver| { 368 module_id,
412 // NOTE: this doesn't use `check_full_res` because we explicitly want to ignore `TypeNS` (we already checked it) 369 partial_res: None,
413 for &ns in &[MacroNS, ValueNS] { 370 unresolved: path_root.into(),
414 match resolver 371 }
415 .resolve_str_path_error(DUMMY_SP, &path_root, ns, module_id) 372 .into())
416 {
417 Ok((_, Res::Err)) | Err(()) => {}
418 Ok((_, res)) => {
419 let res = res.map_id(|_| panic!("unexpected node_id"));
420 return ResolutionFailure::CannotHaveAssociatedItems(res, ns);
421 }
422 }
423 }
424 ResolutionFailure::NotInScope { module_id, name: path_root.into() }
425 });
426 Err(kind.into())
427 }; 373 };
428 } 374 }
429 Ok(res) => res, 375 Ok(res) => res,
@@ -473,7 +419,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
473 // but the disambiguator logic expects the associated item. 419 // but the disambiguator logic expects the associated item.
474 // Store the kind in a side channel so that only the disambiguator logic looks at it. 420 // Store the kind in a side channel so that only the disambiguator logic looks at it.
475 self.kind_side_channel.set(Some((kind.as_def_kind(), id))); 421 self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
476 Ok((ty_res, Some(format!("{}.{}", out, item_name)))) 422 Ok((ty_res, Some(format!("{}.{}", out, item_str))))
477 }) 423 })
478 } else if ns == Namespace::ValueNS { 424 } else if ns == Namespace::ValueNS {
479 debug!("looking for variants or fields named {} for {:?}", item_name, did); 425 debug!("looking for variants or fields named {} for {:?}", item_name, did);
@@ -516,7 +462,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
516 } 462 }
517 } else { 463 } else {
518 // We already know this isn't in ValueNS, so no need to check variant_field 464 // We already know this isn't in ValueNS, so no need to check variant_field
519 return Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into()); 465 return Err(ResolutionFailure::NotResolved {
466 module_id,
467 partial_res: Some(ty_res),
468 unresolved: item_str.into(),
469 }
470 .into());
520 } 471 }
521 } 472 }
522 Res::Def(DefKind::Trait, did) => cx 473 Res::Def(DefKind::Trait, did) => cx
@@ -540,16 +491,21 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
540 Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res))) 491 Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
541 } else { 492 } else {
542 let res = Res::Def(item.kind.as_def_kind(), item.def_id); 493 let res = Res::Def(item.kind.as_def_kind(), item.def_id);
543 Ok((res, Some(format!("{}.{}", kind, item_name)))) 494 Ok((res, Some(format!("{}.{}", kind, item_str))))
544 } 495 }
545 }), 496 }),
546 _ => None, 497 _ => None,
547 }; 498 };
548 res.unwrap_or_else(|| { 499 res.unwrap_or_else(|| {
549 if ns == Namespace::ValueNS { 500 if ns == Namespace::ValueNS {
550 self.variant_field(path_str, current_item, module_id, extra_fragment) 501 self.variant_field(path_str, current_item, module_id)
551 } else { 502 } else {
552 Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into()) 503 Err(ResolutionFailure::NotResolved {
504 module_id,
505 partial_res: Some(ty_res),
506 unresolved: item_str.into(),
507 }
508 .into())
553 } 509 }
554 }) 510 })
555 } 511 }
@@ -1044,12 +1000,12 @@ impl LinkCollector<'_, '_> {
1044 suggest_disambiguator(resolved, diag, path_str, dox, sp, &link_range); 1000 suggest_disambiguator(resolved, diag, path_str, dox, sp, &link_range);
1045 }); 1001 });
1046 }; 1002 };
1047 if let Res::PrimTy(_) = res { 1003 if let Res::PrimTy(..) = res {
1048 match disambiguator { 1004 match disambiguator {
1049 Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => { 1005 Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
1050 item.attrs.links.push(ItemLink { 1006 item.attrs.links.push(ItemLink {
1051 link: ori_link, 1007 link: ori_link,
1052 link_text: path_str.to_owned(), 1008 link_text,
1053 did: None, 1009 did: None,
1054 fragment, 1010 fragment,
1055 }); 1011 });
@@ -1127,6 +1083,8 @@ impl LinkCollector<'_, '_> {
1127 // We only looked in one namespace. Try to give a better error if possible. 1083 // We only looked in one namespace. Try to give a better error if possible.
1128 if kind.full_res().is_none() { 1084 if kind.full_res().is_none() {
1129 let other_ns = if ns == ValueNS { TypeNS } else { ValueNS }; 1085 let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
1086 // FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
1087 // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
1130 for &new_ns in &[other_ns, MacroNS] { 1088 for &new_ns in &[other_ns, MacroNS] {
1131 if let Some(res) = self.check_full_res( 1089 if let Some(res) = self.check_full_res(
1132 new_ns, 1090 new_ns,
@@ -1529,7 +1487,6 @@ fn resolution_failure(
1529 dox, 1487 dox,
1530 &link_range, 1488 &link_range,
1531 |diag, sp| { 1489 |diag, sp| {
1532 let in_scope = kinds.iter().any(|kind| kind.res().is_some());
1533 let item = |res: Res| { 1490 let item = |res: Res| {
1534 format!( 1491 format!(
1535 "the {} `{}`", 1492 "the {} `{}`",
@@ -1550,53 +1507,142 @@ fn resolution_failure(
1550 // ignore duplicates 1507 // ignore duplicates
1551 let mut variants_seen = SmallVec::<[_; 3]>::new(); 1508 let mut variants_seen = SmallVec::<[_; 3]>::new();
1552 for mut failure in kinds { 1509 for mut failure in kinds {
1553 // Check if _any_ parent of the path gets resolved.
1554 // If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
1555 if let ResolutionFailure::NotInScope { module_id, name } = &mut failure {
1556 let mut current = name.as_ref();
1557 loop {
1558 current = match current.rsplitn(2, "::").nth(1) {
1559 Some(p) => p,
1560 None => {
1561 *name = current.to_owned().into();
1562 break;
1563 }
1564 };
1565 if let Some(res) =
1566 collector.check_full_res(TypeNS, &current, *module_id, &None, &None)
1567 {
1568 failure = ResolutionFailure::NoAssocItem(res, Symbol::intern(current));
1569 break;
1570 }
1571 }
1572 }
1573 let variant = std::mem::discriminant(&failure); 1510 let variant = std::mem::discriminant(&failure);
1574 if variants_seen.contains(&variant) { 1511 if variants_seen.contains(&variant) {
1575 continue; 1512 continue;
1576 } 1513 }
1577 variants_seen.push(variant); 1514 variants_seen.push(variant);
1578 let note = match failure { 1515
1579 ResolutionFailure::NotInScope { module_id, name, .. } => { 1516 if let ResolutionFailure::NotResolved { module_id, partial_res, unresolved } =
1580 if in_scope { 1517 &mut failure
1581 continue; 1518 {
1519 use DefKind::*;
1520
1521 let module_id = *module_id;
1522 // FIXME(jynelson): this might conflict with my `Self` fix in #76467
1523 // FIXME: maybe use itertools `collect_tuple` instead?
1524 fn split(path: &str) -> Option<(&str, &str)> {
1525 let mut splitter = path.rsplitn(2, "::");
1526 splitter.next().and_then(|right| splitter.next().map(|left| (left, right)))
1527 }
1528
1529 // Check if _any_ parent of the path gets resolved.
1530 // If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
1531 let mut name = path_str;
1532 'outer: loop {
1533 let (start, end) = if let Some(x) = split(name) {
1534 x
1535 } else {
1536 // avoid bug that marked [Quux::Z] as missing Z, not Quux
1537 if partial_res.is_none() {
1538 *unresolved = name.into();
1539 }
1540 break;
1541 };
1542 name = start;
1543 for &ns in &[TypeNS, ValueNS, MacroNS] {
1544 if let Some(res) =
1545 collector.check_full_res(ns, &start, module_id, &None, &None)
1546 {
1547 debug!("found partial_res={:?}", res);
1548 *partial_res = Some(res);
1549 *unresolved = end.into();
1550 break 'outer;
1551 }
1582 } 1552 }
1583 // NOTE: uses an explicit `continue` so the `note:` will come before the `help:` 1553 *unresolved = end.into();
1584 let module_name = collector.cx.tcx.item_name(module_id); 1554 }
1585 let note = format!("no item named `{}` in `{}`", name, module_name); 1555
1556 let last_found_module = match *partial_res {
1557 Some(Res::Def(DefKind::Mod, id)) => Some(id),
1558 None => Some(module_id),
1559 _ => None,
1560 };
1561 // See if this was a module: `[path]` or `[std::io::nope]`
1562 if let Some(module) = last_found_module {
1563 let module_name = collector.cx.tcx.item_name(module);
1564 let note = format!(
1565 "the module `{}` contains no item named `{}`",
1566 module_name, unresolved
1567 );
1586 if let Some(span) = sp { 1568 if let Some(span) = sp {
1587 diag.span_label(span, &note); 1569 diag.span_label(span, &note);
1588 } else { 1570 } else {
1589 diag.note(&note); 1571 diag.note(&note);
1590 } 1572 }
1591 // If the link has `::` in the path, assume it's meant to be an intra-doc link 1573 // If the link has `::` in it, assume it was meant to be an intra-doc link.
1574 // Otherwise, the `[]` might be unrelated.
1575 // FIXME: don't show this for autolinks (`<>`), `()` style links, or reference links
1592 if !path_str.contains("::") { 1576 if !path_str.contains("::") {
1593 // Otherwise, the `[]` might be unrelated.
1594 // FIXME(https://github.com/raphlinus/pulldown-cmark/issues/373):
1595 // don't show this for autolinks (`<>`), `()` style links, or reference links
1596 diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); 1577 diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
1597 } 1578 }
1598 continue; 1579 continue;
1599 } 1580 }
1581
1582 // Otherwise, it must be an associated item or variant
1583 let res = partial_res.expect("None case was handled by `last_found_module`");
1584 let diagnostic_name;
1585 let (kind, name) = match res {
1586 Res::Def(kind, def_id) => {
1587 diagnostic_name = collector.cx.tcx.item_name(def_id).as_str();
1588 (Some(kind), &*diagnostic_name)
1589 }
1590 Res::PrimTy(ty) => (None, ty.name_str()),
1591 _ => unreachable!("only ADTs and primitives are in scope at module level"),
1592 };
1593 let path_description = if let Some(kind) = kind {
1594 match kind {
1595 Mod | ForeignMod => "inner item",
1596 Struct => "field or associated item",
1597 Enum | Union => "variant or associated item",
1598 Variant
1599 | Field
1600 | Closure
1601 | Generator
1602 | AssocTy
1603 | AssocConst
1604 | AssocFn
1605 | Fn
1606 | Macro(_)
1607 | Const
1608 | ConstParam
1609 | ExternCrate
1610 | Use
1611 | LifetimeParam
1612 | Ctor(_, _)
1613 | AnonConst => {
1614 let note = assoc_item_not_allowed(res);
1615 if let Some(span) = sp {
1616 diag.span_label(span, &note);
1617 } else {
1618 diag.note(&note);
1619 }
1620 return;
1621 }
1622 Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
1623 | Static => "associated item",
1624 Impl | GlobalAsm => unreachable!("not a path"),
1625 }
1626 } else {
1627 "associated item"
1628 };
1629 let note = format!(
1630 "the {} `{}` has no {} named `{}`",
1631 res.descr(),
1632 name,
1633 disambiguator.map_or(path_description, |d| d.descr()),
1634 unresolved,
1635 );
1636 if let Some(span) = sp {
1637 diag.span_label(span, &note);
1638 } else {
1639 diag.note(&note);
1640 }
1641
1642 continue;
1643 }
1644 let note = match failure {
1645 ResolutionFailure::NotResolved { .. } => unreachable!("handled above"),
1600 ResolutionFailure::Dummy => continue, 1646 ResolutionFailure::Dummy => continue,
1601 ResolutionFailure::WrongNamespace(res, expected_ns) => { 1647 ResolutionFailure::WrongNamespace(res, expected_ns) => {
1602 if let Res::Def(kind, _) = res { 1648 if let Res::Def(kind, _) = res {
@@ -1621,79 +1667,6 @@ fn resolution_failure(
1621 diag.level = rustc_errors::Level::Bug; 1667 diag.level = rustc_errors::Level::Bug;
1622 "all intra doc links should have a parent item".to_owned() 1668 "all intra doc links should have a parent item".to_owned()
1623 } 1669 }
1624 ResolutionFailure::NoPrimitiveImpl(res, _) => format!(
1625 "this link partially resolves to {}, which does not have any associated items",
1626 item(res),
1627 ),
1628 ResolutionFailure::NoPrimitiveAssocItem { prim_name, assoc_item, .. } => {
1629 format!(
1630 "the builtin type `{}` does not have an associated item named `{}`",
1631 prim_name, assoc_item
1632 )
1633 }
1634 ResolutionFailure::NoAssocItem(res, assoc_item) => {
1635 use DefKind::*;
1636
1637 let (kind, def_id) = match res {
1638 Res::Def(kind, def_id) => (kind, def_id),
1639 x => unreachable!(
1640 "primitives are covered above and other `Res` variants aren't possible at module scope: {:?}",
1641 x,
1642 ),
1643 };
1644 let name = collector.cx.tcx.item_name(def_id);
1645 let path_description = if let Some(disambiguator) = disambiguator {
1646 disambiguator.descr()
1647 } else {
1648 match kind {
1649 Mod | ForeignMod => "inner item",
1650 Struct => "field or associated item",
1651 Enum | Union => "variant or associated item",
1652 Variant
1653 | Field
1654 | Closure
1655 | Generator
1656 | AssocTy
1657 | AssocConst
1658 | AssocFn
1659 | Fn
1660 | Macro(_)
1661 | Const
1662 | ConstParam
1663 | ExternCrate
1664 | Use
1665 | LifetimeParam
1666 | Ctor(_, _)
1667 | AnonConst => {
1668 let note = assoc_item_not_allowed(res);
1669 if let Some(span) = sp {
1670 diag.span_label(span, &note);
1671 } else {
1672 diag.note(&note);
1673 }
1674 return;
1675 }
1676 Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
1677 | Static => "associated item",
1678 Impl | GlobalAsm => unreachable!("not a path"),
1679 }
1680 };
1681 format!(
1682 "the {} `{}` has no {} named `{}`",
1683 res.descr(),
1684 name,
1685 path_description,
1686 assoc_item
1687 )
1688 }
1689 ResolutionFailure::CannotHaveAssociatedItems(res, _) => {
1690 assoc_item_not_allowed(res)
1691 }
1692 ResolutionFailure::NotAVariant(res, variant) => format!(
1693 "this link partially resolves to {}, but there is no variant named {}",
1694 item(res),
1695 variant
1696 ),
1697 }; 1670 };
1698 if let Some(span) = sp { 1671 if let Some(span) = sp {
1699 diag.span_label(span, &note); 1672 diag.span_label(span, &note);
diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr
index 5020b97..33260fa 100644
--- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr
+++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr
@@ -2,7 +2,7 @@ error: unresolved link to `v2`
2 --> $DIR/deny-intra-link-resolution-failure.rs:3:6 2 --> $DIR/deny-intra-link-resolution-failure.rs:3:6
3 | 3 |
4LL | /// [v2] 4LL | /// [v2]
5 | ^^ no item named `v2` in `deny_intra_link_resolution_failure` 5 | ^^ the module `deny_intra_link_resolution_failure` contains no item named `v2`
6 | 6 |
7note: the lint level is defined here 7note: the lint level is defined here
8 --> $DIR/deny-intra-link-resolution-failure.rs:1:9 8 --> $DIR/deny-intra-link-resolution-failure.rs:1:9
diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs
index 26b629b..0278caf 100644
--- a/src/test/rustdoc-ui/intra-link-errors.rs
+++ b/src/test/rustdoc-ui/intra-link-errors.rs
@@ -6,19 +6,23 @@
6 6
7/// [path::to::nonexistent::module] 7/// [path::to::nonexistent::module]
8//~^ ERROR unresolved link 8//~^ ERROR unresolved link
9//~| NOTE no item named `path` in `intra_link_errors` 9//~| NOTE `intra_link_errors` contains no item named `path`
10 10
11/// [path::to::nonexistent::macro!] 11/// [path::to::nonexistent::macro!]
12//~^ ERROR unresolved link 12//~^ ERROR unresolved link
13//~| NOTE no item named `path` in `intra_link_errors` 13//~| NOTE `intra_link_errors` contains no item named `path`
14 14
15/// [type@path::to::nonexistent::type] 15/// [type@path::to::nonexistent::type]
16//~^ ERROR unresolved link 16//~^ ERROR unresolved link
17//~| NOTE no item named `path` in `intra_link_errors` 17//~| NOTE `intra_link_errors` contains no item named `path`
18 18
19/// [std::io::not::here] 19/// [std::io::not::here]
20//~^ ERROR unresolved link 20//~^ ERROR unresolved link
21//~| NOTE the module `io` has no inner item 21//~| NOTE `io` contains no item named `not`
22
23/// [type@std::io::not::here]
24//~^ ERROR unresolved link
25//~| NOTE `io` contains no item named `not`
22 26
23/// [std::io::Error::x] 27/// [std::io::Error::x]
24//~^ ERROR unresolved link 28//~^ ERROR unresolved link
@@ -32,6 +36,10 @@
32//~^ ERROR unresolved link 36//~^ ERROR unresolved link
33//~| NOTE `f` is a function, not a module 37//~| NOTE `f` is a function, not a module
34 38
39/// [f::A!]
40//~^ ERROR unresolved link
41//~| NOTE `f` is a function, not a module
42
35/// [S::A] 43/// [S::A]
36//~^ ERROR unresolved link 44//~^ ERROR unresolved link
37//~| NOTE struct `S` has no field or associated item 45//~| NOTE struct `S` has no field or associated item
@@ -46,7 +54,16 @@
46 54
47/// [u8::not_found] 55/// [u8::not_found]
48//~^ ERROR unresolved link 56//~^ ERROR unresolved link
49//~| NOTE the builtin type `u8` does not have an associated item named `not_found` 57//~| NOTE the builtin type `u8` has no associated item named `not_found`
58
59/// [std::primitive::u8::not_found]
60//~^ ERROR unresolved link
61//~| NOTE the builtin type `u8` has no associated item named `not_found`
62
63/// [type@Vec::into_iter]
64//~^ ERROR unresolved link
65//~| HELP to link to the associated function, add parentheses
66//~| NOTE this link resolves to the associated function `into_iter`
50 67
51/// [S!] 68/// [S!]
52//~^ ERROR unresolved link 69//~^ ERROR unresolved link
diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr
index fbf3dcb..b63f799 100644
--- a/src/test/rustdoc-ui/intra-link-errors.stderr
+++ b/src/test/rustdoc-ui/intra-link-errors.stderr
@@ -2,7 +2,7 @@ error: unresolved link to `path::to::nonexistent::module`
2 --> $DIR/intra-link-errors.rs:7:6 2 --> $DIR/intra-link-errors.rs:7:6
3 | 3 |
4LL | /// [path::to::nonexistent::module] 4LL | /// [path::to::nonexistent::module]
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors` 5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the module `intra_link_errors` contains no item named `path`
6 | 6 |
7note: the lint level is defined here 7note: the lint level is defined here
8 --> $DIR/intra-link-errors.rs:1:9 8 --> $DIR/intra-link-errors.rs:1:9
@@ -14,64 +14,91 @@ error: unresolved link to `path::to::nonexistent::macro`
14 --> $DIR/intra-link-errors.rs:11:6 14 --> $DIR/intra-link-errors.rs:11:6
15 | 15 |
16LL | /// [path::to::nonexistent::macro!] 16LL | /// [path::to::nonexistent::macro!]
17 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors` 17 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the module `intra_link_errors` contains no item named `path`
18 18
19error: unresolved link to `path::to::nonexistent::type` 19error: unresolved link to `path::to::nonexistent::type`
20 --> $DIR/intra-link-errors.rs:15:6 20 --> $DIR/intra-link-errors.rs:15:6
21 | 21 |
22LL | /// [type@path::to::nonexistent::type] 22LL | /// [type@path::to::nonexistent::type]
23 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors` 23 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the module `intra_link_errors` contains no item named `path`
24 24
25error: unresolved link to `std::io::not::here` 25error: unresolved link to `std::io::not::here`
26 --> $DIR/intra-link-errors.rs:19:6 26 --> $DIR/intra-link-errors.rs:19:6
27 | 27 |
28LL | /// [std::io::not::here] 28LL | /// [std::io::not::here]
29 | ^^^^^^^^^^^^^^^^^^ the module `io` has no inner item named `not` 29 | ^^^^^^^^^^^^^^^^^^ the module `io` contains no item named `not`
30 30
31error: unresolved link to `std::io::Error::x` 31error: unresolved link to `std::io::not::here`
32 --> $DIR/intra-link-errors.rs:23:6 32 --> $DIR/intra-link-errors.rs:23:6
33 | 33 |
34LL | /// [type@std::io::not::here]
35 | ^^^^^^^^^^^^^^^^^^^^^^^ the module `io` contains no item named `not`
36
37error: unresolved link to `std::io::Error::x`
38 --> $DIR/intra-link-errors.rs:27:6
39 |
34LL | /// [std::io::Error::x] 40LL | /// [std::io::Error::x]
35 | ^^^^^^^^^^^^^^^^^ the struct `Error` has no field or associated item named `x` 41 | ^^^^^^^^^^^^^^^^^ the struct `Error` has no field or associated item named `x`
36 42
37error: unresolved link to `std::io::ErrorKind::x` 43error: unresolved link to `std::io::ErrorKind::x`
38 --> $DIR/intra-link-errors.rs:27:6 44 --> $DIR/intra-link-errors.rs:31:6
39 | 45 |
40LL | /// [std::io::ErrorKind::x] 46LL | /// [std::io::ErrorKind::x]
41 | ^^^^^^^^^^^^^^^^^^^^^ the enum `ErrorKind` has no variant or associated item named `x` 47 | ^^^^^^^^^^^^^^^^^^^^^ the enum `ErrorKind` has no variant or associated item named `x`
42 48
43error: unresolved link to `f::A` 49error: unresolved link to `f::A`
44 --> $DIR/intra-link-errors.rs:31:6 50 --> $DIR/intra-link-errors.rs:35:6
45 | 51 |
46LL | /// [f::A] 52LL | /// [f::A]
47 | ^^^^ `f` is a function, not a module or type, and cannot have associated items 53 | ^^^^ `f` is a function, not a module or type, and cannot have associated items
48 54
55error: unresolved link to `f::A`
56 --> $DIR/intra-link-errors.rs:39:6
57 |
58LL | /// [f::A!]
59 | ^^^^^ `f` is a function, not a module or type, and cannot have associated items
60
49error: unresolved link to `S::A` 61error: unresolved link to `S::A`
50 --> $DIR/intra-link-errors.rs:35:6 62 --> $DIR/intra-link-errors.rs:43:6
51 | 63 |
52LL | /// [S::A] 64LL | /// [S::A]
53 | ^^^^ the struct `S` has no field or associated item named `A` 65 | ^^^^ the struct `S` has no field or associated item named `A`
54 66
55error: unresolved link to `S::fmt` 67error: unresolved link to `S::fmt`
56 --> $DIR/intra-link-errors.rs:39:6 68 --> $DIR/intra-link-errors.rs:47:6
57 | 69 |
58LL | /// [S::fmt] 70LL | /// [S::fmt]
59 | ^^^^^^ the struct `S` has no field or associated item named `fmt` 71 | ^^^^^^ the struct `S` has no field or associated item named `fmt`
60 72
61error: unresolved link to `E::D` 73error: unresolved link to `E::D`
62 --> $DIR/intra-link-errors.rs:43:6 74 --> $DIR/intra-link-errors.rs:51:6
63 | 75 |
64LL | /// [E::D] 76LL | /// [E::D]
65 | ^^^^ the enum `E` has no variant or associated item named `D` 77 | ^^^^ the enum `E` has no variant or associated item named `D`
66 78
67error: unresolved link to `u8::not_found` 79error: unresolved link to `u8::not_found`
68 --> $DIR/intra-link-errors.rs:47:6 80 --> $DIR/intra-link-errors.rs:55:6
69 | 81 |
70LL | /// [u8::not_found] 82LL | /// [u8::not_found]
71 | ^^^^^^^^^^^^^ the builtin type `u8` does not have an associated item named `not_found` 83 | ^^^^^^^^^^^^^ the builtin type `u8` has no associated item named `not_found`
84
85error: unresolved link to `std::primitive::u8::not_found`
86 --> $DIR/intra-link-errors.rs:59:6
87 |
88LL | /// [std::primitive::u8::not_found]
89 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the builtin type `u8` has no associated item named `not_found`
90
91error: unresolved link to `Vec::into_iter`
92 --> $DIR/intra-link-errors.rs:63:6
93 |
94LL | /// [type@Vec::into_iter]
95 | ^^^^^^^^^^^^^^^^^^^
96 | |
97 | this link resolves to the associated function `into_iter`, which is not in the type namespace
98 | help: to link to the associated function, add parentheses: `Vec::into_iter()`
72 99
73error: unresolved link to `S` 100error: unresolved link to `S`
74 --> $DIR/intra-link-errors.rs:51:6 101 --> $DIR/intra-link-errors.rs:68:6
75 | 102 |
76LL | /// [S!] 103LL | /// [S!]
77 | ^^ 104 | ^^
@@ -80,7 +107,7 @@ LL | /// [S!]
80 | help: to link to the struct, prefix with `struct@`: `struct@S` 107 | help: to link to the struct, prefix with `struct@`: `struct@S`
81 108
82error: unresolved link to `T::g` 109error: unresolved link to `T::g`
83 --> $DIR/intra-link-errors.rs:69:6 110 --> $DIR/intra-link-errors.rs:86:6
84 | 111 |
85LL | /// [type@T::g] 112LL | /// [type@T::g]
86 | ^^^^^^^^^ 113 | ^^^^^^^^^
@@ -89,13 +116,13 @@ LL | /// [type@T::g]
89 | help: to link to the associated function, add parentheses: `T::g()` 116 | help: to link to the associated function, add parentheses: `T::g()`
90 117
91error: unresolved link to `T::h` 118error: unresolved link to `T::h`
92 --> $DIR/intra-link-errors.rs:74:6 119 --> $DIR/intra-link-errors.rs:91:6
93 | 120 |
94LL | /// [T::h!] 121LL | /// [T::h!]
95 | ^^^^^ the trait `T` has no macro named `h` 122 | ^^^^^ the trait `T` has no macro named `h`
96 123
97error: unresolved link to `S::h` 124error: unresolved link to `S::h`
98 --> $DIR/intra-link-errors.rs:61:6 125 --> $DIR/intra-link-errors.rs:78:6
99 | 126 |
100LL | /// [type@S::h] 127LL | /// [type@S::h]
101 | ^^^^^^^^^ 128 | ^^^^^^^^^
@@ -104,7 +131,7 @@ LL | /// [type@S::h]
104 | help: to link to the associated function, add parentheses: `S::h()` 131 | help: to link to the associated function, add parentheses: `S::h()`
105 132
106error: unresolved link to `m` 133error: unresolved link to `m`
107 --> $DIR/intra-link-errors.rs:81:6 134 --> $DIR/intra-link-errors.rs:98:6
108 | 135 |
109LL | /// [m()] 136LL | /// [m()]
110 | ^^^ 137 | ^^^
@@ -112,5 +139,5 @@ LL | /// [m()]
112 | this link resolves to the macro `m`, which is not in the value namespace 139 | this link resolves to the macro `m`, which is not in the value namespace
113 | help: to link to the macro, add an exclamation mark: `m!` 140 | help: to link to the macro, add an exclamation mark: `m!`
114 141
115error: aborting due to 16 previous errors 142error: aborting due to 20 previous errors
116 143
diff --git a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr
index 3c13df2..d946aa9 100644
--- a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr
+++ b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr
@@ -2,7 +2,7 @@ error: unresolved link to `i`
2 --> $DIR/intra-link-span-ice-55723.rs:9:10 2 --> $DIR/intra-link-span-ice-55723.rs:9:10
3 | 3 |
4LL | /// (arr[i]) 4LL | /// (arr[i])
5 | ^ no item named `i` in `intra_link_span_ice_55723` 5 | ^ the module `intra_link_span_ice_55723` contains no item named `i`
6 | 6 |
7note: the lint level is defined here 7note: the lint level is defined here
8 --> $DIR/intra-link-span-ice-55723.rs:1:9 8 --> $DIR/intra-link-span-ice-55723.rs:1:9
diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr
index 351f8fa..76a2ac0 100644
--- a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr
+++ b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr
@@ -2,7 +2,7 @@ warning: unresolved link to `error`
2 --> $DIR/intra-links-warning-crlf.rs:7:6 2 --> $DIR/intra-links-warning-crlf.rs:7:6
3 | 3 |
4LL | /// [error] 4LL | /// [error]
5 | ^^^^^ no item named `error` in `intra_links_warning_crlf` 5 | ^^^^^ the module `intra_links_warning_crlf` contains no item named `error`
6 | 6 |
7 = note: `#[warn(broken_intra_doc_links)]` on by default 7 = note: `#[warn(broken_intra_doc_links)]` on by default
8 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 8 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@@ -11,7 +11,7 @@ warning: unresolved link to `error1`
11 --> $DIR/intra-links-warning-crlf.rs:12:11 11 --> $DIR/intra-links-warning-crlf.rs:12:11
12 | 12 |
13LL | /// docs [error1] 13LL | /// docs [error1]
14 | ^^^^^^ no item named `error1` in `intra_links_warning_crlf` 14 | ^^^^^^ the module `intra_links_warning_crlf` contains no item named `error1`
15 | 15 |
16 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 16 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
17 17
@@ -19,7 +19,7 @@ warning: unresolved link to `error2`
19 --> $DIR/intra-links-warning-crlf.rs:15:11 19 --> $DIR/intra-links-warning-crlf.rs:15:11
20 | 20 |
21LL | /// docs [error2] 21LL | /// docs [error2]
22 | ^^^^^^ no item named `error2` in `intra_links_warning_crlf` 22 | ^^^^^^ the module `intra_links_warning_crlf` contains no item named `error2`
23 | 23 |
24 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 24 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
25 25
@@ -27,7 +27,7 @@ warning: unresolved link to `error`
27 --> $DIR/intra-links-warning-crlf.rs:23:20 27 --> $DIR/intra-links-warning-crlf.rs:23:20
28 | 28 |
29LL | * It also has an [error]. 29LL | * It also has an [error].
30 | ^^^^^ no item named `error` in `intra_links_warning_crlf` 30 | ^^^^^ the module `intra_links_warning_crlf` contains no item named `error`
31 | 31 |
32 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 32 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
33 33
diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr
index 0832e00..09db465 100644
--- a/src/test/rustdoc-ui/intra-links-warning.stderr
+++ b/src/test/rustdoc-ui/intra-links-warning.stderr
@@ -10,37 +10,37 @@ warning: unresolved link to `Bar::foo`
10 --> $DIR/intra-links-warning.rs:3:35 10 --> $DIR/intra-links-warning.rs:3:35
11 | 11 |
12LL | //! Test with [Foo::baz], [Bar::foo], ... 12LL | //! Test with [Foo::baz], [Bar::foo], ...
13 | ^^^^^^^^ no item named `Bar` in `intra_links_warning` 13 | ^^^^^^^^ the module `intra_links_warning` contains no item named `Bar`
14 14
15warning: unresolved link to `Uniooon::X` 15warning: unresolved link to `Uniooon::X`
16 --> $DIR/intra-links-warning.rs:6:13 16 --> $DIR/intra-links-warning.rs:6:13
17 | 17 |
18LL | //! , [Uniooon::X] and [Qux::Z]. 18LL | //! , [Uniooon::X] and [Qux::Z].
19 | ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning` 19 | ^^^^^^^^^^ the module `intra_links_warning` contains no item named `Uniooon`
20 20
21warning: unresolved link to `Qux::Z` 21warning: unresolved link to `Qux::Z`
22 --> $DIR/intra-links-warning.rs:6:30 22 --> $DIR/intra-links-warning.rs:6:30
23 | 23 |
24LL | //! , [Uniooon::X] and [Qux::Z]. 24LL | //! , [Uniooon::X] and [Qux::Z].
25 | ^^^^^^ no item named `Qux` in `intra_links_warning` 25 | ^^^^^^ the module `intra_links_warning` contains no item named `Qux`
26 26
27warning: unresolved link to `Uniooon::X` 27warning: unresolved link to `Uniooon::X`
28 --> $DIR/intra-links-warning.rs:10:14 28 --> $DIR/intra-links-warning.rs:10:14
29 | 29 |
30LL | //! , [Uniooon::X] and [Qux::Z]. 30LL | //! , [Uniooon::X] and [Qux::Z].
31 | ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning` 31 | ^^^^^^^^^^ the module `intra_links_warning` contains no item named `Uniooon`
32 32
33warning: unresolved link to `Qux::Z` 33warning: unresolved link to `Qux::Z`
34 --> $DIR/intra-links-warning.rs:10:31 34 --> $DIR/intra-links-warning.rs:10:31
35 | 35 |
36LL | //! , [Uniooon::X] and [Qux::Z]. 36LL | //! , [Uniooon::X] and [Qux::Z].
37 | ^^^^^^ no item named `Qux` in `intra_links_warning` 37 | ^^^^^^ the module `intra_links_warning` contains no item named `Qux`
38 38
39warning: unresolved link to `Qux:Y` 39warning: unresolved link to `Qux:Y`
40 --> $DIR/intra-links-warning.rs:14:13 40 --> $DIR/intra-links-warning.rs:14:13
41 | 41 |
42LL | /// [Qux:Y] 42LL | /// [Qux:Y]
43 | ^^^^^ no item named `Qux:Y` in `intra_links_warning` 43 | ^^^^^ the module `intra_links_warning` contains no item named `Qux:Y`
44 | 44 |
45 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 45 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
46 46
@@ -48,7 +48,7 @@ warning: unresolved link to `error`
48 --> $DIR/intra-links-warning.rs:58:30 48 --> $DIR/intra-links-warning.rs:58:30
49 | 49 |
50LL | * time to introduce a link [error]*/ 50LL | * time to introduce a link [error]*/
51 | ^^^^^ no item named `error` in `intra_links_warning` 51 | ^^^^^ the module `intra_links_warning` contains no item named `error`
52 | 52 |
53 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 53 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
54 54
@@ -56,7 +56,7 @@ warning: unresolved link to `error`
56 --> $DIR/intra-links-warning.rs:64:30 56 --> $DIR/intra-links-warning.rs:64:30
57 | 57 |
58LL | * time to introduce a link [error] 58LL | * time to introduce a link [error]
59 | ^^^^^ no item named `error` in `intra_links_warning` 59 | ^^^^^ the module `intra_links_warning` contains no item named `error`
60 | 60 |
61 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 61 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
62 62
@@ -70,7 +70,7 @@ LL | #[doc = "single line [error]"]
70 70
71 single line [error] 71 single line [error]
72 ^^^^^ 72 ^^^^^
73 = note: no item named `error` in `intra_links_warning` 73 = note: the module `intra_links_warning` contains no item named `error`
74 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 74 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
75 75
76warning: unresolved link to `error` 76warning: unresolved link to `error`
@@ -83,7 +83,7 @@ LL | #[doc = "single line with \"escaping\" [error]"]
83 83
84 single line with "escaping" [error] 84 single line with "escaping" [error]
85 ^^^^^ 85 ^^^^^
86 = note: no item named `error` in `intra_links_warning` 86 = note: the module `intra_links_warning` contains no item named `error`
87 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 87 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
88 88
89warning: unresolved link to `error` 89warning: unresolved link to `error`
@@ -98,14 +98,14 @@ LL | | /// [error]
98 98
99 [error] 99 [error]
100 ^^^^^ 100 ^^^^^
101 = note: no item named `error` in `intra_links_warning` 101 = note: the module `intra_links_warning` contains no item named `error`
102 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 102 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
103 103
104warning: unresolved link to `error1` 104warning: unresolved link to `error1`
105 --> $DIR/intra-links-warning.rs:80:11 105 --> $DIR/intra-links-warning.rs:80:11
106 | 106 |
107LL | /// docs [error1] 107LL | /// docs [error1]
108 | ^^^^^^ no item named `error1` in `intra_links_warning` 108 | ^^^^^^ the module `intra_links_warning` contains no item named `error1`
109 | 109 |
110 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 110 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
111 111
@@ -113,7 +113,7 @@ warning: unresolved link to `error2`
113 --> $DIR/intra-links-warning.rs:82:11 113 --> $DIR/intra-links-warning.rs:82:11
114 | 114 |
115LL | /// docs [error2] 115LL | /// docs [error2]
116 | ^^^^^^ no item named `error2` in `intra_links_warning` 116 | ^^^^^^ the module `intra_links_warning` contains no item named `error2`
117 | 117 |
118 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 118 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
119 119
@@ -121,7 +121,7 @@ warning: unresolved link to `BarA`
121 --> $DIR/intra-links-warning.rs:21:10 121 --> $DIR/intra-links-warning.rs:21:10
122 | 122 |
123LL | /// bar [BarA] bar 123LL | /// bar [BarA] bar
124 | ^^^^ no item named `BarA` in `intra_links_warning` 124 | ^^^^ the module `intra_links_warning` contains no item named `BarA`
125 | 125 |
126 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 126 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
127 127
@@ -129,7 +129,7 @@ warning: unresolved link to `BarB`
129 --> $DIR/intra-links-warning.rs:27:9 129 --> $DIR/intra-links-warning.rs:27:9
130 | 130 |
131LL | * bar [BarB] bar 131LL | * bar [BarB] bar
132 | ^^^^ no item named `BarB` in `intra_links_warning` 132 | ^^^^ the module `intra_links_warning` contains no item named `BarB`
133 | 133 |
134 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 134 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
135 135
@@ -137,7 +137,7 @@ warning: unresolved link to `BarC`
137 --> $DIR/intra-links-warning.rs:34:6 137 --> $DIR/intra-links-warning.rs:34:6
138 | 138 |
139LL | bar [BarC] bar 139LL | bar [BarC] bar
140 | ^^^^ no item named `BarC` in `intra_links_warning` 140 | ^^^^ the module `intra_links_warning` contains no item named `BarC`
141 | 141 |
142 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 142 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
143 143
@@ -151,7 +151,7 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"]
151 151
152 bar [BarD] bar 152 bar [BarD] bar
153 ^^^^ 153 ^^^^
154 = note: no item named `BarD` in `intra_links_warning` 154 = note: the module `intra_links_warning` contains no item named `BarD`
155 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 155 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
156 156
157warning: unresolved link to `BarF` 157warning: unresolved link to `BarF`
@@ -167,7 +167,7 @@ LL | f!("Foo\nbar [BarF] bar\nbaz");
167 167
168 bar [BarF] bar 168 bar [BarF] bar
169 ^^^^ 169 ^^^^
170 = note: no item named `BarF` in `intra_links_warning` 170 = note: the module `intra_links_warning` contains no item named `BarF`
171 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 171 = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
172 = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) 172 = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
173 173
diff --git a/src/test/rustdoc-ui/lint-group.stderr b/src/test/rustdoc-ui/lint-group.stderr
index 550b79f..4e9134e 100644
--- a/src/test/rustdoc-ui/lint-group.stderr
+++ b/src/test/rustdoc-ui/lint-group.stderr
@@ -32,7 +32,7 @@ error: unresolved link to `error`
32 --> $DIR/lint-group.rs:9:29 32 --> $DIR/lint-group.rs:9:29
33 | 33 |
34LL | /// what up, let's make an [error] 34LL | /// what up, let's make an [error]
35 | ^^^^^ no item named `error` in `lint_group` 35 | ^^^^^ the module `lint_group` contains no item named `error`
36 | 36 |
37note: the lint level is defined here 37note: the lint level is defined here
38 --> $DIR/lint-group.rs:7:9 38 --> $DIR/lint-group.rs:7:9
diff --git a/src/test/rustdoc/intra-link-associated-items.rs b/src/test/rustdoc/intra-link-associated-items.rs
index 16a21e3..daf7075 100644
--- a/src/test/rustdoc/intra-link-associated-items.rs
+++ b/src/test/rustdoc/intra-link-associated-items.rs
@@ -3,8 +3,10 @@
3 3
4/// [`std::collections::BTreeMap::into_iter`] 4/// [`std::collections::BTreeMap::into_iter`]
5/// [`String::from`] is ambiguous as to which `From` impl 5/// [`String::from`] is ambiguous as to which `From` impl
6/// [Vec::into_iter()] uses a disambiguator
6// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/collections/btree/map/struct.BTreeMap.html#method.into_iter"]' 'std::collections::BTreeMap::into_iter' 7// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/collections/btree/map/struct.BTreeMap.html#method.into_iter"]' 'std::collections::BTreeMap::into_iter'
7// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.from"]' 'String::from' 8// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.from"]' 'String::from'
9// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.into_iter"]' 'Vec::into_iter'
8pub fn foo() {} 10pub fn foo() {}
9 11
10/// Link to [MyStruct], [link from struct][MyStruct::method], [MyStruct::clone], [MyStruct::Input] 12/// Link to [MyStruct], [link from struct][MyStruct::method], [MyStruct::clone], [MyStruct::Input]
diff --git a/src/test/rustdoc/primitive-link.rs b/src/test/rustdoc/primitive-link.rs
index 819ef05..8f69b89 100644
--- a/src/test/rustdoc/primitive-link.rs
+++ b/src/test/rustdoc/primitive-link.rs
@@ -4,6 +4,13 @@
4 4
5// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.u32.html"]' 'u32' 5// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.u32.html"]' 'u32'
6// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.i64.html"]' 'i64' 6// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.i64.html"]' 'i64'
7// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.i32.html"]' 'std::primitive::i32'
8// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html"]' 'std::primitive::str'
9
10// FIXME: this doesn't resolve
11// @ has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.i32.html#associatedconstant.MAX"]' 'std::primitive::i32::MAX'
7 12
8/// It contains [`u32`] and [i64]. 13/// It contains [`u32`] and [i64].
14/// It also links to [std::primitive::i32], [std::primitive::str],
15/// and [`std::primitive::i32::MAX`].
9pub struct Foo; 16pub struct Foo;