From db277f528407864e24d3d7934d9c23e28d950165 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 12 Jun 2023 14:56:54 -0700 Subject: [PATCH] rustdoc-search: search never type with `!` This feature extends rustdoc to support the syntax that most users will naturally attempt to use to search for diverging functions. Part of #60485 It's already possible to do this search with `primitive:never`, but that's not what the Rust language itself uses, so nobody will try it if they aren't told or helped along. --- src/librustdoc/html/static/js/search.js | 48 +++++++++++++-- tests/rustdoc-js-std/never.js | 20 +++++-- tests/rustdoc-js-std/parser-errors.js | 9 +++ tests/rustdoc-js-std/parser-ident.js | 80 ++++++++++++++++++++++--- tests/rustdoc-js-std/parser-returned.js | 8 +-- tests/rustdoc-js/never-search.js | 46 ++++++++++++++ tests/rustdoc-js/never-search.rs | 13 ++++ 7 files changed, 200 insertions(+), 24 deletions(-) create mode 100644 tests/rustdoc-js/never-search.js create mode 100644 tests/rustdoc-js/never-search.rs diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 984358396ab..1ccfca8d0d5 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -386,6 +386,35 @@ function initSearch(rawSearchIndex) { if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) { throw ["You cannot have more than one element if you use quotes"]; } + const typeFilter = parserState.typeFilter; + parserState.typeFilter = null; + if (name === "!") { + if (typeFilter !== null && typeFilter !== "primitive") { + throw [ + "Invalid search type: primitive never type ", + "!", + " and ", + typeFilter, + " both specified", + ]; + } + if (generics.length !== 0) { + throw [ + "Never type ", + "!", + " does not accept generic parameters", + ]; + } + return { + name: "never", + id: -1, + fullPath: ["never"], + pathWithoutLast: [], + pathLast: "never", + generics: [], + typeFilter: "primitive", + }; + } const pathSegments = name.split("::"); if (pathSegments.length > 1) { for (let i = 0, len = pathSegments.length; i < len; ++i) { @@ -399,6 +428,13 @@ function initSearch(rawSearchIndex) { } throw ["Unexpected ", "::::"]; } + + if (pathSegment === "!") { + pathSegments[i] = "never"; + if (i !== 0) { + throw ["Never type ", "!", " is not associated item"]; + } + } } } // In case we only have something like `

