about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_span/src/lib.rs6
-rw-r--r--compiler/rustc_span/src/span_encoding.rs14
-rw-r--r--src/librustdoc/html/markdown/footnotes.rs60
-rw-r--r--src/librustdoc/html/static/js/search.js11
-rw-r--r--tests/rustdoc-js-std/write.js24
-rw-r--r--tests/rustdoc-js/case.js17
-rw-r--r--tests/rustdoc-js/case.rs7
-rw-r--r--tests/rustdoc/footnote-reference-in-footnote-def.rs20
8 files changed, 120 insertions, 39 deletions
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index baa1c2c91c7..8966cfc9171 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -559,12 +559,6 @@ impl Span {
         !self.is_dummy() && sm.is_span_accessible(self)
     }
 
-    /// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
-    #[inline]
-    pub fn from_expansion(self) -> bool {
-        !self.ctxt().is_root()
-    }
-
     /// Returns `true` if `span` originates in a derive-macro's expansion.
     pub fn in_derive_expansion(self) -> bool {
         matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs
index db8170fa6ae..9d6c7d2a42a 100644
--- a/compiler/rustc_span/src/span_encoding.rs
+++ b/compiler/rustc_span/src/span_encoding.rs
@@ -303,6 +303,13 @@ impl Span {
         }
     }
 
