Auto merge of #115960 - GuillaumeGomez:rollup-8tky3qu, r=GuillaumeGomez
Rollup of 6 pull requests Successful merges: - #112725 (rustdoc-search: add support for type parameters) - #114941 (Don't resolve generic impls that may be shadowed by dyn built-in impls) - #115625 (Explain HRTB + infer limitations of old solver) - #115839 (Bump libc to 0.2.148) - #115924 (Don't complain on a single non-exhaustive 1-ZST) - #115946 (panic when encountering an illegal cpumask in thread::available_parallelism) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
8769c26989
@ -2142,9 +2142,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
version = "0.2.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
||||
dependencies = [
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
@ -1201,25 +1201,35 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
|
||||
);
|
||||
return;
|
||||
}
|
||||
for (span, _trivial, non_exhaustive) in field_infos {
|
||||
if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
|
||||
tcx.struct_span_lint_hir(
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
|
||||
span,
|
||||
"zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types",
|
||||
|lint| {
|
||||
let note = if non_exhaustive {
|
||||
"is marked with `#[non_exhaustive]`"
|
||||
} else {
|
||||
"contains private fields"
|
||||
};
|
||||
let field_ty = tcx.def_path_str_with_args(def_id, args);
|
||||
lint
|
||||
.note(format!("this {descr} contains `{field_ty}`, which {note}, \
|
||||
and makes it not a breaking change to become non-zero-sized in the future."))
|
||||
},
|
||||
)
|
||||
let mut prev_non_exhaustive_1zst = false;
|
||||
for (span, _trivial, non_exhaustive_1zst) in field_infos {
|
||||
if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive_1zst {
|
||||
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
|
||||
// Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
|
||||
if non_trivial_count > 0 || prev_non_exhaustive_1zst {
|
||||
tcx.struct_span_lint_hir(
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
|
||||
span,
|
||||
"zero-sized fields in `repr(transparent)` cannot \
|
||||
contain external non-exhaustive types",
|
||||
|lint| {
|
||||
let note = if non_exhaustive {
|
||||
"is marked with `#[non_exhaustive]`"
|
||||
} else {
|
||||
"contains private fields"
|
||||
};
|
||||
let field_ty = tcx.def_path_str_with_args(def_id, args);
|
||||
lint.note(format!(
|
||||
"this {descr} contains `{field_ty}`, which {note}, \
|
||||
and makes it not a breaking change to become \
|
||||
non-zero-sized in the future."
|
||||
))
|
||||
},
|
||||
)
|
||||
} else {
|
||||
prev_non_exhaustive_1zst = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2945,6 +2945,33 @@ impl<'tcx> Ty<'tcx> {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_known_rigid(self) -> bool {
|
||||
match self.kind() {
|
||||
Bool
|
||||
| Char
|
||||
| Int(_)
|
||||
| Uint(_)
|
||||
| Float(_)
|
||||
| Adt(_, _)
|
||||
| Foreign(_)
|
||||
| Str
|
||||
| Array(_, _)
|
||||
| Slice(_)
|
||||
| RawPtr(_)
|
||||
| Ref(_, _, _)
|
||||
| FnDef(_, _)
|
||||
| FnPtr(_)
|
||||
| Dynamic(_, _, _)
|
||||
| Closure(_, _)
|
||||
| Generator(_, _, _)
|
||||
| GeneratorWitness(_)
|
||||
| GeneratorWitnessMIR(_, _)
|
||||
| Never
|
||||
| Tuple(_) => true,
|
||||
Error(_) | Infer(_) | Alias(_, _) | Param(_) | Bound(_, _) | Placeholder(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extra information about why we ended up with a particular variance.
|
||||
|
@ -986,6 +986,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause);
|
||||
|
||||
// Return early if the trait is Debug or Display and the invocation
|
||||
// originates within a standard library macro, because the output
|
||||
// is otherwise overwhelming and unhelpful (see #85844 for an
|
||||
|
@ -406,6 +406,14 @@ pub trait TypeErrCtxtExt<'tcx> {
|
||||
candidate_impls: &[ImplCandidate<'tcx>],
|
||||
span: Span,
|
||||
);
|
||||
|
||||
fn explain_hrtb_projection(
|
||||
&self,
|
||||
diag: &mut Diagnostic,
|
||||
pred: ty::PolyTraitPredicate<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
);
|
||||
}
|
||||
|
||||
fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
|
||||
@ -4027,6 +4035,71 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn explain_hrtb_projection(
|
||||
&self,
|
||||
diag: &mut Diagnostic,
|
||||
pred: ty::PolyTraitPredicate<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
) {
|
||||
if pred.skip_binder().has_escaping_bound_vars() && pred.skip_binder().has_non_region_infer()
|
||||
{
|
||||
self.probe(|_| {
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
let pred = self.instantiate_binder_with_placeholders(pred);
|
||||
let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
|
||||
ocx.register_obligation(Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
pred,
|
||||
));
|
||||
if !ocx.select_where_possible().is_empty() {
|
||||
// encountered errors.
|
||||
return;
|
||||
}
|
||||
|
||||
if let ObligationCauseCode::FunctionArgumentObligation {
|
||||
call_hir_id,
|
||||
arg_hir_id,
|
||||
parent_code: _,
|
||||
} = cause.code()
|
||||
{
|
||||
let arg_span = self.tcx.hir().span(*arg_hir_id);
|
||||
let mut sp: MultiSpan = arg_span.into();
|
||||
|
||||
sp.push_span_label(
|
||||
arg_span,
|
||||
"the trait solver is unable to infer the \
|
||||
generic types that should be inferred from this argument",
|
||||
);
|
||||
sp.push_span_label(
|
||||
self.tcx.hir().span(*call_hir_id),
|
||||
"add turbofish arguments to this call to \
|
||||
specify the types manually, even if it's redundant",
|
||||
);
|
||||
diag.span_note(
|
||||
sp,
|
||||
"this is a known limitation of the trait solver that \
|
||||
will be lifted in the future",
|
||||
);
|
||||
} else {
|
||||
let mut sp: MultiSpan = cause.span.into();
|
||||
sp.push_span_label(
|
||||
cause.span,
|
||||
"try adding turbofish arguments to this expression to \
|
||||
specify the types manually, even if it's redundant",
|
||||
);
|
||||
diag.span_note(
|
||||
sp,
|
||||
"this is a known limitation of the trait solver that \
|
||||
will be lifted in the future",
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a hint to add a missing borrow or remove an unnecessary one.
|
||||
|
@ -141,11 +141,34 @@ fn resolve_associated_item<'tcx>(
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if !eligible {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// HACK: We may have overlapping `dyn Trait` built-in impls and
|
||||
// user-provided blanket impls. Detect that case here, and return
|
||||
// ambiguity.
|
||||
//
|
||||
// This should not affect totally monomorphized contexts, only
|
||||
// resolve calls that happen polymorphically, such as the mir-inliner
|
||||
// and const-prop (and also some lints).
|
||||
let self_ty = rcvr_args.type_at(0);
|
||||
if !self_ty.is_known_rigid() {
|
||||
let predicates = tcx
|
||||
.predicates_of(impl_data.impl_def_id)
|
||||
.instantiate(tcx, impl_data.args)
|
||||
.predicates;
|
||||
let sized_def_id = tcx.lang_items().sized_trait();
|
||||
// If we find a `Self: Sized` bound on the item, then we know
|
||||
// that `dyn Trait` can certainly never apply here.
|
||||
if !predicates.into_iter().filter_map(ty::Clause::as_trait_clause).any(|clause| {
|
||||
Some(clause.def_id()) == sized_def_id
|
||||
&& clause.skip_binder().self_ty() == self_ty
|
||||
}) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
// Any final impl is required to define all associated items.
|
||||
if !leaf_def.item.defaultness(tcx).has_value() {
|
||||
let guard = tcx.sess.delay_span_bug(
|
||||
|
@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
|
||||
panic_unwind = { path = "../panic_unwind", optional = true }
|
||||
panic_abort = { path = "../panic_abort" }
|
||||
core = { path = "../core", public = true }
|
||||
libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true }
|
||||
libc = { version = "0.2.148", default-features = false, features = ['rustc-dep-of-std'], public = true }
|
||||
compiler_builtins = { version = "0.1.100" }
|
||||
profiler_builtins = { path = "../profiler_builtins", optional = true }
|
||||
unwind = { path = "../unwind" }
|
||||
|
@ -324,8 +324,10 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
|
||||
if libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) == 0 {
|
||||
let count = libc::CPU_COUNT(&set) as usize;
|
||||
let count = count.min(quota);
|
||||
// SAFETY: affinity mask can't be empty and the quota gets clamped to a minimum of 1
|
||||
return Ok(NonZeroUsize::new_unchecked(count));
|
||||
// reported to occur on MIPS kernels older than our minimum supported kernel version for those targets
|
||||
let count = NonZeroUsize::new(count)
|
||||
.expect("CPU count must be > 0. This may be a bug in sched_getaffinity(); try upgrading the kernel.");
|
||||
return Ok(count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
- [Command-line arguments](command-line-arguments.md)
|
||||
- [How to read rustdoc output](how-to-read-rustdoc.md)
|
||||
- [In-doc settings](read-documentation/in-doc-settings.md)
|
||||
- [Search](read-documentation/search.md)
|
||||
- [How to write documentation](how-to-write-documentation.md)
|
||||
- [What to include (and exclude)](write-documentation/what-to-include.md)
|
||||
- [The `#[doc]` attribute](write-documentation/the-doc-attribute.md)
|
||||
|
@ -75,56 +75,11 @@ or the current item whose documentation is being displayed.
|
||||
## The Theme Picker and Search Interface
|
||||
|
||||
When viewing `rustdoc`'s output in a browser with JavaScript enabled,
|
||||
a dynamic interface appears at the top of the page composed of the search
|
||||
interface, help screen, and options.
|
||||
a dynamic interface appears at the top of the page composed of the [search]
|
||||
interface, help screen, and [options].
|
||||
|
||||
### The Search Interface
|
||||
|
||||
Typing in the search bar instantly searches the available documentation for
|
||||
the string entered with a fuzzy matching algorithm that is tolerant of minor
|
||||
typos.
|
||||
|
||||
By default, the search results given are "In Names",
|
||||
meaning that the fuzzy match is made against the names of items.
|
||||
Matching names are shown on the left, and the first few words of their
|
||||
descriptions are given on the right.
|
||||
By clicking an item, you will navigate to its particular documentation.
|
||||
|
||||
There are two other sets of results, shown as tabs in the search results pane.
|
||||
"In Parameters" shows matches for the string in the types of parameters to
|
||||
functions, and "In Return Types" shows matches in the return types of functions.
|
||||
Both are very useful when looking for a function whose name you can't quite
|
||||
bring to mind when you know the type you have or want.
|
||||
|
||||
Names in the search interface can be prefixed with an item type followed by a
|
||||
colon (such as `mod:`) to restrict the results to just that kind of item. Also,
|
||||
searching for `println!` will search for a macro named `println`, just like
|
||||
searching for `macro:println` does.
|
||||
|
||||
Function signature searches can query generics, wrapped in angle brackets, and
|
||||
traits are normalized like types in the search engine. For example, a function
|
||||
with the signature `fn my_function<I: Iterator<Item=u32>>(input: I) -> usize`
|
||||
can be matched with the following queries:
|
||||
|
||||
* `Iterator<u32> -> usize`
|
||||
* `trait:Iterator<primitive:u32> -> primitive:usize`
|
||||
* `Iterator -> usize`
|
||||
|
||||
Generics and function parameters are order-agnostic, but sensitive to nesting
|
||||
and number of matches. For example, a function with the signature
|
||||
`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
|
||||
will match these queries:
|
||||
|
||||
* `Read -> Result<Vec<u8>, Error>`
|
||||
* `Read -> Result<Error, Vec>`
|
||||
* `Read -> Result<Vec<u8>>`
|
||||
|
||||
But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
|
||||
|
||||
Function signature searches also support arrays and slices. The explicit name
|
||||
`primitive:slice<u8>` and `primitive:array<u8>` can be used to match a slice
|
||||
or array of bytes, while square brackets `[u8]` will match either one. Empty
|
||||
square brackets, `[]`, will match any slice regardless of what it contains.
|
||||
[options]: read-documentation/in-doc-settings.html
|
||||
[search]: read-documentation/search.md
|
||||
|
||||
Paths are supported as well, you can look for `Vec::new` or `Option::Some` or
|
||||
even `module::module_child::another_child::struct::field`. Whitespace characters
|
||||
|
237
src/doc/rustdoc/src/read-documentation/search.md
Normal file
237
src/doc/rustdoc/src/read-documentation/search.md
Normal file
@ -0,0 +1,237 @@
|
||||
# Rustdoc search
|
||||
|
||||
Typing in the search bar instantly searches the available documentation,
|
||||
matching either the name and path of an item, or a function's approximate
|
||||
type signature.
|
||||
|
||||
## Search By Name
|
||||
|
||||
To search by the name of an item (items include modules, types, traits,
|
||||
functions, and macros), write its name or path. As a special case, the parts
|
||||
of a path that normally get divided by `::` double colons can instead be
|
||||
separated by spaces. For example:
|
||||
|
||||
* [`vec new`] and [`vec::new`] both show the function `std::vec::Vec::new`
|
||||
as a result.
|
||||
* [`vec`], [`vec vec`], [`std::vec`], and [`std::vec::Vec`] all include the struct
|
||||
`std::vec::Vec` itself in the results (and all but the last one also
|
||||
include the module in the results).
|
||||
|
||||
[`vec new`]: ../../std/vec/struct.Vec.html?search=vec%20new&filter-crate=std
|
||||
[`vec::new`]: ../../std/vec/struct.Vec.html?search=vec::new&filter-crate=std
|
||||
[`vec`]: ../../std/vec/struct.Vec.html?search=vec&filter-crate=std
|
||||
[`vec vec`]: ../../std/vec/struct.Vec.html?search=vec%20vec&filter-crate=std
|
||||
[`std::vec`]: ../../std/vec/struct.Vec.html?search=std::vec&filter-crate=std
|
||||
[`std::vec::Vec`]: ../../std/vec/struct.Vec.html?search=std::vec::Vec&filter-crate=std
|
||||
[`std::vec::Vec`]: ../../std/vec/struct.Vec.html?search=std::vec::Vec&filter-crate=std
|
||||
|
||||
As a quick way to trim down the list of results, there's a drop-down selector
|
||||
below the search input, labeled "Results in \[std\]". Clicking it can change
|
||||
which crate is being searched.
|
||||
|
||||
Rustdoc uses a fuzzy matching function that can tolerate typos for this,
|
||||
though it's based on the length of the name that's typed in, so a good example
|
||||
of how this works would be [`HahsMap`]. To avoid this, wrap the item in quotes,
|
||||
searching for `"HahsMap"` (in this example, no results will be returned).
|
||||
|
||||
[`HahsMap`]: ../../std/collections/struct.HashMap.html?search=HahsMap&filter-crate=std
|
||||
|
||||
### Tabs in the Search By Name interface
|
||||
|
||||
In fact, using [`HahsMap`] again as the example, it tells you that you're
|
||||
using "In Names" by default, but also lists two other tabs below the crate
|
||||
drop-down: "In Parameters" and "In Return Types".
|
||||
|
||||
These two tabs are lists of functions, defined on the closest matching type
|
||||
to the search (for `HahsMap`, it loudly auto-corrects to `hashmap`). This
|
||||
auto-correct only kicks in if nothing is found that matches the literal.
|
||||
|
||||
These tabs are not just methods. For example, searching the alloc crate for
|
||||
[`Layout`] also lists functions that accept layouts even though they're
|
||||
methods on the allocator or free functions.
|
||||
|
||||
[`Layout`]: ../../alloc/index.html?search=Layout&filter-crate=alloc
|
||||
|
||||
## Searching By Type Signature for functions
|
||||
|
||||
If you know more specifically what the function you want to look at does,
|
||||
Rustdoc can search by more than one type at once in the parameters and return
|
||||
value. Multiple parameters are separated by `,` commas, and the return value
|
||||
is written with after a `->` arrow.
|
||||
|
||||
Before describing the syntax in more detail, here's a few sample searches of
|
||||
the standard library and functions that are included in the results list:
|
||||
|
||||
| Query | Results |
|
||||
|-------|--------|
|
||||
| [`usize -> vec`][] | `slice::repeat` and `Vec::with_capacity` |
|
||||
| [`vec, vec -> bool`][] | `Vec::eq` |
|
||||
| [`option<T>, fnonce -> option<U>`][] | `Option::map` and `Option::and_then` |
|
||||
| [`option<T>, fnonce -> option<T>`][] | `Option::filter` and `Option::inspect` |
|
||||
| [`option -> default`][] | `Option::unwrap_or_default` |
|
||||
| [`stdout, [u8]`][stdoutu8] | `Stdout::write` |
|
||||
| [`any -> !`][] | `panic::panic_any` |
|
||||
| [`vec::intoiter<T> -> [T]`][iterasslice] | `IntoIter::as_slice` and `IntoIter::next_chunk` |
|
||||
|
||||
[`usize -> vec`]: ../../std/vec/struct.Vec.html?search=usize%20-%3E%20vec&filter-crate=std
|
||||
[`vec, vec -> bool`]: ../../std/vec/struct.Vec.html?search=vec,%20vec%20-%3E%20bool&filter-crate=std
|
||||
[`option<T>, fnonce -> option<U>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<U>&filter-crate=std
|
||||
[`option<T>, fnonce -> option<T>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<T>&filter-crate=std
|
||||
[`option -> default`]: ../../std/vec/struct.Vec.html?search=option%20-%3E%20default&filter-crate=std
|
||||
[`any -> !`]: ../../std/vec/struct.Vec.html?search=any%20-%3E%20!&filter-crate=std
|
||||
[stdoutu8]: ../../std/vec/struct.Vec.html?search=stdout%2C%20[u8]&filter-crate=std
|
||||
[iterasslice]: ../../std/vec/struct.Vec.html?search=vec%3A%3Aintoiter<T>%20->%20[T]&filter-crate=std
|
||||
|
||||
### How type-based search works
|
||||
|
||||
In a complex type-based search, Rustdoc always treats every item's name as literal.
|
||||
If a name is used and nothing in the docs matches the individual item, such as
|
||||
a typo-ed [`uize -> vec`][] search, the item `uize` is treated as a generic
|
||||
type parameter (resulting in `vec::from` and other generic vec constructors).
|
||||
|
||||
[`uize -> vec`]: ../../std/vec/struct.Vec.html?search=uize%20-%3E%20vec&filter-crate=std
|
||||
|
||||
After deciding which items are type parameters and which are actual types, it
|
||||
then searches by matching up the function parameters (written before the `->`)
|
||||
and the return types (written after the `->`). Type matching is order-agnostic,
|
||||
and allows items to be left out of the query, but items that are present in the
|
||||
query must be present in the function for it to match.
|
||||
|
||||
Function signature searches can query generics, wrapped in angle brackets, and
|
||||
traits will be normalized like types in the search engine if no type parameters
|
||||
match them. For example, a function with the signature
|
||||
`fn my_function<I: Iterator<Item=u32>>(input: I) -> usize`
|
||||
can be matched with the following queries:
|
||||
|
||||
* `Iterator<u32> -> usize`
|
||||
* `Iterator -> usize`
|
||||
|
||||
Generics and function parameters are order-agnostic, but sensitive to nesting
|
||||
and number of matches. For example, a function with the signature
|
||||
`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
|
||||
will match these queries:
|
||||
|
||||
* `Read -> Result<Vec<u8>, Error>`
|
||||
* `Read -> Result<Error, Vec>`
|
||||
* `Read -> Result<Vec<u8>>`
|
||||
|
||||
But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
|
||||
|
||||
Function signature searches also support arrays and slices. The explicit name
|
||||
`primitive:slice<u8>` and `primitive:array<u8>` can be used to match a slice
|
||||
or array of bytes, while square brackets `[u8]` will match either one. Empty
|
||||
square brackets, `[]`, will match any slice or array regardless of what
|
||||
it contains, while a slice with a type parameter, like `[T]`, will only match
|
||||
functions that actually operate on generic slices.
|
||||
|
||||
### Limitations and quirks of type-based search
|
||||
|
||||
Type-based search is still a buggy, experimental, work-in-progress feature.
|
||||
Most of these limitations should be addressed in future version of Rustdoc.
|
||||
|
||||
* There's no way to write trait constraints on generic parameters.
|
||||
You can name traits directly, and if there's a type parameter
|
||||
with that bound, it'll match, but `option<T> -> T where T: Default`
|
||||
cannot be precisely searched for (use `option<Default> -> Default`).
|
||||
|
||||
* Type parameters match type parameters, such that `Option<A>` matches
|
||||
`Option<T>`, but never match concrete types in function signatures.
|
||||
A trait named as if it were a type, such as `Option<Read>`, will match
|
||||
a type parameter constrained by that trait, such as
|
||||
`Option<T> where T: Read`, as well as matching `dyn Trait` and
|
||||
`impl Trait`.
|
||||
|
||||
* `impl Trait` in argument position is treated exactly like a type
|
||||
parameter, but in return position it will not match type parameters.
|
||||
|
||||
* Any type named in a complex type-based search will be assumed to be a
|
||||
type parameter if nothing matching the name exactly is found. If you
|
||||
want to force a type parameter, write `generic:T` and it will be used
|
||||
as a type parameter even if a matching name is found. If you know
|
||||
that you don't want a type parameter, you can force it to match
|
||||
something else by giving it a different prefix like `struct:T`.
|
||||
|
||||
* It's impossible to search for references, pointers, or tuples. The
|
||||
wrapped types can be searched for, so a function that takes `&File` can
|
||||
be found with `File`, but you'll get a parse error when typing an `&`
|
||||
into the search field. Similarly, `Option<(T, U)>` can be matched with
|
||||
`Option<T, U>`, but `(` will give a parse error.
|
||||
|
||||
* Searching for lifetimes is not supported.
|
||||
|
||||
* It's impossible to search for closures based on their parameters or
|
||||
return values.
|
||||
|
||||
* It's impossible to search based on the length of an array.
|
||||
|
||||
## Item filtering
|
||||
|
||||
Names in the search interface can be prefixed with an item type followed by a
|
||||
colon (such as `mod:`) to restrict the results to just that kind of item. Also,
|
||||
searching for `println!` will search for a macro named `println`, just like
|
||||
searching for `macro:println` does. The complete list of available filters is
|
||||
given under the <kbd>?</kbd> Help area, and in the detailed syntax below.
|
||||
|
||||
Item filters can be used in both name-based and type signature-based searches.
|
||||
|
||||
## Search query syntax
|
||||
|
||||
```text
|
||||
ident = *(ALPHA / DIGIT / "_")
|
||||
path = ident *(DOUBLE-COLON ident) [!]
|
||||
slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
|
||||
arg = [type-filter *WS COLON *WS] (path [generics] / slice / [!])
|
||||
type-sep = COMMA/WS *(COMMA/WS)
|
||||
nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
|
||||
generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
|
||||
CLOSE-ANGLE-BRACKET
|
||||
return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
|
||||
|
||||
exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
|
||||
type-search = [ nonempty-arg-list ] [ return-args ]
|
||||
|
||||
query = *WS (exact-search / type-search) *WS
|
||||
|
||||
type-filter = (
|
||||
"mod" /
|
||||
"externcrate" /
|
||||
"import" /
|
||||
"struct" /
|
||||
"enum" /
|
||||
"fn" /
|
||||
"type" /
|
||||
"static" /
|
||||
"trait" /
|
||||
"impl" /
|
||||
"tymethod" /
|
||||
"method" /
|
||||
"structfield" /
|
||||
"variant" /
|
||||
"macro" /
|
||||
"primitive" /
|
||||
"associatedtype" /
|
||||
"constant" /
|
||||
"associatedconstant" /
|
||||
"union" /
|
||||
"foreigntype" /
|
||||
"keyword" /
|
||||
"existential" /
|
||||
"attr" /
|
||||
"derive" /
|
||||
"traitalias" /
|
||||
"generic")
|
||||
|
||||
OPEN-ANGLE-BRACKET = "<"
|
||||
CLOSE-ANGLE-BRACKET = ">"
|
||||
OPEN-SQUARE-BRACKET = "["
|
||||
CLOSE-SQUARE-BRACKET = "]"
|
||||
COLON = ":"
|
||||
DOUBLE-COLON = "::"
|
||||
QUOTE = %x22
|
||||
COMMA = ","
|
||||
RETURN-ARROW = "->"
|
||||
|
||||
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
|
||||
DIGIT = %x30-39
|
||||
WS = %x09 / " "
|
||||
```
|
@ -1639,10 +1639,6 @@ impl Type {
|
||||
matches!(self, Type::Generic(_))
|
||||
}
|
||||
|
||||
pub(crate) fn is_impl_trait(&self) -> bool {
|
||||
matches!(self, Type::ImplTrait(_))
|
||||
}
|
||||
|
||||
pub(crate) fn is_unit(&self) -> bool {
|
||||
matches!(self, Type::Tuple(v) if v.is_empty())
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ pub(crate) struct IndexItem {
|
||||
pub(crate) path: String,
|
||||
pub(crate) desc: String,
|
||||
pub(crate) parent: Option<DefId>,
|
||||
pub(crate) parent_idx: Option<usize>,
|
||||
pub(crate) parent_idx: Option<isize>,
|
||||
pub(crate) search_type: Option<IndexItemFunctionType>,
|
||||
pub(crate) aliases: Box<[Symbol]>,
|
||||
pub(crate) deprecation: Option<Deprecation>,
|
||||
@ -122,7 +122,10 @@ impl Serialize for RenderType {
|
||||
let id = match &self.id {
|
||||
// 0 is a sentinel, everything else is one-indexed
|
||||
None => 0,
|
||||
Some(RenderTypeId::Index(idx)) => idx + 1,
|
||||
// concrete type
|
||||
Some(RenderTypeId::Index(idx)) if *idx >= 0 => idx + 1,
|
||||
// generic type parameter
|
||||
Some(RenderTypeId::Index(idx)) => *idx,
|
||||
_ => panic!("must convert render types to indexes before serializing"),
|
||||
};
|
||||
if let Some(generics) = &self.generics {
|
||||
@ -140,7 +143,7 @@ impl Serialize for RenderType {
|
||||
pub(crate) enum RenderTypeId {
|
||||
DefId(DefId),
|
||||
Primitive(clean::PrimitiveType),
|
||||
Index(usize),
|
||||
Index(isize),
|
||||
}
|
||||
|
||||
/// Full type of functions/methods in the search index.
|
||||
@ -148,6 +151,7 @@ pub(crate) enum RenderTypeId {
|
||||
pub(crate) struct IndexItemFunctionType {
|
||||
inputs: Vec<RenderType>,
|
||||
output: Vec<RenderType>,
|
||||
where_clause: Vec<Vec<RenderType>>,
|
||||
}
|
||||
|
||||
impl Serialize for IndexItemFunctionType {
|
||||
@ -170,10 +174,17 @@ impl Serialize for IndexItemFunctionType {
|
||||
_ => seq.serialize_element(&self.inputs)?,
|
||||
}
|
||||
match &self.output[..] {
|
||||
[] => {}
|
||||
[] if self.where_clause.is_empty() => {}
|
||||
[one] if one.generics.is_none() => seq.serialize_element(one)?,
|
||||
_ => seq.serialize_element(&self.output)?,
|
||||
}
|
||||
for constraint in &self.where_clause {
|
||||
if let [one] = &constraint[..] && one.generics.is_none() {
|
||||
seq.serialize_element(one)?;
|
||||
} else {
|
||||
seq.serialize_element(constraint)?;
|
||||
}
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
|
@ -68,16 +68,16 @@ pub(crate) fn build_index<'tcx>(
|
||||
// Reduce `DefId` in paths into smaller sequential numbers,
|
||||
// and prune the paths that do not appear in the index.
|
||||
let mut lastpath = "";
|
||||
let mut lastpathid = 0usize;
|
||||
let mut lastpathid = 0isize;
|
||||
|
||||
// First, on function signatures
|
||||
let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new());
|
||||
for item in search_index.iter_mut() {
|
||||
fn insert_into_map<F: std::hash::Hash + Eq>(
|
||||
ty: &mut RenderType,
|
||||
map: &mut FxHashMap<F, usize>,
|
||||
map: &mut FxHashMap<F, isize>,
|
||||
itemid: F,
|
||||
lastpathid: &mut usize,
|
||||
lastpathid: &mut isize,
|
||||
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
|
||||
item_type: ItemType,
|
||||
path: &[Symbol],
|
||||
@ -97,9 +97,9 @@ pub(crate) fn build_index<'tcx>(
|
||||
fn convert_render_type(
|
||||
ty: &mut RenderType,
|
||||
cache: &mut Cache,
|
||||
itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
|
||||
primitives: &mut FxHashMap<Symbol, usize>,
|
||||
lastpathid: &mut usize,
|
||||
itemid_to_pathid: &mut FxHashMap<ItemId, isize>,
|
||||
primitives: &mut FxHashMap<Symbol, isize>,
|
||||
lastpathid: &mut isize,
|
||||
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
|
||||
) {
|
||||
if let Some(generics) = &mut ty.generics {
|
||||
@ -173,6 +173,18 @@ pub(crate) fn build_index<'tcx>(
|
||||
&mut crate_paths,
|
||||
);
|
||||
}
|
||||
for constraint in &mut search_type.where_clause {
|
||||
for trait_ in &mut constraint[..] {
|
||||
convert_render_type(
|
||||
trait_,
|
||||
cache,
|
||||
&mut itemid_to_pathid,
|
||||
&mut primitives,
|
||||
&mut lastpathid,
|
||||
&mut crate_paths,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -402,7 +414,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
|
||||
impl_generics: Option<&(clean::Type, clean::Generics)>,
|
||||
cache: &Cache,
|
||||
) -> Option<IndexItemFunctionType> {
|
||||
let (mut inputs, mut output) = match *item.kind {
|
||||
let (mut inputs, mut output, where_clause) = match *item.kind {
|
||||
clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
|
||||
clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
|
||||
clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
|
||||
@ -412,7 +424,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
|
||||
inputs.retain(|a| a.id.is_some() || a.generics.is_some());
|
||||
output.retain(|a| a.id.is_some() || a.generics.is_some());
|
||||
|
||||
Some(IndexItemFunctionType { inputs, output })
|
||||
Some(IndexItemFunctionType { inputs, output, where_clause })
|
||||
}
|
||||
|
||||
fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
|
||||
@ -432,96 +444,48 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
|
||||
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
|
||||
get_index_type_id(type_)
|
||||
}
|
||||
// The type parameters are converted to generics in `add_generics_and_bounds_as_types`
|
||||
// The type parameters are converted to generics in `simplify_fn_type`
|
||||
clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
|
||||
clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
|
||||
clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
|
||||
// Not supported yet
|
||||
clean::BareFunction(_)
|
||||
| clean::Generic(_)
|
||||
| clean::ImplTrait(_)
|
||||
| clean::Tuple(_)
|
||||
| clean::QPath { .. }
|
||||
| clean::Infer => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The point of this function is to replace bounds with types.
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
enum SimplifiedParam {
|
||||
// other kinds of type parameters are identified by their name
|
||||
Symbol(Symbol),
|
||||
// every argument-position impl trait is its own type parameter
|
||||
Anonymous(isize),
|
||||
}
|
||||
|
||||
/// The point of this function is to lower generics and types into the simplified form that the
|
||||
/// frontend search engine can use.
|
||||
///
|
||||
/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
|
||||
/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
|
||||
/// For example, `[T, U, i32]]` where you have the bounds: `T: Display, U: Option<T>` will return
|
||||
/// `[-1, -2, i32] where -1: Display, -2: Option<-1>`. If a type parameter has no traid bound, it
|
||||
/// will still get a number. If a constraint is present but not used in the actual types, it will
|
||||
/// not be added to the map.
|
||||
///
|
||||
/// Important note: It goes through generics recursively. So if you have
|
||||
/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
|
||||
#[instrument(level = "trace", skip(tcx, res, cache))]
|
||||
fn add_generics_and_bounds_as_types<'tcx, 'a>(
|
||||
/// This function also works recursively.
|
||||
#[instrument(level = "trace", skip(tcx, res, rgen, cache))]
|
||||
fn simplify_fn_type<'tcx, 'a>(
|
||||
self_: Option<&'a Type>,
|
||||
generics: &Generics,
|
||||
arg: &'a Type,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
recurse: usize,
|
||||
res: &mut Vec<RenderType>,
|
||||
rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
|
||||
is_return: bool,
|
||||
cache: &Cache,
|
||||
) {
|
||||
fn insert_ty(res: &mut Vec<RenderType>, ty: Type, mut generics: Vec<RenderType>) {
|
||||
// generics and impl trait are both identified by their generics,
|
||||
// rather than a type name itself
|
||||
let anonymous = ty.is_full_generic() || ty.is_impl_trait();
|
||||
let generics_empty = generics.is_empty();
|
||||
|
||||
if anonymous {
|
||||
if generics_empty {
|
||||
// This is a type parameter with no trait bounds (for example: `T` in
|
||||
// `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
|
||||
// with an empty type with an empty name. Let's just discard it.
|
||||
return;
|
||||
} else if generics.len() == 1 {
|
||||
// In this case, no need to go through an intermediate state if the type parameter
|
||||
// contains only one trait bound.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// `fn foo<T: Display>(r: Option<T>) {}`
|
||||
//
|
||||
// In this case, it would contain:
|
||||
//
|
||||
// ```
|
||||
// [{
|
||||
// name: "option",
|
||||
// generics: [{
|
||||
// name: "",
|
||||
// generics: [
|
||||
// name: "Display",
|
||||
// generics: []
|
||||
// }]
|
||||
// }]
|
||||
// }]
|
||||
// ```
|
||||
//
|
||||
// After removing the intermediate (unnecessary) type parameter, it'll become:
|
||||
//
|
||||
// ```
|
||||
// [{
|
||||
// name: "option",
|
||||
// generics: [{
|
||||
// name: "Display",
|
||||
// generics: []
|
||||
// }]
|
||||
// }]
|
||||
// ```
|
||||
//
|
||||
// To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
|
||||
// need to keep it as is!
|
||||
res.push(generics.pop().unwrap());
|
||||
return;
|
||||
}
|
||||
}
|
||||
let index_ty = get_index_type(&ty, generics);
|
||||
if index_ty.id.is_none() && generics_empty {
|
||||
return;
|
||||
}
|
||||
res.push(index_ty);
|
||||
}
|
||||
|
||||
if recurse >= 10 {
|
||||
// FIXME: remove this whole recurse thing when the recursion bug is fixed
|
||||
// See #59502 for the original issue.
|
||||
@ -548,88 +512,126 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
|
||||
// for its bounds.
|
||||
if let Type::Generic(arg_s) = *arg {
|
||||
// First we check if the bounds are in a `where` predicate...
|
||||
let mut type_bounds = Vec::new();
|
||||
for where_pred in generics.where_predicates.iter().filter(|g| match g {
|
||||
WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s,
|
||||
_ => false,
|
||||
}) {
|
||||
let mut ty_generics = Vec::new();
|
||||
let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
|
||||
for bound in bounds.iter() {
|
||||
if let Some(path) = bound.get_trait_path() {
|
||||
let ty = Type::Path { path };
|
||||
add_generics_and_bounds_as_types(
|
||||
simplify_fn_type(
|
||||
self_,
|
||||
generics,
|
||||
&ty,
|
||||
tcx,
|
||||
recurse + 1,
|
||||
&mut ty_generics,
|
||||
&mut type_bounds,
|
||||
rgen,
|
||||
is_return,
|
||||
cache,
|
||||
);
|
||||
}
|
||||
}
|
||||
insert_ty(res, arg.clone(), ty_generics);
|
||||
}
|
||||
// Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
|
||||
if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
|
||||
let mut ty_generics = Vec::new();
|
||||
for bound in bound.get_bounds().unwrap_or(&[]) {
|
||||
if let Some(path) = bound.get_trait_path() {
|
||||
let ty = Type::Path { path };
|
||||
add_generics_and_bounds_as_types(
|
||||
simplify_fn_type(
|
||||
self_,
|
||||
generics,
|
||||
&ty,
|
||||
tcx,
|
||||
recurse + 1,
|
||||
&mut ty_generics,
|
||||
&mut type_bounds,
|
||||
rgen,
|
||||
is_return,
|
||||
cache,
|
||||
);
|
||||
}
|
||||
}
|
||||
insert_ty(res, arg.clone(), ty_generics);
|
||||
}
|
||||
if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) {
|
||||
res.push(RenderType { id: Some(RenderTypeId::Index(*idx)), generics: None });
|
||||
} else {
|
||||
let idx = -isize::try_from(rgen.len() + 1).unwrap();
|
||||
rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds));
|
||||
res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
|
||||
}
|
||||
} else if let Type::ImplTrait(ref bounds) = *arg {
|
||||
let mut ty_generics = Vec::new();
|
||||
let mut type_bounds = Vec::new();
|
||||
for bound in bounds {
|
||||
if let Some(path) = bound.get_trait_path() {
|
||||
let ty = Type::Path { path };
|
||||
add_generics_and_bounds_as_types(
|
||||
simplify_fn_type(
|
||||
self_,
|
||||
generics,
|
||||
&ty,
|
||||
tcx,
|
||||
recurse + 1,
|
||||
&mut ty_generics,
|
||||
&mut type_bounds,
|
||||
rgen,
|
||||
is_return,
|
||||
cache,
|
||||
);
|
||||
}
|
||||
}
|
||||
insert_ty(res, arg.clone(), ty_generics);
|
||||
if is_return && !type_bounds.is_empty() {
|
||||
// In parameter position, `impl Trait` is a unique thing.
|
||||
res.push(RenderType { id: None, generics: Some(type_bounds) });
|
||||
} else {
|
||||
// In parameter position, `impl Trait` is the same as an unnamed generic parameter.
|
||||
let idx = -isize::try_from(rgen.len() + 1).unwrap();
|
||||
rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds));
|
||||
res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
|
||||
}
|
||||
} else if let Type::Slice(ref ty) = *arg {
|
||||
let mut ty_generics = Vec::new();
|
||||
add_generics_and_bounds_as_types(
|
||||
simplify_fn_type(
|
||||
self_,
|
||||
generics,
|
||||
&ty,
|
||||
tcx,
|
||||
recurse + 1,
|
||||
&mut ty_generics,
|
||||
rgen,
|
||||
is_return,
|
||||
cache,
|
||||
);
|
||||
insert_ty(res, arg.clone(), ty_generics);
|
||||
res.push(get_index_type(arg, ty_generics));
|
||||
} else if let Type::Array(ref ty, _) = *arg {
|
||||
let mut ty_generics = Vec::new();
|
||||
add_generics_and_bounds_as_types(
|
||||
simplify_fn_type(
|
||||
self_,
|
||||
generics,
|
||||
&ty,
|
||||
tcx,
|
||||
recurse + 1,
|
||||
&mut ty_generics,
|
||||
rgen,
|
||||
is_return,
|
||||
cache,
|
||||
);
|
||||
insert_ty(res, arg.clone(), ty_generics);
|
||||
res.push(get_index_type(arg, ty_generics));
|
||||
} else if let Type::Tuple(ref tys) = *arg {
|
||||
let mut ty_generics = Vec::new();
|
||||
for ty in tys {
|
||||
simplify_fn_type(
|
||||
self_,
|
||||
generics,
|
||||
&ty,
|
||||
tcx,
|
||||
recurse + 1,
|
||||
&mut ty_generics,
|
||||
rgen,
|
||||
is_return,
|
||||
cache,
|
||||
);
|
||||
}
|
||||
res.push(get_index_type(arg, ty_generics));
|
||||
} else {
|
||||
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
|
||||
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
|
||||
@ -639,18 +641,26 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
|
||||
let mut ty_generics = Vec::new();
|
||||
if let Some(arg_generics) = arg.generics() {
|
||||
for gen in arg_generics.iter() {
|
||||
add_generics_and_bounds_as_types(
|
||||
simplify_fn_type(
|
||||
self_,
|
||||
generics,
|
||||
gen,
|
||||
tcx,
|
||||
recurse + 1,
|
||||
&mut ty_generics,
|
||||
rgen,
|
||||
is_return,
|
||||
cache,
|
||||
);
|
||||
}
|
||||
}
|
||||
insert_ty(res, arg.clone(), ty_generics);
|
||||
let id = get_index_type_id(&arg);
|
||||
if id.is_some() || !ty_generics.is_empty() {
|
||||
res.push(RenderType {
|
||||
id,
|
||||
generics: if ty_generics.is_empty() { None } else { Some(ty_generics) },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,7 +673,7 @@ fn get_fn_inputs_and_outputs<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
impl_generics: Option<&(clean::Type, clean::Generics)>,
|
||||
cache: &Cache,
|
||||
) -> (Vec<RenderType>, Vec<RenderType>) {
|
||||
) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
|
||||
let decl = &func.decl;
|
||||
|
||||
let combined_generics;
|
||||
@ -689,21 +699,27 @@ fn get_fn_inputs_and_outputs<'tcx>(
|
||||
(None, &func.generics)
|
||||
};
|
||||
|
||||
let mut all_types = Vec::new();
|
||||
let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
|
||||
|
||||
let mut arg_types = Vec::new();
|
||||
for arg in decl.inputs.values.iter() {
|
||||
let mut args = Vec::new();
|
||||
add_generics_and_bounds_as_types(self_, generics, &arg.type_, tcx, 0, &mut args, cache);
|
||||
if !args.is_empty() {
|
||||
all_types.extend(args);
|
||||
} else {
|
||||
all_types.push(get_index_type(&arg.type_, vec![]));
|
||||
}
|
||||
simplify_fn_type(
|
||||
self_,
|
||||
generics,
|
||||
&arg.type_,
|
||||
tcx,
|
||||
0,
|
||||
&mut arg_types,
|
||||
&mut rgen,
|
||||
false,
|
||||
cache,
|
||||
);
|
||||
}
|
||||
|
||||
let mut ret_types = Vec::new();
|
||||
add_generics_and_bounds_as_types(self_, generics, &decl.output, tcx, 0, &mut ret_types, cache);
|
||||
if ret_types.is_empty() {
|
||||
ret_types.push(get_index_type(&decl.output, vec![]));
|
||||
}
|
||||
(all_types, ret_types)
|
||||
simplify_fn_type(self_, generics, &decl.output, tcx, 0, &mut ret_types, &mut rgen, true, cache);
|
||||
|
||||
let mut simplified_params = rgen.into_values().collect::<Vec<_>>();
|
||||
simplified_params.sort_by_key(|(idx, _)| -idx);
|
||||
(arg_types, ret_types, simplified_params.into_iter().map(|(_idx, traits)| traits).collect())
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ function initSearch(searchIndex){}
|
||||
/**
|
||||
* @typedef {{
|
||||
* name: string,
|
||||
* id: integer,
|
||||
* id: integer|null,
|
||||
* fullPath: Array<string>,
|
||||
* pathWithoutLast: Array<string>,
|
||||
* pathLast: string,
|
||||
@ -37,6 +37,7 @@ let ParserState;
|
||||
* args: Array<QueryElement>,
|
||||
* returned: Array<QueryElement>,
|
||||
* foundElems: number,
|
||||
* totalElems: number,
|
||||
* literalSearch: boolean,
|
||||
* corrections: Array<{from: string, to: integer}>,
|
||||
* }}
|
||||
@ -103,7 +104,7 @@ let ResultObject;
|
||||
*
|
||||
* fn something() -> Result<usize, usize>
|
||||
*
|
||||
* If output was allowed to be any RawFunctionType, it would look like this
|
||||
* If output was allowed to be any RawFunctionType, it would look like thi
|
||||
*
|
||||
* [[], [50, [3, 3]]]
|
||||
*
|
||||
@ -113,10 +114,56 @@ let ResultObject;
|
||||
* in favor of the pair of types interpretation. This is why the `(number|Array<RawFunctionType>)`
|
||||
* is used instead of `(RawFunctionType|Array<RawFunctionType>)`.
|
||||
*
|
||||
* The output can be skipped if it's actually unit and there's no type constraints. If thi
|
||||
* function accepts constrained generics, then the output will be unconditionally emitted, and
|
||||
* after it will come a list of trait constraints. The position of the item in the list will
|
||||
* determine which type parameter it is. For example:
|
||||
*
|
||||
* [1, 2, 3, 4, 5]
|
||||
* ^ ^ ^ ^ ^
|
||||
* | | | | - generic parameter (-3) of trait 5
|
||||
* | | | - generic parameter (-2) of trait 4
|
||||
* | | - generic parameter (-1) of trait 3
|
||||
* | - this function returns a single value (type 2)
|
||||
* - this function takes a single input parameter (type 1)
|
||||
*
|
||||
* Or, for a less contrived version:
|
||||
*
|
||||
* [[[4, -1], 3], [[5, -1]], 11]
|
||||
* -^^^^^^^---- ^^^^^^^ ^^
|
||||
* | | | - generic parameter, roughly `where -1: 11`
|
||||
* | | | since -1 is the type parameter and 11 the trait
|
||||
* | | - function output 5<-1>
|
||||
* | - the overall function signature is something like
|
||||
* | `fn(4<-1>, 3) -> 5<-1> where -1: 11`
|
||||
* - function input, corresponds roughly to 4<-1>
|
||||
* 4 is an index into the `p` array for a type
|
||||
* -1 is the generic parameter, given by 11
|
||||
*
|
||||
* If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like
|
||||
* function inputs and outputs:
|
||||
*
|
||||
* [-1, -1, [4, 3]]
|
||||
* ^^^^^^ where -1: 4 + 3
|
||||
*
|
||||
* If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array
|
||||
* even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in
|
||||
* favor of `4 + 3`:
|
||||
*
|
||||
* [-1, -1, [[4, 3]]]
|
||||
* ^^^^^^^^ where -1: 4 + 3
|
||||
*
|
||||
* [-1, -1, [5, [4, 3]]]
|
||||
* ^^^^^^^^^^^ where -1: 5, -2: 4 + 3
|
||||
*
|
||||
* If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i
|
||||
* implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0.
|
||||
*
|
||||
* @typedef {(
|
||||
* 0 |
|
||||
* [(number|Array<RawFunctionType>)] |
|
||||
* [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)]
|
||||
* [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] |
|
||||
* Array<(number|Array<RawFunctionType>)>
|
||||
* )}
|
||||
*/
|
||||
let RawFunctionSearchType;
|
||||
@ -136,6 +183,7 @@ let RawFunctionType;
|
||||
* @typedef {{
|
||||
* inputs: Array<FunctionType>,
|
||||
* output: Array<FunctionType>,
|
||||
* where_clause: Array<Array<FunctionType>>,
|
||||
* }}
|
||||
*/
|
||||
let FunctionSearchType;
|
||||
|
@ -3,6 +3,17 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// polyfill
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced
|
||||
if (!Array.prototype.toSpliced) {
|
||||
// Can't use arrow functions, because we want `this`
|
||||
Array.prototype.toSpliced = function() {
|
||||
const me = this.slice();
|
||||
Array.prototype.splice.apply(me, arguments);
|
||||
return me;
|
||||
};
|
||||
}
|
||||
|
||||
(function() {
|
||||
// This mapping table should match the discriminants of
|
||||
// `rustdoc::formats::item_type::ItemType` type in Rust.
|
||||
@ -33,6 +44,7 @@ const itemTypes = [
|
||||
"attr",
|
||||
"derive",
|
||||
"traitalias",
|
||||
"generic",
|
||||
];
|
||||
|
||||
const longItemTypes = [
|
||||
@ -67,6 +79,7 @@ const longItemTypes = [
|
||||
// used for special search precedence
|
||||
const TY_PRIMITIVE = itemTypes.indexOf("primitive");
|
||||
const TY_KEYWORD = itemTypes.indexOf("keyword");
|
||||
const TY_GENERIC = itemTypes.indexOf("generic");
|
||||
const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
|
||||
|
||||
function hasOwnPropertyRustdoc(obj, property) {
|
||||
@ -252,7 +265,7 @@ function initSearch(rawSearchIndex) {
|
||||
|
||||
/**
|
||||
* Add an item to the type Name->ID map, or, if one already exists, use it.
|
||||
* Returns the number. If name is "" or null, return -1 (pure generic).
|
||||
* Returns the number. If name is "" or null, return null (pure generic).
|
||||
*
|
||||
* This is effectively string interning, so that function matching can be
|
||||
* done more quickly. Two types with the same name but different item kinds
|
||||
@ -264,7 +277,7 @@ function initSearch(rawSearchIndex) {
|
||||
*/
|
||||
function buildTypeMapIndex(name) {
|
||||
if (name === "" || name === null) {
|
||||
return -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeNameIdMap.has(name)) {
|
||||
@ -489,7 +502,7 @@ function initSearch(rawSearchIndex) {
|
||||
}
|
||||
return {
|
||||
name: "never",
|
||||
id: -1,
|
||||
id: null,
|
||||
fullPath: ["never"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "never",
|
||||
@ -531,7 +544,7 @@ function initSearch(rawSearchIndex) {
|
||||
}
|
||||
return {
|
||||
name: name.trim(),
|
||||
id: -1,
|
||||
id: null,
|
||||
fullPath: pathSegments,
|
||||
pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
|
||||
pathLast: pathSegments[pathSegments.length - 1],
|
||||
@ -660,7 +673,7 @@ function initSearch(rawSearchIndex) {
|
||||
}
|
||||
elems.push({
|
||||
name: "[]",
|
||||
id: -1,
|
||||
id: null,
|
||||
fullPath: ["[]"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "[]",
|
||||
@ -971,9 +984,13 @@ function initSearch(rawSearchIndex) {
|
||||
returned: [],
|
||||
// Total number of "top" elements (does not include generics).
|
||||
foundElems: 0,
|
||||
// Total number of elements (includes generics).
|
||||
totalElems: 0,
|
||||
literalSearch: false,
|
||||
error: null,
|
||||
correction: null,
|
||||
proposeCorrectionFrom: null,
|
||||
proposeCorrectionTo: null,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1014,64 +1031,10 @@ function initSearch(rawSearchIndex) {
|
||||
/**
|
||||
* Parses the query.
|
||||
*
|
||||
* The supported syntax by this parser is as follow:
|
||||
* The supported syntax by this parser is given in the rustdoc book chapter
|
||||
* /src/doc/rustdoc/src/read-documentation/search.md
|
||||
*
|
||||
* ident = *(ALPHA / DIGIT / "_")
|
||||
* path = ident *(DOUBLE-COLON/{WS} ident) [!]
|
||||
* slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
|
||||
* arg = [type-filter *WS COLON *WS] (path [generics] / slice)
|
||||
* type-sep = *WS COMMA *(COMMA)
|
||||
* nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
|
||||
* generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
|
||||
* CLOSE-ANGLE-BRACKET
|
||||
* return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
|
||||
*
|
||||
* exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
|
||||
* type-search = [ nonempty-arg-list ] [ return-args ]
|
||||
*
|
||||
* query = *WS (exact-search / type-search) *WS
|
||||
*
|
||||
* type-filter = (
|
||||
* "mod" /
|
||||
* "externcrate" /
|
||||
* "import" /
|
||||
* "struct" /
|
||||
* "enum" /
|
||||
* "fn" /
|
||||
* "type" /
|
||||
* "static" /
|
||||
* "trait" /
|
||||
* "impl" /
|
||||
* "tymethod" /
|
||||
* "method" /
|
||||
* "structfield" /
|
||||
* "variant" /
|
||||
* "macro" /
|
||||
* "primitive" /
|
||||
* "associatedtype" /
|
||||
* "constant" /
|
||||
* "associatedconstant" /
|
||||
* "union" /
|
||||
* "foreigntype" /
|
||||
* "keyword" /
|
||||
* "existential" /
|
||||
* "attr" /
|
||||
* "derive" /
|
||||
* "traitalias")
|
||||
*
|
||||
* OPEN-ANGLE-BRACKET = "<"
|
||||
* CLOSE-ANGLE-BRACKET = ">"
|
||||
* OPEN-SQUARE-BRACKET = "["
|
||||
* CLOSE-SQUARE-BRACKET = "]"
|
||||
* COLON = ":"
|
||||
* DOUBLE-COLON = "::"
|
||||
* QUOTE = %x22
|
||||
* COMMA = ","
|
||||
* RETURN-ARROW = "->"
|
||||
*
|
||||
* ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
|
||||
* DIGIT = %x30-39
|
||||
* WS = %x09 / " "
|
||||
* When adding new things to the parser, add them there, too!
|
||||
*
|
||||
* @param {string} val - The user query
|
||||
*
|
||||
@ -1124,6 +1087,7 @@ function initSearch(rawSearchIndex) {
|
||||
query.literalSearch = parserState.totalElems > 1;
|
||||
}
|
||||
query.foundElems = query.elems.length + query.returned.length;
|
||||
query.totalElems = parserState.totalElems;
|
||||
return query;
|
||||
}
|
||||
|
||||
@ -1172,7 +1136,7 @@ function initSearch(rawSearchIndex) {
|
||||
const out = [];
|
||||
|
||||
for (const result of results) {
|
||||
if (result.id > -1) {
|
||||
if (result.id !== -1) {
|
||||
const obj = searchIndex[result.id];
|
||||
obj.dist = result.dist;
|
||||
const res = buildHrefAndPath(obj);
|
||||
@ -1348,192 +1312,311 @@ function initSearch(rawSearchIndex) {
|
||||
* This function checks generics in search query `queryElem` can all be found in the
|
||||
* search index (`fnType`),
|
||||
*
|
||||
* @param {FunctionType} fnType - The object to check.
|
||||
* @param {QueryElement} queryElem - The element from the parsed query.
|
||||
* This function returns `true` if it matches, and also writes the results to mgensInout.
|
||||
* It returns `false` if no match is found, and leaves mgensInout untouched.
|
||||
*
|
||||
* @param {FunctionType} fnType - The object to check.
|
||||
* @param {QueryElement} queryElem - The element from the parsed query.
|
||||
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
|
||||
* @param {Map<number,number>|null} mgensInout - Map functions generics to query generics.
|
||||
*
|
||||
* @return {boolean} - Returns true if a match, false otherwise.
|
||||
*/
|
||||
function checkGenerics(fnType, queryElem) {
|
||||
return unifyFunctionTypes(fnType.generics, queryElem.generics);
|
||||
function checkGenerics(fnType, queryElem, whereClause, mgensInout) {
|
||||
return unifyFunctionTypes(
|
||||
fnType.generics,
|
||||
queryElem.generics,
|
||||
whereClause,
|
||||
mgensInout,
|
||||
mgens => {
|
||||
if (mgensInout) {
|
||||
for (const [fid, qid] of mgens.entries()) {
|
||||
mgensInout.set(fid, qid);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
/**
|
||||
* This function checks if a list of search query `queryElems` can all be found in the
|
||||
* search index (`fnTypes`).
|
||||
*
|
||||
* @param {Array<FunctionType>} fnTypes - The objects to check.
|
||||
* This function returns `true` on a match, or `false` if none. If `solutionCb` is
|
||||
* supplied, it will call that function with mgens, and that callback can accept or
|
||||
* reject the result bu returning `true` or `false`. If the callback returns false,
|
||||
* then this function will try with a different solution, or bail with false if it
|
||||
* runs out of candidates.
|
||||
*
|
||||
* @param {Array<FunctionType>} fnTypes - The objects to check.
|
||||
* @param {Array<QueryElement>} queryElems - The elements from the parsed query.
|
||||
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
|
||||
* @param {Map<number,number>|null} mgensIn
|
||||
* - Map functions generics to query generics (never modified).
|
||||
* @param {null|Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
|
||||
*
|
||||
* @return {boolean} - Returns true if a match, false otherwise.
|
||||
*/
|
||||
function unifyFunctionTypes(fnTypes, queryElems) {
|
||||
// This search engine implements order-agnostic unification. There
|
||||
// should be no missing duplicates (generics have "bag semantics"),
|
||||
// and the row is allowed to have extras.
|
||||
function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) {
|
||||
/**
|
||||
* @type Map<integer, integer>
|
||||
*/
|
||||
let mgens = new Map(mgensIn);
|
||||
if (queryElems.length === 0) {
|
||||
return true;
|
||||
return !solutionCb || solutionCb(mgens);
|
||||
}
|
||||
if (!fnTypes || fnTypes.length === 0) {
|
||||
if (!fnTypesIn || fnTypesIn.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const ql = queryElems.length;
|
||||
let fl = fnTypesIn.length;
|
||||
/**
|
||||
* @type Map<integer, QueryElement[]>
|
||||
* @type Array<FunctionType>
|
||||
*/
|
||||
const queryElemSet = new Map();
|
||||
const addQueryElemToQueryElemSet = queryElem => {
|
||||
let currentQueryElemList;
|
||||
if (queryElemSet.has(queryElem.id)) {
|
||||
currentQueryElemList = queryElemSet.get(queryElem.id);
|
||||
} else {
|
||||
currentQueryElemList = [];
|
||||
queryElemSet.set(queryElem.id, currentQueryElemList);
|
||||
}
|
||||
currentQueryElemList.push(queryElem);
|
||||
};
|
||||
for (const queryElem of queryElems) {
|
||||
addQueryElemToQueryElemSet(queryElem);
|
||||
}
|
||||
let fnTypes = fnTypesIn.slice();
|
||||
/**
|
||||
* @type Map<integer, FunctionType[]>
|
||||
* loop works by building up a solution set in the working arrays
|
||||
* fnTypes gets mutated in place to make this work, while queryElems
|
||||
* is left alone
|
||||
*
|
||||
* vvvvvvv `i` points here
|
||||
* queryElems = [ good, good, good, unknown, unknown ],
|
||||
* fnTypes = [ good, good, good, unknown, unknown ],
|
||||
* ---------------- ^^^^^^^^^^^^^^^^ `j` iterates after `i`,
|
||||
* | looking for candidates
|
||||
* everything before `i` is the
|
||||
* current working solution
|
||||
*
|
||||
* Everything in the current working solution is known to be a good
|
||||
* match, but it might not be the match we wind up going with, because
|
||||
* there might be more than one candidate match, and we need to try them all
|
||||
* before giving up. So, to handle this, it backtracks on failure.
|
||||
*
|
||||
* @type Array<{
|
||||
* "fnTypesScratch": Array<FunctionType>,
|
||||
* "queryElemsOffset": integer,
|
||||
* "fnTypesOffset": integer
|
||||
* }>
|
||||
*/
|
||||
const fnTypeSet = new Map();
|
||||
const addFnTypeToFnTypeSet = fnType => {
|
||||
// Pure generic, or an item that's not matched by any query elems.
|
||||
// Try [unboxing] it.
|
||||
//
|
||||
// [unboxing]:
|
||||
// http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
|
||||
const queryContainsArrayOrSliceElem = queryElemSet.has(typeNameIdOfArrayOrSlice);
|
||||
if (fnType.id === -1 || !(
|
||||
queryElemSet.has(fnType.id) ||
|
||||
(fnType.id === typeNameIdOfSlice && queryContainsArrayOrSliceElem) ||
|
||||
(fnType.id === typeNameIdOfArray && queryContainsArrayOrSliceElem)
|
||||
)) {
|
||||
for (const innerFnType of fnType.generics) {
|
||||
addFnTypeToFnTypeSet(innerFnType);
|
||||
const backtracking = [];
|
||||
let i = 0;
|
||||
let j = 0;
|
||||
const backtrack = () => {
|
||||
while (backtracking.length !== 0) {
|
||||
// this session failed, but there are other possible solutions
|
||||
// to backtrack, reset to (a copy of) the old array, do the swap or unboxing
|
||||
const {
|
||||
fnTypesScratch,
|
||||
mgensScratch,
|
||||
queryElemsOffset,
|
||||
fnTypesOffset,
|
||||
unbox,
|
||||
} = backtracking.pop();
|
||||
mgens = new Map(mgensScratch);
|
||||
const fnType = fnTypesScratch[fnTypesOffset];
|
||||
const queryElem = queryElems[queryElemsOffset];
|
||||
if (unbox) {
|
||||
if (fnType.id < 0) {
|
||||
if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
|
||||
continue;
|
||||
}
|
||||
mgens.set(fnType.id, 0);
|
||||
}
|
||||
const generics = fnType.id < 0 ?
|
||||
whereClause[(-fnType.id) - 1] :
|
||||
fnType.generics;
|
||||
fnTypes = fnTypesScratch.toSpliced(fnTypesOffset, 1, ...generics);
|
||||
fl = fnTypes.length;
|
||||
// re-run the matching algorithm on this item
|
||||
i = queryElemsOffset - 1;
|
||||
} else {
|
||||
if (fnType.id < 0) {
|
||||
if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
|
||||
continue;
|
||||
}
|
||||
mgens.set(fnType.id, queryElem.id);
|
||||
}
|
||||
fnTypes = fnTypesScratch.slice();
|
||||
fl = fnTypes.length;
|
||||
const tmp = fnTypes[queryElemsOffset];
|
||||
fnTypes[queryElemsOffset] = fnTypes[fnTypesOffset];
|
||||
fnTypes[fnTypesOffset] = tmp;
|
||||
// this is known as a good match; go to the next one
|
||||
i = queryElemsOffset;
|
||||
}
|
||||
return;
|
||||
}
|
||||
let currentQueryElemList = queryElemSet.get(fnType.id) || [];
|
||||
let matchIdx = currentQueryElemList.findIndex(queryElem => {
|
||||
return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
|
||||
checkGenerics(fnType, queryElem);
|
||||
});
|
||||
if (matchIdx === -1 &&
|
||||
(fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) &&
|
||||
queryContainsArrayOrSliceElem
|
||||
) {
|
||||
currentQueryElemList = queryElemSet.get(typeNameIdOfArrayOrSlice) || [];
|
||||
matchIdx = currentQueryElemList.findIndex(queryElem => {
|
||||
return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
|
||||
checkGenerics(fnType, queryElem);
|
||||
});
|
||||
}
|
||||
// None of the query elems match the function type.
|
||||
// Try [unboxing] it.
|
||||
if (matchIdx === -1) {
|
||||
for (const innerFnType of fnType.generics) {
|
||||
addFnTypeToFnTypeSet(innerFnType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let currentFnTypeList;
|
||||
if (fnTypeSet.has(fnType.id)) {
|
||||
currentFnTypeList = fnTypeSet.get(fnType.id);
|
||||
} else {
|
||||
currentFnTypeList = [];
|
||||
fnTypeSet.set(fnType.id, currentFnTypeList);
|
||||
}
|
||||
currentFnTypeList.push(fnType);
|
||||
};
|
||||
for (const fnType of fnTypes) {
|
||||
addFnTypeToFnTypeSet(fnType);
|
||||
}
|
||||
const doHandleQueryElemList = (currentFnTypeList, queryElemList) => {
|
||||
if (queryElemList.length === 0) {
|
||||
return true;
|
||||
}
|
||||
// Multiple items in one list might match multiple items in another.
|
||||
// Since an item with fewer generics can match an item with more, we
|
||||
// need to check all combinations for a potential match.
|
||||
const queryElem = queryElemList.pop();
|
||||
const l = currentFnTypeList.length;
|
||||
for (let i = 0; i < l; i += 1) {
|
||||
const fnType = currentFnTypeList[i];
|
||||
if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
|
||||
continue;
|
||||
}
|
||||
const queryElemPathLength = queryElem.pathWithoutLast.length;
|
||||
// If the query element is a path (it contains `::`), we need to check if this
|
||||
// path is compatible with the target type.
|
||||
if (queryElemPathLength > 0) {
|
||||
const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
|
||||
fnType.path.split("::") : [];
|
||||
// If the path provided in the query element is longer than this type,
|
||||
// no need to check it since it won't match in any case.
|
||||
if (queryElemPathLength > fnTypePath.length) {
|
||||
continue;
|
||||
}
|
||||
let i = 0;
|
||||
for (const path of fnTypePath) {
|
||||
if (path === queryElem.pathWithoutLast[i]) {
|
||||
i += 1;
|
||||
if (i >= queryElemPathLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i < queryElemPathLength) {
|
||||
// If we didn't find all parts of the path of the query element inside
|
||||
// the fn type, then it's not the right one.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
|
||||
currentFnTypeList.splice(i, 1);
|
||||
const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
currentFnTypeList.splice(i, 0, fnType);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const handleQueryElemList = (id, queryElemList) => {
|
||||
if (!fnTypeSet.has(id)) {
|
||||
if (id === typeNameIdOfArrayOrSlice) {
|
||||
return handleQueryElemList(typeNameIdOfSlice, queryElemList) ||
|
||||
handleQueryElemList(typeNameIdOfArray, queryElemList);
|
||||
for (i = 0; i !== ql; ++i) {
|
||||
const queryElem = queryElems[i];
|
||||
/**
|
||||
* list of potential function types that go with the current query element.
|
||||
* @type Array<integer>
|
||||
*/
|
||||
const matchCandidates = [];
|
||||
let fnTypesScratch = null;
|
||||
let mgensScratch = null;
|
||||
// don't try anything before `i`, because they've already been
|
||||
// paired off with the other query elements
|
||||
for (j = i; j !== fl; ++j) {
|
||||
const fnType = fnTypes[j];
|
||||
if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) {
|
||||
if (!fnTypesScratch) {
|
||||
fnTypesScratch = fnTypes.slice();
|
||||
}
|
||||
unifyFunctionTypes(
|
||||
fnType.generics,
|
||||
queryElem.generics,
|
||||
whereClause,
|
||||
mgens,
|
||||
mgensScratch => {
|
||||
matchCandidates.push({
|
||||
fnTypesScratch,
|
||||
mgensScratch,
|
||||
queryElemsOffset: i,
|
||||
fnTypesOffset: j,
|
||||
unbox: false,
|
||||
});
|
||||
return false; // "reject" all candidates to gather all of them
|
||||
}
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const currentFnTypeList = fnTypeSet.get(id);
|
||||
if (currentFnTypeList.length < queryElemList.length) {
|
||||
// It's not possible for all the query elems to find a match.
|
||||
return false;
|
||||
}
|
||||
const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
|
||||
if (result) {
|
||||
// Found a solution.
|
||||
// Any items that weren't used for it can be unboxed, and might form
|
||||
// part of the solution for another item.
|
||||
for (const innerFnType of currentFnTypeList) {
|
||||
addFnTypeToFnTypeSet(innerFnType);
|
||||
if (unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) {
|
||||
if (!fnTypesScratch) {
|
||||
fnTypesScratch = fnTypes.slice();
|
||||
}
|
||||
if (!mgensScratch) {
|
||||
mgensScratch = new Map(mgens);
|
||||
}
|
||||
backtracking.push({
|
||||
fnTypesScratch,
|
||||
mgensScratch,
|
||||
queryElemsOffset: i,
|
||||
fnTypesOffset: j,
|
||||
unbox: true,
|
||||
});
|
||||
}
|
||||
fnTypeSet.delete(id);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
let queryElemSetSize = -1;
|
||||
while (queryElemSetSize !== queryElemSet.size) {
|
||||
queryElemSetSize = queryElemSet.size;
|
||||
for (const [id, queryElemList] of queryElemSet) {
|
||||
if (handleQueryElemList(id, queryElemList)) {
|
||||
queryElemSet.delete(id);
|
||||
if (matchCandidates.length === 0) {
|
||||
if (backtrack()) {
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// use the current candidate
|
||||
const {fnTypesOffset: candidate, mgensScratch: mgensNew} = matchCandidates.pop();
|
||||
if (fnTypes[candidate].id < 0 && queryElems[i].id < 0) {
|
||||
mgens.set(fnTypes[candidate].id, queryElems[i].id);
|
||||
}
|
||||
for (const [fid, qid] of mgensNew) {
|
||||
mgens.set(fid, qid);
|
||||
}
|
||||
// `i` and `j` are paired off
|
||||
// `queryElems[i]` is left in place
|
||||
// `fnTypes[j]` is swapped with `fnTypes[i]` to pair them off
|
||||
const tmp = fnTypes[candidate];
|
||||
fnTypes[candidate] = fnTypes[i];
|
||||
fnTypes[i] = tmp;
|
||||
// write other candidates to backtracking queue
|
||||
for (const otherCandidate of matchCandidates) {
|
||||
backtracking.push(otherCandidate);
|
||||
}
|
||||
// If we're on the last item, check the solution with the callback
|
||||
// backtrack if the callback says its unsuitable
|
||||
while (i === (ql - 1) && solutionCb && !solutionCb(mgens)) {
|
||||
if (!backtrack()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return queryElemSetSize === 0;
|
||||
return true;
|
||||
}
|
||||
function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens) {
|
||||
// type filters look like `trait:Read` or `enum:Result`
|
||||
if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
|
||||
return false;
|
||||
}
|
||||
// fnType.id < 0 means generic
|
||||
// queryElem.id < 0 does too
|
||||
// mgens[fnType.id] = queryElem.id
|
||||
// or, if mgens[fnType.id] = 0, then we've matched this generic with a bare trait
|
||||
// and should make that same decision everywhere it appears
|
||||
if (fnType.id < 0 && queryElem.id < 0) {
|
||||
if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
|
||||
return false;
|
||||
}
|
||||
for (const [fid, qid] of mgens.entries()) {
|
||||
if (fnType.id !== fid && queryElem.id === qid) {
|
||||
return false;
|
||||
}
|
||||
if (fnType.id === fid && queryElem.id !== qid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (fnType.id !== null) {
|
||||
if (queryElem.id === typeNameIdOfArrayOrSlice &&
|
||||
(fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
|
||||
) {
|
||||
// [] matches primitive:array or primitive:slice
|
||||
// if it matches, then we're fine, and this is an appropriate match candidate
|
||||
} else if (fnType.id !== queryElem.id) {
|
||||
return false;
|
||||
}
|
||||
// If the query elem has generics, and the function doesn't,
|
||||
// it can't match.
|
||||
if (fnType.generics.length === 0 && queryElem.generics.length !== 0) {
|
||||
return false;
|
||||
}
|
||||
// If the query element is a path (it contains `::`), we need to check if this
|
||||
// path is compatible with the target type.
|
||||
const queryElemPathLength = queryElem.pathWithoutLast.length;
|
||||
if (queryElemPathLength > 0) {
|
||||
const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
|
||||
fnType.path.split("::") : [];
|
||||
// If the path provided in the query element is longer than this type,
|
||||
// no need to check it since it won't match in any case.
|
||||
if (queryElemPathLength > fnTypePath.length) {
|
||||
return false;
|
||||
}
|
||||
let i = 0;
|
||||
for (const path of fnTypePath) {
|
||||
if (path === queryElem.pathWithoutLast[i]) {
|
||||
i += 1;
|
||||
if (i >= queryElemPathLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i < queryElemPathLength) {
|
||||
// If we didn't find all parts of the path of the query element inside
|
||||
// the fn type, then it's not the right one.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) {
|
||||
if (fnType.id < 0 && queryElem.id >= 0) {
|
||||
if (!whereClause) {
|
||||
return false;
|
||||
}
|
||||
// mgens[fnType.id] === 0 indicates that we committed to unboxing this generic
|
||||
// mgens[fnType.id] === null indicates that we haven't decided yet
|
||||
if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
|
||||
return false;
|
||||
}
|
||||
// This is only a potential unbox if the search query appears in the where clause
|
||||
// for example, searching `Read -> usize` should find
|
||||
// `fn read_all<R: Read>(R) -> Result<usize>`
|
||||
// generic `R` is considered "unboxed"
|
||||
return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause);
|
||||
} else if (fnType.generics && fnType.generics.length > 0) {
|
||||
return checkIfInList(fnType.generics, queryElem, whereClause);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1541,13 +1624,14 @@ function initSearch(rawSearchIndex) {
|
||||
* generics (if any).
|
||||
*
|
||||
* @param {Array<FunctionType>} list
|
||||
* @param {QueryElement} elem - The element from the parsed query.
|
||||
* @param {QueryElement} elem - The element from the parsed query.
|
||||
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
|
||||
*
|
||||
* @return {boolean} - Returns true if found, false otherwise.
|
||||
*/
|
||||
function checkIfInList(list, elem) {
|
||||
function checkIfInList(list, elem, whereClause) {
|
||||
for (const entry of list) {
|
||||
if (checkType(entry, elem)) {
|
||||
if (checkType(entry, elem, whereClause)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1559,14 +1643,26 @@ function initSearch(rawSearchIndex) {
|
||||
* generics (if any).
|
||||
*
|
||||
* @param {Row} row
|
||||
* @param {QueryElement} elem - The element from the parsed query.
|
||||
* @param {QueryElement} elem - The element from the parsed query.
|
||||
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
|
||||
*
|
||||
* @return {boolean} - Returns true if the type matches, false otherwise.
|
||||
*/
|
||||
function checkType(row, elem) {
|
||||
if (row.id === -1) {
|
||||
function checkType(row, elem, whereClause) {
|
||||
if (row.id === null) {
|
||||
// This is a pure "generic" search, no need to run other checks.
|
||||
return row.generics.length > 0 ? checkIfInList(row.generics, elem) : false;
|
||||
return row.generics.length > 0
|
||||
? checkIfInList(row.generics, elem, whereClause)
|
||||
: false;
|
||||
}
|
||||
|
||||
if (row.id < 0 && elem.id >= 0) {
|
||||
const gid = (-row.id) - 1;
|
||||
return checkIfInList(whereClause[gid], elem, whereClause);
|
||||
}
|
||||
|
||||
if (row.id < 0 && elem.id < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const matchesExact = row.id === elem.id;
|
||||
@ -1576,7 +1672,7 @@ function initSearch(rawSearchIndex) {
|
||||
if ((matchesExact || matchesArrayOrSlice) &&
|
||||
typePassesFilter(elem.typeFilter, row.ty)) {
|
||||
if (elem.generics.length > 0) {
|
||||
return checkGenerics(row, elem);
|
||||
return checkGenerics(row, elem, whereClause, new Map());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1584,7 +1680,7 @@ function initSearch(rawSearchIndex) {
|
||||
// If the current item does not match, try [unboxing] the generic.
|
||||
// [unboxing]:
|
||||
// https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
|
||||
return checkIfInList(row.generics, elem);
|
||||
return checkIfInList(row.generics, elem, whereClause);
|
||||
}
|
||||
|
||||
function checkPath(contains, ty, maxEditDistance) {
|
||||
@ -1785,13 +1881,15 @@ function initSearch(rawSearchIndex) {
|
||||
const fullId = row.id;
|
||||
const searchWord = searchWords[pos];
|
||||
|
||||
const in_args = row.type && row.type.inputs && checkIfInList(row.type.inputs, elem);
|
||||
const in_args = row.type && row.type.inputs
|
||||
&& checkIfInList(row.type.inputs, elem, row.type.where_clause);
|
||||
if (in_args) {
|
||||
// path_dist is 0 because no parent path information is currently stored
|
||||
// in the search index
|
||||
addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance);
|
||||
}
|
||||
const returned = row.type && row.type.output && checkIfInList(row.type.output, elem);
|
||||
const returned = row.type && row.type.output
|
||||
&& checkIfInList(row.type.output, elem, row.type.where_clause);
|
||||
if (returned) {
|
||||
addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance);
|
||||
}
|
||||
@ -1853,10 +1951,20 @@ function initSearch(rawSearchIndex) {
|
||||
}
|
||||
|
||||
// If the result is too "bad", we return false and it ends this search.
|
||||
if (!unifyFunctionTypes(row.type.inputs, parsedQuery.elems)) {
|
||||
return;
|
||||
}
|
||||
if (!unifyFunctionTypes(row.type.output, parsedQuery.returned)) {
|
||||
if (!unifyFunctionTypes(
|
||||
row.type.inputs,
|
||||
parsedQuery.elems,
|
||||
row.type.where_clause,
|
||||
null,
|
||||
mgens => {
|
||||
return unifyFunctionTypes(
|
||||
row.type.output,
|
||||
parsedQuery.returned,
|
||||
row.type.where_clause,
|
||||
mgens
|
||||
);
|
||||
}
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1875,6 +1983,11 @@ function initSearch(rawSearchIndex) {
|
||||
}
|
||||
const maxEditDistance = Math.floor(queryLen / 3);
|
||||
|
||||
/**
|
||||
* @type {Map<string, integer>}
|
||||
*/
|
||||
const genericSymbols = new Map();
|
||||
|
||||
/**
|
||||
* Convert names to ids in parsed query elements.
|
||||
* This is not used for the "In Names" tab, but is used for the
|
||||
@ -1891,7 +2004,7 @@ function initSearch(rawSearchIndex) {
|
||||
if (typeNameIdMap.has(elem.pathLast)) {
|
||||
elem.id = typeNameIdMap.get(elem.pathLast);
|
||||
} else if (!parsedQuery.literalSearch) {
|
||||
let match = -1;
|
||||
let match = null;
|
||||
let matchDist = maxEditDistance + 1;
|
||||
let matchName = "";
|
||||
for (const [name, id] of typeNameIdMap) {
|
||||
@ -1905,11 +2018,52 @@ function initSearch(rawSearchIndex) {
|
||||
matchName = name;
|
||||
}
|
||||
}
|
||||
if (match !== -1) {
|
||||
if (match !== null) {
|
||||
parsedQuery.correction = matchName;
|
||||
}
|
||||
elem.id = match;
|
||||
}
|
||||
if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1
|
||||
&& elem.generics.length === 0)
|
||||
|| elem.typeFilter === TY_GENERIC) {
|
||||
if (genericSymbols.has(elem.name)) {
|
||||
elem.id = genericSymbols.get(elem.name);
|
||||
} else {
|
||||
elem.id = -(genericSymbols.size + 1);
|
||||
genericSymbols.set(elem.name, elem.id);
|
||||
}
|
||||
if (elem.typeFilter === -1 && elem.name.length >= 3) {
|
||||
// Silly heuristic to catch if the user probably meant
|
||||
// to not write a generic parameter. We don't use it,
|
||||
// just bring it up.
|
||||
const maxPartDistance = Math.floor(elem.name.length / 3);
|
||||
let matchDist = maxPartDistance + 1;
|
||||
let matchName = "";
|
||||
for (const name of typeNameIdMap.keys()) {
|
||||
const dist = editDistance(name, elem.name, maxPartDistance);
|
||||
if (dist <= matchDist && dist <= maxPartDistance) {
|
||||
if (dist === matchDist && matchName > name) {
|
||||
continue;
|
||||
}
|
||||
matchDist = dist;
|
||||
matchName = name;
|
||||
}
|
||||
}
|
||||
if (matchName !== "") {
|
||||
parsedQuery.proposeCorrectionFrom = elem.name;
|
||||
parsedQuery.proposeCorrectionTo = matchName;
|
||||
}
|
||||
}
|
||||
elem.typeFilter = TY_GENERIC;
|
||||
}
|
||||
if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) {
|
||||
// Rust does not have HKT
|
||||
parsedQuery.error = [
|
||||
"Generic type parameter ",
|
||||
elem.name,
|
||||
" does not accept generic parameters",
|
||||
];
|
||||
}
|
||||
for (const elem2 of elem.generics) {
|
||||
convertNameToId(elem2);
|
||||
}
|
||||
@ -1943,8 +2097,11 @@ function initSearch(rawSearchIndex) {
|
||||
elem = parsedQuery.returned[0];
|
||||
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
|
||||
row = searchIndex[i];
|
||||
in_returned = row.type &&
|
||||
unifyFunctionTypes(row.type.output, parsedQuery.returned);
|
||||
in_returned = row.type && unifyFunctionTypes(
|
||||
row.type.output,
|
||||
parsedQuery.returned,
|
||||
row.type.where_clause
|
||||
);
|
||||
if (in_returned) {
|
||||
addIntoResults(
|
||||
results_others,
|
||||
@ -2295,6 +2452,13 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||
"Showing results for closest type name " +
|
||||
`"${results.query.correction}" instead.</h3>`;
|
||||
}
|
||||
if (results.query.proposeCorrectionFrom !== null) {
|
||||
const orig = results.query.proposeCorrectionFrom;
|
||||
const targ = results.query.proposeCorrectionTo;
|
||||
output += "<h3 class=\"search-corrections\">" +
|
||||
`Type "${orig}" not found and used as generic parameter. ` +
|
||||
`Consider searching for "${targ}" instead.</h3>`;
|
||||
}
|
||||
|
||||
const resultsElem = document.createElement("div");
|
||||
resultsElem.id = "results";
|
||||
@ -2396,37 +2560,54 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||
* @return {Array<FunctionSearchType>}
|
||||
*/
|
||||
function buildItemSearchTypeAll(types, lowercasePaths) {
|
||||
return types.map(type => buildItemSearchType(type, lowercasePaths));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a single type.
|
||||
*
|
||||
* @param {RawFunctionType} type
|
||||
*/
|
||||
function buildItemSearchType(type, lowercasePaths) {
|
||||
const PATH_INDEX_DATA = 0;
|
||||
const GENERICS_DATA = 1;
|
||||
return types.map(type => {
|
||||
let pathIndex, generics;
|
||||
if (typeof type === "number") {
|
||||
pathIndex = type;
|
||||
generics = [];
|
||||
} else {
|
||||
pathIndex = type[PATH_INDEX_DATA];
|
||||
generics = buildItemSearchTypeAll(
|
||||
type[GENERICS_DATA],
|
||||
lowercasePaths
|
||||
);
|
||||
}
|
||||
// `0` is used as a sentinel because it's fewer bytes than `null`
|
||||
if (pathIndex === 0) {
|
||||
return {
|
||||
id: -1,
|
||||
ty: null,
|
||||
path: null,
|
||||
generics: generics,
|
||||
};
|
||||
}
|
||||
const item = lowercasePaths[pathIndex - 1];
|
||||
let pathIndex, generics;
|
||||
if (typeof type === "number") {
|
||||
pathIndex = type;
|
||||
generics = [];
|
||||
} else {
|
||||
pathIndex = type[PATH_INDEX_DATA];
|
||||
generics = buildItemSearchTypeAll(
|
||||
type[GENERICS_DATA],
|
||||
lowercasePaths
|
||||
);
|
||||
}
|
||||
if (pathIndex < 0) {
|
||||
// types less than 0 are generic parameters
|
||||
// the actual names of generic parameters aren't stored, since they aren't API
|
||||
return {
|
||||
id: buildTypeMapIndex(item.name),
|
||||
ty: item.ty,
|
||||
path: item.path,
|
||||
generics: generics,
|
||||
id: pathIndex,
|
||||
ty: TY_GENERIC,
|
||||
path: null,
|
||||
generics,
|
||||
};
|
||||
});
|
||||
}
|
||||
if (pathIndex === 0) {
|
||||
// `0` is used as a sentinel because it's fewer bytes than `null`
|
||||
return {
|
||||
id: null,
|
||||
ty: null,
|
||||
path: null,
|
||||
generics,
|
||||
};
|
||||
}
|
||||
const item = lowercasePaths[pathIndex - 1];
|
||||
return {
|
||||
id: buildTypeMapIndex(item.name),
|
||||
ty: item.ty,
|
||||
path: item.path,
|
||||
generics,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2454,23 +2635,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||
}
|
||||
let inputs, output;
|
||||
if (typeof functionSearchType[INPUTS_DATA] === "number") {
|
||||
const pathIndex = functionSearchType[INPUTS_DATA];
|
||||
if (pathIndex === 0) {
|
||||
inputs = [{
|
||||
id: -1,
|
||||
ty: null,
|
||||
path: null,
|
||||
generics: [],
|
||||
}];
|
||||
} else {
|
||||
const item = lowercasePaths[pathIndex - 1];
|
||||
inputs = [{
|
||||
id: buildTypeMapIndex(item.name),
|
||||
ty: item.ty,
|
||||
path: item.path,
|
||||
generics: [],
|
||||
}];
|
||||
}
|
||||
inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)];
|
||||
} else {
|
||||
inputs = buildItemSearchTypeAll(
|
||||
functionSearchType[INPUTS_DATA],
|
||||
@ -2479,23 +2644,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||
}
|
||||
if (functionSearchType.length > 1) {
|
||||
if (typeof functionSearchType[OUTPUT_DATA] === "number") {
|
||||
const pathIndex = functionSearchType[OUTPUT_DATA];
|
||||
if (pathIndex === 0) {
|
||||
output = [{
|
||||
id: -1,
|
||||
ty: null,
|
||||
path: null,
|
||||
generics: [],
|
||||
}];
|
||||
} else {
|
||||
const item = lowercasePaths[pathIndex - 1];
|
||||
output = [{
|
||||
id: buildTypeMapIndex(item.name),
|
||||
ty: item.ty,
|
||||
path: item.path,
|
||||
generics: [],
|
||||
}];
|
||||
}
|
||||
output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)];
|
||||
} else {
|
||||
output = buildItemSearchTypeAll(
|
||||
functionSearchType[OUTPUT_DATA],
|
||||
@ -2505,8 +2654,15 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||
} else {
|
||||
output = [];
|
||||
}
|
||||
const where_clause = [];
|
||||
const l = functionSearchType.length;
|
||||
for (let i = 2; i < l; ++i) {
|
||||
where_clause.push(typeof functionSearchType[i] === "number"
|
||||
? [buildItemSearchType(functionSearchType[i], lowercasePaths)]
|
||||
: buildItemSearchTypeAll(functionSearchType[i], lowercasePaths));
|
||||
}
|
||||
return {
|
||||
inputs, output,
|
||||
inputs, output, where_clause,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,9 @@ function contentToDiffLine(key, value) {
|
||||
}
|
||||
|
||||
function shouldIgnoreField(fieldName) {
|
||||
return fieldName === "query" || fieldName === "correction";
|
||||
return fieldName === "query" || fieldName === "correction" ||
|
||||
fieldName === "proposeCorrectionFrom" ||
|
||||
fieldName === "proposeCorrectionTo";
|
||||
}
|
||||
|
||||
// This function is only called when no matching result was found and therefore will only display
|
||||
|
20
tests/mir-opt/dont_inline_type_id.call.Inline.diff
Normal file
20
tests/mir-opt/dont_inline_type_id.call.Inline.diff
Normal file
@ -0,0 +1,20 @@
|
||||
- // MIR for `call` before Inline
|
||||
+ // MIR for `call` after Inline
|
||||
|
||||
fn call(_1: &T) -> TypeId {
|
||||
debug s => _1;
|
||||
let mut _0: std::any::TypeId;
|
||||
let mut _2: &T;
|
||||
|
||||
bb0: {
|
||||
StorageLive(_2);
|
||||
_2 = &(*_1);
|
||||
_0 = <T as Any>::type_id(move _2) -> [return: bb1, unwind unreachable];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
15
tests/mir-opt/dont_inline_type_id.rs
Normal file
15
tests/mir-opt/dont_inline_type_id.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// unit-test: Inline
|
||||
// compile-flags: --crate-type=lib -C panic=abort
|
||||
|
||||
use std::any::Any;
|
||||
use std::any::TypeId;
|
||||
|
||||
struct A<T: ?Sized + 'static> {
|
||||
a: i32,
|
||||
b: T,
|
||||
}
|
||||
|
||||
// EMIT_MIR dont_inline_type_id.call.Inline.diff
|
||||
pub fn call<T: ?Sized + 'static>(s: &T) -> TypeId {
|
||||
s.type_id()
|
||||
}
|
24
tests/mir-opt/inline_generically_if_sized.call.Inline.diff
Normal file
24
tests/mir-opt/inline_generically_if_sized.call.Inline.diff
Normal file
@ -0,0 +1,24 @@
|
||||
- // MIR for `call` before Inline
|
||||
+ // MIR for `call` after Inline
|
||||
|
||||
fn call(_1: &T) -> i32 {
|
||||
debug s => _1;
|
||||
let mut _0: i32;
|
||||
let mut _2: &T;
|
||||
+ scope 1 (inlined <T as Foo>::bar) {
|
||||
+ debug self => _2;
|
||||
+ }
|
||||
|
||||
bb0: {
|
||||
StorageLive(_2);
|
||||
_2 = &(*_1);
|
||||
- _0 = <T as Foo>::bar(move _2) -> [return: bb1, unwind unreachable];
|
||||
- }
|
||||
-
|
||||
- bb1: {
|
||||
+ _0 = const 0_i32;
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
17
tests/mir-opt/inline_generically_if_sized.rs
Normal file
17
tests/mir-opt/inline_generically_if_sized.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// unit-test: Inline
|
||||
// compile-flags: --crate-type=lib -C panic=abort
|
||||
|
||||
trait Foo {
|
||||
fn bar(&self) -> i32;
|
||||
}
|
||||
|
||||
impl<T> Foo for T {
|
||||
fn bar(&self) -> i32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
// EMIT_MIR inline_generically_if_sized.call.Inline.diff
|
||||
pub fn call<T>(s: &T) -> i32 {
|
||||
s.bar()
|
||||
}
|
@ -54,3 +54,53 @@ assert-text: (
|
||||
".search-corrections",
|
||||
"Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
|
||||
)
|
||||
|
||||
// Now, generic correction
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// Intentionally wrong spelling of "NotableStructWithLongName"
|
||||
write: (".search-input", "NotableStructWithLongNamr, NotableStructWithLongNamr")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
|
||||
assert-css: (".search-corrections", {
|
||||
"display": "block"
|
||||
})
|
||||
assert-text: (
|
||||
".search-corrections",
|
||||
"Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
|
||||
)
|
||||
|
||||
// Now, generic correction plus error
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// Intentionally wrong spelling of "NotableStructWithLongName"
|
||||
write: (".search-input", "Foo<NotableStructWithLongNamr>,y")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
|
||||
assert-css: (".search-corrections", {
|
||||
"display": "block"
|
||||
})
|
||||
assert-text: (
|
||||
".search-corrections",
|
||||
"Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
|
||||
)
|
||||
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// Intentionally wrong spelling of "NotableStructWithLongName"
|
||||
write: (".search-input", "generic:NotableStructWithLongNamr<x>,y")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
|
||||
assert-css: (".error", {
|
||||
"display": "block"
|
||||
})
|
||||
assert-text: (
|
||||
".error",
|
||||
"Query parser error: \"Generic type parameter notablestructwithlongnamr does not accept generic parameters\"."
|
||||
)
|
||||
|
@ -1,3 +1,7 @@
|
||||
// ignore-order
|
||||
|
||||
const FILTER_CRATE = "std";
|
||||
|
||||
const EXPECTED = [
|
||||
{
|
||||
'query': 'option, fnonce -> option',
|
||||
@ -19,4 +23,62 @@ const EXPECTED = [
|
||||
{ 'path': 'std::option::Option', 'name': 'as_mut_slice' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'option<t>, option<t> -> option<t>',
|
||||
'others': [
|
||||
{ 'path': 'std::option::Option', 'name': 'or' },
|
||||
{ 'path': 'std::option::Option', 'name': 'xor' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'option<t>, option<u> -> option<u>',
|
||||
'others': [
|
||||
{ 'path': 'std::option::Option', 'name': 'and' },
|
||||
{ 'path': 'std::option::Option', 'name': 'zip' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'option<t>, option<u> -> option<t>',
|
||||
'others': [
|
||||
{ 'path': 'std::option::Option', 'name': 'and' },
|
||||
{ 'path': 'std::option::Option', 'name': 'zip' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'option<t>, option<u> -> option<t, u>',
|
||||
'others': [
|
||||
{ 'path': 'std::option::Option', 'name': 'zip' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'option<t>, e -> result<t, e>',
|
||||
'others': [
|
||||
{ 'path': 'std::option::Option', 'name': 'ok_or' },
|
||||
{ 'path': 'std::result::Result', 'name': 'transpose' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'result<option<t>, e> -> option<result<t, e>>',
|
||||
'others': [
|
||||
{ 'path': 'std::result::Result', 'name': 'transpose' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'option<t>, option<t> -> bool',
|
||||
'others': [
|
||||
{ 'path': 'std::option::Option', 'name': 'eq' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'option<option<t>> -> option<t>',
|
||||
'others': [
|
||||
{ 'path': 'std::option::Option', 'name': 'flatten' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'option<t>',
|
||||
'returned': [
|
||||
{ 'path': 'std::result::Result', 'name': 'ok' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
22
tests/rustdoc-js-std/vec-type-signatures.js
Normal file
22
tests/rustdoc-js-std/vec-type-signatures.js
Normal file
@ -0,0 +1,22 @@
|
||||
// ignore-order
|
||||
|
||||
const FILTER_CRATE = "std";
|
||||
|
||||
const EXPECTED = [
|
||||
{
|
||||
'query': 'vec::intoiter<T> -> [T]',
|
||||
'others': [
|
||||
{ 'path': 'std::vec::IntoIter', 'name': 'as_slice' },
|
||||
{ 'path': 'std::vec::IntoIter', 'name': 'as_mut_slice' },
|
||||
{ 'path': 'std::vec::IntoIter', 'name': 'next_chunk' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'vec::intoiter<T> -> []',
|
||||
'others': [
|
||||
{ 'path': 'std::vec::IntoIter', 'name': 'as_slice' },
|
||||
{ 'path': 'std::vec::IntoIter', 'name': 'as_mut_slice' },
|
||||
{ 'path': 'std::vec::IntoIter', 'name': 'next_chunk' },
|
||||
],
|
||||
},
|
||||
];
|
@ -79,6 +79,7 @@ const EXPECTED = [
|
||||
{ 'path': 'generics_match_ambiguity', 'name': 'baac' },
|
||||
{ 'path': 'generics_match_ambiguity', 'name': 'baaf' },
|
||||
{ 'path': 'generics_match_ambiguity', 'name': 'baag' },
|
||||
{ 'path': 'generics_match_ambiguity', 'name': 'baah' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -12,11 +12,15 @@ const EXPECTED = [
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'Result<SomeTraiz>',
|
||||
'correction': null,
|
||||
'query': 'Resulx<SomeTrait>',
|
||||
'in_args': [],
|
||||
'returned': [],
|
||||
},
|
||||
{
|
||||
'query': 'Result<SomeTraiz>',
|
||||
'proposeCorrectionFrom': 'SomeTraiz',
|
||||
'proposeCorrectionTo': 'SomeTrait',
|
||||
},
|
||||
{
|
||||
'query': 'OtherThingxxxxxxxx',
|
||||
'correction': null,
|
||||
|
38
tests/rustdoc-js/generics-unbox.js
Normal file
38
tests/rustdoc-js/generics-unbox.js
Normal file
@ -0,0 +1,38 @@
|
||||
// exact-check
|
||||
|
||||
const EXPECTED = [
|
||||
{
|
||||
'query': 'Inside<T> -> Out1<T>',
|
||||
'others': [
|
||||
{ 'path': 'generics_unbox', 'name': 'alpha' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'Inside<T> -> Out3<T>',
|
||||
'others': [
|
||||
{ 'path': 'generics_unbox', 'name': 'beta' },
|
||||
{ 'path': 'generics_unbox', 'name': 'gamma' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'Inside<T> -> Out4<T>',
|
||||
'others': [
|
||||
{ 'path': 'generics_unbox', 'name': 'beta' },
|
||||
{ 'path': 'generics_unbox', 'name': 'gamma' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'Inside<T> -> Out3<U, T>',
|
||||
'others': [
|
||||
{ 'path': 'generics_unbox', 'name': 'beta' },
|
||||
{ 'path': 'generics_unbox', 'name': 'gamma' },
|
||||
],
|
||||
},
|
||||
{
|
||||
'query': 'Inside<T> -> Out4<U, T>',
|
||||
'others': [
|
||||
{ 'path': 'generics_unbox', 'name': 'beta' },
|
||||
{ 'path': 'generics_unbox', 'name': 'gamma' },
|
||||
],
|
||||
},
|
||||
];
|
36
tests/rustdoc-js/generics-unbox.rs
Normal file
36
tests/rustdoc-js/generics-unbox.rs
Normal file
@ -0,0 +1,36 @@
|
||||
pub struct Out<A, B = ()> {
|
||||
a: A,
|
||||
b: B,
|
||||
}
|
||||
|
||||
pub struct Out1<A, const N: usize> {
|
||||
a: [A; N],
|
||||
}
|
||||
|
||||
pub struct Out2<A, const N: usize> {
|
||||
a: [A; N],
|
||||
}
|
||||
|
||||
pub struct Out3<A, B> {
|
||||
a: A,
|
||||
b: B,
|
||||
}
|
||||
|
||||
pub struct Out4<A, B> {
|
||||
a: A,
|
||||
b: B,
|
||||
}
|
||||
|
||||
pub struct Inside<T>(T);
|
||||
|
||||
pub fn alpha<const N: usize, T>(_: Inside<T>) -> Out<Out1<T, N>, Out2<T, N>> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub fn beta<T, U>(_: Inside<T>) -> Out<Out3<T, U>, Out4<U, T>> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub fn gamma<T, U>(_: Inside<T>) -> Out<Out3<U, T>, Out4<T, U>> {
|
||||
loop {}
|
||||
}
|
87
tests/rustdoc-js/type-parameters.js
Normal file
87
tests/rustdoc-js/type-parameters.js
Normal file
@ -0,0 +1,87 @@
|
||||
// exact-check
|
||||
// ignore-order
|
||||
|
||||
const EXPECTED = [
|
||||
{
|
||||
query: '-> trait:Some',
|
||||
others: [
|
||||
{ path: 'foo', name: 'alef' },
|
||||
{ path: 'foo', name: 'alpha' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: '-> generic:T',
|
||||
others: [
|
||||
{ path: 'foo', name: 'bet' },
|
||||
{ path: 'foo', name: 'alef' },
|
||||
{ path: 'foo', name: 'beta' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'A -> B',
|
||||
others: [
|
||||
{ path: 'foo', name: 'bet' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'A -> A',
|
||||
others: [
|
||||
{ path: 'foo', name: 'beta' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'A, A',
|
||||
others: [
|
||||
{ path: 'foo', name: 'alternate' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'A, B',
|
||||
others: [
|
||||
{ path: 'foo', name: 'other' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'Other, Other',
|
||||
others: [
|
||||
{ path: 'foo', name: 'other' },
|
||||
{ path: 'foo', name: 'alternate' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'generic:T',
|
||||
in_args: [
|
||||
{ path: 'foo', name: 'bet' },
|
||||
{ path: 'foo', name: 'beta' },
|
||||
{ path: 'foo', name: 'other' },
|
||||
{ path: 'foo', name: 'alternate' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'generic:Other',
|
||||
in_args: [
|
||||
{ path: 'foo', name: 'bet' },
|
||||
{ path: 'foo', name: 'beta' },
|
||||
{ path: 'foo', name: 'other' },
|
||||
{ path: 'foo', name: 'alternate' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'trait:Other',
|
||||
in_args: [
|
||||
{ path: 'foo', name: 'other' },
|
||||
{ path: 'foo', name: 'alternate' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'Other',
|
||||
in_args: [
|
||||
{ path: 'foo', name: 'other' },
|
||||
{ path: 'foo', name: 'alternate' },
|
||||
],
|
||||
},
|
||||
{
|
||||
query: 'trait:T',
|
||||
in_args: [],
|
||||
},
|
||||
];
|
15
tests/rustdoc-js/type-parameters.rs
Normal file
15
tests/rustdoc-js/type-parameters.rs
Normal file
@ -0,0 +1,15 @@
|
||||
#![crate_name="foo"]
|
||||
|
||||
pub trait Some {}
|
||||
impl Some for () {}
|
||||
pub trait Other {}
|
||||
impl Other for () {}
|
||||
|
||||
pub fn alef<T: Some>() -> T { loop {} }
|
||||
pub fn alpha() -> impl Some { }
|
||||
|
||||
pub fn bet<T, U>(t: T) -> U { loop {} }
|
||||
pub fn beta<T>(t: T) -> T {}
|
||||
|
||||
pub fn other<T: Other, U: Other>(t: T, u: U) { loop {} }
|
||||
pub fn alternate<T: Other>(t: T, u: T) { loop {} }
|
@ -16,6 +16,14 @@ LL | build(Bar);
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `for<'a> Send` is not implemented for `impl Future<Output = ()> { <_ as Foo>::bar() }`
|
||||
note: this is a known limitation of the trait solver that will be lifted in the future
|
||||
--> $DIR/normalizing-self-auto-trait-issue-109924.rs:23:11
|
||||
|
|
||||
LL | build(Bar);
|
||||
| ------^^^-
|
||||
| | |
|
||||
| | the trait solver is unable to infer the generic types that should be inferred from this argument
|
||||
| add turbofish arguments to this call to specify the types manually, even if it's redundant
|
||||
note: required by a bound in `build`
|
||||
--> $DIR/normalizing-self-auto-trait-issue-109924.rs:20:39
|
||||
|
|
||||
|
26
tests/ui/const_prop/dont-propagate-generic-instance-2.rs
Normal file
26
tests/ui/const_prop/dont-propagate-generic-instance-2.rs
Normal file
@ -0,0 +1,26 @@
|
||||
// run-pass
|
||||
|
||||
#![feature(inline_const)]
|
||||
|
||||
// Makes sure we don't propagate generic instances of `Self: ?Sized` blanket impls.
|
||||
// This is relevant when we have an overlapping impl and builtin dyn instance.
|
||||
// See <https://github.com/rust-lang/rust/pull/114941> for more context.
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> &'static str;
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Trait for T {
|
||||
fn foo(&self) -> &'static str {
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
fn bar<T: ?Sized>() -> fn(&T) -> &'static str {
|
||||
const { Trait::foo as fn(&T) -> &'static str }
|
||||
// If const prop were to propagate the instance
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!("i32", bar::<dyn Trait>()(&1i32));
|
||||
}
|
24
tests/ui/const_prop/dont-propagate-generic-instance.rs
Normal file
24
tests/ui/const_prop/dont-propagate-generic-instance.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// run-pass
|
||||
|
||||
// Makes sure we don't propagate generic instances of `Self: ?Sized` blanket impls.
|
||||
// This is relevant when we have an overlapping impl and builtin dyn instance.
|
||||
// See <https://github.com/rust-lang/rust/pull/114941> for more context.
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> &'static str;
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Trait for T {
|
||||
fn foo(&self) -> &'static str {
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
const fn bar<T: ?Sized>() -> fn(&T) -> &'static str {
|
||||
Trait::foo
|
||||
// If const prop were to propagate the instance
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!("i32", bar::<dyn Trait>()(&1i32));
|
||||
}
|
@ -7,6 +7,14 @@ LL | test(Foo);
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Marker` is implemented for `()`
|
||||
note: this is a known limitation of the trait solver that will be lifted in the future
|
||||
--> $DIR/issue-88460.rs:28:10
|
||||
|
|
||||
LL | test(Foo);
|
||||
| -----^^^-
|
||||
| | |
|
||||
| | the trait solver is unable to infer the generic types that should be inferred from this argument
|
||||
| add turbofish arguments to this call to specify the types manually, even if it's redundant
|
||||
note: required by a bound in `test`
|
||||
--> $DIR/issue-88460.rs:15:27
|
||||
|
|
||||
|
@ -8,6 +8,14 @@ LL | call(f, ());
|
||||
|
|
||||
= note: expected a closure with arguments `((),)`
|
||||
found a closure with arguments `(<_ as ATC<'a>>::Type,)`
|
||||
note: this is a known limitation of the trait solver that will be lifted in the future
|
||||
--> $DIR/issue-62529-3.rs:25:14
|
||||
|
|
||||
LL | call(f, ());
|
||||
| -----^-----
|
||||
| | |
|
||||
| | the trait solver is unable to infer the generic types that should be inferred from this argument
|
||||
| add turbofish arguments to this call to specify the types manually, even if it's redundant
|
||||
note: required by a bound in `call`
|
||||
--> $DIR/issue-62529-3.rs:9:36
|
||||
|
|
||||
|
@ -7,6 +7,14 @@ LL | upcast(y)
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `IsCovariant<'a>` is implemented for `std::borrow::Cow<'a, T>`
|
||||
note: this is a known limitation of the trait solver that will be lifted in the future
|
||||
--> $DIR/issue-90950.rs:50:12
|
||||
|
|
||||
LL | upcast(y)
|
||||
| -------^-
|
||||
| | |
|
||||
| | the trait solver is unable to infer the generic types that should be inferred from this argument
|
||||
| add turbofish arguments to this call to specify the types manually, even if it's redundant
|
||||
note: required by a bound in `upcast`
|
||||
--> $DIR/issue-90950.rs:27:42
|
||||
|
|
||||
|
@ -4,6 +4,11 @@ error[E0277]: the trait bound `for<'a> <_ as Trait<'a>>::Out: Copy` is not satis
|
||||
LL | let _: () = weird_bound();
|
||||
| ^^^^^^^^^^^ the trait `for<'a> Copy` is not implemented for `<_ as Trait<'a>>::Out`
|
||||
|
|
||||
note: this is a known limitation of the trait solver that will be lifted in the future
|
||||
--> $DIR/norm-before-method-resolution.rs:22:17
|
||||
|
|
||||
LL | let _: () = weird_bound();
|
||||
| ^^^^^^^^^^^ try adding turbofish arguments to this expression to specify the types manually, even if it's redundant
|
||||
note: required by a bound in `weird_bound`
|
||||
--> $DIR/norm-before-method-resolution.rs:18:40
|
||||
|
|
||||
|
@ -93,4 +93,44 @@ pub struct T16(Sized, ExternalIndirection<NonExhaustiveVariant>);
|
||||
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T17(NonExhaustive, Sized);
|
||||
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T18(NonExhaustive, NonExhaustive);
|
||||
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T19(NonExhaustive, Private);
|
||||
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T19Flipped(Private, NonExhaustive);
|
||||
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T20(NonExhaustive);
|
||||
// Okay, since it's the only field.
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T21(NonExhaustive, InternalNonExhaustive);
|
||||
// Okay, since there's only 1 foreign non-exhaustive type.
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T21Flipped(InternalNonExhaustive, NonExhaustive);
|
||||
// Okay, since there's only 1 foreign non-exhaustive type.
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T22(NonExhaustive, InternalPrivate);
|
||||
// Okay, since there's only 1 foreign non-exhaustive type.
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T22Flipped(InternalPrivate, NonExhaustive);
|
||||
// Okay, since there's only 1 foreign non-exhaustive type.
|
||||
|
||||
fn main() {}
|
||||
|
@ -123,5 +123,45 @@ LL | pub struct T16(Sized, ExternalIndirection<NonExhaustiveVariant>);
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:97:16
|
||||
|
|
||||
LL | pub struct T17(NonExhaustive, Sized);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:102:31
|
||||
|
|
||||
LL | pub struct T18(NonExhaustive, NonExhaustive);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:107:31
|
||||
|
|
||||
LL | pub struct T19(NonExhaustive, Private);
|
||||
| ^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:112:32
|
||||
|
|
||||
LL | pub struct T19Flipped(Private, NonExhaustive);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: aborting due to 16 previous errors
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user