Change:
Consider this code: I'm not sure if this is a deliberate decision or not. If it is, what's the rationale for not accepting and ? cc
It isn't valid markdown syntax.
Well, might be. But [] definitely is not.
seems to work fine (it renders as and the link is correct). But maybe you're saying that we shouldn't accept because it wouldn't be valid markdown? Is it worth implementing?
hmm, it might be valid in that position, but it's not valid as a link reference: But I guess there's no reason for rustdoc to explicitly reject it. I would be ok with it, may have opinions.
I'm okay with it. I'm mostly wary about characters that are common in URLs, curly braces are not.
Oh, the reason why works is because the rules for inline links are more relaxed: vs So instead do you think rustdoc should parse ?

Discussion:
} // Parse and strip the disambiguator from the link, if present. <del> let (path_str, disambiguator) = match Disambiguator::from_str(&link) { Ok(Some((d, path))) => (path.trim(), Some(d)), Ok(None) => (link.trim(), None), </del> <ins> let (disambiguator, path_str, link_text) = match Disambiguator::from_str(&link) { Ok(Some((d, path, link_text))) => (Some(d), path.trim(), link_text.trim()), Ok(None) => (None, link.trim(), link.trim()), </ins> Err((err_msg, relative_range)) => { // Only report error if we would not have ignored this link. See issue #83859. if !should_ignore_link_with_disambiguators(link) {

return None; } <del> // We stripped `()` and `!` when parsing the disambiguator. // Add them back to be displayed, but not prefix disambiguators. let link_text = disambiguator.map(|d| d.display_for(path_str)).unwrap_or_else(|| path_str.to_owned()); </del> // Strip generics from the path. let path_str = if path_str.contains(['<', '>'].as_slice()) { match strip_generics_from_path(&path_str) {

path_str, disambiguator, extra_fragment: extra_fragment.map(String::from), <del> link_text, </del> <ins> link_text: link_text.to_owned(), </ins> })) }

} impl Disambiguator { <del> /// The text that should be displayed when the path is rendered as HTML. /// /// NOTE: `path` is not the original link given by the user, but a name suitable for passing to `resolve`. fn display_for(&self, path: &str) -> String { match self { // FIXME: this will have different output if the user had `m!()` originally. Self::Kind(DefKind::Macro(MacroKind::Bang)) => format!("{}!", path), Self::Kind(DefKind::Fn) => format!("{}()", path), _ => path.to_owned(), } } /// Given a link, parse and return `(disambiguator, path_str)`. </del> <ins> /// Given a link, parse and return `(disambiguator, path_str, link_text)`. </ins> /// /// This returns `Ok(Some(...))` if a disambiguator was found, /// `Ok(None)` if no disambiguator was found, or `Err(...)` /// if there was a problem with the disambiguator. <del> fn from_str(link: &str) -> Result<Option<(Self, &str)>, (String, Range<usize>)> { </del> <ins> fn from_str(link: &str) -> Result<Option<(Self, &str, &str)>, (String, Range<usize>)> { </ins> use Disambiguator::{Kind, Namespace as NS, Primitive}; if let Some(idx) = link.find('@') {

"prim" | "primitive" => Primitive, _ => return Err((format!("unknown disambiguator `{}`", prefix), 0..idx)), }; <del> Ok(Some((d, &rest[1..]))) </del> <ins> Ok(Some((d, &rest[1..], &rest[1..]))) </ins> } else { let suffixes = [ ("!()", DefKind::Macro(MacroKind::Bang)), <ins> ("!{}", DefKind::Macro(MacroKind::Bang)), ("![]", DefKind::Macro(MacroKind::Bang)), </ins> ("()", DefKind::Fn), ("!", DefKind::Macro(MacroKind::Bang)), ]; for (suffix, kind) in suffixes { <del> if let Some(link) = link.strip_suffix(suffix) { </del> <ins> if let Some(path_str) = link.strip_suffix(suffix) { </ins> // Avoid turning `!` or `()` into an empty string <del> if !link.is_empty() { return Ok(Some((Kind(kind), link))); </del> <ins> if !path_str.is_empty() { return Ok(Some((Kind(kind), path_str, link))); </ins> } } }

<ins> #![crate_name = "foo"] #![deny(rustdoc::broken_intra_doc_links)] //! [foo!()] // @has foo/index.html '//a[@href="macro.foo.html"]' 'foo!()' //! [foo!{}] // @has - '//a[@href="macro.foo.html"]' 'foo!{}' //! [foo![]](foo![]) // @has - '//a[@href="macro.foo.html"]' 'foo![]' //! [foo1](foo!()) // @has - '//a[@href="macro.foo.html"]' 'foo1' //! [foo2](foo!{}) // @has - '//a[@href="macro.foo.html"]' 'foo2' //! [foo3](foo![]) // @has - '//a[@href="macro.foo.html"]' 'foo3' #[macro_export] macro_rules! foo { () => {}; } </ins>