+    /// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
+    #[inline]
+    pub fn from_expansion(self) -> bool {
+        // If the span is fully inferred then ctxt > MAX_CTXT
+        self.inline_ctxt().map_or(true, |ctxt| !ctxt.is_root())
+    }
+
     /// Returns `true` if this is a dummy span with any hygienic context.
     #[inline]
     pub fn is_dummy(self) -> bool {
@@ -370,9 +377,10 @@ impl Span {
     pub fn eq_ctxt(self, other: Span) -> bool {
         match (self.inline_ctxt(), other.inline_ctxt()) {
             (Ok(ctxt1), Ok(ctxt2)) => ctxt1 == ctxt2,
-            (Ok(ctxt), Err(index)) | (Err(index), Ok(ctxt)) => {
-                with_span_interner(|interner| ctxt == interner.spans[index].ctxt)
-            }
+            // If `inline_ctxt` returns `Ok` the context is <= MAX_CTXT.
+            // If it returns `Err` the span is fully interned and the context is > MAX_CTXT.
+            // As these do not overlap an `Ok` and `Err` result cannot have an equal context.
+            (Ok(_), Err(_)) | (Err(_), Ok(_)) => false,
             (Err(index1), Err(index2)) => with_span_interner(|interner| {
                 interner.spans[index1].ctxt == interner.spans[index2].ctxt
             }),
diff --git a/src/librustdoc/html/markdown/footnotes.rs b/src/librustdoc/html/markdown/footnotes.rs
index 0958d446c2d..fcb8a174a95 100644
--- a/src/librustdoc/html/markdown/footnotes.rs
+++ b/src/librustdoc/html/markdown/footnotes.rs
@@ -1,7 +1,7 @@
 //! Markdown footnote handling.
 use std::fmt::Write as _;
 
-use pulldown_cmark::{Event, Tag, TagEnd, html};
+use pulldown_cmark::{CowStr, Event, Tag, TagEnd, html};
 use rustc_data_structures::fx::FxIndexMap;
 
 use super::SpannedEvent;
@@ -21,7 +21,7 @@ struct FootnoteDef<'a> {
     id: usize,
 }
 
-impl<'a, 'b, I> Footnotes<'a, 'b, I> {
+impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> {
     pub(super) fn new(iter: I, existing_footnotes: &'b mut usize) -> Self {
         Footnotes { inner: iter, footnotes: FxIndexMap::default(), existing_footnotes }
     }
@@ -34,6 +34,34 @@ impl<'a, 'b, I> Footnotes<'a, 'b, I> {
         // Don't allow changing the ID of existing entrys, but allow changing the contents.
         (content, *id)
     }
+
+    fn handle_footnote_reference(&mut self, reference: &CowStr<'a>) -> Event<'a> {
+        // When we see a reference (to a footnote we may not know) the definition of,
+        // reserve a number for it, and emit a link to that number.
+        let (_, id) = self.get_entry(reference);
+        let reference = format!(
+            "<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{1}</a></sup>",
+            id,
+            // Although the ID count is for the whole page, the footnote reference
+            // are local to the item so we make this ID "local" when displayed.
+            id - *self.existing_footnotes
+        );
+        Event::Html(reference.into())
+    }
+
+    fn collect_footnote_def(&mut self) -> Vec<Event<'a>> {
+        let mut content = Vec::new();
+        while let Some((event, _)) = self.inner.next() {
+            match event {
+                Event::End(TagEnd::FootnoteDefinition) => break,
+                Event::FootnoteReference(ref reference) => {
+                    content.push(self.handle_footnote_reference(reference));
+                }
+                event => content.push(event),
+            }
+        }
+        content
+    }
 }
 
 impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, 'b, I> {
@@ -41,24 +69,15 @@ impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, 'b
 
     fn next(&mut self) -> Option<Self::Item> {
         loop {
-            match self.inner.next() {
+            let next = self.inner.next();
+            match next {
                 Some((Event::FootnoteReference(ref reference), range)) => {
-                    // When we see a reference (to a footnote we may not know) the definition of,
-                    // reserve a number for it, and emit a link to that number.
-                    let (_, id) = self.get_entry(reference);
-                    let reference = format!(
-                        "<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{1}</a></sup>",
-                        id,
-                        // Although the ID count is for the whole page, the footnote reference
-                        // are local to the item so we make this ID "local" when displayed.
-                        id - *self.existing_footnotes
-                    );
-                    return Some((Event::Html(reference.into()), range));
+                    return Some((self.handle_footnote_reference(reference), range));
                 }
                 Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
                     // When we see a footnote definition, collect the assocated content, and store
                     // that for rendering later.
-                    let content = collect_footnote_def(&mut self.inner);
+                    let content = self.collect_footnote_def();
                     let (entry_content, _) = self.get_entry(&def);
                     *entry_content = content;
                 }
@@ -80,17 +99,6 @@ impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, 'b
     }
 }
 
-fn collect_footnote_def<'a>(events: impl Iterator<Item = SpannedEvent<'a>>) -> Vec<Event<'a>> {
-    let mut content = Vec::new();
-    for (event, _) in events {
-        if let Event::End(TagEnd::FootnoteDefinition) = event {
-            break;
-        }
-        content.push(event);
-    }
-    content
-}
-
 fn render_footnotes_defs(mut footnotes: Vec<FootnoteDef<'_>>) -> String {
     let mut ret = String::from("<div class=\"footnotes\"><hr><ol>");
 
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 4e1bbbbf59d..a95a13aac47 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -2500,6 +2500,7 @@ class DocSearch {
         const sortResults = async(results, typeInfo, preferredCrate) => {
             const userQuery = parsedQuery.userQuery;
             const normalizedUserQuery = parsedQuery.userQuery.toLowerCase();
+            const isMixedCase = normalizedUserQuery !== userQuery;
             const result_list = [];
             for (const result of results.values()) {
                 result.item = this.searchIndex[result.id];
@@ -2511,10 +2512,12 @@ class DocSearch {
                 let a, b;
 
                 // sort by exact case-sensitive match
-                a = (aaa.item.name !== userQuery);
-                b = (bbb.item.name !== userQuery);
-                if (a !== b) {
-                    return a - b;
+                if (isMixedCase) {
+                    a = (aaa.item.name !== userQuery);
+                    b = (bbb.item.name !== userQuery);
+                    if (a !== b) {
+                        return a - b;
+                    }
                 }
 
                 // sort by exact match with regard to the last word (mismatch goes later)
diff --git a/tests/rustdoc-js-std/write.js b/tests/rustdoc-js-std/write.js
new file mode 100644
index 00000000000..4a9475102a5
--- /dev/null
+++ b/tests/rustdoc-js-std/write.js
@@ -0,0 +1,24 @@
+const EXPECTED = [
+    {
+        'query': 'write',
+        'others': [
+            { 'path': 'std::fmt', 'name': 'write' },
+            { 'path': 'std::fs', 'name': 'write' },
+            { 'path': 'std::ptr', 'name': 'write' },
+            { 'path': 'std::fmt', 'name': 'Write' },
+            { 'path': 'std::io', 'name': 'Write' },
+            { 'path': 'std::hash::Hasher', 'name': 'write' },
+        ],
+    },
+    {
+        'query': 'Write',
+        'others': [
+            { 'path': 'std::fmt', 'name': 'Write' },
+            { 'path': 'std::io', 'name': 'Write' },
+            { 'path': 'std::fmt', 'name': 'write' },
+            { 'path': 'std::fs', 'name': 'write' },
+            { 'path': 'std::ptr', 'name': 'write' },
+            { 'path': 'std::hash::Hasher', 'name': 'write' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/case.js b/tests/rustdoc-js/case.js
new file mode 100644
index 00000000000..22b970eb139
--- /dev/null
+++ b/tests/rustdoc-js/case.js
@@ -0,0 +1,17 @@
+const EXPECTED = [
+    {
+        'query': 'Foo',
+        'others': [
+            { 'path': 'case', 'name': 'Foo', 'desc': 'Docs for Foo' },
+            { 'path': 'case', 'name': 'foo', 'desc': 'Docs for foo' },
+        ],
+    },
+    {
+        'query': 'foo',
+        'others': [
+            // https://github.com/rust-lang/rust/issues/133017
+            { 'path': 'case', 'name': 'Foo', 'desc': 'Docs for Foo' },
+            { 'path': 'case', 'name': 'foo', 'desc': 'Docs for foo' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/case.rs b/tests/rustdoc-js/case.rs
new file mode 100644
index 00000000000..532edd55f1d
--- /dev/null
+++ b/tests/rustdoc-js/case.rs
@@ -0,0 +1,7 @@
+#![allow(nonstandard_style)]
+
+/// Docs for Foo
+pub struct Foo;
+
+/// Docs for foo
+pub struct foo;
diff --git a/tests/rustdoc/footnote-reference-in-footnote-def.rs b/tests/rustdoc/footnote-reference-in-footnote-def.rs
new file mode 100644
index 00000000000..db3f9a59ef8
--- /dev/null
+++ b/tests/rustdoc/footnote-reference-in-footnote-def.rs
@@ -0,0 +1,20 @@
+// Checks that footnote references in footnote definitions are correctly generated.
+// Regression test for <https://github.com/rust-lang/rust/issues/131946>.
+
+#![crate_name = "foo"]
+
+//@ has 'foo/index.html'
+//@ has - '//*[@class="docblock"]/p/sup[@id="fnref1"]/a[@href="#fn1"]' '1'
+//@ has - '//li[@id="fn1"]/p' 'meow'
+//@ has - '//li[@id="fn1"]/p/sup[@id="fnref2"]/a[@href="#fn2"]' '2'
+//@ has - '//li[@id="fn1"]//a[@href="#fn2"]' '2'
+//@ has - '//li[@id="fn2"]/p' 'uwu'
+//@ has - '//li[@id="fn2"]/p/sup[@id="fnref1"]/a[@href="#fn1"]' '1'
+//@ has - '//li[@id="fn2"]//a[@href="#fn1"]' '1'
+
+//! # footnote-hell
+//!
+//! Hello [^a].
+//!
+//! [^a]: meow [^b]
+//! [^b]: uwu [^a]