`, there is no name. @@ -409,8 +445,6 @@ function initSearch(rawSearchIndex) { if (isInGenerics) { parserState.genericsElems += 1; } - const typeFilter = parserState.typeFilter; - parserState.typeFilter = null; return { name: name, id: -1, @@ -459,10 +493,11 @@ function initSearch(rawSearchIndex) { break; } if (foundExclamation !== -1) { - if (start <= (end - 2)) { + if (foundExclamation !== start && + isIdentCharacter(parserState.userQuery[foundExclamation - 1]) + ) { throw ["Cannot have associated items in macros"]; } else { - // if start == end - 1, we got the never type // while the never type has no associated macros, we still // can parse a path like that foundExclamation = -1; @@ -478,7 +513,10 @@ function initSearch(rawSearchIndex) { end = parserState.pos; } // if start == end - 1, we got the never type - if (foundExclamation !== -1 && start <= (end - 2)) { + if (foundExclamation !== -1 && + foundExclamation !== start && + isIdentCharacter(parserState.userQuery[foundExclamation - 1]) + ) { if (parserState.typeFilter === null) { parserState.typeFilter = "macro"; } else if (parserState.typeFilter !== "macro") { diff --git a/tests/rustdoc-js-std/never.js b/tests/rustdoc-js-std/never.js index ed3776b3c2a..27d415b5e48 100644 --- a/tests/rustdoc-js-std/never.js +++ b/tests/rustdoc-js-std/never.js @@ -1,6 +1,14 @@ -const EXPECTED = { - 'query': '!', - 'others': [ - { 'path': 'std', 'name': 'never' }, - ], -}; +const EXPECTED = [ + { + 'query': '!', + 'others': [ + { 'path': 'std', 'name': 'never' }, + ], + }, + { + 'query': '!::clone', + 'others': [ + { 'path': 'std::never', 'name': 'clone' }, + ], + }, +]; diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index aa8ee86d672..af7f63f99cb 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -359,6 +359,15 @@ const PARSED = [ userQuery: "mod:a!", error: 'Invalid search type: macro `!` and `mod` both specified', }, + { + query: "mod:!", + elems: [], + foundElems: 0, + original: "mod:!", + returned: [], + userQuery: "mod:!", + error: 'Invalid search type: primitive never type `!` and `mod` both specified', + }, { query: "a!::a", elems: [], diff --git a/tests/rustdoc-js-std/parser-ident.js b/tests/rustdoc-js-std/parser-ident.js index d9ee5fb564b..f65a7ce6692 100644 --- a/tests/rustdoc-js-std/parser-ident.js +++ b/tests/rustdoc-js-std/parser-ident.js @@ -8,11 +8,12 @@ const PARSED = [ pathLast: "r", generics: [ { - name: "!", - fullPath: ["!"], + name: "never", + fullPath: ["never"], pathWithoutLast: [], - pathLast: "!", + pathLast: "never", generics: [], + typeFilter: 15, }, ], typeFilter: -1, @@ -26,12 +27,12 @@ const PARSED = [ { query: "!", elems: [{ - name: "!", - fullPath: ["!"], + name: "never", + fullPath: ["never"], pathWithoutLast: [], - pathLast: "!", + pathLast: "never", generics: [], - typeFilter: -1, + typeFilter: 15, }], foundElems: 1, original: "!", @@ -64,12 +65,21 @@ const PARSED = [ userQuery: "a!::b", error: "Cannot have associated items in macros", }, + { + query: "!", + elems: [], + foundElems: 0, + original: "!", + returned: [], + userQuery: "!", + error: "Never type `!` does not accept generic parameters", + }, { query: "!::b", elems: [{ name: "!::b", - fullPath: ["!", "b"], - pathWithoutLast: ["!"], + fullPath: ["never", "b"], + pathWithoutLast: ["never"], pathLast: "b", generics: [], typeFilter: -1, @@ -80,6 +90,58 @@ const PARSED = [ userQuery: "!::b", error: null, }, + { + query: "b::!", + elems: [], + foundElems: 0, + original: "b::!", + returned: [], + userQuery: "b::!", + error: "Never type `!` is not associated item", + }, + { + query: "!::!", + elems: [], + foundElems: 0, + original: "!::!", + returned: [], + userQuery: "!::!", + error: "Never type `!` is not associated item", + }, + { + query: "b::!::c", + elems: [], + foundElems: 0, + original: "b::!::c", + returned: [], + userQuery: "b::!::c", + error: "Never type `!` is not associated item", + }, + { + query: "!::b", + elems: [{ + name: "!::b", + fullPath: ["never", "b"], + pathWithoutLast: ["never"], + pathLast: "b", + generics: [ + { + name: "t", + fullPath: ["t"], + pathWithoutLast: [], + pathLast: "t", + generics: [], + typeFilter: -1, + } + ], + typeFilter: -1, + }], + foundElems: 1, + original: "!::b", + returned: [], + userQuery: "!::b", + error: null, + }, { query: "a!::b!", elems: [], diff --git a/tests/rustdoc-js-std/parser-returned.js b/tests/rustdoc-js-std/parser-returned.js index 665e2a9b2e3..6ea86609115 100644 --- a/tests/rustdoc-js-std/parser-returned.js +++ b/tests/rustdoc-js-std/parser-returned.js @@ -84,12 +84,12 @@ const PARSED = [ foundElems: 1, original: "-> !", returned: [{ - name: "!", - fullPath: ["!"], + name: "never", + fullPath: ["never"], pathWithoutLast: [], - pathLast: "!", + pathLast: "never", generics: [], - typeFilter: -1, + typeFilter: 15, }], userQuery: "-> !", error: null, diff --git a/tests/rustdoc-js/never-search.js b/tests/rustdoc-js/never-search.js new file mode 100644 index 00000000000..ed24d693133 --- /dev/null +++ b/tests/rustdoc-js/never-search.js @@ -0,0 +1,46 @@ +// exact-check + +const EXPECTED = [ + { + 'query': '-> !', + 'others': [ + { 'path': 'never_search', 'name': 'loops' }, + ], + }, + { + 'query': '-> never', + 'others': [ + { 'path': 'never_search', 'name': 'loops' }, + { 'path': 'never_search', 'name': 'returns' }, + ], + }, + { + 'query': '!', + 'in_args': [ + { 'path': 'never_search', 'name': 'impossible' }, + { 'path': 'never_search', 'name': 'box_impossible' }, + ], + }, + { + 'query': 'never', + 'in_args': [ + { 'path': 'never_search', 'name': 'impossible' }, + { 'path': 'never_search', 'name': 'uninteresting' }, + { 'path': 'never_search', 'name': 'box_impossible' }, + { 'path': 'never_search', 'name': 'box_uninteresting' }, + ], + }, + { + 'query': 'box', + 'in_args': [ + { 'path': 'never_search', 'name': 'box_impossible' }, + ], + }, + { + 'query': 'box', + 'in_args': [ + { 'path': 'never_search', 'name': 'box_impossible' }, + { 'path': 'never_search', 'name': 'box_uninteresting' }, + ], + }, +]; diff --git a/tests/rustdoc-js/never-search.rs b/tests/rustdoc-js/never-search.rs new file mode 100644 index 00000000000..299b4660dae --- /dev/null +++ b/tests/rustdoc-js/never-search.rs @@ -0,0 +1,13 @@ +#![feature(never_type)] + +#[allow(nonstandard_style)] +pub struct never; + +pub fn loops() -> ! { loop {} } +pub fn returns() -> never { never } + +pub fn impossible(x: !) { match x {} } +pub fn uninteresting(x: never) { match x { never => {} } } + +pub fn box_impossible(x: Box) { match *x {} } +pub fn box_uninteresting(x: Box) { match *x { never => {} } }