| author | bors <bors@rust-lang.org> 2025-08-19 04:22:49 UTC |
| committer | bors <bors@rust-lang.org> 2025-08-19 04:22:49 UTC |
| log | 8365fcb2b840c95eeb0bc377af8bd498fad22245 |
| tree | 803b7bb87deb46b579af4ec13dc1f415d4ac4e73 |
| parent | b96868fa2ef174b0a5aeb3bf041b3a5b517f11f8 |
| parent | 531ec858e9a1c69840f70d4f2a485c288f50fc11 |
Rollup of 19 pull requests
Successful merges:
- rust-lang/rust#140956 (`impl PartialEq<{str,String}> for {Path,PathBuf}`)
- rust-lang/rust#141744 (Stabilize `ip_from`)
- rust-lang/rust#142681 (Remove the `#[no_sanitize]` attribute in favor of `#[sanitize(xyz = "on|off")]`)
- rust-lang/rust#142871 (Trivial improve doc for transpose )
- rust-lang/rust#144252 (Do not copy .rmeta files into the sysroot of the build compiler during check of rustc/std)
- rust-lang/rust#144476 (rustdoc-search: search backend with partitioned suffix tree)
- rust-lang/rust#144567 (Fix RISC-V Test Failures in ./x test for Multiple Codegen Cases)
- rust-lang/rust#144804 (Don't warn on never to any `as` casts as unreachable)
- rust-lang/rust#144960 ([RTE-513] Ignore sleep_until test on SGX)
- rust-lang/rust#145013 (overhaul `&mut` suggestions in borrowck errors)
- rust-lang/rust#145041 (rework GAT borrowck limitation error)
- rust-lang/rust#145243 (take attr style into account in diagnostics)
- rust-lang/rust#145405 (cleanup: use run_in_tmpdir in run-make/rustdoc-scrape-examples-paths)
- rust-lang/rust#145432 (cg_llvm: Small cleanups to `owned_target_machine`)
- rust-lang/rust#145484 (Remove `LlvmArchiveBuilder` and supporting code/bindings)
- rust-lang/rust#145557 (Fix uplifting in `Assemble` step)
- rust-lang/rust#145563 (Remove the `From` derive macro from prelude)
- rust-lang/rust#145565 (Improve context of bootstrap errors in CI)
- rust-lang/rust#145584 (interpret: avoid forcing all integer newtypes into memory during clear_provenance)
Failed merges:
- rust-lang/rust#145359 (Fix bug where `rustdoc-js` tester would not pick the right `search.js` file if there is more than one)
- rust-lang/rust#145573 (Add an experimental unsafe(force_target_feature) attribute.)
r? `@ghost`
`@rustbot` modify labels: rollup287 files changed, 11492 insertions(+), 6762 deletions(-)
Cargo.lock+10| ... | ... | @@ -4812,6 +4812,7 @@ dependencies = [ |
| 4812 | 4812 | "serde_json", |
| 4813 | 4813 | "sha2", |
| 4814 | 4814 | "smallvec", |
| 4815 | "stringdex", | |
| 4815 | 4816 | "tempfile", |
| 4816 | 4817 | "threadpool", |
| 4817 | 4818 | "tracing", |
| ... | ... | @@ -5225,6 +5226,15 @@ dependencies = [ |
| 5225 | 5226 | "quote", |
| 5226 | 5227 | ] |
| 5227 | 5228 | |
| 5229 | [[package]] | |
| 5230 | name = "stringdex" | |
| 5231 | version = "0.0.1-alpha4" | |
| 5232 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 5233 | checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0" | |
| 5234 | dependencies = [ | |
| 5235 | "stacker", | |
| 5236 | ] | |
| 5237 | ||
| 5228 | 5238 | [[package]] |
| 5229 | 5239 | name = "strsim" |
| 5230 | 5240 | version = "0.11.1" |
compiler/rustc_attr_parsing/src/attributes/inline.rs+2-2| ... | ... | @@ -62,8 +62,8 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser { |
| 62 | 62 | } |
| 63 | 63 | } |
| 64 | 64 | ArgParser::NameValue(_) => { |
| 65 | let suggestions = | |
| 66 | <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline"); | |
| 65 | let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE | |
| 66 | .suggestions(cx.attr_style, "inline"); | |
| 67 | 67 | let span = cx.attr_span; |
| 68 | 68 | cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); |
| 69 | 69 | return None; |
compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs+1-1| ... | ... | @@ -107,7 +107,7 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser { |
| 107 | 107 | } |
| 108 | 108 | } |
| 109 | 109 | ArgParser::NameValue(_) => { |
| 110 | let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use); | |
| 110 | let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use); | |
| 111 | 111 | cx.emit_err(session_diagnostics::IllFormedAttributeInputLint { |
| 112 | 112 | num_suggestions: suggestions.len(), |
| 113 | 113 | suggestions: DiagArgValue::StrListSepByAnd( |
compiler/rustc_attr_parsing/src/attributes/must_use.rs+2-2| ... | ... | @@ -35,8 +35,8 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser { |
| 35 | 35 | Some(value_str) |
| 36 | 36 | } |
| 37 | 37 | ArgParser::List(_) => { |
| 38 | let suggestions = | |
| 39 | <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use"); | |
| 38 | let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE | |
| 39 | .suggestions(cx.attr_style, "must_use"); | |
| 40 | 40 | cx.emit_err(session_diagnostics::IllFormedAttributeInputLint { |
| 41 | 41 | num_suggestions: suggestions.len(), |
| 42 | 42 | suggestions: DiagArgValue::StrListSepByAnd( |
compiler/rustc_attr_parsing/src/attributes/test_attrs.rs+3-3| ... | ... | @@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser { |
| 29 | 29 | ArgParser::NameValue(name_value) => { |
| 30 | 30 | let Some(str_value) = name_value.value_as_str() else { |
| 31 | 31 | let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE |
| 32 | .suggestions(false, "ignore"); | |
| 32 | .suggestions(cx.attr_style, "ignore"); | |
| 33 | 33 | let span = cx.attr_span; |
| 34 | 34 | cx.emit_lint( |
| 35 | 35 | AttributeLintKind::IllFormedAttributeInput { suggestions }, |
| ... | ... | @@ -40,8 +40,8 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser { |
| 40 | 40 | Some(str_value) |
| 41 | 41 | } |
| 42 | 42 | ArgParser::List(_) => { |
| 43 | let suggestions = | |
| 44 | <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "ignore"); | |
| 43 | let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE | |
| 44 | .suggestions(cx.attr_style, "ignore"); | |
| 45 | 45 | let span = cx.attr_span; |
| 46 | 46 | cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); |
| 47 | 47 | return None; |
compiler/rustc_attr_parsing/src/context.rs+17-1| ... | ... | @@ -5,7 +5,7 @@ use std::sync::LazyLock; |
| 5 | 5 | |
| 6 | 6 | use itertools::Itertools; |
| 7 | 7 | use private::Sealed; |
| 8 | use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId}; | |
| 8 | use rustc_ast::{self as ast, AttrStyle, LitKind, MetaItemLit, NodeId}; | |
| 9 | 9 | use rustc_errors::{DiagCtxtHandle, Diagnostic}; |
| 10 | 10 | use rustc_feature::{AttributeTemplate, Features}; |
| 11 | 11 | use rustc_hir::attrs::AttributeKind; |
| ... | ... | @@ -315,6 +315,7 @@ pub struct AcceptContext<'f, 'sess, S: Stage> { |
| 315 | 315 | /// The span of the attribute currently being parsed |
| 316 | 316 | pub(crate) attr_span: Span, |
| 317 | 317 | |
| 318 | pub(crate) attr_style: AttrStyle, | |
| 318 | 319 | /// The expected structure of the attribute. |
| 319 | 320 | /// |
| 320 | 321 | /// Used in reporting errors to give a hint to users what the attribute *should* look like. |
| ... | ... | @@ -396,6 +397,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 396 | 397 | i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) |
| 397 | 398 | }), |
| 398 | 399 | }, |
| 400 | attr_style: self.attr_style, | |
| 399 | 401 | }) |
| 400 | 402 | } |
| 401 | 403 | |
| ... | ... | @@ -406,6 +408,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 406 | 408 | template: self.template.clone(), |
| 407 | 409 | attribute: self.attr_path.clone(), |
| 408 | 410 | reason: AttributeParseErrorReason::ExpectedIntegerLiteral, |
| 411 | attr_style: self.attr_style, | |
| 409 | 412 | }) |
| 410 | 413 | } |
| 411 | 414 | |
| ... | ... | @@ -416,6 +419,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 416 | 419 | template: self.template.clone(), |
| 417 | 420 | attribute: self.attr_path.clone(), |
| 418 | 421 | reason: AttributeParseErrorReason::ExpectedList, |
| 422 | attr_style: self.attr_style, | |
| 419 | 423 | }) |
| 420 | 424 | } |
| 421 | 425 | |
| ... | ... | @@ -426,6 +430,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 426 | 430 | template: self.template.clone(), |
| 427 | 431 | attribute: self.attr_path.clone(), |
| 428 | 432 | reason: AttributeParseErrorReason::ExpectedNoArgs, |
| 433 | attr_style: self.attr_style, | |
| 429 | 434 | }) |
| 430 | 435 | } |
| 431 | 436 | |
| ... | ... | @@ -437,6 +442,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 437 | 442 | template: self.template.clone(), |
| 438 | 443 | attribute: self.attr_path.clone(), |
| 439 | 444 | reason: AttributeParseErrorReason::ExpectedIdentifier, |
| 445 | attr_style: self.attr_style, | |
| 440 | 446 | }) |
| 441 | 447 | } |
| 442 | 448 | |
| ... | ... | @@ -449,6 +455,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 449 | 455 | template: self.template.clone(), |
| 450 | 456 | attribute: self.attr_path.clone(), |
| 451 | 457 | reason: AttributeParseErrorReason::ExpectedNameValue(name), |
| 458 | attr_style: self.attr_style, | |
| 452 | 459 | }) |
| 453 | 460 | } |
| 454 | 461 | |
| ... | ... | @@ -460,6 +467,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 460 | 467 | template: self.template.clone(), |
| 461 | 468 | attribute: self.attr_path.clone(), |
| 462 | 469 | reason: AttributeParseErrorReason::DuplicateKey(key), |
| 470 | attr_style: self.attr_style, | |
| 463 | 471 | }) |
| 464 | 472 | } |
| 465 | 473 | |
| ... | ... | @@ -472,6 +480,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 472 | 480 | template: self.template.clone(), |
| 473 | 481 | attribute: self.attr_path.clone(), |
| 474 | 482 | reason: AttributeParseErrorReason::UnexpectedLiteral, |
| 483 | attr_style: self.attr_style, | |
| 475 | 484 | }) |
| 476 | 485 | } |
| 477 | 486 | |
| ... | ... | @@ -482,6 +491,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 482 | 491 | template: self.template.clone(), |
| 483 | 492 | attribute: self.attr_path.clone(), |
| 484 | 493 | reason: AttributeParseErrorReason::ExpectedSingleArgument, |
| 494 | attr_style: self.attr_style, | |
| 485 | 495 | }) |
| 486 | 496 | } |
| 487 | 497 | |
| ... | ... | @@ -492,6 +502,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 492 | 502 | template: self.template.clone(), |
| 493 | 503 | attribute: self.attr_path.clone(), |
| 494 | 504 | reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument, |
| 505 | attr_style: self.attr_style, | |
| 495 | 506 | }) |
| 496 | 507 | } |
| 497 | 508 | |
| ... | ... | @@ -510,6 +521,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 510 | 521 | strings: false, |
| 511 | 522 | list: false, |
| 512 | 523 | }, |
| 524 | attr_style: self.attr_style, | |
| 513 | 525 | }) |
| 514 | 526 | } |
| 515 | 527 | |
| ... | ... | @@ -528,6 +540,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 528 | 540 | strings: false, |
| 529 | 541 | list: true, |
| 530 | 542 | }, |
| 543 | attr_style: self.attr_style, | |
| 531 | 544 | }) |
| 532 | 545 | } |
| 533 | 546 | |
| ... | ... | @@ -546,6 +559,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { |
| 546 | 559 | strings: true, |
| 547 | 560 | list: false, |
| 548 | 561 | }, |
| 562 | attr_style: self.attr_style, | |
| 549 | 563 | }) |
| 550 | 564 | } |
| 551 | 565 | |
| ... | ... | @@ -804,6 +818,7 @@ impl<'sess> AttributeParser<'sess, Early> { |
| 804 | 818 | }, |
| 805 | 819 | }, |
| 806 | 820 | attr_span: attr.span, |
| 821 | attr_style: attr.style, | |
| 807 | 822 | template, |
| 808 | 823 | attr_path: path.get_attribute_path(), |
| 809 | 824 | }; |
| ... | ... | @@ -914,6 +929,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { |
| 914 | 929 | emit_lint: &mut emit_lint, |
| 915 | 930 | }, |
| 916 | 931 | attr_span: lower_span(attr.span), |
| 932 | attr_style: attr.style, | |
| 917 | 933 | template: &accept.template, |
| 918 | 934 | attr_path: path.get_attribute_path(), |
| 919 | 935 | }; |
compiler/rustc_attr_parsing/src/session_diagnostics.rs+4-2| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | use std::num::IntErrorKind; |
| 2 | 2 | |
| 3 | use rustc_ast as ast; | |
| 3 | use rustc_ast::{self as ast, AttrStyle}; | |
| 4 | 4 | use rustc_errors::codes::*; |
| 5 | 5 | use rustc_errors::{ |
| 6 | 6 | Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, |
| ... | ... | @@ -579,6 +579,7 @@ pub(crate) enum AttributeParseErrorReason { |
| 579 | 579 | pub(crate) struct AttributeParseError { |
| 580 | 580 | pub(crate) span: Span, |
| 581 | 581 | pub(crate) attr_span: Span, |
| 582 | pub(crate) attr_style: AttrStyle, | |
| 582 | 583 | pub(crate) template: AttributeTemplate, |
| 583 | 584 | pub(crate) attribute: AttrPath, |
| 584 | 585 | pub(crate) reason: AttributeParseErrorReason, |
| ... | ... | @@ -717,7 +718,8 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { |
| 717 | 718 | if let Some(link) = self.template.docs { |
| 718 | 719 | diag.note(format!("for more information, visit <{link}>")); |
| 719 | 720 | } |
| 720 | let suggestions = self.template.suggestions(false, &name); | |
| 721 | let suggestions = self.template.suggestions(self.attr_style, &name); | |
| 722 | ||
| 721 | 723 | diag.span_suggestions( |
| 722 | 724 | self.attr_span, |
| 723 | 725 | if suggestions.len() == 1 { |
compiler/rustc_borrowck/messages.ftl+1-1| ... | ... | @@ -90,7 +90,7 @@ borrowck_lifetime_constraints_error = |
| 90 | 90 | lifetime may not live long enough |
| 91 | 91 | |
| 92 | 92 | borrowck_limitations_implies_static = |
| 93 | due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 93 | due to a current limitation of the type system, this implies a `'static` lifetime | |
| 94 | 94 | |
| 95 | 95 | borrowck_move_closure_suggestion = |
| 96 | 96 | consider adding 'move' keyword before the nested closure |
compiler/rustc_borrowck/src/diagnostics/mod.rs+52-9| ... | ... | @@ -6,7 +6,9 @@ use rustc_abi::{FieldIdx, VariantIdx}; |
| 6 | 6 | use rustc_data_structures::fx::FxIndexMap; |
| 7 | 7 | use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify}; |
| 8 | 8 | use rustc_hir::def::{CtorKind, Namespace}; |
| 9 | use rustc_hir::{self as hir, CoroutineKind, LangItem}; | |
| 9 | use rustc_hir::{ | |
| 10 | self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind, | |
| 11 | }; | |
| 10 | 12 | use rustc_index::{IndexSlice, IndexVec}; |
| 11 | 13 | use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin}; |
| 12 | 14 | use rustc_infer::traits::SelectionError; |
| ... | ... | @@ -658,25 +660,66 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 658 | 660 | |
| 659 | 661 | /// Add a note to region errors and borrow explanations when higher-ranked regions in predicates |
| 660 | 662 | /// implicitly introduce an "outlives `'static`" constraint. |
| 663 | /// | |
| 664 | /// This is very similar to `fn suggest_static_lifetime_for_gat_from_hrtb` which handles this | |
| 665 | /// note for failed type tests instead of outlives errors. | |
| 661 | 666 | fn add_placeholder_from_predicate_note<G: EmissionGuarantee>( |
| 662 | 667 | &self, |
| 663 | err: &mut Diag<'_, G>, | |
| 668 | diag: &mut Diag<'_, G>, | |
| 664 | 669 | path: &[OutlivesConstraint<'tcx>], |
| 665 | 670 | ) { |
| 666 | let predicate_span = path.iter().find_map(|constraint| { | |
| 671 | let tcx = self.infcx.tcx; | |
| 672 | let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| { | |
| 667 | 673 | let outlived = constraint.sub; |
| 668 | 674 | if let Some(origin) = self.regioncx.definitions.get(outlived) |
| 669 | && let NllRegionVariableOrigin::Placeholder(_) = origin.origin | |
| 670 | && let ConstraintCategory::Predicate(span) = constraint.category | |
| 675 | && let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin | |
| 676 | && let Some(id) = placeholder.bound.kind.get_id() | |
| 677 | && let Some(placeholder_id) = id.as_local() | |
| 678 | && let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id) | |
| 679 | && let Some(generics_impl) = | |
| 680 | tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics() | |
| 671 | 681 | { |
| 672 | Some(span) | |
| 682 | Some((gat_hir_id, generics_impl)) | |
| 673 | 683 | } else { |
| 674 | 684 | None |
| 675 | 685 | } |
| 676 | }); | |
| 686 | }) else { | |
| 687 | return; | |
| 688 | }; | |
| 677 | 689 | |
| 678 | if let Some(span) = predicate_span { | |
| 679 | err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); | |
| 690 | // Look for the where-bound which introduces the placeholder. | |
| 691 | // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>` | |
| 692 | // and `T: for<'a> Trait`<'a>. | |
| 693 | for pred in generics.predicates { | |
| 694 | let WherePredicateKind::BoundPredicate(WhereBoundPredicate { | |
| 695 | bound_generic_params, | |
| 696 | bounds, | |
| 697 | .. | |
| 698 | }) = pred.kind | |
| 699 | else { | |
| 700 | continue; | |
| 701 | }; | |
| 702 | if bound_generic_params | |
| 703 | .iter() | |
| 704 | .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) | |
| 705 | .is_some() | |
| 706 | { | |
| 707 | diag.span_note(pred.span, fluent::borrowck_limitations_implies_static); | |
| 708 | return; | |
| 709 | } | |
| 710 | for bound in bounds.iter() { | |
| 711 | if let GenericBound::Trait(bound) = bound { | |
| 712 | if bound | |
| 713 | .bound_generic_params | |
| 714 | .iter() | |
| 715 | .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) | |
| 716 | .is_some() | |
| 717 | { | |
| 718 | diag.span_note(bound.span, fluent::borrowck_limitations_implies_static); | |
| 719 | return; | |
| 720 | } | |
| 721 | } | |
| 722 | } | |
| 680 | 723 | } |
| 681 | 724 | } |
| 682 | 725 |
compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs+408-365| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | |
| 4 | 4 | use core::ops::ControlFlow; |
| 5 | 5 | |
| 6 | use either::Either; | |
| 6 | 7 | use hir::{ExprKind, Param}; |
| 7 | 8 | use rustc_abi::FieldIdx; |
| 8 | 9 | use rustc_errors::{Applicability, Diag}; |
| ... | ... | @@ -12,15 +13,16 @@ use rustc_middle::bug; |
| 12 | 13 | use rustc_middle::hir::place::PlaceBase; |
| 13 | 14 | use rustc_middle::mir::visit::PlaceContext; |
| 14 | 15 | use rustc_middle::mir::{ |
| 15 | self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place, | |
| 16 | PlaceRef, ProjectionElem, | |
| 16 | self, BindingForm, Body, BorrowKind, Local, LocalDecl, LocalInfo, LocalKind, Location, | |
| 17 | Mutability, Operand, Place, PlaceRef, ProjectionElem, RawPtrKind, Rvalue, Statement, | |
| 18 | StatementKind, TerminatorKind, | |
| 17 | 19 | }; |
| 18 | 20 | use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast}; |
| 19 | 21 | use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym}; |
| 20 | 22 | use rustc_trait_selection::error_reporting::InferCtxtErrorExt; |
| 21 | 23 | use rustc_trait_selection::infer::InferCtxtExt; |
| 22 | 24 | use rustc_trait_selection::traits; |
| 23 | use tracing::debug; | |
| 25 | use tracing::{debug, trace}; | |
| 24 | 26 | |
| 25 | 27 | use crate::diagnostics::BorrowedContentSource; |
| 26 | 28 | use crate::{MirBorrowckCtxt, session_diagnostics}; |
| ... | ... | @@ -31,6 +33,33 @@ pub(crate) enum AccessKind { |
| 31 | 33 | Mutate, |
| 32 | 34 | } |
| 33 | 35 | |
| 36 | /// Finds all statements that assign directly to local (i.e., X = ...) and returns their | |
| 37 | /// locations. | |
| 38 | fn find_assignments(body: &Body<'_>, local: Local) -> Vec<Location> { | |
| 39 | use rustc_middle::mir::visit::Visitor; | |
| 40 | ||
| 41 | struct FindLocalAssignmentVisitor { | |
| 42 | needle: Local, | |
| 43 | locations: Vec<Location>, | |
| 44 | } | |
| 45 | ||
| 46 | impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor { | |
| 47 | fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) { | |
| 48 | if self.needle != local { | |
| 49 | return; | |
| 50 | } | |
| 51 | ||
| 52 | if place_context.is_place_assignment() { | |
| 53 | self.locations.push(location); | |
| 54 | } | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] }; | |
| 59 | visitor.visit_body(body); | |
| 60 | visitor.locations | |
| 61 | } | |
| 62 | ||
| 34 | 63 | impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 35 | 64 | pub(crate) fn report_mutability_error( |
| 36 | 65 | &mut self, |
| ... | ... | @@ -384,7 +413,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 384 | 413 | } |
| 385 | 414 | } |
| 386 | 415 | |
| 387 | // Also suggest adding mut for upvars | |
| 416 | // Also suggest adding mut for upvars. | |
| 388 | 417 | PlaceRef { |
| 389 | 418 | local, |
| 390 | 419 | projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)], |
| ... | ... | @@ -438,9 +467,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 438 | 467 | } |
| 439 | 468 | } |
| 440 | 469 | |
| 441 | // complete hack to approximate old AST-borrowck | |
| 442 | // diagnostic: if the span starts with a mutable borrow of | |
| 443 | // a local variable, then just suggest the user remove it. | |
| 470 | // Complete hack to approximate old AST-borrowck diagnostic: if the span starts | |
| 471 | // with a mutable borrow of a local variable, then just suggest the user remove it. | |
| 444 | 472 | PlaceRef { local: _, projection: [] } |
| 445 | 473 | if self |
| 446 | 474 | .infcx |
| ... | ... | @@ -769,7 +797,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 769 | 797 | ); |
| 770 | 798 | } |
| 771 | 799 | |
| 772 | // point to span of upvar making closure call require mutable borrow | |
| 800 | // Point to span of upvar making closure call that requires a mutable borrow | |
| 773 | 801 | fn show_mutating_upvar( |
| 774 | 802 | &self, |
| 775 | 803 | tcx: TyCtxt<'_>, |
| ... | ... | @@ -825,7 +853,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 825 | 853 | } else { |
| 826 | 854 | bug!("not an upvar") |
| 827 | 855 | }; |
| 828 | // sometimes we deliberately don't store the name of a place when coming from a macro in | |
| 856 | // Sometimes we deliberately don't store the name of a place when coming from a macro in | |
| 829 | 857 | // another crate. We generally want to limit those diagnostics a little, to hide |
| 830 | 858 | // implementation details (such as those from pin!() or format!()). In that case show a |
| 831 | 859 | // slightly different error message, or none at all if something else happened. In other |
| ... | ... | @@ -936,8 +964,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 936 | 964 | let def_id = tcx.hir_enclosing_body_owner(fn_call_id); |
| 937 | 965 | let mut look_at_return = true; |
| 938 | 966 | |
| 939 | // If the HIR node is a function or method call gets the def ID | |
| 940 | // of the called function or method and the span and args of the call expr | |
| 967 | // If the HIR node is a function or method call, get the DefId | |
| 968 | // of the callee function or method, the span, and args of the call expr | |
| 941 | 969 | let get_call_details = || { |
| 942 | 970 | let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else { |
| 943 | 971 | return None; |
| ... | ... | @@ -1051,7 +1079,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 1051 | 1079 | let mut cur_expr = expr; |
| 1052 | 1080 | while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind { |
| 1053 | 1081 | if path_segment.ident.name == sym::iter { |
| 1054 | // check `_ty` has `iter_mut` method | |
| 1082 | // Check that the type has an `iter_mut` method. | |
| 1055 | 1083 | let res = self |
| 1056 | 1084 | .infcx |
| 1057 | 1085 | .tcx |
| ... | ... | @@ -1081,38 +1109,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 1081 | 1109 | } |
| 1082 | 1110 | } |
| 1083 | 1111 | |
| 1084 | /// Finds all statements that assign directly to local (i.e., X = ...) and returns their | |
| 1085 | /// locations. | |
| 1086 | fn find_assignments(&self, local: Local) -> Vec<Location> { | |
| 1087 | use rustc_middle::mir::visit::Visitor; | |
| 1088 | ||
| 1089 | struct FindLocalAssignmentVisitor { | |
| 1090 | needle: Local, | |
| 1091 | locations: Vec<Location>, | |
| 1092 | } | |
| 1093 | ||
| 1094 | impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor { | |
| 1095 | fn visit_local( | |
| 1096 | &mut self, | |
| 1097 | local: Local, | |
| 1098 | place_context: PlaceContext, | |
| 1099 | location: Location, | |
| 1100 | ) { | |
| 1101 | if self.needle != local { | |
| 1102 | return; | |
| 1103 | } | |
| 1104 | ||
| 1105 | if place_context.is_place_assignment() { | |
| 1106 | self.locations.push(location); | |
| 1107 | } | |
| 1108 | } | |
| 1109 | } | |
| 1110 | ||
| 1111 | let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] }; | |
| 1112 | visitor.visit_body(self.body); | |
| 1113 | visitor.locations | |
| 1114 | } | |
| 1115 | ||
| 1116 | 1112 | fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) { |
| 1117 | 1113 | let local_decl = &self.body.local_decls[local]; |
| 1118 | 1114 | |
| ... | ... | @@ -1122,7 +1118,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 1122 | 1118 | let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local); |
| 1123 | 1119 | |
| 1124 | 1120 | if is_trait_sig && !is_local { |
| 1125 | // Do not suggest to change the signature when the trait comes from another crate. | |
| 1121 | // Do not suggest changing the signature when the trait comes from another crate. | |
| 1126 | 1122 | err.span_label( |
| 1127 | 1123 | local_decl.source_info.span, |
| 1128 | 1124 | format!("this is an immutable {pointer_desc}"), |
| ... | ... | @@ -1131,11 +1127,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 1131 | 1127 | } |
| 1132 | 1128 | let decl_span = local_decl.source_info.span; |
| 1133 | 1129 | |
| 1134 | let amp_mut_sugg = match *local_decl.local_info() { | |
| 1130 | let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() { | |
| 1135 | 1131 | LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => { |
| 1136 | 1132 | let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span); |
| 1137 | 1133 | let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span)); |
| 1138 | Some(AmpMutSugg { has_sugg: true, span, suggestion, additional }) | |
| 1134 | (AmpMutSugg::Type { span, suggestion, additional }, None) | |
| 1139 | 1135 | } |
| 1140 | 1136 | |
| 1141 | 1137 | LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { |
| ... | ... | @@ -1143,79 +1139,54 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 1143 | 1139 | opt_ty_info, |
| 1144 | 1140 | .. |
| 1145 | 1141 | })) => { |
| 1146 | // check if the RHS is from desugaring | |
| 1142 | // Check if the RHS is from desugaring. | |
| 1143 | let first_assignment = find_assignments(&self.body, local).first().copied(); | |
| 1144 | let first_assignment_stmt = first_assignment | |
| 1145 | .and_then(|loc| self.body[loc.block].statements.get(loc.statement_index)); | |
| 1146 | trace!(?first_assignment_stmt); | |
| 1147 | 1147 | let opt_assignment_rhs_span = |
| 1148 | self.find_assignments(local).first().map(|&location| { | |
| 1149 | if let Some(mir::Statement { | |
| 1150 | source_info: _, | |
| 1151 | kind: | |
| 1152 | mir::StatementKind::Assign(box ( | |
| 1153 | _, | |
| 1154 | mir::Rvalue::Use(mir::Operand::Copy(place)), | |
| 1155 | )), | |
| 1156 | .. | |
| 1157 | }) = self.body[location.block].statements.get(location.statement_index) | |
| 1158 | { | |
| 1159 | self.body.local_decls[place.local].source_info.span | |
| 1160 | } else { | |
| 1161 | self.body.source_info(location).span | |
| 1162 | } | |
| 1163 | }); | |
| 1164 | match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) { | |
| 1165 | // on for loops, RHS points to the iterator part | |
| 1166 | Some(DesugaringKind::ForLoop) => { | |
| 1167 | let span = opt_assignment_rhs_span.unwrap(); | |
| 1168 | self.suggest_similar_mut_method_for_for_loop(err, span); | |
| 1148 | first_assignment.map(|loc| self.body.source_info(loc).span); | |
| 1149 | let mut source_span = opt_assignment_rhs_span; | |
| 1150 | if let Some(mir::Statement { | |
| 1151 | source_info: _, | |
| 1152 | kind: | |
| 1153 | mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))), | |
| 1154 | .. | |
| 1155 | }) = first_assignment_stmt | |
| 1156 | { | |
| 1157 | let local_span = self.body.local_decls[place.local].source_info.span; | |
| 1158 | // `&self` in async functions have a `desugaring_kind`, but the local we assign | |
| 1159 | // it with does not, so use the local_span for our checks later. | |
| 1160 | source_span = Some(local_span); | |
| 1161 | if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() { | |
| 1162 | // On for loops, RHS points to the iterator part. | |
| 1163 | self.suggest_similar_mut_method_for_for_loop(err, local_span); | |
| 1169 | 1164 | err.span_label( |
| 1170 | span, | |
| 1165 | local_span, | |
| 1171 | 1166 | format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",), |
| 1172 | 1167 | ); |
| 1173 | None | |
| 1174 | } | |
| 1175 | // don't create labels for compiler-generated spans | |
| 1176 | Some(_) => None, | |
| 1177 | // don't create labels for the span not from user's code | |
| 1178 | None if opt_assignment_rhs_span | |
| 1179 | .is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) => | |
| 1180 | { | |
| 1181 | None | |
| 1182 | } | |
| 1183 | None => { | |
| 1184 | if name != kw::SelfLower { | |
| 1185 | suggest_ampmut( | |
| 1186 | self.infcx.tcx, | |
| 1187 | local_decl.ty, | |
| 1188 | decl_span, | |
| 1189 | opt_assignment_rhs_span, | |
| 1190 | opt_ty_info, | |
| 1191 | ) | |
| 1192 | } else { | |
| 1193 | match local_decl.local_info() { | |
| 1194 | LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { | |
| 1195 | opt_ty_info: None, | |
| 1196 | .. | |
| 1197 | })) => { | |
| 1198 | let (span, sugg) = | |
| 1199 | suggest_ampmut_self(self.infcx.tcx, decl_span); | |
| 1200 | Some(AmpMutSugg { | |
| 1201 | has_sugg: true, | |
| 1202 | span, | |
| 1203 | suggestion: sugg, | |
| 1204 | additional: None, | |
| 1205 | }) | |
| 1206 | } | |
| 1207 | // explicit self (eg `self: &'a Self`) | |
| 1208 | _ => suggest_ampmut( | |
| 1209 | self.infcx.tcx, | |
| 1210 | local_decl.ty, | |
| 1211 | decl_span, | |
| 1212 | opt_assignment_rhs_span, | |
| 1213 | opt_ty_info, | |
| 1214 | ), | |
| 1215 | } | |
| 1216 | } | |
| 1168 | return; | |
| 1217 | 1169 | } |
| 1218 | 1170 | } |
| 1171 | ||
| 1172 | // Don't create labels for compiler-generated spans or spans not from users' code. | |
| 1173 | if source_span.is_some_and(|s| { | |
| 1174 | s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s) | |
| 1175 | }) { | |
| 1176 | return; | |
| 1177 | } | |
| 1178 | ||
| 1179 | // This could be because we're in an `async fn`. | |
| 1180 | if name == kw::SelfLower && opt_ty_info.is_none() { | |
| 1181 | let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span); | |
| 1182 | (AmpMutSugg::Type { span, suggestion, additional: None }, None) | |
| 1183 | } else if let Some(sugg) = | |
| 1184 | suggest_ampmut(self.infcx, self.body(), first_assignment_stmt) | |
| 1185 | { | |
| 1186 | (sugg, opt_ty_info) | |
| 1187 | } else { | |
| 1188 | return; | |
| 1189 | } | |
| 1219 | 1190 | } |
| 1220 | 1191 | |
| 1221 | 1192 | LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { |
| ... | ... | @@ -1223,181 +1194,238 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 1223 | 1194 | .. |
| 1224 | 1195 | })) => { |
| 1225 | 1196 | let pattern_span: Span = local_decl.source_info.span; |
| 1226 | suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg { | |
| 1227 | has_sugg: true, | |
| 1228 | span, | |
| 1229 | suggestion: "mut ".to_owned(), | |
| 1230 | additional: None, | |
| 1231 | }) | |
| 1197 | let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else { | |
| 1198 | return; | |
| 1199 | }; | |
| 1200 | (AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None) | |
| 1232 | 1201 | } |
| 1233 | 1202 | |
| 1234 | 1203 | _ => unreachable!(), |
| 1235 | 1204 | }; |
| 1236 | 1205 | |
| 1237 | match amp_mut_sugg { | |
| 1238 | Some(AmpMutSugg { | |
| 1239 | has_sugg: true, | |
| 1240 | span: err_help_span, | |
| 1241 | suggestion: suggested_code, | |
| 1242 | additional, | |
| 1243 | }) => { | |
| 1244 | let mut sugg = vec![(err_help_span, suggested_code)]; | |
| 1245 | if let Some(s) = additional { | |
| 1246 | sugg.push(s); | |
| 1247 | } | |
| 1206 | let mut suggest = |suggs: Vec<_>, applicability, extra| { | |
| 1207 | if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) { | |
| 1208 | return; | |
| 1209 | } | |
| 1248 | 1210 | |
| 1249 | if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span)) | |
| 1250 | { | |
| 1251 | err.multipart_suggestion_verbose( | |
| 1252 | format!( | |
| 1253 | "consider changing this to be a mutable {pointer_desc}{}", | |
| 1254 | if is_trait_sig { | |
| 1255 | " in the `impl` method and the `trait` definition" | |
| 1256 | } else { | |
| 1257 | "" | |
| 1258 | } | |
| 1259 | ), | |
| 1260 | sugg, | |
| 1261 | Applicability::MachineApplicable, | |
| 1262 | ); | |
| 1211 | err.multipart_suggestion_verbose( | |
| 1212 | format!( | |
| 1213 | "consider changing this to be a mutable {pointer_desc}{}{extra}", | |
| 1214 | if is_trait_sig { | |
| 1215 | " in the `impl` method and the `trait` definition" | |
| 1216 | } else { | |
| 1217 | "" | |
| 1218 | } | |
| 1219 | ), | |
| 1220 | suggs, | |
| 1221 | applicability, | |
| 1222 | ); | |
| 1223 | }; | |
| 1224 | ||
| 1225 | let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg { | |
| 1226 | AmpMutSugg::Type { span, suggestion, additional } => { | |
| 1227 | let mut sugg = vec![(span, suggestion)]; | |
| 1228 | sugg.extend(additional); | |
| 1229 | suggest(sugg, Applicability::MachineApplicable, ""); | |
| 1230 | return; | |
| 1231 | } | |
| 1232 | AmpMutSugg::MapGetMut { span, suggestion } => { | |
| 1233 | if self.infcx.tcx.sess.source_map().is_imported(span) { | |
| 1234 | return; | |
| 1263 | 1235 | } |
| 1236 | err.multipart_suggestion_verbose( | |
| 1237 | "consider using `get_mut`", | |
| 1238 | vec![(span, suggestion)], | |
| 1239 | Applicability::MaybeIncorrect, | |
| 1240 | ); | |
| 1241 | return; | |
| 1264 | 1242 | } |
| 1265 | Some(AmpMutSugg { | |
| 1266 | has_sugg: false, span: err_label_span, suggestion: message, .. | |
| 1267 | }) => { | |
| 1268 | let def_id = self.body.source.def_id(); | |
| 1269 | let hir_id = if let Some(local_def_id) = def_id.as_local() | |
| 1270 | && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) | |
| 1271 | { | |
| 1272 | BindingFinder { span: err_label_span }.visit_body(&body).break_value() | |
| 1273 | } else { | |
| 1274 | None | |
| 1275 | }; | |
| 1243 | AmpMutSugg::Expr { span, suggestion } => { | |
| 1244 | // `Expr` suggestions should change type annotations if they already exist (probably immut), | |
| 1245 | // but do not add new type annotations. | |
| 1246 | (vec![(span, suggestion)], false) | |
| 1247 | } | |
| 1248 | AmpMutSugg::ChangeBinding => (vec![], true), | |
| 1249 | }; | |
| 1276 | 1250 | |
| 1277 | if let Some(hir_id) = hir_id | |
| 1278 | && let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id) | |
| 1279 | { | |
| 1280 | let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap()); | |
| 1281 | if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() | |
| 1282 | && let Some(expr) = local.init | |
| 1283 | && let ty = tables.node_type_opt(expr.hir_id) | |
| 1284 | && let Some(ty) = ty | |
| 1285 | && let ty::Ref(..) = ty.kind() | |
| 1251 | // Find a binding's type to make mutable. | |
| 1252 | let (binding_exists, span) = match local_var_ty_info { | |
| 1253 | // If this is a variable binding with an explicit type, | |
| 1254 | // then we will suggest changing it to be mutable. | |
| 1255 | // This is `Applicability::MachineApplicable`. | |
| 1256 | Some(ty_span) => (true, ty_span), | |
| 1257 | ||
| 1258 | // Otherwise, we'll suggest *adding* an annotated type, we'll suggest | |
| 1259 | // the RHS's type for that. | |
| 1260 | // This is `Applicability::HasPlaceholders`. | |
| 1261 | None => (false, decl_span), | |
| 1262 | }; | |
| 1263 | ||
| 1264 | if !binding_exists && !add_type_annotation_if_not_exists { | |
| 1265 | suggest(sugg, Applicability::MachineApplicable, ""); | |
| 1266 | return; | |
| 1267 | } | |
| 1268 | ||
| 1269 | // If the binding already exists and is a reference with an explicit | |
| 1270 | // lifetime, then we can suggest adding ` mut`. This is special-cased from | |
| 1271 | // the path without an explicit lifetime. | |
| 1272 | let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span) | |
| 1273 | && src.starts_with("&'") | |
| 1274 | // Note that `&' a T` is invalid so this is correct. | |
| 1275 | && let Some(ws_pos) = src.find(char::is_whitespace) | |
| 1276 | { | |
| 1277 | let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo(); | |
| 1278 | (span, " mut".to_owned(), true) | |
| 1279 | // If there is already a binding, we modify it to be `mut`. | |
| 1280 | } else if binding_exists { | |
| 1281 | // Shrink the span to just after the `&` in `&variable`. | |
| 1282 | let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo(); | |
| 1283 | (span, "mut ".to_owned(), true) | |
| 1284 | } else { | |
| 1285 | // Otherwise, suggest that the user annotates the binding; We provide the | |
| 1286 | // type of the local. | |
| 1287 | let ty = local_decl.ty.builtin_deref(true).unwrap(); | |
| 1288 | ||
| 1289 | (span, format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false) | |
| 1290 | }; | |
| 1291 | ||
| 1292 | if suggest_now { | |
| 1293 | // Suggest changing `&x` to `&mut x` and changing `&T` to `&mut T` at the same time. | |
| 1294 | let has_change = !sugg.is_empty(); | |
| 1295 | sugg.push((sugg_span, sugg_str)); | |
| 1296 | suggest( | |
| 1297 | sugg, | |
| 1298 | Applicability::MachineApplicable, | |
| 1299 | // FIXME(fee1-dead) this somehow doesn't fire | |
| 1300 | if has_change { " and changing the binding's type" } else { "" }, | |
| 1301 | ); | |
| 1302 | return; | |
| 1303 | } else if !sugg.is_empty() { | |
| 1304 | suggest(sugg, Applicability::MachineApplicable, ""); | |
| 1305 | return; | |
| 1306 | } | |
| 1307 | ||
| 1308 | let def_id = self.body.source.def_id(); | |
| 1309 | let hir_id = if let Some(local_def_id) = def_id.as_local() | |
| 1310 | && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) | |
| 1311 | { | |
| 1312 | BindingFinder { span: sugg_span }.visit_body(&body).break_value() | |
| 1313 | } else { | |
| 1314 | None | |
| 1315 | }; | |
| 1316 | let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id)); | |
| 1317 | ||
| 1318 | let Some(hir::Node::LetStmt(local)) = node else { | |
| 1319 | err.span_label( | |
| 1320 | sugg_span, | |
| 1321 | format!("consider changing this binding's type to be: `{sugg_str}`"), | |
| 1322 | ); | |
| 1323 | return; | |
| 1324 | }; | |
| 1325 | ||
| 1326 | let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap()); | |
| 1327 | if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() | |
| 1328 | && let Some(expr) = local.init | |
| 1329 | && let ty = tables.node_type_opt(expr.hir_id) | |
| 1330 | && let Some(ty) = ty | |
| 1331 | && let ty::Ref(..) = ty.kind() | |
| 1332 | { | |
| 1333 | match self | |
| 1334 | .infcx | |
| 1335 | .type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env) | |
| 1336 | .as_deref() | |
| 1337 | { | |
| 1338 | Some([]) => { | |
| 1339 | // FIXME: This error message isn't useful, since we're just | |
| 1340 | // vaguely suggesting to clone a value that already | |
| 1341 | // implements `Clone`. | |
| 1342 | // | |
| 1343 | // A correct suggestion here would take into account the fact | |
| 1344 | // that inference may be affected by missing types on bindings, | |
| 1345 | // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for | |
| 1346 | // example. | |
| 1347 | } | |
| 1348 | None => { | |
| 1349 | if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind | |
| 1350 | && segment.ident.name == sym::clone | |
| 1286 | 1351 | { |
| 1287 | match self | |
| 1288 | .infcx | |
| 1289 | .type_implements_trait_shallow( | |
| 1290 | clone_trait, | |
| 1291 | ty.peel_refs(), | |
| 1292 | self.infcx.param_env, | |
| 1293 | ) | |
| 1294 | .as_deref() | |
| 1295 | { | |
| 1296 | Some([]) => { | |
| 1297 | // FIXME: This error message isn't useful, since we're just | |
| 1298 | // vaguely suggesting to clone a value that already | |
| 1299 | // implements `Clone`. | |
| 1300 | // | |
| 1301 | // A correct suggestion here would take into account the fact | |
| 1302 | // that inference may be affected by missing types on bindings, | |
| 1303 | // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for | |
| 1304 | // example. | |
| 1305 | } | |
| 1306 | None => { | |
| 1307 | if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = | |
| 1308 | expr.kind | |
| 1309 | && segment.ident.name == sym::clone | |
| 1310 | { | |
| 1311 | err.span_help( | |
| 1312 | span, | |
| 1313 | format!( | |
| 1314 | "`{}` doesn't implement `Clone`, so this call clones \ | |
| 1352 | err.span_help( | |
| 1353 | span, | |
| 1354 | format!( | |
| 1355 | "`{}` doesn't implement `Clone`, so this call clones \ | |
| 1315 | 1356 | the reference `{ty}`", |
| 1316 | ty.peel_refs(), | |
| 1317 | ), | |
| 1318 | ); | |
| 1319 | } | |
| 1320 | // The type doesn't implement Clone. | |
| 1321 | let trait_ref = ty::Binder::dummy(ty::TraitRef::new( | |
| 1322 | self.infcx.tcx, | |
| 1323 | clone_trait, | |
| 1324 | [ty.peel_refs()], | |
| 1325 | )); | |
| 1326 | let obligation = traits::Obligation::new( | |
| 1327 | self.infcx.tcx, | |
| 1328 | traits::ObligationCause::dummy(), | |
| 1329 | self.infcx.param_env, | |
| 1330 | trait_ref, | |
| 1331 | ); | |
| 1332 | self.infcx.err_ctxt().suggest_derive( | |
| 1333 | &obligation, | |
| 1334 | err, | |
| 1335 | trait_ref.upcast(self.infcx.tcx), | |
| 1336 | ); | |
| 1337 | } | |
| 1338 | Some(errors) => { | |
| 1339 | if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = | |
| 1340 | expr.kind | |
| 1341 | && segment.ident.name == sym::clone | |
| 1342 | { | |
| 1343 | err.span_help( | |
| 1344 | span, | |
| 1345 | format!( | |
| 1346 | "`{}` doesn't implement `Clone` because its \ | |
| 1357 | ty.peel_refs(), | |
| 1358 | ), | |
| 1359 | ); | |
| 1360 | } | |
| 1361 | // The type doesn't implement Clone. | |
| 1362 | let trait_ref = ty::Binder::dummy(ty::TraitRef::new( | |
| 1363 | self.infcx.tcx, | |
| 1364 | clone_trait, | |
| 1365 | [ty.peel_refs()], | |
| 1366 | )); | |
| 1367 | let obligation = traits::Obligation::new( | |
| 1368 | self.infcx.tcx, | |
| 1369 | traits::ObligationCause::dummy(), | |
| 1370 | self.infcx.param_env, | |
| 1371 | trait_ref, | |
| 1372 | ); | |
| 1373 | self.infcx.err_ctxt().suggest_derive( | |
| 1374 | &obligation, | |
| 1375 | err, | |
| 1376 | trait_ref.upcast(self.infcx.tcx), | |
| 1377 | ); | |
| 1378 | } | |
| 1379 | Some(errors) => { | |
| 1380 | if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind | |
| 1381 | && segment.ident.name == sym::clone | |
| 1382 | { | |
| 1383 | err.span_help( | |
| 1384 | span, | |
| 1385 | format!( | |
| 1386 | "`{}` doesn't implement `Clone` because its \ | |
| 1347 | 1387 | implementations trait bounds could not be met, so \ |
| 1348 | 1388 | this call clones the reference `{ty}`", |
| 1349 | ty.peel_refs(), | |
| 1350 | ), | |
| 1351 | ); | |
| 1352 | err.note(format!( | |
| 1353 | "the following trait bounds weren't met: {}", | |
| 1354 | errors | |
| 1355 | .iter() | |
| 1356 | .map(|e| e.obligation.predicate.to_string()) | |
| 1357 | .collect::<Vec<_>>() | |
| 1358 | .join("\n"), | |
| 1359 | )); | |
| 1360 | } | |
| 1361 | // The type doesn't implement Clone because of unmet obligations. | |
| 1362 | for error in errors { | |
| 1363 | if let traits::FulfillmentErrorCode::Select( | |
| 1364 | traits::SelectionError::Unimplemented, | |
| 1365 | ) = error.code | |
| 1366 | && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( | |
| 1367 | pred, | |
| 1368 | )) = error.obligation.predicate.kind().skip_binder() | |
| 1369 | { | |
| 1370 | self.infcx.err_ctxt().suggest_derive( | |
| 1371 | &error.obligation, | |
| 1372 | err, | |
| 1373 | error.obligation.predicate.kind().rebind(pred), | |
| 1374 | ); | |
| 1375 | } | |
| 1376 | } | |
| 1377 | } | |
| 1378 | } | |
| 1389 | ty.peel_refs(), | |
| 1390 | ), | |
| 1391 | ); | |
| 1392 | err.note(format!( | |
| 1393 | "the following trait bounds weren't met: {}", | |
| 1394 | errors | |
| 1395 | .iter() | |
| 1396 | .map(|e| e.obligation.predicate.to_string()) | |
| 1397 | .collect::<Vec<_>>() | |
| 1398 | .join("\n"), | |
| 1399 | )); | |
| 1379 | 1400 | } |
| 1380 | let (changing, span, sugg) = match local.ty { | |
| 1381 | Some(ty) => ("changing", ty.span, message), | |
| 1382 | None => { | |
| 1383 | ("specifying", local.pat.span.shrink_to_hi(), format!(": {message}")) | |
| 1401 | // The type doesn't implement Clone because of unmet obligations. | |
| 1402 | for error in errors { | |
| 1403 | if let traits::FulfillmentErrorCode::Select( | |
| 1404 | traits::SelectionError::Unimplemented, | |
| 1405 | ) = error.code | |
| 1406 | && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = | |
| 1407 | error.obligation.predicate.kind().skip_binder() | |
| 1408 | { | |
| 1409 | self.infcx.err_ctxt().suggest_derive( | |
| 1410 | &error.obligation, | |
| 1411 | err, | |
| 1412 | error.obligation.predicate.kind().rebind(pred), | |
| 1413 | ); | |
| 1384 | 1414 | } |
| 1385 | }; | |
| 1386 | err.span_suggestion_verbose( | |
| 1387 | span, | |
| 1388 | format!("consider {changing} this binding's type"), | |
| 1389 | sugg, | |
| 1390 | Applicability::HasPlaceholders, | |
| 1391 | ); | |
| 1392 | } else { | |
| 1393 | err.span_label( | |
| 1394 | err_label_span, | |
| 1395 | format!("consider changing this binding's type to be: `{message}`"), | |
| 1396 | ); | |
| 1415 | } | |
| 1397 | 1416 | } |
| 1398 | 1417 | } |
| 1399 | None => {} | |
| 1400 | 1418 | } |
| 1419 | let (changing, span, sugg) = match local.ty { | |
| 1420 | Some(ty) => ("changing", ty.span, sugg_str), | |
| 1421 | None => ("specifying", local.pat.span.shrink_to_hi(), format!(": {sugg_str}")), | |
| 1422 | }; | |
| 1423 | err.span_suggestion_verbose( | |
| 1424 | span, | |
| 1425 | format!("consider {changing} this binding's type"), | |
| 1426 | sugg, | |
| 1427 | Applicability::HasPlaceholders, | |
| 1428 | ); | |
| 1401 | 1429 | } |
| 1402 | 1430 | } |
| 1403 | 1431 | |
| ... | ... | @@ -1464,11 +1492,25 @@ fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) { |
| 1464 | 1492 | } |
| 1465 | 1493 | } |
| 1466 | 1494 | |
| 1467 | struct AmpMutSugg { | |
| 1468 | has_sugg: bool, | |
| 1469 | span: Span, | |
| 1470 | suggestion: String, | |
| 1471 | additional: Option<(Span, String)>, | |
| 1495 | enum AmpMutSugg { | |
| 1496 | /// Type suggestion. Changes `&self` to `&mut self`, `x: &T` to `x: &mut T`, | |
| 1497 | /// `ref x` to `ref mut x`, etc. | |
| 1498 | Type { | |
| 1499 | span: Span, | |
| 1500 | suggestion: String, | |
| 1501 | additional: Option<(Span, String)>, | |
| 1502 | }, | |
| 1503 | /// Suggestion for expressions, `&x` to `&mut x`, `&x[i]` to `&mut x[i]`, etc. | |
| 1504 | Expr { | |
| 1505 | span: Span, | |
| 1506 | suggestion: String, | |
| 1507 | }, | |
| 1508 | /// Suggests `.get_mut` in the case of `&map[&key]` for Hash/BTreeMap. | |
| 1509 | MapGetMut { | |
| 1510 | span: Span, | |
| 1511 | suggestion: String, | |
| 1512 | }, | |
| 1513 | ChangeBinding, | |
| 1472 | 1514 | } |
| 1473 | 1515 | |
| 1474 | 1516 | // When we want to suggest a user change a local variable to be a `&mut`, there |
| ... | ... | @@ -1487,110 +1529,111 @@ struct AmpMutSugg { |
| 1487 | 1529 | // This implementation attempts to emulate AST-borrowck prioritization |
| 1488 | 1530 | // by trying (3.), then (2.) and finally falling back on (1.). |
| 1489 | 1531 | fn suggest_ampmut<'tcx>( |
| 1490 | tcx: TyCtxt<'tcx>, | |
| 1491 | decl_ty: Ty<'tcx>, | |
| 1492 | decl_span: Span, | |
| 1493 | opt_assignment_rhs_span: Option<Span>, | |
| 1494 | opt_ty_info: Option<Span>, | |
| 1532 | infcx: &crate::BorrowckInferCtxt<'tcx>, | |
| 1533 | body: &Body<'tcx>, | |
| 1534 | opt_assignment_rhs_stmt: Option<&Statement<'tcx>>, | |
| 1495 | 1535 | ) -> Option<AmpMutSugg> { |
| 1496 | // if there is a RHS and it starts with a `&` from it, then check if it is | |
| 1536 | let tcx = infcx.tcx; | |
| 1537 | // If there is a RHS and it starts with a `&` from it, then check if it is | |
| 1497 | 1538 | // mutable, and if not, put suggest putting `mut ` to make it mutable. |
| 1498 | // we don't have to worry about lifetime annotations here because they are | |
| 1539 | // We don't have to worry about lifetime annotations here because they are | |
| 1499 | 1540 | // not valid when taking a reference. For example, the following is not valid Rust: |
| 1500 | 1541 | // |
| 1501 | 1542 | // let x: &i32 = &'a 5; |
| 1502 | 1543 | // ^^ lifetime annotation not allowed |
| 1503 | 1544 | // |
| 1504 | if let Some(rhs_span) = opt_assignment_rhs_span | |
| 1505 | && let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span) | |
| 1506 | && let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&') | |
| 1545 | if let Some(rhs_stmt) = opt_assignment_rhs_stmt | |
| 1546 | && let StatementKind::Assign(box (lhs, rvalue)) = &rhs_stmt.kind | |
| 1547 | && let mut rhs_span = rhs_stmt.source_info.span | |
| 1548 | && let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span) | |
| 1507 | 1549 | { |
| 1508 | // Suggest changing `&raw const` to `&raw mut` if applicable. | |
| 1509 | if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() { | |
| 1510 | let const_idx = rhs_str.find("const").unwrap() as u32; | |
| 1511 | let const_span = rhs_span | |
| 1512 | .with_lo(rhs_span.lo() + BytePos(const_idx)) | |
| 1513 | .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)); | |
| 1514 | ||
| 1515 | return Some(AmpMutSugg { | |
| 1516 | has_sugg: true, | |
| 1517 | span: const_span, | |
| 1518 | suggestion: "mut".to_owned(), | |
| 1519 | additional: None, | |
| 1520 | }); | |
| 1550 | let mut rvalue = rvalue; | |
| 1551 | ||
| 1552 | // Take some special care when handling `let _x = &*_y`: | |
| 1553 | // We want to know if this is part of an overloaded index, so `let x = &a[0]`, | |
| 1554 | // or whether this is a usertype ascription (`let _x: &T = y`). | |
| 1555 | if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue | |
| 1556 | && place.projection.len() == 1 | |
| 1557 | && place.projection[0] == ProjectionElem::Deref | |
| 1558 | && let Some(assign) = find_assignments(&body, place.local).first() | |
| 1559 | { | |
| 1560 | // If this is a usertype ascription (`let _x: &T = _y`) then pierce through it as either we want | |
| 1561 | // to suggest `&mut` on the expression (handled here) or we return `None` and let the caller | |
| 1562 | // suggest `&mut` on the type if the expression seems fine (e.g. `let _x: &T = &mut _y`). | |
| 1563 | if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref() | |
| 1564 | && let [user_ty_proj] = user_ty_projs.contents.as_slice() | |
| 1565 | && user_ty_proj.projs.is_empty() | |
| 1566 | && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign) | |
| 1567 | && let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind | |
| 1568 | && let rhs_span_new = rhs_stmt_new.source_info.span | |
| 1569 | && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span) | |
| 1570 | { | |
| 1571 | (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new); | |
| 1572 | } | |
| 1573 | ||
| 1574 | if let Either::Right(call) = body.stmt_at(*assign) | |
| 1575 | && let TerminatorKind::Call { | |
| 1576 | func: Operand::Constant(box const_operand), args, .. | |
| 1577 | } = &call.kind | |
| 1578 | && let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind() | |
| 1579 | && let Some(trait_) = tcx.trait_of_assoc(method_def_id) | |
| 1580 | && tcx.is_lang_item(trait_, hir::LangItem::Index) | |
| 1581 | { | |
| 1582 | let trait_ref = ty::TraitRef::from_assoc( | |
| 1583 | tcx, | |
| 1584 | tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span), | |
| 1585 | method_args, | |
| 1586 | ); | |
| 1587 | // The type only implements `Index` but not `IndexMut`, we must not suggest `&mut`. | |
| 1588 | if !infcx | |
| 1589 | .type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env) | |
| 1590 | .must_apply_considering_regions() | |
| 1591 | { | |
| 1592 | // Suggest `get_mut` if type is a `BTreeMap` or `HashMap`. | |
| 1593 | if let ty::Adt(def, _) = trait_ref.self_ty().kind() | |
| 1594 | && [sym::BTreeMap, sym::HashMap] | |
| 1595 | .into_iter() | |
| 1596 | .any(|s| tcx.is_diagnostic_item(s, def.did())) | |
| 1597 | && let [map, key] = &**args | |
| 1598 | && let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span) | |
| 1599 | && let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span) | |
| 1600 | { | |
| 1601 | let span = rhs_span; | |
| 1602 | let suggestion = format!("{map}.get_mut({key}).unwrap()"); | |
| 1603 | return Some(AmpMutSugg::MapGetMut { span, suggestion }); | |
| 1604 | } | |
| 1605 | return None; | |
| 1606 | } | |
| 1607 | } | |
| 1521 | 1608 | } |
| 1522 | 1609 | |
| 1523 | // Figure out if rhs already is `&mut`. | |
| 1524 | let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") { | |
| 1525 | match rest.chars().next() { | |
| 1526 | // e.g. `&mut x` | |
| 1527 | Some(c) if c.is_whitespace() => true, | |
| 1528 | // e.g. `&mut(x)` | |
| 1529 | Some('(') => true, | |
| 1530 | // e.g. `&mut{x}` | |
| 1531 | Some('{') => true, | |
| 1532 | // e.g. `&mutablevar` | |
| 1533 | _ => false, | |
| 1610 | let sugg = match rvalue { | |
| 1611 | Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => { | |
| 1612 | // Shrink the span to just after the `&` in `&variable`. | |
| 1613 | Some(( | |
| 1614 | rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(), | |
| 1615 | "mut ".to_owned(), | |
| 1616 | )) | |
| 1534 | 1617 | } |
| 1535 | } else { | |
| 1536 | false | |
| 1618 | Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => { | |
| 1619 | // Suggest changing `&raw const` to `&raw mut` if applicable. | |
| 1620 | let const_idx = const_idx as u32; | |
| 1621 | Some(( | |
| 1622 | rhs_span | |
| 1623 | .with_lo(rhs_span.lo() + BytePos(const_idx)) | |
| 1624 | .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)), | |
| 1625 | "mut".to_owned(), | |
| 1626 | )) | |
| 1627 | } | |
| 1628 | _ => None, | |
| 1537 | 1629 | }; |
| 1538 | // if the reference is already mutable then there is nothing we can do | |
| 1539 | // here. | |
| 1540 | if !is_mut { | |
| 1541 | // shrink the span to just after the `&` in `&variable` | |
| 1542 | let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo(); | |
| 1543 | ||
| 1544 | // FIXME(Ezrashaw): returning is bad because we still might want to | |
| 1545 | // update the annotated type, see #106857. | |
| 1546 | return Some(AmpMutSugg { | |
| 1547 | has_sugg: true, | |
| 1548 | span, | |
| 1549 | suggestion: "mut ".to_owned(), | |
| 1550 | additional: None, | |
| 1551 | }); | |
| 1630 | ||
| 1631 | if let Some((span, suggestion)) = sugg { | |
| 1632 | return Some(AmpMutSugg::Expr { span, suggestion }); | |
| 1552 | 1633 | } |
| 1553 | 1634 | } |
| 1554 | 1635 | |
| 1555 | let (binding_exists, span) = match opt_ty_info { | |
| 1556 | // if this is a variable binding with an explicit type, | |
| 1557 | // then we will suggest changing it to be mutable. | |
| 1558 | // this is `Applicability::MachineApplicable`. | |
| 1559 | Some(ty_span) => (true, ty_span), | |
| 1560 | ||
| 1561 | // otherwise, we'll suggest *adding* an annotated type, we'll suggest | |
| 1562 | // the RHS's type for that. | |
| 1563 | // this is `Applicability::HasPlaceholders`. | |
| 1564 | None => (false, decl_span), | |
| 1565 | }; | |
| 1566 | ||
| 1567 | // if the binding already exists and is a reference with an explicit | |
| 1568 | // lifetime, then we can suggest adding ` mut`. this is special-cased from | |
| 1569 | // the path without an explicit lifetime. | |
| 1570 | if let Ok(src) = tcx.sess.source_map().span_to_snippet(span) | |
| 1571 | && src.starts_with("&'") | |
| 1572 | // note that `& 'a T` is invalid so this is correct. | |
| 1573 | && let Some(ws_pos) = src.find(char::is_whitespace) | |
| 1574 | { | |
| 1575 | let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo(); | |
| 1576 | Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None }) | |
| 1577 | // if there is already a binding, we modify it to be `mut` | |
| 1578 | } else if binding_exists { | |
| 1579 | // shrink the span to just after the `&` in `&variable` | |
| 1580 | let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo(); | |
| 1581 | Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None }) | |
| 1582 | } else { | |
| 1583 | // otherwise, suggest that the user annotates the binding; we provide the | |
| 1584 | // type of the local. | |
| 1585 | let ty = decl_ty.builtin_deref(true).unwrap(); | |
| 1586 | ||
| 1587 | Some(AmpMutSugg { | |
| 1588 | has_sugg: false, | |
| 1589 | span, | |
| 1590 | suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty), | |
| 1591 | additional: None, | |
| 1592 | }) | |
| 1593 | } | |
| 1636 | Some(AmpMutSugg::ChangeBinding) | |
| 1594 | 1637 | } |
| 1595 | 1638 | |
| 1596 | 1639 | /// If the type is a `Coroutine`, `Closure`, or `CoroutineClosure` |
compiler/rustc_borrowck/src/diagnostics/region_errors.rs+20-4| ... | ... | @@ -215,7 +215,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 215 | 215 | diag: &mut Diag<'_>, |
| 216 | 216 | lower_bound: RegionVid, |
| 217 | 217 | ) { |
| 218 | let mut suggestions = vec![]; | |
| 219 | 218 | let tcx = self.infcx.tcx; |
| 220 | 219 | |
| 221 | 220 | // find generic associated types in the given region 'lower_bound' |
| ... | ... | @@ -237,9 +236,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 237 | 236 | .collect::<Vec<_>>(); |
| 238 | 237 | debug!(?gat_id_and_generics); |
| 239 | 238 | |
| 240 | // find higher-ranked trait bounds bounded to the generic associated types | |
| 239 | // Look for the where-bound which introduces the placeholder. | |
| 240 | // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>` | |
| 241 | // and `T: for<'a> Trait`<'a>. | |
| 241 | 242 | let mut hrtb_bounds = vec![]; |
| 242 | gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| { | |
| 243 | gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| { | |
| 243 | 244 | for pred in generics.predicates { |
| 244 | 245 | let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = |
| 245 | 246 | pred.kind |
| ... | ... | @@ -248,17 +249,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { |
| 248 | 249 | }; |
| 249 | 250 | if bound_generic_params |
| 250 | 251 | .iter() |
| 251 | .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id) | |
| 252 | .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) | |
| 252 | 253 | .is_some() |
| 253 | 254 | { |
| 254 | 255 | for bound in *bounds { |
| 255 | 256 | hrtb_bounds.push(bound); |
| 256 | 257 | } |
| 258 | } else { | |
| 259 | for bound in *bounds { | |
| 260 | if let Trait(trait_bound) = bound { | |
| 261 | if trait_bound | |
| 262 | .bound_generic_params | |
| 263 | .iter() | |
| 264 | .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) | |
| 265 | .is_some() | |
| 266 | { | |
| 267 | hrtb_bounds.push(bound); | |
| 268 | return; | |
| 269 | } | |
| 270 | } | |
| 271 | } | |
| 257 | 272 | } |
| 258 | 273 | } |
| 259 | 274 | }); |
| 260 | 275 | debug!(?hrtb_bounds); |
| 261 | 276 | |
| 277 | let mut suggestions = vec![]; | |
| 262 | 278 | hrtb_bounds.iter().for_each(|bound| { |
| 263 | 279 | let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else { |
| 264 | 280 | return; |
compiler/rustc_codegen_llvm/src/back/archive.rs+6-177| ... | ... | @@ -1,104 +1,21 @@ |
| 1 | 1 | //! A helper class for dealing with static archives |
| 2 | 2 | |
| 3 | use std::ffi::{CStr, CString, c_char, c_void}; | |
| 4 | use std::path::{Path, PathBuf}; | |
| 5 | use std::{io, mem, ptr, str}; | |
| 3 | use std::ffi::{CStr, c_char, c_void}; | |
| 4 | use std::io; | |
| 6 | 5 | |
| 7 | 6 | use rustc_codegen_ssa::back::archive::{ |
| 8 | ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, | |
| 9 | DEFAULT_OBJECT_READER, ObjectReader, UnknownArchiveKind, try_extract_macho_fat_archive, | |
| 7 | ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, ObjectReader, | |
| 10 | 8 | }; |
| 11 | 9 | use rustc_session::Session; |
| 12 | 10 | |
| 13 | use crate::llvm::archive_ro::{ArchiveRO, Child}; | |
| 14 | use crate::llvm::{self, ArchiveKind, last_error}; | |
| 15 | ||
| 16 | /// Helper for adding many files to an archive. | |
| 17 | #[must_use = "must call build() to finish building the archive"] | |
| 18 | pub(crate) struct LlvmArchiveBuilder<'a> { | |
| 19 | sess: &'a Session, | |
| 20 | additions: Vec<Addition>, | |
| 21 | } | |
| 22 | ||
| 23 | enum Addition { | |
| 24 | File { path: PathBuf, name_in_archive: String }, | |
| 25 | Archive { path: PathBuf, archive: ArchiveRO, skip: Box<dyn FnMut(&str) -> bool> }, | |
| 26 | } | |
| 27 | ||
| 28 | impl Addition { | |
| 29 | fn path(&self) -> &Path { | |
| 30 | match self { | |
| 31 | Addition::File { path, .. } | Addition::Archive { path, .. } => path, | |
| 32 | } | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | fn is_relevant_child(c: &Child<'_>) -> bool { | |
| 37 | match c.name() { | |
| 38 | Some(name) => !name.contains("SYMDEF"), | |
| 39 | None => false, | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | impl<'a> ArchiveBuilder for LlvmArchiveBuilder<'a> { | |
| 44 | fn add_archive( | |
| 45 | &mut self, | |
| 46 | archive: &Path, | |
| 47 | skip: Box<dyn FnMut(&str) -> bool + 'static>, | |
| 48 | ) -> io::Result<()> { | |
| 49 | let mut archive = archive.to_path_buf(); | |
| 50 | if self.sess.target.llvm_target.contains("-apple-macosx") { | |
| 51 | if let Some(new_archive) = try_extract_macho_fat_archive(self.sess, &archive)? { | |
| 52 | archive = new_archive | |
| 53 | } | |
| 54 | } | |
| 55 | let archive_ro = match ArchiveRO::open(&archive) { | |
| 56 | Ok(ar) => ar, | |
| 57 | Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), | |
| 58 | }; | |
| 59 | if self.additions.iter().any(|ar| ar.path() == archive) { | |
| 60 | return Ok(()); | |
| 61 | } | |
| 62 | self.additions.push(Addition::Archive { | |
| 63 | path: archive, | |
| 64 | archive: archive_ro, | |
| 65 | skip: Box::new(skip), | |
| 66 | }); | |
| 67 | Ok(()) | |
| 68 | } | |
| 69 | ||
| 70 | /// Adds an arbitrary file to this archive | |
| 71 | fn add_file(&mut self, file: &Path) { | |
| 72 | let name = file.file_name().unwrap().to_str().unwrap(); | |
| 73 | self.additions | |
| 74 | .push(Addition::File { path: file.to_path_buf(), name_in_archive: name.to_owned() }); | |
| 75 | } | |
| 76 | ||
| 77 | /// Combine the provided files, rlibs, and native libraries into a single | |
| 78 | /// `Archive`. | |
| 79 | fn build(mut self: Box<Self>, output: &Path) -> bool { | |
| 80 | match self.build_with_llvm(output) { | |
| 81 | Ok(any_members) => any_members, | |
| 82 | Err(error) => { | |
| 83 | self.sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error }) | |
| 84 | } | |
| 85 | } | |
| 86 | } | |
| 87 | } | |
| 11 | use crate::llvm; | |
| 88 | 12 | |
| 89 | 13 | pub(crate) struct LlvmArchiveBuilderBuilder; |
| 90 | 14 | |
| 91 | 15 | impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { |
| 92 | 16 | fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> { |
| 93 | // Keeping LlvmArchiveBuilder around in case of a regression caused by using | |
| 94 | // ArArchiveBuilder. | |
| 95 | // FIXME(#128955) remove a couple of months after #128936 gets merged in case | |
| 96 | // no regression is found. | |
| 97 | if false { | |
| 98 | Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() }) | |
| 99 | } else { | |
| 100 | Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER)) | |
| 101 | } | |
| 17 | // Use the `object` crate to build archives, with a little bit of help from LLVM. | |
| 18 | Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER)) | |
| 102 | 19 | } |
| 103 | 20 | } |
| 104 | 21 | |
| ... | ... | @@ -178,91 +95,3 @@ fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool { |
| 178 | 95 | fn llvm_is_ec_object_file(buf: &[u8]) -> bool { |
| 179 | 96 | unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) } |
| 180 | 97 | } |
| 181 | ||
| 182 | impl<'a> LlvmArchiveBuilder<'a> { | |
| 183 | fn build_with_llvm(&mut self, output: &Path) -> io::Result<bool> { | |
| 184 | let kind = &*self.sess.target.archive_format; | |
| 185 | let kind = kind | |
| 186 | .parse::<ArchiveKind>() | |
| 187 | .map_err(|_| kind) | |
| 188 | .unwrap_or_else(|kind| self.sess.dcx().emit_fatal(UnknownArchiveKind { kind })); | |
| 189 | ||
| 190 | let mut additions = mem::take(&mut self.additions); | |
| 191 | // Values in the `members` list below will contain pointers to the strings allocated here. | |
| 192 | // So they need to get dropped after all elements of `members` get freed. | |
| 193 | let mut strings = Vec::new(); | |
| 194 | let mut members = Vec::new(); | |
| 195 | ||
| 196 | let dst = CString::new(output.to_str().unwrap())?; | |
| 197 | ||
| 198 | unsafe { | |
| 199 | for addition in &mut additions { | |
| 200 | match addition { | |
| 201 | Addition::File { path, name_in_archive } => { | |
| 202 | let path = CString::new(path.to_str().unwrap())?; | |
| 203 | let name = CString::new(name_in_archive.as_bytes())?; | |
| 204 | members.push(llvm::LLVMRustArchiveMemberNew( | |
| 205 | path.as_ptr(), | |
| 206 | name.as_ptr(), | |
| 207 | None, | |
| 208 | )); | |
| 209 | strings.push(path); | |
| 210 | strings.push(name); | |
| 211 | } | |
| 212 | Addition::Archive { archive, skip, .. } => { | |
| 213 | for child in archive.iter() { | |
| 214 | let child = child.map_err(string_to_io_error)?; | |
| 215 | if !is_relevant_child(&child) { | |
| 216 | continue; | |
| 217 | } | |
| 218 | let child_name = child.name().unwrap(); | |
| 219 | if skip(child_name) { | |
| 220 | continue; | |
| 221 | } | |
| 222 | ||
| 223 | // It appears that LLVM's archive writer is a little | |
| 224 | // buggy if the name we pass down isn't just the | |
| 225 | // filename component, so chop that off here and | |
| 226 | // pass it in. | |
| 227 | // | |
| 228 | // See LLVM bug 25877 for more info. | |
| 229 | let child_name = | |
| 230 | Path::new(child_name).file_name().unwrap().to_str().unwrap(); | |
| 231 | let name = CString::new(child_name)?; | |
| 232 | let m = llvm::LLVMRustArchiveMemberNew( | |
| 233 | ptr::null(), | |
| 234 | name.as_ptr(), | |
| 235 | Some(child.raw), | |
| 236 | ); | |
| 237 | members.push(m); | |
| 238 | strings.push(name); | |
| 239 | } | |
| 240 | } | |
| 241 | } | |
| 242 | } | |
| 243 | ||
| 244 | let r = llvm::LLVMRustWriteArchive( | |
| 245 | dst.as_ptr(), | |
| 246 | members.len() as libc::size_t, | |
| 247 | members.as_ptr() as *const &_, | |
| 248 | true, | |
| 249 | kind, | |
| 250 | self.sess.target.arch == "arm64ec", | |
| 251 | ); | |
| 252 | let ret = if r.into_result().is_err() { | |
| 253 | let msg = last_error().unwrap_or_else(|| "failed to write archive".into()); | |
| 254 | Err(io::Error::new(io::ErrorKind::Other, msg)) | |
| 255 | } else { | |
| 256 | Ok(!members.is_empty()) | |
| 257 | }; | |
| 258 | for member in members { | |
| 259 | llvm::LLVMRustArchiveMemberFree(member); | |
| 260 | } | |
| 261 | ret | |
| 262 | } | |
| 263 | } | |
| 264 | } | |
| 265 | ||
| 266 | fn string_to_io_error(s: String) -> io::Error { | |
| 267 | io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}")) | |
| 268 | } |
compiler/rustc_codegen_llvm/src/back/mod.rs created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | pub(crate) mod archive; | |
| 2 | pub(crate) mod lto; | |
| 3 | pub(crate) mod owned_target_machine; | |
| 4 | mod profiling; | |
| 5 | pub(crate) mod write; |
compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs+7-8| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | use std::ffi::{CStr, c_char}; | |
| 1 | use std::assert_matches::assert_matches; | |
| 2 | use std::ffi::CStr; | |
| 2 | 3 | use std::marker::PhantomData; |
| 3 | 4 | use std::ptr::NonNull; |
| 4 | 5 | |
| ... | ... | @@ -41,11 +42,9 @@ impl OwnedTargetMachine { |
| 41 | 42 | args_cstr_buff: &[u8], |
| 42 | 43 | use_wasm_eh: bool, |
| 43 | 44 | ) -> Result<Self, LlvmError<'static>> { |
| 44 | assert!(args_cstr_buff.len() > 0); | |
| 45 | assert!( | |
| 46 | *args_cstr_buff.last().unwrap() == 0, | |
| 47 | "The last character must be a null terminator." | |
| 48 | ); | |
| 45 | // The argument list is passed as the concatenation of one or more C strings. | |
| 46 | // This implies that there must be a last byte, and it must be 0. | |
| 47 | assert_matches!(args_cstr_buff, [.., b'\0'], "the last byte must be a NUL terminator"); | |
| 49 | 48 | |
| 50 | 49 | // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data |
| 51 | 50 | let tm_ptr = unsafe { |
| ... | ... | @@ -71,7 +70,7 @@ impl OwnedTargetMachine { |
| 71 | 70 | output_obj_file.as_ptr(), |
| 72 | 71 | debug_info_compression.as_ptr(), |
| 73 | 72 | use_emulated_tls, |
| 74 | args_cstr_buff.as_ptr() as *const c_char, | |
| 73 | args_cstr_buff.as_ptr(), | |
| 75 | 74 | args_cstr_buff.len(), |
| 76 | 75 | use_wasm_eh, |
| 77 | 76 | ) |
| ... | ... | @@ -99,7 +98,7 @@ impl Drop for OwnedTargetMachine { |
| 99 | 98 | // llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no |
| 100 | 99 | // double free or use after free. |
| 101 | 100 | unsafe { |
| 102 | llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_mut()); | |
| 101 | llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr()); | |
| 103 | 102 | } |
| 104 | 103 | } |
| 105 | 104 | } |
compiler/rustc_codegen_llvm/src/lib.rs+1-8| ... | ... | @@ -46,18 +46,11 @@ use rustc_session::Session; |
| 46 | 46 | use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; |
| 47 | 47 | use rustc_span::Symbol; |
| 48 | 48 | |
| 49 | mod back { | |
| 50 | pub(crate) mod archive; | |
| 51 | pub(crate) mod lto; | |
| 52 | pub(crate) mod owned_target_machine; | |
| 53 | mod profiling; | |
| 54 | pub(crate) mod write; | |
| 55 | } | |
| 56 | ||
| 57 | 49 | mod abi; |
| 58 | 50 | mod allocator; |
| 59 | 51 | mod asm; |
| 60 | 52 | mod attributes; |
| 53 | mod back; | |
| 61 | 54 | mod base; |
| 62 | 55 | mod builder; |
| 63 | 56 | mod callee; |
compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs deleted-94| ... | ... | @@ -1,94 +0,0 @@ |
| 1 | //! A wrapper around LLVM's archive (.a) code | |
| 2 | ||
| 3 | use std::path::Path; | |
| 4 | use std::{slice, str}; | |
| 5 | ||
| 6 | use rustc_fs_util::path_to_c_string; | |
| 7 | ||
| 8 | pub(crate) struct ArchiveRO { | |
| 9 | pub raw: &'static mut super::Archive, | |
| 10 | } | |
| 11 | ||
| 12 | unsafe impl Send for ArchiveRO {} | |
| 13 | ||
| 14 | pub(crate) struct Iter<'a> { | |
| 15 | raw: &'a mut super::ArchiveIterator<'a>, | |
| 16 | } | |
| 17 | ||
| 18 | pub(crate) struct Child<'a> { | |
| 19 | pub raw: &'a mut super::ArchiveChild<'a>, | |
| 20 | } | |
| 21 | ||
| 22 | impl ArchiveRO { | |
| 23 | /// Opens a static archive for read-only purposes. This is more optimized | |
| 24 | /// than the `open` method because it uses LLVM's internal `Archive` class | |
| 25 | /// rather than shelling out to `ar` for everything. | |
| 26 | /// | |
| 27 | /// If this archive is used with a mutable method, then an error will be | |
| 28 | /// raised. | |
| 29 | pub(crate) fn open(dst: &Path) -> Result<ArchiveRO, String> { | |
| 30 | unsafe { | |
| 31 | let s = path_to_c_string(dst); | |
| 32 | let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| { | |
| 33 | super::last_error().unwrap_or_else(|| "failed to open archive".to_owned()) | |
| 34 | })?; | |
| 35 | Ok(ArchiveRO { raw: ar }) | |
| 36 | } | |
| 37 | } | |
| 38 | ||
| 39 | pub(crate) fn iter(&self) -> Iter<'_> { | |
| 40 | unsafe { Iter { raw: super::LLVMRustArchiveIteratorNew(self.raw) } } | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | impl Drop for ArchiveRO { | |
| 45 | fn drop(&mut self) { | |
| 46 | unsafe { | |
| 47 | super::LLVMRustDestroyArchive(&mut *(self.raw as *mut _)); | |
| 48 | } | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | impl<'a> Iterator for Iter<'a> { | |
| 53 | type Item = Result<Child<'a>, String>; | |
| 54 | ||
| 55 | fn next(&mut self) -> Option<Result<Child<'a>, String>> { | |
| 56 | unsafe { | |
| 57 | match super::LLVMRustArchiveIteratorNext(self.raw) { | |
| 58 | Some(raw) => Some(Ok(Child { raw })), | |
| 59 | None => super::last_error().map(Err), | |
| 60 | } | |
| 61 | } | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | impl<'a> Drop for Iter<'a> { | |
| 66 | fn drop(&mut self) { | |
| 67 | unsafe { | |
| 68 | super::LLVMRustArchiveIteratorFree(&mut *(self.raw as *mut _)); | |
| 69 | } | |
| 70 | } | |
| 71 | } | |
| 72 | ||
| 73 | impl<'a> Child<'a> { | |
| 74 | pub(crate) fn name(&self) -> Option<&'a str> { | |
| 75 | unsafe { | |
| 76 | let mut name_len = 0; | |
| 77 | let name_ptr = super::LLVMRustArchiveChildName(self.raw, &mut name_len); | |
| 78 | if name_ptr.is_null() { | |
| 79 | None | |
| 80 | } else { | |
| 81 | let name = slice::from_raw_parts(name_ptr as *const u8, name_len as usize); | |
| 82 | str::from_utf8(name).ok().map(|s| s.trim()) | |
| 83 | } | |
| 84 | } | |
| 85 | } | |
| 86 | } | |
| 87 | ||
| 88 | impl<'a> Drop for Child<'a> { | |
| 89 | fn drop(&mut self) { | |
| 90 | unsafe { | |
| 91 | super::LLVMRustArchiveChildFree(&mut *(self.raw as *mut _)); | |
| 92 | } | |
| 93 | } | |
| 94 | } |
compiler/rustc_codegen_llvm/src/llvm/ffi.rs+1-47| ... | ... | @@ -616,17 +616,6 @@ pub(crate) enum DiagnosticLevel { |
| 616 | 616 | Remark, |
| 617 | 617 | } |
| 618 | 618 | |
| 619 | /// LLVMRustArchiveKind | |
| 620 | #[derive(Copy, Clone)] | |
| 621 | #[repr(C)] | |
| 622 | pub(crate) enum ArchiveKind { | |
| 623 | K_GNU, | |
| 624 | K_BSD, | |
| 625 | K_DARWIN, | |
| 626 | K_COFF, | |
| 627 | K_AIXBIG, | |
| 628 | } | |
| 629 | ||
| 630 | 619 | unsafe extern "C" { |
| 631 | 620 | // LLVMRustThinLTOData |
| 632 | 621 | pub(crate) type ThinLTOData; |
| ... | ... | @@ -775,19 +764,12 @@ pub(crate) struct Builder<'a>(InvariantOpaque<'a>); |
| 775 | 764 | pub(crate) struct PassManager<'a>(InvariantOpaque<'a>); |
| 776 | 765 | unsafe extern "C" { |
| 777 | 766 | pub type TargetMachine; |
| 778 | pub(crate) type Archive; | |
| 779 | 767 | } |
| 780 | #[repr(C)] | |
| 781 | pub(crate) struct ArchiveIterator<'a>(InvariantOpaque<'a>); | |
| 782 | #[repr(C)] | |
| 783 | pub(crate) struct ArchiveChild<'a>(InvariantOpaque<'a>); | |
| 784 | 768 | unsafe extern "C" { |
| 785 | 769 | pub(crate) type Twine; |
| 786 | 770 | pub(crate) type DiagnosticInfo; |
| 787 | 771 | pub(crate) type SMDiagnostic; |
| 788 | 772 | } |
| 789 | #[repr(C)] | |
| 790 | pub(crate) struct RustArchiveMember<'a>(InvariantOpaque<'a>); | |
| 791 | 773 | /// Opaque pointee of `LLVMOperandBundleRef`. |
| 792 | 774 | #[repr(C)] |
| 793 | 775 | pub(crate) struct OperandBundle<'a>(InvariantOpaque<'a>); |
| ... | ... | @@ -2443,7 +2425,7 @@ unsafe extern "C" { |
| 2443 | 2425 | OutputObjFile: *const c_char, |
| 2444 | 2426 | DebugInfoCompression: *const c_char, |
| 2445 | 2427 | UseEmulatedTls: bool, |
| 2446 | ArgsCstrBuff: *const c_char, | |
| 2428 | ArgsCstrBuff: *const c_uchar, // See "PTR_LEN_STR". | |
| 2447 | 2429 | ArgsCstrBuffLen: usize, |
| 2448 | 2430 | UseWasmEH: bool, |
| 2449 | 2431 | ) -> *mut TargetMachine; |
| ... | ... | @@ -2510,19 +2492,6 @@ unsafe extern "C" { |
| 2510 | 2492 | pub(crate) fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char); |
| 2511 | 2493 | pub(crate) fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); |
| 2512 | 2494 | |
| 2513 | pub(crate) fn LLVMRustOpenArchive(path: *const c_char) -> Option<&'static mut Archive>; | |
| 2514 | pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; | |
| 2515 | pub(crate) fn LLVMRustArchiveIteratorNext<'a>( | |
| 2516 | AIR: &ArchiveIterator<'a>, | |
| 2517 | ) -> Option<&'a mut ArchiveChild<'a>>; | |
| 2518 | pub(crate) fn LLVMRustArchiveChildName( | |
| 2519 | ACR: &ArchiveChild<'_>, | |
| 2520 | size: &mut size_t, | |
| 2521 | ) -> *const c_char; | |
| 2522 | pub(crate) fn LLVMRustArchiveChildFree<'a>(ACR: &'a mut ArchiveChild<'a>); | |
| 2523 | pub(crate) fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>); | |
| 2524 | pub(crate) fn LLVMRustDestroyArchive(AR: &'static mut Archive); | |
| 2525 | ||
| 2526 | 2495 | pub(crate) fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString); |
| 2527 | 2496 | |
| 2528 | 2497 | pub(crate) fn LLVMRustUnpackOptimizationDiagnostic<'a>( |
| ... | ... | @@ -2560,21 +2529,6 @@ unsafe extern "C" { |
| 2560 | 2529 | num_ranges: &mut usize, |
| 2561 | 2530 | ) -> bool; |
| 2562 | 2531 | |
| 2563 | pub(crate) fn LLVMRustWriteArchive( | |
| 2564 | Dst: *const c_char, | |
| 2565 | NumMembers: size_t, | |
| 2566 | Members: *const &RustArchiveMember<'_>, | |
| 2567 | WriteSymbtab: bool, | |
| 2568 | Kind: ArchiveKind, | |
| 2569 | isEC: bool, | |
| 2570 | ) -> LLVMRustResult; | |
| 2571 | pub(crate) fn LLVMRustArchiveMemberNew<'a>( | |
| 2572 | Filename: *const c_char, | |
| 2573 | Name: *const c_char, | |
| 2574 | Child: Option<&ArchiveChild<'a>>, | |
| 2575 | ) -> &'a mut RustArchiveMember<'a>; | |
| 2576 | pub(crate) fn LLVMRustArchiveMemberFree<'a>(Member: &'a mut RustArchiveMember<'a>); | |
| 2577 | ||
| 2578 | 2532 | pub(crate) fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine); |
| 2579 | 2533 | |
| 2580 | 2534 | pub(crate) fn LLVMRustPositionBuilderPastAllocas<'a>(B: &Builder<'a>, Fn: &'a Value); |
compiler/rustc_codegen_llvm/src/llvm/mod.rs-17| ... | ... | @@ -3,7 +3,6 @@ |
| 3 | 3 | use std::ffi::{CStr, CString}; |
| 4 | 4 | use std::num::NonZero; |
| 5 | 5 | use std::ptr; |
| 6 | use std::str::FromStr; | |
| 7 | 6 | use std::string::FromUtf8Error; |
| 8 | 7 | |
| 9 | 8 | use libc::c_uint; |
| ... | ... | @@ -16,7 +15,6 @@ pub(crate) use self::MetadataType::*; |
| 16 | 15 | pub(crate) use self::ffi::*; |
| 17 | 16 | use crate::common::AsCCharPtr; |
| 18 | 17 | |
| 19 | pub(crate) mod archive_ro; | |
| 20 | 18 | pub(crate) mod diagnostic; |
| 21 | 19 | pub(crate) mod enzyme_ffi; |
| 22 | 20 | mod ffi; |
| ... | ... | @@ -152,21 +150,6 @@ pub(crate) enum CodeGenOptSize { |
| 152 | 150 | CodeGenOptSizeAggressive = 2, |
| 153 | 151 | } |
| 154 | 152 | |
| 155 | impl FromStr for ArchiveKind { | |
| 156 | type Err = (); | |
| 157 | ||
| 158 | fn from_str(s: &str) -> Result<Self, Self::Err> { | |
| 159 | match s { | |
| 160 | "gnu" => Ok(ArchiveKind::K_GNU), | |
| 161 | "bsd" => Ok(ArchiveKind::K_BSD), | |
| 162 | "darwin" => Ok(ArchiveKind::K_DARWIN), | |
| 163 | "coff" => Ok(ArchiveKind::K_COFF), | |
| 164 | "aix_big" => Ok(ArchiveKind::K_AIXBIG), | |
| 165 | _ => Err(()), | |
| 166 | } | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | 153 | pub(crate) fn SetInstructionCallConv(instr: &Value, cc: CallConv) { |
| 171 | 154 | unsafe { |
| 172 | 155 | LLVMSetInstructionCallConv(instr, cc as c_uint); |
compiler/rustc_codegen_ssa/messages.ftl+2-2| ... | ... | @@ -171,8 +171,8 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol = invalid monomorphizati |
| 171 | 171 | |
| 172 | 172 | codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` of size `{$size}` to `{$ret_ty}` |
| 173 | 173 | |
| 174 | codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize` | |
| 175 | .note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` | |
| 174 | codegen_ssa_invalid_sanitize = invalid argument for `sanitize` | |
| 175 | .note = expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread` | |
| 176 | 176 | |
| 177 | 177 | codegen_ssa_invalid_windows_subsystem = invalid windows subsystem `{$subsystem}`, only `windows` and `console` are allowed |
| 178 | 178 |
compiler/rustc_codegen_ssa/src/codegen_attrs.rs+100-36| ... | ... | @@ -77,32 +77,6 @@ fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<Instr |
| 77 | 77 | } |
| 78 | 78 | } |
| 79 | 79 | |
| 80 | // FIXME(jdonszelmann): remove when no_sanitize becomes a parsed attr | |
| 81 | fn parse_no_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<SanitizerSet> { | |
| 82 | let list = attr.meta_item_list()?; | |
| 83 | let mut sanitizer_set = SanitizerSet::empty(); | |
| 84 | ||
| 85 | for item in list.iter() { | |
| 86 | match item.name() { | |
| 87 | Some(sym::address) => { | |
| 88 | sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | |
| 89 | } | |
| 90 | Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI, | |
| 91 | Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI, | |
| 92 | Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY, | |
| 93 | Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG, | |
| 94 | Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK, | |
| 95 | Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD, | |
| 96 | Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS, | |
| 97 | _ => { | |
| 98 | tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() }); | |
| 99 | } | |
| 100 | } | |
| 101 | } | |
| 102 | ||
| 103 | Some(sanitizer_set) | |
| 104 | } | |
| 105 | ||
| 106 | 80 | // FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr |
| 107 | 81 | fn parse_patchable_function_entry( |
| 108 | 82 | tcx: TyCtxt<'_>, |
| ... | ... | @@ -161,7 +135,7 @@ fn parse_patchable_function_entry( |
| 161 | 135 | #[derive(Default)] |
| 162 | 136 | struct InterestingAttributeDiagnosticSpans { |
| 163 | 137 | link_ordinal: Option<Span>, |
| 164 | no_sanitize: Option<Span>, | |
| 138 | sanitize: Option<Span>, | |
| 165 | 139 | inline: Option<Span>, |
| 166 | 140 | no_mangle: Option<Span>, |
| 167 | 141 | } |
| ... | ... | @@ -330,11 +304,7 @@ fn process_builtin_attrs( |
| 330 | 304 | codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED |
| 331 | 305 | } |
| 332 | 306 | sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, |
| 333 | sym::no_sanitize => { | |
| 334 | interesting_spans.no_sanitize = Some(attr.span()); | |
| 335 | codegen_fn_attrs.no_sanitize |= | |
| 336 | parse_no_sanitize_attr(tcx, attr).unwrap_or_default(); | |
| 337 | } | |
| 307 | sym::sanitize => interesting_spans.sanitize = Some(attr.span()), | |
| 338 | 308 | sym::instruction_set => { |
| 339 | 309 | codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr) |
| 340 | 310 | } |
| ... | ... | @@ -358,6 +328,8 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code |
| 358 | 328 | codegen_fn_attrs.alignment = |
| 359 | 329 | Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment); |
| 360 | 330 | |
| 331 | // Compute the disabled sanitizers. | |
| 332 | codegen_fn_attrs.no_sanitize |= tcx.disabled_sanitizers_for(did); | |
| 361 | 333 | // On trait methods, inherit the `#[align]` of the trait's method prototype. |
| 362 | 334 | codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did)); |
| 363 | 335 | |
| ... | ... | @@ -455,11 +427,11 @@ fn check_result( |
| 455 | 427 | if !codegen_fn_attrs.no_sanitize.is_empty() |
| 456 | 428 | && codegen_fn_attrs.inline.always() |
| 457 | 429 | && let (Some(no_sanitize_span), Some(inline_span)) = |
| 458 | (interesting_spans.no_sanitize, interesting_spans.inline) | |
| 430 | (interesting_spans.sanitize, interesting_spans.inline) | |
| 459 | 431 | { |
| 460 | 432 | let hir_id = tcx.local_def_id_to_hir_id(did); |
| 461 | 433 | tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| { |
| 462 | lint.primary_message("`no_sanitize` will have no effect after inlining"); | |
| 434 | lint.primary_message("setting `sanitize` off will have no effect after inlining"); | |
| 463 | 435 | lint.span_note(inline_span, "inlining requested here"); |
| 464 | 436 | }) |
| 465 | 437 | } |
| ... | ... | @@ -585,6 +557,93 @@ fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { |
| 585 | 557 | } |
| 586 | 558 | } |
| 587 | 559 | |
| 560 | /// For an attr that has the `sanitize` attribute, read the list of | |
| 561 | /// disabled sanitizers. `current_attr` holds the information about | |
| 562 | /// previously parsed attributes. | |
| 563 | fn parse_sanitize_attr( | |
| 564 | tcx: TyCtxt<'_>, | |
| 565 | attr: &Attribute, | |
| 566 | current_attr: SanitizerSet, | |
| 567 | ) -> SanitizerSet { | |
| 568 | let mut result = current_attr; | |
| 569 | if let Some(list) = attr.meta_item_list() { | |
| 570 | for item in list.iter() { | |
| 571 | let MetaItemInner::MetaItem(set) = item else { | |
| 572 | tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() }); | |
| 573 | break; | |
| 574 | }; | |
| 575 | let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>(); | |
| 576 | match segments.as_slice() { | |
| 577 | // Similar to clang, sanitize(address = ..) and | |
| 578 | // sanitize(kernel_address = ..) control both ASan and KASan | |
| 579 | // Source: https://reviews.llvm.org/D44981. | |
| 580 | [sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::off) => { | |
| 581 | result |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | |
| 582 | } | |
| 583 | [sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::on) => { | |
| 584 | result &= !SanitizerSet::ADDRESS; | |
| 585 | result &= !SanitizerSet::KERNELADDRESS; | |
| 586 | } | |
| 587 | [sym::cfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::CFI, | |
| 588 | [sym::cfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::CFI, | |
| 589 | [sym::kcfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::KCFI, | |
| 590 | [sym::kcfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::KCFI, | |
| 591 | [sym::memory] if set.value_str() == Some(sym::off) => { | |
| 592 | result |= SanitizerSet::MEMORY | |
| 593 | } | |
| 594 | [sym::memory] if set.value_str() == Some(sym::on) => { | |
| 595 | result &= !SanitizerSet::MEMORY | |
| 596 | } | |
| 597 | [sym::memtag] if set.value_str() == Some(sym::off) => { | |
| 598 | result |= SanitizerSet::MEMTAG | |
| 599 | } | |
| 600 | [sym::memtag] if set.value_str() == Some(sym::on) => { | |
| 601 | result &= !SanitizerSet::MEMTAG | |
| 602 | } | |
| 603 | [sym::shadow_call_stack] if set.value_str() == Some(sym::off) => { | |
| 604 | result |= SanitizerSet::SHADOWCALLSTACK | |
| 605 | } | |
| 606 | [sym::shadow_call_stack] if set.value_str() == Some(sym::on) => { | |
| 607 | result &= !SanitizerSet::SHADOWCALLSTACK | |
| 608 | } | |
| 609 | [sym::thread] if set.value_str() == Some(sym::off) => { | |
| 610 | result |= SanitizerSet::THREAD | |
| 611 | } | |
| 612 | [sym::thread] if set.value_str() == Some(sym::on) => { | |
| 613 | result &= !SanitizerSet::THREAD | |
| 614 | } | |
| 615 | [sym::hwaddress] if set.value_str() == Some(sym::off) => { | |
| 616 | result |= SanitizerSet::HWADDRESS | |
| 617 | } | |
| 618 | [sym::hwaddress] if set.value_str() == Some(sym::on) => { | |
| 619 | result &= !SanitizerSet::HWADDRESS | |
| 620 | } | |
| 621 | _ => { | |
| 622 | tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() }); | |
| 623 | } | |
| 624 | } | |
| 625 | } | |
| 626 | } | |
| 627 | result | |
| 628 | } | |
| 629 | ||
| 630 | fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { | |
| 631 | // Backtrack to the crate root. | |
| 632 | let disabled = match tcx.opt_local_parent(did) { | |
| 633 | // Check the parent (recursively). | |
| 634 | Some(parent) => tcx.disabled_sanitizers_for(parent), | |
| 635 | // We reached the crate root without seeing an attribute, so | |
| 636 | // there is no sanitizers to exclude. | |
| 637 | None => SanitizerSet::empty(), | |
| 638 | }; | |
| 639 | ||
| 640 | // Check for a sanitize annotation directly on this def. | |
| 641 | if let Some(attr) = tcx.get_attr(did, sym::sanitize) { | |
| 642 | return parse_sanitize_attr(tcx, attr, disabled); | |
| 643 | } | |
| 644 | disabled | |
| 645 | } | |
| 646 | ||
| 588 | 647 | /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller |
| 589 | 648 | /// applied to the method prototype. |
| 590 | 649 | fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { |
| ... | ... | @@ -709,6 +768,11 @@ pub fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> { |
| 709 | 768 | } |
| 710 | 769 | |
| 711 | 770 | pub(crate) fn provide(providers: &mut Providers) { |
| 712 | *providers = | |
| 713 | Providers { codegen_fn_attrs, should_inherit_track_caller, inherited_align, ..*providers }; | |
| 771 | *providers = Providers { | |
| 772 | codegen_fn_attrs, | |
| 773 | should_inherit_track_caller, | |
| 774 | inherited_align, | |
| 775 | disabled_sanitizers_for, | |
| 776 | ..*providers | |
| 777 | }; | |
| 714 | 778 | } |
compiler/rustc_codegen_ssa/src/errors.rs+2-2| ... | ... | @@ -1121,9 +1121,9 @@ impl IntoDiagArg for ExpectedPointerMutability { |
| 1121 | 1121 | } |
| 1122 | 1122 | |
| 1123 | 1123 | #[derive(Diagnostic)] |
| 1124 | #[diag(codegen_ssa_invalid_no_sanitize)] | |
| 1124 | #[diag(codegen_ssa_invalid_sanitize)] | |
| 1125 | 1125 | #[note] |
| 1126 | pub(crate) struct InvalidNoSanitize { | |
| 1126 | pub(crate) struct InvalidSanitize { | |
| 1127 | 1127 | #[primary_span] |
| 1128 | 1128 | pub span: Span, |
| 1129 | 1129 | } |
compiler/rustc_const_eval/src/interpret/operand.rs+10| ... | ... | @@ -175,6 +175,16 @@ impl<Prov: Provenance> Immediate<Prov> { |
| 175 | 175 | } |
| 176 | 176 | interp_ok(()) |
| 177 | 177 | } |
| 178 | ||
| 179 | pub fn has_provenance(&self) -> bool { | |
| 180 | match self { | |
| 181 | Immediate::Scalar(scalar) => matches!(scalar, Scalar::Ptr { .. }), | |
| 182 | Immediate::ScalarPair(s1, s2) => { | |
| 183 | matches!(s1, Scalar::Ptr { .. }) || matches!(s2, Scalar::Ptr { .. }) | |
| 184 | } | |
| 185 | Immediate::Uninit => false, | |
| 186 | } | |
| 187 | } | |
| 178 | 188 | } |
| 179 | 189 | |
| 180 | 190 | // ScalarPair needs a type to interpret, so we often have an immediate and a type together |
compiler/rustc_const_eval/src/interpret/place.rs+7| ... | ... | @@ -759,6 +759,13 @@ where |
| 759 | 759 | &mut self, |
| 760 | 760 | dest: &impl Writeable<'tcx, M::Provenance>, |
| 761 | 761 | ) -> InterpResult<'tcx> { |
| 762 | // If this is an efficiently represented local variable without provenance, skip the | |
| 763 | // `as_mplace_or_mutable_local` that would otherwise force this local into memory. | |
| 764 | if let Right(imm) = dest.to_op(self)?.as_mplace_or_imm() { | |
| 765 | if !imm.has_provenance() { | |
| 766 | return interp_ok(()); | |
| 767 | } | |
| 768 | } | |
| 762 | 769 | match self.as_mplace_or_mutable_local(&dest.to_place())? { |
| 763 | 770 | Right((local_val, _local_layout, local)) => { |
| 764 | 771 | local_val.clear_provenance()?; |
compiler/rustc_feature/src/builtin_attrs.rs+8-5| ... | ... | @@ -6,6 +6,7 @@ use AttributeDuplicates::*; |
| 6 | 6 | use AttributeGate::*; |
| 7 | 7 | use AttributeType::*; |
| 8 | 8 | use rustc_data_structures::fx::FxHashMap; |
| 9 | use rustc_hir::AttrStyle; | |
| 9 | 10 | use rustc_hir::attrs::EncodeCrossCrate; |
| 10 | 11 | use rustc_span::edition::Edition; |
| 11 | 12 | use rustc_span::{Symbol, sym}; |
| ... | ... | @@ -132,9 +133,12 @@ pub struct AttributeTemplate { |
| 132 | 133 | } |
| 133 | 134 | |
| 134 | 135 | impl AttributeTemplate { |
| 135 | pub fn suggestions(&self, inner: bool, name: impl std::fmt::Display) -> Vec<String> { | |
| 136 | pub fn suggestions(&self, style: AttrStyle, name: impl std::fmt::Display) -> Vec<String> { | |
| 136 | 137 | let mut suggestions = vec![]; |
| 137 | let inner = if inner { "!" } else { "" }; | |
| 138 | let inner = match style { | |
| 139 | AttrStyle::Outer => "", | |
| 140 | AttrStyle::Inner => "!", | |
| 141 | }; | |
| 138 | 142 | if self.word { |
| 139 | 143 | suggestions.push(format!("#{inner}[{name}]")); |
| 140 | 144 | } |
| ... | ... | @@ -741,9 +745,8 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ |
| 741 | 745 | ErrorPreceding, EncodeCrossCrate::No |
| 742 | 746 | ), |
| 743 | 747 | gated!( |
| 744 | no_sanitize, Normal, | |
| 745 | template!(List: &["address, kcfi, memory, thread"]), DuplicatesOk, | |
| 746 | EncodeCrossCrate::No, experimental!(no_sanitize) | |
| 748 | sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding, | |
| 749 | EncodeCrossCrate::No, sanitize, experimental!(sanitize), | |
| 747 | 750 | ), |
| 748 | 751 | gated!( |
| 749 | 752 | coverage, Normal, template!(OneOf: &[sym::off, sym::on]), |
compiler/rustc_feature/src/removed.rs+3| ... | ... | @@ -190,6 +190,9 @@ declare_features! ( |
| 190 | 190 | (removed, no_coverage, "1.74.0", Some(84605), Some("renamed to `coverage_attribute`"), 114656), |
| 191 | 191 | /// Allows `#[no_debug]`. |
| 192 | 192 | (removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667), |
| 193 | // Allows the use of `no_sanitize` attribute. | |
| 194 | /// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]` | |
| 195 | (removed, no_sanitize, "CURRENT_RUSTC_VERSION", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681), | |
| 193 | 196 | /// Note: this feature was previously recorded in a separate |
| 194 | 197 | /// `STABLE_REMOVED` list because it, uniquely, was once stable but was |
| 195 | 198 | /// then removed. But there was no utility storing it separately, so now |
compiler/rustc_feature/src/unstable.rs+2-2| ... | ... | @@ -594,8 +594,6 @@ declare_features! ( |
| 594 | 594 | (unstable, new_range, "1.86.0", Some(123741)), |
| 595 | 595 | /// Allows `#![no_core]`. |
| 596 | 596 | (unstable, no_core, "1.3.0", Some(29639)), |
| 597 | /// Allows the use of `no_sanitize` attribute. | |
| 598 | (unstable, no_sanitize, "1.42.0", Some(39699)), | |
| 599 | 597 | /// Allows using the `non_exhaustive_omitted_patterns` lint. |
| 600 | 598 | (unstable, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554)), |
| 601 | 599 | /// Allows `for<T>` binders in where-clauses |
| ... | ... | @@ -628,6 +626,8 @@ declare_features! ( |
| 628 | 626 | (unstable, return_type_notation, "1.70.0", Some(109417)), |
| 629 | 627 | /// Allows `extern "rust-cold"`. |
| 630 | 628 | (unstable, rust_cold_cc, "1.63.0", Some(97544)), |
| 629 | /// Allows the use of the `sanitize` attribute. | |
| 630 | (unstable, sanitize, "CURRENT_RUSTC_VERSION", Some(39699)), | |
| 631 | 631 | /// Allows the use of SIMD types in functions declared in `extern` blocks. |
| 632 | 632 | (unstable, simd_ffi, "1.0.0", Some(27731)), |
| 633 | 633 | /// Allows specialization of implementations (RFC 1210). |
compiler/rustc_hir_typeck/src/expr.rs+3| ... | ... | @@ -290,6 +290,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| 290 | 290 | | ExprKind::Let(..) |
| 291 | 291 | | ExprKind::Loop(..) |
| 292 | 292 | | ExprKind::Match(..) => {} |
| 293 | // Do not warn on `as` casts from never to any, | |
| 294 | // they are sometimes required to appeal typeck. | |
| 295 | ExprKind::Cast(_, _) => {} | |
| 293 | 296 | // If `expr` is a result of desugaring the try block and is an ok-wrapped |
| 294 | 297 | // diverging expression (e.g. it arose from desugaring of `try { return }`), |
| 295 | 298 | // we skip issuing a warning because it is autogenerated code. |
compiler/rustc_lint_defs/src/builtin.rs+6-6| ... | ... | @@ -2301,18 +2301,18 @@ declare_lint! { |
| 2301 | 2301 | |
| 2302 | 2302 | declare_lint! { |
| 2303 | 2303 | /// The `inline_no_sanitize` lint detects incompatible use of |
| 2304 | /// [`#[inline(always)]`][inline] and [`#[no_sanitize(...)]`][no_sanitize]. | |
| 2304 | /// [`#[inline(always)]`][inline] and [`#[sanitize(xyz = "off")]`][sanitize]. | |
| 2305 | 2305 | /// |
| 2306 | 2306 | /// [inline]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute |
| 2307 | /// [no_sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html | |
| 2307 | /// [sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html | |
| 2308 | 2308 | /// |
| 2309 | 2309 | /// ### Example |
| 2310 | 2310 | /// |
| 2311 | 2311 | /// ```rust |
| 2312 | /// #![feature(no_sanitize)] | |
| 2312 | /// #![feature(sanitize)] | |
| 2313 | 2313 | /// |
| 2314 | 2314 | /// #[inline(always)] |
| 2315 | /// #[no_sanitize(address)] | |
| 2315 | /// #[sanitize(address = "off")] | |
| 2316 | 2316 | /// fn x() {} |
| 2317 | 2317 | /// |
| 2318 | 2318 | /// fn main() { |
| ... | ... | @@ -2325,11 +2325,11 @@ declare_lint! { |
| 2325 | 2325 | /// ### Explanation |
| 2326 | 2326 | /// |
| 2327 | 2327 | /// The use of the [`#[inline(always)]`][inline] attribute prevents the |
| 2328 | /// the [`#[no_sanitize(...)]`][no_sanitize] attribute from working. | |
| 2328 | /// the [`#[sanitize(xyz = "off")]`][sanitize] attribute from working. | |
| 2329 | 2329 | /// Consider temporarily removing `inline` attribute. |
| 2330 | 2330 | pub INLINE_NO_SANITIZE, |
| 2331 | 2331 | Warn, |
| 2332 | "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`", | |
| 2332 | r#"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"#, | |
| 2333 | 2333 | } |
| 2334 | 2334 | |
| 2335 | 2335 | declare_lint! { |
compiler/rustc_llvm/build.rs-1| ... | ... | @@ -226,7 +226,6 @@ fn main() { |
| 226 | 226 | rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper")); |
| 227 | 227 | cfg.file("llvm-wrapper/PassWrapper.cpp") |
| 228 | 228 | .file("llvm-wrapper/RustWrapper.cpp") |
| 229 | .file("llvm-wrapper/ArchiveWrapper.cpp") | |
| 230 | 229 | .file("llvm-wrapper/CoverageMappingWrapper.cpp") |
| 231 | 230 | .file("llvm-wrapper/SymbolWrapper.cpp") |
| 232 | 231 | .file("llvm-wrapper/Linker.cpp") |
compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp deleted-208| ... | ... | @@ -1,208 +0,0 @@ |
| 1 | #include "LLVMWrapper.h" | |
| 2 | ||
| 3 | #include "llvm/Object/Archive.h" | |
| 4 | #include "llvm/Object/ArchiveWriter.h" | |
| 5 | #include "llvm/Support/Path.h" | |
| 6 | ||
| 7 | using namespace llvm; | |
| 8 | using namespace llvm::object; | |
| 9 | ||
| 10 | struct RustArchiveMember { | |
| 11 | const char *Filename; | |
| 12 | const char *Name; | |
| 13 | Archive::Child Child; | |
| 14 | ||
| 15 | RustArchiveMember() | |
| 16 | : Filename(nullptr), Name(nullptr), Child(nullptr, nullptr, nullptr) {} | |
| 17 | ~RustArchiveMember() {} | |
| 18 | }; | |
| 19 | ||
| 20 | struct RustArchiveIterator { | |
| 21 | bool First; | |
| 22 | Archive::child_iterator Cur; | |
| 23 | Archive::child_iterator End; | |
| 24 | std::unique_ptr<Error> Err; | |
| 25 | ||
| 26 | RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End, | |
| 27 | std::unique_ptr<Error> Err) | |
| 28 | : First(true), Cur(Cur), End(End), Err(std::move(Err)) {} | |
| 29 | }; | |
| 30 | ||
| 31 | enum class LLVMRustArchiveKind { | |
| 32 | GNU, | |
| 33 | BSD, | |
| 34 | DARWIN, | |
| 35 | COFF, | |
| 36 | AIX_BIG, | |
| 37 | }; | |
| 38 | ||
| 39 | static Archive::Kind fromRust(LLVMRustArchiveKind Kind) { | |
| 40 | switch (Kind) { | |
| 41 | case LLVMRustArchiveKind::GNU: | |
| 42 | return Archive::K_GNU; | |
| 43 | case LLVMRustArchiveKind::BSD: | |
| 44 | return Archive::K_BSD; | |
| 45 | case LLVMRustArchiveKind::DARWIN: | |
| 46 | return Archive::K_DARWIN; | |
| 47 | case LLVMRustArchiveKind::COFF: | |
| 48 | return Archive::K_COFF; | |
| 49 | case LLVMRustArchiveKind::AIX_BIG: | |
| 50 | return Archive::K_AIXBIG; | |
| 51 | default: | |
| 52 | report_fatal_error("Bad ArchiveKind."); | |
| 53 | } | |
| 54 | } | |
| 55 | ||
| 56 | typedef OwningBinary<Archive> *LLVMRustArchiveRef; | |
| 57 | typedef RustArchiveMember *LLVMRustArchiveMemberRef; | |
| 58 | typedef Archive::Child *LLVMRustArchiveChildRef; | |
| 59 | typedef Archive::Child const *LLVMRustArchiveChildConstRef; | |
| 60 | typedef RustArchiveIterator *LLVMRustArchiveIteratorRef; | |
| 61 | ||
| 62 | extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) { | |
| 63 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr = MemoryBuffer::getFile( | |
| 64 | Path, /*IsText*/ false, /*RequiresNullTerminator=*/false); | |
| 65 | if (!BufOr) { | |
| 66 | LLVMRustSetLastError(BufOr.getError().message().c_str()); | |
| 67 | return nullptr; | |
| 68 | } | |
| 69 | ||
| 70 | Expected<std::unique_ptr<Archive>> ArchiveOr = | |
| 71 | Archive::create(BufOr.get()->getMemBufferRef()); | |
| 72 | ||
| 73 | if (!ArchiveOr) { | |
| 74 | LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str()); | |
| 75 | return nullptr; | |
| 76 | } | |
| 77 | ||
| 78 | OwningBinary<Archive> *Ret = new OwningBinary<Archive>( | |
| 79 | std::move(ArchiveOr.get()), std::move(BufOr.get())); | |
| 80 | ||
| 81 | return Ret; | |
| 82 | } | |
| 83 | ||
| 84 | extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) { | |
| 85 | delete RustArchive; | |
| 86 | } | |
| 87 | ||
| 88 | extern "C" LLVMRustArchiveIteratorRef | |
| 89 | LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) { | |
| 90 | Archive *Archive = RustArchive->getBinary(); | |
| 91 | std::unique_ptr<Error> Err = std::make_unique<Error>(Error::success()); | |
| 92 | auto Cur = Archive->child_begin(*Err); | |
| 93 | if (*Err) { | |
| 94 | LLVMRustSetLastError(toString(std::move(*Err)).c_str()); | |
| 95 | return nullptr; | |
| 96 | } | |
| 97 | auto End = Archive->child_end(); | |
| 98 | return new RustArchiveIterator(Cur, End, std::move(Err)); | |
| 99 | } | |
| 100 | ||
| 101 | extern "C" LLVMRustArchiveChildConstRef | |
| 102 | LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) { | |
| 103 | if (RAI->Cur == RAI->End) | |
| 104 | return nullptr; | |
| 105 | ||
| 106 | // Advancing the iterator validates the next child, and this can | |
| 107 | // uncover an error. LLVM requires that we check all Errors, | |
| 108 | // so we only advance the iterator if we actually need to fetch | |
| 109 | // the next child. | |
| 110 | // This means we must not advance the iterator in the *first* call, | |
| 111 | // but instead advance it *before* fetching the child in all later calls. | |
| 112 | if (!RAI->First) { | |
| 113 | ++RAI->Cur; | |
| 114 | if (*RAI->Err) { | |
| 115 | LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str()); | |
| 116 | return nullptr; | |
| 117 | } | |
| 118 | } else { | |
| 119 | RAI->First = false; | |
| 120 | } | |
| 121 | ||
| 122 | if (RAI->Cur == RAI->End) | |
| 123 | return nullptr; | |
| 124 | ||
| 125 | const Archive::Child &Child = *RAI->Cur.operator->(); | |
| 126 | Archive::Child *Ret = new Archive::Child(Child); | |
| 127 | ||
| 128 | return Ret; | |
| 129 | } | |
| 130 | ||
| 131 | extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) { | |
| 132 | delete Child; | |
| 133 | } | |
| 134 | ||
| 135 | extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) { | |
| 136 | delete RAI; | |
| 137 | } | |
| 138 | ||
| 139 | extern "C" const char * | |
| 140 | LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) { | |
| 141 | Expected<StringRef> NameOrErr = Child->getName(); | |
| 142 | if (!NameOrErr) { | |
| 143 | // rustc_codegen_llvm currently doesn't use this error string, but it might | |
| 144 | // be useful in the future, and in the meantime this tells LLVM that the | |
| 145 | // error was not ignored and that it shouldn't abort the process. | |
| 146 | LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str()); | |
| 147 | return nullptr; | |
| 148 | } | |
| 149 | StringRef Name = NameOrErr.get(); | |
| 150 | *Size = Name.size(); | |
| 151 | return Name.data(); | |
| 152 | } | |
| 153 | ||
| 154 | extern "C" LLVMRustArchiveMemberRef | |
| 155 | LLVMRustArchiveMemberNew(char *Filename, char *Name, | |
| 156 | LLVMRustArchiveChildRef Child) { | |
| 157 | RustArchiveMember *Member = new RustArchiveMember; | |
| 158 | Member->Filename = Filename; | |
| 159 | Member->Name = Name; | |
| 160 | if (Child) | |
| 161 | Member->Child = *Child; | |
| 162 | return Member; | |
| 163 | } | |
| 164 | ||
| 165 | extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) { | |
| 166 | delete Member; | |
| 167 | } | |
| 168 | ||
| 169 | extern "C" LLVMRustResult LLVMRustWriteArchive( | |
| 170 | char *Dst, size_t NumMembers, const LLVMRustArchiveMemberRef *NewMembers, | |
| 171 | bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) { | |
| 172 | ||
| 173 | std::vector<NewArchiveMember> Members; | |
| 174 | auto Kind = fromRust(RustKind); | |
| 175 | ||
| 176 | for (size_t I = 0; I < NumMembers; I++) { | |
| 177 | auto Member = NewMembers[I]; | |
| 178 | assert(Member->Name); | |
| 179 | if (Member->Filename) { | |
| 180 | Expected<NewArchiveMember> MOrErr = | |
| 181 | NewArchiveMember::getFile(Member->Filename, true); | |
| 182 | if (!MOrErr) { | |
| 183 | LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); | |
| 184 | return LLVMRustResult::Failure; | |
| 185 | } | |
| 186 | MOrErr->MemberName = sys::path::filename(MOrErr->MemberName); | |
| 187 | Members.push_back(std::move(*MOrErr)); | |
| 188 | } else { | |
| 189 | Expected<NewArchiveMember> MOrErr = | |
| 190 | NewArchiveMember::getOldMember(Member->Child, true); | |
| 191 | if (!MOrErr) { | |
| 192 | LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); | |
| 193 | return LLVMRustResult::Failure; | |
| 194 | } | |
| 195 | Members.push_back(std::move(*MOrErr)); | |
| 196 | } | |
| 197 | } | |
| 198 | ||
| 199 | auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab | |
| 200 | : SymtabWritingMode::NoSymtab; | |
| 201 | auto Result = | |
| 202 | writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC); | |
| 203 | if (!Result) | |
| 204 | return LLVMRustResult::Success; | |
| 205 | LLVMRustSetLastError(toString(std::move(Result)).c_str()); | |
| 206 | ||
| 207 | return LLVMRustResult::Failure; | |
| 208 | } |
compiler/rustc_middle/src/middle/codegen_fn_attrs.rs+2-2| ... | ... | @@ -61,8 +61,8 @@ pub struct CodegenFnAttrs { |
| 61 | 61 | /// The `#[link_section = "..."]` attribute, or what executable section this |
| 62 | 62 | /// should be placed in. |
| 63 | 63 | pub link_section: Option<Symbol>, |
| 64 | /// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which | |
| 65 | /// instrumentation should be disabled inside the annotated function. | |
| 64 | /// The `#[sanitize(xyz = "off")]` attribute. Indicates sanitizers for which | |
| 65 | /// instrumentation should be disabled inside the function. | |
| 66 | 66 | pub no_sanitize: SanitizerSet, |
| 67 | 67 | /// The `#[instruction_set(set)]` attribute. Indicates if the generated code should |
| 68 | 68 | /// be generated against a specific instruction set. Only usable on architectures which allow |
compiler/rustc_middle/src/query/erase.rs+1| ... | ... | @@ -343,6 +343,7 @@ trivial! { |
| 343 | 343 | rustc_span::Symbol, |
| 344 | 344 | rustc_span::Ident, |
| 345 | 345 | rustc_target::spec::PanicStrategy, |
| 346 | rustc_target::spec::SanitizerSet, | |
| 346 | 347 | rustc_type_ir::Variance, |
| 347 | 348 | u32, |
| 348 | 349 | usize, |
compiler/rustc_middle/src/query/mod.rs+11-1| ... | ... | @@ -100,7 +100,7 @@ use rustc_session::lint::LintExpectationId; |
| 100 | 100 | use rustc_span::def_id::LOCAL_CRATE; |
| 101 | 101 | use rustc_span::source_map::Spanned; |
| 102 | 102 | use rustc_span::{DUMMY_SP, Span, Symbol}; |
| 103 | use rustc_target::spec::PanicStrategy; | |
| 103 | use rustc_target::spec::{PanicStrategy, SanitizerSet}; | |
| 104 | 104 | use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; |
| 105 | 105 | |
| 106 | 106 | use crate::infer::canonical::{self, Canonical}; |
| ... | ... | @@ -2686,6 +2686,16 @@ rustc_queries! { |
| 2686 | 2686 | desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) } |
| 2687 | 2687 | separate_provide_extern |
| 2688 | 2688 | } |
| 2689 | ||
| 2690 | /// Checks for the nearest `#[sanitize(xyz = "off")]` or | |
| 2691 | /// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the | |
| 2692 | /// crate root. | |
| 2693 | /// | |
| 2694 | /// Returns the set of sanitizers that is explicitly disabled for this def. | |
| 2695 | query disabled_sanitizers_for(key: LocalDefId) -> SanitizerSet { | |
| 2696 | desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) } | |
| 2697 | feedable | |
| 2698 | } | |
| 2689 | 2699 | } |
| 2690 | 2700 | |
| 2691 | 2701 | rustc_with_all_queries! { define_callbacks! } |
compiler/rustc_passes/messages.ftl+6-4| ... | ... | @@ -454,10 +454,6 @@ passes_no_main_function = |
| 454 | 454 | .teach_note = If you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/ |
| 455 | 455 | .non_function_main = non-function item at `crate::main` is found |
| 456 | 456 | |
| 457 | passes_no_sanitize = | |
| 458 | `#[no_sanitize({$attr_str})]` should be applied to {$accepted_kind} | |
| 459 | .label = not {$accepted_kind} | |
| 460 | ||
| 461 | 457 | passes_non_exhaustive_with_default_field_values = |
| 462 | 458 | `#[non_exhaustive]` can't be used to annotate items with default field values |
| 463 | 459 | .label = this struct has default field values |
| ... | ... | @@ -552,6 +548,12 @@ passes_rustc_pub_transparent = |
| 552 | 548 | attribute should be applied to `#[repr(transparent)]` types |
| 553 | 549 | .label = not a `#[repr(transparent)]` type |
| 554 | 550 | |
| 551 | passes_sanitize_attribute_not_allowed = | |
| 552 | sanitize attribute not allowed here | |
| 553 | .not_fn_impl_mod = not a function, impl block, or module | |
| 554 | .no_body = function has no body | |
| 555 | .help = sanitize attribute can be applied to a function (with body), impl block, or module | |
| 556 | ||
| 555 | 557 | passes_should_be_applied_to_fn = |
| 556 | 558 | attribute should be applied to a function definition |
| 557 | 559 | .label = {$on_crate -> |
compiler/rustc_passes/src/check_attr.rs+32-29| ... | ... | @@ -260,8 +260,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { |
| 260 | 260 | [sym::diagnostic, sym::on_unimplemented, ..] => { |
| 261 | 261 | self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) |
| 262 | 262 | } |
| 263 | [sym::no_sanitize, ..] => { | |
| 264 | self.check_no_sanitize(attr, span, target) | |
| 263 | [sym::sanitize, ..] => { | |
| 264 | self.check_sanitize(attr, span, target) | |
| 265 | 265 | } |
| 266 | 266 | [sym::thread_local, ..] => self.check_thread_local(attr, span, target), |
| 267 | 267 | [sym::doc, ..] => self.check_doc_attrs( |
| ... | ... | @@ -483,39 +483,43 @@ impl<'tcx> CheckAttrVisitor<'tcx> { |
| 483 | 483 | } |
| 484 | 484 | } |
| 485 | 485 | |
| 486 | fn check_no_sanitize(&self, attr: &Attribute, span: Span, target: Target) { | |
| 486 | /// Checks that the `#[sanitize(..)]` attribute is applied to a | |
| 487 | /// function/closure/method, or to an impl block or module. | |
| 488 | fn check_sanitize(&self, attr: &Attribute, target_span: Span, target: Target) { | |
| 489 | let mut not_fn_impl_mod = None; | |
| 490 | let mut no_body = None; | |
| 491 | ||
| 487 | 492 | if let Some(list) = attr.meta_item_list() { |
| 488 | 493 | for item in list.iter() { |
| 489 | let sym = item.name(); | |
| 490 | match sym { | |
| 491 | Some(s @ sym::address | s @ sym::hwaddress) => { | |
| 492 | let is_valid = | |
| 493 | matches!(target, Target::Fn | Target::Method(..) | Target::Static); | |
| 494 | if !is_valid { | |
| 495 | self.dcx().emit_err(errors::NoSanitize { | |
| 496 | attr_span: item.span(), | |
| 497 | defn_span: span, | |
| 498 | accepted_kind: "a function or static", | |
| 499 | attr_str: s.as_str(), | |
| 500 | }); | |
| 501 | } | |
| 494 | let MetaItemInner::MetaItem(set) = item else { | |
| 495 | return; | |
| 496 | }; | |
| 497 | let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>(); | |
| 498 | match target { | |
| 499 | Target::Fn | |
| 500 | | Target::Closure | |
| 501 | | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) | |
| 502 | | Target::Impl { .. } | |
| 503 | | Target::Mod => return, | |
| 504 | Target::Static if matches!(segments.as_slice(), [sym::address]) => return, | |
| 505 | ||
| 506 | // These are "functions", but they aren't allowed because they don't | |
| 507 | // have a body, so the usual explanation would be confusing. | |
| 508 | Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { | |
| 509 | no_body = Some(target_span); | |
| 502 | 510 | } |
| 511 | ||
| 503 | 512 | _ => { |
| 504 | let is_valid = matches!(target, Target::Fn | Target::Method(..)); | |
| 505 | if !is_valid { | |
| 506 | self.dcx().emit_err(errors::NoSanitize { | |
| 507 | attr_span: item.span(), | |
| 508 | defn_span: span, | |
| 509 | accepted_kind: "a function", | |
| 510 | attr_str: &match sym { | |
| 511 | Some(name) => name.to_string(), | |
| 512 | None => "...".to_string(), | |
| 513 | }, | |
| 514 | }); | |
| 515 | } | |
| 513 | not_fn_impl_mod = Some(target_span); | |
| 516 | 514 | } |
| 517 | 515 | } |
| 518 | 516 | } |
| 517 | self.dcx().emit_err(errors::SanitizeAttributeNotAllowed { | |
| 518 | attr_span: attr.span(), | |
| 519 | not_fn_impl_mod, | |
| 520 | no_body, | |
| 521 | help: (), | |
| 522 | }); | |
| 519 | 523 | } |
| 520 | 524 | } |
| 521 | 525 | |
| ... | ... | @@ -562,7 +566,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { |
| 562 | 566 | } |
| 563 | 567 | } |
| 564 | 568 | } |
| 565 | ||
| 566 | 569 | /// Checks if `#[collapse_debuginfo]` is applied to a macro. |
| 567 | 570 | fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) { |
| 568 | 571 | match target { |
compiler/rustc_passes/src/errors.rs+12-6| ... | ... | @@ -1489,15 +1489,21 @@ pub(crate) struct AttrCrateLevelOnlySugg { |
| 1489 | 1489 | pub attr: Span, |
| 1490 | 1490 | } |
| 1491 | 1491 | |
| 1492 | /// "sanitize attribute not allowed here" | |
| 1492 | 1493 | #[derive(Diagnostic)] |
| 1493 | #[diag(passes_no_sanitize)] | |
| 1494 | pub(crate) struct NoSanitize<'a> { | |
| 1494 | #[diag(passes_sanitize_attribute_not_allowed)] | |
| 1495 | pub(crate) struct SanitizeAttributeNotAllowed { | |
| 1495 | 1496 | #[primary_span] |
| 1496 | 1497 | pub attr_span: Span, |
| 1497 | #[label] | |
| 1498 | pub defn_span: Span, | |
| 1499 | pub accepted_kind: &'a str, | |
| 1500 | pub attr_str: &'a str, | |
| 1498 | /// "not a function, impl block, or module" | |
| 1499 | #[label(passes_not_fn_impl_mod)] | |
| 1500 | pub not_fn_impl_mod: Option<Span>, | |
| 1501 | /// "function has no body" | |
| 1502 | #[label(passes_no_body)] | |
| 1503 | pub no_body: Option<Span>, | |
| 1504 | /// "sanitize attribute can be applied to a function (with body), impl block, or module" | |
| 1505 | #[help] | |
| 1506 | pub help: (), | |
| 1501 | 1507 | } |
| 1502 | 1508 | |
| 1503 | 1509 | // FIXME(jdonszelmann): move back to rustc_attr |
compiler/rustc_span/src/symbol.rs+1| ... | ... | @@ -1261,6 +1261,7 @@ symbols! { |
| 1261 | 1261 | iterator, |
| 1262 | 1262 | iterator_collect_fn, |
| 1263 | 1263 | kcfi, |
| 1264 | kernel_address, | |
| 1264 | 1265 | keylocker_x86, |
| 1265 | 1266 | keyword, |
| 1266 | 1267 | kind, |
library/core/src/lib.rs+7| ... | ... | @@ -226,6 +226,13 @@ pub mod assert_matches { |
| 226 | 226 | pub use crate::macros::{assert_matches, debug_assert_matches}; |
| 227 | 227 | } |
| 228 | 228 | |
| 229 | #[unstable(feature = "derive_from", issue = "144889")] | |
| 230 | /// Unstable module containing the unstable `From` derive macro. | |
| 231 | pub mod from { | |
| 232 | #[unstable(feature = "derive_from", issue = "144889")] | |
| 233 | pub use crate::macros::builtin::From; | |
| 234 | } | |
| 235 | ||
| 229 | 236 | // We don't export this through #[macro_export] for now, to avoid breakage. |
| 230 | 237 | #[unstable(feature = "autodiff", issue = "124509")] |
| 231 | 238 | /// Unstable module containing the unstable `autodiff` macro. |
library/core/src/net/ip_addr.rs+6-6| ... | ... | @@ -626,13 +626,13 @@ impl Ipv4Addr { |
| 626 | 626 | /// # Examples |
| 627 | 627 | /// |
| 628 | 628 | /// ``` |
| 629 | /// #![feature(ip_from)] | |
| 630 | 629 | /// use std::net::Ipv4Addr; |
| 631 | 630 | /// |
| 632 | 631 | /// let addr = Ipv4Addr::from_octets([13u8, 12u8, 11u8, 10u8]); |
| 633 | 632 | /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); |
| 634 | 633 | /// ``` |
| 635 | #[unstable(feature = "ip_from", issue = "131360")] | |
| 634 | #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] | |
| 635 | #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] | |
| 636 | 636 | #[must_use] |
| 637 | 637 | #[inline] |
| 638 | 638 | pub const fn from_octets(octets: [u8; 4]) -> Ipv4Addr { |
| ... | ... | @@ -1464,7 +1464,6 @@ impl Ipv6Addr { |
| 1464 | 1464 | /// # Examples |
| 1465 | 1465 | /// |
| 1466 | 1466 | /// ``` |
| 1467 | /// #![feature(ip_from)] | |
| 1468 | 1467 | /// use std::net::Ipv6Addr; |
| 1469 | 1468 | /// |
| 1470 | 1469 | /// let addr = Ipv6Addr::from_segments([ |
| ... | ... | @@ -1479,7 +1478,8 @@ impl Ipv6Addr { |
| 1479 | 1478 | /// addr |
| 1480 | 1479 | /// ); |
| 1481 | 1480 | /// ``` |
| 1482 | #[unstable(feature = "ip_from", issue = "131360")] | |
| 1481 | #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] | |
| 1482 | #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] | |
| 1483 | 1483 | #[must_use] |
| 1484 | 1484 | #[inline] |
| 1485 | 1485 | pub const fn from_segments(segments: [u16; 8]) -> Ipv6Addr { |
| ... | ... | @@ -2029,7 +2029,6 @@ impl Ipv6Addr { |
| 2029 | 2029 | /// # Examples |
| 2030 | 2030 | /// |
| 2031 | 2031 | /// ``` |
| 2032 | /// #![feature(ip_from)] | |
| 2033 | 2032 | /// use std::net::Ipv6Addr; |
| 2034 | 2033 | /// |
| 2035 | 2034 | /// let addr = Ipv6Addr::from_octets([ |
| ... | ... | @@ -2044,7 +2043,8 @@ impl Ipv6Addr { |
| 2044 | 2043 | /// addr |
| 2045 | 2044 | /// ); |
| 2046 | 2045 | /// ``` |
| 2047 | #[unstable(feature = "ip_from", issue = "131360")] | |
| 2046 | #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] | |
| 2047 | #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] | |
| 2048 | 2048 | #[must_use] |
| 2049 | 2049 | #[inline] |
| 2050 | 2050 | pub const fn from_octets(octets: [u8; 16]) -> Ipv6Addr { |
library/core/src/option.rs+6-6| ... | ... | @@ -2095,9 +2095,9 @@ impl<T> Option<&mut T> { |
| 2095 | 2095 | impl<T, E> Option<Result<T, E>> { |
| 2096 | 2096 | /// Transposes an `Option` of a [`Result`] into a [`Result`] of an `Option`. |
| 2097 | 2097 | /// |
| 2098 | /// [`None`] will be mapped to <code>[Ok]\([None])</code>. | |
| 2099 | /// <code>[Some]\([Ok]\(\_))</code> and <code>[Some]\([Err]\(\_))</code> will be mapped to | |
| 2100 | /// <code>[Ok]\([Some]\(\_))</code> and <code>[Err]\(\_)</code>. | |
| 2098 | /// <code>[Some]\([Ok]\(\_))</code> is mapped to <code>[Ok]\([Some]\(\_))</code>, | |
| 2099 | /// <code>[Some]\([Err]\(\_))</code> is mapped to <code>[Err]\(\_)</code>, | |
| 2100 | /// and [`None`] will be mapped to <code>[Ok]\([None])</code>. | |
| 2101 | 2101 | /// |
| 2102 | 2102 | /// # Examples |
| 2103 | 2103 | /// |
| ... | ... | @@ -2105,9 +2105,9 @@ impl<T, E> Option<Result<T, E>> { |
| 2105 | 2105 | /// #[derive(Debug, Eq, PartialEq)] |
| 2106 | 2106 | /// struct SomeErr; |
| 2107 | 2107 | /// |
| 2108 | /// let x: Result<Option<i32>, SomeErr> = Ok(Some(5)); | |
| 2109 | /// let y: Option<Result<i32, SomeErr>> = Some(Ok(5)); | |
| 2110 | /// assert_eq!(x, y.transpose()); | |
| 2108 | /// let x: Option<Result<i32, SomeErr>> = Some(Ok(5)); | |
| 2109 | /// let y: Result<Option<i32>, SomeErr> = Ok(Some(5)); | |
| 2110 | /// assert_eq!(x.transpose(), y); | |
| 2111 | 2111 | /// ``` |
| 2112 | 2112 | #[inline] |
| 2113 | 2113 | #[stable(feature = "transpose_result", since = "1.33.0")] |
library/core/src/prelude/v1.rs-7| ... | ... | @@ -117,10 +117,3 @@ pub use crate::macros::builtin::deref; |
| 117 | 117 | reason = "`type_alias_impl_trait` has open design concerns" |
| 118 | 118 | )] |
| 119 | 119 | pub use crate::macros::builtin::define_opaque; |
| 120 | ||
| 121 | #[unstable( | |
| 122 | feature = "derive_from", | |
| 123 | issue = "144889", | |
| 124 | reason = "`derive(From)` is unstable" | |
| 125 | )] | |
| 126 | pub use crate::macros::builtin::From; |
library/coretests/tests/lib.rs-1| ... | ... | @@ -56,7 +56,6 @@ |
| 56 | 56 | #![feature(hashmap_internals)] |
| 57 | 57 | #![feature(int_roundings)] |
| 58 | 58 | #![feature(ip)] |
| 59 | #![feature(ip_from)] | |
| 60 | 59 | #![feature(is_ascii_octdigit)] |
| 61 | 60 | #![feature(isolate_most_least_significant_one)] |
| 62 | 61 | #![feature(iter_advance_by)] |
library/std/src/lib.rs+8| ... | ... | @@ -738,6 +738,14 @@ pub use core::{ |
| 738 | 738 | unreachable, write, writeln, |
| 739 | 739 | }; |
| 740 | 740 | |
| 741 | // Re-export unstable derive macro defined through core. | |
| 742 | #[unstable(feature = "derive_from", issue = "144889")] | |
| 743 | /// Unstable module containing the unstable `From` derive macro. | |
| 744 | pub mod from { | |
| 745 | #[unstable(feature = "derive_from", issue = "144889")] | |
| 746 | pub use core::from::From; | |
| 747 | } | |
| 748 | ||
| 741 | 749 | // Include a number of private modules that exist solely to provide |
| 742 | 750 | // the rustdoc documentation for primitive types. Using `include!` |
| 743 | 751 | // because rustdoc only looks for these modules at the crate level. |
library/std/src/path.rs+65| ... | ... | @@ -2105,6 +2105,38 @@ impl PartialEq for PathBuf { |
| 2105 | 2105 | } |
| 2106 | 2106 | } |
| 2107 | 2107 | |
| 2108 | #[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] | |
| 2109 | impl cmp::PartialEq<str> for PathBuf { | |
| 2110 | #[inline] | |
| 2111 | fn eq(&self, other: &str) -> bool { | |
| 2112 | &*self == other | |
| 2113 | } | |
| 2114 | } | |
| 2115 | ||
| 2116 | #[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] | |
| 2117 | impl cmp::PartialEq<PathBuf> for str { | |
| 2118 | #[inline] | |
| 2119 | fn eq(&self, other: &PathBuf) -> bool { | |
| 2120 | other == self | |
| 2121 | } | |
| 2122 | } | |
| 2123 | ||
| 2124 | #[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] | |
| 2125 | impl cmp::PartialEq<String> for PathBuf { | |
| 2126 | #[inline] | |
| 2127 | fn eq(&self, other: &String) -> bool { | |
| 2128 | **self == **other | |
| 2129 | } | |
| 2130 | } | |
| 2131 | ||
| 2132 | #[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] | |
| 2133 | impl cmp::PartialEq<PathBuf> for String { | |
| 2134 | #[inline] | |
| 2135 | fn eq(&self, other: &PathBuf) -> bool { | |
| 2136 | other == self | |
| 2137 | } | |
| 2138 | } | |
| 2139 | ||
| 2108 | 2140 | #[stable(feature = "rust1", since = "1.0.0")] |
| 2109 | 2141 | impl Hash for PathBuf { |
| 2110 | 2142 | fn hash<H: Hasher>(&self, h: &mut H) { |
| ... | ... | @@ -3366,6 +3398,39 @@ impl PartialEq for Path { |
| 3366 | 3398 | } |
| 3367 | 3399 | } |
| 3368 | 3400 | |
| 3401 | #[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] | |
| 3402 | impl cmp::PartialEq<str> for Path { | |
| 3403 | #[inline] | |
| 3404 | fn eq(&self, other: &str) -> bool { | |
| 3405 | let other: &OsStr = other.as_ref(); | |
| 3406 | self == other | |
| 3407 | } | |
| 3408 | } | |
| 3409 | ||
| 3410 | #[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] | |
| 3411 | impl cmp::PartialEq<Path> for str { | |
| 3412 | #[inline] | |
| 3413 | fn eq(&self, other: &Path) -> bool { | |
| 3414 | other == self | |
| 3415 | } | |
| 3416 | } | |
| 3417 | ||
| 3418 | #[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] | |
| 3419 | impl cmp::PartialEq<String> for Path { | |
| 3420 | #[inline] | |
| 3421 | fn eq(&self, other: &String) -> bool { | |
| 3422 | self == &*other | |
| 3423 | } | |
| 3424 | } | |
| 3425 | ||
| 3426 | #[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] | |
| 3427 | impl cmp::PartialEq<Path> for String { | |
| 3428 | #[inline] | |
| 3429 | fn eq(&self, other: &Path) -> bool { | |
| 3430 | other == self | |
| 3431 | } | |
| 3432 | } | |
| 3433 | ||
| 3369 | 3434 | #[stable(feature = "rust1", since = "1.0.0")] |
| 3370 | 3435 | impl Hash for Path { |
| 3371 | 3436 | fn hash<H: Hasher>(&self, h: &mut H) { |
library/std/tests/thread.rs+1| ... | ... | @@ -19,6 +19,7 @@ fn sleep_very_long() { |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | #[test] |
| 22 | #[cfg_attr(target_env = "sgx", ignore = "Time within SGX enclave cannot be trusted")] | |
| 22 | 23 | fn sleep_until() { |
| 23 | 24 | let now = Instant::now(); |
| 24 | 25 | let period = Duration::from_millis(100); |
src/bootstrap/src/bin/rustc.rs+10| ... | ... | @@ -179,6 +179,16 @@ fn main() { |
| 179 | 179 | } |
| 180 | 180 | } |
| 181 | 181 | |
| 182 | // Here we pass additional paths that essentially act as a sysroot. | |
| 183 | // These are used to load rustc crates (e.g. `extern crate rustc_ast;`) | |
| 184 | // for rustc_private tools, so that we do not have to copy them into the | |
| 185 | // actual sysroot of the compiler that builds the tool. | |
| 186 | if let Ok(dirs) = env::var("RUSTC_ADDITIONAL_SYSROOT_PATHS") { | |
| 187 | for dir in dirs.split(",") { | |
| 188 | cmd.arg(format!("-L{dir}")); | |
| 189 | } | |
| 190 | } | |
| 191 | ||
| 182 | 192 | // Force all crates compiled by this compiler to (a) be unstable and (b) |
| 183 | 193 | // allow the `rustc_private` feature to link to other unstable crates |
| 184 | 194 | // also in the sysroot. We also do this for host crates, since those |
src/bootstrap/src/core/build_steps/check.rs+277-75| ... | ... | @@ -1,5 +1,8 @@ |
| 1 | 1 | //! Implementation of compiling the compiler and standard library, in "check"-based modes. |
| 2 | 2 | |
| 3 | use std::fs; | |
| 4 | use std::path::{Path, PathBuf}; | |
| 5 | ||
| 3 | 6 | use crate::core::build_steps::compile::{ |
| 4 | 7 | add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, |
| 5 | 8 | }; |
| ... | ... | @@ -9,11 +12,11 @@ use crate::core::build_steps::tool::{ |
| 9 | 12 | prepare_tool_cargo, |
| 10 | 13 | }; |
| 11 | 14 | use crate::core::builder::{ |
| 12 | self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, | |
| 15 | self, Alias, Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, | |
| 13 | 16 | }; |
| 14 | 17 | use crate::core::config::TargetSelection; |
| 15 | 18 | use crate::utils::build_stamp::{self, BuildStamp}; |
| 16 | use crate::{CodegenBackendKind, Compiler, Mode, Subcommand}; | |
| 19 | use crate::{CodegenBackendKind, Compiler, Mode, Subcommand, t}; | |
| 17 | 20 | |
| 18 | 21 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 19 | 22 | pub struct Std { |
| ... | ... | @@ -33,7 +36,7 @@ impl Std { |
| 33 | 36 | } |
| 34 | 37 | |
| 35 | 38 | impl Step for Std { |
| 36 | type Output = (); | |
| 39 | type Output = BuildStamp; | |
| 37 | 40 | const DEFAULT: bool = true; |
| 38 | 41 | |
| 39 | 42 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
| ... | ... | @@ -60,13 +63,14 @@ impl Step for Std { |
| 60 | 63 | |
| 61 | 64 | let crates = std_crates_for_run_make(&run); |
| 62 | 65 | run.builder.ensure(Std { |
| 63 | build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std), | |
| 66 | build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std) | |
| 67 | .build_compiler(), | |
| 64 | 68 | target: run.target, |
| 65 | 69 | crates, |
| 66 | 70 | }); |
| 67 | 71 | } |
| 68 | 72 | |
| 69 | fn run(self, builder: &Builder<'_>) { | |
| 73 | fn run(self, builder: &Builder<'_>) -> Self::Output { | |
| 70 | 74 | let build_compiler = self.build_compiler; |
| 71 | 75 | let target = self.target; |
| 72 | 76 | |
| ... | ... | @@ -93,18 +97,27 @@ impl Step for Std { |
| 93 | 97 | Kind::Check, |
| 94 | 98 | format_args!("library artifacts{}", crate_description(&self.crates)), |
| 95 | 99 | Mode::Std, |
| 96 | self.build_compiler, | |
| 100 | build_compiler, | |
| 97 | 101 | target, |
| 98 | 102 | ); |
| 99 | 103 | |
| 100 | let stamp = build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check"); | |
| 101 | run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); | |
| 104 | let check_stamp = | |
| 105 | build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check"); | |
| 106 | run_cargo( | |
| 107 | builder, | |
| 108 | cargo, | |
| 109 | builder.config.free_args.clone(), | |
| 110 | &check_stamp, | |
| 111 | vec![], | |
| 112 | true, | |
| 113 | false, | |
| 114 | ); | |
| 102 | 115 | |
| 103 | 116 | drop(_guard); |
| 104 | 117 | |
| 105 | 118 | // don't check test dependencies if we haven't built libtest |
| 106 | 119 | if !self.crates.iter().any(|krate| krate == "test") { |
| 107 | return; | |
| 120 | return check_stamp; | |
| 108 | 121 | } |
| 109 | 122 | |
| 110 | 123 | // Then run cargo again, once we've put the rmeta files for the library |
| ... | ... | @@ -137,10 +150,11 @@ impl Step for Std { |
| 137 | 150 | Kind::Check, |
| 138 | 151 | "library test/bench/example targets", |
| 139 | 152 | Mode::Std, |
| 140 | self.build_compiler, | |
| 153 | build_compiler, | |
| 141 | 154 | target, |
| 142 | 155 | ); |
| 143 | 156 | run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); |
| 157 | check_stamp | |
| 144 | 158 | } |
| 145 | 159 | |
| 146 | 160 | fn metadata(&self) -> Option<StepMetadata> { |
| ... | ... | @@ -148,12 +162,135 @@ impl Step for Std { |
| 148 | 162 | } |
| 149 | 163 | } |
| 150 | 164 | |
| 151 | /// Checks rustc using `build_compiler` and copies the built | |
| 152 | /// .rmeta files into the sysroot of `build_compiler`. | |
| 165 | /// Represents a proof that rustc was **checked**. | |
| 166 | /// Contains directories with .rmeta files generated by checking rustc for a specific | |
| 167 | /// target. | |
| 168 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | |
| 169 | struct RmetaSysroot { | |
| 170 | host_dir: PathBuf, | |
| 171 | target_dir: PathBuf, | |
| 172 | } | |
| 173 | ||
| 174 | impl RmetaSysroot { | |
| 175 | /// Copy rmeta artifacts from the given `stamp` into a sysroot located at `directory`. | |
| 176 | fn from_stamp( | |
| 177 | builder: &Builder<'_>, | |
| 178 | stamp: BuildStamp, | |
| 179 | target: TargetSelection, | |
| 180 | directory: &Path, | |
| 181 | ) -> Self { | |
| 182 | let host_dir = directory.join("host"); | |
| 183 | let target_dir = directory.join(target); | |
| 184 | let _ = fs::remove_dir_all(directory); | |
| 185 | t!(fs::create_dir_all(directory)); | |
| 186 | add_to_sysroot(builder, &target_dir, &host_dir, &stamp); | |
| 187 | ||
| 188 | Self { host_dir, target_dir } | |
| 189 | } | |
| 190 | ||
| 191 | /// Configure the given cargo invocation so that the compiled crate will be able to use | |
| 192 | /// rustc .rmeta artifacts that were previously generated. | |
| 193 | fn configure_cargo(&self, cargo: &mut Cargo) { | |
| 194 | cargo.append_to_env( | |
| 195 | "RUSTC_ADDITIONAL_SYSROOT_PATHS", | |
| 196 | format!("{},{}", self.host_dir.to_str().unwrap(), self.target_dir.to_str().unwrap()), | |
| 197 | ",", | |
| 198 | ); | |
| 199 | } | |
| 200 | } | |
| 201 | ||
| 202 | /// Checks rustc using the given `build_compiler` for the given `target`, and produces | |
| 203 | /// a sysroot in the build directory that stores the generated .rmeta files. | |
| 204 | /// | |
| 205 | /// This step exists so that we can store the generated .rmeta artifacts into a separate | |
| 206 | /// directory, instead of copying them into the sysroot of `build_compiler`, which would | |
| 207 | /// "pollute" it (that is especially problematic for the external stage0 rustc). | |
| 208 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | |
| 209 | struct PrepareRustcRmetaSysroot { | |
| 210 | build_compiler: CompilerForCheck, | |
| 211 | target: TargetSelection, | |
| 212 | } | |
| 213 | ||
| 214 | impl PrepareRustcRmetaSysroot { | |
| 215 | fn new(build_compiler: CompilerForCheck, target: TargetSelection) -> Self { | |
| 216 | Self { build_compiler, target } | |
| 217 | } | |
| 218 | } | |
| 219 | ||
| 220 | impl Step for PrepareRustcRmetaSysroot { | |
| 221 | type Output = RmetaSysroot; | |
| 222 | ||
| 223 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { | |
| 224 | run.never() | |
| 225 | } | |
| 226 | ||
| 227 | fn run(self, builder: &Builder<'_>) -> Self::Output { | |
| 228 | // Check rustc | |
| 229 | let stamp = builder.ensure(Rustc::from_build_compiler( | |
| 230 | self.build_compiler.clone(), | |
| 231 | self.target, | |
| 232 | vec![], | |
| 233 | )); | |
| 234 | ||
| 235 | let build_compiler = self.build_compiler.build_compiler(); | |
| 236 | ||
| 237 | // Copy the generated rmeta artifacts to a separate directory | |
| 238 | let dir = builder | |
| 239 | .out | |
| 240 | .join(build_compiler.host) | |
| 241 | .join(format!("stage{}-rustc-rmeta-artifacts", build_compiler.stage + 1)); | |
| 242 | RmetaSysroot::from_stamp(builder, stamp, self.target, &dir) | |
| 243 | } | |
| 244 | } | |
| 245 | ||
| 246 | /// Checks std using the given `build_compiler` for the given `target`, and produces | |
| 247 | /// a sysroot in the build directory that stores the generated .rmeta files. | |
| 248 | /// | |
| 249 | /// This step exists so that we can store the generated .rmeta artifacts into a separate | |
| 250 | /// directory, instead of copying them into the sysroot of `build_compiler`, which would | |
| 251 | /// "pollute" it (that is especially problematic for the external stage0 rustc). | |
| 252 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | |
| 253 | struct PrepareStdRmetaSysroot { | |
| 254 | build_compiler: Compiler, | |
| 255 | target: TargetSelection, | |
| 256 | } | |
| 257 | ||
| 258 | impl PrepareStdRmetaSysroot { | |
| 259 | fn new(build_compiler: Compiler, target: TargetSelection) -> Self { | |
| 260 | Self { build_compiler, target } | |
| 261 | } | |
| 262 | } | |
| 263 | ||
| 264 | impl Step for PrepareStdRmetaSysroot { | |
| 265 | type Output = RmetaSysroot; | |
| 266 | ||
| 267 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { | |
| 268 | run.never() | |
| 269 | } | |
| 270 | ||
| 271 | fn run(self, builder: &Builder<'_>) -> Self::Output { | |
| 272 | // Check std | |
| 273 | let stamp = builder.ensure(Std { | |
| 274 | build_compiler: self.build_compiler, | |
| 275 | target: self.target, | |
| 276 | crates: vec![], | |
| 277 | }); | |
| 278 | ||
| 279 | // Copy the generated rmeta artifacts to a separate directory | |
| 280 | let dir = builder | |
| 281 | .out | |
| 282 | .join(self.build_compiler.host) | |
| 283 | .join(format!("stage{}-std-rmeta-artifacts", self.build_compiler.stage)); | |
| 284 | ||
| 285 | RmetaSysroot::from_stamp(builder, stamp, self.target, &dir) | |
| 286 | } | |
| 287 | } | |
| 288 | ||
| 289 | /// Checks rustc using `build_compiler`. | |
| 153 | 290 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 154 | 291 | pub struct Rustc { |
| 155 | 292 | /// Compiler that will check this rustc. |
| 156 | pub build_compiler: Compiler, | |
| 293 | pub build_compiler: CompilerForCheck, | |
| 157 | 294 | pub target: TargetSelection, |
| 158 | 295 | /// Whether to build only a subset of crates. |
| 159 | 296 | /// |
| ... | ... | @@ -166,12 +303,20 @@ pub struct Rustc { |
| 166 | 303 | impl Rustc { |
| 167 | 304 | pub fn new(builder: &Builder<'_>, target: TargetSelection, crates: Vec<String>) -> Self { |
| 168 | 305 | let build_compiler = prepare_compiler_for_check(builder, target, Mode::Rustc); |
| 306 | Self::from_build_compiler(build_compiler, target, crates) | |
| 307 | } | |
| 308 | ||
| 309 | fn from_build_compiler( | |
| 310 | build_compiler: CompilerForCheck, | |
| 311 | target: TargetSelection, | |
| 312 | crates: Vec<String>, | |
| 313 | ) -> Self { | |
| 169 | 314 | Self { build_compiler, target, crates } |
| 170 | 315 | } |
| 171 | 316 | } |
| 172 | 317 | |
| 173 | 318 | impl Step for Rustc { |
| 174 | type Output = (); | |
| 319 | type Output = BuildStamp; | |
| 175 | 320 | const IS_HOST: bool = true; |
| 176 | 321 | const DEFAULT: bool = true; |
| 177 | 322 | |
| ... | ... | @@ -191,8 +336,8 @@ impl Step for Rustc { |
| 191 | 336 | /// created will also be linked into the sysroot directory. |
| 192 | 337 | /// |
| 193 | 338 | /// If we check a stage 2 compiler, we will have to first build a stage 1 compiler to check it. |
| 194 | fn run(self, builder: &Builder<'_>) { | |
| 195 | let build_compiler = self.build_compiler; | |
| 339 | fn run(self, builder: &Builder<'_>) -> Self::Output { | |
| 340 | let build_compiler = self.build_compiler.build_compiler; | |
| 196 | 341 | let target = self.target; |
| 197 | 342 | |
| 198 | 343 | let mut cargo = builder::Cargo::new( |
| ... | ... | @@ -205,6 +350,7 @@ impl Step for Rustc { |
| 205 | 350 | ); |
| 206 | 351 | |
| 207 | 352 | rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates); |
| 353 | self.build_compiler.configure_cargo(&mut cargo); | |
| 208 | 354 | |
| 209 | 355 | // Explicitly pass -p for all compiler crates -- this will force cargo |
| 210 | 356 | // to also check the tests/benches/examples for these crates, rather |
| ... | ... | @@ -217,7 +363,7 @@ impl Step for Rustc { |
| 217 | 363 | Kind::Check, |
| 218 | 364 | format_args!("compiler artifacts{}", crate_description(&self.crates)), |
| 219 | 365 | Mode::Rustc, |
| 220 | self.build_compiler, | |
| 366 | self.build_compiler.build_compiler(), | |
| 221 | 367 | target, |
| 222 | 368 | ); |
| 223 | 369 | |
| ... | ... | @@ -226,13 +372,12 @@ impl Step for Rustc { |
| 226 | 372 | |
| 227 | 373 | run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); |
| 228 | 374 | |
| 229 | let libdir = builder.sysroot_target_libdir(build_compiler, target); | |
| 230 | let hostdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host); | |
| 231 | add_to_sysroot(builder, &libdir, &hostdir, &stamp); | |
| 375 | stamp | |
| 232 | 376 | } |
| 233 | 377 | |
| 234 | 378 | fn metadata(&self) -> Option<StepMetadata> { |
| 235 | let metadata = StepMetadata::check("rustc", self.target).built_by(self.build_compiler); | |
| 379 | let metadata = StepMetadata::check("rustc", self.target) | |
| 380 | .built_by(self.build_compiler.build_compiler()); | |
| 236 | 381 | let metadata = if self.crates.is_empty() { |
| 237 | 382 | metadata |
| 238 | 383 | } else { |
| ... | ... | @@ -242,45 +387,101 @@ impl Step for Rustc { |
| 242 | 387 | } |
| 243 | 388 | } |
| 244 | 389 | |
| 390 | /// Represents a compiler that can check something. | |
| 391 | /// | |
| 392 | /// If the compiler was created for `Mode::ToolRustc` or `Mode::Codegen`, it will also contain | |
| 393 | /// .rmeta artifacts from rustc that was already checked using `build_compiler`. | |
| 394 | /// | |
| 395 | /// All steps that use this struct in a "general way" (i.e. they don't know exactly what kind of | |
| 396 | /// thing is being built) should call `configure_cargo` to ensure that the rmeta artifacts are | |
| 397 | /// properly linked, if present. | |
| 398 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | |
| 399 | pub struct CompilerForCheck { | |
| 400 | build_compiler: Compiler, | |
| 401 | rustc_rmeta_sysroot: Option<RmetaSysroot>, | |
| 402 | std_rmeta_sysroot: Option<RmetaSysroot>, | |
| 403 | } | |
| 404 | ||
| 405 | impl CompilerForCheck { | |
| 406 | pub fn build_compiler(&self) -> Compiler { | |
| 407 | self.build_compiler | |
| 408 | } | |
| 409 | ||
| 410 | /// If there are any rustc rmeta artifacts available, configure the Cargo invocation | |
| 411 | /// so that the artifact being built can find them. | |
| 412 | pub fn configure_cargo(&self, cargo: &mut Cargo) { | |
| 413 | if let Some(sysroot) = &self.rustc_rmeta_sysroot { | |
| 414 | sysroot.configure_cargo(cargo); | |
| 415 | } | |
| 416 | if let Some(sysroot) = &self.std_rmeta_sysroot { | |
| 417 | sysroot.configure_cargo(cargo); | |
| 418 | } | |
| 419 | } | |
| 420 | } | |
| 421 | ||
| 422 | /// Prepare the standard library for checking something (that requires stdlib) using | |
| 423 | /// `build_compiler`. | |
| 424 | fn prepare_std( | |
| 425 | builder: &Builder<'_>, | |
| 426 | build_compiler: Compiler, | |
| 427 | target: TargetSelection, | |
| 428 | ) -> Option<RmetaSysroot> { | |
| 429 | // We need to build the host stdlib even if we only check, to compile build scripts and proc | |
| 430 | // macros | |
| 431 | builder.std(build_compiler, builder.host_target); | |
| 432 | ||
| 433 | // If we're cross-compiling, we generate the rmeta files for the given target | |
| 434 | // This check has to be here, because if we generate both .so and .rmeta files, rustc will fail, | |
| 435 | // as it will have multiple candidates for linking. | |
| 436 | if builder.host_target != target { | |
| 437 | Some(builder.ensure(PrepareStdRmetaSysroot::new(build_compiler, target))) | |
| 438 | } else { | |
| 439 | None | |
| 440 | } | |
| 441 | } | |
| 442 | ||
| 245 | 443 | /// Prepares a compiler that will check something with the given `mode`. |
| 246 | 444 | pub fn prepare_compiler_for_check( |
| 247 | 445 | builder: &Builder<'_>, |
| 248 | 446 | target: TargetSelection, |
| 249 | 447 | mode: Mode, |
| 250 | ) -> Compiler { | |
| 448 | ) -> CompilerForCheck { | |
| 251 | 449 | let host = builder.host_target; |
| 252 | 450 | |
| 253 | match mode { | |
| 451 | let mut rustc_rmeta_sysroot = None; | |
| 452 | let mut std_rmeta_sysroot = None; | |
| 453 | let build_compiler = match mode { | |
| 254 | 454 | Mode::ToolBootstrap => builder.compiler(0, host), |
| 455 | // We could also only check std here and use `prepare_std`, but `ToolTarget` is currently | |
| 456 | // only used for running in-tree Clippy on bootstrap tools, so it does not seem worth it to | |
| 457 | // optimize it. Therefore, here we build std for the target, instead of just checking it. | |
| 255 | 458 | Mode::ToolTarget => get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target)), |
| 256 | 459 | Mode::ToolStd => { |
| 257 | 460 | if builder.config.compile_time_deps { |
| 258 | 461 | // When --compile-time-deps is passed, we can't use any rustc |
| 259 | 462 | // other than the bootstrap compiler. Luckily build scripts and |
| 260 | 463 | // proc macros for tools are unlikely to need nightly. |
| 261 | return builder.compiler(0, host); | |
| 464 | builder.compiler(0, host) | |
| 465 | } else { | |
| 466 | // These tools require the local standard library to be checked | |
| 467 | let build_compiler = builder.compiler(builder.top_stage, host); | |
| 468 | std_rmeta_sysroot = prepare_std(builder, build_compiler, target); | |
| 469 | build_compiler | |
| 262 | 470 | } |
| 263 | ||
| 264 | // These tools require the local standard library to be checked | |
| 265 | let build_compiler = builder.compiler(builder.top_stage, host); | |
| 266 | ||
| 267 | // We need to build the host stdlib to check the tool itself. | |
| 268 | // We need to build the target stdlib so that the tool can link to it. | |
| 269 | builder.std(build_compiler, host); | |
| 270 | // We could only check this library in theory, but `check::Std` doesn't copy rmetas | |
| 271 | // into `build_compiler`'s sysroot to avoid clashes with `.rlibs`, so we build it | |
| 272 | // instead. | |
| 273 | builder.std(build_compiler, target); | |
| 274 | build_compiler | |
| 275 | 471 | } |
| 276 | 472 | Mode::ToolRustc | Mode::Codegen => { |
| 277 | 473 | // Check Rustc to produce the required rmeta artifacts for rustc_private, and then |
| 278 | 474 | // return the build compiler that was used to check rustc. |
| 279 | 475 | // We do not need to check examples/tests/etc. of Rustc for rustc_private, so we pass |
| 280 | 476 | // an empty set of crates, which will avoid using `cargo -p`. |
| 281 | let check = Rustc::new(builder, target, vec![]); | |
| 282 | let build_compiler = check.build_compiler; | |
| 283 | builder.ensure(check); | |
| 477 | let compiler_for_rustc = prepare_compiler_for_check(builder, target, Mode::Rustc); | |
| 478 | rustc_rmeta_sysroot = Some( | |
| 479 | builder.ensure(PrepareRustcRmetaSysroot::new(compiler_for_rustc.clone(), target)), | |
| 480 | ); | |
| 481 | let build_compiler = compiler_for_rustc.build_compiler(); | |
| 482 | ||
| 483 | // To check a rustc_private tool, we also need to check std that it will link to | |
| 484 | std_rmeta_sysroot = prepare_std(builder, build_compiler, target); | |
| 284 | 485 | build_compiler |
| 285 | 486 | } |
| 286 | 487 | Mode::Rustc => { |
| ... | ... | @@ -294,15 +495,8 @@ pub fn prepare_compiler_for_check( |
| 294 | 495 | let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage }; |
| 295 | 496 | let build_compiler = builder.compiler(stage, host); |
| 296 | 497 | |
| 297 | // Build host std for compiling build scripts | |
| 298 | builder.std(build_compiler, build_compiler.host); | |
| 299 | ||
| 300 | // Build target std so that the checked rustc can link to it during the check | |
| 301 | // FIXME: maybe we can a way to only do a check of std here? | |
| 302 | // But for that we would have to copy the stdlib rmetas to the sysroot of the build | |
| 303 | // compiler, which conflicts with std rlibs, if we also build std. | |
| 304 | builder.std(build_compiler, target); | |
| 305 | ||
| 498 | // To check rustc, we need to check std that it will link to | |
| 499 | std_rmeta_sysroot = prepare_std(builder, build_compiler, target); | |
| 306 | 500 | build_compiler |
| 307 | 501 | } |
| 308 | 502 | Mode::Std => { |
| ... | ... | @@ -311,13 +505,14 @@ pub fn prepare_compiler_for_check( |
| 311 | 505 | // stage 0 stdlib is used to compile build scripts and proc macros. |
| 312 | 506 | builder.compiler(builder.top_stage, host) |
| 313 | 507 | } |
| 314 | } | |
| 508 | }; | |
| 509 | CompilerForCheck { build_compiler, rustc_rmeta_sysroot, std_rmeta_sysroot } | |
| 315 | 510 | } |
| 316 | 511 | |
| 317 | 512 | /// Check the Cranelift codegen backend. |
| 318 | 513 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 319 | 514 | pub struct CraneliftCodegenBackend { |
| 320 | build_compiler: Compiler, | |
| 515 | build_compiler: CompilerForCheck, | |
| 321 | 516 | target: TargetSelection, |
| 322 | 517 | } |
| 323 | 518 | |
| ... | ... | @@ -332,12 +527,14 @@ impl Step for CraneliftCodegenBackend { |
| 332 | 527 | } |
| 333 | 528 | |
| 334 | 529 | fn make_run(run: RunConfig<'_>) { |
| 335 | let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen); | |
| 336 | run.builder.ensure(CraneliftCodegenBackend { build_compiler, target: run.target }); | |
| 530 | run.builder.ensure(CraneliftCodegenBackend { | |
| 531 | build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Codegen), | |
| 532 | target: run.target, | |
| 533 | }); | |
| 337 | 534 | } |
| 338 | 535 | |
| 339 | 536 | fn run(self, builder: &Builder<'_>) { |
| 340 | let build_compiler = self.build_compiler; | |
| 537 | let build_compiler = self.build_compiler.build_compiler(); | |
| 341 | 538 | let target = self.target; |
| 342 | 539 | |
| 343 | 540 | let mut cargo = builder::Cargo::new( |
| ... | ... | @@ -353,12 +550,13 @@ impl Step for CraneliftCodegenBackend { |
| 353 | 550 | .arg("--manifest-path") |
| 354 | 551 | .arg(builder.src.join("compiler/rustc_codegen_cranelift/Cargo.toml")); |
| 355 | 552 | rustc_cargo_env(builder, &mut cargo, target); |
| 553 | self.build_compiler.configure_cargo(&mut cargo); | |
| 356 | 554 | |
| 357 | 555 | let _guard = builder.msg( |
| 358 | 556 | Kind::Check, |
| 359 | 557 | "rustc_codegen_cranelift", |
| 360 | 558 | Mode::Codegen, |
| 361 | self.build_compiler, | |
| 559 | build_compiler, | |
| 362 | 560 | target, |
| 363 | 561 | ); |
| 364 | 562 | |
| ... | ... | @@ -376,7 +574,7 @@ impl Step for CraneliftCodegenBackend { |
| 376 | 574 | fn metadata(&self) -> Option<StepMetadata> { |
| 377 | 575 | Some( |
| 378 | 576 | StepMetadata::check("rustc_codegen_cranelift", self.target) |
| 379 | .built_by(self.build_compiler), | |
| 577 | .built_by(self.build_compiler.build_compiler()), | |
| 380 | 578 | ) |
| 381 | 579 | } |
| 382 | 580 | } |
| ... | ... | @@ -384,7 +582,7 @@ impl Step for CraneliftCodegenBackend { |
| 384 | 582 | /// Check the GCC codegen backend. |
| 385 | 583 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 386 | 584 | pub struct GccCodegenBackend { |
| 387 | build_compiler: Compiler, | |
| 585 | build_compiler: CompilerForCheck, | |
| 388 | 586 | target: TargetSelection, |
| 389 | 587 | } |
| 390 | 588 | |
| ... | ... | @@ -399,8 +597,10 @@ impl Step for GccCodegenBackend { |
| 399 | 597 | } |
| 400 | 598 | |
| 401 | 599 | fn make_run(run: RunConfig<'_>) { |
| 402 | let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen); | |
| 403 | run.builder.ensure(GccCodegenBackend { build_compiler, target: run.target }); | |
| 600 | run.builder.ensure(GccCodegenBackend { | |
| 601 | build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Codegen), | |
| 602 | target: run.target, | |
| 603 | }); | |
| 404 | 604 | } |
| 405 | 605 | |
| 406 | 606 | fn run(self, builder: &Builder<'_>) { |
| ... | ... | @@ -410,7 +610,7 @@ impl Step for GccCodegenBackend { |
| 410 | 610 | return; |
| 411 | 611 | } |
| 412 | 612 | |
| 413 | let build_compiler = self.build_compiler; | |
| 613 | let build_compiler = self.build_compiler.build_compiler(); | |
| 414 | 614 | let target = self.target; |
| 415 | 615 | |
| 416 | 616 | let mut cargo = builder::Cargo::new( |
| ... | ... | @@ -424,14 +624,10 @@ impl Step for GccCodegenBackend { |
| 424 | 624 | |
| 425 | 625 | cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml")); |
| 426 | 626 | rustc_cargo_env(builder, &mut cargo, target); |
| 627 | self.build_compiler.configure_cargo(&mut cargo); | |
| 427 | 628 | |
| 428 | let _guard = builder.msg( | |
| 429 | Kind::Check, | |
| 430 | "rustc_codegen_gcc", | |
| 431 | Mode::Codegen, | |
| 432 | self.build_compiler, | |
| 433 | target, | |
| 434 | ); | |
| 629 | let _guard = | |
| 630 | builder.msg(Kind::Check, "rustc_codegen_gcc", Mode::Codegen, build_compiler, target); | |
| 435 | 631 | |
| 436 | 632 | let stamp = build_stamp::codegen_backend_stamp( |
| 437 | 633 | builder, |
| ... | ... | @@ -445,7 +641,10 @@ impl Step for GccCodegenBackend { |
| 445 | 641 | } |
| 446 | 642 | |
| 447 | 643 | fn metadata(&self) -> Option<StepMetadata> { |
| 448 | Some(StepMetadata::check("rustc_codegen_gcc", self.target).built_by(self.build_compiler)) | |
| 644 | Some( | |
| 645 | StepMetadata::check("rustc_codegen_gcc", self.target) | |
| 646 | .built_by(self.build_compiler.build_compiler()), | |
| 647 | ) | |
| 449 | 648 | } |
| 450 | 649 | } |
| 451 | 650 | |
| ... | ... | @@ -467,8 +666,8 @@ macro_rules! tool_check_step { |
| 467 | 666 | ) => { |
| 468 | 667 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 469 | 668 | pub struct $name { |
| 470 | pub build_compiler: Compiler, | |
| 471 | pub target: TargetSelection, | |
| 669 | compiler: CompilerForCheck, | |
| 670 | target: TargetSelection, | |
| 472 | 671 | } |
| 473 | 672 | |
| 474 | 673 | impl Step for $name { |
| ... | ... | @@ -486,7 +685,7 @@ macro_rules! tool_check_step { |
| 486 | 685 | let builder = run.builder; |
| 487 | 686 | let mode = $mode(builder); |
| 488 | 687 | |
| 489 | let build_compiler = prepare_compiler_for_check(run.builder, target, mode); | |
| 688 | let compiler = prepare_compiler_for_check(run.builder, target, mode); | |
| 490 | 689 | |
| 491 | 690 | // It doesn't make sense to cross-check bootstrap tools |
| 492 | 691 | if mode == Mode::ToolBootstrap && target != run.builder.host_target { |
| ... | ... | @@ -494,11 +693,11 @@ macro_rules! tool_check_step { |
| 494 | 693 | return; |
| 495 | 694 | }; |
| 496 | 695 | |
| 497 | run.builder.ensure($name { target, build_compiler }); | |
| 696 | run.builder.ensure($name { target, compiler }); | |
| 498 | 697 | } |
| 499 | 698 | |
| 500 | 699 | fn run(self, builder: &Builder<'_>) { |
| 501 | let Self { target, build_compiler } = self; | |
| 700 | let Self { target, compiler } = self; | |
| 502 | 701 | let allow_features = { |
| 503 | 702 | let mut _value = ""; |
| 504 | 703 | $( _value = $allow_features; )? |
| ... | ... | @@ -506,11 +705,11 @@ macro_rules! tool_check_step { |
| 506 | 705 | }; |
| 507 | 706 | let extra_features: &[&str] = &[$($($enable_features),*)?]; |
| 508 | 707 | let mode = $mode(builder); |
| 509 | run_tool_check_step(builder, build_compiler, target, $path, mode, allow_features, extra_features); | |
| 708 | run_tool_check_step(builder, compiler, target, $path, mode, allow_features, extra_features); | |
| 510 | 709 | } |
| 511 | 710 | |
| 512 | 711 | fn metadata(&self) -> Option<StepMetadata> { |
| 513 | Some(StepMetadata::check(stringify!($name), self.target).built_by(self.build_compiler)) | |
| 712 | Some(StepMetadata::check(stringify!($name), self.target).built_by(self.compiler.build_compiler)) | |
| 514 | 713 | } |
| 515 | 714 | } |
| 516 | 715 | } |
| ... | ... | @@ -519,7 +718,7 @@ macro_rules! tool_check_step { |
| 519 | 718 | /// Used by the implementation of `Step::run` in `tool_check_step!`. |
| 520 | 719 | fn run_tool_check_step( |
| 521 | 720 | builder: &Builder<'_>, |
| 522 | build_compiler: Compiler, | |
| 721 | compiler: CompilerForCheck, | |
| 523 | 722 | target: TargetSelection, |
| 524 | 723 | path: &str, |
| 525 | 724 | mode: Mode, |
| ... | ... | @@ -528,6 +727,8 @@ fn run_tool_check_step( |
| 528 | 727 | ) { |
| 529 | 728 | let display_name = path.rsplit('/').next().unwrap(); |
| 530 | 729 | |
| 730 | let build_compiler = compiler.build_compiler(); | |
| 731 | ||
| 531 | 732 | let extra_features = extra_features.iter().map(|f| f.to_string()).collect::<Vec<String>>(); |
| 532 | 733 | let mut cargo = prepare_tool_cargo( |
| 533 | 734 | builder, |
| ... | ... | @@ -544,6 +745,7 @@ fn run_tool_check_step( |
| 544 | 745 | &extra_features, |
| 545 | 746 | ); |
| 546 | 747 | cargo.allow_features(allow_features); |
| 748 | compiler.configure_cargo(&mut cargo); | |
| 547 | 749 | |
| 548 | 750 | // FIXME: check bootstrap doesn't currently work when multiple targets are checked |
| 549 | 751 | // FIXME: rust-analyzer does not work with --all-targets |
src/bootstrap/src/core/build_steps/clippy.rs+21-12| ... | ... | @@ -18,7 +18,7 @@ use build_helper::exit; |
| 18 | 18 | use super::compile::{run_cargo, rustc_cargo, std_cargo}; |
| 19 | 19 | use super::tool::{SourceType, prepare_tool_cargo}; |
| 20 | 20 | use crate::builder::{Builder, ShouldRun}; |
| 21 | use crate::core::build_steps::check::prepare_compiler_for_check; | |
| 21 | use crate::core::build_steps::check::{CompilerForCheck, prepare_compiler_for_check}; | |
| 22 | 22 | use crate::core::build_steps::compile::std_crates_for_run_make; |
| 23 | 23 | use crate::core::builder; |
| 24 | 24 | use crate::core::builder::{Alias, Kind, RunConfig, Step, StepMetadata, crate_description}; |
| ... | ... | @@ -231,7 +231,7 @@ impl Step for Std { |
| 231 | 231 | /// in-tree rustc. |
| 232 | 232 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 233 | 233 | pub struct Rustc { |
| 234 | build_compiler: Compiler, | |
| 234 | build_compiler: CompilerForCheck, | |
| 235 | 235 | target: TargetSelection, |
| 236 | 236 | config: LintConfig, |
| 237 | 237 | /// Whether to lint only a subset of crates. |
| ... | ... | @@ -271,7 +271,7 @@ impl Step for Rustc { |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | 273 | fn run(self, builder: &Builder<'_>) { |
| 274 | let build_compiler = self.build_compiler; | |
| 274 | let build_compiler = self.build_compiler.build_compiler(); | |
| 275 | 275 | let target = self.target; |
| 276 | 276 | |
| 277 | 277 | let mut cargo = builder::Cargo::new( |
| ... | ... | @@ -284,6 +284,7 @@ impl Step for Rustc { |
| 284 | 284 | ); |
| 285 | 285 | |
| 286 | 286 | rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates); |
| 287 | self.build_compiler.configure_cargo(&mut cargo); | |
| 287 | 288 | |
| 288 | 289 | // Explicitly pass -p for all compiler crates -- this will force cargo |
| 289 | 290 | // to also lint the tests/benches/examples for these crates, rather |
| ... | ... | @@ -312,13 +313,16 @@ impl Step for Rustc { |
| 312 | 313 | } |
| 313 | 314 | |
| 314 | 315 | fn metadata(&self) -> Option<StepMetadata> { |
| 315 | Some(StepMetadata::clippy("rustc", self.target).built_by(self.build_compiler)) | |
| 316 | Some( | |
| 317 | StepMetadata::clippy("rustc", self.target) | |
| 318 | .built_by(self.build_compiler.build_compiler()), | |
| 319 | ) | |
| 316 | 320 | } |
| 317 | 321 | } |
| 318 | 322 | |
| 319 | 323 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
| 320 | 324 | pub struct CodegenGcc { |
| 321 | build_compiler: Compiler, | |
| 325 | build_compiler: CompilerForCheck, | |
| 322 | 326 | target: TargetSelection, |
| 323 | 327 | config: LintConfig, |
| 324 | 328 | } |
| ... | ... | @@ -347,10 +351,10 @@ impl Step for CodegenGcc { |
| 347 | 351 | } |
| 348 | 352 | |
| 349 | 353 | fn run(self, builder: &Builder<'_>) -> Self::Output { |
| 350 | let build_compiler = self.build_compiler; | |
| 354 | let build_compiler = self.build_compiler.build_compiler(); | |
| 351 | 355 | let target = self.target; |
| 352 | 356 | |
| 353 | let cargo = prepare_tool_cargo( | |
| 357 | let mut cargo = prepare_tool_cargo( | |
| 354 | 358 | builder, |
| 355 | 359 | build_compiler, |
| 356 | 360 | Mode::Codegen, |
| ... | ... | @@ -360,6 +364,7 @@ impl Step for CodegenGcc { |
| 360 | 364 | SourceType::InTree, |
| 361 | 365 | &[], |
| 362 | 366 | ); |
| 367 | self.build_compiler.configure_cargo(&mut cargo); | |
| 363 | 368 | |
| 364 | 369 | let _guard = |
| 365 | 370 | builder.msg(Kind::Clippy, "rustc_codegen_gcc", Mode::ToolRustc, build_compiler, target); |
| ... | ... | @@ -379,7 +384,10 @@ impl Step for CodegenGcc { |
| 379 | 384 | } |
| 380 | 385 | |
| 381 | 386 | fn metadata(&self) -> Option<StepMetadata> { |
| 382 | Some(StepMetadata::clippy("rustc_codegen_gcc", self.target).built_by(self.build_compiler)) | |
| 387 | Some( | |
| 388 | StepMetadata::clippy("rustc_codegen_gcc", self.target) | |
| 389 | .built_by(self.build_compiler.build_compiler()), | |
| 390 | ) | |
| 383 | 391 | } |
| 384 | 392 | } |
| 385 | 393 | |
| ... | ... | @@ -396,7 +404,7 @@ macro_rules! lint_any { |
| 396 | 404 | |
| 397 | 405 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
| 398 | 406 | pub struct $name { |
| 399 | build_compiler: Compiler, | |
| 407 | build_compiler: CompilerForCheck, | |
| 400 | 408 | target: TargetSelection, |
| 401 | 409 | config: LintConfig, |
| 402 | 410 | } |
| ... | ... | @@ -419,9 +427,9 @@ macro_rules! lint_any { |
| 419 | 427 | } |
| 420 | 428 | |
| 421 | 429 | fn run(self, builder: &Builder<'_>) -> Self::Output { |
| 422 | let build_compiler = self.build_compiler; | |
| 430 | let build_compiler = self.build_compiler.build_compiler(); | |
| 423 | 431 | let target = self.target; |
| 424 | let cargo = prepare_tool_cargo( | |
| 432 | let mut cargo = prepare_tool_cargo( | |
| 425 | 433 | builder, |
| 426 | 434 | build_compiler, |
| 427 | 435 | $mode, |
| ... | ... | @@ -431,6 +439,7 @@ macro_rules! lint_any { |
| 431 | 439 | SourceType::InTree, |
| 432 | 440 | &[], |
| 433 | 441 | ); |
| 442 | self.build_compiler.configure_cargo(&mut cargo); | |
| 434 | 443 | |
| 435 | 444 | let _guard = builder.msg( |
| 436 | 445 | Kind::Clippy, |
| ... | ... | @@ -456,7 +465,7 @@ macro_rules! lint_any { |
| 456 | 465 | } |
| 457 | 466 | |
| 458 | 467 | fn metadata(&self) -> Option<StepMetadata> { |
| 459 | Some(StepMetadata::clippy($readable_name, self.target).built_by(self.build_compiler)) | |
| 468 | Some(StepMetadata::clippy($readable_name, self.target).built_by(self.build_compiler.build_compiler())) | |
| 460 | 469 | } |
| 461 | 470 | } |
| 462 | 471 | )+ |
src/bootstrap/src/core/build_steps/compile.rs+78-34| ... | ... | @@ -933,6 +933,15 @@ fn cp_rustc_component_to_ci_sysroot(builder: &Builder<'_>, sysroot: &Path, conte |
| 933 | 933 | } |
| 934 | 934 | } |
| 935 | 935 | |
| 936 | /// Represents information about a built rustc. | |
| 937 | #[derive(Clone, Debug)] | |
| 938 | pub struct BuiltRustc { | |
| 939 | /// The compiler that actually built this *rustc*. | |
| 940 | /// This can be different from the *build_compiler* passed to the `Rustc` step because of | |
| 941 | /// uplifting. | |
| 942 | pub build_compiler: Compiler, | |
| 943 | } | |
| 944 | ||
| 936 | 945 | /// Build rustc using the passed `build_compiler`. |
| 937 | 946 | /// |
| 938 | 947 | /// - Makes sure that `build_compiler` has a standard library prepared for its host target, |
| ... | ... | @@ -960,7 +969,7 @@ impl Rustc { |
| 960 | 969 | } |
| 961 | 970 | |
| 962 | 971 | impl Step for Rustc { |
| 963 | type Output = (); | |
| 972 | type Output = BuiltRustc; | |
| 964 | 973 | |
| 965 | 974 | const IS_HOST: bool = true; |
| 966 | 975 | const DEFAULT: bool = false; |
| ... | ... | @@ -1000,7 +1009,7 @@ impl Step for Rustc { |
| 1000 | 1009 | /// This will build the compiler for a particular stage of the build using |
| 1001 | 1010 | /// the `build_compiler` targeting the `target` architecture. The artifacts |
| 1002 | 1011 | /// created will also be linked into the sysroot directory. |
| 1003 | fn run(self, builder: &Builder<'_>) { | |
| 1012 | fn run(self, builder: &Builder<'_>) -> Self::Output { | |
| 1004 | 1013 | let build_compiler = self.build_compiler; |
| 1005 | 1014 | let target = self.target; |
| 1006 | 1015 | |
| ... | ... | @@ -1016,7 +1025,7 @@ impl Step for Rustc { |
| 1016 | 1025 | &sysroot, |
| 1017 | 1026 | builder.config.ci_rustc_dev_contents(), |
| 1018 | 1027 | ); |
| 1019 | return; | |
| 1028 | return BuiltRustc { build_compiler }; | |
| 1020 | 1029 | } |
| 1021 | 1030 | |
| 1022 | 1031 | // Build a standard library for `target` using the `build_compiler`. |
| ... | ... | @@ -1028,9 +1037,9 @@ impl Step for Rustc { |
| 1028 | 1037 | |
| 1029 | 1038 | builder.info("WARNING: Using a potentially old librustc. This may not behave well."); |
| 1030 | 1039 | builder.info("WARNING: Use `--keep-stage-std` if you want to rebuild the compiler when it changes"); |
| 1031 | builder.ensure(RustcLink::from_rustc(self, build_compiler)); | |
| 1040 | builder.ensure(RustcLink::from_rustc(self)); | |
| 1032 | 1041 | |
| 1033 | return; | |
| 1042 | return BuiltRustc { build_compiler }; | |
| 1034 | 1043 | } |
| 1035 | 1044 | |
| 1036 | 1045 | // The stage of the compiler that we're building |
| ... | ... | @@ -1042,21 +1051,35 @@ impl Step for Rustc { |
| 1042 | 1051 | && !builder.config.full_bootstrap |
| 1043 | 1052 | && (target == builder.host_target || builder.hosts.contains(&target)) |
| 1044 | 1053 | { |
| 1045 | // If we're cross-compiling, the earliest rustc that we could have is stage 2. | |
| 1046 | // If we're not cross-compiling, then we should have rustc stage 1. | |
| 1047 | let stage_to_uplift = if target == builder.host_target { 1 } else { 2 }; | |
| 1048 | let rustc_to_uplift = builder.compiler(stage_to_uplift, target); | |
| 1049 | let msg = if rustc_to_uplift.host == target { | |
| 1050 | format!("Uplifting rustc (stage{} -> stage{stage})", rustc_to_uplift.stage,) | |
| 1054 | // Here we need to determine the **build compiler** that built the stage that we will | |
| 1055 | // be uplifting. We cannot uplift stage 1, as it has a different ABI than stage 2+, | |
| 1056 | // so we always uplift the stage2 compiler (compiled with stage 1). | |
| 1057 | let uplift_build_compiler = builder.compiler(1, build_compiler.host); | |
| 1058 | let msg = if uplift_build_compiler.host == target { | |
| 1059 | format!("Uplifting rustc (stage2 -> stage{stage})") | |
| 1051 | 1060 | } else { |
| 1052 | 1061 | format!( |
| 1053 | "Uplifting rustc (stage{}:{} -> stage{stage}:{target})", | |
| 1054 | rustc_to_uplift.stage, rustc_to_uplift.host, | |
| 1062 | "Uplifting rustc (stage2:{} -> stage{stage}:{target})", | |
| 1063 | uplift_build_compiler.host | |
| 1055 | 1064 | ) |
| 1056 | 1065 | }; |
| 1057 | 1066 | builder.info(&msg); |
| 1058 | builder.ensure(RustcLink::from_rustc(self, rustc_to_uplift)); | |
| 1059 | return; | |
| 1067 | ||
| 1068 | // Here the compiler that built the rlibs (`uplift_build_compiler`) can be different | |
| 1069 | // from the compiler whose sysroot should be modified in this step. So we need to copy | |
| 1070 | // the (previously built) rlibs into the correct sysroot. | |
| 1071 | builder.ensure(RustcLink::from_build_compiler_and_sysroot( | |
| 1072 | // This is the compiler that actually built the rustc rlibs | |
| 1073 | uplift_build_compiler, | |
| 1074 | // We copy the rlibs into the sysroot of `build_compiler` | |
| 1075 | build_compiler, | |
| 1076 | target, | |
| 1077 | self.crates, | |
| 1078 | )); | |
| 1079 | ||
| 1080 | // Here we have performed an uplift, so we return the actual build compiler that "built" | |
| 1081 | // this rustc. | |
| 1082 | return BuiltRustc { build_compiler: uplift_build_compiler }; | |
| 1060 | 1083 | } |
| 1061 | 1084 | |
| 1062 | 1085 | // Build a standard library for the current host target using the `build_compiler`. |
| ... | ... | @@ -1129,10 +1152,8 @@ impl Step for Rustc { |
| 1129 | 1152 | strip_debug(builder, target, &target_root_dir.join("rustc-main")); |
| 1130 | 1153 | } |
| 1131 | 1154 | |
| 1132 | builder.ensure(RustcLink::from_rustc( | |
| 1133 | self, | |
| 1134 | builder.compiler(build_compiler.stage, builder.config.host_target), | |
| 1135 | )); | |
| 1155 | builder.ensure(RustcLink::from_rustc(self)); | |
| 1156 | BuiltRustc { build_compiler } | |
| 1136 | 1157 | } |
| 1137 | 1158 | |
| 1138 | 1159 | fn metadata(&self) -> Option<StepMetadata> { |
| ... | ... | @@ -1441,31 +1462,51 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect |
| 1441 | 1462 | } |
| 1442 | 1463 | } |
| 1443 | 1464 | |
| 1444 | /// `RustcLink` copies all of the rlibs from the rustc build into the previous stage's sysroot. | |
| 1465 | /// `RustcLink` copies compiler rlibs from a rustc build into a compiler sysroot. | |
| 1466 | /// It works with (potentially up to) three compilers: | |
| 1467 | /// - `build_compiler` is a compiler that built rustc rlibs | |
| 1468 | /// - `sysroot_compiler` is a compiler into whose sysroot we will copy the rlibs | |
| 1469 | /// - In most situations, `build_compiler` == `sysroot_compiler` | |
| 1470 | /// - `target_compiler` is the compiler whose rlibs were built. It is not represented explicitly | |
| 1471 | /// in this step, rather we just read the rlibs from a rustc build stamp of `build_compiler`. | |
| 1472 | /// | |
| 1445 | 1473 | /// This is necessary for tools using `rustc_private`, where the previous compiler will build |
| 1446 | 1474 | /// a tool against the next compiler. |
| 1447 | 1475 | /// To build a tool against a compiler, the rlibs of that compiler that it links against |
| 1448 | 1476 | /// must be in the sysroot of the compiler that's doing the compiling. |
| 1449 | 1477 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 1450 | 1478 | struct RustcLink { |
| 1451 | /// The compiler whose rlibs we are copying around. | |
| 1452 | pub compiler: Compiler, | |
| 1453 | /// This is the compiler into whose sysroot we want to copy the rlibs into. | |
| 1454 | pub previous_stage_compiler: Compiler, | |
| 1455 | pub target: TargetSelection, | |
| 1479 | /// This compiler **built** some rustc, whose rlibs we will copy into a sysroot. | |
| 1480 | build_compiler: Compiler, | |
| 1481 | /// This is the compiler into whose sysroot we want to copy the built rlibs. | |
| 1482 | /// In most cases, it will correspond to `build_compiler`. | |
| 1483 | sysroot_compiler: Compiler, | |
| 1484 | target: TargetSelection, | |
| 1456 | 1485 | /// Not actually used; only present to make sure the cache invalidation is correct. |
| 1457 | 1486 | crates: Vec<String>, |
| 1458 | 1487 | } |
| 1459 | 1488 | |
| 1460 | 1489 | impl RustcLink { |
| 1461 | fn from_rustc(rustc: Rustc, host_compiler: Compiler) -> Self { | |
| 1490 | /// Copy rlibs from the build compiler that build this `rustc` into the sysroot of that | |
| 1491 | /// build compiler. | |
| 1492 | fn from_rustc(rustc: Rustc) -> Self { | |
| 1462 | 1493 | Self { |
| 1463 | compiler: host_compiler, | |
| 1464 | previous_stage_compiler: rustc.build_compiler, | |
| 1494 | build_compiler: rustc.build_compiler, | |
| 1495 | sysroot_compiler: rustc.build_compiler, | |
| 1465 | 1496 | target: rustc.target, |
| 1466 | 1497 | crates: rustc.crates, |
| 1467 | 1498 | } |
| 1468 | 1499 | } |
| 1500 | ||
| 1501 | /// Copy rlibs **built** by `build_compiler` into the sysroot of `sysroot_compiler`. | |
| 1502 | fn from_build_compiler_and_sysroot( | |
| 1503 | build_compiler: Compiler, | |
| 1504 | sysroot_compiler: Compiler, | |
| 1505 | target: TargetSelection, | |
| 1506 | crates: Vec<String>, | |
| 1507 | ) -> Self { | |
| 1508 | Self { build_compiler, sysroot_compiler, target, crates } | |
| 1509 | } | |
| 1469 | 1510 | } |
| 1470 | 1511 | |
| 1471 | 1512 | impl Step for RustcLink { |
| ... | ... | @@ -1477,14 +1518,14 @@ impl Step for RustcLink { |
| 1477 | 1518 | |
| 1478 | 1519 | /// Same as `std_link`, only for librustc |
| 1479 | 1520 | fn run(self, builder: &Builder<'_>) { |
| 1480 | let compiler = self.compiler; | |
| 1481 | let previous_stage_compiler = self.previous_stage_compiler; | |
| 1521 | let build_compiler = self.build_compiler; | |
| 1522 | let sysroot_compiler = self.sysroot_compiler; | |
| 1482 | 1523 | let target = self.target; |
| 1483 | 1524 | add_to_sysroot( |
| 1484 | 1525 | builder, |
| 1485 | &builder.sysroot_target_libdir(previous_stage_compiler, target), | |
| 1486 | &builder.sysroot_target_libdir(previous_stage_compiler, compiler.host), | |
| 1487 | &build_stamp::librustc_stamp(builder, compiler, target), | |
| 1526 | &builder.sysroot_target_libdir(sysroot_compiler, target), | |
| 1527 | &builder.sysroot_target_libdir(sysroot_compiler, sysroot_compiler.host), | |
| 1528 | &build_stamp::librustc_stamp(builder, build_compiler, target), | |
| 1488 | 1529 | ); |
| 1489 | 1530 | } |
| 1490 | 1531 | } |
| ... | ... | @@ -2099,7 +2140,10 @@ impl Step for Assemble { |
| 2099 | 2140 | "target_compiler.host" = ?target_compiler.host, |
| 2100 | 2141 | "building compiler libraries to link to" |
| 2101 | 2142 | ); |
| 2102 | builder.ensure(Rustc::new(build_compiler, target_compiler.host)); | |
| 2143 | ||
| 2144 | // It is possible that an uplift has happened, so we override build_compiler here. | |
| 2145 | let BuiltRustc { build_compiler } = | |
| 2146 | builder.ensure(Rustc::new(build_compiler, target_compiler.host)); | |
| 2103 | 2147 | |
| 2104 | 2148 | let stage = target_compiler.stage; |
| 2105 | 2149 | let host = target_compiler.host; |
src/bootstrap/src/core/builder/cargo.rs+30| ... | ... | @@ -101,6 +101,7 @@ pub struct Cargo { |
| 101 | 101 | impl Cargo { |
| 102 | 102 | /// Calls [`Builder::cargo`] and [`Cargo::configure_linker`] to prepare an invocation of `cargo` |
| 103 | 103 | /// to be run. |
| 104 | #[track_caller] | |
| 104 | 105 | pub fn new( |
| 105 | 106 | builder: &Builder<'_>, |
| 106 | 107 | compiler: Compiler, |
| ... | ... | @@ -139,6 +140,7 @@ impl Cargo { |
| 139 | 140 | |
| 140 | 141 | /// Same as [`Cargo::new`] except this one doesn't configure the linker with |
| 141 | 142 | /// [`Cargo::configure_linker`]. |
| 143 | #[track_caller] | |
| 142 | 144 | pub fn new_for_mir_opt_tests( |
| 143 | 145 | builder: &Builder<'_>, |
| 144 | 146 | compiler: Compiler, |
| ... | ... | @@ -186,6 +188,32 @@ impl Cargo { |
| 186 | 188 | self |
| 187 | 189 | } |
| 188 | 190 | |
| 191 | /// Append a value to an env var of the cargo command instance. | |
| 192 | /// If the variable was unset previously, this is equivalent to [`Cargo::env`]. | |
| 193 | /// If the variable was already set, this will append `delimiter` and then `value` to it. | |
| 194 | /// | |
| 195 | /// Note that this only considers the existence of the env. var. configured on this `Cargo` | |
| 196 | /// instance. It does not look at the environment of this process. | |
| 197 | pub fn append_to_env( | |
| 198 | &mut self, | |
| 199 | key: impl AsRef<OsStr>, | |
| 200 | value: impl AsRef<OsStr>, | |
| 201 | delimiter: impl AsRef<OsStr>, | |
| 202 | ) -> &mut Cargo { | |
| 203 | assert_ne!(key.as_ref(), "RUSTFLAGS"); | |
| 204 | assert_ne!(key.as_ref(), "RUSTDOCFLAGS"); | |
| 205 | ||
| 206 | let key = key.as_ref(); | |
| 207 | if let Some((_, Some(previous_value))) = self.command.get_envs().find(|(k, _)| *k == key) { | |
| 208 | let mut combined: OsString = previous_value.to_os_string(); | |
| 209 | combined.push(delimiter.as_ref()); | |
| 210 | combined.push(value.as_ref()); | |
| 211 | self.env(key, combined) | |
| 212 | } else { | |
| 213 | self.env(key, value) | |
| 214 | } | |
| 215 | } | |
| 216 | ||
| 189 | 217 | pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) { |
| 190 | 218 | builder.add_rustc_lib_path(self.compiler, &mut self.command); |
| 191 | 219 | } |
| ... | ... | @@ -396,6 +424,7 @@ impl From<Cargo> for BootstrapCommand { |
| 396 | 424 | |
| 397 | 425 | impl Builder<'_> { |
| 398 | 426 | /// Like [`Builder::cargo`], but only passes flags that are valid for all commands. |
| 427 | #[track_caller] | |
| 399 | 428 | pub fn bare_cargo( |
| 400 | 429 | &self, |
| 401 | 430 | compiler: Compiler, |
| ... | ... | @@ -480,6 +509,7 @@ impl Builder<'_> { |
| 480 | 509 | /// scoped by `mode`'s output directory, it will pass the `--target` flag for the specified |
| 481 | 510 | /// `target`, and will be executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for |
| 482 | 511 | /// commands to be run with Miri. |
| 512 | #[track_caller] | |
| 483 | 513 | fn cargo( |
| 484 | 514 | &self, |
| 485 | 515 | compiler: Compiler, |
src/bootstrap/src/core/builder/tests.rs+1-1| ... | ... | @@ -1569,7 +1569,7 @@ mod snapshot { |
| 1569 | 1569 | [build] llvm <host> |
| 1570 | 1570 | [build] rustc 0 <host> -> rustc 1 <host> |
| 1571 | 1571 | [build] rustc 1 <host> -> std 1 <host> |
| 1572 | [build] rustc 1 <host> -> std 1 <target1> | |
| 1572 | [check] rustc 1 <host> -> std 1 <target1> | |
| 1573 | 1573 | [check] rustc 1 <host> -> rustc 2 <target1> (73 crates) |
| 1574 | 1574 | [check] rustc 1 <host> -> rustc 2 <target1> |
| 1575 | 1575 | [check] rustc 1 <host> -> Rustdoc 2 <target1> |
src/build_helper/src/util.rs+11| ... | ... | @@ -3,6 +3,8 @@ use std::io::{BufRead, BufReader}; |
| 3 | 3 | use std::path::Path; |
| 4 | 4 | use std::process::Command; |
| 5 | 5 | |
| 6 | use crate::ci::CiEnv; | |
| 7 | ||
| 6 | 8 | /// Invokes `build_helper::util::detail_exit` with `cfg!(test)` |
| 7 | 9 | /// |
| 8 | 10 | /// This is a macro instead of a function so that it uses `cfg(test)` in the *calling* crate, not in build helper. |
| ... | ... | @@ -20,6 +22,15 @@ pub fn detail_exit(code: i32, is_test: bool) -> ! { |
| 20 | 22 | if is_test { |
| 21 | 23 | panic!("status code: {code}"); |
| 22 | 24 | } else { |
| 25 | // If we're in CI, print the current bootstrap invocation command, to make it easier to | |
| 26 | // figure out what exactly has failed. | |
| 27 | if CiEnv::is_ci() { | |
| 28 | // Skip the first argument, as it will be some absolute path to the bootstrap binary. | |
| 29 | let bootstrap_args = | |
| 30 | std::env::args().skip(1).map(|a| a.to_string()).collect::<Vec<_>>().join(" "); | |
| 31 | eprintln!("Bootstrap failed while executing `{bootstrap_args}`"); | |
| 32 | } | |
| 33 | ||
| 23 | 34 | // otherwise, exit with provided status code |
| 24 | 35 | std::process::exit(code); |
| 25 | 36 | } |
src/ci/docker/host-x86_64/tidy/eslint.version+1-1| ... | ... | @@ -1 +1 @@ |
| 1 | 8.6.0 | |
| \ No newline at end of file | ||
| 1 | 8.57.1 |
src/doc/rustc-dev-guide/src/sanitizers.md+1-1| ... | ... | @@ -45,7 +45,7 @@ implementation: |
| 45 | 45 | [marked][sanitizer-attribute] with appropriate LLVM attribute: |
| 46 | 46 | `SanitizeAddress`, `SanitizeHWAddress`, `SanitizeMemory`, or |
| 47 | 47 | `SanitizeThread`. By default all functions are instrumented, but this |
| 48 | behaviour can be changed with `#[no_sanitize(...)]`. | |
| 48 | behaviour can be changed with `#[sanitize(xyz = "on|off")]`. | |
| 49 | 49 | |
| 50 | 50 | * The decision whether to perform instrumentation or not is possible only at a |
| 51 | 51 | function granularity. In the cases were those decision differ between |
src/doc/unstable-book/src/language-features/no-sanitize.md deleted-29| ... | ... | @@ -1,29 +0,0 @@ |
| 1 | # `no_sanitize` | |
| 2 | ||
| 3 | The tracking issue for this feature is: [#39699] | |
| 4 | ||
| 5 | [#39699]: https://github.com/rust-lang/rust/issues/39699 | |
| 6 | ||
| 7 | ------------------------ | |
| 8 | ||
| 9 | The `no_sanitize` attribute can be used to selectively disable sanitizer | |
| 10 | instrumentation in an annotated function. This might be useful to: avoid | |
| 11 | instrumentation overhead in a performance critical function, or avoid | |
| 12 | instrumenting code that contains constructs unsupported by given sanitizer. | |
| 13 | ||
| 14 | The precise effect of this annotation depends on particular sanitizer in use. | |
| 15 | For example, with `no_sanitize(thread)`, the thread sanitizer will no longer | |
| 16 | instrument non-atomic store / load operations, but it will instrument atomic | |
| 17 | operations to avoid reporting false positives and provide meaning full stack | |
| 18 | traces. | |
| 19 | ||
| 20 | ## Examples | |
| 21 | ||
| 22 | ``` rust | |
| 23 | #![feature(no_sanitize)] | |
| 24 | ||
| 25 | #[no_sanitize(address)] | |
| 26 | fn foo() { | |
| 27 | // ... | |
| 28 | } | |
| 29 | ``` |
src/doc/unstable-book/src/language-features/sanitize.md created+73| ... | ... | @@ -0,0 +1,73 @@ |
| 1 | # `sanitize` | |
| 2 | ||
| 3 | The tracking issue for this feature is: [#39699] | |
| 4 | ||
| 5 | [#39699]: https://github.com/rust-lang/rust/issues/39699 | |
| 6 | ||
| 7 | ------------------------ | |
| 8 | ||
| 9 | The `sanitize` attribute can be used to selectively disable or enable sanitizer | |
| 10 | instrumentation in an annotated function. This might be useful to: avoid | |
| 11 | instrumentation overhead in a performance critical function, or avoid | |
| 12 | instrumenting code that contains constructs unsupported by given sanitizer. | |
| 13 | ||
| 14 | The precise effect of this annotation depends on particular sanitizer in use. | |
| 15 | For example, with `sanitize(thread = "off")`, the thread sanitizer will no | |
| 16 | longer instrument non-atomic store / load operations, but it will instrument | |
| 17 | atomic operations to avoid reporting false positives and provide meaning full | |
| 18 | stack traces. | |
| 19 | ||
| 20 | This attribute was previously named `no_sanitize`. | |
| 21 | ||
| 22 | ## Examples | |
| 23 | ||
| 24 | ``` rust | |
| 25 | #![feature(sanitize)] | |
| 26 | ||
| 27 | #[sanitize(address = "off")] | |
| 28 | fn foo() { | |
| 29 | // ... | |
| 30 | } | |
| 31 | ``` | |
| 32 | ||
| 33 | It is also possible to disable sanitizers for entire modules and enable them | |
| 34 | for single items or functions. | |
| 35 | ||
| 36 | ```rust | |
| 37 | #![feature(sanitize)] | |
| 38 | ||
| 39 | #[sanitize(address = "off")] | |
| 40 | mod foo { | |
| 41 | fn unsanitized() { | |
| 42 | // ... | |
| 43 | } | |
| 44 | ||
| 45 | #[sanitize(address = "on")] | |
| 46 | fn sanitized() { | |
| 47 | // ... | |
| 48 | } | |
| 49 | } | |
| 50 | ``` | |
| 51 | ||
| 52 | It's also applicable to impl blocks. | |
| 53 | ||
| 54 | ```rust | |
| 55 | #![feature(sanitize)] | |
| 56 | ||
| 57 | trait MyTrait { | |
| 58 | fn foo(&self); | |
| 59 | fn bar(&self); | |
| 60 | } | |
| 61 | ||
| 62 | #[sanitize(address = "off")] | |
| 63 | impl MyTrait for () { | |
| 64 | fn foo(&self) { | |
| 65 | // ... | |
| 66 | } | |
| 67 | ||
| 68 | #[sanitize(address = "on")] | |
| 69 | fn bar(&self) { | |
| 70 | // ... | |
| 71 | } | |
| 72 | } | |
| 73 | ``` |
src/etc/htmldocck.py+6| ... | ... | @@ -15,6 +15,7 @@ import os.path |
| 15 | 15 | import re |
| 16 | 16 | import shlex |
| 17 | 17 | from collections import namedtuple |
| 18 | from pathlib import Path | |
| 18 | 19 | |
| 19 | 20 | try: |
| 20 | 21 | from html.parser import HTMLParser |
| ... | ... | @@ -242,6 +243,11 @@ class CachedFiles(object): |
| 242 | 243 | return self.last_path |
| 243 | 244 | |
| 244 | 245 | def get_absolute_path(self, path): |
| 246 | if "*" in path: | |
| 247 | paths = list(Path(self.root).glob(path)) | |
| 248 | if len(paths) != 1: | |
| 249 | raise FailedCheck("glob path does not resolve to one file") | |
| 250 | path = str(paths[0]) | |
| 245 | 251 | return os.path.join(self.root, path) |
| 246 | 252 | |
| 247 | 253 | def get_file(self, path): |
src/librustdoc/Cargo.toml+1| ... | ... | @@ -21,6 +21,7 @@ rustdoc-json-types = { path = "../rustdoc-json-types" } |
| 21 | 21 | serde = { version = "1.0", features = ["derive"] } |
| 22 | 22 | serde_json = "1.0" |
| 23 | 23 | smallvec = "1.8.1" |
| 24 | stringdex = { version = "0.0.1-alpha4" } | |
| 24 | 25 | tempfile = "3" |
| 25 | 26 | threadpool = "1.8.1" |
| 26 | 27 | tracing = "0.1" |
src/librustdoc/build.rs+1| ... | ... | @@ -10,6 +10,7 @@ fn main() { |
| 10 | 10 | "static/css/normalize.css", |
| 11 | 11 | "static/js/main.js", |
| 12 | 12 | "static/js/search.js", |
| 13 | "static/js/stringdex.js", | |
| 13 | 14 | "static/js/settings.js", |
| 14 | 15 | "static/js/src-script.js", |
| 15 | 16 | "static/js/storage.js", |
src/librustdoc/formats/cache.rs+2-4| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | use std::mem; |
| 2 | 2 | |
| 3 | use rustc_ast::join_path_syms; | |
| 4 | 3 | use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; |
| 5 | 4 | use rustc_hir::StabilityLevel; |
| 6 | 5 | use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; |
| ... | ... | @@ -574,7 +573,6 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It |
| 574 | 573 | clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id), |
| 575 | 574 | _ => item_def_id, |
| 576 | 575 | }; |
| 577 | let path = join_path_syms(parent_path); | |
| 578 | 576 | let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { |
| 579 | 577 | item_id.as_def_id() |
| 580 | 578 | } else { |
| ... | ... | @@ -593,11 +591,11 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It |
| 593 | 591 | ty: item.type_(), |
| 594 | 592 | defid: Some(defid), |
| 595 | 593 | name, |
| 596 | path, | |
| 594 | module_path: parent_path.to_vec(), | |
| 597 | 595 | desc, |
| 598 | 596 | parent: parent_did, |
| 599 | 597 | parent_idx: None, |
| 600 | exact_path: None, | |
| 598 | exact_module_path: None, | |
| 601 | 599 | impl_id, |
| 602 | 600 | search_type, |
| 603 | 601 | aliases, |
src/librustdoc/formats/item_type.rs+51-1| ... | ... | @@ -4,7 +4,7 @@ use std::fmt; |
| 4 | 4 | |
| 5 | 5 | use rustc_hir::def::{CtorOf, DefKind, MacroKinds}; |
| 6 | 6 | use rustc_span::hygiene::MacroKind; |
| 7 | use serde::{Serialize, Serializer}; | |
| 7 | use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; | |
| 8 | 8 | |
| 9 | 9 | use crate::clean; |
| 10 | 10 | |
| ... | ... | @@ -68,6 +68,52 @@ impl Serialize for ItemType { |
| 68 | 68 | } |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | impl<'de> Deserialize<'de> for ItemType { | |
| 72 | fn deserialize<D>(deserializer: D) -> Result<ItemType, D::Error> | |
| 73 | where | |
| 74 | D: Deserializer<'de>, | |
| 75 | { | |
| 76 | struct ItemTypeVisitor; | |
| 77 | impl<'de> de::Visitor<'de> for ItemTypeVisitor { | |
| 78 | type Value = ItemType; | |
| 79 | fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 80 | write!(formatter, "an integer between 0 and 25") | |
| 81 | } | |
| 82 | fn visit_u64<E: de::Error>(self, v: u64) -> Result<ItemType, E> { | |
| 83 | Ok(match v { | |
| 84 | 0 => ItemType::Keyword, | |
| 85 | 1 => ItemType::Primitive, | |
| 86 | 2 => ItemType::Module, | |
| 87 | 3 => ItemType::ExternCrate, | |
| 88 | 4 => ItemType::Import, | |
| 89 | 5 => ItemType::Struct, | |
| 90 | 6 => ItemType::Enum, | |
| 91 | 7 => ItemType::Function, | |
| 92 | 8 => ItemType::TypeAlias, | |
| 93 | 9 => ItemType::Static, | |
| 94 | 10 => ItemType::Trait, | |
| 95 | 11 => ItemType::Impl, | |
| 96 | 12 => ItemType::TyMethod, | |
| 97 | 13 => ItemType::Method, | |
| 98 | 14 => ItemType::StructField, | |
| 99 | 15 => ItemType::Variant, | |
| 100 | 16 => ItemType::Macro, | |
| 101 | 17 => ItemType::AssocType, | |
| 102 | 18 => ItemType::Constant, | |
| 103 | 19 => ItemType::AssocConst, | |
| 104 | 20 => ItemType::Union, | |
| 105 | 21 => ItemType::ForeignType, | |
| 106 | 23 => ItemType::ProcAttribute, | |
| 107 | 24 => ItemType::ProcDerive, | |
| 108 | 25 => ItemType::TraitAlias, | |
| 109 | _ => return Err(E::missing_field("unknown number")), | |
| 110 | }) | |
| 111 | } | |
| 112 | } | |
| 113 | deserializer.deserialize_any(ItemTypeVisitor) | |
| 114 | } | |
| 115 | } | |
| 116 | ||
| 71 | 117 | impl<'a> From<&'a clean::Item> for ItemType { |
| 72 | 118 | fn from(item: &'a clean::Item) -> ItemType { |
| 73 | 119 | let kind = match &item.kind { |
| ... | ... | @@ -198,6 +244,10 @@ impl ItemType { |
| 198 | 244 | pub(crate) fn is_adt(&self) -> bool { |
| 199 | 245 | matches!(self, ItemType::Struct | ItemType::Union | ItemType::Enum) |
| 200 | 246 | } |
| 247 | /// Keep this the same as isFnLikeTy in search.js | |
| 248 | pub(crate) fn is_fn_like(&self) -> bool { | |
| 249 | matches!(self, ItemType::Function | ItemType::Method | ItemType::TyMethod) | |
| 250 | } | |
| 201 | 251 | } |
| 202 | 252 | |
| 203 | 253 | impl fmt::Display for ItemType { |
src/librustdoc/html/layout.rs+1| ... | ... | @@ -27,6 +27,7 @@ pub(crate) struct Layout { |
| 27 | 27 | |
| 28 | 28 | pub(crate) struct Page<'a> { |
| 29 | 29 | pub(crate) title: &'a str, |
| 30 | pub(crate) short_title: &'a str, | |
| 30 | 31 | pub(crate) css_class: &'a str, |
| 31 | 32 | pub(crate) root_path: &'a str, |
| 32 | 33 | pub(crate) static_root_path: Option<&'a str>, |
src/librustdoc/html/render/context.rs+14| ... | ... | @@ -204,6 +204,18 @@ impl<'tcx> Context<'tcx> { |
| 204 | 204 | if !is_module { |
| 205 | 205 | title.push_str(it.name.unwrap().as_str()); |
| 206 | 206 | } |
| 207 | let short_title; | |
| 208 | let short_title = if is_module { | |
| 209 | let module_name = self.current.last().unwrap(); | |
| 210 | short_title = if it.is_crate() { | |
| 211 | format!("Crate {module_name}") | |
| 212 | } else { | |
| 213 | format!("Module {module_name}") | |
| 214 | }; | |
| 215 | &short_title[..] | |
| 216 | } else { | |
| 217 | it.name.as_ref().unwrap().as_str() | |
| 218 | }; | |
| 207 | 219 | if !it.is_primitive() && !it.is_keyword() { |
| 208 | 220 | if !is_module { |
| 209 | 221 | title.push_str(" in "); |
| ... | ... | @@ -240,6 +252,7 @@ impl<'tcx> Context<'tcx> { |
| 240 | 252 | root_path: &self.root_path(), |
| 241 | 253 | static_root_path: self.shared.static_root_path.as_deref(), |
| 242 | 254 | title: &title, |
| 255 | short_title, | |
| 243 | 256 | description: &desc, |
| 244 | 257 | resource_suffix: &self.shared.resource_suffix, |
| 245 | 258 | rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo), |
| ... | ... | @@ -617,6 +630,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { |
| 617 | 630 | let shared = &self.shared; |
| 618 | 631 | let mut page = layout::Page { |
| 619 | 632 | title: "List of all items in this crate", |
| 633 | short_title: "All", | |
| 620 | 634 | css_class: "mod sys", |
| 621 | 635 | root_path: "../", |
| 622 | 636 | static_root_path: shared.static_root_path.as_deref(), |
src/librustdoc/html/render/mod.rs+144-21| ... | ... | @@ -130,11 +130,11 @@ pub(crate) struct IndexItem { |
| 130 | 130 | pub(crate) ty: ItemType, |
| 131 | 131 | pub(crate) defid: Option<DefId>, |
| 132 | 132 | pub(crate) name: Symbol, |
| 133 | pub(crate) path: String, | |
| 133 | pub(crate) module_path: Vec<Symbol>, | |
| 134 | 134 | pub(crate) desc: String, |
| 135 | 135 | pub(crate) parent: Option<DefId>, |
| 136 | pub(crate) parent_idx: Option<isize>, | |
| 137 | pub(crate) exact_path: Option<String>, | |
| 136 | pub(crate) parent_idx: Option<usize>, | |
| 137 | pub(crate) exact_module_path: Option<Vec<Symbol>>, | |
| 138 | 138 | pub(crate) impl_id: Option<DefId>, |
| 139 | 139 | pub(crate) search_type: Option<IndexItemFunctionType>, |
| 140 | 140 | pub(crate) aliases: Box<[Symbol]>, |
| ... | ... | @@ -150,6 +150,19 @@ struct RenderType { |
| 150 | 150 | } |
| 151 | 151 | |
| 152 | 152 | impl RenderType { |
| 153 | fn size(&self) -> usize { | |
| 154 | let mut size = 1; | |
| 155 | if let Some(generics) = &self.generics { | |
| 156 | size += generics.iter().map(RenderType::size).sum::<usize>(); | |
| 157 | } | |
| 158 | if let Some(bindings) = &self.bindings { | |
| 159 | for (_, constraints) in bindings.iter() { | |
| 160 | size += 1; | |
| 161 | size += constraints.iter().map(RenderType::size).sum::<usize>(); | |
| 162 | } | |
| 163 | } | |
| 164 | size | |
| 165 | } | |
| 153 | 166 | // Types are rendered as lists of lists, because that's pretty compact. |
| 154 | 167 | // The contents of the lists are always integers in self-terminating hex |
| 155 | 168 | // form, handled by `RenderTypeId::write_to_string`, so no commas are |
| ... | ... | @@ -191,6 +204,62 @@ impl RenderType { |
| 191 | 204 | write_optional_id(self.id, string); |
| 192 | 205 | } |
| 193 | 206 | } |
| 207 | fn read_from_bytes(string: &[u8]) -> (RenderType, usize) { | |
| 208 | let mut i = 0; | |
| 209 | if string[i] == b'{' { | |
| 210 | i += 1; | |
| 211 | let (id, offset) = RenderTypeId::read_from_bytes(&string[i..]); | |
| 212 | i += offset; | |
| 213 | let generics = if string[i] == b'{' { | |
| 214 | i += 1; | |
| 215 | let mut generics = Vec::new(); | |
| 216 | while string[i] != b'}' { | |
| 217 | let (ty, offset) = RenderType::read_from_bytes(&string[i..]); | |
| 218 | i += offset; | |
| 219 | generics.push(ty); | |
| 220 | } | |
| 221 | assert!(string[i] == b'}'); | |
| 222 | i += 1; | |
| 223 | Some(generics) | |
| 224 | } else { | |
| 225 | None | |
| 226 | }; | |
| 227 | let bindings = if string[i] == b'{' { | |
| 228 | i += 1; | |
| 229 | let mut bindings = Vec::new(); | |
| 230 | while string[i] == b'{' { | |
| 231 | i += 1; | |
| 232 | let (binding, boffset) = RenderTypeId::read_from_bytes(&string[i..]); | |
| 233 | i += boffset; | |
| 234 | let mut bconstraints = Vec::new(); | |
| 235 | assert!(string[i] == b'{'); | |
| 236 | i += 1; | |
| 237 | while string[i] != b'}' { | |
| 238 | let (constraint, coffset) = RenderType::read_from_bytes(&string[i..]); | |
| 239 | i += coffset; | |
| 240 | bconstraints.push(constraint); | |
| 241 | } | |
| 242 | assert!(string[i] == b'}'); | |
| 243 | i += 1; | |
| 244 | bindings.push((binding.unwrap(), bconstraints)); | |
| 245 | assert!(string[i] == b'}'); | |
| 246 | i += 1; | |
| 247 | } | |
| 248 | assert!(string[i] == b'}'); | |
| 249 | i += 1; | |
| 250 | Some(bindings) | |
| 251 | } else { | |
| 252 | None | |
| 253 | }; | |
| 254 | assert!(string[i] == b'}'); | |
| 255 | i += 1; | |
| 256 | (RenderType { id, generics, bindings }, i) | |
| 257 | } else { | |
| 258 | let (id, offset) = RenderTypeId::read_from_bytes(string); | |
| 259 | i += offset; | |
| 260 | (RenderType { id, generics: None, bindings: None }, i) | |
| 261 | } | |
| 262 | } | |
| 194 | 263 | } |
| 195 | 264 | |
| 196 | 265 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
| ... | ... | @@ -212,7 +281,20 @@ impl RenderTypeId { |
| 212 | 281 | RenderTypeId::Index(idx) => (*idx).try_into().unwrap(), |
| 213 | 282 | _ => panic!("must convert render types to indexes before serializing"), |
| 214 | 283 | }; |
| 215 | search_index::encode::write_vlqhex_to_string(id, string); | |
| 284 | search_index::encode::write_signed_vlqhex_to_string(id, string); | |
| 285 | } | |
| 286 | fn read_from_bytes(string: &[u8]) -> (Option<RenderTypeId>, usize) { | |
| 287 | let Some((value, offset)) = search_index::encode::read_signed_vlqhex_from_string(string) | |
| 288 | else { | |
| 289 | return (None, 0); | |
| 290 | }; | |
| 291 | let value = isize::try_from(value).unwrap(); | |
| 292 | let ty = match value { | |
| 293 | ..0 => Some(RenderTypeId::Index(value)), | |
| 294 | 0 => None, | |
| 295 | 1.. => Some(RenderTypeId::Index(value - 1)), | |
| 296 | }; | |
| 297 | (ty, offset) | |
| 216 | 298 | } |
| 217 | 299 | } |
| 218 | 300 | |
| ... | ... | @@ -226,12 +308,64 @@ pub(crate) struct IndexItemFunctionType { |
| 226 | 308 | } |
| 227 | 309 | |
| 228 | 310 | impl IndexItemFunctionType { |
| 229 | fn write_to_string<'a>( | |
| 230 | &'a self, | |
| 231 | string: &mut String, | |
| 232 | backref_queue: &mut VecDeque<&'a IndexItemFunctionType>, | |
| 233 | ) { | |
| 234 | assert!(backref_queue.len() <= 16); | |
| 311 | fn size(&self) -> usize { | |
| 312 | self.inputs.iter().map(RenderType::size).sum::<usize>() | |
| 313 | + self.output.iter().map(RenderType::size).sum::<usize>() | |
| 314 | + self | |
| 315 | .where_clause | |
| 316 | .iter() | |
| 317 | .map(|constraints| constraints.iter().map(RenderType::size).sum::<usize>()) | |
| 318 | .sum::<usize>() | |
| 319 | } | |
| 320 | fn read_from_string_without_param_names(string: &[u8]) -> (IndexItemFunctionType, usize) { | |
| 321 | let mut i = 0; | |
| 322 | if string[i] == b'`' { | |
| 323 | return ( | |
| 324 | IndexItemFunctionType { | |
| 325 | inputs: Vec::new(), | |
| 326 | output: Vec::new(), | |
| 327 | where_clause: Vec::new(), | |
| 328 | param_names: Vec::new(), | |
| 329 | }, | |
| 330 | 1, | |
| 331 | ); | |
| 332 | } | |
| 333 | assert_eq!(b'{', string[i]); | |
| 334 | i += 1; | |
| 335 | fn read_args_from_string(string: &[u8]) -> (Vec<RenderType>, usize) { | |
| 336 | let mut i = 0; | |
| 337 | let mut params = Vec::new(); | |
| 338 | if string[i] == b'{' { | |
| 339 | // multiple params | |
| 340 | i += 1; | |
| 341 | while string[i] != b'}' { | |
| 342 | let (ty, offset) = RenderType::read_from_bytes(&string[i..]); | |
| 343 | i += offset; | |
| 344 | params.push(ty); | |
| 345 | } | |
| 346 | i += 1; | |
| 347 | } else if string[i] != b'}' { | |
| 348 | let (tyid, offset) = RenderTypeId::read_from_bytes(&string[i..]); | |
| 349 | params.push(RenderType { id: tyid, generics: None, bindings: None }); | |
| 350 | i += offset; | |
| 351 | } | |
| 352 | (params, i) | |
| 353 | } | |
| 354 | let (inputs, offset) = read_args_from_string(&string[i..]); | |
| 355 | i += offset; | |
| 356 | let (output, offset) = read_args_from_string(&string[i..]); | |
| 357 | i += offset; | |
| 358 | let mut where_clause = Vec::new(); | |
| 359 | while string[i] != b'}' { | |
| 360 | let (constraint, offset) = read_args_from_string(&string[i..]); | |
| 361 | i += offset; | |
| 362 | where_clause.push(constraint); | |
| 363 | } | |
| 364 | assert_eq!(b'}', string[i], "{} {}", String::from_utf8_lossy(&string), i); | |
| 365 | i += 1; | |
| 366 | (IndexItemFunctionType { inputs, output, where_clause, param_names: Vec::new() }, i) | |
| 367 | } | |
| 368 | fn write_to_string_without_param_names<'a>(&'a self, string: &mut String) { | |
| 235 | 369 | // If we couldn't figure out a type, just write 0, |
| 236 | 370 | // which is encoded as `` ` `` (see RenderTypeId::write_to_string). |
| 237 | 371 | let has_missing = self |
| ... | ... | @@ -241,18 +375,7 @@ impl IndexItemFunctionType { |
| 241 | 375 | .any(|i| i.id.is_none() && i.generics.is_none()); |
| 242 | 376 | if has_missing { |
| 243 | 377 | string.push('`'); |
| 244 | } else if let Some(idx) = backref_queue.iter().position(|other| *other == self) { | |
| 245 | // The backref queue has 16 items, so backrefs use | |
| 246 | // a single hexit, disjoint from the ones used for numbers. | |
| 247 | string.push( | |
| 248 | char::try_from('0' as u32 + u32::try_from(idx).unwrap()) | |
| 249 | .expect("last possible value is '?'"), | |
| 250 | ); | |
| 251 | 378 | } else { |
| 252 | backref_queue.push_front(self); | |
| 253 | if backref_queue.len() > 16 { | |
| 254 | backref_queue.pop_back(); | |
| 255 | } | |
| 256 | 379 | string.push('{'); |
| 257 | 380 | match &self.inputs[..] { |
| 258 | 381 | [one] if one.generics.is_none() && one.bindings.is_none() => { |
src/librustdoc/html/render/print_item.rs+1| ... | ... | @@ -35,6 +35,7 @@ use crate::html::format::{ |
| 35 | 35 | visibility_print_with_space, |
| 36 | 36 | }; |
| 37 | 37 | use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; |
| 38 | use crate::html::render::sidebar::filters; | |
| 38 | 39 | use crate::html::render::{document_full, document_item_info}; |
| 39 | 40 | use crate::html::url_parts_builder::UrlPartsBuilder; |
| 40 | 41 |
src/librustdoc/html/render/search_index.rs+1565-612| ... | ... | @@ -1,72 +1,1169 @@ |
| 1 | 1 | pub(crate) mod encode; |
| 2 | 2 | |
| 3 | use std::collections::BTreeSet; | |
| 3 | 4 | use std::collections::hash_map::Entry; |
| 4 | use std::collections::{BTreeMap, VecDeque}; | |
| 5 | use std::path::Path; | |
| 5 | 6 | |
| 6 | use encode::{bitmap_to_string, write_vlqhex_to_string}; | |
| 7 | 7 | use rustc_ast::join_path_syms; |
| 8 | use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; | |
| 8 | use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; | |
| 9 | 9 | use rustc_middle::ty::TyCtxt; |
| 10 | 10 | use rustc_span::def_id::DefId; |
| 11 | 11 | use rustc_span::sym; |
| 12 | 12 | use rustc_span::symbol::{Symbol, kw}; |
| 13 | use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer}; | |
| 13 | use serde::de::{self, Deserializer, Error as _}; | |
| 14 | use serde::ser::{SerializeSeq, Serializer}; | |
| 15 | use serde::{Deserialize, Serialize}; | |
| 16 | use stringdex::internals as stringdex_internals; | |
| 14 | 17 | use thin_vec::ThinVec; |
| 15 | 18 | use tracing::instrument; |
| 16 | 19 | |
| 17 | 20 | use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate}; |
| 18 | 21 | use crate::clean::{self, utils}; |
| 22 | use crate::error::Error; | |
| 19 | 23 | use crate::formats::cache::{Cache, OrphanImplItem}; |
| 20 | 24 | use crate::formats::item_type::ItemType; |
| 21 | 25 | use crate::html::markdown::short_markdown_summary; |
| 22 | use crate::html::render::ordered_json::OrderedJson; | |
| 23 | 26 | use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId}; |
| 24 | 27 | |
| 25 | /// The serialized search description sharded version | |
| 26 | /// | |
| 27 | /// The `index` is a JSON-encoded list of names and other information. | |
| 28 | /// | |
| 29 | /// The desc has newlined descriptions, split up by size into 128KiB shards. | |
| 30 | /// For example, `(4, "foo\nbar\nbaz\nquux")`. | |
| 31 | /// | |
| 32 | /// There is no single, optimal size for these shards, because it depends on | |
| 33 | /// configuration values that we can't predict or control, such as the version | |
| 34 | /// of HTTP used (HTTP/1.1 would work better with larger files, while HTTP/2 | |
| 35 | /// and 3 are more agnostic), transport compression (gzip, zstd, etc), whether | |
| 36 | /// the search query is going to produce a large number of results or a small | |
| 37 | /// number, the bandwidth delay product of the network... | |
| 38 | /// | |
| 39 | /// Gzipping some standard library descriptions to guess what transport | |
| 40 | /// compression will do, the compressed file sizes can be as small as 4.9KiB | |
| 41 | /// or as large as 18KiB (ignoring the final 1.9KiB shard of leftovers). | |
| 42 | /// A "reasonable" range for files is for them to be bigger than 1KiB, | |
| 43 | /// since that's about the amount of data that can be transferred in a | |
| 44 | /// single TCP packet, and 64KiB, the maximum amount of data that | |
| 45 | /// TCP can transfer in a single round trip without extensions. | |
| 46 | /// | |
| 47 | /// [1]: https://en.wikipedia.org/wiki/Maximum_transmission_unit#MTUs_for_common_media | |
| 48 | /// [2]: https://en.wikipedia.org/wiki/Sliding_window_protocol#Basic_concept | |
| 49 | /// [3]: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features | |
| 28 | #[derive(Clone, Debug, Default, Deserialize, Serialize)] | |
| 50 | 29 | pub(crate) struct SerializedSearchIndex { |
| 51 | pub(crate) index: OrderedJson, | |
| 52 | pub(crate) desc: Vec<(usize, String)>, | |
| 30 | // data from disk | |
| 31 | names: Vec<String>, | |
| 32 | path_data: Vec<Option<PathData>>, | |
| 33 | entry_data: Vec<Option<EntryData>>, | |
| 34 | descs: Vec<String>, | |
| 35 | function_data: Vec<Option<FunctionData>>, | |
| 36 | alias_pointers: Vec<Option<usize>>, | |
| 37 | // inverted index for concrete types and generics | |
| 38 | type_data: Vec<Option<TypeData>>, | |
| 39 | /// inverted index of generics | |
| 40 | /// | |
| 41 | /// - The outermost list has one entry per alpha-normalized generic. | |
| 42 | /// | |
| 43 | /// - The second layer is sorted by number of types that appear in the | |
| 44 | /// type signature. The search engine iterates over these in order from | |
| 45 | /// smallest to largest. Functions with less stuff in their type | |
| 46 | /// signature are more likely to be what the user wants, because we never | |
| 47 | /// show functions that are *missing* parts of the query, so removing.. | |
| 48 | /// | |
| 49 | /// - The final layer is the list of functions. | |
| 50 | generic_inverted_index: Vec<Vec<Vec<u32>>>, | |
| 51 | // generated in-memory backref cache | |
| 52 | #[serde(skip)] | |
| 53 | crate_paths_index: FxHashMap<(ItemType, Vec<Symbol>), usize>, | |
| 54 | } | |
| 55 | ||
| 56 | impl SerializedSearchIndex { | |
| 57 | fn load(doc_root: &Path, resource_suffix: &str) -> Result<SerializedSearchIndex, Error> { | |
| 58 | let mut names: Vec<String> = Vec::new(); | |
| 59 | let mut path_data: Vec<Option<PathData>> = Vec::new(); | |
| 60 | let mut entry_data: Vec<Option<EntryData>> = Vec::new(); | |
| 61 | let mut descs: Vec<String> = Vec::new(); | |
| 62 | let mut function_data: Vec<Option<FunctionData>> = Vec::new(); | |
| 63 | let mut type_data: Vec<Option<TypeData>> = Vec::new(); | |
| 64 | let mut alias_pointers: Vec<Option<usize>> = Vec::new(); | |
| 65 | ||
| 66 | let mut generic_inverted_index: Vec<Vec<Vec<u32>>> = Vec::new(); | |
| 67 | ||
| 68 | match perform_read_strings(resource_suffix, doc_root, "name", &mut names) { | |
| 69 | Ok(()) => { | |
| 70 | perform_read_serde(resource_suffix, doc_root, "path", &mut path_data)?; | |
| 71 | perform_read_serde(resource_suffix, doc_root, "entry", &mut entry_data)?; | |
| 72 | perform_read_strings(resource_suffix, doc_root, "desc", &mut descs)?; | |
| 73 | perform_read_serde(resource_suffix, doc_root, "function", &mut function_data)?; | |
| 74 | perform_read_serde(resource_suffix, doc_root, "type", &mut type_data)?; | |
| 75 | perform_read_serde(resource_suffix, doc_root, "alias", &mut alias_pointers)?; | |
| 76 | perform_read_postings( | |
| 77 | resource_suffix, | |
| 78 | doc_root, | |
| 79 | "generic_inverted_index", | |
| 80 | &mut generic_inverted_index, | |
| 81 | )?; | |
| 82 | } | |
| 83 | Err(_) => { | |
| 84 | names.clear(); | |
| 85 | } | |
| 86 | } | |
| 87 | fn perform_read_strings( | |
| 88 | resource_suffix: &str, | |
| 89 | doc_root: &Path, | |
| 90 | column_name: &str, | |
| 91 | column: &mut Vec<String>, | |
| 92 | ) -> Result<(), Error> { | |
| 93 | let root_path = doc_root.join(format!("search.index/root{resource_suffix}.js")); | |
| 94 | let column_path = doc_root.join(format!("search.index/{column_name}/")); | |
| 95 | stringdex_internals::read_data_from_disk_column( | |
| 96 | root_path, | |
| 97 | column_name.as_bytes(), | |
| 98 | column_path.clone(), | |
| 99 | &mut |_id, item| { | |
| 100 | column.push(String::from_utf8(item.to_vec())?); | |
| 101 | Ok(()) | |
| 102 | }, | |
| 103 | ) | |
| 104 | .map_err( | |
| 105 | |error: stringdex_internals::ReadDataError<Box<dyn std::error::Error>>| Error { | |
| 106 | file: column_path, | |
| 107 | error: format!("failed to read column from disk: {error}"), | |
| 108 | }, | |
| 109 | ) | |
| 110 | } | |
| 111 | fn perform_read_serde( | |
| 112 | resource_suffix: &str, | |
| 113 | doc_root: &Path, | |
| 114 | column_name: &str, | |
| 115 | column: &mut Vec<Option<impl for<'de> Deserialize<'de> + 'static>>, | |
| 116 | ) -> Result<(), Error> { | |
| 117 | let root_path = doc_root.join(format!("search.index/root{resource_suffix}.js")); | |
| 118 | let column_path = doc_root.join(format!("search.index/{column_name}/")); | |
| 119 | stringdex_internals::read_data_from_disk_column( | |
| 120 | root_path, | |
| 121 | column_name.as_bytes(), | |
| 122 | column_path.clone(), | |
| 123 | &mut |_id, item| { | |
| 124 | if item.is_empty() { | |
| 125 | column.push(None); | |
| 126 | } else { | |
| 127 | column.push(Some(serde_json::from_slice(item)?)); | |
| 128 | } | |
| 129 | Ok(()) | |
| 130 | }, | |
| 131 | ) | |
| 132 | .map_err( | |
| 133 | |error: stringdex_internals::ReadDataError<Box<dyn std::error::Error>>| Error { | |
| 134 | file: column_path, | |
| 135 | error: format!("failed to read column from disk: {error}"), | |
| 136 | }, | |
| 137 | ) | |
| 138 | } | |
| 139 | fn perform_read_postings( | |
| 140 | resource_suffix: &str, | |
| 141 | doc_root: &Path, | |
| 142 | column_name: &str, | |
| 143 | column: &mut Vec<Vec<Vec<u32>>>, | |
| 144 | ) -> Result<(), Error> { | |
| 145 | let root_path = doc_root.join(format!("search.index/root{resource_suffix}.js")); | |
| 146 | let column_path = doc_root.join(format!("search.index/{column_name}/")); | |
| 147 | stringdex_internals::read_data_from_disk_column( | |
| 148 | root_path, | |
| 149 | column_name.as_bytes(), | |
| 150 | column_path.clone(), | |
| 151 | &mut |_id, buf| { | |
| 152 | let mut postings = Vec::new(); | |
| 153 | encode::read_postings_from_string(&mut postings, buf); | |
| 154 | column.push(postings); | |
| 155 | Ok(()) | |
| 156 | }, | |
| 157 | ) | |
| 158 | .map_err( | |
| 159 | |error: stringdex_internals::ReadDataError<Box<dyn std::error::Error>>| Error { | |
| 160 | file: column_path, | |
| 161 | error: format!("failed to read column from disk: {error}"), | |
| 162 | }, | |
| 163 | ) | |
| 164 | } | |
| 165 | ||
| 166 | assert_eq!(names.len(), path_data.len()); | |
| 167 | assert_eq!(path_data.len(), entry_data.len()); | |
| 168 | assert_eq!(entry_data.len(), descs.len()); | |
| 169 | assert_eq!(descs.len(), function_data.len()); | |
| 170 | assert_eq!(function_data.len(), type_data.len()); | |
| 171 | assert_eq!(type_data.len(), alias_pointers.len()); | |
| 172 | ||
| 173 | // generic_inverted_index is not the same length as other columns, | |
| 174 | // because it's actually a completely different set of objects | |
| 175 | ||
| 176 | let mut crate_paths_index: FxHashMap<(ItemType, Vec<Symbol>), usize> = FxHashMap::default(); | |
| 177 | for (i, (name, path_data)) in names.iter().zip(path_data.iter()).enumerate() { | |
| 178 | if let Some(path_data) = path_data { | |
| 179 | let full_path = if path_data.module_path.is_empty() { | |
| 180 | vec![Symbol::intern(name)] | |
| 181 | } else { | |
| 182 | let mut full_path = path_data.module_path.to_vec(); | |
| 183 | full_path.push(Symbol::intern(name)); | |
| 184 | full_path | |
| 185 | }; | |
| 186 | crate_paths_index.insert((path_data.ty, full_path), i); | |
| 187 | } | |
| 188 | } | |
| 189 | ||
| 190 | Ok(SerializedSearchIndex { | |
| 191 | names, | |
| 192 | path_data, | |
| 193 | entry_data, | |
| 194 | descs, | |
| 195 | function_data, | |
| 196 | type_data, | |
| 197 | alias_pointers, | |
| 198 | generic_inverted_index, | |
| 199 | crate_paths_index, | |
| 200 | }) | |
| 201 | } | |
| 202 | fn push( | |
| 203 | &mut self, | |
| 204 | name: String, | |
| 205 | path_data: Option<PathData>, | |
| 206 | entry_data: Option<EntryData>, | |
| 207 | desc: String, | |
| 208 | function_data: Option<FunctionData>, | |
| 209 | type_data: Option<TypeData>, | |
| 210 | alias_pointer: Option<usize>, | |
| 211 | ) -> usize { | |
| 212 | let index = self.names.len(); | |
| 213 | assert_eq!(self.names.len(), self.path_data.len()); | |
| 214 | if let Some(path_data) = &path_data | |
| 215 | && let name = Symbol::intern(&name) | |
| 216 | && let fqp = if path_data.module_path.is_empty() { | |
| 217 | vec![name] | |
| 218 | } else { | |
| 219 | let mut v = path_data.module_path.clone(); | |
| 220 | v.push(name); | |
| 221 | v | |
| 222 | } | |
| 223 | && let Some(&other_path) = self.crate_paths_index.get(&(path_data.ty, fqp)) | |
| 224 | && self.path_data.get(other_path).map_or(false, Option::is_some) | |
| 225 | { | |
| 226 | self.path_data.push(None); | |
| 227 | } else { | |
| 228 | self.path_data.push(path_data); | |
| 229 | } | |
| 230 | self.names.push(name); | |
| 231 | assert_eq!(self.entry_data.len(), self.descs.len()); | |
| 232 | self.entry_data.push(entry_data); | |
| 233 | assert_eq!(self.descs.len(), self.function_data.len()); | |
| 234 | self.descs.push(desc); | |
| 235 | assert_eq!(self.function_data.len(), self.type_data.len()); | |
| 236 | self.function_data.push(function_data); | |
| 237 | assert_eq!(self.type_data.len(), self.alias_pointers.len()); | |
| 238 | self.type_data.push(type_data); | |
| 239 | self.alias_pointers.push(alias_pointer); | |
| 240 | index | |
| 241 | } | |
| 242 | fn push_path(&mut self, name: String, path_data: PathData) -> usize { | |
| 243 | self.push(name, Some(path_data), None, String::new(), None, None, None) | |
| 244 | } | |
| 245 | fn push_type(&mut self, name: String, path_data: PathData, type_data: TypeData) -> usize { | |
| 246 | self.push(name, Some(path_data), None, String::new(), None, Some(type_data), None) | |
| 247 | } | |
| 248 | fn push_alias(&mut self, name: String, alias_pointer: usize) -> usize { | |
| 249 | self.push(name, None, None, String::new(), None, None, Some(alias_pointer)) | |
| 250 | } | |
| 251 | ||
| 252 | fn get_id_by_module_path(&mut self, path: &[Symbol]) -> usize { | |
| 253 | let ty = if path.len() == 1 { ItemType::ExternCrate } else { ItemType::Module }; | |
| 254 | match self.crate_paths_index.entry((ty, path.to_vec())) { | |
| 255 | Entry::Occupied(index) => *index.get(), | |
| 256 | Entry::Vacant(slot) => { | |
| 257 | slot.insert(self.path_data.len()); | |
| 258 | let (name, module_path) = path.split_last().unwrap(); | |
| 259 | self.push_path( | |
| 260 | name.as_str().to_string(), | |
| 261 | PathData { ty, module_path: module_path.to_vec(), exact_module_path: None }, | |
| 262 | ) | |
| 263 | } | |
| 264 | } | |
| 265 | } | |
| 266 | ||
| 267 | pub(crate) fn union(mut self, other: &SerializedSearchIndex) -> SerializedSearchIndex { | |
| 268 | let other_entryid_offset = self.names.len(); | |
| 269 | let mut map_other_pathid_to_self_pathid: Vec<usize> = Vec::new(); | |
| 270 | let mut skips = FxHashSet::default(); | |
| 271 | for (other_pathid, other_path_data) in other.path_data.iter().enumerate() { | |
| 272 | if let Some(other_path_data) = other_path_data { | |
| 273 | let mut fqp = other_path_data.module_path.clone(); | |
| 274 | let name = Symbol::intern(&other.names[other_pathid]); | |
| 275 | fqp.push(name); | |
| 276 | let self_pathid = other_entryid_offset + other_pathid; | |
| 277 | let self_pathid = match self.crate_paths_index.entry((other_path_data.ty, fqp)) { | |
| 278 | Entry::Vacant(slot) => { | |
| 279 | slot.insert(self_pathid); | |
| 280 | self_pathid | |
| 281 | } | |
| 282 | Entry::Occupied(existing_entryid) => { | |
| 283 | skips.insert(other_pathid); | |
| 284 | let self_pathid = *existing_entryid.get(); | |
| 285 | let new_type_data = match ( | |
| 286 | self.type_data[self_pathid].take(), | |
| 287 | other.type_data[other_pathid].as_ref(), | |
| 288 | ) { | |
| 289 | (Some(self_type_data), None) => Some(self_type_data), | |
| 290 | (None, Some(other_type_data)) => Some(TypeData { | |
| 291 | search_unbox: other_type_data.search_unbox, | |
| 292 | inverted_function_signature_index: other_type_data | |
| 293 | .inverted_function_signature_index | |
| 294 | .iter() | |
| 295 | .cloned() | |
| 296 | .map(|mut list: Vec<u32>| { | |
| 297 | for fnid in &mut list { | |
| 298 | assert!( | |
| 299 | other.function_data | |
| 300 | [usize::try_from(*fnid).unwrap()] | |
| 301 | .is_some(), | |
| 302 | ); | |
| 303 | // this is valid because we call `self.push()` once, exactly, for every entry, | |
| 304 | // even if we're just pushing a tombstone | |
| 305 | *fnid += u32::try_from(other_entryid_offset).unwrap(); | |
| 306 | } | |
| 307 | list | |
| 308 | }) | |
| 309 | .collect(), | |
| 310 | }), | |
| 311 | (Some(mut self_type_data), Some(other_type_data)) => { | |
| 312 | for (size, other_list) in other_type_data | |
| 313 | .inverted_function_signature_index | |
| 314 | .iter() | |
| 315 | .enumerate() | |
| 316 | { | |
| 317 | while self_type_data.inverted_function_signature_index.len() | |
| 318 | <= size | |
| 319 | { | |
| 320 | self_type_data | |
| 321 | .inverted_function_signature_index | |
| 322 | .push(Vec::new()); | |
| 323 | } | |
| 324 | self_type_data.inverted_function_signature_index[size].extend( | |
| 325 | other_list.iter().copied().map(|fnid| { | |
| 326 | assert!( | |
| 327 | other.function_data[usize::try_from(fnid).unwrap()] | |
| 328 | .is_some(), | |
| 329 | ); | |
| 330 | // this is valid because we call `self.push()` once, exactly, for every entry, | |
| 331 | // even if we're just pushing a tombstone | |
| 332 | fnid + u32::try_from(other_entryid_offset).unwrap() | |
| 333 | }), | |
| 334 | ) | |
| 335 | } | |
| 336 | Some(self_type_data) | |
| 337 | } | |
| 338 | (None, None) => None, | |
| 339 | }; | |
| 340 | self.type_data[self_pathid] = new_type_data; | |
| 341 | self_pathid | |
| 342 | } | |
| 343 | }; | |
| 344 | map_other_pathid_to_self_pathid.push(self_pathid); | |
| 345 | } else { | |
| 346 | // if this gets used, we want it to crash | |
| 347 | // this should be impossible as a valid index, since some of the | |
| 348 | // memory must be used for stuff other than the list | |
| 349 | map_other_pathid_to_self_pathid.push(!0); | |
| 350 | } | |
| 351 | } | |
| 352 | for other_entryid in 0..other.names.len() { | |
| 353 | if skips.contains(&other_entryid) { | |
| 354 | // we push tombstone entries to keep the IDs lined up | |
| 355 | self.push(String::new(), None, None, String::new(), None, None, None); | |
| 356 | } else { | |
| 357 | self.push( | |
| 358 | other.names[other_entryid].clone(), | |
| 359 | other.path_data[other_entryid].clone(), | |
| 360 | other.entry_data[other_entryid].as_ref().map(|other_entry_data| EntryData { | |
| 361 | parent: other_entry_data | |
| 362 | .parent | |
| 363 | .map(|parent| map_other_pathid_to_self_pathid[parent]) | |
| 364 | .clone(), | |
| 365 | module_path: other_entry_data | |
| 366 | .module_path | |
| 367 | .map(|path| map_other_pathid_to_self_pathid[path]) | |
| 368 | .clone(), | |
| 369 | exact_module_path: other_entry_data | |
| 370 | .exact_module_path | |
| 371 | .map(|exact_path| map_other_pathid_to_self_pathid[exact_path]) | |
| 372 | .clone(), | |
| 373 | krate: map_other_pathid_to_self_pathid[other_entry_data.krate], | |
| 374 | ..other_entry_data.clone() | |
| 375 | }), | |
| 376 | other.descs[other_entryid].clone(), | |
| 377 | other.function_data[other_entryid].as_ref().map(|function_data| FunctionData { | |
| 378 | function_signature: { | |
| 379 | let (mut func, _offset) = | |
| 380 | IndexItemFunctionType::read_from_string_without_param_names( | |
| 381 | function_data.function_signature.as_bytes(), | |
| 382 | ); | |
| 383 | fn map_fn_sig_item( | |
| 384 | map_other_pathid_to_self_pathid: &mut Vec<usize>, | |
| 385 | ty: &mut RenderType, | |
| 386 | ) { | |
| 387 | match ty.id { | |
| 388 | None => {} | |
| 389 | Some(RenderTypeId::Index(generic)) if generic < 0 => {} | |
| 390 | Some(RenderTypeId::Index(id)) => { | |
| 391 | let id = usize::try_from(id).unwrap(); | |
| 392 | let id = map_other_pathid_to_self_pathid[id]; | |
| 393 | assert!(id != !0); | |
| 394 | ty.id = | |
| 395 | Some(RenderTypeId::Index(isize::try_from(id).unwrap())); | |
| 396 | } | |
| 397 | _ => unreachable!(), | |
| 398 | } | |
| 399 | if let Some(generics) = &mut ty.generics { | |
| 400 | for generic in generics { | |
| 401 | map_fn_sig_item(map_other_pathid_to_self_pathid, generic); | |
| 402 | } | |
| 403 | } | |
| 404 | if let Some(bindings) = &mut ty.bindings { | |
| 405 | for (param, constraints) in bindings { | |
| 406 | *param = match *param { | |
| 407 | param @ RenderTypeId::Index(generic) if generic < 0 => { | |
| 408 | param | |
| 409 | } | |
| 410 | RenderTypeId::Index(id) => { | |
| 411 | let id = usize::try_from(id).unwrap(); | |
| 412 | let id = map_other_pathid_to_self_pathid[id]; | |
| 413 | assert!(id != !0); | |
| 414 | RenderTypeId::Index(isize::try_from(id).unwrap()) | |
| 415 | } | |
| 416 | _ => unreachable!(), | |
| 417 | }; | |
| 418 | for constraint in constraints { | |
| 419 | map_fn_sig_item( | |
| 420 | map_other_pathid_to_self_pathid, | |
| 421 | constraint, | |
| 422 | ); | |
| 423 | } | |
| 424 | } | |
| 425 | } | |
| 426 | } | |
| 427 | for input in &mut func.inputs { | |
| 428 | map_fn_sig_item(&mut map_other_pathid_to_self_pathid, input); | |
| 429 | } | |
| 430 | for output in &mut func.output { | |
| 431 | map_fn_sig_item(&mut map_other_pathid_to_self_pathid, output); | |
| 432 | } | |
| 433 | for clause in &mut func.where_clause { | |
| 434 | for entry in clause { | |
| 435 | map_fn_sig_item(&mut map_other_pathid_to_self_pathid, entry); | |
| 436 | } | |
| 437 | } | |
| 438 | let mut result = | |
| 439 | String::with_capacity(function_data.function_signature.len()); | |
| 440 | func.write_to_string_without_param_names(&mut result); | |
| 441 | result | |
| 442 | }, | |
| 443 | param_names: function_data.param_names.clone(), | |
| 444 | }), | |
| 445 | other.type_data[other_entryid].as_ref().map(|type_data| TypeData { | |
| 446 | inverted_function_signature_index: type_data | |
| 447 | .inverted_function_signature_index | |
| 448 | .iter() | |
| 449 | .cloned() | |
| 450 | .map(|mut list| { | |
| 451 | for fnid in &mut list { | |
| 452 | assert!( | |
| 453 | other.function_data[usize::try_from(*fnid).unwrap()] | |
| 454 | .is_some(), | |
| 455 | ); | |
| 456 | // this is valid because we call `self.push()` once, exactly, for every entry, | |
| 457 | // even if we're just pushing a tombstone | |
| 458 | *fnid += u32::try_from(other_entryid_offset).unwrap(); | |
| 459 | } | |
| 460 | list | |
| 461 | }) | |
| 462 | .collect(), | |
| 463 | search_unbox: type_data.search_unbox, | |
| 464 | }), | |
| 465 | other.alias_pointers[other_entryid] | |
| 466 | .map(|alias_pointer| alias_pointer + other_entryid_offset), | |
| 467 | ); | |
| 468 | } | |
| 469 | } | |
| 470 | for (i, other_generic_inverted_index) in other.generic_inverted_index.iter().enumerate() { | |
| 471 | for (size, other_list) in other_generic_inverted_index.iter().enumerate() { | |
| 472 | let self_generic_inverted_index = match self.generic_inverted_index.get_mut(i) { | |
| 473 | Some(self_generic_inverted_index) => self_generic_inverted_index, | |
| 474 | None => { | |
| 475 | self.generic_inverted_index.push(Vec::new()); | |
| 476 | self.generic_inverted_index.last_mut().unwrap() | |
| 477 | } | |
| 478 | }; | |
| 479 | while self_generic_inverted_index.len() <= size { | |
| 480 | self_generic_inverted_index.push(Vec::new()); | |
| 481 | } | |
| 482 | self_generic_inverted_index[size].extend( | |
| 483 | other_list | |
| 484 | .iter() | |
| 485 | .copied() | |
| 486 | .map(|fnid| fnid + u32::try_from(other_entryid_offset).unwrap()), | |
| 487 | ); | |
| 488 | } | |
| 489 | } | |
| 490 | self | |
| 491 | } | |
| 492 | ||
| 493 | pub(crate) fn sort(self) -> SerializedSearchIndex { | |
| 494 | let mut idlist: Vec<usize> = (0..self.names.len()).collect(); | |
| 495 | // nameless entries are tombstones, and will be removed after sorting | |
| 496 | // sort shorter names first, so that we can present them in order out of search.js | |
| 497 | idlist.sort_by_key(|&id| { | |
| 498 | ( | |
| 499 | self.names[id].is_empty(), | |
| 500 | self.names[id].len(), | |
| 501 | &self.names[id], | |
| 502 | self.entry_data[id].as_ref().map_or("", |entry| self.names[entry.krate].as_str()), | |
| 503 | self.path_data[id].as_ref().map_or(&[][..], |entry| &entry.module_path[..]), | |
| 504 | ) | |
| 505 | }); | |
| 506 | let map = FxHashMap::from_iter( | |
| 507 | idlist.iter().enumerate().map(|(new_id, &old_id)| (old_id, new_id)), | |
| 508 | ); | |
| 509 | let mut new = SerializedSearchIndex::default(); | |
| 510 | for &id in &idlist { | |
| 511 | if self.names[id].is_empty() { | |
| 512 | break; | |
| 513 | } | |
| 514 | new.push( | |
| 515 | self.names[id].clone(), | |
| 516 | self.path_data[id].clone(), | |
| 517 | self.entry_data[id].as_ref().map( | |
| 518 | |EntryData { | |
| 519 | krate, | |
| 520 | ty, | |
| 521 | module_path, | |
| 522 | exact_module_path, | |
| 523 | parent, | |
| 524 | deprecated, | |
| 525 | associated_item_disambiguator, | |
| 526 | }| EntryData { | |
| 527 | krate: *map.get(krate).unwrap(), | |
| 528 | ty: *ty, | |
| 529 | module_path: module_path.and_then(|path_id| map.get(&path_id).copied()), | |
| 530 | exact_module_path: exact_module_path | |
| 531 | .and_then(|path_id| map.get(&path_id).copied()), | |
| 532 | parent: parent.and_then(|path_id| map.get(&path_id).copied()), | |
| 533 | deprecated: *deprecated, | |
| 534 | associated_item_disambiguator: associated_item_disambiguator.clone(), | |
| 535 | }, | |
| 536 | ), | |
| 537 | self.descs[id].clone(), | |
| 538 | self.function_data[id].as_ref().map( | |
| 539 | |FunctionData { function_signature, param_names }| FunctionData { | |
| 540 | function_signature: { | |
| 541 | let (mut func, _offset) = | |
| 542 | IndexItemFunctionType::read_from_string_without_param_names( | |
| 543 | function_signature.as_bytes(), | |
| 544 | ); | |
| 545 | fn map_fn_sig_item(map: &FxHashMap<usize, usize>, ty: &mut RenderType) { | |
| 546 | match ty.id { | |
| 547 | None => {} | |
| 548 | Some(RenderTypeId::Index(generic)) if generic < 0 => {} | |
| 549 | Some(RenderTypeId::Index(id)) => { | |
| 550 | let id = usize::try_from(id).unwrap(); | |
| 551 | let id = *map.get(&id).unwrap(); | |
| 552 | assert!(id != !0); | |
| 553 | ty.id = | |
| 554 | Some(RenderTypeId::Index(isize::try_from(id).unwrap())); | |
| 555 | } | |
| 556 | _ => unreachable!(), | |
| 557 | } | |
| 558 | if let Some(generics) = &mut ty.generics { | |
| 559 | for generic in generics { | |
| 560 | map_fn_sig_item(map, generic); | |
| 561 | } | |
| 562 | } | |
| 563 | if let Some(bindings) = &mut ty.bindings { | |
| 564 | for (param, constraints) in bindings { | |
| 565 | *param = match *param { | |
| 566 | param @ RenderTypeId::Index(generic) if generic < 0 => { | |
| 567 | param | |
| 568 | } | |
| 569 | RenderTypeId::Index(id) => { | |
| 570 | let id = usize::try_from(id).unwrap(); | |
| 571 | let id = *map.get(&id).unwrap(); | |
| 572 | assert!(id != !0); | |
| 573 | RenderTypeId::Index(isize::try_from(id).unwrap()) | |
| 574 | } | |
| 575 | _ => unreachable!(), | |
| 576 | }; | |
| 577 | for constraint in constraints { | |
| 578 | map_fn_sig_item(map, constraint); | |
| 579 | } | |
| 580 | } | |
| 581 | } | |
| 582 | } | |
| 583 | for input in &mut func.inputs { | |
| 584 | map_fn_sig_item(&map, input); | |
| 585 | } | |
| 586 | for output in &mut func.output { | |
| 587 | map_fn_sig_item(&map, output); | |
| 588 | } | |
| 589 | for clause in &mut func.where_clause { | |
| 590 | for entry in clause { | |
| 591 | map_fn_sig_item(&map, entry); | |
| 592 | } | |
| 593 | } | |
| 594 | let mut result = String::with_capacity(function_signature.len()); | |
| 595 | func.write_to_string_without_param_names(&mut result); | |
| 596 | result | |
| 597 | }, | |
| 598 | param_names: param_names.clone(), | |
| 599 | }, | |
| 600 | ), | |
| 601 | self.type_data[id].as_ref().map( | |
| 602 | |TypeData { search_unbox, inverted_function_signature_index }| { | |
| 603 | let inverted_function_signature_index: Vec<Vec<u32>> = | |
| 604 | inverted_function_signature_index | |
| 605 | .iter() | |
| 606 | .cloned() | |
| 607 | .map(|mut list| { | |
| 608 | for id in &mut list { | |
| 609 | *id = u32::try_from( | |
| 610 | *map.get(&usize::try_from(*id).unwrap()).unwrap(), | |
| 611 | ) | |
| 612 | .unwrap(); | |
| 613 | } | |
| 614 | list.sort(); | |
| 615 | list | |
| 616 | }) | |
| 617 | .collect(); | |
| 618 | TypeData { search_unbox: *search_unbox, inverted_function_signature_index } | |
| 619 | }, | |
| 620 | ), | |
| 621 | self.alias_pointers[id].and_then(|alias| map.get(&alias).copied()), | |
| 622 | ); | |
| 623 | } | |
| 624 | new.generic_inverted_index = self | |
| 625 | .generic_inverted_index | |
| 626 | .into_iter() | |
| 627 | .map(|mut postings| { | |
| 628 | for list in postings.iter_mut() { | |
| 629 | let mut new_list: Vec<u32> = list | |
| 630 | .iter() | |
| 631 | .copied() | |
| 632 | .filter_map(|id| u32::try_from(*map.get(&usize::try_from(id).ok()?)?).ok()) | |
| 633 | .collect(); | |
| 634 | new_list.sort(); | |
| 635 | *list = new_list; | |
| 636 | } | |
| 637 | postings | |
| 638 | }) | |
| 639 | .collect(); | |
| 640 | new | |
| 641 | } | |
| 642 | ||
| 643 | pub(crate) fn write_to(self, doc_root: &Path, resource_suffix: &str) -> Result<(), Error> { | |
| 644 | let SerializedSearchIndex { | |
| 645 | names, | |
| 646 | path_data, | |
| 647 | entry_data, | |
| 648 | descs, | |
| 649 | function_data, | |
| 650 | type_data, | |
| 651 | alias_pointers, | |
| 652 | generic_inverted_index, | |
| 653 | crate_paths_index: _, | |
| 654 | } = self; | |
| 655 | let mut serialized_root = Vec::new(); | |
| 656 | serialized_root.extend_from_slice(br#"rr_('{"normalizedName":{"I":""#); | |
| 657 | let normalized_names = names | |
| 658 | .iter() | |
| 659 | .map(|name| { | |
| 660 | if name.contains("_") { | |
| 661 | name.replace("_", "").to_ascii_lowercase() | |
| 662 | } else { | |
| 663 | name.to_ascii_lowercase() | |
| 664 | } | |
| 665 | }) | |
| 666 | .collect::<Vec<String>>(); | |
| 667 | let names_search_tree = stringdex_internals::tree::encode_search_tree_ukkonen( | |
| 668 | normalized_names.iter().map(|name| name.as_bytes()), | |
| 669 | ); | |
| 670 | let dir_path = doc_root.join(format!("search.index/")); | |
| 671 | let _ = std::fs::remove_dir_all(&dir_path); // if already missing, no problem | |
| 672 | stringdex_internals::write_tree_to_disk( | |
| 673 | &names_search_tree, | |
| 674 | &dir_path, | |
| 675 | &mut serialized_root, | |
| 676 | ) | |
| 677 | .map_err(|error| Error { | |
| 678 | file: dir_path, | |
| 679 | error: format!("failed to write name tree to disk: {error}"), | |
| 680 | })?; | |
| 681 | std::mem::drop(names_search_tree); | |
| 682 | serialized_root.extend_from_slice(br#"","#); | |
| 683 | serialized_root.extend_from_slice(&perform_write_strings( | |
| 684 | doc_root, | |
| 685 | "normalizedName", | |
| 686 | normalized_names.into_iter(), | |
| 687 | )?); | |
| 688 | serialized_root.extend_from_slice(br#"},"crateNames":{"#); | |
| 689 | let mut crates: Vec<&[u8]> = entry_data | |
| 690 | .iter() | |
| 691 | .filter_map(|entry_data| Some(names[entry_data.as_ref()?.krate].as_bytes())) | |
| 692 | .collect(); | |
| 693 | crates.sort(); | |
| 694 | crates.dedup(); | |
| 695 | serialized_root.extend_from_slice(&perform_write_strings( | |
| 696 | doc_root, | |
| 697 | "crateNames", | |
| 698 | crates.into_iter(), | |
| 699 | )?); | |
| 700 | serialized_root.extend_from_slice(br#"},"name":{"#); | |
| 701 | serialized_root.extend_from_slice(&perform_write_strings(doc_root, "name", names.iter())?); | |
| 702 | serialized_root.extend_from_slice(br#"},"path":{"#); | |
| 703 | serialized_root.extend_from_slice(&perform_write_serde(doc_root, "path", path_data)?); | |
| 704 | serialized_root.extend_from_slice(br#"},"entry":{"#); | |
| 705 | serialized_root.extend_from_slice(&perform_write_serde(doc_root, "entry", entry_data)?); | |
| 706 | serialized_root.extend_from_slice(br#"},"desc":{"#); | |
| 707 | serialized_root.extend_from_slice(&perform_write_strings( | |
| 708 | doc_root, | |
| 709 | "desc", | |
| 710 | descs.into_iter(), | |
| 711 | )?); | |
| 712 | serialized_root.extend_from_slice(br#"},"function":{"#); | |
| 713 | serialized_root.extend_from_slice(&perform_write_serde( | |
| 714 | doc_root, | |
| 715 | "function", | |
| 716 | function_data, | |
| 717 | )?); | |
| 718 | serialized_root.extend_from_slice(br#"},"type":{"#); | |
| 719 | serialized_root.extend_from_slice(&perform_write_serde(doc_root, "type", type_data)?); | |
| 720 | serialized_root.extend_from_slice(br#"},"alias":{"#); | |
| 721 | serialized_root.extend_from_slice(&perform_write_serde(doc_root, "alias", alias_pointers)?); | |
| 722 | serialized_root.extend_from_slice(br#"},"generic_inverted_index":{"#); | |
| 723 | serialized_root.extend_from_slice(&perform_write_postings( | |
| 724 | doc_root, | |
| 725 | "generic_inverted_index", | |
| 726 | generic_inverted_index, | |
| 727 | )?); | |
| 728 | serialized_root.extend_from_slice(br#"}}')"#); | |
| 729 | fn perform_write_strings( | |
| 730 | doc_root: &Path, | |
| 731 | dirname: &str, | |
| 732 | mut column: impl Iterator<Item = impl AsRef<[u8]> + Clone> + ExactSizeIterator, | |
| 733 | ) -> Result<Vec<u8>, Error> { | |
| 734 | let dir_path = doc_root.join(format!("search.index/{dirname}")); | |
| 735 | stringdex_internals::write_data_to_disk(&mut column, &dir_path).map_err(|error| Error { | |
| 736 | file: dir_path, | |
| 737 | error: format!("failed to write column to disk: {error}"), | |
| 738 | }) | |
| 739 | } | |
| 740 | fn perform_write_serde( | |
| 741 | doc_root: &Path, | |
| 742 | dirname: &str, | |
| 743 | column: Vec<Option<impl Serialize>>, | |
| 744 | ) -> Result<Vec<u8>, Error> { | |
| 745 | perform_write_strings( | |
| 746 | doc_root, | |
| 747 | dirname, | |
| 748 | column.into_iter().map(|value| { | |
| 749 | if let Some(value) = value { | |
| 750 | serde_json::to_vec(&value).unwrap() | |
| 751 | } else { | |
| 752 | Vec::new() | |
| 753 | } | |
| 754 | }), | |
| 755 | ) | |
| 756 | } | |
| 757 | fn perform_write_postings( | |
| 758 | doc_root: &Path, | |
| 759 | dirname: &str, | |
| 760 | column: Vec<Vec<Vec<u32>>>, | |
| 761 | ) -> Result<Vec<u8>, Error> { | |
| 762 | perform_write_strings( | |
| 763 | doc_root, | |
| 764 | dirname, | |
| 765 | column.into_iter().map(|postings| { | |
| 766 | let mut buf = Vec::new(); | |
| 767 | encode::write_postings_to_string(&postings, &mut buf); | |
| 768 | buf | |
| 769 | }), | |
| 770 | ) | |
| 771 | } | |
| 772 | std::fs::write( | |
| 773 | doc_root.join(format!("search.index/root{resource_suffix}.js")), | |
| 774 | serialized_root, | |
| 775 | ) | |
| 776 | .map_err(|error| Error { | |
| 777 | file: doc_root.join(format!("search.index/root{resource_suffix}.js")), | |
| 778 | error: format!("failed to write root to disk: {error}"), | |
| 779 | })?; | |
| 780 | Ok(()) | |
| 781 | } | |
| 782 | } | |
| 783 | ||
| 784 | #[derive(Clone, Debug)] | |
| 785 | struct EntryData { | |
| 786 | krate: usize, | |
| 787 | ty: ItemType, | |
| 788 | module_path: Option<usize>, | |
| 789 | exact_module_path: Option<usize>, | |
| 790 | parent: Option<usize>, | |
| 791 | deprecated: bool, | |
| 792 | associated_item_disambiguator: Option<String>, | |
| 793 | } | |
| 794 | ||
| 795 | impl Serialize for EntryData { | |
| 796 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
| 797 | where | |
| 798 | S: Serializer, | |
| 799 | { | |
| 800 | let mut seq = serializer.serialize_seq(None)?; | |
| 801 | seq.serialize_element(&self.krate)?; | |
| 802 | seq.serialize_element(&self.ty)?; | |
| 803 | seq.serialize_element(&self.module_path.map(|id| id + 1).unwrap_or(0))?; | |
| 804 | seq.serialize_element(&self.exact_module_path.map(|id| id + 1).unwrap_or(0))?; | |
| 805 | seq.serialize_element(&self.parent.map(|id| id + 1).unwrap_or(0))?; | |
| 806 | seq.serialize_element(&if self.deprecated { 1 } else { 0 })?; | |
| 807 | if let Some(disambig) = &self.associated_item_disambiguator { | |
| 808 | seq.serialize_element(&disambig)?; | |
| 809 | } | |
| 810 | seq.end() | |
| 811 | } | |
| 812 | } | |
| 813 | ||
| 814 | impl<'de> Deserialize<'de> for EntryData { | |
| 815 | fn deserialize<D>(deserializer: D) -> Result<EntryData, D::Error> | |
| 816 | where | |
| 817 | D: Deserializer<'de>, | |
| 818 | { | |
| 819 | struct EntryDataVisitor; | |
| 820 | impl<'de> de::Visitor<'de> for EntryDataVisitor { | |
| 821 | type Value = EntryData; | |
| 822 | fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 823 | write!(formatter, "path data") | |
| 824 | } | |
| 825 | fn visit_seq<A: de::SeqAccess<'de>>(self, mut v: A) -> Result<EntryData, A::Error> { | |
| 826 | let krate: usize = | |
| 827 | v.next_element()?.ok_or_else(|| A::Error::missing_field("krate"))?; | |
| 828 | let ty: ItemType = | |
| 829 | v.next_element()?.ok_or_else(|| A::Error::missing_field("ty"))?; | |
| 830 | let module_path: SerializedOptional32 = | |
| 831 | v.next_element()?.ok_or_else(|| A::Error::missing_field("module_path"))?; | |
| 832 | let exact_module_path: SerializedOptional32 = v | |
| 833 | .next_element()? | |
| 834 | .ok_or_else(|| A::Error::missing_field("exact_module_path"))?; | |
| 835 | let parent: SerializedOptional32 = | |
| 836 | v.next_element()?.ok_or_else(|| A::Error::missing_field("parent"))?; | |
| 837 | let deprecated: u32 = v.next_element()?.unwrap_or(0); | |
| 838 | let associated_item_disambiguator: Option<String> = v.next_element()?; | |
| 839 | Ok(EntryData { | |
| 840 | krate, | |
| 841 | ty, | |
| 842 | module_path: Option::<i32>::from(module_path).map(|path| path as usize), | |
| 843 | exact_module_path: Option::<i32>::from(exact_module_path) | |
| 844 | .map(|path| path as usize), | |
| 845 | parent: Option::<i32>::from(parent).map(|path| path as usize), | |
| 846 | deprecated: deprecated != 0, | |
| 847 | associated_item_disambiguator, | |
| 848 | }) | |
| 849 | } | |
| 850 | } | |
| 851 | deserializer.deserialize_any(EntryDataVisitor) | |
| 852 | } | |
| 853 | } | |
| 854 | ||
| 855 | #[derive(Clone, Debug)] | |
| 856 | struct PathData { | |
| 857 | ty: ItemType, | |
| 858 | module_path: Vec<Symbol>, | |
| 859 | exact_module_path: Option<Vec<Symbol>>, | |
| 53 | 860 | } |
| 54 | 861 | |
| 55 | const DESC_INDEX_SHARD_LEN: usize = 128 * 1024; | |
| 862 | impl Serialize for PathData { | |
| 863 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
| 864 | where | |
| 865 | S: Serializer, | |
| 866 | { | |
| 867 | let mut seq = serializer.serialize_seq(None)?; | |
| 868 | seq.serialize_element(&self.ty)?; | |
| 869 | seq.serialize_element(&if self.module_path.is_empty() { | |
| 870 | String::new() | |
| 871 | } else { | |
| 872 | join_path_syms(&self.module_path) | |
| 873 | })?; | |
| 874 | if let Some(ref path) = self.exact_module_path { | |
| 875 | seq.serialize_element(&if path.is_empty() { | |
| 876 | String::new() | |
| 877 | } else { | |
| 878 | join_path_syms(path) | |
| 879 | })?; | |
| 880 | } | |
| 881 | seq.end() | |
| 882 | } | |
| 883 | } | |
| 884 | ||
| 885 | impl<'de> Deserialize<'de> for PathData { | |
| 886 | fn deserialize<D>(deserializer: D) -> Result<PathData, D::Error> | |
| 887 | where | |
| 888 | D: Deserializer<'de>, | |
| 889 | { | |
| 890 | struct PathDataVisitor; | |
| 891 | impl<'de> de::Visitor<'de> for PathDataVisitor { | |
| 892 | type Value = PathData; | |
| 893 | fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 894 | write!(formatter, "path data") | |
| 895 | } | |
| 896 | fn visit_seq<A: de::SeqAccess<'de>>(self, mut v: A) -> Result<PathData, A::Error> { | |
| 897 | let ty: ItemType = | |
| 898 | v.next_element()?.ok_or_else(|| A::Error::missing_field("ty"))?; | |
| 899 | let module_path: String = | |
| 900 | v.next_element()?.ok_or_else(|| A::Error::missing_field("module_path"))?; | |
| 901 | let exact_module_path: Option<String> = | |
| 902 | v.next_element()?.and_then(SerializedOptionalString::into); | |
| 903 | Ok(PathData { | |
| 904 | ty, | |
| 905 | module_path: if module_path.is_empty() { | |
| 906 | vec![] | |
| 907 | } else { | |
| 908 | module_path.split("::").map(Symbol::intern).collect() | |
| 909 | }, | |
| 910 | exact_module_path: exact_module_path.map(|path| { | |
| 911 | if path.is_empty() { | |
| 912 | vec![] | |
| 913 | } else { | |
| 914 | path.split("::").map(Symbol::intern).collect() | |
| 915 | } | |
| 916 | }), | |
| 917 | }) | |
| 918 | } | |
| 919 | } | |
| 920 | deserializer.deserialize_any(PathDataVisitor) | |
| 921 | } | |
| 922 | } | |
| 923 | ||
| 924 | #[derive(Clone, Debug)] | |
| 925 | struct TypeData { | |
| 926 | /// If set to "true", the generics can be matched without having to | |
| 927 | /// mention the type itself. The truth table, assuming `Unboxable` | |
| 928 | /// has `search_unbox = true` and `Inner` has `search_unbox = false` | |
| 929 | /// | |
| 930 | /// | **query** | `Unboxable<Inner>` | `Inner` | `Inner<Unboxable>` | | |
| 931 | /// |--------------------|--------------------|---------|--------------------| | |
| 932 | /// | `Inner` | yes | yes | yes | | |
| 933 | /// | `Unboxable` | yes | no | no | | |
| 934 | /// | `Unboxable<Inner>` | yes | no | no | | |
| 935 | /// | `Inner<Unboxable>` | no | no | yes | | |
| 936 | search_unbox: bool, | |
| 937 | /// List of functions that mention this type in their type signature. | |
| 938 | /// | |
| 939 | /// - The outermost list has one entry per alpha-normalized generic. | |
| 940 | /// | |
| 941 | /// - The second layer is sorted by number of types that appear in the | |
| 942 | /// type signature. The search engine iterates over these in order from | |
| 943 | /// smallest to largest. Functions with less stuff in their type | |
| 944 | /// signature are more likely to be what the user wants, because we never | |
| 945 | /// show functions that are *missing* parts of the query, so removing.. | |
| 946 | /// | |
| 947 | /// - The final layer is the list of functions. | |
| 948 | inverted_function_signature_index: Vec<Vec<u32>>, | |
| 949 | } | |
| 950 | ||
| 951 | impl Serialize for TypeData { | |
| 952 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
| 953 | where | |
| 954 | S: Serializer, | |
| 955 | { | |
| 956 | if self.search_unbox || !self.inverted_function_signature_index.is_empty() { | |
| 957 | let mut seq = serializer.serialize_seq(None)?; | |
| 958 | if !self.inverted_function_signature_index.is_empty() { | |
| 959 | let mut buf = Vec::new(); | |
| 960 | encode::write_postings_to_string(&self.inverted_function_signature_index, &mut buf); | |
| 961 | let mut serialized_result = Vec::new(); | |
| 962 | stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result); | |
| 963 | seq.serialize_element(&String::from_utf8(serialized_result).unwrap())?; | |
| 964 | } | |
| 965 | if self.search_unbox { | |
| 966 | seq.serialize_element(&1)?; | |
| 967 | } | |
| 968 | seq.end() | |
| 969 | } else { | |
| 970 | None::<()>.serialize(serializer) | |
| 971 | } | |
| 972 | } | |
| 973 | } | |
| 974 | ||
| 975 | impl<'de> Deserialize<'de> for TypeData { | |
| 976 | fn deserialize<D>(deserializer: D) -> Result<TypeData, D::Error> | |
| 977 | where | |
| 978 | D: Deserializer<'de>, | |
| 979 | { | |
| 980 | struct TypeDataVisitor; | |
| 981 | impl<'de> de::Visitor<'de> for TypeDataVisitor { | |
| 982 | type Value = TypeData; | |
| 983 | fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 984 | write!(formatter, "type data") | |
| 985 | } | |
| 986 | fn visit_none<E>(self) -> Result<TypeData, E> { | |
| 987 | Ok(TypeData { inverted_function_signature_index: vec![], search_unbox: false }) | |
| 988 | } | |
| 989 | fn visit_seq<A: de::SeqAccess<'de>>(self, mut v: A) -> Result<TypeData, A::Error> { | |
| 990 | let inverted_function_signature_index: String = | |
| 991 | v.next_element()?.unwrap_or(String::new()); | |
| 992 | let search_unbox: u32 = v.next_element()?.unwrap_or(0); | |
| 993 | let mut idx: Vec<u8> = Vec::new(); | |
| 994 | stringdex_internals::decode::read_base64_from_bytes( | |
| 995 | inverted_function_signature_index.as_bytes(), | |
| 996 | &mut idx, | |
| 997 | ) | |
| 998 | .unwrap(); | |
| 999 | let mut inverted_function_signature_index = Vec::new(); | |
| 1000 | encode::read_postings_from_string(&mut inverted_function_signature_index, &idx); | |
| 1001 | Ok(TypeData { inverted_function_signature_index, search_unbox: search_unbox == 1 }) | |
| 1002 | } | |
| 1003 | } | |
| 1004 | deserializer.deserialize_any(TypeDataVisitor) | |
| 1005 | } | |
| 1006 | } | |
| 1007 | ||
| 1008 | enum SerializedOptionalString { | |
| 1009 | None, | |
| 1010 | Some(String), | |
| 1011 | } | |
| 1012 | ||
| 1013 | impl From<SerializedOptionalString> for Option<String> { | |
| 1014 | fn from(me: SerializedOptionalString) -> Option<String> { | |
| 1015 | match me { | |
| 1016 | SerializedOptionalString::Some(string) => Some(string), | |
| 1017 | SerializedOptionalString::None => None, | |
| 1018 | } | |
| 1019 | } | |
| 1020 | } | |
| 1021 | ||
| 1022 | impl Serialize for SerializedOptionalString { | |
| 1023 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
| 1024 | where | |
| 1025 | S: Serializer, | |
| 1026 | { | |
| 1027 | match self { | |
| 1028 | SerializedOptionalString::Some(string) => string.serialize(serializer), | |
| 1029 | SerializedOptionalString::None => 0.serialize(serializer), | |
| 1030 | } | |
| 1031 | } | |
| 1032 | } | |
| 1033 | impl<'de> Deserialize<'de> for SerializedOptionalString { | |
| 1034 | fn deserialize<D>(deserializer: D) -> Result<SerializedOptionalString, D::Error> | |
| 1035 | where | |
| 1036 | D: Deserializer<'de>, | |
| 1037 | { | |
| 1038 | struct SerializedOptionalStringVisitor; | |
| 1039 | impl<'de> de::Visitor<'de> for SerializedOptionalStringVisitor { | |
| 1040 | type Value = SerializedOptionalString; | |
| 1041 | fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 1042 | write!(formatter, "0 or string") | |
| 1043 | } | |
| 1044 | fn visit_u64<E: de::Error>(self, v: u64) -> Result<SerializedOptionalString, E> { | |
| 1045 | if v != 0 { | |
| 1046 | return Err(E::missing_field("not 0")); | |
| 1047 | } | |
| 1048 | Ok(SerializedOptionalString::None) | |
| 1049 | } | |
| 1050 | fn visit_string<E: de::Error>(self, v: String) -> Result<SerializedOptionalString, E> { | |
| 1051 | Ok(SerializedOptionalString::Some(v)) | |
| 1052 | } | |
| 1053 | fn visit_str<E: de::Error>(self, v: &str) -> Result<SerializedOptionalString, E> { | |
| 1054 | Ok(SerializedOptionalString::Some(v.to_string())) | |
| 1055 | } | |
| 1056 | } | |
| 1057 | deserializer.deserialize_any(SerializedOptionalStringVisitor) | |
| 1058 | } | |
| 1059 | } | |
| 1060 | ||
| 1061 | enum SerializedOptional32 { | |
| 1062 | None, | |
| 1063 | Some(i32), | |
| 1064 | } | |
| 1065 | ||
| 1066 | impl From<SerializedOptional32> for Option<i32> { | |
| 1067 | fn from(me: SerializedOptional32) -> Option<i32> { | |
| 1068 | match me { | |
| 1069 | SerializedOptional32::Some(number) => Some(number), | |
| 1070 | SerializedOptional32::None => None, | |
| 1071 | } | |
| 1072 | } | |
| 1073 | } | |
| 1074 | ||
| 1075 | impl Serialize for SerializedOptional32 { | |
| 1076 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
| 1077 | where | |
| 1078 | S: Serializer, | |
| 1079 | { | |
| 1080 | match self { | |
| 1081 | &SerializedOptional32::Some(number) if number < 0 => number.serialize(serializer), | |
| 1082 | &SerializedOptional32::Some(number) => (number + 1).serialize(serializer), | |
| 1083 | &SerializedOptional32::None => 0.serialize(serializer), | |
| 1084 | } | |
| 1085 | } | |
| 1086 | } | |
| 1087 | impl<'de> Deserialize<'de> for SerializedOptional32 { | |
| 1088 | fn deserialize<D>(deserializer: D) -> Result<SerializedOptional32, D::Error> | |
| 1089 | where | |
| 1090 | D: Deserializer<'de>, | |
| 1091 | { | |
| 1092 | struct SerializedOptional32Visitor; | |
| 1093 | impl<'de> de::Visitor<'de> for SerializedOptional32Visitor { | |
| 1094 | type Value = SerializedOptional32; | |
| 1095 | fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 1096 | write!(formatter, "integer") | |
| 1097 | } | |
| 1098 | fn visit_i64<E: de::Error>(self, v: i64) -> Result<SerializedOptional32, E> { | |
| 1099 | Ok(match v { | |
| 1100 | 0 => SerializedOptional32::None, | |
| 1101 | v if v < 0 => SerializedOptional32::Some(v as i32), | |
| 1102 | v => SerializedOptional32::Some(v as i32 - 1), | |
| 1103 | }) | |
| 1104 | } | |
| 1105 | fn visit_u64<E: de::Error>(self, v: u64) -> Result<SerializedOptional32, E> { | |
| 1106 | Ok(match v { | |
| 1107 | 0 => SerializedOptional32::None, | |
| 1108 | v => SerializedOptional32::Some(v as i32 - 1), | |
| 1109 | }) | |
| 1110 | } | |
| 1111 | } | |
| 1112 | deserializer.deserialize_any(SerializedOptional32Visitor) | |
| 1113 | } | |
| 1114 | } | |
| 1115 | ||
| 1116 | #[derive(Clone, Debug)] | |
| 1117 | pub struct FunctionData { | |
| 1118 | function_signature: String, | |
| 1119 | param_names: Vec<String>, | |
| 1120 | } | |
| 1121 | ||
| 1122 | impl Serialize for FunctionData { | |
| 1123 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
| 1124 | where | |
| 1125 | S: Serializer, | |
| 1126 | { | |
| 1127 | let mut seq = serializer.serialize_seq(None)?; | |
| 1128 | seq.serialize_element(&self.function_signature)?; | |
| 1129 | seq.serialize_element(&self.param_names)?; | |
| 1130 | seq.end() | |
| 1131 | } | |
| 1132 | } | |
| 1133 | ||
| 1134 | impl<'de> Deserialize<'de> for FunctionData { | |
| 1135 | fn deserialize<D>(deserializer: D) -> Result<FunctionData, D::Error> | |
| 1136 | where | |
| 1137 | D: Deserializer<'de>, | |
| 1138 | { | |
| 1139 | struct FunctionDataVisitor; | |
| 1140 | impl<'de> de::Visitor<'de> for FunctionDataVisitor { | |
| 1141 | type Value = FunctionData; | |
| 1142 | fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 1143 | write!(formatter, "fn data") | |
| 1144 | } | |
| 1145 | fn visit_seq<A: de::SeqAccess<'de>>(self, mut v: A) -> Result<FunctionData, A::Error> { | |
| 1146 | let function_signature: String = v | |
| 1147 | .next_element()? | |
| 1148 | .ok_or_else(|| A::Error::missing_field("function_signature"))?; | |
| 1149 | let param_names: Vec<String> = | |
| 1150 | v.next_element()?.ok_or_else(|| A::Error::missing_field("param_names"))?; | |
| 1151 | Ok(FunctionData { function_signature, param_names }) | |
| 1152 | } | |
| 1153 | } | |
| 1154 | deserializer.deserialize_any(FunctionDataVisitor) | |
| 1155 | } | |
| 1156 | } | |
| 56 | 1157 | |
| 57 | 1158 | /// Builds the search index from the collected metadata |
| 58 | 1159 | pub(crate) fn build_index( |
| 59 | 1160 | krate: &clean::Crate, |
| 60 | 1161 | cache: &mut Cache, |
| 61 | 1162 | tcx: TyCtxt<'_>, |
| 62 | ) -> SerializedSearchIndex { | |
| 63 | // Maps from ID to position in the `crate_paths` array. | |
| 64 | let mut itemid_to_pathid = FxHashMap::default(); | |
| 65 | let mut primitives = FxHashMap::default(); | |
| 66 | let mut associated_types = FxHashMap::default(); | |
| 67 | ||
| 68 | // item type, display path, re-exported internal path | |
| 69 | let mut crate_paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)> = vec![]; | |
| 1163 | doc_root: &Path, | |
| 1164 | resource_suffix: &str, | |
| 1165 | ) -> Result<SerializedSearchIndex, Error> { | |
| 1166 | let mut search_index = std::mem::take(&mut cache.search_index); | |
| 70 | 1167 | |
| 71 | 1168 | // Attach all orphan items to the type's definition if the type |
| 72 | 1169 | // has since been learned. |
| ... | ... | @@ -74,15 +1171,15 @@ pub(crate) fn build_index( |
| 74 | 1171 | { |
| 75 | 1172 | if let Some((fqp, _)) = cache.paths.get(&parent) { |
| 76 | 1173 | let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); |
| 77 | cache.search_index.push(IndexItem { | |
| 1174 | search_index.push(IndexItem { | |
| 78 | 1175 | ty: item.type_(), |
| 79 | 1176 | defid: item.item_id.as_def_id(), |
| 80 | 1177 | name: item.name.unwrap(), |
| 81 | path: join_path_syms(&fqp[..fqp.len() - 1]), | |
| 1178 | module_path: fqp[..fqp.len() - 1].to_vec(), | |
| 82 | 1179 | desc, |
| 83 | 1180 | parent: Some(parent), |
| 84 | 1181 | parent_idx: None, |
| 85 | exact_path: None, | |
| 1182 | exact_module_path: None, | |
| 86 | 1183 | impl_id, |
| 87 | 1184 | search_type: get_function_type_for_search( |
| 88 | 1185 | item, |
| ... | ... | @@ -97,85 +1194,299 @@ pub(crate) fn build_index( |
| 97 | 1194 | } |
| 98 | 1195 | } |
| 99 | 1196 | |
| 1197 | // Sort search index items. This improves the compressibility of the search index. | |
| 1198 | search_index.sort_unstable_by(|k1, k2| { | |
| 1199 | // `sort_unstable_by_key` produces lifetime errors | |
| 1200 | // HACK(rustdoc): should not be sorting `CrateNum` or `DefIndex`, this will soon go away, too | |
| 1201 | let k1 = | |
| 1202 | (&k1.module_path, k1.name.as_str(), &k1.ty, k1.parent.map(|id| (id.index, id.krate))); | |
| 1203 | let k2 = | |
| 1204 | (&k2.module_path, k2.name.as_str(), &k2.ty, k2.parent.map(|id| (id.index, id.krate))); | |
| 1205 | Ord::cmp(&k1, &k2) | |
| 1206 | }); | |
| 1207 | ||
| 1208 | // Now, convert to an on-disk search index format | |
| 1209 | // | |
| 1210 | // if there's already a search index, load it into memory and add the new entries to it | |
| 1211 | // otherwise, do nothing | |
| 1212 | let mut serialized_index = SerializedSearchIndex::load(doc_root, resource_suffix)?; | |
| 1213 | ||
| 1214 | // The crate always goes first in this list | |
| 1215 | let crate_name = krate.name(tcx); | |
| 100 | 1216 | let crate_doc = |
| 101 | 1217 | short_markdown_summary(&krate.module.doc_value(), &krate.module.link_names(cache)); |
| 1218 | let crate_idx = { | |
| 1219 | let crate_path = (ItemType::ExternCrate, vec![crate_name]); | |
| 1220 | match serialized_index.crate_paths_index.entry(crate_path) { | |
| 1221 | Entry::Occupied(index) => { | |
| 1222 | let index = *index.get(); | |
| 1223 | serialized_index.descs[index] = crate_doc; | |
| 1224 | for type_data in serialized_index.type_data.iter_mut() { | |
| 1225 | if let Some(TypeData { inverted_function_signature_index, .. }) = type_data { | |
| 1226 | for list in &mut inverted_function_signature_index[..] { | |
| 1227 | list.retain(|fnid| { | |
| 1228 | serialized_index.entry_data[usize::try_from(*fnid).unwrap()] | |
| 1229 | .as_ref() | |
| 1230 | .unwrap() | |
| 1231 | .krate | |
| 1232 | != index | |
| 1233 | }); | |
| 1234 | } | |
| 1235 | } | |
| 1236 | } | |
| 1237 | for i in (index + 1)..serialized_index.entry_data.len() { | |
| 1238 | // if this crate has been built before, replace its stuff with new | |
| 1239 | if let Some(EntryData { krate, .. }) = serialized_index.entry_data[i] | |
| 1240 | && krate == index | |
| 1241 | { | |
| 1242 | serialized_index.entry_data[i] = None; | |
| 1243 | serialized_index.descs[i] = String::new(); | |
| 1244 | serialized_index.function_data[i] = None; | |
| 1245 | if serialized_index.path_data[i].is_none() { | |
| 1246 | serialized_index.names[i] = String::new(); | |
| 1247 | } | |
| 1248 | } | |
| 1249 | if let Some(alias_pointer) = serialized_index.alias_pointers[i] | |
| 1250 | && serialized_index.entry_data[alias_pointer].is_none() | |
| 1251 | { | |
| 1252 | serialized_index.alias_pointers[i] = None; | |
| 1253 | if serialized_index.path_data[i].is_none() | |
| 1254 | && serialized_index.entry_data[i].is_none() | |
| 1255 | { | |
| 1256 | serialized_index.names[i] = String::new(); | |
| 1257 | } | |
| 1258 | } | |
| 1259 | } | |
| 1260 | index | |
| 1261 | } | |
| 1262 | Entry::Vacant(slot) => { | |
| 1263 | let krate = serialized_index.names.len(); | |
| 1264 | slot.insert(krate); | |
| 1265 | serialized_index.push( | |
| 1266 | crate_name.as_str().to_string(), | |
| 1267 | Some(PathData { | |
| 1268 | ty: ItemType::ExternCrate, | |
| 1269 | module_path: vec![], | |
| 1270 | exact_module_path: None, | |
| 1271 | }), | |
| 1272 | Some(EntryData { | |
| 1273 | krate, | |
| 1274 | ty: ItemType::ExternCrate, | |
| 1275 | module_path: None, | |
| 1276 | exact_module_path: None, | |
| 1277 | parent: None, | |
| 1278 | deprecated: false, | |
| 1279 | associated_item_disambiguator: None, | |
| 1280 | }), | |
| 1281 | crate_doc, | |
| 1282 | None, | |
| 1283 | None, | |
| 1284 | None, | |
| 1285 | ); | |
| 1286 | krate | |
| 1287 | } | |
| 1288 | } | |
| 1289 | }; | |
| 1290 | ||
| 1291 | // First, populate associated item parents | |
| 1292 | let crate_items: Vec<&mut IndexItem> = search_index | |
| 1293 | .iter_mut() | |
| 1294 | .map(|item| { | |
| 1295 | item.parent_idx = item.parent.and_then(|defid| { | |
| 1296 | cache.paths.get(&defid).map(|&(ref fqp, ty)| { | |
| 1297 | let pathid = serialized_index.names.len(); | |
| 1298 | match serialized_index.crate_paths_index.entry((ty, fqp.clone())) { | |
| 1299 | Entry::Occupied(entry) => *entry.get(), | |
| 1300 | Entry::Vacant(entry) => { | |
| 1301 | entry.insert(pathid); | |
| 1302 | let (name, path) = fqp.split_last().unwrap(); | |
| 1303 | serialized_index.push_path( | |
| 1304 | name.as_str().to_string(), | |
| 1305 | PathData { | |
| 1306 | ty, | |
| 1307 | module_path: path.to_vec(), | |
| 1308 | exact_module_path: if let Some(exact_path) = | |
| 1309 | cache.exact_paths.get(&defid) | |
| 1310 | && let Some((name2, exact_path)) = exact_path.split_last() | |
| 1311 | && name == name2 | |
| 1312 | { | |
| 1313 | Some(exact_path.to_vec()) | |
| 1314 | } else { | |
| 1315 | None | |
| 1316 | }, | |
| 1317 | }, | |
| 1318 | ); | |
| 1319 | usize::try_from(pathid).unwrap() | |
| 1320 | } | |
| 1321 | } | |
| 1322 | }) | |
| 1323 | }); | |
| 1324 | ||
| 1325 | if let Some(defid) = item.defid | |
| 1326 | && item.parent_idx.is_none() | |
| 1327 | { | |
| 1328 | // If this is a re-export, retain the original path. | |
| 1329 | // Associated items don't use this. | |
| 1330 | // Their parent carries the exact fqp instead. | |
| 1331 | let exact_fqp = cache | |
| 1332 | .exact_paths | |
| 1333 | .get(&defid) | |
| 1334 | .or_else(|| cache.external_paths.get(&defid).map(|(fqp, _)| fqp)); | |
| 1335 | item.exact_module_path = exact_fqp.and_then(|fqp| { | |
| 1336 | // Re-exports only count if the name is exactly the same. | |
| 1337 | // This is a size optimization, since it means we only need | |
| 1338 | // to store the name once (and the path is re-used for everything | |
| 1339 | // exported from this same module). It's also likely to Do | |
| 1340 | // What I Mean, since if a re-export changes the name, it might | |
| 1341 | // also be a change in semantic meaning. | |
| 1342 | if fqp.last() != Some(&item.name) { | |
| 1343 | return None; | |
| 1344 | } | |
| 1345 | let path = | |
| 1346 | if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export) { | |
| 1347 | // `#[macro_export]` always exports to the crate root. | |
| 1348 | vec![tcx.crate_name(defid.krate)] | |
| 1349 | } else { | |
| 1350 | if fqp.len() < 2 { | |
| 1351 | return None; | |
| 1352 | } | |
| 1353 | fqp[..fqp.len() - 1].to_vec() | |
| 1354 | }; | |
| 1355 | if path == item.module_path { | |
| 1356 | return None; | |
| 1357 | } | |
| 1358 | Some(path) | |
| 1359 | }); | |
| 1360 | } else if let Some(parent_idx) = item.parent_idx { | |
| 1361 | let i = usize::try_from(parent_idx).unwrap(); | |
| 1362 | item.module_path = | |
| 1363 | serialized_index.path_data[i].as_ref().unwrap().module_path.clone(); | |
| 1364 | item.exact_module_path = | |
| 1365 | serialized_index.path_data[i].as_ref().unwrap().exact_module_path.clone(); | |
| 1366 | } | |
| 102 | 1367 | |
| 103 | #[derive(Eq, Ord, PartialEq, PartialOrd)] | |
| 104 | struct SerSymbolAsStr(Symbol); | |
| 1368 | &mut *item | |
| 1369 | }) | |
| 1370 | .collect(); | |
| 105 | 1371 | |
| 106 | impl Serialize for SerSymbolAsStr { | |
| 107 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
| 108 | where | |
| 109 | S: Serializer, | |
| 1372 | // Now, find anywhere that the same name is used for two different items | |
| 1373 | // these need a disambiguator hash for lints | |
| 1374 | let mut associated_item_duplicates = FxHashMap::<(usize, ItemType, Symbol), usize>::default(); | |
| 1375 | for item in crate_items.iter().map(|x| &*x) { | |
| 1376 | if item.impl_id.is_some() | |
| 1377 | && let Some(parent_idx) = item.parent_idx | |
| 110 | 1378 | { |
| 111 | self.0.as_str().serialize(serializer) | |
| 1379 | let count = | |
| 1380 | associated_item_duplicates.entry((parent_idx, item.ty, item.name)).or_insert(0); | |
| 1381 | *count += 1; | |
| 112 | 1382 | } |
| 113 | 1383 | } |
| 114 | 1384 | |
| 115 | type AliasMap = BTreeMap<SerSymbolAsStr, Vec<usize>>; | |
| 116 | // Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, | |
| 117 | // we need the alias element to have an array of items. | |
| 118 | let mut aliases: AliasMap = BTreeMap::new(); | |
| 1385 | // now populate the actual entries, type data, and function data | |
| 1386 | for item in crate_items { | |
| 1387 | assert_eq!( | |
| 1388 | item.parent.is_some(), | |
| 1389 | item.parent_idx.is_some(), | |
| 1390 | "`{}` is missing idx", | |
| 1391 | item.name | |
| 1392 | ); | |
| 119 | 1393 | |
| 120 | // Sort search index items. This improves the compressibility of the search index. | |
| 121 | cache.search_index.sort_unstable_by(|k1, k2| { | |
| 122 | // `sort_unstable_by_key` produces lifetime errors | |
| 123 | // HACK(rustdoc): should not be sorting `CrateNum` or `DefIndex`, this will soon go away, too | |
| 124 | let k1 = (&k1.path, k1.name.as_str(), &k1.ty, k1.parent.map(|id| (id.index, id.krate))); | |
| 125 | let k2 = (&k2.path, k2.name.as_str(), &k2.ty, k2.parent.map(|id| (id.index, id.krate))); | |
| 126 | Ord::cmp(&k1, &k2) | |
| 127 | }); | |
| 1394 | let module_path = Some(serialized_index.get_id_by_module_path(&item.module_path)); | |
| 1395 | let exact_module_path = item | |
| 1396 | .exact_module_path | |
| 1397 | .as_ref() | |
| 1398 | .map(|path| serialized_index.get_id_by_module_path(path)); | |
| 1399 | ||
| 1400 | let new_entry_id = serialized_index.push( | |
| 1401 | item.name.as_str().to_string(), | |
| 1402 | None, | |
| 1403 | Some(EntryData { | |
| 1404 | ty: item.ty, | |
| 1405 | parent: item.parent_idx, | |
| 1406 | module_path, | |
| 1407 | exact_module_path, | |
| 1408 | deprecated: item.deprecation.is_some(), | |
| 1409 | associated_item_disambiguator: if let Some(impl_id) = item.impl_id | |
| 1410 | && let Some(parent_idx) = item.parent_idx | |
| 1411 | && associated_item_duplicates | |
| 1412 | .get(&(parent_idx, item.ty, item.name)) | |
| 1413 | .copied() | |
| 1414 | .unwrap_or(0) | |
| 1415 | > 1 | |
| 1416 | { | |
| 1417 | Some(render::get_id_for_impl(tcx, ItemId::DefId(impl_id))) | |
| 1418 | } else { | |
| 1419 | None | |
| 1420 | }, | |
| 1421 | krate: crate_idx, | |
| 1422 | }), | |
| 1423 | item.desc.to_string(), | |
| 1424 | None, // filled in after all the types have been indexed | |
| 1425 | None, | |
| 1426 | None, | |
| 1427 | ); | |
| 128 | 1428 | |
| 129 | // Set up alias indexes. | |
| 130 | for (i, item) in cache.search_index.iter().enumerate() { | |
| 1429 | // Aliases | |
| 1430 | // ------- | |
| 131 | 1431 | for alias in &item.aliases[..] { |
| 132 | aliases.entry(SerSymbolAsStr(*alias)).or_default().push(i); | |
| 1432 | serialized_index.push_alias(alias.as_str().to_string(), new_entry_id); | |
| 133 | 1433 | } |
| 134 | } | |
| 135 | ||
| 136 | // Reduce `DefId` in paths into smaller sequential numbers, | |
| 137 | // and prune the paths that do not appear in the index. | |
| 138 | let mut lastpath = ""; | |
| 139 | let mut lastpathid = 0isize; | |
| 140 | 1434 | |
| 141 | // First, on function signatures | |
| 142 | let mut search_index = std::mem::take(&mut cache.search_index); | |
| 143 | for item in search_index.iter_mut() { | |
| 144 | fn insert_into_map<F: std::hash::Hash + Eq>( | |
| 145 | map: &mut FxHashMap<F, isize>, | |
| 146 | itemid: F, | |
| 147 | lastpathid: &mut isize, | |
| 148 | crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)>, | |
| 149 | item_type: ItemType, | |
| 1435 | // Function signature reverse index | |
| 1436 | // -------------------------------- | |
| 1437 | fn insert_into_map( | |
| 1438 | ty: ItemType, | |
| 150 | 1439 | path: &[Symbol], |
| 151 | 1440 | exact_path: Option<&[Symbol]>, |
| 152 | 1441 | search_unbox: bool, |
| 1442 | serialized_index: &mut SerializedSearchIndex, | |
| 1443 | used_in_function_signature: &mut BTreeSet<isize>, | |
| 153 | 1444 | ) -> RenderTypeId { |
| 154 | match map.entry(itemid) { | |
| 155 | Entry::Occupied(entry) => RenderTypeId::Index(*entry.get()), | |
| 1445 | let pathid = serialized_index.names.len(); | |
| 1446 | let pathid = match serialized_index.crate_paths_index.entry((ty, path.to_vec())) { | |
| 1447 | Entry::Occupied(entry) => { | |
| 1448 | let id = *entry.get(); | |
| 1449 | if serialized_index.type_data[id].as_mut().is_none() { | |
| 1450 | serialized_index.type_data[id] = Some(TypeData { | |
| 1451 | search_unbox, | |
| 1452 | inverted_function_signature_index: Vec::new(), | |
| 1453 | }); | |
| 1454 | } else if search_unbox { | |
| 1455 | serialized_index.type_data[id].as_mut().unwrap().search_unbox = true; | |
| 1456 | } | |
| 1457 | id | |
| 1458 | } | |
| 156 | 1459 | Entry::Vacant(entry) => { |
| 157 | let pathid = *lastpathid; | |
| 158 | 1460 | entry.insert(pathid); |
| 159 | *lastpathid += 1; | |
| 160 | crate_paths.push(( | |
| 161 | item_type, | |
| 162 | path.to_vec(), | |
| 163 | exact_path.map(|path| path.to_vec()), | |
| 164 | search_unbox, | |
| 165 | )); | |
| 166 | RenderTypeId::Index(pathid) | |
| 1461 | let (name, path) = path.split_last().unwrap(); | |
| 1462 | serialized_index.push_type( | |
| 1463 | name.to_string(), | |
| 1464 | PathData { | |
| 1465 | ty, | |
| 1466 | module_path: path.to_vec(), | |
| 1467 | exact_module_path: if let Some(exact_path) = exact_path | |
| 1468 | && let Some((name2, exact_path)) = exact_path.split_last() | |
| 1469 | && name == name2 | |
| 1470 | { | |
| 1471 | Some(exact_path.to_vec()) | |
| 1472 | } else { | |
| 1473 | None | |
| 1474 | }, | |
| 1475 | }, | |
| 1476 | TypeData { search_unbox, inverted_function_signature_index: Vec::new() }, | |
| 1477 | ); | |
| 1478 | pathid | |
| 167 | 1479 | } |
| 168 | } | |
| 1480 | }; | |
| 1481 | used_in_function_signature.insert(isize::try_from(pathid).unwrap()); | |
| 1482 | RenderTypeId::Index(isize::try_from(pathid).unwrap()) | |
| 169 | 1483 | } |
| 170 | 1484 | |
| 171 | 1485 | fn convert_render_type_id( |
| 172 | 1486 | id: RenderTypeId, |
| 173 | 1487 | cache: &mut Cache, |
| 174 | itemid_to_pathid: &mut FxHashMap<ItemId, isize>, | |
| 175 | primitives: &mut FxHashMap<Symbol, isize>, | |
| 176 | associated_types: &mut FxHashMap<Symbol, isize>, | |
| 177 | lastpathid: &mut isize, | |
| 178 | crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)>, | |
| 1488 | serialized_index: &mut SerializedSearchIndex, | |
| 1489 | used_in_function_signature: &mut BTreeSet<isize>, | |
| 179 | 1490 | tcx: TyCtxt<'_>, |
| 180 | 1491 | ) -> Option<RenderTypeId> { |
| 181 | 1492 | use crate::clean::PrimitiveType; |
| ... | ... | @@ -192,39 +1503,55 @@ pub(crate) fn build_index( |
| 192 | 1503 | }; |
| 193 | 1504 | match id { |
| 194 | 1505 | RenderTypeId::Mut => Some(insert_into_map( |
| 195 | primitives, | |
| 196 | kw::Mut, | |
| 197 | lastpathid, | |
| 198 | crate_paths, | |
| 199 | 1506 | ItemType::Keyword, |
| 200 | 1507 | &[kw::Mut], |
| 201 | 1508 | None, |
| 202 | 1509 | search_unbox, |
| 1510 | serialized_index, | |
| 1511 | used_in_function_signature, | |
| 203 | 1512 | )), |
| 204 | 1513 | RenderTypeId::DefId(defid) => { |
| 205 | 1514 | if let Some(&(ref fqp, item_type)) = |
| 206 | 1515 | paths.get(&defid).or_else(|| external_paths.get(&defid)) |
| 207 | 1516 | { |
| 208 | let exact_fqp = exact_paths | |
| 209 | .get(&defid) | |
| 210 | .or_else(|| external_paths.get(&defid).map(|(fqp, _)| fqp)) | |
| 211 | // Re-exports only count if the name is exactly the same. | |
| 212 | // This is a size optimization, since it means we only need | |
| 213 | // to store the name once (and the path is re-used for everything | |
| 214 | // exported from this same module). It's also likely to Do | |
| 215 | // What I Mean, since if a re-export changes the name, it might | |
| 216 | // also be a change in semantic meaning. | |
| 217 | .filter(|this_fqp| this_fqp.last() == fqp.last()); | |
| 218 | Some(insert_into_map( | |
| 219 | itemid_to_pathid, | |
| 220 | ItemId::DefId(defid), | |
| 221 | lastpathid, | |
| 222 | crate_paths, | |
| 223 | item_type, | |
| 224 | fqp, | |
| 225 | exact_fqp.map(|x| &x[..]).filter(|exact_fqp| exact_fqp != fqp), | |
| 226 | search_unbox, | |
| 227 | )) | |
| 1517 | if tcx.lang_items().fn_mut_trait() == Some(defid) | |
| 1518 | || tcx.lang_items().fn_once_trait() == Some(defid) | |
| 1519 | || tcx.lang_items().fn_trait() == Some(defid) | |
| 1520 | { | |
| 1521 | let name = *fqp.last().unwrap(); | |
| 1522 | // Make absolutely sure we use this single, correct path, | |
| 1523 | // because search.js needs to match. If we don't do this, | |
| 1524 | // there are three different paths that these traits may | |
| 1525 | // appear to come from. | |
| 1526 | Some(insert_into_map( | |
| 1527 | item_type, | |
| 1528 | &[sym::core, sym::ops, name], | |
| 1529 | Some(&[sym::core, sym::ops, name]), | |
| 1530 | search_unbox, | |
| 1531 | serialized_index, | |
| 1532 | used_in_function_signature, | |
| 1533 | )) | |
| 1534 | } else { | |
| 1535 | let exact_fqp = exact_paths | |
| 1536 | .get(&defid) | |
| 1537 | .or_else(|| external_paths.get(&defid).map(|(fqp, _)| fqp)) | |
| 1538 | .map(|v| &v[..]) | |
| 1539 | // Re-exports only count if the name is exactly the same. | |
| 1540 | // This is a size optimization, since it means we only need | |
| 1541 | // to store the name once (and the path is re-used for everything | |
| 1542 | // exported from this same module). It's also likely to Do | |
| 1543 | // What I Mean, since if a re-export changes the name, it might | |
| 1544 | // also be a change in semantic meaning. | |
| 1545 | .filter(|this_fqp| this_fqp.last() == fqp.last()); | |
| 1546 | Some(insert_into_map( | |
| 1547 | item_type, | |
| 1548 | fqp, | |
| 1549 | exact_fqp, | |
| 1550 | search_unbox, | |
| 1551 | serialized_index, | |
| 1552 | used_in_function_signature, | |
| 1553 | )) | |
| 1554 | } | |
| 228 | 1555 | } else { |
| 229 | 1556 | None |
| 230 | 1557 | } |
| ... | ... | @@ -232,26 +1559,25 @@ pub(crate) fn build_index( |
| 232 | 1559 | RenderTypeId::Primitive(primitive) => { |
| 233 | 1560 | let sym = primitive.as_sym(); |
| 234 | 1561 | Some(insert_into_map( |
| 235 | primitives, | |
| 236 | sym, | |
| 237 | lastpathid, | |
| 238 | crate_paths, | |
| 239 | 1562 | ItemType::Primitive, |
| 240 | 1563 | &[sym], |
| 241 | 1564 | None, |
| 242 | 1565 | search_unbox, |
| 1566 | serialized_index, | |
| 1567 | used_in_function_signature, | |
| 243 | 1568 | )) |
| 244 | 1569 | } |
| 245 | RenderTypeId::Index(_) => Some(id), | |
| 1570 | RenderTypeId::Index(index) => { | |
| 1571 | used_in_function_signature.insert(index); | |
| 1572 | Some(id) | |
| 1573 | } | |
| 246 | 1574 | RenderTypeId::AssociatedType(sym) => Some(insert_into_map( |
| 247 | associated_types, | |
| 248 | sym, | |
| 249 | lastpathid, | |
| 250 | crate_paths, | |
| 251 | 1575 | ItemType::AssocType, |
| 252 | 1576 | &[sym], |
| 253 | 1577 | None, |
| 254 | 1578 | search_unbox, |
| 1579 | serialized_index, | |
| 1580 | used_in_function_signature, | |
| 255 | 1581 | )), |
| 256 | 1582 | } |
| 257 | 1583 | } |
| ... | ... | @@ -259,11 +1585,8 @@ pub(crate) fn build_index( |
| 259 | 1585 | fn convert_render_type( |
| 260 | 1586 | ty: &mut RenderType, |
| 261 | 1587 | cache: &mut Cache, |
| 262 | itemid_to_pathid: &mut FxHashMap<ItemId, isize>, | |
| 263 | primitives: &mut FxHashMap<Symbol, isize>, | |
| 264 | associated_types: &mut FxHashMap<Symbol, isize>, | |
| 265 | lastpathid: &mut isize, | |
| 266 | crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)>, | |
| 1588 | serialized_index: &mut SerializedSearchIndex, | |
| 1589 | used_in_function_signature: &mut BTreeSet<isize>, | |
| 267 | 1590 | tcx: TyCtxt<'_>, |
| 268 | 1591 | ) { |
| 269 | 1592 | if let Some(generics) = &mut ty.generics { |
| ... | ... | @@ -271,11 +1594,8 @@ pub(crate) fn build_index( |
| 271 | 1594 | convert_render_type( |
| 272 | 1595 | item, |
| 273 | 1596 | cache, |
| 274 | itemid_to_pathid, | |
| 275 | primitives, | |
| 276 | associated_types, | |
| 277 | lastpathid, | |
| 278 | crate_paths, | |
| 1597 | serialized_index, | |
| 1598 | used_in_function_signature, | |
| 279 | 1599 | tcx, |
| 280 | 1600 | ); |
| 281 | 1601 | } |
| ... | ... | @@ -285,11 +1605,8 @@ pub(crate) fn build_index( |
| 285 | 1605 | let converted_associated_type = convert_render_type_id( |
| 286 | 1606 | *associated_type, |
| 287 | 1607 | cache, |
| 288 | itemid_to_pathid, | |
| 289 | primitives, | |
| 290 | associated_types, | |
| 291 | lastpathid, | |
| 292 | crate_paths, | |
| 1608 | serialized_index, | |
| 1609 | used_in_function_signature, | |
| 293 | 1610 | tcx, |
| 294 | 1611 | ); |
| 295 | 1612 | let Some(converted_associated_type) = converted_associated_type else { |
| ... | ... | @@ -300,11 +1617,8 @@ pub(crate) fn build_index( |
| 300 | 1617 | convert_render_type( |
| 301 | 1618 | constraint, |
| 302 | 1619 | cache, |
| 303 | itemid_to_pathid, | |
| 304 | primitives, | |
| 305 | associated_types, | |
| 306 | lastpathid, | |
| 307 | crate_paths, | |
| 1620 | serialized_index, | |
| 1621 | used_in_function_signature, | |
| 308 | 1622 | tcx, |
| 309 | 1623 | ); |
| 310 | 1624 | } |
| ... | ... | @@ -318,24 +1632,74 @@ pub(crate) fn build_index( |
| 318 | 1632 | ty.id = convert_render_type_id( |
| 319 | 1633 | id, |
| 320 | 1634 | cache, |
| 321 | itemid_to_pathid, | |
| 322 | primitives, | |
| 323 | associated_types, | |
| 324 | lastpathid, | |
| 325 | crate_paths, | |
| 1635 | serialized_index, | |
| 1636 | used_in_function_signature, | |
| 326 | 1637 | tcx, |
| 327 | 1638 | ); |
| 1639 | use crate::clean::PrimitiveType; | |
| 1640 | // These cases are added to the inverted index, but not actually included | |
| 1641 | // in the signature. There's a matching set of cases in the | |
| 1642 | // `unifyFunctionTypeIsMatchCandidate` function, for the slow path. | |
| 1643 | match id { | |
| 1644 | // typeNameIdOfArrayOrSlice | |
| 1645 | RenderTypeId::Primitive(PrimitiveType::Array | PrimitiveType::Slice) => { | |
| 1646 | insert_into_map( | |
| 1647 | ItemType::Primitive, | |
| 1648 | &[Symbol::intern("[]")], | |
| 1649 | None, | |
| 1650 | false, | |
| 1651 | serialized_index, | |
| 1652 | used_in_function_signature, | |
| 1653 | ); | |
| 1654 | } | |
| 1655 | RenderTypeId::Primitive(PrimitiveType::Tuple | PrimitiveType::Unit) => { | |
| 1656 | // typeNameIdOfArrayOrSlice | |
| 1657 | insert_into_map( | |
| 1658 | ItemType::Primitive, | |
| 1659 | &[Symbol::intern("()")], | |
| 1660 | None, | |
| 1661 | false, | |
| 1662 | serialized_index, | |
| 1663 | used_in_function_signature, | |
| 1664 | ); | |
| 1665 | } | |
| 1666 | // typeNameIdOfHof | |
| 1667 | RenderTypeId::Primitive(PrimitiveType::Fn) => { | |
| 1668 | insert_into_map( | |
| 1669 | ItemType::Primitive, | |
| 1670 | &[Symbol::intern("->")], | |
| 1671 | None, | |
| 1672 | false, | |
| 1673 | serialized_index, | |
| 1674 | used_in_function_signature, | |
| 1675 | ); | |
| 1676 | } | |
| 1677 | RenderTypeId::DefId(did) | |
| 1678 | if tcx.lang_items().fn_mut_trait() == Some(did) | |
| 1679 | || tcx.lang_items().fn_once_trait() == Some(did) | |
| 1680 | || tcx.lang_items().fn_trait() == Some(did) => | |
| 1681 | { | |
| 1682 | insert_into_map( | |
| 1683 | ItemType::Primitive, | |
| 1684 | &[Symbol::intern("->")], | |
| 1685 | None, | |
| 1686 | false, | |
| 1687 | serialized_index, | |
| 1688 | used_in_function_signature, | |
| 1689 | ); | |
| 1690 | } | |
| 1691 | // not special | |
| 1692 | _ => {} | |
| 1693 | } | |
| 328 | 1694 | } |
| 329 | 1695 | if let Some(search_type) = &mut item.search_type { |
| 1696 | let mut used_in_function_signature = BTreeSet::new(); | |
| 330 | 1697 | for item in &mut search_type.inputs { |
| 331 | 1698 | convert_render_type( |
| 332 | 1699 | item, |
| 333 | 1700 | cache, |
| 334 | &mut itemid_to_pathid, | |
| 335 | &mut primitives, | |
| 336 | &mut associated_types, | |
| 337 | &mut lastpathid, | |
| 338 | &mut crate_paths, | |
| 1701 | &mut serialized_index, | |
| 1702 | &mut used_in_function_signature, | |
| 339 | 1703 | tcx, |
| 340 | 1704 | ); |
| 341 | 1705 | } |
| ... | ... | @@ -343,11 +1707,8 @@ pub(crate) fn build_index( |
| 343 | 1707 | convert_render_type( |
| 344 | 1708 | item, |
| 345 | 1709 | cache, |
| 346 | &mut itemid_to_pathid, | |
| 347 | &mut primitives, | |
| 348 | &mut associated_types, | |
| 349 | &mut lastpathid, | |
| 350 | &mut crate_paths, | |
| 1710 | &mut serialized_index, | |
| 1711 | &mut used_in_function_signature, | |
| 351 | 1712 | tcx, |
| 352 | 1713 | ); |
| 353 | 1714 | } |
| ... | ... | @@ -356,464 +1717,56 @@ pub(crate) fn build_index( |
| 356 | 1717 | convert_render_type( |
| 357 | 1718 | trait_, |
| 358 | 1719 | cache, |
| 359 | &mut itemid_to_pathid, | |
| 360 | &mut primitives, | |
| 361 | &mut associated_types, | |
| 362 | &mut lastpathid, | |
| 363 | &mut crate_paths, | |
| 1720 | &mut serialized_index, | |
| 1721 | &mut used_in_function_signature, | |
| 364 | 1722 | tcx, |
| 365 | 1723 | ); |
| 366 | 1724 | } |
| 367 | 1725 | } |
| 368 | } | |
| 369 | } | |
| 370 | ||
| 371 | let Cache { ref paths, ref exact_paths, ref external_paths, .. } = *cache; | |
| 372 | ||
| 373 | // Then, on parent modules | |
| 374 | let crate_items: Vec<&IndexItem> = search_index | |
| 375 | .iter_mut() | |
| 376 | .map(|item| { | |
| 377 | item.parent_idx = | |
| 378 | item.parent.and_then(|defid| match itemid_to_pathid.entry(ItemId::DefId(defid)) { | |
| 379 | Entry::Occupied(entry) => Some(*entry.get()), | |
| 380 | Entry::Vacant(entry) => { | |
| 381 | let pathid = lastpathid; | |
| 382 | entry.insert(pathid); | |
| 383 | lastpathid += 1; | |
| 384 | ||
| 385 | if let Some(&(ref fqp, short)) = paths.get(&defid) { | |
| 386 | let exact_fqp = exact_paths | |
| 387 | .get(&defid) | |
| 388 | .or_else(|| external_paths.get(&defid).map(|(fqp, _)| fqp)) | |
| 389 | .filter(|exact_fqp| { | |
| 390 | exact_fqp.last() == Some(&item.name) && *exact_fqp != fqp | |
| 391 | }); | |
| 392 | crate_paths.push(( | |
| 393 | short, | |
| 394 | fqp.clone(), | |
| 395 | exact_fqp.cloned(), | |
| 396 | utils::has_doc_flag(tcx, defid, sym::search_unbox), | |
| 397 | )); | |
| 398 | Some(pathid) | |
| 399 | } else { | |
| 400 | None | |
| 401 | } | |
| 402 | } | |
| 403 | }); | |
| 404 | ||
| 405 | if let Some(defid) = item.defid | |
| 406 | && item.parent_idx.is_none() | |
| 407 | { | |
| 408 | // If this is a re-export, retain the original path. | |
| 409 | // Associated items don't use this. | |
| 410 | // Their parent carries the exact fqp instead. | |
| 411 | let exact_fqp = exact_paths | |
| 412 | .get(&defid) | |
| 413 | .or_else(|| external_paths.get(&defid).map(|(fqp, _)| fqp)); | |
| 414 | item.exact_path = exact_fqp.and_then(|fqp| { | |
| 415 | // Re-exports only count if the name is exactly the same. | |
| 416 | // This is a size optimization, since it means we only need | |
| 417 | // to store the name once (and the path is re-used for everything | |
| 418 | // exported from this same module). It's also likely to Do | |
| 419 | // What I Mean, since if a re-export changes the name, it might | |
| 420 | // also be a change in semantic meaning. | |
| 421 | if fqp.last() != Some(&item.name) { | |
| 422 | return None; | |
| 423 | } | |
| 424 | let path = | |
| 425 | if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export) { | |
| 426 | // `#[macro_export]` always exports to the crate root. | |
| 427 | tcx.crate_name(defid.krate).to_string() | |
| 428 | } else { | |
| 429 | if fqp.len() < 2 { | |
| 430 | return None; | |
| 431 | } | |
| 432 | join_path_syms(&fqp[..fqp.len() - 1]) | |
| 433 | }; | |
| 434 | if path == item.path { | |
| 435 | return None; | |
| 436 | } | |
| 437 | Some(path) | |
| 438 | }); | |
| 439 | } else if let Some(parent_idx) = item.parent_idx { | |
| 440 | let i = <isize as TryInto<usize>>::try_into(parent_idx).unwrap(); | |
| 441 | item.path = { | |
| 442 | let p = &crate_paths[i].1; | |
| 443 | join_path_syms(&p[..p.len() - 1]) | |
| 444 | }; | |
| 445 | item.exact_path = | |
| 446 | crate_paths[i].2.as_ref().map(|xp| join_path_syms(&xp[..xp.len() - 1])); | |
| 447 | } | |
| 448 | ||
| 449 | // Omit the parent path if it is same to that of the prior item. | |
| 450 | if lastpath == item.path { | |
| 451 | item.path.clear(); | |
| 452 | } else { | |
| 453 | lastpath = &item.path; | |
| 454 | } | |
| 455 | ||
| 456 | &*item | |
| 457 | }) | |
| 458 | .collect(); | |
| 459 | ||
| 460 | // Find associated items that need disambiguators | |
| 461 | let mut associated_item_duplicates = FxHashMap::<(isize, ItemType, Symbol), usize>::default(); | |
| 462 | ||
| 463 | for &item in &crate_items { | |
| 464 | if item.impl_id.is_some() | |
| 465 | && let Some(parent_idx) = item.parent_idx | |
| 466 | { | |
| 467 | let count = | |
| 468 | associated_item_duplicates.entry((parent_idx, item.ty, item.name)).or_insert(0); | |
| 469 | *count += 1; | |
| 470 | } | |
| 471 | } | |
| 472 | ||
| 473 | let associated_item_disambiguators = crate_items | |
| 474 | .iter() | |
| 475 | .enumerate() | |
| 476 | .filter_map(|(index, item)| { | |
| 477 | let impl_id = ItemId::DefId(item.impl_id?); | |
| 478 | let parent_idx = item.parent_idx?; | |
| 479 | let count = *associated_item_duplicates.get(&(parent_idx, item.ty, item.name))?; | |
| 480 | if count > 1 { Some((index, render::get_id_for_impl(tcx, impl_id))) } else { None } | |
| 481 | }) | |
| 482 | .collect::<Vec<_>>(); | |
| 483 | ||
| 484 | struct CrateData<'a> { | |
| 485 | items: Vec<&'a IndexItem>, | |
| 486 | paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>, bool)>, | |
| 487 | // The String is alias name and the vec is the list of the elements with this alias. | |
| 488 | // | |
| 489 | // To be noted: the `usize` elements are indexes to `items`. | |
| 490 | aliases: &'a AliasMap, | |
| 491 | // Used when a type has more than one impl with an associated item with the same name. | |
| 492 | associated_item_disambiguators: &'a Vec<(usize, String)>, | |
| 493 | // A list of shard lengths encoded as vlqhex. See the comment in write_vlqhex_to_string | |
| 494 | // for information on the format. | |
| 495 | desc_index: String, | |
| 496 | // A list of items with no description. This is eventually turned into a bitmap. | |
| 497 | empty_desc: Vec<u32>, | |
| 498 | } | |
| 499 | ||
| 500 | struct Paths { | |
| 501 | ty: ItemType, | |
| 502 | name: Symbol, | |
| 503 | path: Option<usize>, | |
| 504 | exact_path: Option<usize>, | |
| 505 | search_unbox: bool, | |
| 506 | } | |
| 507 | ||
| 508 | impl Serialize for Paths { | |
| 509 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
| 510 | where | |
| 511 | S: Serializer, | |
| 512 | { | |
| 513 | let mut seq = serializer.serialize_seq(None)?; | |
| 514 | seq.serialize_element(&self.ty)?; | |
| 515 | seq.serialize_element(self.name.as_str())?; | |
| 516 | if let Some(ref path) = self.path { | |
| 517 | seq.serialize_element(path)?; | |
| 518 | } | |
| 519 | if let Some(ref path) = self.exact_path { | |
| 520 | assert!(self.path.is_some()); | |
| 521 | seq.serialize_element(path)?; | |
| 522 | } | |
| 523 | if self.search_unbox { | |
| 524 | if self.path.is_none() { | |
| 525 | seq.serialize_element(&None::<u8>)?; | |
| 526 | } | |
| 527 | if self.exact_path.is_none() { | |
| 528 | seq.serialize_element(&None::<u8>)?; | |
| 529 | } | |
| 530 | seq.serialize_element(&1)?; | |
| 531 | } | |
| 532 | seq.end() | |
| 533 | } | |
| 534 | } | |
| 535 | ||
| 536 | impl Serialize for CrateData<'_> { | |
| 537 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
| 538 | where | |
| 539 | S: Serializer, | |
| 540 | { | |
| 541 | let mut extra_paths = FxHashMap::default(); | |
| 542 | // We need to keep the order of insertion, hence why we use an `IndexMap`. Then we will | |
| 543 | // insert these "extra paths" (which are paths of items from external crates) into the | |
| 544 | // `full_paths` list at the end. | |
| 545 | let mut revert_extra_paths = FxIndexMap::default(); | |
| 546 | let mut mod_paths = FxHashMap::default(); | |
| 547 | for (index, item) in self.items.iter().enumerate() { | |
| 548 | if item.path.is_empty() { | |
| 549 | continue; | |
| 550 | } | |
| 551 | mod_paths.insert(&item.path, index); | |
| 552 | } | |
| 553 | let mut paths = Vec::with_capacity(self.paths.len()); | |
| 554 | for &(ty, ref path, ref exact, search_unbox) in &self.paths { | |
| 555 | if path.len() < 2 { | |
| 556 | paths.push(Paths { | |
| 557 | ty, | |
| 558 | name: path[0], | |
| 559 | path: None, | |
| 560 | exact_path: None, | |
| 561 | search_unbox, | |
| 562 | }); | |
| 563 | continue; | |
| 564 | } | |
| 565 | let full_path = join_path_syms(&path[..path.len() - 1]); | |
| 566 | let full_exact_path = exact | |
| 567 | .as_ref() | |
| 568 | .filter(|exact| exact.last() == path.last() && exact.len() >= 2) | |
| 569 | .map(|exact| join_path_syms(&exact[..exact.len() - 1])); | |
| 570 | let exact_path = extra_paths.len() + self.items.len(); | |
| 571 | let exact_path = full_exact_path.as_ref().map(|full_exact_path| match extra_paths | |
| 572 | .entry(full_exact_path.clone()) | |
| 573 | { | |
| 574 | Entry::Occupied(entry) => *entry.get(), | |
| 575 | Entry::Vacant(entry) => { | |
| 576 | if let Some(index) = mod_paths.get(&full_exact_path) { | |
| 577 | return *index; | |
| 578 | } | |
| 579 | entry.insert(exact_path); | |
| 580 | if !revert_extra_paths.contains_key(&exact_path) { | |
| 581 | revert_extra_paths.insert(exact_path, full_exact_path.clone()); | |
| 582 | } | |
| 583 | exact_path | |
| 584 | } | |
| 585 | }); | |
| 586 | if let Some(index) = mod_paths.get(&full_path) { | |
| 587 | paths.push(Paths { | |
| 588 | ty, | |
| 589 | name: *path.last().unwrap(), | |
| 590 | path: Some(*index), | |
| 591 | exact_path, | |
| 592 | search_unbox, | |
| 593 | }); | |
| 594 | continue; | |
| 595 | } | |
| 596 | // It means it comes from an external crate so the item and its path will be | |
| 597 | // stored into another array. | |
| 1726 | let search_type_size = search_type.size() + | |
| 1727 | // Artificially give struct fields a size of 8 instead of their real | |
| 1728 | // size of 2. This is because search.js sorts them to the end, so | |
| 1729 | // by pushing them down, we prevent them from blocking real 2-arity functions. | |
| 598 | 1730 | // |
| 599 | // `index` is put after the last `mod_paths` | |
| 600 | let index = extra_paths.len() + self.items.len(); | |
| 601 | match extra_paths.entry(full_path.clone()) { | |
| 602 | Entry::Occupied(entry) => { | |
| 603 | paths.push(Paths { | |
| 604 | ty, | |
| 605 | name: *path.last().unwrap(), | |
| 606 | path: Some(*entry.get()), | |
| 607 | exact_path, | |
| 608 | search_unbox, | |
| 609 | }); | |
| 610 | } | |
| 611 | Entry::Vacant(entry) => { | |
| 612 | entry.insert(index); | |
| 613 | if !revert_extra_paths.contains_key(&index) { | |
| 614 | revert_extra_paths.insert(index, full_path); | |
| 615 | } | |
| 616 | paths.push(Paths { | |
| 617 | ty, | |
| 618 | name: *path.last().unwrap(), | |
| 619 | path: Some(index), | |
| 620 | exact_path, | |
| 621 | search_unbox, | |
| 622 | }); | |
| 623 | } | |
| 624 | } | |
| 625 | } | |
| 626 | ||
| 627 | // Direct exports use adjacent arrays for the current crate's items, | |
| 628 | // but re-exported exact paths don't. | |
| 629 | let mut re_exports = Vec::new(); | |
| 630 | for (item_index, item) in self.items.iter().enumerate() { | |
| 631 | if let Some(exact_path) = item.exact_path.as_ref() { | |
| 632 | if let Some(path_index) = mod_paths.get(&exact_path) { | |
| 633 | re_exports.push((item_index, *path_index)); | |
| 634 | } else { | |
| 635 | let path_index = extra_paths.len() + self.items.len(); | |
| 636 | let path_index = match extra_paths.entry(exact_path.clone()) { | |
| 637 | Entry::Occupied(entry) => *entry.get(), | |
| 638 | Entry::Vacant(entry) => { | |
| 639 | entry.insert(path_index); | |
| 640 | if !revert_extra_paths.contains_key(&path_index) { | |
| 641 | revert_extra_paths.insert(path_index, exact_path.clone()); | |
| 642 | } | |
| 643 | path_index | |
| 644 | } | |
| 645 | }; | |
| 646 | re_exports.push((item_index, path_index)); | |
| 647 | } | |
| 648 | } | |
| 649 | } | |
| 650 | ||
| 651 | let mut names = Vec::with_capacity(self.items.len()); | |
| 652 | let mut types = String::with_capacity(self.items.len()); | |
| 653 | let mut full_paths = Vec::with_capacity(self.items.len()); | |
| 654 | let mut parents = String::with_capacity(self.items.len()); | |
| 655 | let mut parents_backref_queue = VecDeque::new(); | |
| 656 | let mut functions = String::with_capacity(self.items.len()); | |
| 657 | let mut deprecated = Vec::with_capacity(self.items.len()); | |
| 658 | ||
| 659 | let mut type_backref_queue = VecDeque::new(); | |
| 660 | ||
| 661 | let mut last_name = None; | |
| 662 | for (index, item) in self.items.iter().enumerate() { | |
| 663 | let n = item.ty as u8; | |
| 664 | let c = char::from(n + b'A'); | |
| 665 | assert!(c <= 'z', "item types must fit within ASCII printables"); | |
| 666 | types.push(c); | |
| 667 | ||
| 668 | assert_eq!( | |
| 669 | item.parent.is_some(), | |
| 670 | item.parent_idx.is_some(), | |
| 671 | "`{}` is missing idx", | |
| 672 | item.name | |
| 673 | ); | |
| 674 | assert!( | |
| 675 | parents_backref_queue.len() <= 16, | |
| 676 | "the string encoding only supports 16 slots of lookback" | |
| 677 | ); | |
| 678 | let parent: i32 = item.parent_idx.map(|x| x + 1).unwrap_or(0).try_into().unwrap(); | |
| 679 | if let Some(idx) = parents_backref_queue.iter().position(|p: &i32| *p == parent) { | |
| 680 | parents.push( | |
| 681 | char::try_from('0' as u32 + u32::try_from(idx).unwrap()) | |
| 682 | .expect("last possible value is '?'"), | |
| 683 | ); | |
| 684 | } else if parent == 0 { | |
| 685 | write_vlqhex_to_string(parent, &mut parents); | |
| 686 | } else { | |
| 687 | parents_backref_queue.push_front(parent); | |
| 688 | write_vlqhex_to_string(parent, &mut parents); | |
| 689 | if parents_backref_queue.len() > 16 { | |
| 690 | parents_backref_queue.pop_back(); | |
| 691 | } | |
| 692 | } | |
| 693 | ||
| 694 | if Some(item.name.as_str()) == last_name { | |
| 695 | names.push(""); | |
| 1731 | // The number 8 is arbitrary. We want it big, but not enormous, | |
| 1732 | // because the postings list has to fill in an empty array for each | |
| 1733 | // unoccupied size. | |
| 1734 | if item.ty.is_fn_like() { 0 } else { 16 }; | |
| 1735 | serialized_index.function_data[new_entry_id] = Some(FunctionData { | |
| 1736 | function_signature: { | |
| 1737 | let mut function_signature = String::new(); | |
| 1738 | search_type.write_to_string_without_param_names(&mut function_signature); | |
| 1739 | function_signature | |
| 1740 | }, | |
| 1741 | param_names: search_type | |
| 1742 | .param_names | |
| 1743 | .iter() | |
| 1744 | .map(|sym| sym.map(|sym| sym.to_string()).unwrap_or(String::new())) | |
| 1745 | .collect::<Vec<String>>(), | |
| 1746 | }); | |
| 1747 | for index in used_in_function_signature { | |
| 1748 | let postings = if index >= 0 { | |
| 1749 | assert!(serialized_index.path_data[index as usize].is_some()); | |
| 1750 | &mut serialized_index.type_data[index as usize] | |
| 1751 | .as_mut() | |
| 1752 | .unwrap() | |
| 1753 | .inverted_function_signature_index | |
| 696 | 1754 | } else { |
| 697 | names.push(item.name.as_str()); | |
| 698 | last_name = Some(item.name.as_str()); | |
| 699 | } | |
| 700 | ||
| 701 | if !item.path.is_empty() { | |
| 702 | full_paths.push((index, &item.path)); | |
| 703 | } | |
| 704 | ||
| 705 | match &item.search_type { | |
| 706 | Some(ty) => ty.write_to_string(&mut functions, &mut type_backref_queue), | |
| 707 | None => functions.push('`'), | |
| 708 | } | |
| 709 | ||
| 710 | if item.deprecation.is_some() { | |
| 711 | // bitmasks always use 1-indexing for items, with 0 as the crate itself | |
| 712 | deprecated.push(u32::try_from(index + 1).unwrap()); | |
| 713 | } | |
| 714 | } | |
| 715 | ||
| 716 | for (index, path) in &revert_extra_paths { | |
| 717 | full_paths.push((*index, path)); | |
| 718 | } | |
| 719 | ||
| 720 | let param_names: Vec<(usize, String)> = { | |
| 721 | let mut prev = Vec::new(); | |
| 722 | let mut result = Vec::new(); | |
| 723 | for (index, item) in self.items.iter().enumerate() { | |
| 724 | if let Some(ty) = &item.search_type | |
| 725 | && let my = ty | |
| 726 | .param_names | |
| 727 | .iter() | |
| 728 | .filter_map(|sym| sym.map(|sym| sym.to_string())) | |
| 729 | .collect::<Vec<_>>() | |
| 730 | && my != prev | |
| 731 | { | |
| 732 | result.push((index, my.join(","))); | |
| 733 | prev = my; | |
| 1755 | let generic_id = usize::try_from(-index).unwrap() - 1; | |
| 1756 | for _ in serialized_index.generic_inverted_index.len()..=generic_id { | |
| 1757 | serialized_index.generic_inverted_index.push(Vec::new()); | |
| 734 | 1758 | } |
| 1759 | &mut serialized_index.generic_inverted_index[generic_id] | |
| 1760 | }; | |
| 1761 | while postings.len() <= search_type_size { | |
| 1762 | postings.push(Vec::new()); | |
| 735 | 1763 | } |
| 736 | result | |
| 737 | }; | |
| 738 | ||
| 739 | let has_aliases = !self.aliases.is_empty(); | |
| 740 | let mut crate_data = | |
| 741 | serializer.serialize_struct("CrateData", if has_aliases { 13 } else { 12 })?; | |
| 742 | crate_data.serialize_field("t", &types)?; | |
| 743 | crate_data.serialize_field("n", &names)?; | |
| 744 | crate_data.serialize_field("q", &full_paths)?; | |
| 745 | crate_data.serialize_field("i", &parents)?; | |
| 746 | crate_data.serialize_field("f", &functions)?; | |
| 747 | crate_data.serialize_field("D", &self.desc_index)?; | |
| 748 | crate_data.serialize_field("p", &paths)?; | |
| 749 | crate_data.serialize_field("r", &re_exports)?; | |
| 750 | crate_data.serialize_field("b", &self.associated_item_disambiguators)?; | |
| 751 | crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?; | |
| 752 | crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?; | |
| 753 | crate_data.serialize_field("P", &param_names)?; | |
| 754 | if has_aliases { | |
| 755 | crate_data.serialize_field("a", &self.aliases)?; | |
| 1764 | postings[search_type_size].push(new_entry_id as u32); | |
| 756 | 1765 | } |
| 757 | crate_data.end() | |
| 758 | 1766 | } |
| 759 | 1767 | } |
| 760 | 1768 | |
| 761 | let (empty_desc, desc) = { | |
| 762 | let mut empty_desc = Vec::new(); | |
| 763 | let mut result = Vec::new(); | |
| 764 | let mut set = String::new(); | |
| 765 | let mut len: usize = 0; | |
| 766 | let mut item_index: u32 = 0; | |
| 767 | for desc in std::iter::once(&crate_doc).chain(crate_items.iter().map(|item| &item.desc)) { | |
| 768 | if desc.is_empty() { | |
| 769 | empty_desc.push(item_index); | |
| 770 | item_index += 1; | |
| 771 | continue; | |
| 772 | } | |
| 773 | if set.len() >= DESC_INDEX_SHARD_LEN { | |
| 774 | result.push((len, std::mem::take(&mut set))); | |
| 775 | len = 0; | |
| 776 | } else if len != 0 { | |
| 777 | set.push('\n'); | |
| 778 | } | |
| 779 | set.push_str(desc); | |
| 780 | len += 1; | |
| 781 | item_index += 1; | |
| 782 | } | |
| 783 | result.push((len, std::mem::take(&mut set))); | |
| 784 | (empty_desc, result) | |
| 785 | }; | |
| 786 | ||
| 787 | let desc_index = { | |
| 788 | let mut desc_index = String::with_capacity(desc.len() * 4); | |
| 789 | for &(len, _) in desc.iter() { | |
| 790 | write_vlqhex_to_string(len.try_into().unwrap(), &mut desc_index); | |
| 791 | } | |
| 792 | desc_index | |
| 793 | }; | |
| 794 | ||
| 795 | assert_eq!( | |
| 796 | crate_items.len() + 1, | |
| 797 | desc.iter().map(|(len, _)| *len).sum::<usize>() + empty_desc.len() | |
| 798 | ); | |
| 799 | ||
| 800 | // The index, which is actually used to search, is JSON | |
| 801 | // It uses `JSON.parse(..)` to actually load, since JSON | |
| 802 | // parses faster than the full JavaScript syntax. | |
| 803 | let crate_name = krate.name(tcx); | |
| 804 | let data = CrateData { | |
| 805 | items: crate_items, | |
| 806 | paths: crate_paths, | |
| 807 | aliases: &aliases, | |
| 808 | associated_item_disambiguators: &associated_item_disambiguators, | |
| 809 | desc_index, | |
| 810 | empty_desc, | |
| 811 | }; | |
| 812 | let index = OrderedJson::array_unsorted([ | |
| 813 | OrderedJson::serialize(crate_name.as_str()).unwrap(), | |
| 814 | OrderedJson::serialize(data).unwrap(), | |
| 815 | ]); | |
| 816 | SerializedSearchIndex { index, desc } | |
| 1769 | Ok(serialized_index.sort()) | |
| 817 | 1770 | } |
| 818 | 1771 | |
| 819 | 1772 | pub(crate) fn get_function_type_for_search( |
src/librustdoc/html/render/search_index/encode.rs+51-193| ... | ... | @@ -1,6 +1,4 @@ |
| 1 | use base64::prelude::*; | |
| 2 | ||
| 3 | pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) { | |
| 1 | pub(crate) fn write_signed_vlqhex_to_string(n: i32, string: &mut String) { | |
| 4 | 2 | let (sign, magnitude): (bool, u32) = |
| 5 | 3 | if n >= 0 { (false, n.try_into().unwrap()) } else { (true, (-n).try_into().unwrap()) }; |
| 6 | 4 | // zig-zag encoding |
| ... | ... | @@ -37,206 +35,66 @@ pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) { |
| 37 | 35 | } |
| 38 | 36 | } |
| 39 | 37 | |
| 40 | // Used during bitmap encoding | |
| 41 | enum Container { | |
| 42 | /// number of ones, bits | |
| 43 | Bits(Box<[u64; 1024]>), | |
| 44 | /// list of entries | |
| 45 | Array(Vec<u16>), | |
| 46 | /// list of (start, len-1) | |
| 47 | Run(Vec<(u16, u16)>), | |
| 48 | } | |
| 49 | impl Container { | |
| 50 | fn popcount(&self) -> u32 { | |
| 51 | match self { | |
| 52 | Container::Bits(bits) => bits.iter().copied().map(|x| x.count_ones()).sum(), | |
| 53 | Container::Array(array) => { | |
| 54 | array.len().try_into().expect("array can't be bigger than 2**32") | |
| 55 | } | |
| 56 | Container::Run(runs) => { | |
| 57 | runs.iter().copied().map(|(_, lenm1)| u32::from(lenm1) + 1).sum() | |
| 58 | } | |
| 38 | pub fn read_signed_vlqhex_from_string(string: &[u8]) -> Option<(i32, usize)> { | |
| 39 | let mut n = 0i32; | |
| 40 | let mut i = 0; | |
| 41 | while let Some(&c) = string.get(i) { | |
| 42 | i += 1; | |
| 43 | n = (n << 4) | i32::from(c & 0xF); | |
| 44 | if c >= 96 { | |
| 45 | // zig-zag encoding | |
| 46 | let (sign, magnitude) = (n & 1, n >> 1); | |
| 47 | let value = if sign == 0 { 1 } else { -1 } * magnitude; | |
| 48 | return Some((value, i)); | |
| 59 | 49 | } |
| 60 | 50 | } |
| 61 | fn push(&mut self, value: u16) { | |
| 62 | match self { | |
| 63 | Container::Bits(bits) => bits[value as usize >> 6] |= 1 << (value & 0x3F), | |
| 64 | Container::Array(array) => { | |
| 65 | array.push(value); | |
| 66 | if array.len() >= 4096 { | |
| 67 | let array = std::mem::take(array); | |
| 68 | *self = Container::Bits(Box::new([0; 1024])); | |
| 69 | for value in array { | |
| 70 | self.push(value); | |
| 71 | } | |
| 72 | } | |
| 73 | } | |
| 74 | Container::Run(runs) => { | |
| 75 | if let Some(r) = runs.last_mut() | |
| 76 | && r.0 + r.1 + 1 == value | |
| 77 | { | |
| 78 | r.1 += 1; | |
| 79 | } else { | |
| 80 | runs.push((value, 0)); | |
| 81 | } | |
| 82 | } | |
| 51 | None | |
| 52 | } | |
| 53 | ||
| 54 | pub fn write_postings_to_string(postings: &[Vec<u32>], buf: &mut Vec<u8>) { | |
| 55 | for list in postings { | |
| 56 | if list.is_empty() { | |
| 57 | buf.push(0); | |
| 58 | continue; | |
| 83 | 59 | } |
| 84 | } | |
| 85 | fn try_make_run(&mut self) -> bool { | |
| 86 | match self { | |
| 87 | Container::Bits(bits) => { | |
| 88 | let mut r: u64 = 0; | |
| 89 | for (i, chunk) in bits.iter().copied().enumerate() { | |
| 90 | let next_chunk = | |
| 91 | i.checked_add(1).and_then(|i| bits.get(i)).copied().unwrap_or(0); | |
| 92 | r += !chunk & u64::from((chunk << 1).count_ones()); | |
| 93 | r += !next_chunk & u64::from((chunk >> 63).count_ones()); | |
| 94 | } | |
| 95 | if (2 + 4 * r) >= 8192 { | |
| 96 | return false; | |
| 97 | } | |
| 98 | let bits = std::mem::replace(bits, Box::new([0; 1024])); | |
| 99 | *self = Container::Run(Vec::new()); | |
| 100 | for (i, bits) in bits.iter().copied().enumerate() { | |
| 101 | if bits == 0 { | |
| 102 | continue; | |
| 103 | } | |
| 104 | for j in 0..64 { | |
| 105 | let value = (u16::try_from(i).unwrap() << 6) | j; | |
| 106 | if bits & (1 << j) != 0 { | |
| 107 | self.push(value); | |
| 108 | } | |
| 109 | } | |
| 110 | } | |
| 111 | true | |
| 60 | let len_before = buf.len(); | |
| 61 | stringdex::internals::encode::write_bitmap_to_bytes(&list, &mut *buf).unwrap(); | |
| 62 | let len_after = buf.len(); | |
| 63 | if len_after - len_before > 1 + (4 * list.len()) && list.len() < 0x3a { | |
| 64 | buf.truncate(len_before); | |
| 65 | buf.push(list.len() as u8); | |
| 66 | for &item in list { | |
| 67 | buf.push(item as u8); | |
| 68 | buf.push((item >> 8) as u8); | |
| 69 | buf.push((item >> 16) as u8); | |
| 70 | buf.push((item >> 24) as u8); | |
| 112 | 71 | } |
| 113 | Container::Array(array) if array.len() <= 5 => false, | |
| 114 | Container::Array(array) => { | |
| 115 | let mut r = 0; | |
| 116 | let mut prev = None; | |
| 117 | for value in array.iter().copied() { | |
| 118 | if value.checked_sub(1) != prev { | |
| 119 | r += 1; | |
| 120 | } | |
| 121 | prev = Some(value); | |
| 122 | } | |
| 123 | if 2 + 4 * r >= 2 * array.len() + 2 { | |
| 124 | return false; | |
| 125 | } | |
| 126 | let array = std::mem::take(array); | |
| 127 | *self = Container::Run(Vec::new()); | |
| 128 | for value in array { | |
| 129 | self.push(value); | |
| 130 | } | |
| 131 | true | |
| 132 | } | |
| 133 | Container::Run(_) => true, | |
| 134 | 72 | } |
| 135 | 73 | } |
| 136 | 74 | } |
| 137 | 75 | |
| 138 | // checked against roaring-rs in | |
| 139 | // https://gitlab.com/notriddle/roaring-test | |
| 140 | pub(crate) fn write_bitmap_to_bytes( | |
| 141 | domain: &[u32], | |
| 142 | mut out: impl std::io::Write, | |
| 143 | ) -> std::io::Result<()> { | |
| 144 | // https://arxiv.org/pdf/1603.06549.pdf | |
| 145 | let mut keys = Vec::<u16>::new(); | |
| 146 | let mut containers = Vec::<Container>::new(); | |
| 147 | let mut key: u16; | |
| 148 | let mut domain_iter = domain.iter().copied().peekable(); | |
| 149 | let mut has_run = false; | |
| 150 | while let Some(entry) = domain_iter.next() { | |
| 151 | key = (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit"); | |
| 152 | let value: u16 = (entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit"); | |
| 153 | let mut container = Container::Array(vec![value]); | |
| 154 | while let Some(entry) = domain_iter.peek().copied() { | |
| 155 | let entry_key: u16 = | |
| 156 | (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit"); | |
| 157 | if entry_key != key { | |
| 158 | break; | |
| 76 | pub fn read_postings_from_string(postings: &mut Vec<Vec<u32>>, mut buf: &[u8]) { | |
| 77 | use stringdex::internals::decode::RoaringBitmap; | |
| 78 | while let Some(&c) = buf.get(0) { | |
| 79 | if c < 0x3a { | |
| 80 | buf = &buf[1..]; | |
| 81 | let mut slot = Vec::new(); | |
| 82 | for _ in 0..c { | |
| 83 | slot.push( | |
| 84 | (buf[0] as u32) | |
| 85 | | ((buf[1] as u32) << 8) | |
| 86 | | ((buf[2] as u32) << 16) | |
| 87 | | ((buf[3] as u32) << 24), | |
| 88 | ); | |
| 89 | buf = &buf[4..]; | |
| 159 | 90 | } |
| 160 | domain_iter.next().expect("peeking just succeeded"); | |
| 161 | container | |
| 162 | .push((entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit")); | |
| 163 | } | |
| 164 | keys.push(key); | |
| 165 | has_run = container.try_make_run() || has_run; | |
| 166 | containers.push(container); | |
| 167 | } | |
| 168 | // https://github.com/RoaringBitmap/RoaringFormatSpec | |
| 169 | const SERIAL_COOKIE_NO_RUNCONTAINER: u32 = 12346; | |
| 170 | const SERIAL_COOKIE: u32 = 12347; | |
| 171 | const NO_OFFSET_THRESHOLD: u32 = 4; | |
| 172 | let size: u32 = containers.len().try_into().unwrap(); | |
| 173 | let start_offset = if has_run { | |
| 174 | out.write_all(&u32::to_le_bytes(SERIAL_COOKIE | ((size - 1) << 16)))?; | |
| 175 | for set in containers.chunks(8) { | |
| 176 | let mut b = 0; | |
| 177 | for (i, container) in set.iter().enumerate() { | |
| 178 | if matches!(container, &Container::Run(..)) { | |
| 179 | b |= 1 << i; | |
| 180 | } | |
| 181 | } | |
| 182 | out.write_all(&[b])?; | |
| 183 | } | |
| 184 | if size < NO_OFFSET_THRESHOLD { | |
| 185 | 4 + 4 * size + size.div_ceil(8) | |
| 91 | postings.push(slot); | |
| 186 | 92 | } else { |
| 187 | 4 + 8 * size + size.div_ceil(8) | |
| 188 | } | |
| 189 | } else { | |
| 190 | out.write_all(&u32::to_le_bytes(SERIAL_COOKIE_NO_RUNCONTAINER))?; | |
| 191 | out.write_all(&u32::to_le_bytes(containers.len().try_into().unwrap()))?; | |
| 192 | 4 + 4 + 4 * size + 4 * size | |
| 193 | }; | |
| 194 | for (&key, container) in keys.iter().zip(&containers) { | |
| 195 | // descriptive header | |
| 196 | let key: u32 = key.into(); | |
| 197 | let count: u32 = container.popcount() - 1; | |
| 198 | out.write_all(&u32::to_le_bytes((count << 16) | key))?; | |
| 199 | } | |
| 200 | if !has_run || size >= NO_OFFSET_THRESHOLD { | |
| 201 | // offset header | |
| 202 | let mut starting_offset = start_offset; | |
| 203 | for container in &containers { | |
| 204 | out.write_all(&u32::to_le_bytes(starting_offset))?; | |
| 205 | starting_offset += match container { | |
| 206 | Container::Bits(_) => 8192u32, | |
| 207 | Container::Array(array) => u32::try_from(array.len()).unwrap() * 2, | |
| 208 | Container::Run(runs) => 2 + u32::try_from(runs.len()).unwrap() * 4, | |
| 209 | }; | |
| 93 | let (bitmap, consumed_bytes_len) = | |
| 94 | RoaringBitmap::from_bytes(buf).unwrap_or_else(|| (RoaringBitmap::default(), 0)); | |
| 95 | assert_ne!(consumed_bytes_len, 0); | |
| 96 | postings.push(bitmap.to_vec()); | |
| 97 | buf = &buf[consumed_bytes_len..]; | |
| 210 | 98 | } |
| 211 | 99 | } |
| 212 | for container in &containers { | |
| 213 | match container { | |
| 214 | Container::Bits(bits) => { | |
| 215 | for chunk in bits.iter() { | |
| 216 | out.write_all(&u64::to_le_bytes(*chunk))?; | |
| 217 | } | |
| 218 | } | |
| 219 | Container::Array(array) => { | |
| 220 | for value in array.iter() { | |
| 221 | out.write_all(&u16::to_le_bytes(*value))?; | |
| 222 | } | |
| 223 | } | |
| 224 | Container::Run(runs) => { | |
| 225 | out.write_all(&u16::to_le_bytes(runs.len().try_into().unwrap()))?; | |
| 226 | for (start, lenm1) in runs.iter().copied() { | |
| 227 | out.write_all(&u16::to_le_bytes(start))?; | |
| 228 | out.write_all(&u16::to_le_bytes(lenm1))?; | |
| 229 | } | |
| 230 | } | |
| 231 | } | |
| 232 | } | |
| 233 | Ok(()) | |
| 234 | } | |
| 235 | ||
| 236 | pub(crate) fn bitmap_to_string(domain: &[u32]) -> String { | |
| 237 | let mut buf = Vec::new(); | |
| 238 | let mut strbuf = String::new(); | |
| 239 | write_bitmap_to_bytes(domain, &mut buf).unwrap(); | |
| 240 | BASE64_STANDARD.encode_string(&buf, &mut strbuf); | |
| 241 | strbuf | |
| 242 | 100 | } |
src/librustdoc/html/render/write_shared.rs+14-64| ... | ... | @@ -65,17 +65,17 @@ pub(crate) fn write_shared( |
| 65 | 65 | // Write shared runs within a flock; disable thread dispatching of IO temporarily. |
| 66 | 66 | let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); |
| 67 | 67 | |
| 68 | let SerializedSearchIndex { index, desc } = build_index(krate, &mut cx.shared.cache, tcx); | |
| 69 | write_search_desc(cx, krate, &desc)?; // does not need to be merged | |
| 68 | let search_index = | |
| 69 | build_index(krate, &mut cx.shared.cache, tcx, &cx.dst, &cx.shared.resource_suffix)?; | |
| 70 | 70 | |
| 71 | 71 | let crate_name = krate.name(cx.tcx()); |
| 72 | 72 | let crate_name = crate_name.as_str(); // rand |
| 73 | 73 | let crate_name_json = OrderedJson::serialize(crate_name).unwrap(); // "rand" |
| 74 | 74 | let external_crates = hack_get_external_crate_names(&cx.dst, &cx.shared.resource_suffix)?; |
| 75 | 75 | let info = CrateInfo { |
| 76 | version: CrateInfoVersion::V1, | |
| 76 | version: CrateInfoVersion::V2, | |
| 77 | 77 | src_files_js: SourcesPart::get(cx, &crate_name_json)?, |
| 78 | search_index_js: SearchIndexPart::get(index, &cx.shared.resource_suffix)?, | |
| 78 | search_index, | |
| 79 | 79 | all_crates: AllCratesPart::get(crate_name_json.clone(), &cx.shared.resource_suffix)?, |
| 80 | 80 | crates_index: CratesIndexPart::get(crate_name, &external_crates)?, |
| 81 | 81 | trait_impl: TraitAliasPart::get(cx, &crate_name_json)?, |
| ... | ... | @@ -141,7 +141,7 @@ pub(crate) fn write_not_crate_specific( |
| 141 | 141 | resource_suffix: &str, |
| 142 | 142 | include_sources: bool, |
| 143 | 143 | ) -> Result<(), Error> { |
| 144 | write_rendered_cross_crate_info(crates, dst, opt, include_sources)?; | |
| 144 | write_rendered_cross_crate_info(crates, dst, opt, include_sources, resource_suffix)?; | |
| 145 | 145 | write_static_files(dst, opt, style_files, css_file_extension, resource_suffix)?; |
| 146 | 146 | Ok(()) |
| 147 | 147 | } |
| ... | ... | @@ -151,13 +151,18 @@ fn write_rendered_cross_crate_info( |
| 151 | 151 | dst: &Path, |
| 152 | 152 | opt: &RenderOptions, |
| 153 | 153 | include_sources: bool, |
| 154 | resource_suffix: &str, | |
| 154 | 155 | ) -> Result<(), Error> { |
| 155 | 156 | let m = &opt.should_merge; |
| 156 | 157 | if opt.should_emit_crate() { |
| 157 | 158 | if include_sources { |
| 158 | 159 | write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, crates, m)?; |
| 159 | 160 | } |
| 160 | write_rendered_cci::<SearchIndexPart, _>(SearchIndexPart::blank, dst, crates, m)?; | |
| 161 | crates | |
| 162 | .iter() | |
| 163 | .fold(SerializedSearchIndex::default(), |a, b| a.union(&b.search_index)) | |
| 164 | .sort() | |
| 165 | .write_to(dst, resource_suffix)?; | |
| 161 | 166 | write_rendered_cci::<AllCratesPart, _>(AllCratesPart::blank, dst, crates, m)?; |
| 162 | 167 | } |
| 163 | 168 | write_rendered_cci::<TraitAliasPart, _>(TraitAliasPart::blank, dst, crates, m)?; |
| ... | ... | @@ -215,38 +220,12 @@ fn write_static_files( |
| 215 | 220 | Ok(()) |
| 216 | 221 | } |
| 217 | 222 | |
| 218 | /// Write the search description shards to disk | |
| 219 | fn write_search_desc( | |
| 220 | cx: &mut Context<'_>, | |
| 221 | krate: &Crate, | |
| 222 | search_desc: &[(usize, String)], | |
| 223 | ) -> Result<(), Error> { | |
| 224 | let crate_name = krate.name(cx.tcx()).to_string(); | |
| 225 | let encoded_crate_name = OrderedJson::serialize(&crate_name).unwrap(); | |
| 226 | let path = PathBuf::from_iter([&cx.dst, Path::new("search.desc"), Path::new(&crate_name)]); | |
| 227 | if path.exists() { | |
| 228 | try_err!(fs::remove_dir_all(&path), &path); | |
| 229 | } | |
| 230 | for (i, (_, part)) in search_desc.iter().enumerate() { | |
| 231 | let filename = static_files::suffix_path( | |
| 232 | &format!("{crate_name}-desc-{i}-.js"), | |
| 233 | &cx.shared.resource_suffix, | |
| 234 | ); | |
| 235 | let path = path.join(filename); | |
| 236 | let part = OrderedJson::serialize(part).unwrap(); | |
| 237 | let part = format!("searchState.loadedDescShard({encoded_crate_name}, {i}, {part})"); | |
| 238 | create_parents(&path)?; | |
| 239 | try_err!(fs::write(&path, part), &path); | |
| 240 | } | |
| 241 | Ok(()) | |
| 242 | } | |
| 243 | ||
| 244 | 223 | /// Contains pre-rendered contents to insert into the CCI template |
| 245 | 224 | #[derive(Serialize, Deserialize, Clone, Debug)] |
| 246 | 225 | pub(crate) struct CrateInfo { |
| 247 | 226 | version: CrateInfoVersion, |
| 248 | 227 | src_files_js: PartsAndLocations<SourcesPart>, |
| 249 | search_index_js: PartsAndLocations<SearchIndexPart>, | |
| 228 | search_index: SerializedSearchIndex, | |
| 250 | 229 | all_crates: PartsAndLocations<AllCratesPart>, |
| 251 | 230 | crates_index: PartsAndLocations<CratesIndexPart>, |
| 252 | 231 | trait_impl: PartsAndLocations<TraitAliasPart>, |
| ... | ... | @@ -277,7 +256,7 @@ impl CrateInfo { |
| 277 | 256 | /// to provide better diagnostics about including an invalid file. |
| 278 | 257 | #[derive(Serialize, Deserialize, Clone, Debug)] |
| 279 | 258 | enum CrateInfoVersion { |
| 280 | V1, | |
| 259 | V2, | |
| 281 | 260 | } |
| 282 | 261 | |
| 283 | 262 | /// Paths (relative to the doc root) and their pre-merge contents |
| ... | ... | @@ -331,36 +310,6 @@ trait CciPart: Sized + fmt::Display + DeserializeOwned + 'static { |
| 331 | 310 | fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self>; |
| 332 | 311 | } |
| 333 | 312 | |
| 334 | #[derive(Serialize, Deserialize, Clone, Default, Debug)] | |
| 335 | struct SearchIndex; | |
| 336 | type SearchIndexPart = Part<SearchIndex, EscapedJson>; | |
| 337 | impl CciPart for SearchIndexPart { | |
| 338 | type FileFormat = sorted_template::Js; | |
| 339 | fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> { | |
| 340 | &crate_info.search_index_js | |
| 341 | } | |
| 342 | } | |
| 343 | ||
| 344 | impl SearchIndexPart { | |
| 345 | fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> { | |
| 346 | SortedTemplate::from_before_after( | |
| 347 | r"var searchIndex = new Map(JSON.parse('[", | |
| 348 | r"]')); | |
| 349 | if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; | |
| 350 | else if (window.initSearch) window.initSearch(searchIndex);", | |
| 351 | ) | |
| 352 | } | |
| 353 | ||
| 354 | fn get( | |
| 355 | search_index: OrderedJson, | |
| 356 | resource_suffix: &str, | |
| 357 | ) -> Result<PartsAndLocations<Self>, Error> { | |
| 358 | let path = suffix_path("search-index.js", resource_suffix); | |
| 359 | let search_index = EscapedJson::from(search_index); | |
| 360 | Ok(PartsAndLocations::with(path, search_index)) | |
| 361 | } | |
| 362 | } | |
| 363 | ||
| 364 | 313 | #[derive(Serialize, Deserialize, Clone, Default, Debug)] |
| 365 | 314 | struct AllCrates; |
| 366 | 315 | type AllCratesPart = Part<AllCrates, OrderedJson>; |
| ... | ... | @@ -426,6 +375,7 @@ impl CratesIndexPart { |
| 426 | 375 | fn blank(cx: &Context<'_>) -> SortedTemplate<<Self as CciPart>::FileFormat> { |
| 427 | 376 | let page = layout::Page { |
| 428 | 377 | title: "Index of crates", |
| 378 | short_title: "Crates", | |
| 429 | 379 | css_class: "mod sys", |
| 430 | 380 | root_path: "./", |
| 431 | 381 | static_root_path: cx.shared.static_root_path.as_deref(), |
src/librustdoc/html/render/write_shared/tests.rs-33| ... | ... | @@ -29,14 +29,6 @@ fn sources_template() { |
| 29 | 29 | assert_eq!(but_last_line(&template.to_string()), r#"createSrcSidebar('["u","v"]');"#); |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | #[test] | |
| 33 | fn sources_parts() { | |
| 34 | let parts = | |
| 35 | SearchIndexPart::get(OrderedJson::serialize(["foo", "bar"]).unwrap(), "suffix").unwrap(); | |
| 36 | assert_eq!(&parts.parts[0].0, Path::new("search-indexsuffix.js")); | |
| 37 | assert_eq!(&parts.parts[0].1.to_string(), r#"["foo","bar"]"#); | |
| 38 | } | |
| 39 | ||
| 40 | 32 | #[test] |
| 41 | 33 | fn all_crates_template() { |
| 42 | 34 | let mut template = AllCratesPart::blank(); |
| ... | ... | @@ -54,31 +46,6 @@ fn all_crates_parts() { |
| 54 | 46 | assert_eq!(&parts.parts[0].1.to_string(), r#""crate""#); |
| 55 | 47 | } |
| 56 | 48 | |
| 57 | #[test] | |
| 58 | fn search_index_template() { | |
| 59 | let mut template = SearchIndexPart::blank(); | |
| 60 | assert_eq!( | |
| 61 | but_last_line(&template.to_string()), | |
| 62 | r"var searchIndex = new Map(JSON.parse('[]')); | |
| 63 | if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; | |
| 64 | else if (window.initSearch) window.initSearch(searchIndex);" | |
| 65 | ); | |
| 66 | template.append(EscapedJson::from(OrderedJson::serialize([1, 2]).unwrap()).to_string()); | |
| 67 | assert_eq!( | |
| 68 | but_last_line(&template.to_string()), | |
| 69 | r"var searchIndex = new Map(JSON.parse('[[1,2]]')); | |
| 70 | if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; | |
| 71 | else if (window.initSearch) window.initSearch(searchIndex);" | |
| 72 | ); | |
| 73 | template.append(EscapedJson::from(OrderedJson::serialize([4, 3]).unwrap()).to_string()); | |
| 74 | assert_eq!( | |
| 75 | but_last_line(&template.to_string()), | |
| 76 | r"var searchIndex = new Map(JSON.parse('[[1,2],[4,3]]')); | |
| 77 | if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; | |
| 78 | else if (window.initSearch) window.initSearch(searchIndex);" | |
| 79 | ); | |
| 80 | } | |
| 81 | ||
| 82 | 49 | #[test] |
| 83 | 50 | fn crates_index_part() { |
| 84 | 51 | let external_crates = ["bar".to_string(), "baz".to_string()]; |
src/librustdoc/html/sources.rs+1| ... | ... | @@ -230,6 +230,7 @@ impl SourceCollector<'_, '_> { |
| 230 | 230 | ); |
| 231 | 231 | let page = layout::Page { |
| 232 | 232 | title: &title, |
| 233 | short_title: &src_fname.to_string_lossy(), | |
| 233 | 234 | css_class: "src", |
| 234 | 235 | root_path: &root_path, |
| 235 | 236 | static_root_path: shared.static_root_path.as_deref(), |
src/librustdoc/html/static/css/rustdoc.css+317-91| ... | ... | @@ -258,6 +258,17 @@ h1, h2, h3, h4 { |
| 258 | 258 | 	padding-bottom: 6px; |
| 259 | 259 | 	margin-bottom: 15px; |
| 260 | 260 | } |
| 261 | .search-results-main-heading { | |
| 262 | 	grid-template-areas: | |
| 263 | 		"main-heading-breadcrumbs main-heading-placeholder" | |
| 264 | 		"main-heading-breadcrumbs main-heading-toolbar " | |
| 265 | 		"main-heading-h1 main-heading-toolbar "; | |
| 266 | } | |
| 267 | .search-results-main-heading nav.sub { | |
| 268 | 	grid-area: main-heading-h1; | |
| 269 | 	align-items: end; | |
| 270 | 	margin: 4px 0 8px 0; | |
| 271 | } | |
| 261 | 272 | .rustdoc-breadcrumbs { |
| 262 | 273 | 	grid-area: main-heading-breadcrumbs; |
| 263 | 274 | 	line-height: 1.25; |
| ... | ... | @@ -265,6 +276,16 @@ h1, h2, h3, h4 { |
| 265 | 276 | 	position: relative; |
| 266 | 277 | 	z-index: 1; |
| 267 | 278 | } |
| 279 | .search-switcher { | |
| 280 | 	grid-area: main-heading-breadcrumbs; | |
| 281 | 	line-height: 1.5; | |
| 282 | 	display: flex; | |
| 283 | 	color: var(--main-color); | |
| 284 | 	align-items: baseline; | |
| 285 | 	white-space: nowrap; | |
| 286 | 	padding-top: 8px; | |
| 287 | 	min-height: 34px; | |
| 288 | } | |
| 268 | 289 | .rustdoc-breadcrumbs a { |
| 269 | 290 | 	padding: 5px 0 7px; |
| 270 | 291 | } |
| ... | ... | @@ -305,7 +326,7 @@ h4.code-header { |
| 305 | 326 | #crate-search, |
| 306 | 327 | h1, h2, h3, h4, h5, h6, |
| 307 | 328 | .sidebar, |
| 308 | .mobile-topbar, | |
| 329 | rustdoc-topbar, | |
| 309 | 330 | .search-input, |
| 310 | 331 | .search-results .result-name, |
| 311 | 332 | .item-table dt > a, |
| ... | ... | @@ -317,6 +338,7 @@ rustdoc-toolbar, |
| 317 | 338 | summary.hideme, |
| 318 | 339 | .scraped-example-list, |
| 319 | 340 | .rustdoc-breadcrumbs, |
| 341 | .search-switcher, | |
| 320 | 342 | /* This selector is for the items listed in the "all items" page. */ |
| 321 | 343 | ul.all-items { |
| 322 | 344 | 	font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; |
| ... | ... | @@ -329,7 +351,7 @@ a.anchor, |
| 329 | 351 | .rust a, |
| 330 | 352 | .sidebar h2 a, |
| 331 | 353 | .sidebar h3 a, |
| 332 | .mobile-topbar h2 a, | |
| 354 | rustdoc-topbar h2 a, | |
| 333 | 355 | h1 a, |
| 334 | 356 | .search-results a, |
| 335 | 357 | .search-results li, |
| ... | ... | @@ -616,7 +638,7 @@ img { |
| 616 | 638 | 	color: var(--sidebar-resizer-active); |
| 617 | 639 | } |
| 618 | 640 | |
| 619 | .sidebar, .mobile-topbar, .sidebar-menu-toggle, | |
| 641 | .sidebar, rustdoc-topbar, .sidebar-menu-toggle, | |
| 620 | 642 | #src-sidebar { |
| 621 | 643 | 	background-color: var(--sidebar-background-color); |
| 622 | 644 | } |
| ... | ... | @@ -857,7 +879,7 @@ ul.block, .block li, .block ul { |
| 857 | 879 | 	margin-bottom: 1rem; |
| 858 | 880 | } |
| 859 | 881 | |
| 860 | .mobile-topbar { | |
| 882 | rustdoc-topbar { | |
| 861 | 883 | 	display: none; |
| 862 | 884 | } |
| 863 | 885 | |
| ... | ... | @@ -1098,16 +1120,15 @@ div.where { |
| 1098 | 1120 | nav.sub { |
| 1099 | 1121 | 	flex-grow: 1; |
| 1100 | 1122 | 	flex-flow: row nowrap; |
| 1101 | 	margin: 4px 0 0 0; | |
| 1102 | 1123 | 	display: flex; |
| 1103 | 	align-items: center; | |
| 1124 | 	align-items: start; | |
| 1125 | 	margin-top: 4px; | |
| 1104 | 1126 | } |
| 1105 | 1127 | .search-form { |
| 1106 | 1128 | 	position: relative; |
| 1107 | 1129 | 	display: flex; |
| 1108 | 1130 | 	height: 34px; |
| 1109 | 1131 | 	flex-grow: 1; |
| 1110 | 	margin-bottom: 4px; | |
| 1111 | 1132 | } |
| 1112 | 1133 | .src nav.sub { |
| 1113 | 1134 | 	margin: 0 0 -10px 0; |
| ... | ... | @@ -1208,27 +1229,14 @@ table, |
| 1208 | 1229 | 	margin-left: 0; |
| 1209 | 1230 | } |
| 1210 | 1231 | |
| 1211 | .search-results-title { | |
| 1212 | 	margin-top: 0; | |
| 1213 | 	white-space: nowrap; | |
| 1214 | 	/* flex layout allows shrinking the <select> appropriately if it becomes too large */ | |
| 1215 | 	display: flex; | |
| 1216 | 	/* make things look like in a line, despite the fact that we're using a layout | |
| 1217 | 	with boxes (i.e. from the flex layout) */ | |
| 1218 | 	align-items: baseline; | |
| 1219 | } | |
| 1220 | .search-results-title + .sub-heading { | |
| 1221 | 	color: var(--main-color); | |
| 1222 | 	display: flex; | |
| 1223 | 	align-items: baseline; | |
| 1224 | 	white-space: nowrap; | |
| 1225 | } | |
| 1226 | 1232 | #crate-search-div { |
| 1227 | 1233 | 	/* ensures that 100% in properties of #crate-search-div:after |
| 1228 | 1234 | 	are relative to the size of this div */ |
| 1229 | 1235 | 	position: relative; |
| 1230 | 1236 | 	/* allows this div (and with it the <select>-element "#crate-search") to be shrunk */ |
| 1231 | 1237 | 	min-width: 0; |
| 1238 | 	/* keep label text for switcher from moving down when this appears */ | |
| 1239 | 	margin-top: -1px; | |
| 1232 | 1240 | } |
| 1233 | 1241 | #crate-search { |
| 1234 | 1242 | 	padding: 0 23px 0 4px; |
| ... | ... | @@ -1294,6 +1302,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ |
| 1294 | 1302 | 	flex-grow: 1; |
| 1295 | 1303 | 	background-color: var(--button-background-color); |
| 1296 | 1304 | 	color: var(--search-color); |
| 1305 | 	max-width: 100%; | |
| 1297 | 1306 | } |
| 1298 | 1307 | .search-input:focus { |
| 1299 | 1308 | 	border-color: var(--search-input-focused-border-color); |
| ... | ... | @@ -1459,14 +1468,14 @@ so that we can apply CSS-filters to change the arrow color in themes */ |
| 1459 | 1468 | } |
| 1460 | 1469 | |
| 1461 | 1470 | #settings.popover { |
| 1462 | 	--popover-arrow-offset: 202px; | |
| 1471 | 	--popover-arrow-offset: 196px; | |
| 1463 | 1472 | 	top: calc(100% - 16px); |
| 1464 | 1473 | } |
| 1465 | 1474 | |
| 1466 | 1475 | /* use larger max-width for help popover, but not for help.html */ |
| 1467 | 1476 | #help.popover { |
| 1468 | 1477 | 	max-width: 600px; |
| 1469 | 	--popover-arrow-offset: 118px; | |
| 1478 | 	--popover-arrow-offset: 115px; | |
| 1470 | 1479 | 	top: calc(100% - 16px); |
| 1471 | 1480 | } |
| 1472 | 1481 | |
| ... | ... | @@ -1929,10 +1938,12 @@ a.tooltip:hover::after { |
| 1929 | 1938 | 	color: inherit; |
| 1930 | 1939 | } |
| 1931 | 1940 | #search-tabs button:not(.selected) { |
| 1941 | 	--search-tab-button-background: var(--search-tab-button-not-selected-background); | |
| 1932 | 1942 | 	background-color: var(--search-tab-button-not-selected-background); |
| 1933 | 1943 | 	border-top-color: var(--search-tab-button-not-selected-border-top-color); |
| 1934 | 1944 | } |
| 1935 | 1945 | #search-tabs button:hover, #search-tabs button.selected { |
| 1946 | 	--search-tab-button-background: var(--search-tab-button-selected-background); | |
| 1936 | 1947 | 	background-color: var(--search-tab-button-selected-background); |
| 1937 | 1948 | 	border-top-color: var(--search-tab-button-selected-border-top-color); |
| 1938 | 1949 | } |
| ... | ... | @@ -1941,6 +1952,73 @@ a.tooltip:hover::after { |
| 1941 | 1952 | 	font-size: 1rem; |
| 1942 | 1953 | 	font-variant-numeric: tabular-nums; |
| 1943 | 1954 | 	color: var(--search-tab-title-count-color); |
| 1955 | 	position: relative; | |
| 1956 | } | |
| 1957 | ||
| 1958 | #search-tabs .count.loading { | |
| 1959 | 	color: transparent; | |
| 1960 | } | |
| 1961 | ||
| 1962 | .search-form.loading { | |
| 1963 | 	--search-tab-button-background: var(--button-background-color); | |
| 1964 | } | |
| 1965 | ||
| 1966 | #search-tabs .count.loading::before, | |
| 1967 | .search-form.loading::before | |
| 1968 | { | |
| 1969 | 	width: 16px; | |
| 1970 | 	height: 16px; | |
| 1971 | 	border-radius: 16px; | |
| 1972 | 	background: radial-gradient( | |
| 1973 | 		var(--search-tab-button-background) 0 50%, | |
| 1974 | 		transparent 50% 100% | |
| 1975 | 	), conic-gradient( | |
| 1976 | 		var(--code-highlight-kw-color) 0deg 30deg, | |
| 1977 | 		var(--code-highlight-prelude-color) 30deg 60deg, | |
| 1978 | 		var(--code-highlight-number-color) 90deg 120deg, | |
| 1979 | 		var(--code-highlight-lifetime-color ) 120deg 150deg, | |
| 1980 | 		var(--code-highlight-comment-color) 150deg 180deg, | |
| 1981 | 		var(--code-highlight-self-color) 180deg 210deg, | |
| 1982 | 		var(--code-highlight-attribute-color) 210deg 240deg, | |
| 1983 | 		var(--code-highlight-literal-color) 210deg 240deg, | |
| 1984 | 		var(--code-highlight-macro-color) 240deg 270deg, | |
| 1985 | 		var(--code-highlight-question-mark-color) 270deg 300deg, | |
| 1986 | 		var(--code-highlight-prelude-val-color) 300deg 330deg, | |
| 1987 | 		var(--code-highlight-doc-comment-color) 330deg 360deg | |
| 1988 | 	); | |
| 1989 | 	content: ""; | |
| 1990 | 	position: absolute; | |
| 1991 | 	left: 2px; | |
| 1992 | 	top: 2px; | |
| 1993 | 	animation: rotating 1.25s linear infinite; | |
| 1994 | } | |
| 1995 | #search-tabs .count.loading::after, | |
| 1996 | .search-form.loading::after | |
| 1997 | { | |
| 1998 | 	width: 18px; | |
| 1999 | 	height: 18px; | |
| 2000 | 	border-radius: 18px; | |
| 2001 | 	background: conic-gradient( | |
| 2002 | 		var(--search-tab-button-background) 0deg 180deg, | |
| 2003 | 		transparent 270deg 360deg | |
| 2004 | 	); | |
| 2005 | 	content: ""; | |
| 2006 | 	position: absolute; | |
| 2007 | 	left: 1px; | |
| 2008 | 	top: 1px; | |
| 2009 | 	animation: rotating 0.66s linear infinite; | |
| 2010 | } | |
| 2011 | ||
| 2012 | .search-form.loading::before { | |
| 2013 | 	left: auto; | |
| 2014 | 	right: 9px; | |
| 2015 | 	top: 8px; | |
| 2016 | } | |
| 2017 | ||
| 2018 | .search-form.loading::after { | |
| 2019 | 	left: auto; | |
| 2020 | 	right: 8px; | |
| 2021 | 	top: 8px; | |
| 1944 | 2022 | } |
| 1945 | 2023 | |
| 1946 | 2024 | #search .error code { |
| ... | ... | @@ -1974,7 +2052,7 @@ a.tooltip:hover::after { |
| 1974 | 2052 | 	border-bottom: 1px solid var(--border-color); |
| 1975 | 2053 | } |
| 1976 | 2054 | |
| 1977 | #settings-menu, #help-button, button#toggle-all-docs { | |
| 2055 | #search-button, .settings-menu, .help-menu, button#toggle-all-docs { | |
| 1978 | 2056 | 	margin-left: var(--button-left-margin); |
| 1979 | 2057 | 	display: flex; |
| 1980 | 2058 | 	line-height: 1.25; |
| ... | ... | @@ -1989,69 +2067,100 @@ a.tooltip:hover::after { |
| 1989 | 2067 | 	display: flex; |
| 1990 | 2068 | 	margin-right: 4px; |
| 1991 | 2069 | 	position: fixed; |
| 2070 | 	margin-top: 25px; | |
| 2071 | 	left: 6px; | |
| 1992 | 2072 | 	height: 34px; |
| 1993 | 2073 | 	width: 34px; |
| 2074 | 	z-index: calc(var(--desktop-sidebar-z-index) + 1); | |
| 1994 | 2075 | } |
| 1995 | 2076 | .hide-sidebar #sidebar-button { |
| 1996 | 2077 | 	left: 6px; |
| 1997 | 2078 | 	background-color: var(--main-background-color); |
| 1998 | 	z-index: 1; | |
| 1999 | 2079 | } |
| 2000 | 2080 | .src #sidebar-button { |
| 2081 | 	margin-top: 0; | |
| 2082 | 	top: 8px; | |
| 2001 | 2083 | 	left: 8px; |
| 2002 | 	z-index: calc(var(--desktop-sidebar-z-index) + 1); | |
| 2084 | 	border-color: var(--border-color); | |
| 2003 | 2085 | } |
| 2004 | 2086 | .hide-sidebar .src #sidebar-button { |
| 2005 | 2087 | 	position: static; |
| 2006 | 2088 | } |
| 2007 | #settings-menu > a, #help-button > a, #sidebar-button > a, button#toggle-all-docs { | |
| 2089 | #search-button > a, | |
| 2090 | .settings-menu > a, | |
| 2091 | .help-menu > a, | |
| 2092 | #sidebar-button > a, | |
| 2093 | button#toggle-all-docs { | |
| 2008 | 2094 | 	display: flex; |
| 2009 | 2095 | 	align-items: center; |
| 2010 | 2096 | 	justify-content: center; |
| 2011 | 2097 | 	flex-direction: column; |
| 2012 | 2098 | } |
| 2013 | #settings-menu > a, #help-button > a, button#toggle-all-docs { | |
| 2099 | #search-button > a, | |
| 2100 | .settings-menu > a, | |
| 2101 | .help-menu > a, | |
| 2102 | button#toggle-all-docs { | |
| 2014 | 2103 | 	border: 1px solid transparent; |
| 2015 | 2104 | 	border-radius: var(--button-border-radius); |
| 2016 | 2105 | 	color: var(--main-color); |
| 2017 | 2106 | } |
| 2018 | #settings-menu > a, #help-button > a, button#toggle-all-docs { | |
| 2107 | #search-button > a, .settings-menu > a, .help-menu > a, button#toggle-all-docs { | |
| 2019 | 2108 | 	width: 80px; |
| 2020 | 2109 | 	border-radius: var(--toolbar-button-border-radius); |
| 2021 | 2110 | } |
| 2022 | #settings-menu > a, #help-button > a { | |
| 2111 | #search-button > a, .settings-menu > a, .help-menu > a { | |
| 2023 | 2112 | 	min-width: 0; |
| 2024 | 2113 | } |
| 2025 | 2114 | #sidebar-button > a { |
| 2026 | 	background-color: var(--sidebar-background-color); | |
| 2115 | 	border: solid 1px transparent; | |
| 2116 | 	border-radius: var(--button-border-radius); | |
| 2117 | 	background-color: var(--button-background-color); | |
| 2027 | 2118 | 	width: 33px; |
| 2028 | 2119 | } |
| 2029 | #sidebar-button > a:hover, #sidebar-button > a:focus-visible { | |
| 2030 | 	background-color: var(--main-background-color); | |
| 2120 | .src #sidebar-button > a { | |
| 2121 | 	background-color: var(--sidebar-background-color); | |
| 2122 | 	border-color: var(--border-color); | |
| 2031 | 2123 | } |
| 2032 | 2124 | |
| 2033 | #settings-menu > a:hover, #settings-menu > a:focus-visible, | |
| 2034 | #help-button > a:hover, #help-button > a:focus-visible, | |
| 2125 | #search-button > a:hover, #search-button > a:focus-visible, | |
| 2126 | .settings-menu > a:hover, .settings-menu > a:focus-visible, | |
| 2127 | .help-menu > a:hover, #help-menu > a:focus-visible, | |
| 2128 | #sidebar-button > a:hover, #sidebar-button > a:focus-visible, | |
| 2129 | #copy-path:hover, #copy-path:focus-visible, | |
| 2035 | 2130 | button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible { |
| 2036 | 2131 | 	border-color: var(--settings-button-border-focus); |
| 2037 | 2132 | 	text-decoration: none; |
| 2038 | 2133 | } |
| 2039 | 2134 | |
| 2040 | #settings-menu > a::before { | |
| 2135 | #search-button > a::before { | |
| 2136 | 	/* Magnifying glass */ | |
| 2137 | 	content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \ | |
| 2138 | 		width="18" height="18" viewBox="0 0 16 16">\ | |
| 2139 | 	 <circle r="5" cy="7" cx="7" style="fill:none;stroke:black;stroke-width:3"/><path \ | |
| 2140 | 	 d="M14.5,14.5 12,12" style="fill:none;stroke:black;stroke-width:3;stroke-linecap:round">\ | |
| 2141 | 	 </path><desc>Search</desc>\ | |
| 2142 | 	 </svg>'); | |
| 2143 | 	width: 18px; | |
| 2144 | 	height: 18px; | |
| 2145 | 	filter: var(--settings-menu-filter); | |
| 2146 | } | |
| 2147 | ||
| 2148 | .settings-menu > a::before { | |
| 2041 | 2149 | 	/* Wheel <https://www.svgrepo.com/svg/384069/settings-cog-gear> */ |
| 2042 | 2150 | 	content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \ |
| 2043 | 2151 | 	enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg">\ |
| 2044 | 	<path d="M10.25,6c0-0.1243286-0.0261841-0.241333-0.0366211-0.362915l1.6077881-1.5545654l\ | |
| 2045 | 	-1.25-2.1650391 c0,0-1.2674561,0.3625488-2.1323853,0.6099854c-0.2034912-0.1431885-0.421875\ | |
| 2046 | 	-0.2639771-0.6494751-0.3701782L7.25,0h-2.5 c0,0-0.3214111,1.2857666-0.5393066,2.1572876\ | |
| 2047 | 	C3.9830933,2.2634888,3.7647095,2.3842773,3.5612183,2.5274658L1.428833,1.9174805 \ | |
| 2048 | 	l-1.25,2.1650391c0,0,0.9641113,0.9321899,1.6077881,1.5545654C1.7761841,5.758667,\ | |
| 2049 | 	1.75,5.8756714,1.75,6 s0.0261841,0.241333,0.0366211,0.362915L0.178833,7.9174805l1.25,\ | |
| 2050 | 	2.1650391l2.1323853-0.6099854 c0.2034912,0.1432495,0.421875,0.2639771,0.6494751,0.3701782\ | |
| 2051 | 	L4.75,12h2.5l0.5393066-2.1572876 c0.2276001-0.1062012,0.4459839-0.2269287,0.6494751\ | |
| 2052 | 	-0.3701782l2.1323853,0.6099854l1.25-2.1650391L10.2133789,6.362915 C10.2238159,6.241333,\ | |
| 2053 | 	10.25,6.1243286,10.25,6z M6,7.5C5.1715698,7.5,4.5,6.8284302,4.5,6S5.1715698,4.5,6,4.5S7.5\ | |
| 2054 | 	,5.1715698,7.5,6 S6.8284302,7.5,6,7.5z" fill="black"/></svg>'); | |
| 2152 | 	<path d="m4.75 0s-0.32117 1.286-0.53906 2.1576c-0.2276 0.1062-0.44625 \ | |
| 2153 | 	0.2266-0.64974 0.36979l-2.1328-0.60938-1.25 2.1641s0.9644 0.93231 1.6081 1.5547c-0.010437 \ | |
| 2154 | 	0.12158-0.036458 0.23895-0.036458 0.36328s0.026021 0.2417 0.036458 0.36328l-1.6081 \ | |
| 2155 | 	1.5547 1.25 2.1641 2.1328-0.60937c0.20349 0.14325 0.42214 0.26359 0.64974 0.36979l0.53906 \ | |
| 2156 | 	2.1576h2.5l0.53906-2.1576c0.2276-0.1062 0.44625-0.22654 0.64974-0.36979l2.1328 0.60937 \ | |
| 2157 | 	1.25-2.1641-1.6081-1.5547c0.010437-0.12158 0.036458-0.23895 \ | |
| 2158 | 	0.036458-0.36328s-0.02602-0.2417-0.03646-0.36328l1.6081-1.5547-1.25-2.1641s-1.2679 \ | |
| 2159 | 	0.36194-2.1328 0.60938c-0.20349-0.14319-0.42214-0.26359-0.64974-0.36979l-0.53906-2.1576\ | |
| 2160 | 	zm1.25 2.5495c1.9058-2.877e-4 3.4508 1.5447 3.4505 3.4505 2.877e-4 1.9058-1.5447 3.4508-3.4505 \ | |
| 2161 | 	3.4505-1.9058 2.877e-4 -3.4508-1.5447-3.4505-3.4505-2.877e-4 -1.9058 1.5447-3.4508 \ | |
| 2162 | 	3.4505-3.4505z" fill="black"/>\ | |
| 2163 | 	<circle cx="6" cy="6" r="1.75" fill="none" stroke="black" stroke-width="1"/></svg>'); | |
| 2055 | 2164 | 	width: 18px; |
| 2056 | 2165 | 	height: 18px; |
| 2057 | 2166 | 	filter: var(--settings-menu-filter); |
| ... | ... | @@ -2067,36 +2176,51 @@ button#toggle-all-docs::before { |
| 2067 | 2176 | 	filter: var(--settings-menu-filter); |
| 2068 | 2177 | } |
| 2069 | 2178 | |
| 2070 | button#toggle-all-docs.will-expand::before { | |
| 2071 | 	/* Custom arrow icon */ | |
| 2072 | 	content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \ | |
| 2073 | 	enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg">\ | |
| 2074 | 	<path d="M2,5l4,-4l4,4M2,7l4,4l4,-4" stroke="black" fill="none" stroke-width="2px"/></svg>'); | |
| 2075 | } | |
| 2076 | ||
| 2077 | #help-button > a::before { | |
| 2078 | 	/* Question mark with circle */ | |
| 2079 | 	content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \ | |
| 2080 | 	enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg" fill="none">\ | |
| 2081 | 	<circle r="5.25" cx="6" cy="6" stroke-width="1.25" stroke="black"/>\ | |
| 2082 | 	<text x="6" y="7" style="font:8px sans-serif;font-weight:1000" text-anchor="middle" \ | |
| 2083 | 		dominant-baseline="middle" fill="black">?</text></svg>'); | |
| 2179 | .help-menu > a::before { | |
| 2180 | 	/* Question mark with "circle" */ | |
| 2181 | 	content: url('data:image/svg+xml,\ | |
| 2182 | 		<svg width="18" height="18" enable-background="new 0 0 12 12" fill="none" \ | |
| 2183 | 		version="1.1" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"> \ | |
| 2184 | 		<path d="m6.007 0.6931c2.515 0 5.074 1.908 5.074 5.335 0 3.55-2.567 5.278-5.088 \ | |
| 2185 | 		5.278-2.477 0-5.001-1.742-5.001-5.3 0-3.38 2.527-5.314 5.014-5.314z" stroke="black" \ | |
| 2186 | 		stroke-width="1.5"/>\ | |
| 2187 | 		<path d="m5.999 7.932c0.3111 0 0.7062 0.2915 0.7062 0.7257 0 0.5458-0.3951 \ | |
| 2188 | 		0.8099-0.7081 0.8099-0.2973 0-0.7023-0.266-0.7023-0.7668 0-0.4695 0.3834-0.7688 \ | |
| 2189 | 		0.7042-0.7688z" fill="black"/>\ | |
| 2190 | 		<path d="m4.281 3.946c0.0312-0.03057 0.06298-0.06029 0.09528-0.08916 0.4833-0.432 1.084-0.6722 \ | |
| 2191 | 		1.634-0.6722 1.141 0 1.508 1.043 1.221 1.621-0.2753 0.5542-1.061 0.5065-1.273 \ | |
| 2192 | 		1.595-0.05728 0.2939 0.0134 0.9812 0.0134 1.205" fill="none" stroke="black" \ | |
| 2193 | 		stroke-width="1.25"/>\ | |
| 2194 | 		</svg>'); | |
| 2084 | 2195 | 	width: 18px; |
| 2085 | 2196 | 	height: 18px; |
| 2086 | 2197 | 	filter: var(--settings-menu-filter); |
| 2087 | 2198 | } |
| 2088 | 2199 | |
| 2200 | /* design hack to cope with "Help" being far shorter than "Settings" etc */ | |
| 2201 | .help-menu > a { | |
| 2202 | 	width: 74px; | |
| 2203 | } | |
| 2204 | .help-menu > a > .label { | |
| 2205 | 	padding-right: 1px; | |
| 2206 | } | |
| 2207 | #toggle-all-docs:not(.will-expand) > .label { | |
| 2208 | 	padding-left: 1px; | |
| 2209 | } | |
| 2210 | ||
| 2211 | #search-button > a::before, | |
| 2089 | 2212 | button#toggle-all-docs::before, |
| 2090 | #help-button > a::before, | |
| 2091 | #settings-menu > a::before { | |
| 2213 | .help-menu > a::before, | |
| 2214 | .settings-menu > a::before { | |
| 2092 | 2215 | 	filter: var(--settings-menu-filter); |
| 2093 | 2216 | 	margin: 8px; |
| 2094 | 2217 | } |
| 2095 | 2218 | |
| 2096 | 2219 | @media not (pointer: coarse) { |
| 2220 | 	#search-button > a:hover::before, | |
| 2097 | 2221 | 	button#toggle-all-docs:hover::before, |
| 2098 | 	#help-button > a:hover::before, | |
| 2099 | 	#settings-menu > a:hover::before { | |
| 2222 | 	.help-menu > a:hover::before, | |
| 2223 | 	.settings-menu > a:hover::before { | |
| 2100 | 2224 | 		filter: var(--settings-menu-hover-filter); |
| 2101 | 2225 | 	} |
| 2102 | 2226 | } |
| ... | ... | @@ -2122,9 +2246,9 @@ rustdoc-toolbar span.label { |
| 2122 | 2246 | 	/* sidebar resizer image */ |
| 2123 | 2247 | 	content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" \ |
| 2124 | 2248 | 		fill="none" stroke="black">\ |
| 2125 | 		<rect x="1" y="1" width="20" height="20" ry="1.5" stroke-width="1.5" stroke="%23777"/>\ | |
| 2126 | 		<circle cx="4.375" cy="4.375" r="1" stroke-width=".75"/>\ | |
| 2127 | 		<path d="m7.6121 3v16 M5.375 7.625h-2 m2 3h-2 m2 3h-2" stroke-width="1.25"/></svg>'); | |
| 2249 | 		<rect x="1" y="2" width="20" height="18" ry="1.5" stroke-width="1.5" stroke="%23777"/>\ | |
| 2250 | 		<circle cx="4.375" cy="5.375" r="1" stroke-width=".75"/>\ | |
| 2251 | 		<path d="m7.6121 4v14 M5.375 8.625h-2 m2 3h-2 m2 3h-2" stroke-width="1.25"/></svg>'); | |
| 2128 | 2252 | 	width: 22px; |
| 2129 | 2253 | 	height: 22px; |
| 2130 | 2254 | } |
| ... | ... | @@ -2137,7 +2261,8 @@ rustdoc-toolbar span.label { |
| 2137 | 2261 | 	margin-left: 10px; |
| 2138 | 2262 | 	padding: 0; |
| 2139 | 2263 | 	padding-left: 2px; |
| 2140 | 	border: 0; | |
| 2264 | 	border: solid 1px transparent; | |
| 2265 | 	border-radius: var(--button-border-radius); | |
| 2141 | 2266 | 	font-size: 0; |
| 2142 | 2267 | } |
| 2143 | 2268 | #copy-path::before { |
| ... | ... | @@ -2159,7 +2284,7 @@ rustdoc-toolbar span.label { |
| 2159 | 2284 | 		transform: rotate(360deg); |
| 2160 | 2285 | 	} |
| 2161 | 2286 | } |
| 2162 | #settings-menu.rotate > a img { | |
| 2287 | .settings-menu.rotate > a img { | |
| 2163 | 2288 | 	animation: rotating 2s linear infinite; |
| 2164 | 2289 | } |
| 2165 | 2290 | |
| ... | ... | @@ -2402,6 +2527,9 @@ However, it's not needed with smaller screen width because the doc/code block is |
| 2402 | 2527 | 	opacity: 0.75; |
| 2403 | 2528 | 	filter: var(--mobile-sidebar-menu-filter); |
| 2404 | 2529 | } |
| 2530 | .src #sidebar-button > a:hover { | |
| 2531 | 	background: var(--main-background-color); | |
| 2532 | } | |
| 2405 | 2533 | .sidebar-menu-toggle:hover::before, |
| 2406 | 2534 | .sidebar-menu-toggle:active::before, |
| 2407 | 2535 | .sidebar-menu-toggle:focus::before { |
| ... | ... | @@ -2410,8 +2538,8 @@ However, it's not needed with smaller screen width because the doc/code block is |
| 2410 | 2538 | |
| 2411 | 2539 | /* Media Queries */ |
| 2412 | 2540 | |
| 2413 | /* Make sure all the buttons line wrap at the same time */ | |
| 2414 | 2541 | @media (max-width: 850px) { |
| 2542 | 	/* Make sure all the buttons line wrap at the same time */ | |
| 2415 | 2543 | 	#search-tabs .count { |
| 2416 | 2544 | 		display: block; |
| 2417 | 2545 | 	} |
| ... | ... | @@ -2421,6 +2549,81 @@ However, it's not needed with smaller screen width because the doc/code block is |
| 2421 | 2549 | 	.side-by-side > div { |
| 2422 | 2550 | 		width: auto; |
| 2423 | 2551 | 	} |
| 2552 | ||
| 2553 | 	/* Text label takes up too much space at this size. */ | |
| 2554 | 	.main-heading { | |
| 2555 | 		grid-template-areas: | |
| 2556 | 			"main-heading-breadcrumbs main-heading-toolbar" | |
| 2557 | 			"main-heading-h1 main-heading-toolbar" | |
| 2558 | 			"main-heading-sub-heading main-heading-toolbar"; | |
| 2559 | 	} | |
| 2560 | 	.search-results-main-heading { | |
| 2561 | 		display: grid; | |
| 2562 | 		grid-template-areas: | |
| 2563 | 			"main-heading-breadcrumbs main-heading-toolbar" | |
| 2564 | 			"main-heading-breadcrumbs main-heading-toolbar" | |
| 2565 | 			"main-heading-h1 main-heading-toolbar"; | |
| 2566 | 	} | |
| 2567 | 	rustdoc-toolbar { | |
| 2568 | 		margin-top: -10px; | |
| 2569 | 		display: grid; | |
| 2570 | 		grid-template-areas: | |
| 2571 | 			"x settings help" | |
| 2572 | 			"search summary summary"; | |
| 2573 | 		grid-template-rows: 35px 1fr; | |
| 2574 | 	} | |
| 2575 | 	.search-results-main-heading rustdoc-toolbar { | |
| 2576 | 		display: grid; | |
| 2577 | 		grid-template-areas: | |
| 2578 | 			"settings help" | |
| 2579 | 			"search search"; | |
| 2580 | 	} | |
| 2581 | 	.search-results-main-heading #toggle-all-docs { | |
| 2582 | 		display: none; | |
| 2583 | 	} | |
| 2584 | 	rustdoc-toolbar .settings-menu span.label, | |
| 2585 | 	rustdoc-toolbar .help-menu span.label | |
| 2586 | 	{ | |
| 2587 | 		display: none; | |
| 2588 | 	} | |
| 2589 | 	rustdoc-toolbar .settings-menu { | |
| 2590 | 		grid-area: settings; | |
| 2591 | 	} | |
| 2592 | 	rustdoc-toolbar .help-menu { | |
| 2593 | 		grid-area: help; | |
| 2594 | 	} | |
| 2595 | 	rustdoc-toolbar .settings-menu { | |
| 2596 | 		grid-area: settings; | |
| 2597 | 	} | |
| 2598 | 	rustdoc-toolbar #search-button { | |
| 2599 | 		grid-area: search; | |
| 2600 | 	} | |
| 2601 | 	rustdoc-toolbar #toggle-all-docs { | |
| 2602 | 		grid-area: summary; | |
| 2603 | 	} | |
| 2604 | 	rustdoc-toolbar .settings-menu, | |
| 2605 | 	rustdoc-toolbar .help-menu { | |
| 2606 | 		height: 35px; | |
| 2607 | 	} | |
| 2608 | 	rustdoc-toolbar .settings-menu > a, | |
| 2609 | 	rustdoc-toolbar .help-menu > a { | |
| 2610 | 		border-radius: 2px; | |
| 2611 | 		text-align: center; | |
| 2612 | 		width: 34px; | |
| 2613 | 		padding: 5px 0; | |
| 2614 | 	} | |
| 2615 | 	rustdoc-toolbar .settings-menu > a:before, | |
| 2616 | 	rustdoc-toolbar .help-menu > a:before { | |
| 2617 | 		margin: 0 4px; | |
| 2618 | 	} | |
| 2619 | 	#settings.popover { | |
| 2620 | 		top: 16px; | |
| 2621 | 		--popover-arrow-offset: 58px; | |
| 2622 | 	} | |
| 2623 | 	#help.popover { | |
| 2624 | 		top: 16px; | |
| 2625 | 		--popover-arrow-offset: 16px; | |
| 2626 | 	} | |
| 2424 | 2627 | } |
| 2425 | 2628 | |
| 2426 | 2629 | /* |
| ... | ... | @@ -2435,7 +2638,7 @@ in src-script.js and main.js |
| 2435 | 2638 | |
| 2436 | 2639 | 	/* When linking to an item with an `id` (for instance, by clicking a link in the sidebar, |
| 2437 | 2640 | 	 or visiting a URL with a fragment like `#method.new`, we don't want the item to be obscured |
| 2438 | 	 by the topbar. Anything with an `id` gets scroll-margin-top equal to .mobile-topbar's size. | |
| 2641 | 	 by the topbar. Anything with an `id` gets scroll-margin-top equal to rustdoc-topbar's size. | |
| 2439 | 2642 | 	*/ |
| 2440 | 2643 | 	*[id] { |
| 2441 | 2644 | 		scroll-margin-top: 45px; |
| ... | ... | @@ -2451,18 +2654,32 @@ in src-script.js and main.js |
| 2451 | 2654 | 		visibility: hidden; |
| 2452 | 2655 | 	} |
| 2453 | 2656 | |
| 2454 | 	/* Text label takes up too much space at this size. */ | |
| 2455 | 	rustdoc-toolbar span.label { | |
| 2657 | ||
| 2658 | 	/* Pull settings and help up into the top bar. */ | |
| 2659 | 	rustdoc-topbar span.label, | |
| 2660 | 	html:not(.hide-sidebar) .rustdoc:not(.src) rustdoc-toolbar .settings-menu > a, | |
| 2661 | 	html:not(.hide-sidebar) .rustdoc:not(.src) rustdoc-toolbar .help-menu > a | |
| 2662 | 	{ | |
| 2456 | 2663 | 		display: none; |
| 2457 | 2664 | 	} |
| 2458 | 	#settings-menu > a, #help-button > a, button#toggle-all-docs { | |
| 2665 | 	rustdoc-topbar .settings-menu > a, | |
| 2666 | 	rustdoc-topbar .help-menu > a { | |
| 2459 | 2667 | 		width: 33px; |
| 2668 | 		line-height: 0; | |
| 2669 | 	} | |
| 2670 | 	rustdoc-topbar .settings-menu > a:hover, | |
| 2671 | 	rustdoc-topbar .help-menu > a:hover { | |
| 2672 | 		border: none; | |
| 2673 | 		background: var(--main-background-color); | |
| 2674 | 		border-radius: 0; | |
| 2460 | 2675 | 	} |
| 2461 | 2676 | 	#settings.popover { |
| 2462 | 		--popover-arrow-offset: 86px; | |
| 2677 | 		top: 32px; | |
| 2678 | 		--popover-arrow-offset: 48px; | |
| 2463 | 2679 | 	} |
| 2464 | 2680 | 	#help.popover { |
| 2465 | 		--popover-arrow-offset: 48px; | |
| 2681 | 		top: 32px; | |
| 2682 | 		--popover-arrow-offset: 12px; | |
| 2466 | 2683 | 	} |
| 2467 | 2684 | |
| 2468 | 2685 | 	.rustdoc { |
| ... | ... | @@ -2471,13 +2688,13 @@ in src-script.js and main.js |
| 2471 | 2688 | 		display: block; |
| 2472 | 2689 | 	} |
| 2473 | 2690 | |
| 2474 | 	main { | |
| 2691 | 	html:not(.hide-sidebar) main { | |
| 2475 | 2692 | 		padding-left: 15px; |
| 2476 | 2693 | 		padding-top: 0px; |
| 2477 | 2694 | 	} |
| 2478 | 2695 | |
| 2479 | 2696 | 	/* Hide the logo and item name from the sidebar. Those are displayed |
| 2480 | 	 in the mobile-topbar instead. */ | |
| 2697 | 	 in the rustdoc-topbar instead. */ | |
| 2481 | 2698 | 	.sidebar .logo-container, |
| 2482 | 2699 | 	.sidebar .location, |
| 2483 | 2700 | 	.sidebar-resizer { |
| ... | ... | @@ -2510,6 +2727,9 @@ in src-script.js and main.js |
| 2510 | 2727 | 		height: 100vh; |
| 2511 | 2728 | 		border: 0; |
| 2512 | 2729 | 	} |
| 2730 | 	html .src main { | |
| 2731 | 		padding: 18px 0; | |
| 2732 | 	} | |
| 2513 | 2733 | 	.src .search-form { |
| 2514 | 2734 | 		margin-left: 40px; |
| 2515 | 2735 | 	} |
| ... | ... | @@ -2529,9 +2749,9 @@ in src-script.js and main.js |
| 2529 | 2749 | 		left: 0; |
| 2530 | 2750 | 	} |
| 2531 | 2751 | |
| 2532 | 	.mobile-topbar h2 { | |
| 2752 | 	rustdoc-topbar > h2 { | |
| 2533 | 2753 | 		padding-bottom: 0; |
| 2534 | 		margin: auto 0.5em auto auto; | |
| 2754 | 		margin: auto; | |
| 2535 | 2755 | 		overflow: hidden; |
| 2536 | 2756 | 		/* Rare exception to specifying font sizes in rem. Since the topbar |
| 2537 | 2757 | 		 height is specified in pixels, this also has to be specified in |
| ... | ... | @@ -2540,32 +2760,34 @@ in src-script.js and main.js |
| 2540 | 2760 | 		font-size: 24px; |
| 2541 | 2761 | 		white-space: nowrap; |
| 2542 | 2762 | 		text-overflow: ellipsis; |
| 2763 | 		text-align: center; | |
| 2543 | 2764 | 	} |
| 2544 | 2765 | |
| 2545 | 	.mobile-topbar .logo-container > img { | |
| 2766 | 	rustdoc-topbar .logo-container > img { | |
| 2546 | 2767 | 		max-width: 35px; |
| 2547 | 2768 | 		max-height: 35px; |
| 2548 | 2769 | 		margin: 5px 0 5px 20px; |
| 2549 | 2770 | 	} |
| 2550 | 2771 | |
| 2551 | 	.mobile-topbar { | |
| 2772 | 	rustdoc-topbar { | |
| 2552 | 2773 | 		display: flex; |
| 2553 | 2774 | 		flex-direction: row; |
| 2554 | 2775 | 		position: sticky; |
| 2555 | 2776 | 		z-index: 10; |
| 2556 | 		font-size: 2rem; | |
| 2557 | 2777 | 		height: 45px; |
| 2558 | 2778 | 		width: 100%; |
| 2559 | 2779 | 		left: 0; |
| 2560 | 2780 | 		top: 0; |
| 2561 | 2781 | 	} |
| 2562 | 2782 | |
| 2563 | 	.hide-sidebar .mobile-topbar { | |
| 2783 | 	.hide-sidebar rustdoc-topbar { | |
| 2564 | 2784 | 		display: none; |
| 2565 | 2785 | 	} |
| 2566 | 2786 | |
| 2567 | 2787 | 	.sidebar-menu-toggle { |
| 2568 | 		width: 45px; | |
| 2788 | 		/* prevent flexbox shrinking */ | |
| 2789 | 		width: 41px; | |
| 2790 | 		min-width: 41px; | |
| 2569 | 2791 | 		border: none; |
| 2570 | 2792 | 		line-height: 0; |
| 2571 | 2793 | 	} |
| ... | ... | @@ -2591,9 +2813,13 @@ in src-script.js and main.js |
| 2591 | 2813 | 	#sidebar-button > a::before { |
| 2592 | 2814 | 		content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \ |
| 2593 | 2815 | 			viewBox="0 0 22 22" fill="none" stroke="black">\ |
| 2594 | 			<rect x="1" y="1" width="20" height="20" ry="1.5" stroke-width="1.5" stroke="%23777"/>\ | |
| 2595 | 			<circle cx="4.375" cy="4.375" r="1" stroke-width=".75"/>\ | |
| 2596 | 			<path d="m3 7.375h16m0-3h-4" stroke-width="1.25"/></svg>'); | |
| 2816 | 			<rect x="1" y="2" width="20" height="18" ry="1.5" stroke-width="1.5" stroke="%23777"/>\ | |
| 2817 | 			<g fill="black" stroke="none">\ | |
| 2818 | 			<circle cx="4.375" cy="5.375" r="1" stroke-width=".75"/>\ | |
| 2819 | 			<circle cx="17.375" cy="5.375" r="1" stroke-width=".75"/>\ | |
| 2820 | 			<circle cx="14.375" cy="5.375" r="1" stroke-width=".75"/>\ | |
| 2821 | 			</g>\ | |
| 2822 | 			<path d="m3 8.375h16" stroke-width="1.25"/></svg>'); | |
| 2597 | 2823 | 		width: 22px; |
| 2598 | 2824 | 		height: 22px; |
| 2599 | 2825 | 	} |
| ... | ... | @@ -3283,7 +3509,7 @@ Original by Dempfi (https://github.com/dempfi/ayu) |
| 3283 | 3509 | 	border-bottom: 1px solid rgba(242, 151, 24, 0.3); |
| 3284 | 3510 | } |
| 3285 | 3511 | |
| 3286 | :root[data-theme="ayu"] #settings-menu > a img, | |
| 3512 | :root[data-theme="ayu"] .settings-menu > a img, | |
| 3287 | 3513 | :root[data-theme="ayu"] #sidebar-button > a::before { |
| 3288 | 3514 | 	filter: invert(100); |
| 3289 | 3515 | } |
src/librustdoc/html/static/js/main.js+244-141| ... | ... | @@ -54,23 +54,6 @@ function showMain() { |
| 54 | 54 | window.rootPath = getVar("root-path"); |
| 55 | 55 | window.currentCrate = getVar("current-crate"); |
| 56 | 56 | |
| 57 | function setMobileTopbar() { | |
| 58 | // FIXME: It would be nicer to generate this text content directly in HTML, | |
| 59 | // but with the current code it's hard to get the right information in the right place. | |
| 60 | const mobileTopbar = document.querySelector(".mobile-topbar"); | |
| 61 | const locationTitle = document.querySelector(".sidebar h2.location"); | |
| 62 | if (mobileTopbar) { | |
| 63 | const mobileTitle = document.createElement("h2"); | |
| 64 | mobileTitle.className = "location"; | |
| 65 | if (hasClass(document.querySelector(".rustdoc"), "crate")) { | |
| 66 | mobileTitle.innerHTML = `Crate <a href="#">${window.currentCrate}</a>`; | |
| 67 | } else if (locationTitle) { | |
| 68 | mobileTitle.innerHTML = locationTitle.innerHTML; | |
| 69 | } | |
| 70 | mobileTopbar.appendChild(mobileTitle); | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | 57 | /** |
| 75 | 58 | * Gets the human-readable string for the virtual-key code of the |
| 76 | 59 | * given KeyboardEvent, ev. |
| ... | ... | @@ -84,6 +67,7 @@ function setMobileTopbar() { |
| 84 | 67 | * So I guess you could say things are getting pretty interoperable. |
| 85 | 68 | * |
| 86 | 69 | * @param {KeyboardEvent} ev |
| 70 | * @returns {string} | |
| 87 | 71 | */ |
| 88 | 72 | function getVirtualKey(ev) { |
| 89 | 73 | if ("key" in ev && typeof ev.key !== "undefined") { |
| ... | ... | @@ -98,18 +82,8 @@ function getVirtualKey(ev) { |
| 98 | 82 | } |
| 99 | 83 | |
| 100 | 84 | const MAIN_ID = "main-content"; |
| 101 | const SETTINGS_BUTTON_ID = "settings-menu"; | |
| 102 | 85 | const ALTERNATIVE_DISPLAY_ID = "alternative-display"; |
| 103 | 86 | const NOT_DISPLAYED_ID = "not-displayed"; |
| 104 | const HELP_BUTTON_ID = "help-button"; | |
| 105 | ||
| 106 | function getSettingsButton() { | |
| 107 | return document.getElementById(SETTINGS_BUTTON_ID); | |
| 108 | } | |
| 109 | ||
| 110 | function getHelpButton() { | |
| 111 | return document.getElementById(HELP_BUTTON_ID); | |
| 112 | } | |
| 113 | 87 | |
| 114 | 88 | // Returns the current URL without any query parameter or hash. |
| 115 | 89 | function getNakedUrl() { |
| ... | ... | @@ -174,7 +148,7 @@ function getNotDisplayedElem() { |
| 174 | 148 | * contains the displayed element (there can be only one at the same time!). So basically, we switch |
| 175 | 149 | * elements between the two `<section>` elements. |
| 176 | 150 | * |
| 177 | * @param {HTMLElement|null} elemToDisplay | |
| 151 | * @param {Element|null} elemToDisplay | |
| 178 | 152 | */ |
| 179 | 153 | function switchDisplayedElement(elemToDisplay) { |
| 180 | 154 | const el = getAlternativeDisplayElem(); |
| ... | ... | @@ -239,14 +213,14 @@ function preLoadCss(cssUrl) { |
| 239 | 213 | document.head.append(script); |
| 240 | 214 | } |
| 241 | 215 | |
| 242 | const settingsButton = getSettingsButton(); | |
| 243 | if (settingsButton) { | |
| 244 | settingsButton.onclick = event => { | |
| 216 | onEachLazy(document.querySelectorAll(".settings-menu"), settingsMenu => { | |
| 217 | /** @param {MouseEvent} event */ | |
| 218 | settingsMenu.querySelector("a").onclick = event => { | |
| 245 | 219 | if (event.ctrlKey || event.altKey || event.metaKey) { |
| 246 | 220 | return; |
| 247 | 221 | } |
| 248 | 222 | window.hideAllModals(false); |
| 249 | addClass(getSettingsButton(), "rotate"); | |
| 223 | addClass(settingsMenu, "rotate"); | |
| 250 | 224 | event.preventDefault(); |
| 251 | 225 | // Sending request for the CSS and the JS files at the same time so it will |
| 252 | 226 | // hopefully be loaded when the JS will generate the settings content. |
| ... | ... | @@ -268,15 +242,42 @@ function preLoadCss(cssUrl) { |
| 268 | 242 | } |
| 269 | 243 | }, 0); |
| 270 | 244 | }; |
| 271 | } | |
| 245 | }); | |
| 272 | 246 | |
| 273 | 247 | window.searchState = { |
| 274 | 248 | rustdocToolbar: document.querySelector("rustdoc-toolbar"), |
| 275 | 249 | loadingText: "Loading search results...", |
| 276 | // This will always be an HTMLInputElement, but tsc can't see that | |
| 277 | // @ts-expect-error | |
| 278 | input: document.getElementsByClassName("search-input")[0], | |
| 279 | outputElement: () => { | |
| 250 | inputElement: () => { | |
| 251 | let el = document.getElementsByClassName("search-input")[0]; | |
| 252 | if (!el) { | |
| 253 | const out = nonnull(nonnull(window.searchState.outputElement()).parentElement); | |
| 254 | const hdr = document.createElement("div"); | |
| 255 | hdr.className = "main-heading search-results-main-heading"; | |
| 256 | const params = window.searchState.getQueryStringParams(); | |
| 257 | const autofocusParam = params.search === "" ? "autofocus" : ""; | |
| 258 | hdr.innerHTML = `<nav class="sub"> | |
| 259 | <form class="search-form loading"> | |
| 260 | <span></span> <!-- This empty span is a hacky fix for Safari: see #93184 --> | |
| 261 | <input | |
| 262 | ${autofocusParam} | |
| 263 | class="search-input" | |
| 264 | name="search" | |
| 265 | aria-label="Run search in the documentation" | |
| 266 | autocomplete="off" | |
| 267 | spellcheck="false" | |
| 268 | placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…" | |
| 269 | type="search"> | |
| 270 | </form> | |
| 271 | </nav><div class="search-switcher"></div>`; | |
| 272 | out.insertBefore(hdr, window.searchState.outputElement()); | |
| 273 | el = document.getElementsByClassName("search-input")[0]; | |
| 274 | } | |
| 275 | if (el instanceof HTMLInputElement) { | |
| 276 | return el; | |
| 277 | } | |
| 278 | return null; | |
| 279 | }, | |
| 280 | containerElement: () => { | |
| 280 | 281 | let el = document.getElementById("search"); |
| 281 | 282 | if (!el) { |
| 282 | 283 | el = document.createElement("section"); |
| ... | ... | @@ -285,6 +286,19 @@ function preLoadCss(cssUrl) { |
| 285 | 286 | } |
| 286 | 287 | return el; |
| 287 | 288 | }, |
| 289 | outputElement: () => { | |
| 290 | const container = window.searchState.containerElement(); | |
| 291 | if (!container) { | |
| 292 | return null; | |
| 293 | } | |
| 294 | let el = container.querySelector(".search-out"); | |
| 295 | if (!el) { | |
| 296 | el = document.createElement("div"); | |
| 297 | el.className = "search-out"; | |
| 298 | container.appendChild(el); | |
| 299 | } | |
| 300 | return el; | |
| 301 | }, | |
| 288 | 302 | title: document.title, |
| 289 | 303 | titleBeforeSearch: document.title, |
| 290 | 304 | timeout: null, |
| ... | ... | @@ -303,25 +317,52 @@ function preLoadCss(cssUrl) { |
| 303 | 317 | } |
| 304 | 318 | }, |
| 305 | 319 | isDisplayed: () => { |
| 306 | const outputElement = window.searchState.outputElement(); | |
| 307 | return !!outputElement && | |
| 308 | !!outputElement.parentElement && | |
| 309 | outputElement.parentElement.id === ALTERNATIVE_DISPLAY_ID; | |
| 320 | const container = window.searchState.containerElement(); | |
| 321 | if (!container) { | |
| 322 | return false; | |
| 323 | } | |
| 324 | return !!container.parentElement && container.parentElement.id === | |
| 325 | ALTERNATIVE_DISPLAY_ID; | |
| 310 | 326 | }, |
| 311 | 327 | // Sets the focus on the search bar at the top of the page |
| 312 | 328 | focus: () => { |
| 313 | window.searchState.input && window.searchState.input.focus(); | |
| 329 | const inputElement = window.searchState.inputElement(); | |
| 330 | window.searchState.showResults(); | |
| 331 | if (inputElement) { | |
| 332 | inputElement.focus(); | |
| 333 | // Avoid glitch if something focuses the search button after clicking. | |
| 334 | requestAnimationFrame(() => inputElement.focus()); | |
| 335 | } | |
| 314 | 336 | }, |
| 315 | 337 | // Removes the focus from the search bar. |
| 316 | 338 | defocus: () => { |
| 317 | window.searchState.input && window.searchState.input.blur(); | |
| 339 | nonnull(window.searchState.inputElement()).blur(); | |
| 318 | 340 | }, |
| 319 | showResults: search => { | |
| 320 | if (search === null || typeof search === "undefined") { | |
| 321 | search = window.searchState.outputElement(); | |
| 341 | toggle: () => { | |
| 342 | if (window.searchState.isDisplayed()) { | |
| 343 | window.searchState.defocus(); | |
| 344 | window.searchState.hideResults(); | |
| 345 | } else { | |
| 346 | window.searchState.focus(); | |
| 322 | 347 | } |
| 323 | switchDisplayedElement(search); | |
| 348 | }, | |
| 349 | showResults: () => { | |
| 324 | 350 | document.title = window.searchState.title; |
| 351 | if (window.searchState.isDisplayed()) { | |
| 352 | return; | |
| 353 | } | |
| 354 | const search = window.searchState.containerElement(); | |
| 355 | switchDisplayedElement(search); | |
| 356 | const btn = document.querySelector("#search-button a"); | |
| 357 | if (browserSupportsHistoryApi() && btn instanceof HTMLAnchorElement && | |
| 358 | window.searchState.getQueryStringParams().search === undefined | |
| 359 | ) { | |
| 360 | history.pushState(null, "", btn.href); | |
| 361 | } | |
| 362 | const btnLabel = document.querySelector("#search-button a span.label"); | |
| 363 | if (btnLabel) { | |
| 364 | btnLabel.innerHTML = "Exit"; | |
| 365 | } | |
| 325 | 366 | }, |
| 326 | 367 | removeQueryParameters: () => { |
| 327 | 368 | // We change the document title. |
| ... | ... | @@ -334,6 +375,10 @@ function preLoadCss(cssUrl) { |
| 334 | 375 | switchDisplayedElement(null); |
| 335 | 376 | // We also remove the query parameter from the URL. |
| 336 | 377 | window.searchState.removeQueryParameters(); |
| 378 | const btnLabel = document.querySelector("#search-button a span.label"); | |
| 379 | if (btnLabel) { | |
| 380 | btnLabel.innerHTML = "Search"; | |
| 381 | } | |
| 337 | 382 | }, |
| 338 | 383 | getQueryStringParams: () => { |
| 339 | 384 | /** @type {Object.<any, string>} */ |
| ... | ... | @@ -348,11 +393,11 @@ function preLoadCss(cssUrl) { |
| 348 | 393 | return params; |
| 349 | 394 | }, |
| 350 | 395 | setup: () => { |
| 351 | const search_input = window.searchState.input; | |
| 396 | let searchLoaded = false; | |
| 397 | const search_input = window.searchState.inputElement(); | |
| 352 | 398 | if (!search_input) { |
| 353 | 399 | return; |
| 354 | 400 | } |
| 355 | let searchLoaded = false; | |
| 356 | 401 | // If you're browsing the nightly docs, the page might need to be refreshed for the |
| 357 | 402 | // search to work because the hash of the JS scripts might have changed. |
| 358 | 403 | function sendSearchForm() { |
| ... | ... | @@ -363,21 +408,102 @@ function preLoadCss(cssUrl) { |
| 363 | 408 | if (!searchLoaded) { |
| 364 | 409 | searchLoaded = true; |
| 365 | 410 | // @ts-expect-error |
| 366 | loadScript(getVar("static-root-path") + getVar("search-js"), sendSearchForm); | |
| 367 | loadScript(resourcePath("search-index", ".js"), sendSearchForm); | |
| 411 | window.rr_ = data => { | |
| 412 | // @ts-expect-error | |
| 413 | window.searchIndex = data; | |
| 414 | }; | |
| 415 | if (!window.StringdexOnload) { | |
| 416 | window.StringdexOnload = []; | |
| 417 | } | |
| 418 | window.StringdexOnload.push(() => { | |
| 419 | loadScript( | |
| 420 | // @ts-expect-error | |
| 421 | getVar("static-root-path") + getVar("search-js"), | |
| 422 | sendSearchForm, | |
| 423 | ); | |
| 424 | }); | |
| 425 | // @ts-expect-error | |
| 426 | loadScript(getVar("static-root-path") + getVar("stringdex-js"), sendSearchForm); | |
| 427 | loadScript(resourcePath("search.index/root", ".js"), sendSearchForm); | |
| 368 | 428 | } |
| 369 | 429 | } |
| 370 | 430 | |
| 371 | 431 | search_input.addEventListener("focus", () => { |
| 372 | window.searchState.origPlaceholder = search_input.placeholder; | |
| 373 | search_input.placeholder = "Type your search here."; | |
| 374 | 432 | loadSearch(); |
| 375 | 433 | }); |
| 376 | 434 | |
| 377 | if (search_input.value !== "") { | |
| 378 | loadSearch(); | |
| 435 | const btn = document.getElementById("search-button"); | |
| 436 | if (btn) { | |
| 437 | btn.onclick = event => { | |
| 438 | if (event.ctrlKey || event.altKey || event.metaKey) { | |
| 439 | return; | |
| 440 | } | |
| 441 | event.preventDefault(); | |
| 442 | window.searchState.toggle(); | |
| 443 | loadSearch(); | |
| 444 | }; | |
| 445 | } | |
| 446 | ||
| 447 | // Push and pop states are used to add search results to the browser | |
| 448 | // history. | |
| 449 | if (browserSupportsHistoryApi()) { | |
| 450 | // Store the previous <title> so we can revert back to it later. | |
| 451 | const previousTitle = document.title; | |
| 452 | ||
| 453 | window.addEventListener("popstate", e => { | |
| 454 | const params = window.searchState.getQueryStringParams(); | |
| 455 | // Revert to the previous title manually since the History | |
| 456 | // API ignores the title parameter. | |
| 457 | document.title = previousTitle; | |
| 458 | // Synchronize search bar with query string state and | |
| 459 | // perform the search. This will empty the bar if there's | |
| 460 | // nothing there, which lets you really go back to a | |
| 461 | // previous state with nothing in the bar. | |
| 462 | const inputElement = window.searchState.inputElement(); | |
| 463 | if (params.search !== undefined && inputElement !== null) { | |
| 464 | loadSearch(); | |
| 465 | inputElement.value = params.search; | |
| 466 | // Some browsers fire "onpopstate" for every page load | |
| 467 | // (Chrome), while others fire the event only when actually | |
| 468 | // popping a state (Firefox), which is why search() is | |
| 469 | // called both here and at the end of the startSearch() | |
| 470 | // function. | |
| 471 | e.preventDefault(); | |
| 472 | window.searchState.showResults(); | |
| 473 | if (params.search === "") { | |
| 474 | window.searchState.focus(); | |
| 475 | } | |
| 476 | } else { | |
| 477 | // When browsing back from search results the main page | |
| 478 | // visibility must be reset. | |
| 479 | window.searchState.hideResults(); | |
| 480 | } | |
| 481 | }); | |
| 379 | 482 | } |
| 380 | 483 | |
| 484 | // This is required in firefox to avoid this problem: Navigating to a search result | |
| 485 | // with the keyboard, hitting enter, and then hitting back would take you back to | |
| 486 | // the doc page, rather than the search that should overlay it. | |
| 487 | // This was an interaction between the back-forward cache and our handlers | |
| 488 | // that try to sync state between the URL and the search input. To work around it, | |
| 489 | // do a small amount of re-init on page show. | |
| 490 | window.onpageshow = () => { | |
| 491 | const inputElement = window.searchState.inputElement(); | |
| 492 | const qSearch = window.searchState.getQueryStringParams().search; | |
| 493 | if (qSearch !== undefined && inputElement !== null) { | |
| 494 | if (inputElement.value === "") { | |
| 495 | inputElement.value = qSearch; | |
| 496 | } | |
| 497 | window.searchState.showResults(); | |
| 498 | if (qSearch === "") { | |
| 499 | loadSearch(); | |
| 500 | window.searchState.focus(); | |
| 501 | } | |
| 502 | } else { | |
| 503 | window.searchState.hideResults(); | |
| 504 | } | |
| 505 | }; | |
| 506 | ||
| 381 | 507 | const params = window.searchState.getQueryStringParams(); |
| 382 | 508 | if (params.search !== undefined) { |
| 383 | 509 | window.searchState.setLoadingSearch(); |
| ... | ... | @@ -386,13 +512,9 @@ function preLoadCss(cssUrl) { |
| 386 | 512 | }, |
| 387 | 513 | setLoadingSearch: () => { |
| 388 | 514 | const search = window.searchState.outputElement(); |
| 389 | if (!search) { | |
| 390 | return; | |
| 391 | } | |
| 392 | search.innerHTML = "<h3 class=\"search-loading\">" + | |
| 393 | window.searchState.loadingText + | |
| 394 | "</h3>"; | |
| 395 | window.searchState.showResults(search); | |
| 515 | nonnull(search).innerHTML = "<h3 class=\"search-loading\">" + | |
| 516 | window.searchState.loadingText + "</h3>"; | |
| 517 | window.searchState.showResults(); | |
| 396 | 518 | }, |
| 397 | 519 | descShards: new Map(), |
| 398 | 520 | loadDesc: async function({descShard, descIndex}) { |
| ... | ... | @@ -1500,15 +1622,13 @@ function preLoadCss(cssUrl) { |
| 1500 | 1622 | |
| 1501 | 1623 | // @ts-expect-error |
| 1502 | 1624 | function helpBlurHandler(event) { |
| 1503 | // @ts-expect-error | |
| 1504 | if (!getHelpButton().contains(document.activeElement) && | |
| 1505 | // @ts-expect-error | |
| 1506 | !getHelpButton().contains(event.relatedTarget) && | |
| 1507 | // @ts-expect-error | |
| 1508 | !getSettingsButton().contains(document.activeElement) && | |
| 1509 | // @ts-expect-error | |
| 1510 | !getSettingsButton().contains(event.relatedTarget) | |
| 1511 | ) { | |
| 1625 | const isInPopover = onEachLazy( | |
| 1626 | document.querySelectorAll(".settings-menu, .help-menu"), | |
| 1627 | menu => { | |
| 1628 | return menu.contains(document.activeElement) || menu.contains(event.relatedTarget); | |
| 1629 | }, | |
| 1630 | ); | |
| 1631 | if (!isInPopover) { | |
| 1512 | 1632 | window.hidePopoverMenus(); |
| 1513 | 1633 | } |
| 1514 | 1634 | } |
| ... | ... | @@ -1571,10 +1691,9 @@ function preLoadCss(cssUrl) { |
| 1571 | 1691 | |
| 1572 | 1692 | const container = document.createElement("div"); |
| 1573 | 1693 | if (!isHelpPage) { |
| 1574 | container.className = "popover"; | |
| 1694 | container.className = "popover content"; | |
| 1575 | 1695 | } |
| 1576 | 1696 | container.id = "help"; |
| 1577 | container.style.display = "none"; | |
| 1578 | 1697 | |
| 1579 | 1698 | const side_by_side = document.createElement("div"); |
| 1580 | 1699 | side_by_side.className = "side-by-side"; |
| ... | ... | @@ -1590,17 +1709,16 @@ function preLoadCss(cssUrl) { |
| 1590 | 1709 | help_section.appendChild(container); |
| 1591 | 1710 | // @ts-expect-error |
| 1592 | 1711 | document.getElementById("main-content").appendChild(help_section); |
| 1593 | container.style.display = "block"; | |
| 1594 | 1712 | } else { |
| 1595 | const help_button = getHelpButton(); | |
| 1596 | // @ts-expect-error | |
| 1597 | help_button.appendChild(container); | |
| 1598 | ||
| 1599 | container.onblur = helpBlurHandler; | |
| 1600 | // @ts-expect-error | |
| 1601 | help_button.onblur = helpBlurHandler; | |
| 1602 | // @ts-expect-error | |
| 1603 | help_button.children[0].onblur = helpBlurHandler; | |
| 1713 | onEachLazy(document.getElementsByClassName("help-menu"), menu => { | |
| 1714 | if (menu.offsetWidth !== 0) { | |
| 1715 | menu.appendChild(container); | |
| 1716 | container.onblur = helpBlurHandler; | |
| 1717 | menu.onblur = helpBlurHandler; | |
| 1718 | menu.children[0].onblur = helpBlurHandler; | |
| 1719 | return true; | |
| 1720 | } | |
| 1721 | }); | |
| 1604 | 1722 | } |
| 1605 | 1723 | |
| 1606 | 1724 | return container; |
| ... | ... | @@ -1621,80 +1739,57 @@ function preLoadCss(cssUrl) { |
| 1621 | 1739 | * Hide all the popover menus. |
| 1622 | 1740 | */ |
| 1623 | 1741 | window.hidePopoverMenus = () => { |
| 1624 | onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { | |
| 1742 | onEachLazy(document.querySelectorAll(".settings-menu .popover"), elem => { | |
| 1625 | 1743 | elem.style.display = "none"; |
| 1626 | 1744 | }); |
| 1627 | const button = getHelpButton(); | |
| 1628 | if (button) { | |
| 1629 | removeClass(button, "help-open"); | |
| 1630 | } | |
| 1745 | onEachLazy(document.querySelectorAll(".help-menu .popover"), elem => { | |
| 1746 | elem.parentElement.removeChild(elem); | |
| 1747 | }); | |
| 1631 | 1748 | }; |
| 1632 | 1749 | |
| 1633 | /** | |
| 1634 | * Returns the help menu element (not the button). | |
| 1635 | * | |
| 1636 | * @param {boolean} buildNeeded - If this argument is `false`, the help menu element won't be | |
| 1637 | * built if it doesn't exist. | |
| 1638 | * | |
| 1639 | * @return {HTMLElement} | |
| 1640 | */ | |
| 1641 | function getHelpMenu(buildNeeded) { | |
| 1642 | // @ts-expect-error | |
| 1643 | let menu = getHelpButton().querySelector(".popover"); | |
| 1644 | if (!menu && buildNeeded) { | |
| 1645 | menu = buildHelpMenu(); | |
| 1646 | } | |
| 1647 | // @ts-expect-error | |
| 1648 | return menu; | |
| 1649 | } | |
| 1650 | ||
| 1651 | 1750 | /** |
| 1652 | 1751 | * Show the help popup menu. |
| 1653 | 1752 | */ |
| 1654 | 1753 | function showHelp() { |
| 1754 | window.hideAllModals(false); | |
| 1655 | 1755 | // Prevent `blur` events from being dispatched as a result of closing |
| 1656 | 1756 | // other modals. |
| 1657 | const button = getHelpButton(); | |
| 1658 | addClass(button, "help-open"); | |
| 1659 | // @ts-expect-error | |
| 1660 | button.querySelector("a").focus(); | |
| 1661 | const menu = getHelpMenu(true); | |
| 1662 | if (menu.style.display === "none") { | |
| 1663 | // @ts-expect-error | |
| 1664 | window.hideAllModals(); | |
| 1665 | menu.style.display = ""; | |
| 1666 | } | |
| 1757 | onEachLazy(document.querySelectorAll(".help-menu a"), menu => { | |
| 1758 | if (menu.offsetWidth !== 0) { | |
| 1759 | menu.focus(); | |
| 1760 | return true; | |
| 1761 | } | |
| 1762 | }); | |
| 1763 | buildHelpMenu(); | |
| 1667 | 1764 | } |
| 1668 | 1765 | |
| 1669 | const helpLink = document.querySelector(`#${HELP_BUTTON_ID} > a`); | |
| 1670 | 1766 | if (isHelpPage) { |
| 1671 | 1767 | buildHelpMenu(); |
| 1672 | } else if (helpLink) { | |
| 1673 | helpLink.addEventListener("click", event => { | |
| 1674 | // By default, have help button open docs in a popover. | |
| 1675 | // If user clicks with a moderator, though, use default browser behavior, | |
| 1676 | // probably opening in a new window or tab. | |
| 1677 | if (!helpLink.contains(helpLink) || | |
| 1678 | // @ts-expect-error | |
| 1679 | event.ctrlKey || | |
| 1680 | // @ts-expect-error | |
| 1681 | event.altKey || | |
| 1682 | // @ts-expect-error | |
| 1683 | event.metaKey) { | |
| 1684 | return; | |
| 1685 | } | |
| 1686 | event.preventDefault(); | |
| 1687 | const menu = getHelpMenu(true); | |
| 1688 | const shouldShowHelp = menu.style.display === "none"; | |
| 1689 | if (shouldShowHelp) { | |
| 1690 | showHelp(); | |
| 1691 | } else { | |
| 1692 | window.hidePopoverMenus(); | |
| 1693 | } | |
| 1768 | } else { | |
| 1769 | onEachLazy(document.querySelectorAll(".help-menu > a"), helpLink => { | |
| 1770 | helpLink.addEventListener( | |
| 1771 | "click", | |
| 1772 | /** @param {MouseEvent} event */ | |
| 1773 | event => { | |
| 1774 | // By default, have help button open docs in a popover. | |
| 1775 | // If user clicks with a moderator, though, use default browser behavior, | |
| 1776 | // probably opening in a new window or tab. | |
| 1777 | if (event.ctrlKey || | |
| 1778 | event.altKey || | |
| 1779 | event.metaKey) { | |
| 1780 | return; | |
| 1781 | } | |
| 1782 | event.preventDefault(); | |
| 1783 | if (document.getElementById("help")) { | |
| 1784 | window.hidePopoverMenus(); | |
| 1785 | } else { | |
| 1786 | showHelp(); | |
| 1787 | } | |
| 1788 | }, | |
| 1789 | ); | |
| 1694 | 1790 | }); |
| 1695 | 1791 | } |
| 1696 | 1792 | |
| 1697 | setMobileTopbar(); | |
| 1698 | 1793 | addSidebarItems(); |
| 1699 | 1794 | addSidebarCrates(); |
| 1700 | 1795 | onHashChange(null); |
| ... | ... | @@ -1746,7 +1841,15 @@ function preLoadCss(cssUrl) { |
| 1746 | 1841 | // On larger, "desktop-sized" viewports (though that includes many |
| 1747 | 1842 | // tablets), it's fixed-position, appears in the left side margin, |
| 1748 | 1843 | // and it can be activated by resizing the sidebar into nothing. |
| 1749 | const sidebarButton = document.getElementById("sidebar-button"); | |
| 1844 | let sidebarButton = document.getElementById("sidebar-button"); | |
| 1845 | const body = document.querySelector(".main-heading"); | |
| 1846 | if (!sidebarButton && body) { | |
| 1847 | sidebarButton = document.createElement("div"); | |
| 1848 | sidebarButton.id = "sidebar-button"; | |
| 1849 | const path = `${window.rootPath}${window.currentCrate}/all.html`; | |
| 1850 | sidebarButton.innerHTML = `<a href="${path}" title="show sidebar"></a>`; | |
| 1851 | body.insertBefore(sidebarButton, body.firstChild); | |
| 1852 | } | |
| 1750 | 1853 | if (sidebarButton) { |
| 1751 | 1854 | sidebarButton.addEventListener("click", e => { |
| 1752 | 1855 | removeClass(document.documentElement, "hide-sidebar"); |
src/librustdoc/html/static/js/rustdoc.d.ts+134-118| ... | ... | @@ -2,6 +2,8 @@ |
| 2 | 2 | // not put into the JavaScript we include as part of the documentation. It is used for |
| 3 | 3 | // type checking. See README.md in this directory for more info. |
| 4 | 4 | |
| 5 | import { RoaringBitmap } from "./stringdex"; | |
| 6 | ||
| 5 | 7 | /* eslint-disable */ |
| 6 | 8 | declare global { |
| 7 | 9 | /** Search engine data used by main.js and search.js */ |
| ... | ... | @@ -10,6 +12,17 @@ declare global { |
| 10 | 12 | declare function nonnull(x: T|null, msg: string|undefined); |
| 11 | 13 | /** Defined and documented in `storage.js` */ |
| 12 | 14 | declare function nonundef(x: T|undefined, msg: string|undefined); |
| 15 | interface PromiseConstructor { | |
| 16 | /** | |
| 17 | * Polyfill | |
| 18 | * @template T | |
| 19 | */ | |
| 20 | withResolvers: function(): { | |
| 21 | "promise": Promise<T>, | |
| 22 | "resolve": (function(T): void), | |
| 23 | "reject": (function(any): void) | |
| 24 | }; | |
| 25 | } | |
| 13 | 26 | interface Window { |
| 14 | 27 | /** Make the current theme easy to find */ |
| 15 | 28 | currentTheme: HTMLLinkElement|null; |
| ... | ... | @@ -95,29 +108,28 @@ declare namespace rustdoc { |
| 95 | 108 | interface SearchState { |
| 96 | 109 | rustdocToolbar: HTMLElement|null; |
| 97 | 110 | loadingText: string; |
| 98 | input: HTMLInputElement|null; | |
| 111 | inputElement: function(): HTMLInputElement|null; | |
| 112 | containerElement: function(): Element|null; | |
| 99 | 113 | title: string; |
| 100 | 114 | titleBeforeSearch: string; |
| 101 | timeout: number|null; | |
| 115 | timeout: ReturnType<typeof setTimeout>|null; | |
| 102 | 116 | currentTab: number; |
| 103 | focusedByTab: [number|null, number|null, number|null]; | |
| 117 | focusedByTab: [Element|null, Element|null, Element|null]; | |
| 104 | 118 | clearInputTimeout: function; |
| 105 | outputElement(): HTMLElement|null; | |
| 106 | focus(); | |
| 107 | defocus(); | |
| 108 | // note: an optional param is not the same as | |
| 109 | // a nullable/undef-able param. | |
| 110 | showResults(elem?: HTMLElement|null); | |
| 111 | removeQueryParameters(); | |
| 112 | hideResults(); | |
| 113 | getQueryStringParams(): Object.<any, string>; | |
| 114 | origPlaceholder: string; | |
| 119 | outputElement: function(): Element|null; | |
| 120 | focus: function(); | |
| 121 | defocus: function(); | |
| 122 | toggle: function(); | |
| 123 | showResults: function(); | |
| 124 | removeQueryParameters: function(); | |
| 125 | hideResults: function(); | |
| 126 | getQueryStringParams: function(): Object.<any, string>; | |
| 115 | 127 | setup: function(); |
| 116 | 128 | setLoadingSearch(); |
| 117 | 129 | descShards: Map<string, SearchDescShard[]>; |
| 118 | 130 | loadDesc: function({descShard: SearchDescShard, descIndex: number}): Promise<string|null>; |
| 119 | loadedDescShard(string, number, string); | |
| 120 | isDisplayed(): boolean, | |
| 131 | loadedDescShard: function(string, number, string); | |
| 132 | isDisplayed: function(): boolean; | |
| 121 | 133 | } |
| 122 | 134 | |
| 123 | 135 | interface SearchDescShard { |
| ... | ... | @@ -131,12 +143,13 @@ declare namespace rustdoc { |
| 131 | 143 | * A single parsed "atom" in a search query. For example, |
| 132 | 144 | * |
| 133 | 145 | * std::fmt::Formatter, Write -> Result<()> |
| 134 | * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ | |
| 135 | * ┃ │ ┗ QueryElement { ┊ | |
| 136 | * ┃ │ name: Result ┊ | |
| 137 | * ┃ │ generics: [ ┊ | |
| 138 | * ┃ │ QueryElement ┘ | |
| 139 | * ┃ │ name: () | |
| 146 | * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ | |
| 147 | * ┃ │ ┗ QueryElement { ┊ | |
| 148 | * ┃ │ name: Result ┊ | |
| 149 | * ┃ │ generics: [ ┊ | |
| 150 | * ┃ │ QueryElement { ┘ | |
| 151 | * ┃ │ name: () | |
| 152 | * ┃ │ } | |
| 140 | 153 | * ┃ │ ] |
| 141 | 154 | * ┃ │ } |
| 142 | 155 | * ┃ └ QueryElement { |
| ... | ... | @@ -156,14 +169,14 @@ declare namespace rustdoc { |
| 156 | 169 | normalizedPathLast: string, |
| 157 | 170 | generics: Array<QueryElement>, |
| 158 | 171 | bindings: Map<number, Array<QueryElement>>, |
| 159 | typeFilter: number|null, | |
| 172 | typeFilter: number, | |
| 160 | 173 | } |
| 161 | 174 | |
| 162 | 175 | /** |
| 163 | 176 | * Same as QueryElement, but bindings and typeFilter support strings |
| 164 | 177 | */ |
| 165 | 178 | interface ParserQueryElement { |
| 166 | name: string|null, | |
| 179 | name: string, | |
| 167 | 180 | id: number|null, |
| 168 | 181 | fullPath: Array<string>, |
| 169 | 182 | pathWithoutLast: Array<string>, |
| ... | ... | @@ -172,7 +185,7 @@ declare namespace rustdoc { |
| 172 | 185 | generics: Array<ParserQueryElement>, |
| 173 | 186 | bindings: Map<string, Array<ParserQueryElement>>, |
| 174 | 187 | bindingName: {name: string|null, generics: ParserQueryElement[]}|null, |
| 175 | typeFilter: number|string|null, | |
| 188 | typeFilter: string|null, | |
| 176 | 189 | } |
| 177 | 190 | |
| 178 | 191 | /** |
| ... | ... | @@ -215,35 +228,74 @@ declare namespace rustdoc { |
| 215 | 228 | /** |
| 216 | 229 | * An entry in the search index database. |
| 217 | 230 | */ |
| 231 | interface EntryData { | |
| 232 | krate: number, | |
| 233 | ty: ItemType, | |
| 234 | modulePath: number?, | |
| 235 | exactModulePath: number?, | |
| 236 | parent: number?, | |
| 237 | deprecated: boolean, | |
| 238 | associatedItemDisambiguator: string?, | |
| 239 | } | |
| 240 | ||
| 241 | /** | |
| 242 | * A path in the search index database | |
| 243 | */ | |
| 244 | interface PathData { | |
| 245 | ty: ItemType, | |
| 246 | modulePath: string, | |
| 247 | exactModulePath: string?, | |
| 248 | } | |
| 249 | ||
| 250 | /** | |
| 251 | * A function signature in the search index database | |
| 252 | * | |
| 253 | * Note that some non-function items (eg. constants, struct fields) have a function signature so they can appear in type-based search. | |
| 254 | */ | |
| 255 | interface FunctionData { | |
| 256 | functionSignature: FunctionSearchType|null, | |
| 257 | paramNames: string[], | |
| 258 | elemCount: number, | |
| 259 | } | |
| 260 | ||
| 261 | /** | |
| 262 | * A function signature in the search index database | |
| 263 | */ | |
| 264 | interface TypeData { | |
| 265 | searchUnbox: boolean, | |
| 266 | invertedFunctionSignatureIndex: RoaringBitmap[], | |
| 267 | } | |
| 268 | ||
| 269 | /** | |
| 270 | * A search entry of some sort. | |
| 271 | */ | |
| 218 | 272 | interface Row { |
| 219 | crate: string, | |
| 220 | descShard: SearchDescShard, | |
| 221 | 273 | id: number, |
| 222 | // This is the name of the item. For doc aliases, if you want the name of the aliased | |
| 223 | // item, take a look at `Row.original.name`. | |
| 274 | crate: string, | |
| 275 | ty: ItemType, | |
| 224 | 276 | name: string, |
| 225 | 277 | normalizedName: string, |
| 226 | word: string, | |
| 227 | paramNames: string[], | |
| 228 | parent: ({ty: number, name: string, path: string, exactPath: string}|null|undefined), | |
| 229 | path: string, | |
| 230 | ty: number, | |
| 231 | type: FunctionSearchType | null, | |
| 232 | descIndex: number, | |
| 233 | bitIndex: number, | |
| 234 | implDisambiguator: String | null, | |
| 235 | is_alias?: boolean, | |
| 236 | original?: Row, | |
| 278 | modulePath: string, | |
| 279 | exactModulePath: string, | |
| 280 | entry: EntryData?, | |
| 281 | path: PathData?, | |
| 282 | type: FunctionData?, | |
| 283 | deprecated: boolean, | |
| 284 | parent: { path: PathData, name: string}?, | |
| 237 | 285 | } |
| 238 | 286 | |
| 287 | type ItemType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | | |
| 288 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | | |
| 289 | 21 | 22 | 23 | 24 | 25 | 26; | |
| 290 | ||
| 239 | 291 | /** |
| 240 | 292 | * The viewmodel for the search engine results page. |
| 241 | 293 | */ |
| 242 | 294 | interface ResultsTable { |
| 243 | in_args: Array<ResultObject>, | |
| 244 | returned: Array<ResultObject>, | |
| 245 | others: Array<ResultObject>, | |
| 246 | query: ParsedQuery, | |
| 295 | in_args: AsyncGenerator<ResultObject>, | |
| 296 | returned: AsyncGenerator<ResultObject>, | |
| 297 | others: AsyncGenerator<ResultObject>, | |
| 298 | query: ParsedQuery<rustdoc.ParserQueryElement>, | |
| 247 | 299 | } |
| 248 | 300 | |
| 249 | 301 | type Results = { max_dist?: number } & Map<number, ResultObject> |
| ... | ... | @@ -252,25 +304,41 @@ declare namespace rustdoc { |
| 252 | 304 | * An annotated `Row`, used in the viewmodel. |
| 253 | 305 | */ |
| 254 | 306 | interface ResultObject { |
| 255 | desc: string, | |
| 307 | desc: Promise<string|null>, | |
| 256 | 308 | displayPath: string, |
| 257 | 309 | fullPath: string, |
| 258 | 310 | href: string, |
| 259 | 311 | id: number, |
| 260 | 312 | dist: number, |
| 261 | 313 | path_dist: number, |
| 262 | name: string, | |
| 263 | normalizedName: string, | |
| 264 | word: string, | |
| 265 | 314 | index: number, |
| 266 | parent: (Object|undefined), | |
| 267 | path: string, | |
| 268 | ty: number, | |
| 315 | parent: ({ | |
| 316 | path: string, | |
| 317 | exactPath: string, | |
| 318 | name: string, | |
| 319 | ty: number, | |
| 320 | }|undefined), | |
| 269 | 321 | type?: FunctionSearchType, |
| 270 | 322 | paramNames?: string[], |
| 271 | 323 | displayTypeSignature: Promise<rustdoc.DisplayTypeSignature> | null, |
| 272 | 324 | item: Row, |
| 273 | dontValidate?: boolean, | |
| 325 | is_alias: boolean, | |
| 326 | alias?: string, | |
| 327 | } | |
| 328 | ||
| 329 | /** | |
| 330 | * An annotated `Row`, used in the viewmodel. | |
| 331 | */ | |
| 332 | interface PlainResultObject { | |
| 333 | id: number, | |
| 334 | dist: number, | |
| 335 | path_dist: number, | |
| 336 | index: number, | |
| 337 | elems: rustdoc.QueryElement[], | |
| 338 | returned: rustdoc.QueryElement[], | |
| 339 | is_alias: boolean, | |
| 340 | alias?: string, | |
| 341 | original?: rustdoc.Rlow, | |
| 274 | 342 | } |
| 275 | 343 | |
| 276 | 344 | /** |
| ... | ... | @@ -364,7 +432,19 @@ declare namespace rustdoc { |
| 364 | 432 | * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` |
| 365 | 433 | * because `null` is four bytes while `0` is one byte. |
| 366 | 434 | */ |
| 367 | type RawFunctionType = number | [number, Array<RawFunctionType>]; | |
| 435 | type RawFunctionType = number | [number, Array<RawFunctionType>] | [number, Array<RawFunctionType>, Array<[RawFunctionType, RawFunctionType[]]>]; | |
| 436 | ||
| 437 | /** | |
| 438 | * Utility typedef for deserializing compact JSON. | |
| 439 | * | |
| 440 | * R is the required part, O is the optional part, which goes afterward. | |
| 441 | * For example, `ArrayWithOptionals<[A, B], [C, D]>` matches | |
| 442 | * `[A, B] | [A, B, C] | [A, B, C, D]`. | |
| 443 | */ | |
| 444 | type ArrayWithOptionals<R extends any[], O extends any[]> = | |
| 445 | O extends [infer First, ...infer Rest] ? | |
| 446 | R | ArrayWithOptionals<[...R, First], Rest> : | |
| 447 | R; | |
| 368 | 448 | |
| 369 | 449 | /** |
| 370 | 450 | * The type signature entry in the decoded search index. |
| ... | ... | @@ -382,8 +462,8 @@ declare namespace rustdoc { |
| 382 | 462 | */ |
| 383 | 463 | interface FunctionType { |
| 384 | 464 | id: null|number, |
| 385 | ty: number|null, | |
| 386 | name?: string, | |
| 465 | ty: ItemType, | |
| 466 | name: string|null, | |
| 387 | 467 | path: string|null, |
| 388 | 468 | exactPath: string|null, |
| 389 | 469 | unboxFlag: boolean, |
| ... | ... | @@ -403,70 +483,6 @@ declare namespace rustdoc { |
| 403 | 483 | bindings: Map<number, FingerprintableType[]>; |
| 404 | 484 | }; |
| 405 | 485 | |
| 406 | /** | |
| 407 | * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` | |
| 408 | * are arrays with the same length. `q`, `a`, and `c` use a sparse | |
| 409 | * representation for compactness. | |
| 410 | * | |
| 411 | * `n[i]` contains the name of an item. | |
| 412 | * | |
| 413 | * `t[i]` contains the type of that item | |
| 414 | * (as a string of characters that represent an offset in `itemTypes`). | |
| 415 | * | |
| 416 | * `d[i]` contains the description of that item. | |
| 417 | * | |
| 418 | * `q` contains the full paths of the items. For compactness, it is a set of | |
| 419 | * (index, path) pairs used to create a map. If a given index `i` is | |
| 420 | * not present, this indicates "same as the last index present". | |
| 421 | * | |
| 422 | * `i[i]` contains an item's parent, usually a module. For compactness, | |
| 423 | * it is a set of indexes into the `p` array. | |
| 424 | * | |
| 425 | * `f` contains function signatures, or `0` if the item isn't a function. | |
| 426 | * More information on how they're encoded can be found in rustc-dev-guide | |
| 427 | * | |
| 428 | * Functions are themselves encoded as arrays. The first item is a list of | |
| 429 | * types representing the function's inputs, and the second list item is a list | |
| 430 | * of types representing the function's output. Tuples are flattened. | |
| 431 | * Types are also represented as arrays; the first item is an index into the `p` | |
| 432 | * array, while the second is a list of types representing any generic parameters. | |
| 433 | * | |
| 434 | * b[i] contains an item's impl disambiguator. This is only present if an item | |
| 435 | * is defined in an impl block and, the impl block's type has more than one associated | |
| 436 | * item with the same name. | |
| 437 | * | |
| 438 | * `a` defines aliases with an Array of pairs: [name, offset], where `offset` | |
| 439 | * points into the n/t/d/q/i/f arrays. | |
| 440 | * | |
| 441 | * `doc` contains the description of the crate. | |
| 442 | * | |
| 443 | * `p` is a list of path/type pairs. It is used for parents and function parameters. | |
| 444 | * The first item is the type, the second is the name, the third is the visible path (if any) and | |
| 445 | * the fourth is the canonical path used for deduplication (if any). | |
| 446 | * | |
| 447 | * `r` is the canonical path used for deduplication of re-exported items. | |
| 448 | * It is not used for associated items like methods (that's the fourth element | |
| 449 | * of `p`) but is used for modules items like free functions. | |
| 450 | * | |
| 451 | * `c` is an array of item indices that are deprecated. | |
| 452 | */ | |
| 453 | type RawSearchIndexCrate = { | |
| 454 | doc: string, | |
| 455 | a: { [key: string]: number[] }, | |
| 456 | n: Array<string>, | |
| 457 | t: string, | |
| 458 | D: string, | |
| 459 | e: string, | |
| 460 | q: Array<[number, string]>, | |
| 461 | i: string, | |
| 462 | f: string, | |
| 463 | p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>, | |
| 464 | b: Array<[number, String]>, | |
| 465 | c: string, | |
| 466 | r: Array<[number, number]>, | |
| 467 | P: Array<[number, string]>, | |
| 468 | }; | |
| 469 | ||
| 470 | 486 | type VlqData = VlqData[] | number; |
| 471 | 487 | |
| 472 | 488 | /** |
src/librustdoc/html/static/js/search.js+2317-2930| ... | ... | @@ -1,9 +1,16 @@ |
| 1 | 1 | // ignore-tidy-filelength |
| 2 | /* global addClass, getNakedUrl, getSettingValue, getVar */ | |
| 3 | /* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi, exports */ | |
| 2 | /* global addClass, getNakedUrl, getVar, nonnull, getSettingValue */ | |
| 3 | /* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */ | |
| 4 | 4 | |
| 5 | 5 | "use strict"; |
| 6 | 6 | |
| 7 | /** | |
| 8 | * @param {stringdex.Stringdex} Stringdex | |
| 9 | * @param {typeof stringdex.RoaringBitmap} RoaringBitmap | |
| 10 | * @param {stringdex.Hooks} hooks | |
| 11 | */ | |
| 12 | const initSearch = async function(Stringdex, RoaringBitmap, hooks) { | |
| 13 | ||
| 7 | 14 | // polyfill |
| 8 | 15 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced |
| 9 | 16 | if (!Array.prototype.toSpliced) { |
| ... | ... | @@ -20,31 +27,65 @@ if (!Array.prototype.toSpliced) { |
| 20 | 27 | * |
| 21 | 28 | * @template T |
| 22 | 29 | * @param {Iterable<T>} arr |
| 23 | * @param {function(T): any} func | |
| 30 | * @param {function(T): Promise<any>} func | |
| 24 | 31 | * @param {function(T): boolean} funcBtwn |
| 25 | 32 | */ |
| 26 | function onEachBtwn(arr, func, funcBtwn) { | |
| 33 | async function onEachBtwnAsync(arr, func, funcBtwn) { | |
| 27 | 34 | let skipped = true; |
| 28 | 35 | for (const value of arr) { |
| 29 | 36 | if (!skipped) { |
| 30 | 37 | funcBtwn(value); |
| 31 | 38 | } |
| 32 | skipped = func(value); | |
| 39 | skipped = await func(value); | |
| 33 | 40 | } |
| 34 | 41 | } |
| 35 | 42 | |
| 36 | 43 | /** |
| 37 | * Convert any `undefined` to `null`. | |
| 38 | * | |
| 39 | * @template T | |
| 40 | * @param {T|undefined} x | |
| 41 | * @returns {T|null} | |
| 44 | * Allow the browser to redraw. | |
| 45 | * @returns {Promise<void>} | |
| 42 | 46 | */ |
| 43 | function undef2null(x) { | |
| 44 | if (x !== undefined) { | |
| 45 | return x; | |
| 46 | } | |
| 47 | return null; | |
| 47 | const yieldToBrowser = typeof window !== "undefined" && window.requestIdleCallback ? | |
| 48 | function() { | |
| 49 | return new Promise((resolve, _reject) => { | |
| 50 | window.requestIdleCallback(resolve); | |
| 51 | }); | |
| 52 | } : | |
| 53 | function() { | |
| 54 | return new Promise((resolve, _reject) => { | |
| 55 | setTimeout(resolve, 0); | |
| 56 | }); | |
| 57 | }; | |
| 58 | ||
| 59 | /** | |
| 60 | * Promise-based timer wrapper. | |
| 61 | * @param {number} ms | |
| 62 | * @returns {Promise<void>} | |
| 63 | */ | |
| 64 | const timeout = function(ms) { | |
| 65 | return new Promise((resolve, _reject) => { | |
| 66 | setTimeout(resolve, ms); | |
| 67 | }); | |
| 68 | }; | |
| 69 | ||
| 70 | if (!Promise.withResolvers) { | |
| 71 | /** | |
| 72 | * Polyfill | |
| 73 | * @template T | |
| 74 | * @returns {{ | |
| 75 | "promise": Promise<T>, | |
| 76 | "resolve": (function(T): void), | |
| 77 | "reject": (function(any): void) | |
| 78 | }} | |
| 79 | */ | |
| 80 | Promise.withResolvers = () => { | |
| 81 | let resolve, reject; | |
| 82 | const promise = new Promise((res, rej) => { | |
| 83 | resolve = res; | |
| 84 | reject = rej; | |
| 85 | }); | |
| 86 | // @ts-expect-error | |
| 87 | return {promise, resolve, reject}; | |
| 88 | }; | |
| 48 | 89 | } |
| 49 | 90 | |
| 50 | 91 | // ==================== Core search logic begin ==================== |
| ... | ... | @@ -81,13 +122,22 @@ const itemTypes = [ |
| 81 | 122 | ]; |
| 82 | 123 | |
| 83 | 124 | // used for special search precedence |
| 84 | const TY_PRIMITIVE = itemTypes.indexOf("primitive"); | |
| 85 | const TY_GENERIC = itemTypes.indexOf("generic"); | |
| 86 | const TY_IMPORT = itemTypes.indexOf("import"); | |
| 87 | const TY_TRAIT = itemTypes.indexOf("trait"); | |
| 88 | const TY_FN = itemTypes.indexOf("fn"); | |
| 89 | const TY_METHOD = itemTypes.indexOf("method"); | |
| 90 | const TY_TYMETHOD = itemTypes.indexOf("tymethod"); | |
| 125 | /** @type {rustdoc.ItemType} */ | |
| 126 | const TY_PRIMITIVE = 1; | |
| 127 | /** @type {rustdoc.ItemType} */ | |
| 128 | const TY_GENERIC = 26; | |
| 129 | /** @type {rustdoc.ItemType} */ | |
| 130 | const TY_IMPORT = 4; | |
| 131 | /** @type {rustdoc.ItemType} */ | |
| 132 | const TY_TRAIT = 10; | |
| 133 | /** @type {rustdoc.ItemType} */ | |
| 134 | const TY_FN = 7; | |
| 135 | /** @type {rustdoc.ItemType} */ | |
| 136 | const TY_METHOD = 13; | |
| 137 | /** @type {rustdoc.ItemType} */ | |
| 138 | const TY_TYMETHOD = 12; | |
| 139 | /** @type {rustdoc.ItemType} */ | |
| 140 | const TY_ASSOCTYPE = 17; | |
| 91 | 141 | const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; |
| 92 | 142 | |
| 93 | 143 | // Hard limit on how deep to recurse into generics when doing type-driven search. |
| ... | ... | @@ -242,7 +292,9 @@ function isEndCharacter(c) { |
| 242 | 292 | } |
| 243 | 293 | |
| 244 | 294 | /** |
| 245 | * @param {number} ty | |
| 295 | * Same thing as ItemType::is_fn_like in item_type.rs | |
| 296 | * | |
| 297 | * @param {rustdoc.ItemType} ty | |
| 246 | 298 | * @returns |
| 247 | 299 | */ |
| 248 | 300 | function isFnLikeTy(ty) { |
| ... | ... | @@ -1023,6 +1075,7 @@ class VlqHexDecoder { |
| 1023 | 1075 | this.string = string; |
| 1024 | 1076 | this.cons = cons; |
| 1025 | 1077 | this.offset = 0; |
| 1078 | this.elemCount = 0; | |
| 1026 | 1079 | /** @type {T[]} */ |
| 1027 | 1080 | this.backrefQueue = []; |
| 1028 | 1081 | } |
| ... | ... | @@ -1060,6 +1113,7 @@ class VlqHexDecoder { |
| 1060 | 1113 | n = (n << 4) | (c & 0xF); |
| 1061 | 1114 | const [sign, value] = [n & 1, n >> 1]; |
| 1062 | 1115 | this.offset += 1; |
| 1116 | this.elemCount += 1; | |
| 1063 | 1117 | return sign ? -value : value; |
| 1064 | 1118 | } |
| 1065 | 1119 | /** |
| ... | ... | @@ -1086,1303 +1140,159 @@ class VlqHexDecoder { |
| 1086 | 1140 | return result; |
| 1087 | 1141 | } |
| 1088 | 1142 | } |
| 1089 | class RoaringBitmap { | |
| 1090 | /** @param {string} str */ | |
| 1091 | constructor(str) { | |
| 1092 | // https://github.com/RoaringBitmap/RoaringFormatSpec | |
| 1093 | // | |
| 1094 | // Roaring bitmaps are used for flags that can be kept in their | |
| 1095 | // compressed form, even when loaded into memory. This decoder | |
| 1096 | // turns the containers into objects, but uses byte array | |
| 1097 | // slices of the original format for the data payload. | |
| 1098 | const strdecoded = atob(str); | |
| 1099 | const u8array = new Uint8Array(strdecoded.length); | |
| 1100 | for (let j = 0; j < strdecoded.length; ++j) { | |
| 1101 | u8array[j] = strdecoded.charCodeAt(j); | |
| 1102 | } | |
| 1103 | const has_runs = u8array[0] === 0x3b; | |
| 1104 | const size = has_runs ? | |
| 1105 | ((u8array[2] | (u8array[3] << 8)) + 1) : | |
| 1106 | ((u8array[4] | (u8array[5] << 8) | (u8array[6] << 16) | (u8array[7] << 24))); | |
| 1107 | let i = has_runs ? 4 : 8; | |
| 1108 | let is_run; | |
| 1109 | if (has_runs) { | |
| 1110 | const is_run_len = Math.floor((size + 7) / 8); | |
| 1111 | is_run = u8array.slice(i, i + is_run_len); | |
| 1112 | i += is_run_len; | |
| 1113 | } else { | |
| 1114 | is_run = new Uint8Array(); | |
| 1115 | } | |
| 1116 | this.keys = []; | |
| 1117 | this.cardinalities = []; | |
| 1118 | for (let j = 0; j < size; ++j) { | |
| 1119 | this.keys.push(u8array[i] | (u8array[i + 1] << 8)); | |
| 1120 | i += 2; | |
| 1121 | this.cardinalities.push((u8array[i] | (u8array[i + 1] << 8)) + 1); | |
| 1122 | i += 2; | |
| 1123 | } | |
| 1124 | this.containers = []; | |
| 1125 | let offsets = null; | |
| 1126 | if (!has_runs || this.keys.length >= 4) { | |
| 1127 | offsets = []; | |
| 1128 | for (let j = 0; j < size; ++j) { | |
| 1129 | offsets.push(u8array[i] | (u8array[i + 1] << 8) | (u8array[i + 2] << 16) | | |
| 1130 | (u8array[i + 3] << 24)); | |
| 1131 | i += 4; | |
| 1132 | } | |
| 1133 | } | |
| 1134 | for (let j = 0; j < size; ++j) { | |
| 1135 | if (offsets && offsets[j] !== i) { | |
| 1136 | // eslint-disable-next-line no-console | |
| 1137 | console.log(this.containers); | |
| 1138 | throw new Error(`corrupt bitmap ${j}: ${i} / ${offsets[j]}`); | |
| 1139 | } | |
| 1140 | if (is_run[j >> 3] & (1 << (j & 0x7))) { | |
| 1141 | const runcount = (u8array[i] | (u8array[i + 1] << 8)); | |
| 1142 | i += 2; | |
| 1143 | this.containers.push(new RoaringBitmapRun( | |
| 1144 | runcount, | |
| 1145 | u8array.slice(i, i + (runcount * 4)), | |
| 1146 | )); | |
| 1147 | i += runcount * 4; | |
| 1148 | } else if (this.cardinalities[j] >= 4096) { | |
| 1149 | this.containers.push(new RoaringBitmapBits(u8array.slice(i, i + 8192))); | |
| 1150 | i += 8192; | |
| 1151 | } else { | |
| 1152 | const end = this.cardinalities[j] * 2; | |
| 1153 | this.containers.push(new RoaringBitmapArray( | |
| 1154 | this.cardinalities[j], | |
| 1155 | u8array.slice(i, i + end), | |
| 1156 | )); | |
| 1157 | i += end; | |
| 1158 | } | |
| 1159 | } | |
| 1160 | } | |
| 1161 | /** @param {number} keyvalue */ | |
| 1162 | contains(keyvalue) { | |
| 1163 | const key = keyvalue >> 16; | |
| 1164 | const value = keyvalue & 0xFFFF; | |
| 1165 | // Binary search algorithm copied from | |
| 1166 | // https://en.wikipedia.org/wiki/Binary_search#Procedure | |
| 1167 | // | |
| 1168 | // Format is required by specification to be sorted. | |
| 1169 | // Because keys are 16 bits and unique, length can't be | |
| 1170 | // bigger than 2**16, and because we have 32 bits of safe int, | |
| 1171 | // left + right can't overflow. | |
| 1172 | let left = 0; | |
| 1173 | let right = this.keys.length - 1; | |
| 1174 | while (left <= right) { | |
| 1175 | const mid = Math.floor((left + right) / 2); | |
| 1176 | const x = this.keys[mid]; | |
| 1177 | if (x < key) { | |
| 1178 | left = mid + 1; | |
| 1179 | } else if (x > key) { | |
| 1180 | right = mid - 1; | |
| 1181 | } else { | |
| 1182 | return this.containers[mid].contains(value); | |
| 1183 | } | |
| 1184 | } | |
| 1185 | return false; | |
| 1186 | } | |
| 1187 | } | |
| 1188 | 1143 | |
| 1189 | class RoaringBitmapRun { | |
| 1190 | /** | |
| 1191 | * @param {number} runcount | |
| 1192 | * @param {Uint8Array} array | |
| 1193 | */ | |
| 1194 | constructor(runcount, array) { | |
| 1195 | this.runcount = runcount; | |
| 1196 | this.array = array; | |
| 1197 | } | |
| 1198 | /** @param {number} value */ | |
| 1199 | contains(value) { | |
| 1200 | // Binary search algorithm copied from | |
| 1201 | // https://en.wikipedia.org/wiki/Binary_search#Procedure | |
| 1202 | // | |
| 1203 | // Since runcount is stored as 16 bits, left + right | |
| 1204 | // can't overflow. | |
| 1205 | let left = 0; | |
| 1206 | let right = this.runcount - 1; | |
| 1207 | while (left <= right) { | |
| 1208 | const mid = Math.floor((left + right) / 2); | |
| 1209 | const i = mid * 4; | |
| 1210 | const start = this.array[i] | (this.array[i + 1] << 8); | |
| 1211 | const lenm1 = this.array[i + 2] | (this.array[i + 3] << 8); | |
| 1212 | if ((start + lenm1) < value) { | |
| 1213 | left = mid + 1; | |
| 1214 | } else if (start > value) { | |
| 1215 | right = mid - 1; | |
| 1216 | } else { | |
| 1217 | return true; | |
| 1218 | } | |
| 1219 | } | |
| 1220 | return false; | |
| 1221 | } | |
| 1222 | } | |
| 1223 | class RoaringBitmapArray { | |
| 1224 | /** | |
| 1225 | * @param {number} cardinality | |
| 1226 | * @param {Uint8Array} array | |
| 1227 | */ | |
| 1228 | constructor(cardinality, array) { | |
| 1229 | this.cardinality = cardinality; | |
| 1230 | this.array = array; | |
| 1231 | } | |
| 1232 | /** @param {number} value */ | |
| 1233 | contains(value) { | |
| 1234 | // Binary search algorithm copied from | |
| 1235 | // https://en.wikipedia.org/wiki/Binary_search#Procedure | |
| 1236 | // | |
| 1237 | // Since cardinality can't be higher than 4096, left + right | |
| 1238 | // cannot overflow. | |
| 1239 | let left = 0; | |
| 1240 | let right = this.cardinality - 1; | |
| 1241 | while (left <= right) { | |
| 1242 | const mid = Math.floor((left + right) / 2); | |
| 1243 | const i = mid * 2; | |
| 1244 | const x = this.array[i] | (this.array[i + 1] << 8); | |
| 1245 | if (x < value) { | |
| 1246 | left = mid + 1; | |
| 1247 | } else if (x > value) { | |
| 1248 | right = mid - 1; | |
| 1249 | } else { | |
| 1250 | return true; | |
| 1251 | } | |
| 1252 | } | |
| 1253 | return false; | |
| 1254 | } | |
| 1255 | } | |
| 1256 | class RoaringBitmapBits { | |
| 1257 | /** | |
| 1258 | * @param {Uint8Array} array | |
| 1259 | */ | |
| 1260 | constructor(array) { | |
| 1261 | this.array = array; | |
| 1262 | } | |
| 1263 | /** @param {number} value */ | |
| 1264 | contains(value) { | |
| 1265 | return !!(this.array[value >> 3] & (1 << (value & 7))); | |
| 1266 | } | |
| 1267 | } | |
| 1144 | /** @type {Array<string>} */ | |
| 1145 | const EMPTY_STRING_ARRAY = []; | |
| 1146 | ||
| 1147 | /** @type {Array<rustdoc.FunctionType>} */ | |
| 1148 | const EMPTY_GENERICS_ARRAY = []; | |
| 1149 | ||
| 1150 | /** @type {Array<[number, rustdoc.FunctionType[]]>} */ | |
| 1151 | const EMPTY_BINDINGS_ARRAY = []; | |
| 1152 | ||
| 1153 | /** @type {Map<number, Array<any>>} */ | |
| 1154 | const EMPTY_BINDINGS_MAP = new Map(); | |
| 1268 | 1155 | |
| 1269 | 1156 | /** |
| 1270 | * A prefix tree, used for name-based search. | |
| 1271 | * | |
| 1272 | * This data structure is used to drive prefix matches, | |
| 1273 | * such as matching the query "link" to `LinkedList`, | |
| 1274 | * and Lev-distance matches, such as matching the | |
| 1275 | * query "hahsmap" to `HashMap`. Substring matches, | |
| 1276 | * such as "list" to `LinkedList`, are done with a | |
| 1277 | * tailTable that deep-links into this trie. | |
| 1278 | * | |
| 1279 | * children | |
| 1280 | * : A [sparse array] of subtrees. The array index | |
| 1281 | * is a charCode. | |
| 1282 | * | |
| 1283 | * [sparse array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/ | |
| 1284 | * Indexed_collections#sparse_arrays | |
| 1285 | * | |
| 1286 | * matches | |
| 1287 | * : A list of search index IDs for this node. | |
| 1288 | * | |
| 1289 | * @type {{ | |
| 1290 | * children: NameTrie[], | |
| 1291 | * matches: number[], | |
| 1292 | * }} | |
| 1157 | * @param {string|null} typename | |
| 1158 | * @returns {number} | |
| 1293 | 1159 | */ |
| 1294 | class NameTrie { | |
| 1295 | constructor() { | |
| 1296 | this.children = []; | |
| 1297 | this.matches = []; | |
| 1298 | } | |
| 1299 | /** | |
| 1300 | * @param {string} name | |
| 1301 | * @param {number} id | |
| 1302 | * @param {Map<string, NameTrie[]>} tailTable | |
| 1303 | */ | |
| 1304 | insert(name, id, tailTable) { | |
| 1305 | this.insertSubstring(name, 0, id, tailTable); | |
| 1306 | } | |
| 1307 | /** | |
| 1308 | * @param {string} name | |
| 1309 | * @param {number} substart | |
| 1310 | * @param {number} id | |
| 1311 | * @param {Map<string, NameTrie[]>} tailTable | |
| 1312 | */ | |
| 1313 | insertSubstring(name, substart, id, tailTable) { | |
| 1314 | const l = name.length; | |
| 1315 | if (substart === l) { | |
| 1316 | this.matches.push(id); | |
| 1317 | } else { | |
| 1318 | const sb = name.charCodeAt(substart); | |
| 1319 | let child; | |
| 1320 | if (this.children[sb] !== undefined) { | |
| 1321 | child = this.children[sb]; | |
| 1322 | } else { | |
| 1323 | child = new NameTrie(); | |
| 1324 | this.children[sb] = child; | |
| 1325 | /** @type {NameTrie[]} */ | |
| 1326 | let sste; | |
| 1327 | if (substart >= 2) { | |
| 1328 | const tail = name.substring(substart - 2, substart + 1); | |
| 1329 | const entry = tailTable.get(tail); | |
| 1330 | if (entry !== undefined) { | |
| 1331 | sste = entry; | |
| 1332 | } else { | |
| 1333 | sste = []; | |
| 1334 | tailTable.set(tail, sste); | |
| 1335 | } | |
| 1336 | sste.push(child); | |
| 1337 | } | |
| 1338 | } | |
| 1339 | child.insertSubstring(name, substart + 1, id, tailTable); | |
| 1340 | } | |
| 1341 | } | |
| 1342 | /** | |
| 1343 | * @param {string} name | |
| 1344 | * @param {Map<string, NameTrie[]>} tailTable | |
| 1345 | */ | |
| 1346 | search(name, tailTable) { | |
| 1347 | const results = new Set(); | |
| 1348 | this.searchSubstringPrefix(name, 0, results); | |
| 1349 | if (results.size < MAX_RESULTS && name.length >= 3) { | |
| 1350 | const levParams = name.length >= 6 ? | |
| 1351 | new Lev2TParametricDescription(name.length) : | |
| 1352 | new Lev1TParametricDescription(name.length); | |
| 1353 | this.searchLev(name, 0, levParams, results); | |
| 1354 | const tail = name.substring(0, 3); | |
| 1355 | const list = tailTable.get(tail); | |
| 1356 | if (list !== undefined) { | |
| 1357 | for (const entry of list) { | |
| 1358 | entry.searchSubstringPrefix(name, 3, results); | |
| 1359 | } | |
| 1360 | } | |
| 1361 | } | |
| 1362 | return [...results]; | |
| 1363 | } | |
| 1364 | /** | |
| 1365 | * @param {string} name | |
| 1366 | * @param {number} substart | |
| 1367 | * @param {Set<number>} results | |
| 1368 | */ | |
| 1369 | searchSubstringPrefix(name, substart, results) { | |
| 1370 | const l = name.length; | |
| 1371 | if (substart === l) { | |
| 1372 | for (const match of this.matches) { | |
| 1373 | results.add(match); | |
| 1374 | } | |
| 1375 | // breadth-first traversal orders prefix matches by length | |
| 1376 | /** @type {NameTrie[]} */ | |
| 1377 | let unprocessedChildren = []; | |
| 1378 | for (const child of this.children) { | |
| 1379 | if (child) { | |
| 1380 | unprocessedChildren.push(child); | |
| 1381 | } | |
| 1382 | } | |
| 1383 | /** @type {NameTrie[]} */ | |
| 1384 | let nextSet = []; | |
| 1385 | while (unprocessedChildren.length !== 0) { | |
| 1386 | /** @type {NameTrie} */ | |
| 1387 | // @ts-expect-error | |
| 1388 | const next = unprocessedChildren.pop(); | |
| 1389 | for (const child of next.children) { | |
| 1390 | if (child) { | |
| 1391 | nextSet.push(child); | |
| 1392 | } | |
| 1393 | } | |
| 1394 | for (const match of next.matches) { | |
| 1395 | results.add(match); | |
| 1396 | } | |
| 1397 | if (unprocessedChildren.length === 0) { | |
| 1398 | const tmp = unprocessedChildren; | |
| 1399 | unprocessedChildren = nextSet; | |
| 1400 | nextSet = tmp; | |
| 1401 | } | |
| 1402 | } | |
| 1403 | } else { | |
| 1404 | const sb = name.charCodeAt(substart); | |
| 1405 | if (this.children[sb] !== undefined) { | |
| 1406 | this.children[sb].searchSubstringPrefix(name, substart + 1, results); | |
| 1407 | } | |
| 1408 | } | |
| 1160 | function itemTypeFromName(typename) { | |
| 1161 | if (typename === null) { | |
| 1162 | return NO_TYPE_FILTER; | |
| 1409 | 1163 | } |
| 1410 | /** | |
| 1411 | * @param {string} name | |
| 1412 | * @param {number} substart | |
| 1413 | * @param {Lev2TParametricDescription|Lev1TParametricDescription} levParams | |
| 1414 | * @param {Set<number>} results | |
| 1415 | */ | |
| 1416 | searchLev(name, substart, levParams, results) { | |
| 1417 | const stack = [[this, 0]]; | |
| 1418 | const n = levParams.n; | |
| 1419 | while (stack.length !== 0) { | |
| 1420 | // It's not empty | |
| 1421 | //@ts-expect-error | |
| 1422 | const [trie, levState] = stack.pop(); | |
| 1423 | for (const [charCode, child] of trie.children.entries()) { | |
| 1424 | if (!child) { | |
| 1425 | continue; | |
| 1426 | } | |
| 1427 | const levPos = levParams.getPosition(levState); | |
| 1428 | const vector = levParams.getVector( | |
| 1429 | name, | |
| 1430 | charCode, | |
| 1431 | levPos, | |
| 1432 | Math.min(name.length, levPos + (2 * n) + 1), | |
| 1433 | ); | |
| 1434 | const newLevState = levParams.transition( | |
| 1435 | levState, | |
| 1436 | levPos, | |
| 1437 | vector, | |
| 1438 | ); | |
| 1439 | if (newLevState >= 0) { | |
| 1440 | stack.push([child, newLevState]); | |
| 1441 | if (levParams.isAccept(newLevState)) { | |
| 1442 | for (const match of child.matches) { | |
| 1443 | results.add(match); | |
| 1444 | } | |
| 1445 | } | |
| 1446 | } | |
| 1447 | } | |
| 1448 | } | |
| 1164 | const index = itemTypes.findIndex(i => i === typename); | |
| 1165 | if (index < 0) { | |
| 1166 | throw ["Unknown type filter ", typename]; | |
| 1449 | 1167 | } |
| 1168 | return index; | |
| 1450 | 1169 | } |
| 1451 | 1170 | |
| 1452 | 1171 | class DocSearch { |
| 1453 | 1172 | /** |
| 1454 | * @param {Map<string, rustdoc.RawSearchIndexCrate>} rawSearchIndex | |
| 1455 | 1173 | * @param {string} rootPath |
| 1456 | * @param {rustdoc.SearchState} searchState | |
| 1174 | * @param {stringdex.Database} database | |
| 1457 | 1175 | */ |
| 1458 | constructor(rawSearchIndex, rootPath, searchState) { | |
| 1459 | /** | |
| 1460 | * @type {Map<String, RoaringBitmap>} | |
| 1461 | */ | |
| 1462 | this.searchIndexDeprecated = new Map(); | |
| 1463 | /** | |
| 1464 | * @type {Map<String, RoaringBitmap>} | |
| 1465 | */ | |
| 1466 | this.searchIndexEmptyDesc = new Map(); | |
| 1467 | /** | |
| 1468 | * @type {Uint32Array} | |
| 1469 | */ | |
| 1470 | this.functionTypeFingerprint = new Uint32Array(0); | |
| 1471 | /** | |
| 1472 | * Map from normalized type names to integers. Used to make type search | |
| 1473 | * more efficient. | |
| 1474 | * | |
| 1475 | * @type {Map<string, {id: number, assocOnly: boolean}>} | |
| 1476 | */ | |
| 1477 | this.typeNameIdMap = new Map(); | |
| 1478 | /** | |
| 1479 | * Map from type ID to associated type name. Used for display, | |
| 1480 | * not for search. | |
| 1481 | * | |
| 1482 | * @type {Map<number, string>} | |
| 1483 | */ | |
| 1484 | this.assocTypeIdNameMap = new Map(); | |
| 1485 | this.ALIASES = new Map(); | |
| 1486 | this.FOUND_ALIASES = new Set(); | |
| 1176 | constructor(rootPath, database) { | |
| 1487 | 1177 | this.rootPath = rootPath; |
| 1488 | this.searchState = searchState; | |
| 1489 | ||
| 1490 | /** | |
| 1491 | * Special type name IDs for searching by array. | |
| 1492 | * @type {number} | |
| 1493 | */ | |
| 1494 | this.typeNameIdOfArray = this.buildTypeMapIndex("array"); | |
| 1495 | /** | |
| 1496 | * Special type name IDs for searching by slice. | |
| 1497 | * @type {number} | |
| 1498 | */ | |
| 1499 | this.typeNameIdOfSlice = this.buildTypeMapIndex("slice"); | |
| 1500 | /** | |
| 1501 | * Special type name IDs for searching by both array and slice (`[]` syntax). | |
| 1502 | * @type {number} | |
| 1503 | */ | |
| 1504 | this.typeNameIdOfArrayOrSlice = this.buildTypeMapIndex("[]"); | |
| 1505 | /** | |
| 1506 | * Special type name IDs for searching by tuple. | |
| 1507 | * @type {number} | |
| 1508 | */ | |
| 1509 | this.typeNameIdOfTuple = this.buildTypeMapIndex("tuple"); | |
| 1510 | /** | |
| 1511 | * Special type name IDs for searching by unit. | |
| 1512 | * @type {number} | |
| 1513 | */ | |
| 1514 | this.typeNameIdOfUnit = this.buildTypeMapIndex("unit"); | |
| 1515 | /** | |
| 1516 | * Special type name IDs for searching by both tuple and unit (`()` syntax). | |
| 1517 | * @type {number} | |
| 1518 | */ | |
| 1519 | this.typeNameIdOfTupleOrUnit = this.buildTypeMapIndex("()"); | |
| 1520 | /** | |
| 1521 | * Special type name IDs for searching `fn`. | |
| 1522 | * @type {number} | |
| 1523 | */ | |
| 1524 | this.typeNameIdOfFn = this.buildTypeMapIndex("fn"); | |
| 1525 | /** | |
| 1526 | * Special type name IDs for searching `fnmut`. | |
| 1527 | * @type {number} | |
| 1528 | */ | |
| 1529 | this.typeNameIdOfFnMut = this.buildTypeMapIndex("fnmut"); | |
| 1530 | /** | |
| 1531 | * Special type name IDs for searching `fnonce`. | |
| 1532 | * @type {number} | |
| 1533 | */ | |
| 1534 | this.typeNameIdOfFnOnce = this.buildTypeMapIndex("fnonce"); | |
| 1535 | /** | |
| 1536 | * Special type name IDs for searching higher order functions (`->` syntax). | |
| 1537 | * @type {number} | |
| 1538 | */ | |
| 1539 | this.typeNameIdOfHof = this.buildTypeMapIndex("->"); | |
| 1540 | /** | |
| 1541 | * Special type name IDs the output assoc type. | |
| 1542 | * @type {number} | |
| 1543 | */ | |
| 1544 | this.typeNameIdOfOutput = this.buildTypeMapIndex("output", true); | |
| 1545 | /** | |
| 1546 | * Special type name IDs for searching by reference. | |
| 1547 | * @type {number} | |
| 1548 | */ | |
| 1549 | this.typeNameIdOfReference = this.buildTypeMapIndex("reference"); | |
| 1178 | this.database = database; | |
| 1550 | 1179 | |
| 1551 | /** | |
| 1552 | * Empty, immutable map used in item search types with no bindings. | |
| 1553 | * | |
| 1554 | * @type {Map<number, Array<any>>} | |
| 1555 | */ | |
| 1556 | this.EMPTY_BINDINGS_MAP = new Map(); | |
| 1180 | this.typeNameIdOfOutput = -1; | |
| 1181 | this.typeNameIdOfArray = -1; | |
| 1182 | this.typeNameIdOfSlice = -1; | |
| 1183 | this.typeNameIdOfArrayOrSlice = -1; | |
| 1184 | this.typeNameIdOfTuple = -1; | |
| 1185 | this.typeNameIdOfUnit = -1; | |
| 1186 | this.typeNameIdOfTupleOrUnit = -1; | |
| 1187 | this.typeNameIdOfReference = -1; | |
| 1188 | this.typeNameIdOfHof = -1; | |
| 1557 | 1189 | |
| 1558 | /** | |
| 1559 | * Empty, immutable map used in item search types with no bindings. | |
| 1560 | * | |
| 1561 | * @type {Array<any>} | |
| 1562 | */ | |
| 1563 | this.EMPTY_GENERICS_ARRAY = []; | |
| 1190 | this.utf8decoder = new TextDecoder(); | |
| 1564 | 1191 | |
| 1565 | /** | |
| 1566 | * Object pool for function types with no bindings or generics. | |
| 1567 | * This is reset after loading the index. | |
| 1568 | * | |
| 1569 | * @type {Map<number|null, rustdoc.FunctionType>} | |
| 1570 | */ | |
| 1192 | /** @type {Map<number|null, rustdoc.FunctionType>} */ | |
| 1571 | 1193 | this.TYPES_POOL = new Map(); |
| 1572 | ||
| 1573 | /** | |
| 1574 | * A trie for finding items by name. | |
| 1575 | * This is used for edit distance and prefix finding. | |
| 1576 | * | |
| 1577 | * @type {NameTrie} | |
| 1578 | */ | |
| 1579 | this.nameTrie = new NameTrie(); | |
| 1580 | ||
| 1581 | /** | |
| 1582 | * Find items by 3-substring. This is a map from three-char | |
| 1583 | * prefixes into lists of subtries. | |
| 1584 | */ | |
| 1585 | this.tailTable = new Map(); | |
| 1586 | ||
| 1587 | /** | |
| 1588 | * @type {Array<rustdoc.Row>} | |
| 1589 | */ | |
| 1590 | this.searchIndex = this.buildIndex(rawSearchIndex); | |
| 1591 | 1194 | } |
| 1592 | 1195 | |
| 1593 | 1196 | /** |
| 1594 | * Add an item to the type Name->ID map, or, if one already exists, use it. | |
| 1595 | * Returns the number. If name is "" or null, return null (pure generic). | |
| 1596 | * | |
| 1597 | * This is effectively string interning, so that function matching can be | |
| 1598 | * done more quickly. Two types with the same name but different item kinds | |
| 1599 | * get the same ID. | |
| 1600 | * | |
| 1601 | * @template T extends string | |
| 1602 | * @overload | |
| 1603 | * @param {T} name | |
| 1604 | * @param {boolean=} isAssocType - True if this is an assoc type | |
| 1605 | * @returns {T extends "" ? null : number} | |
| 1606 | * | |
| 1607 | * @param {string} name | |
| 1608 | * @param {boolean=} isAssocType | |
| 1609 | * @returns {number | null} | |
| 1610 | * | |
| 1197 | * Load search index. If you do not call this function, `execQuery` | |
| 1198 | * will never fulfill. | |
| 1611 | 1199 | */ |
| 1612 | buildTypeMapIndex(name, isAssocType) { | |
| 1613 | if (name === "" || name === null) { | |
| 1614 | return null; | |
| 1615 | } | |
| 1616 | ||
| 1617 | const obj = this.typeNameIdMap.get(name); | |
| 1618 | if (obj !== undefined) { | |
| 1619 | obj.assocOnly = !!(isAssocType && obj.assocOnly); | |
| 1620 | return obj.id; | |
| 1621 | } else { | |
| 1622 | const id = this.typeNameIdMap.size; | |
| 1623 | this.typeNameIdMap.set(name, { id, assocOnly: !!isAssocType }); | |
| 1624 | return id; | |
| 1200 | async buildIndex() { | |
| 1201 | const nn = this.database.getIndex("normalizedName"); | |
| 1202 | if (!nn) { | |
| 1203 | return; | |
| 1625 | 1204 | } |
| 1205 | // Each of these identifiers are used specially by | |
| 1206 | // type-driven search. | |
| 1207 | const [ | |
| 1208 | // output is the special associated type that goes | |
| 1209 | // after the arrow: the type checker desugars | |
| 1210 | // the path `Fn(a) -> b` into `Fn<Output=b, (a)>` | |
| 1211 | output, | |
| 1212 | // fn, fnmut, and fnonce all match `->` | |
| 1213 | fn, | |
| 1214 | fnMut, | |
| 1215 | fnOnce, | |
| 1216 | hof, | |
| 1217 | // array and slice both match `[]` | |
| 1218 | array, | |
| 1219 | slice, | |
| 1220 | arrayOrSlice, | |
| 1221 | // tuple and unit both match `()` | |
| 1222 | tuple, | |
| 1223 | unit, | |
| 1224 | tupleOrUnit, | |
| 1225 | // reference matches `&` | |
| 1226 | reference, | |
| 1227 | // never matches `!` | |
| 1228 | never, | |
| 1229 | ] = await Promise.all([ | |
| 1230 | nn.search("output"), | |
| 1231 | nn.search("fn"), | |
| 1232 | nn.search("fnmut"), | |
| 1233 | nn.search("fnonce"), | |
| 1234 | nn.search("->"), | |
| 1235 | nn.search("array"), | |
| 1236 | nn.search("slice"), | |
| 1237 | nn.search("[]"), | |
| 1238 | nn.search("tuple"), | |
| 1239 | nn.search("unit"), | |
| 1240 | nn.search("()"), | |
| 1241 | nn.search("reference"), | |
| 1242 | nn.search("never"), | |
| 1243 | ]); | |
| 1244 | /** | |
| 1245 | * @param {stringdex.Trie|null|undefined} trie | |
| 1246 | * @param {rustdoc.ItemType} ty | |
| 1247 | * @param {string} modulePath | |
| 1248 | * @returns {Promise<number>} | |
| 1249 | * */ | |
| 1250 | const first = async(trie, ty, modulePath) => { | |
| 1251 | if (trie) { | |
| 1252 | for (const id of trie.matches().entries()) { | |
| 1253 | const pathData = await this.getPathData(id); | |
| 1254 | if (pathData && pathData.ty === ty && pathData.modulePath === modulePath) { | |
| 1255 | return id; | |
| 1256 | } | |
| 1257 | } | |
| 1258 | } | |
| 1259 | return -1; | |
| 1260 | }; | |
| 1261 | this.typeNameIdOfOutput = await first(output, TY_ASSOCTYPE, ""); | |
| 1262 | this.typeNameIdOfFnPtr = await first(fn, TY_PRIMITIVE, ""); | |
| 1263 | this.typeNameIdOfFn = await first(fn, TY_TRAIT, "core::ops"); | |
| 1264 | this.typeNameIdOfFnMut = await first(fnMut, TY_TRAIT, "core::ops"); | |
| 1265 | this.typeNameIdOfFnOnce = await first(fnOnce, TY_TRAIT, "core::ops"); | |
| 1266 | this.typeNameIdOfArray = await first(array, TY_PRIMITIVE, ""); | |
| 1267 | this.typeNameIdOfSlice = await first(slice, TY_PRIMITIVE, ""); | |
| 1268 | this.typeNameIdOfArrayOrSlice = await first(arrayOrSlice, TY_PRIMITIVE, ""); | |
| 1269 | this.typeNameIdOfTuple = await first(tuple, TY_PRIMITIVE, ""); | |
| 1270 | this.typeNameIdOfUnit = await first(unit, TY_PRIMITIVE, ""); | |
| 1271 | this.typeNameIdOfTupleOrUnit = await first(tupleOrUnit, TY_PRIMITIVE, ""); | |
| 1272 | this.typeNameIdOfReference = await first(reference, TY_PRIMITIVE, ""); | |
| 1273 | this.typeNameIdOfHof = await first(hof, TY_PRIMITIVE, ""); | |
| 1274 | this.typeNameIdOfNever = await first(never, TY_PRIMITIVE, ""); | |
| 1626 | 1275 | } |
| 1627 | 1276 | |
| 1628 | 1277 | /** |
| 1629 | * Convert a list of RawFunctionType / ID to object-based FunctionType. | |
| 1630 | * | |
| 1631 | * Crates often have lots of functions in them, and it's common to have a large number of | |
| 1632 | * functions that operate on a small set of data types, so the search index compresses them | |
| 1633 | * by encoding function parameter and return types as indexes into an array of names. | |
| 1634 | * | |
| 1635 | * Even when a general-purpose compression algorithm is used, this is still a win. | |
| 1636 | * I checked. https://github.com/rust-lang/rust/pull/98475#issue-1284395985 | |
| 1278 | * Parses the query. | |
| 1637 | 1279 | * |
| 1638 | * The format for individual function types is encoded in | |
| 1639 | * librustdoc/html/render/mod.rs: impl Serialize for RenderType | |
| 1280 | * The supported syntax by this parser is given in the rustdoc book chapter | |
| 1281 | * /src/doc/rustdoc/src/read-documentation/search.md | |
| 1640 | 1282 | * |
| 1641 | * @param {null|Array<rustdoc.RawFunctionType>} types | |
| 1642 | * @param {Array<{ | |
| 1643 | * name: string, | |
| 1644 | * ty: number, | |
| 1645 | * path: string|null, | |
| 1646 | * exactPath: string|null, | |
| 1647 | * unboxFlag: boolean | |
| 1648 | * }>} paths | |
| 1649 | * @param {Array<{ | |
| 1650 | * name: string, | |
| 1651 | * ty: number, | |
| 1652 | * path: string|null, | |
| 1653 | * exactPath: string|null, | |
| 1654 | * unboxFlag: boolean, | |
| 1655 | * }>} lowercasePaths | |
| 1283 | * When adding new things to the parser, add them there, too! | |
| 1656 | 1284 | * |
| 1657 | * @return {Array<rustdoc.FunctionType>} | |
| 1658 | */ | |
| 1659 | buildItemSearchTypeAll(types, paths, lowercasePaths) { | |
| 1660 | return types && types.length > 0 ? | |
| 1661 | types.map(type => this.buildItemSearchType(type, paths, lowercasePaths)) : | |
| 1662 | this.EMPTY_GENERICS_ARRAY; | |
| 1663 | } | |
| 1664 | ||
| 1665 | /** | |
| 1666 | * Converts a single type. | |
| 1285 | * @param {string} userQuery - The user query | |
| 1667 | 1286 | * |
| 1668 | * @param {rustdoc.RawFunctionType} type | |
| 1669 | * @param {Array<{ | |
| 1670 | * name: string, | |
| 1671 | * ty: number, | |
| 1672 | * path: string|null, | |
| 1673 | * exactPath: string|null, | |
| 1674 | * unboxFlag: boolean | |
| 1675 | * }>} paths | |
| 1676 | * @param {Array<{ | |
| 1677 | * name: string, | |
| 1678 | * ty: number, | |
| 1679 | * path: string|null, | |
| 1680 | * exactPath: string|null, | |
| 1681 | * unboxFlag: boolean, | |
| 1682 | * }>} lowercasePaths | |
| 1683 | * @param {boolean=} isAssocType | |
| 1287 | * @return {rustdoc.ParsedQuery<rustdoc.ParserQueryElement>} - The parsed query | |
| 1684 | 1288 | */ |
| 1685 | buildItemSearchType(type, paths, lowercasePaths, isAssocType) { | |
| 1686 | const PATH_INDEX_DATA = 0; | |
| 1687 | const GENERICS_DATA = 1; | |
| 1688 | const BINDINGS_DATA = 2; | |
| 1689 | let pathIndex, generics, bindings; | |
| 1690 | if (typeof type === "number") { | |
| 1691 | pathIndex = type; | |
| 1692 | generics = this.EMPTY_GENERICS_ARRAY; | |
| 1693 | bindings = this.EMPTY_BINDINGS_MAP; | |
| 1694 | } else { | |
| 1695 | pathIndex = type[PATH_INDEX_DATA]; | |
| 1696 | generics = this.buildItemSearchTypeAll( | |
| 1697 | type[GENERICS_DATA], | |
| 1698 | paths, | |
| 1699 | lowercasePaths, | |
| 1700 | ); | |
| 1701 | // @ts-expect-error | |
| 1702 | if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { | |
| 1703 | // @ts-expect-error | |
| 1704 | bindings = new Map(type[BINDINGS_DATA].map(binding => { | |
| 1705 | const [assocType, constraints] = binding; | |
| 1706 | // Associated type constructors are represented sloppily in rustdoc's | |
| 1707 | // type search, to make the engine simpler. | |
| 1708 | // | |
| 1709 | // MyType<Output<T>=Result<T>> is equivalent to MyType<Output<Result<T>>=T> | |
| 1710 | // and both are, essentially | |
| 1711 | // MyType<Output=(T, Result<T>)>, except the tuple isn't actually there. | |
| 1712 | // It's more like the value of a type binding is naturally an array, | |
| 1713 | // which rustdoc calls "constraints". | |
| 1714 | // | |
| 1715 | // As a result, the key should never have generics on it. | |
| 1716 | return [ | |
| 1717 | this.buildItemSearchType(assocType, paths, lowercasePaths, true).id, | |
| 1718 | this.buildItemSearchTypeAll(constraints, paths, lowercasePaths), | |
| 1719 | ]; | |
| 1720 | })); | |
| 1721 | } else { | |
| 1722 | bindings = this.EMPTY_BINDINGS_MAP; | |
| 1723 | } | |
| 1724 | } | |
| 1289 | static parseQuery(userQuery) { | |
| 1725 | 1290 | /** |
| 1726 | * @type {rustdoc.FunctionType} | |
| 1727 | */ | |
| 1728 | let result; | |
| 1729 | if (pathIndex < 0) { | |
| 1730 | // types less than 0 are generic parameters | |
| 1731 | // the actual names of generic parameters aren't stored, since they aren't API | |
| 1732 | result = { | |
| 1733 | id: pathIndex, | |
| 1734 | name: "", | |
| 1735 | ty: TY_GENERIC, | |
| 1736 | path: null, | |
| 1737 | exactPath: null, | |
| 1738 | generics, | |
| 1739 | bindings, | |
| 1740 | unboxFlag: true, | |
| 1741 | }; | |
| 1742 | } else if (pathIndex === 0) { | |
| 1743 | // `0` is used as a sentinel because it's fewer bytes than `null` | |
| 1744 | result = { | |
| 1745 | id: null, | |
| 1746 | name: "", | |
| 1747 | ty: null, | |
| 1748 | path: null, | |
| 1749 | exactPath: null, | |
| 1750 | generics, | |
| 1751 | bindings, | |
| 1752 | unboxFlag: true, | |
| 1753 | }; | |
| 1754 | } else { | |
| 1755 | const item = lowercasePaths[pathIndex - 1]; | |
| 1756 | const id = this.buildTypeMapIndex(item.name, isAssocType); | |
| 1757 | if (isAssocType && id !== null) { | |
| 1758 | this.assocTypeIdNameMap.set(id, paths[pathIndex - 1].name); | |
| 1759 | } | |
| 1760 | result = { | |
| 1761 | id, | |
| 1762 | name: paths[pathIndex - 1].name, | |
| 1763 | ty: item.ty, | |
| 1764 | path: item.path, | |
| 1765 | exactPath: item.exactPath, | |
| 1766 | generics, | |
| 1767 | bindings, | |
| 1768 | unboxFlag: item.unboxFlag, | |
| 1769 | }; | |
| 1770 | } | |
| 1771 | const cr = this.TYPES_POOL.get(result.id); | |
| 1772 | if (cr) { | |
| 1773 | // Shallow equality check. Since this function is used | |
| 1774 | // to construct every type object, this should be mostly | |
| 1775 | // equivalent to a deep equality check, except if there's | |
| 1776 | // a conflict, we don't keep the old one around, so it's | |
| 1777 | // not a fully precise implementation of hashcons. | |
| 1778 | if (cr.generics.length === result.generics.length && | |
| 1779 | cr.generics !== result.generics && | |
| 1780 | cr.generics.every((x, i) => result.generics[i] === x) | |
| 1781 | ) { | |
| 1782 | result.generics = cr.generics; | |
| 1783 | } | |
| 1784 | if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) { | |
| 1785 | let ok = true; | |
| 1786 | for (const [k, v] of cr.bindings.entries()) { | |
| 1787 | // @ts-expect-error | |
| 1788 | const v2 = result.bindings.get(v); | |
| 1789 | if (!v2) { | |
| 1790 | ok = false; | |
| 1791 | break; | |
| 1792 | } | |
| 1793 | if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) { | |
| 1794 | result.bindings.set(k, v); | |
| 1795 | } else if (v !== v2) { | |
| 1796 | ok = false; | |
| 1797 | break; | |
| 1798 | } | |
| 1799 | } | |
| 1800 | if (ok) { | |
| 1801 | result.bindings = cr.bindings; | |
| 1802 | } | |
| 1803 | } | |
| 1804 | if (cr.ty === result.ty && cr.path === result.path | |
| 1805 | && cr.bindings === result.bindings && cr.generics === result.generics | |
| 1806 | && cr.ty === result.ty && cr.name === result.name | |
| 1807 | && cr.unboxFlag === result.unboxFlag | |
| 1808 | ) { | |
| 1809 | return cr; | |
| 1810 | } | |
| 1811 | } | |
| 1812 | this.TYPES_POOL.set(result.id, result); | |
| 1813 | return result; | |
| 1814 | } | |
| 1815 | ||
| 1816 | /** | |
| 1817 | * Type fingerprints allow fast, approximate matching of types. | |
| 1818 | * | |
| 1819 | * This algo creates a compact representation of the type set using a Bloom filter. | |
| 1820 | * This fingerprint is used three ways: | |
| 1821 | * | |
| 1822 | * - It accelerates the matching algorithm by checking the function fingerprint against the | |
| 1823 | * query fingerprint. If any bits are set in the query but not in the function, it can't | |
| 1824 | * match. | |
| 1825 | * | |
| 1826 | * - The fourth section has the number of items in the set. | |
| 1827 | * This is the distance function, used for filtering and for sorting. | |
| 1828 | * | |
| 1829 | * [^1]: Distance is the relatively naive metric of counting the number of distinct items in | |
| 1830 | * the function that are not present in the query. | |
| 1831 | * | |
| 1832 | * @param {rustdoc.FingerprintableType} type - a single type | |
| 1833 | * @param {Uint32Array} output - write the fingerprint to this data structure: uses 128 bits | |
| 1834 | */ | |
| 1835 | buildFunctionTypeFingerprint(type, output) { | |
| 1836 | let input = type.id; | |
| 1837 | // All forms of `[]`/`()`/`->` get collapsed down to one thing in the bloom filter. | |
| 1838 | // Differentiating between arrays and slices, if the user asks for it, is | |
| 1839 | // still done in the matching algorithm. | |
| 1840 | if (input === this.typeNameIdOfArray || input === this.typeNameIdOfSlice) { | |
| 1841 | input = this.typeNameIdOfArrayOrSlice; | |
| 1842 | } | |
| 1843 | if (input === this.typeNameIdOfTuple || input === this.typeNameIdOfUnit) { | |
| 1844 | input = this.typeNameIdOfTupleOrUnit; | |
| 1845 | } | |
| 1846 | if (input === this.typeNameIdOfFn || input === this.typeNameIdOfFnMut || | |
| 1847 | input === this.typeNameIdOfFnOnce) { | |
| 1848 | input = this.typeNameIdOfHof; | |
| 1849 | } | |
| 1850 | /** | |
| 1851 | * http://burtleburtle.net/bob/hash/integer.html | |
| 1852 | * ~~ is toInt32. It's used before adding, so | |
| 1853 | * the number stays in safe integer range. | |
| 1854 | * @param {number} k | |
| 1855 | */ | |
| 1856 | const hashint1 = k => { | |
| 1857 | k = (~~k + 0x7ed55d16) + (k << 12); | |
| 1858 | k = (k ^ 0xc761c23c) ^ (k >>> 19); | |
| 1859 | k = (~~k + 0x165667b1) + (k << 5); | |
| 1860 | k = (~~k + 0xd3a2646c) ^ (k << 9); | |
| 1861 | k = (~~k + 0xfd7046c5) + (k << 3); | |
| 1862 | return (k ^ 0xb55a4f09) ^ (k >>> 16); | |
| 1863 | }; | |
| 1864 | /** @param {number} k */ | |
| 1865 | const hashint2 = k => { | |
| 1866 | k = ~k + (k << 15); | |
| 1867 | k ^= k >>> 12; | |
| 1868 | k += k << 2; | |
| 1869 | k ^= k >>> 4; | |
| 1870 | k = Math.imul(k, 2057); | |
| 1871 | return k ^ (k >> 16); | |
| 1872 | }; | |
| 1873 | if (input !== null) { | |
| 1874 | const h0a = hashint1(input); | |
| 1875 | const h0b = hashint2(input); | |
| 1876 | // Less Hashing, Same Performance: Building a Better Bloom Filter | |
| 1877 | // doi=10.1.1.72.2442 | |
| 1878 | const h1a = ~~(h0a + Math.imul(h0b, 2)); | |
| 1879 | const h1b = ~~(h0a + Math.imul(h0b, 3)); | |
| 1880 | const h2a = ~~(h0a + Math.imul(h0b, 4)); | |
| 1881 | const h2b = ~~(h0a + Math.imul(h0b, 5)); | |
| 1882 | output[0] |= (1 << (h0a % 32)) | (1 << (h1b % 32)); | |
| 1883 | output[1] |= (1 << (h1a % 32)) | (1 << (h2b % 32)); | |
| 1884 | output[2] |= (1 << (h2a % 32)) | (1 << (h0b % 32)); | |
| 1885 | // output[3] is the total number of items in the type signature | |
| 1886 | output[3] += 1; | |
| 1887 | } | |
| 1888 | for (const g of type.generics) { | |
| 1889 | this.buildFunctionTypeFingerprint(g, output); | |
| 1890 | } | |
| 1891 | /** | |
| 1892 | * @type {{ | |
| 1893 | * id: number|null, | |
| 1894 | * ty: number, | |
| 1895 | * generics: rustdoc.FingerprintableType[], | |
| 1896 | * bindings: Map<number, rustdoc.FingerprintableType[]> | |
| 1897 | * }} | |
| 1898 | */ | |
| 1899 | const fb = { | |
| 1900 | id: null, | |
| 1901 | ty: 0, | |
| 1902 | generics: this.EMPTY_GENERICS_ARRAY, | |
| 1903 | bindings: this.EMPTY_BINDINGS_MAP, | |
| 1904 | }; | |
| 1905 | for (const [k, v] of type.bindings.entries()) { | |
| 1906 | fb.id = k; | |
| 1907 | fb.generics = v; | |
| 1908 | this.buildFunctionTypeFingerprint(fb, output); | |
| 1909 | } | |
| 1910 | } | |
| 1911 | ||
| 1912 | /** | |
| 1913 | * Convert raw search index into in-memory search index. | |
| 1914 | * | |
| 1915 | * @param {Map<string, rustdoc.RawSearchIndexCrate>} rawSearchIndex | |
| 1916 | * @returns {rustdoc.Row[]} | |
| 1917 | */ | |
| 1918 | buildIndex(rawSearchIndex) { | |
| 1919 | /** | |
| 1920 | * Convert from RawFunctionSearchType to FunctionSearchType. | |
| 1921 | * | |
| 1922 | * Crates often have lots of functions in them, and function signatures are sometimes | |
| 1923 | * complex, so rustdoc uses a pretty tight encoding for them. This function converts it | |
| 1924 | * to a simpler, object-based encoding so that the actual search code is more readable | |
| 1925 | * and easier to debug. | |
| 1926 | * | |
| 1927 | * The raw function search type format is generated using serde in | |
| 1928 | * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string | |
| 1929 | * | |
| 1930 | * @param {Array<{ | |
| 1931 | * name: string, | |
| 1932 | * ty: number, | |
| 1933 | * path: string|null, | |
| 1934 | * exactPath: string|null, | |
| 1935 | * unboxFlag: boolean | |
| 1936 | * }>} paths | |
| 1937 | * @param {Array<{ | |
| 1938 | * name: string, | |
| 1939 | * ty: number, | |
| 1940 | * path: string|null, | |
| 1941 | * exactPath: string|null, | |
| 1942 | * unboxFlag: boolean | |
| 1943 | * }>} lowercasePaths | |
| 1944 | * | |
| 1945 | * @return {function(rustdoc.RawFunctionSearchType): null|rustdoc.FunctionSearchType} | |
| 1946 | */ | |
| 1947 | const buildFunctionSearchTypeCallback = (paths, lowercasePaths) => { | |
| 1948 | /** | |
| 1949 | * @param {rustdoc.RawFunctionSearchType} functionSearchType | |
| 1950 | */ | |
| 1951 | const cb = functionSearchType => { | |
| 1952 | if (functionSearchType === 0) { | |
| 1953 | return null; | |
| 1954 | } | |
| 1955 | const INPUTS_DATA = 0; | |
| 1956 | const OUTPUT_DATA = 1; | |
| 1957 | /** @type {rustdoc.FunctionType[]} */ | |
| 1958 | let inputs; | |
| 1959 | /** @type {rustdoc.FunctionType[]} */ | |
| 1960 | let output; | |
| 1961 | if (typeof functionSearchType[INPUTS_DATA] === "number") { | |
| 1962 | inputs = [ | |
| 1963 | this.buildItemSearchType( | |
| 1964 | functionSearchType[INPUTS_DATA], | |
| 1965 | paths, | |
| 1966 | lowercasePaths, | |
| 1967 | ), | |
| 1968 | ]; | |
| 1969 | } else { | |
| 1970 | inputs = this.buildItemSearchTypeAll( | |
| 1971 | functionSearchType[INPUTS_DATA], | |
| 1972 | paths, | |
| 1973 | lowercasePaths, | |
| 1974 | ); | |
| 1975 | } | |
| 1976 | if (functionSearchType.length > 1) { | |
| 1977 | if (typeof functionSearchType[OUTPUT_DATA] === "number") { | |
| 1978 | output = [ | |
| 1979 | this.buildItemSearchType( | |
| 1980 | functionSearchType[OUTPUT_DATA], | |
| 1981 | paths, | |
| 1982 | lowercasePaths, | |
| 1983 | ), | |
| 1984 | ]; | |
| 1985 | } else { | |
| 1986 | output = this.buildItemSearchTypeAll( | |
| 1987 | // @ts-expect-error | |
| 1988 | functionSearchType[OUTPUT_DATA], | |
| 1989 | paths, | |
| 1990 | lowercasePaths, | |
| 1991 | ); | |
| 1992 | } | |
| 1993 | } else { | |
| 1994 | output = []; | |
| 1995 | } | |
| 1996 | const where_clause = []; | |
| 1997 | const l = functionSearchType.length; | |
| 1998 | for (let i = 2; i < l; ++i) { | |
| 1999 | where_clause.push(typeof functionSearchType[i] === "number" | |
| 2000 | // @ts-expect-error | |
| 2001 | ? [this.buildItemSearchType(functionSearchType[i], paths, lowercasePaths)] | |
| 2002 | : this.buildItemSearchTypeAll( | |
| 2003 | // @ts-expect-error | |
| 2004 | functionSearchType[i], | |
| 2005 | paths, | |
| 2006 | lowercasePaths, | |
| 2007 | )); | |
| 2008 | } | |
| 2009 | return { | |
| 2010 | inputs, output, where_clause, | |
| 2011 | }; | |
| 2012 | }; | |
| 2013 | return cb; | |
| 2014 | }; | |
| 2015 | ||
| 2016 | /** @type {rustdoc.Row[]} */ | |
| 2017 | const searchIndex = []; | |
| 2018 | let currentIndex = 0; | |
| 2019 | let id = 0; | |
| 2020 | ||
| 2021 | // Function type fingerprints are 128-bit bloom filters that are used to | |
| 2022 | // estimate the distance between function and query. | |
| 2023 | // This loop counts the number of items to allocate a fingerprint for. | |
| 2024 | for (const crate of rawSearchIndex.values()) { | |
| 2025 | // Each item gets an entry in the fingerprint array, and the crate | |
| 2026 | // does, too | |
| 2027 | id += crate.t.length + 1; | |
| 2028 | } | |
| 2029 | this.functionTypeFingerprint = new Uint32Array((id + 1) * 4); | |
| 2030 | // This loop actually generates the search item indexes, including | |
| 2031 | // normalized names, type signature objects and fingerprints, and aliases. | |
| 2032 | id = 0; | |
| 2033 | ||
| 2034 | /** @type {Array<[string, { [key: string]: Array<number> }, number]>} */ | |
| 2035 | const allAliases = []; | |
| 2036 | for (const [crate, crateCorpus] of rawSearchIndex) { | |
| 2037 | // a string representing the lengths of each description shard | |
| 2038 | // a string representing the list of function types | |
| 2039 | const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => { | |
| 2040 | /** @type {number} */ | |
| 2041 | // @ts-expect-error | |
| 2042 | const n = noop; | |
| 2043 | return n; | |
| 2044 | }); | |
| 2045 | let descShard = { | |
| 2046 | crate, | |
| 2047 | shard: 0, | |
| 2048 | start: 0, | |
| 2049 | len: itemDescShardDecoder.next(), | |
| 2050 | promise: null, | |
| 2051 | resolve: null, | |
| 2052 | }; | |
| 2053 | const descShardList = [descShard]; | |
| 2054 | ||
| 2055 | // Deprecated items and items with no description | |
| 2056 | this.searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c)); | |
| 2057 | this.searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); | |
| 2058 | let descIndex = 0; | |
| 2059 | ||
| 2060 | /** | |
| 2061 | * List of generic function type parameter names. | |
| 2062 | * Used for display, not for searching. | |
| 2063 | * @type {string[]} | |
| 2064 | */ | |
| 2065 | let lastParamNames = []; | |
| 2066 | ||
| 2067 | // This object should have exactly the same set of fields as the "row" | |
| 2068 | // object defined below. Your JavaScript runtime will thank you. | |
| 2069 | // https://mathiasbynens.be/notes/shapes-ics | |
| 2070 | let normalizedName = crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""); | |
| 2071 | const crateRow = { | |
| 2072 | crate, | |
| 2073 | ty: 3, // == ExternCrate | |
| 2074 | name: crate, | |
| 2075 | path: "", | |
| 2076 | descShard, | |
| 2077 | descIndex, | |
| 2078 | exactPath: "", | |
| 2079 | desc: crateCorpus.doc, | |
| 2080 | parent: undefined, | |
| 2081 | type: null, | |
| 2082 | paramNames: lastParamNames, | |
| 2083 | id, | |
| 2084 | word: crate, | |
| 2085 | normalizedName, | |
| 2086 | bitIndex: 0, | |
| 2087 | implDisambiguator: null, | |
| 2088 | }; | |
| 2089 | this.nameTrie.insert(normalizedName, id, this.tailTable); | |
| 2090 | id += 1; | |
| 2091 | searchIndex.push(crateRow); | |
| 2092 | currentIndex += 1; | |
| 2093 | // it's not undefined | |
| 2094 | // @ts-expect-error | |
| 2095 | if (!this.searchIndexEmptyDesc.get(crate).contains(0)) { | |
| 2096 | descIndex += 1; | |
| 2097 | } | |
| 2098 | ||
| 2099 | // see `RawSearchIndexCrate` in `rustdoc.d.ts` for a more | |
| 2100 | // up to date description of these fields | |
| 2101 | const itemTypes = crateCorpus.t; | |
| 2102 | // an array of (String) item names | |
| 2103 | const itemNames = crateCorpus.n; | |
| 2104 | // an array of [(Number) item index, | |
| 2105 | // (String) full path] | |
| 2106 | // an item whose index is not present will fall back to the previous present path | |
| 2107 | // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present, | |
| 2108 | // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11 | |
| 2109 | const itemPaths = new Map(crateCorpus.q); | |
| 2110 | // An array of [(Number) item index, (Number) path index] | |
| 2111 | // Used to de-duplicate inlined and re-exported stuff | |
| 2112 | const itemReexports = new Map(crateCorpus.r); | |
| 2113 | // an array of (Number) the parent path index + 1 to `paths`, or 0 if none | |
| 2114 | const itemParentIdxDecoder = new VlqHexDecoder(crateCorpus.i, noop => noop); | |
| 2115 | // a map Number, string for impl disambiguators | |
| 2116 | const implDisambiguator = new Map(crateCorpus.b); | |
| 2117 | const rawPaths = crateCorpus.p; | |
| 2118 | const aliases = crateCorpus.a; | |
| 2119 | // an array of [(Number) item index, | |
| 2120 | // (String) comma-separated list of function generic param names] | |
| 2121 | // an item whose index is not present will fall back to the previous present path | |
| 2122 | const itemParamNames = new Map(crateCorpus.P); | |
| 2123 | ||
| 2124 | /** | |
| 2125 | * @type {Array<{ | |
| 2126 | * name: string, | |
| 2127 | * ty: number, | |
| 2128 | * path: string|null, | |
| 2129 | * exactPath: string|null, | |
| 2130 | * unboxFlag: boolean | |
| 2131 | * }>} | |
| 2132 | */ | |
| 2133 | const lowercasePaths = []; | |
| 2134 | /** | |
| 2135 | * @type {Array<{ | |
| 2136 | * name: string, | |
| 2137 | * ty: number, | |
| 2138 | * path: string|null, | |
| 2139 | * exactPath: string|null, | |
| 2140 | * unboxFlag: boolean | |
| 2141 | * }>} | |
| 2142 | */ | |
| 2143 | const paths = []; | |
| 2144 | ||
| 2145 | // a string representing the list of function types | |
| 2146 | const itemFunctionDecoder = new VlqHexDecoder( | |
| 2147 | crateCorpus.f, | |
| 2148 | // @ts-expect-error | |
| 2149 | buildFunctionSearchTypeCallback(paths, lowercasePaths), | |
| 2150 | ); | |
| 2151 | ||
| 2152 | // convert `rawPaths` entries into object form | |
| 2153 | // generate normalizedPaths for function search mode | |
| 2154 | let len = rawPaths.length; | |
| 2155 | let lastPath = undef2null(itemPaths.get(0)); | |
| 2156 | for (let i = 0; i < len; ++i) { | |
| 2157 | const elem = rawPaths[i]; | |
| 2158 | const ty = elem[0]; | |
| 2159 | const name = elem[1]; | |
| 2160 | /** | |
| 2161 | * @param {2|3} idx | |
| 2162 | * @param {string|null} if_null | |
| 2163 | * @param {string|null} if_not_found | |
| 2164 | * @returns {string|null} | |
| 2165 | */ | |
| 2166 | const elemPath = (idx, if_null, if_not_found) => { | |
| 2167 | if (elem.length > idx && elem[idx] !== undefined) { | |
| 2168 | const p = itemPaths.get(elem[idx]); | |
| 2169 | if (p !== undefined) { | |
| 2170 | return p; | |
| 2171 | } | |
| 2172 | return if_not_found; | |
| 2173 | } | |
| 2174 | return if_null; | |
| 2175 | }; | |
| 2176 | const path = elemPath(2, lastPath, null); | |
| 2177 | const exactPath = elemPath(3, path, path); | |
| 2178 | const unboxFlag = elem.length > 4 && !!elem[4]; | |
| 2179 | ||
| 2180 | lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath, unboxFlag }); | |
| 2181 | paths[i] = { ty, name, path, exactPath, unboxFlag }; | |
| 2182 | } | |
| 2183 | ||
| 2184 | // Convert `item*` into an object form, and construct word indices. | |
| 2185 | // | |
| 2186 | // Before any analysis is performed, let's gather the search terms to | |
| 2187 | // search against apart from the rest of the data. This is a quick | |
| 2188 | // operation that is cached for the life of the page state so that | |
| 2189 | // all other search operations have access to this cached data for | |
| 2190 | // faster analysis operations | |
| 2191 | lastPath = ""; | |
| 2192 | len = itemTypes.length; | |
| 2193 | let lastName = ""; | |
| 2194 | let lastWord = ""; | |
| 2195 | for (let i = 0; i < len; ++i) { | |
| 2196 | const bitIndex = i + 1; | |
| 2197 | if (descIndex >= descShard.len && | |
| 2198 | // @ts-expect-error | |
| 2199 | !this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { | |
| 2200 | descShard = { | |
| 2201 | crate, | |
| 2202 | shard: descShard.shard + 1, | |
| 2203 | start: descShard.start + descShard.len, | |
| 2204 | len: itemDescShardDecoder.next(), | |
| 2205 | promise: null, | |
| 2206 | resolve: null, | |
| 2207 | }; | |
| 2208 | descIndex = 0; | |
| 2209 | descShardList.push(descShard); | |
| 2210 | } | |
| 2211 | const name = itemNames[i] === "" ? lastName : itemNames[i]; | |
| 2212 | const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase(); | |
| 2213 | const pathU = itemPaths.get(i); | |
| 2214 | const path = pathU !== undefined ? pathU : lastPath; | |
| 2215 | const paramNameString = itemParamNames.get(i); | |
| 2216 | const paramNames = paramNameString !== undefined ? | |
| 2217 | paramNameString.split(",") : | |
| 2218 | lastParamNames; | |
| 2219 | const type = itemFunctionDecoder.next(); | |
| 2220 | if (type !== null) { | |
| 2221 | if (type) { | |
| 2222 | const fp = this.functionTypeFingerprint.subarray(id * 4, (id + 1) * 4); | |
| 2223 | for (const t of type.inputs) { | |
| 2224 | this.buildFunctionTypeFingerprint(t, fp); | |
| 2225 | } | |
| 2226 | for (const t of type.output) { | |
| 2227 | this.buildFunctionTypeFingerprint(t, fp); | |
| 2228 | } | |
| 2229 | for (const w of type.where_clause) { | |
| 2230 | for (const t of w) { | |
| 2231 | this.buildFunctionTypeFingerprint(t, fp); | |
| 2232 | } | |
| 2233 | } | |
| 2234 | } | |
| 2235 | } | |
| 2236 | // This object should have exactly the same set of fields as the "crateRow" | |
| 2237 | // object defined above. | |
| 2238 | const itemParentIdx = itemParentIdxDecoder.next(); | |
| 2239 | normalizedName = word.indexOf("_") === -1 ? word : word.replace(/_/g, ""); | |
| 2240 | /** @type {rustdoc.Row} */ | |
| 2241 | const row = { | |
| 2242 | crate, | |
| 2243 | ty: itemTypes.charCodeAt(i) - 65, // 65 = "A" | |
| 2244 | name, | |
| 2245 | path, | |
| 2246 | descShard, | |
| 2247 | descIndex, | |
| 2248 | exactPath: itemReexports.has(i) ? | |
| 2249 | // @ts-expect-error | |
| 2250 | itemPaths.get(itemReexports.get(i)) : path, | |
| 2251 | // @ts-expect-error | |
| 2252 | parent: itemParentIdx > 0 ? paths[itemParentIdx - 1] : undefined, | |
| 2253 | type, | |
| 2254 | paramNames, | |
| 2255 | id, | |
| 2256 | word, | |
| 2257 | normalizedName, | |
| 2258 | bitIndex, | |
| 2259 | implDisambiguator: undef2null(implDisambiguator.get(i)), | |
| 2260 | }; | |
| 2261 | this.nameTrie.insert(normalizedName, id, this.tailTable); | |
| 2262 | id += 1; | |
| 2263 | searchIndex.push(row); | |
| 2264 | lastPath = row.path; | |
| 2265 | lastParamNames = row.paramNames; | |
| 2266 | // @ts-expect-error | |
| 2267 | if (!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { | |
| 2268 | descIndex += 1; | |
| 2269 | } | |
| 2270 | lastName = name; | |
| 2271 | lastWord = word; | |
| 2272 | } | |
| 2273 | ||
| 2274 | if (aliases) { | |
| 2275 | // We need to add the aliases in `searchIndex` after we finished filling it | |
| 2276 | // to not mess up indexes. | |
| 2277 | allAliases.push([crate, aliases, currentIndex]); | |
| 2278 | } | |
| 2279 | currentIndex += itemTypes.length; | |
| 2280 | this.searchState.descShards.set(crate, descShardList); | |
| 2281 | } | |
| 2282 | ||
| 2283 | for (const [crate, aliases, index] of allAliases) { | |
| 2284 | for (const [alias_name, alias_refs] of Object.entries(aliases)) { | |
| 2285 | if (!this.ALIASES.has(crate)) { | |
| 2286 | this.ALIASES.set(crate, new Map()); | |
| 2287 | } | |
| 2288 | const word = alias_name.toLowerCase(); | |
| 2289 | const crate_alias_map = this.ALIASES.get(crate); | |
| 2290 | if (!crate_alias_map.has(word)) { | |
| 2291 | crate_alias_map.set(word, []); | |
| 2292 | } | |
| 2293 | const aliases_map = crate_alias_map.get(word); | |
| 2294 | ||
| 2295 | const normalizedName = word.indexOf("_") === -1 ? word : word.replace(/_/g, ""); | |
| 2296 | for (const alias of alias_refs) { | |
| 2297 | const originalIndex = alias + index; | |
| 2298 | const original = searchIndex[originalIndex]; | |
| 2299 | /** @type {rustdoc.Row} */ | |
| 2300 | const row = { | |
| 2301 | crate, | |
| 2302 | name: alias_name, | |
| 2303 | normalizedName, | |
| 2304 | is_alias: true, | |
| 2305 | ty: original.ty, | |
| 2306 | type: original.type, | |
| 2307 | paramNames: [], | |
| 2308 | word, | |
| 2309 | id, | |
| 2310 | parent: undefined, | |
| 2311 | original, | |
| 2312 | path: "", | |
| 2313 | implDisambiguator: original.implDisambiguator, | |
| 2314 | // Needed to load the description of the original item. | |
| 2315 | // @ts-ignore | |
| 2316 | descShard: original.descShard, | |
| 2317 | descIndex: original.descIndex, | |
| 2318 | bitIndex: original.bitIndex, | |
| 2319 | }; | |
| 2320 | aliases_map.push(row); | |
| 2321 | this.nameTrie.insert(normalizedName, id, this.tailTable); | |
| 2322 | id += 1; | |
| 2323 | searchIndex.push(row); | |
| 2324 | } | |
| 2325 | } | |
| 2326 | } | |
| 2327 | // Drop the (rather large) hash table used for reusing function items | |
| 2328 | this.TYPES_POOL = new Map(); | |
| 2329 | return searchIndex; | |
| 2330 | } | |
| 2331 | ||
| 2332 | /** | |
| 2333 | * Parses the query. | |
| 2334 | * | |
| 2335 | * The supported syntax by this parser is given in the rustdoc book chapter | |
| 2336 | * /src/doc/rustdoc/src/read-documentation/search.md | |
| 2337 | * | |
| 2338 | * When adding new things to the parser, add them there, too! | |
| 2339 | * | |
| 2340 | * @param {string} userQuery - The user query | |
| 2341 | * | |
| 2342 | * @return {rustdoc.ParsedQuery<rustdoc.ParserQueryElement>} - The parsed query | |
| 2343 | */ | |
| 2344 | static parseQuery(userQuery) { | |
| 2345 | /** | |
| 2346 | * @param {string} typename | |
| 2347 | * @returns {number} | |
| 2348 | */ | |
| 2349 | function itemTypeFromName(typename) { | |
| 2350 | const index = itemTypes.findIndex(i => i === typename); | |
| 2351 | if (index < 0) { | |
| 2352 | throw ["Unknown type filter ", typename]; | |
| 2353 | } | |
| 2354 | return index; | |
| 2355 | } | |
| 2356 | ||
| 2357 | /** | |
| 2358 | * @param {rustdoc.ParserQueryElement} elem | |
| 2359 | */ | |
| 2360 | function convertTypeFilterOnElem(elem) { | |
| 2361 | if (typeof elem.typeFilter === "string") { | |
| 2362 | let typeFilter = elem.typeFilter; | |
| 2363 | if (typeFilter === "const") { | |
| 2364 | typeFilter = "constant"; | |
| 2365 | } | |
| 2366 | elem.typeFilter = itemTypeFromName(typeFilter); | |
| 2367 | } else { | |
| 2368 | elem.typeFilter = NO_TYPE_FILTER; | |
| 2369 | } | |
| 2370 | for (const elem2 of elem.generics) { | |
| 2371 | convertTypeFilterOnElem(elem2); | |
| 2372 | } | |
| 2373 | for (const constraints of elem.bindings.values()) { | |
| 2374 | for (const constraint of constraints) { | |
| 2375 | convertTypeFilterOnElem(constraint); | |
| 2376 | } | |
| 2377 | } | |
| 2378 | } | |
| 2379 | ||
| 2380 | /** | |
| 2381 | * Takes the user search input and returns an empty `ParsedQuery`. | |
| 2382 | * | |
| 2383 | * @param {string} userQuery | |
| 2384 | * | |
| 2385 | * @return {rustdoc.ParsedQuery<rustdoc.ParserQueryElement>} | |
| 1291 | * Takes the user search input and returns an empty `ParsedQuery`. | |
| 1292 | * | |
| 1293 | * @param {string} userQuery | |
| 1294 | * | |
| 1295 | * @return {rustdoc.ParsedQuery<rustdoc.ParserQueryElement>} | |
| 2386 | 1296 | */ |
| 2387 | 1297 | function newParsedQuery(userQuery) { |
| 2388 | 1298 | return { |
| ... | ... | @@ -2437,8 +1347,7 @@ class DocSearch { |
| 2437 | 1347 | continue; |
| 2438 | 1348 | } |
| 2439 | 1349 | if (!foundStopChar) { |
| 2440 | /** @type String[] */ | |
| 2441 | let extra = []; | |
| 1350 | let extra = EMPTY_STRING_ARRAY; | |
| 2442 | 1351 | if (isLastElemGeneric(query.elems, parserState)) { |
| 2443 | 1352 | extra = [" after ", ">"]; |
| 2444 | 1353 | } else if (prevIs(parserState, "\"")) { |
| ... | ... | @@ -2515,236 +1424,623 @@ class DocSearch { |
| 2515 | 1424 | |
| 2516 | 1425 | try { |
| 2517 | 1426 | parseInput(query, parserState); |
| 1427 | ||
| 1428 | // Scan for invalid type filters, so that we can report the error | |
| 1429 | // outside the search loop. | |
| 1430 | /** @param {rustdoc.ParserQueryElement} elem */ | |
| 1431 | const checkTypeFilter = elem => { | |
| 1432 | const ty = itemTypeFromName(elem.typeFilter); | |
| 1433 | if (ty === TY_GENERIC && elem.generics.length !== 0) { | |
| 1434 | throw [ | |
| 1435 | "Generic type parameter ", | |
| 1436 | elem.name, | |
| 1437 | " does not accept generic parameters", | |
| 1438 | ]; | |
| 1439 | } | |
| 1440 | for (const generic of elem.generics) { | |
| 1441 | checkTypeFilter(generic); | |
| 1442 | } | |
| 1443 | for (const constraints of elem.bindings.values()) { | |
| 1444 | for (const constraint of constraints) { | |
| 1445 | checkTypeFilter(constraint); | |
| 1446 | } | |
| 1447 | } | |
| 1448 | }; | |
| 2518 | 1449 | for (const elem of query.elems) { |
| 2519 | convertTypeFilterOnElem(elem); | |
| 1450 | checkTypeFilter(elem); | |
| 1451 | } | |
| 1452 | for (const elem of query.returned) { | |
| 1453 | checkTypeFilter(elem); | |
| 1454 | } | |
| 1455 | } catch (err) { | |
| 1456 | query = newParsedQuery(userQuery); | |
| 1457 | if (Array.isArray(err) && err.every(elem => typeof elem === "string")) { | |
| 1458 | query.error = err; | |
| 1459 | } else { | |
| 1460 | // rethrow the error if it isn't a string array | |
| 1461 | throw err; | |
| 1462 | } | |
| 1463 | ||
| 1464 | return query; | |
| 1465 | } | |
| 1466 | if (!query.literalSearch) { | |
| 1467 | // If there is more than one element in the query, we switch to literalSearch in any | |
| 1468 | // case. | |
| 1469 | query.literalSearch = parserState.totalElems > 1; | |
| 1470 | } | |
| 1471 | query.foundElems = query.elems.length + query.returned.length; | |
| 1472 | query.totalElems = parserState.totalElems; | |
| 1473 | return query; | |
| 1474 | } | |
| 1475 | ||
| 1476 | /** | |
| 1477 | * @param {number} id | |
| 1478 | * @returns {Promise<string|null>} | |
| 1479 | */ | |
| 1480 | async getName(id) { | |
| 1481 | const ni = this.database.getData("name"); | |
| 1482 | if (!ni) { | |
| 1483 | return null; | |
| 1484 | } | |
| 1485 | const name = await ni.at(id); | |
| 1486 | return name === undefined || name === null ? null : this.utf8decoder.decode(name); | |
| 1487 | } | |
| 1488 | ||
| 1489 | /** | |
| 1490 | * @param {number} id | |
| 1491 | * @returns {Promise<string|null>} | |
| 1492 | */ | |
| 1493 | async getDesc(id) { | |
| 1494 | const di = this.database.getData("desc"); | |
| 1495 | if (!di) { | |
| 1496 | return null; | |
| 1497 | } | |
| 1498 | const desc = await di.at(id); | |
| 1499 | return desc === undefined || desc === null ? null : this.utf8decoder.decode(desc); | |
| 1500 | } | |
| 1501 | ||
| 1502 | /** | |
| 1503 | * @param {number} id | |
| 1504 | * @returns {Promise<number|null>} | |
| 1505 | */ | |
| 1506 | async getAliasTarget(id) { | |
| 1507 | const ai = this.database.getData("alias"); | |
| 1508 | if (!ai) { | |
| 1509 | return null; | |
| 1510 | } | |
| 1511 | const bytes = await ai.at(id); | |
| 1512 | if (bytes === undefined || bytes === null || bytes.length === 0) { | |
| 1513 | return null; | |
| 1514 | } else { | |
| 1515 | /** @type {string} */ | |
| 1516 | const encoded = this.utf8decoder.decode(bytes); | |
| 1517 | /** @type {number|null} */ | |
| 1518 | const decoded = JSON.parse(encoded); | |
| 1519 | return decoded; | |
| 1520 | } | |
| 1521 | } | |
| 1522 | ||
| 1523 | /** | |
| 1524 | * @param {number} id | |
| 1525 | * @returns {Promise<rustdoc.EntryData|null>} | |
| 1526 | */ | |
| 1527 | async getEntryData(id) { | |
| 1528 | const ei = this.database.getData("entry"); | |
| 1529 | if (!ei) { | |
| 1530 | return null; | |
| 1531 | } | |
| 1532 | const encoded = this.utf8decoder.decode(await ei.at(id)); | |
| 1533 | if (encoded === "" || encoded === undefined || encoded === null) { | |
| 1534 | return null; | |
| 1535 | } | |
| 1536 | /** | |
| 1537 | * krate, | |
| 1538 | * ty, | |
| 1539 | * module_path, | |
| 1540 | * exact_module_path, | |
| 1541 | * parent, | |
| 1542 | * deprecated, | |
| 1543 | * associated_item_disambiguator | |
| 1544 | * @type {rustdoc.ArrayWithOptionals<[ | |
| 1545 | * number, | |
| 1546 | * rustdoc.ItemType, | |
| 1547 | * number, | |
| 1548 | * number, | |
| 1549 | * number, | |
| 1550 | * number, | |
| 1551 | * ], [string]>} | |
| 1552 | */ | |
| 1553 | const raw = JSON.parse(encoded); | |
| 1554 | return { | |
| 1555 | krate: raw[0], | |
| 1556 | ty: raw[1], | |
| 1557 | modulePath: raw[2] === 0 ? null : raw[2] - 1, | |
| 1558 | exactModulePath: raw[3] === 0 ? null : raw[3] - 1, | |
| 1559 | parent: raw[4] === 0 ? null : raw[4] - 1, | |
| 1560 | deprecated: raw[5] === 1 ? true : false, | |
| 1561 | associatedItemDisambiguator: raw.length === 6 ? null : raw[6], | |
| 1562 | }; | |
| 1563 | } | |
| 1564 | ||
| 1565 | /** | |
| 1566 | * @param {number} id | |
| 1567 | * @returns {Promise<rustdoc.PathData|null>} | |
| 1568 | */ | |
| 1569 | async getPathData(id) { | |
| 1570 | const pi = this.database.getData("path"); | |
| 1571 | if (!pi) { | |
| 1572 | return null; | |
| 1573 | } | |
| 1574 | const encoded = this.utf8decoder.decode(await pi.at(id)); | |
| 1575 | if (encoded === "" || encoded === undefined || encoded === null) { | |
| 1576 | return null; | |
| 1577 | } | |
| 1578 | /** | |
| 1579 | * ty, module_path, exact_module_path, search_unbox, inverted_function_signature_index | |
| 1580 | * @type {rustdoc.ArrayWithOptionals<[rustdoc.ItemType, string], [string|0, 0|1, string]>} | |
| 1581 | */ | |
| 1582 | const raw = JSON.parse(encoded); | |
| 1583 | return { | |
| 1584 | ty: raw[0], | |
| 1585 | modulePath: raw[1], | |
| 1586 | exactModulePath: raw[2] === 0 || raw[2] === undefined ? raw[1] : raw[2], | |
| 1587 | }; | |
| 1588 | } | |
| 1589 | ||
| 1590 | /** | |
| 1591 | * @param {number} id | |
| 1592 | * @returns {Promise<rustdoc.FunctionData|null>} | |
| 1593 | */ | |
| 1594 | async getFunctionData(id) { | |
| 1595 | const fi = this.database.getData("function"); | |
| 1596 | if (!fi) { | |
| 1597 | return null; | |
| 1598 | } | |
| 1599 | const encoded = this.utf8decoder.decode(await fi.at(id)); | |
| 1600 | if (encoded === "" || encoded === undefined || encoded === null) { | |
| 1601 | return null; | |
| 1602 | } | |
| 1603 | /** | |
| 1604 | * function_signature, param_names | |
| 1605 | * @type {[string, string[]]} | |
| 1606 | */ | |
| 1607 | const raw = JSON.parse(encoded); | |
| 1608 | ||
| 1609 | const parser = new VlqHexDecoder(raw[0], async functionSearchType => { | |
| 1610 | if (typeof functionSearchType === "number") { | |
| 1611 | return null; | |
| 1612 | } | |
| 1613 | const INPUTS_DATA = 0; | |
| 1614 | const OUTPUT_DATA = 1; | |
| 1615 | /** @type {Promise<rustdoc.FunctionType[]>} */ | |
| 1616 | let inputs_; | |
| 1617 | /** @type {Promise<rustdoc.FunctionType[]>} */ | |
| 1618 | let output_; | |
| 1619 | if (typeof functionSearchType[INPUTS_DATA] === "number") { | |
| 1620 | inputs_ = Promise.all([ | |
| 1621 | this.buildItemSearchType(functionSearchType[INPUTS_DATA]), | |
| 1622 | ]); | |
| 1623 | } else { | |
| 1624 | // @ts-ignore | |
| 1625 | inputs_ = this.buildItemSearchTypeAll(functionSearchType[INPUTS_DATA]); | |
| 1626 | } | |
| 1627 | if (functionSearchType.length > 1) { | |
| 1628 | if (typeof functionSearchType[OUTPUT_DATA] === "number") { | |
| 1629 | output_ = Promise.all([ | |
| 1630 | this.buildItemSearchType(functionSearchType[OUTPUT_DATA]), | |
| 1631 | ]); | |
| 1632 | } else { | |
| 1633 | // @ts-expect-error | |
| 1634 | output_ = this.buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA]); | |
| 1635 | } | |
| 1636 | } else { | |
| 1637 | output_ = Promise.resolve(EMPTY_GENERICS_ARRAY); | |
| 1638 | } | |
| 1639 | /** @type {Promise<rustdoc.FunctionType[]>[]} */ | |
| 1640 | const where_clause_ = []; | |
| 1641 | const l = functionSearchType.length; | |
| 1642 | for (let i = 2; i < l; ++i) { | |
| 1643 | where_clause_.push(typeof functionSearchType[i] === "number" | |
| 1644 | // @ts-expect-error | |
| 1645 | ? Promise.all([this.buildItemSearchType(functionSearchType[i])]) | |
| 1646 | // @ts-expect-error | |
| 1647 | : this.buildItemSearchTypeAll(functionSearchType[i]), | |
| 1648 | ); | |
| 1649 | } | |
| 1650 | const [inputs, output, where_clause] = await Promise.all([ | |
| 1651 | inputs_, | |
| 1652 | output_, | |
| 1653 | Promise.all(where_clause_), | |
| 1654 | ]); | |
| 1655 | return { | |
| 1656 | inputs, output, where_clause, | |
| 1657 | }; | |
| 1658 | }); | |
| 1659 | ||
| 1660 | return { | |
| 1661 | functionSignature: await parser.next(), | |
| 1662 | paramNames: raw[1], | |
| 1663 | elemCount: parser.elemCount, | |
| 1664 | }; | |
| 1665 | } | |
| 1666 | ||
| 1667 | /** | |
| 1668 | * @param {number} id | |
| 1669 | * @returns {Promise<rustdoc.TypeData|null>} | |
| 1670 | */ | |
| 1671 | async getTypeData(id) { | |
| 1672 | const ti = this.database.getData("type"); | |
| 1673 | if (!ti) { | |
| 1674 | return null; | |
| 1675 | } | |
| 1676 | const encoded = this.utf8decoder.decode(await ti.at(id)); | |
| 1677 | if (encoded === "" || encoded === undefined || encoded === null) { | |
| 1678 | return null; | |
| 1679 | } | |
| 1680 | /** | |
| 1681 | * function_signature, param_names | |
| 1682 | * @type {[string, number] | [number] | [string] | [] | null} | |
| 1683 | */ | |
| 1684 | const raw = JSON.parse(encoded); | |
| 1685 | ||
| 1686 | if (!raw || raw.length === 0) { | |
| 1687 | return null; | |
| 1688 | } | |
| 1689 | ||
| 1690 | let searchUnbox = false; | |
| 1691 | const invertedFunctionSignatureIndex = []; | |
| 1692 | ||
| 1693 | if (typeof raw[0] === "string") { | |
| 1694 | if (raw[1]) { | |
| 1695 | searchUnbox = true; | |
| 1696 | } | |
| 1697 | // the inverted function signature index is a list of bitmaps, | |
| 1698 | // by number of types that appear in the function | |
| 1699 | let i = 0; | |
| 1700 | const pb = makeUint8ArrayFromBase64(raw[0]); | |
| 1701 | const l = pb.length; | |
| 1702 | while (i < l) { | |
| 1703 | if (pb[i] === 0) { | |
| 1704 | invertedFunctionSignatureIndex.push(RoaringBitmap.empty()); | |
| 1705 | i += 1; | |
| 1706 | } else { | |
| 1707 | const bitmap = new RoaringBitmap(pb, i); | |
| 1708 | i += bitmap.consumed_len_bytes; | |
| 1709 | invertedFunctionSignatureIndex.push(bitmap); | |
| 1710 | } | |
| 1711 | } | |
| 1712 | } else if (raw[0]) { | |
| 1713 | searchUnbox = true; | |
| 1714 | } | |
| 1715 | ||
| 1716 | return { searchUnbox, invertedFunctionSignatureIndex }; | |
| 1717 | } | |
| 1718 | ||
| 1719 | /** | |
| 1720 | * @returns {Promise<string[]>} | |
| 1721 | */ | |
| 1722 | async getCrateNameList() { | |
| 1723 | const crateNames = this.database.getData("crateNames"); | |
| 1724 | if (!crateNames) { | |
| 1725 | return []; | |
| 1726 | } | |
| 1727 | const l = crateNames.length; | |
| 1728 | const names = []; | |
| 1729 | for (let i = 0; i < l; ++i) { | |
| 1730 | names.push(crateNames.at(i).then(name => { | |
| 1731 | if (name === undefined) { | |
| 1732 | return ""; | |
| 1733 | } | |
| 1734 | return this.utf8decoder.decode(name); | |
| 1735 | })); | |
| 1736 | } | |
| 1737 | return Promise.all(names); | |
| 1738 | } | |
| 1739 | ||
| 1740 | /** | |
| 1741 | * @param {number} id non-negative generic index | |
| 1742 | * @returns {Promise<stringdex.RoaringBitmap[]>} | |
| 1743 | */ | |
| 1744 | async getGenericInvertedIndex(id) { | |
| 1745 | const gii = this.database.getData("generic_inverted_index"); | |
| 1746 | if (!gii) { | |
| 1747 | return []; | |
| 1748 | } | |
| 1749 | const pb = await gii.at(id); | |
| 1750 | if (pb === undefined || pb === null || pb.length === 0) { | |
| 1751 | return []; | |
| 1752 | } | |
| 1753 | ||
| 1754 | const invertedFunctionSignatureIndex = []; | |
| 1755 | // the inverted function signature index is a list of bitmaps, | |
| 1756 | // by number of types that appear in the function | |
| 1757 | let i = 0; | |
| 1758 | const l = pb.length; | |
| 1759 | while (i < l) { | |
| 1760 | if (pb[i] === 0) { | |
| 1761 | invertedFunctionSignatureIndex.push(RoaringBitmap.empty()); | |
| 1762 | i += 1; | |
| 1763 | } else { | |
| 1764 | const bitmap = new RoaringBitmap(pb, i); | |
| 1765 | i += bitmap.consumed_len_bytes; | |
| 1766 | invertedFunctionSignatureIndex.push(bitmap); | |
| 1767 | } | |
| 1768 | } | |
| 1769 | return invertedFunctionSignatureIndex; | |
| 1770 | } | |
| 1771 | ||
| 1772 | /** | |
| 1773 | * @param {number} id | |
| 1774 | * @returns {Promise<rustdoc.Row?>} | |
| 1775 | */ | |
| 1776 | async getRow(id) { | |
| 1777 | const [name_, entry, path, type] = await Promise.all([ | |
| 1778 | this.getName(id), | |
| 1779 | this.getEntryData(id), | |
| 1780 | this.getPathData(id), | |
| 1781 | this.getFunctionData(id), | |
| 1782 | ]); | |
| 1783 | if (!entry && !path) { | |
| 1784 | return null; | |
| 1785 | } | |
| 1786 | const [ | |
| 1787 | moduleName, | |
| 1788 | modulePathData, | |
| 1789 | exactModuleName, | |
| 1790 | exactModulePathData, | |
| 1791 | ] = await Promise.all([ | |
| 1792 | entry && entry.modulePath !== null ? this.getName(entry.modulePath) : null, | |
| 1793 | entry && entry.modulePath !== null ? this.getPathData(entry.modulePath) : null, | |
| 1794 | entry && entry.exactModulePath !== null ? | |
| 1795 | this.getName(entry.exactModulePath) : | |
| 1796 | null, | |
| 1797 | entry && entry.exactModulePath !== null ? | |
| 1798 | this.getPathData(entry.exactModulePath) : | |
| 1799 | null, | |
| 1800 | ]); | |
| 1801 | const name = name_ === null ? "" : name_; | |
| 1802 | const normalizedName = (name.indexOf("_") === -1 ? | |
| 1803 | name : | |
| 1804 | name.replace(/_/g, "")).toLowerCase(); | |
| 1805 | const modulePath = modulePathData === null || moduleName === null ? "" : | |
| 1806 | (modulePathData.modulePath === "" ? | |
| 1807 | moduleName : | |
| 1808 | `${modulePathData.modulePath}::${moduleName}`); | |
| 1809 | const [parentName, parentPath] = entry !== null && entry.parent !== null ? | |
| 1810 | await Promise.all([this.getName(entry.parent), this.getPathData(entry.parent)]) : | |
| 1811 | [null, null]; | |
| 1812 | return { | |
| 1813 | id, | |
| 1814 | crate: entry ? nonnull(await this.getName(entry.krate)) : "", | |
| 1815 | ty: entry ? entry.ty : nonnull(path).ty, | |
| 1816 | name, | |
| 1817 | normalizedName, | |
| 1818 | modulePath, | |
| 1819 | exactModulePath: exactModulePathData === null || exactModuleName === null ? modulePath : | |
| 1820 | (exactModulePathData.exactModulePath === "" ? | |
| 1821 | exactModuleName : | |
| 1822 | `${exactModulePathData.exactModulePath}::${exactModuleName}`), | |
| 1823 | entry, | |
| 1824 | path, | |
| 1825 | type, | |
| 1826 | deprecated: entry ? entry.deprecated : false, | |
| 1827 | parent: parentName !== null && parentPath !== null ? | |
| 1828 | { name: parentName, path: parentPath } : | |
| 1829 | null, | |
| 1830 | }; | |
| 1831 | } | |
| 1832 | ||
| 1833 | /** | |
| 1834 | * Convert a list of RawFunctionType / ID to object-based FunctionType. | |
| 1835 | * | |
| 1836 | * Crates often have lots of functions in them, and it's common to have a large number of | |
| 1837 | * functions that operate on a small set of data types, so the search index compresses them | |
| 1838 | * by encoding function parameter and return types as indexes into an array of names. | |
| 1839 | * | |
| 1840 | * Even when a general-purpose compression algorithm is used, this is still a win. | |
| 1841 | * I checked. https://github.com/rust-lang/rust/pull/98475#issue-1284395985 | |
| 1842 | * | |
| 1843 | * The format for individual function types is encoded in | |
| 1844 | * librustdoc/html/render/mod.rs: impl Serialize for RenderType | |
| 1845 | * | |
| 1846 | * @param {null|Array<rustdoc.RawFunctionType>} types | |
| 1847 | * | |
| 1848 | * @return {Promise<Array<rustdoc.FunctionType>>} | |
| 1849 | */ | |
| 1850 | async buildItemSearchTypeAll(types) { | |
| 1851 | return types && types.length > 0 ? | |
| 1852 | await Promise.all(types.map(type => this.buildItemSearchType(type))) : | |
| 1853 | EMPTY_GENERICS_ARRAY; | |
| 1854 | } | |
| 1855 | ||
| 1856 | /** | |
| 1857 | * Converts a single type. | |
| 1858 | * | |
| 1859 | * @param {rustdoc.RawFunctionType} type | |
| 1860 | * @return {Promise<rustdoc.FunctionType>} | |
| 1861 | */ | |
| 1862 | async buildItemSearchType(type) { | |
| 1863 | const PATH_INDEX_DATA = 0; | |
| 1864 | const GENERICS_DATA = 1; | |
| 1865 | const BINDINGS_DATA = 2; | |
| 1866 | let id, generics; | |
| 1867 | /** | |
| 1868 | * @type {Map<number, rustdoc.FunctionType[]>} | |
| 1869 | */ | |
| 1870 | let bindings; | |
| 1871 | if (typeof type === "number") { | |
| 1872 | id = type; | |
| 1873 | generics = EMPTY_GENERICS_ARRAY; | |
| 1874 | bindings = EMPTY_BINDINGS_MAP; | |
| 1875 | } else { | |
| 1876 | id = type[PATH_INDEX_DATA]; | |
| 1877 | generics = await this.buildItemSearchTypeAll(type[GENERICS_DATA]); | |
| 1878 | if (type[BINDINGS_DATA] && type[BINDINGS_DATA].length > 0) { | |
| 1879 | bindings = new Map((await Promise.all(type[BINDINGS_DATA].map( | |
| 1880 | /** | |
| 1881 | * @param {[rustdoc.RawFunctionType, rustdoc.RawFunctionType[]]} binding | |
| 1882 | * @returns {Promise<[number, rustdoc.FunctionType[]][]>} | |
| 1883 | */ | |
| 1884 | async binding => { | |
| 1885 | const [assocType, constraints] = binding; | |
| 1886 | // Associated type constructors are represented sloppily in rustdoc's | |
| 1887 | // type search, to make the engine simpler. | |
| 1888 | // | |
| 1889 | // MyType<Output<T>=Result<T>> is equivalent to MyType<Output<Result<T>>=T> | |
| 1890 | // and both are, essentially | |
| 1891 | // MyType<Output=(T, Result<T>)>, except the tuple isn't actually there. | |
| 1892 | // It's more like the value of a type binding is naturally an array, | |
| 1893 | // which rustdoc calls "constraints". | |
| 1894 | // | |
| 1895 | // As a result, the key should never have generics on it. | |
| 1896 | const [k, v] = await Promise.all([ | |
| 1897 | this.buildItemSearchType(assocType).then(t => t.id), | |
| 1898 | this.buildItemSearchTypeAll(constraints), | |
| 1899 | ]); | |
| 1900 | return k === null ? EMPTY_BINDINGS_ARRAY : [[k, v]]; | |
| 1901 | }, | |
| 1902 | ))).flat()); | |
| 1903 | } else { | |
| 1904 | bindings = EMPTY_BINDINGS_MAP; | |
| 1905 | } | |
| 1906 | } | |
| 1907 | /** | |
| 1908 | * @type {rustdoc.FunctionType} | |
| 1909 | */ | |
| 1910 | let result; | |
| 1911 | if (id < 0) { | |
| 1912 | // types less than 0 are generic parameters | |
| 1913 | // the actual names of generic parameters aren't stored, since they aren't API | |
| 1914 | result = { | |
| 1915 | id, | |
| 1916 | name: "", | |
| 1917 | ty: TY_GENERIC, | |
| 1918 | path: null, | |
| 1919 | exactPath: null, | |
| 1920 | generics, | |
| 1921 | bindings, | |
| 1922 | unboxFlag: true, | |
| 1923 | }; | |
| 1924 | } else if (id === 0) { | |
| 1925 | // `0` is used as a sentinel because it's fewer bytes than `null` | |
| 1926 | result = { | |
| 1927 | id: null, | |
| 1928 | name: "", | |
| 1929 | ty: TY_GENERIC, | |
| 1930 | path: null, | |
| 1931 | exactPath: null, | |
| 1932 | generics, | |
| 1933 | bindings, | |
| 1934 | unboxFlag: true, | |
| 1935 | }; | |
| 1936 | } else { | |
| 1937 | const [name, path, type] = await Promise.all([ | |
| 1938 | this.getName(id - 1), | |
| 1939 | this.getPathData(id - 1), | |
| 1940 | this.getTypeData(id - 1), | |
| 1941 | ]); | |
| 1942 | if (path === undefined || path === null || type === undefined || type === null) { | |
| 1943 | return { | |
| 1944 | id: null, | |
| 1945 | name: "", | |
| 1946 | ty: TY_GENERIC, | |
| 1947 | path: null, | |
| 1948 | exactPath: null, | |
| 1949 | generics, | |
| 1950 | bindings, | |
| 1951 | unboxFlag: true, | |
| 1952 | }; | |
| 1953 | } | |
| 1954 | result = { | |
| 1955 | id: id - 1, | |
| 1956 | name, | |
| 1957 | ty: path.ty, | |
| 1958 | path: path.modulePath, | |
| 1959 | exactPath: path.exactModulePath === null ? path.modulePath : path.exactModulePath, | |
| 1960 | generics, | |
| 1961 | bindings, | |
| 1962 | unboxFlag: type.searchUnbox, | |
| 1963 | }; | |
| 1964 | } | |
| 1965 | const cr = this.TYPES_POOL.get(result.id); | |
| 1966 | if (cr) { | |
| 1967 | // Shallow equality check. Since this function is used | |
| 1968 | // to construct every type object, this should be mostly | |
| 1969 | // equivalent to a deep equality check, except if there's | |
| 1970 | // a conflict, we don't keep the old one around, so it's | |
| 1971 | // not a fully precise implementation of hashcons. | |
| 1972 | if (cr.generics.length === result.generics.length && | |
| 1973 | cr.generics !== result.generics && | |
| 1974 | cr.generics.every((x, i) => result.generics[i] === x) | |
| 1975 | ) { | |
| 1976 | result.generics = cr.generics; | |
| 2520 | 1977 | } |
| 2521 | for (const elem of query.returned) { | |
| 2522 | convertTypeFilterOnElem(elem); | |
| 1978 | if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) { | |
| 1979 | let ok = true; | |
| 1980 | for (const [k, v] of cr.bindings.entries()) { | |
| 1981 | const v2 = result.bindings.get(k); | |
| 1982 | if (!v2) { | |
| 1983 | ok = false; | |
| 1984 | break; | |
| 1985 | } | |
| 1986 | if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) { | |
| 1987 | result.bindings.set(k, v); | |
| 1988 | } else if (v !== v2) { | |
| 1989 | ok = false; | |
| 1990 | break; | |
| 1991 | } | |
| 1992 | } | |
| 1993 | if (ok) { | |
| 1994 | result.bindings = cr.bindings; | |
| 1995 | } | |
| 2523 | 1996 | } |
| 2524 | } catch (err) { | |
| 2525 | query = newParsedQuery(userQuery); | |
| 2526 | if (Array.isArray(err) && err.every(elem => typeof elem === "string")) { | |
| 2527 | query.error = err; | |
| 2528 | } else { | |
| 2529 | // rethrow the error if it isn't a string array | |
| 2530 | throw err; | |
| 1997 | if (cr.ty === result.ty && cr.path === result.path | |
| 1998 | && cr.bindings === result.bindings && cr.generics === result.generics | |
| 1999 | && cr.ty === result.ty && cr.name === result.name | |
| 2000 | && cr.unboxFlag === result.unboxFlag | |
| 2001 | ) { | |
| 2002 | return cr; | |
| 2531 | 2003 | } |
| 2532 | ||
| 2533 | return query; | |
| 2534 | } | |
| 2535 | if (!query.literalSearch) { | |
| 2536 | // If there is more than one element in the query, we switch to literalSearch in any | |
| 2537 | // case. | |
| 2538 | query.literalSearch = parserState.totalElems > 1; | |
| 2539 | 2004 | } |
| 2540 | query.foundElems = query.elems.length + query.returned.length; | |
| 2541 | query.totalElems = parserState.totalElems; | |
| 2542 | return query; | |
| 2005 | this.TYPES_POOL.set(result.id, result); | |
| 2006 | return result; | |
| 2543 | 2007 | } |
| 2544 | 2008 | |
| 2545 | 2009 | /** |
| 2546 | 2010 | * Executes the parsed query and builds a {ResultsTable}. |
| 2547 | 2011 | * |
| 2548 | * @param {rustdoc.ParsedQuery<rustdoc.ParserQueryElement>} origParsedQuery | |
| 2012 | * @param {rustdoc.ParsedQuery<rustdoc.ParserQueryElement>} parsedQuery | |
| 2549 | 2013 | * - The parsed user query |
| 2550 | 2014 | * @param {Object} filterCrates - Crate to search in if defined |
| 2551 | 2015 | * @param {string} currentCrate - Current crate, to rank results from this crate higher |
| 2552 | 2016 | * |
| 2553 | 2017 | * @return {Promise<rustdoc.ResultsTable>} |
| 2554 | 2018 | */ |
| 2555 | async execQuery(origParsedQuery, filterCrates, currentCrate) { | |
| 2556 | /** @type {rustdoc.Results} */ | |
| 2557 | const results_others = new Map(), | |
| 2558 | /** @type {rustdoc.Results} */ | |
| 2559 | results_in_args = new Map(), | |
| 2560 | /** @type {rustdoc.Results} */ | |
| 2561 | results_returned = new Map(); | |
| 2562 | ||
| 2563 | /** @type {rustdoc.ParsedQuery<rustdoc.QueryElement>} */ | |
| 2564 | // @ts-expect-error | |
| 2565 | const parsedQuery = origParsedQuery; | |
| 2566 | ||
| 2019 | async execQuery(parsedQuery, filterCrates, currentCrate) { | |
| 2567 | 2020 | const queryLen = |
| 2568 | 2021 | parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) + |
| 2569 | 2022 | parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0); |
| 2570 | 2023 | const maxEditDistance = Math.floor(queryLen / 3); |
| 2571 | // We reinitialize the `FOUND_ALIASES` map. | |
| 2572 | this.FOUND_ALIASES.clear(); | |
| 2573 | ||
| 2574 | /** | |
| 2575 | * @type {Map<string, number>} | |
| 2576 | */ | |
| 2577 | const genericSymbols = new Map(); | |
| 2578 | ||
| 2579 | /** | |
| 2580 | * Convert names to ids in parsed query elements. | |
| 2581 | * This is not used for the "In Names" tab, but is used for the | |
| 2582 | * "In Params", "In Returns", and "In Function Signature" tabs. | |
| 2583 | * | |
| 2584 | * If there is no matching item, but a close-enough match, this | |
| 2585 | * function also that correction. | |
| 2586 | * | |
| 2587 | * See `buildTypeMapIndex` for more information. | |
| 2588 | * | |
| 2589 | * @param {rustdoc.QueryElement} elem | |
| 2590 | * @param {boolean=} isAssocType | |
| 2591 | */ | |
| 2592 | const convertNameToId = (elem, isAssocType) => { | |
| 2593 | const loweredName = elem.pathLast.toLowerCase(); | |
| 2594 | if (this.typeNameIdMap.has(loweredName) && | |
| 2595 | // @ts-expect-error | |
| 2596 | (isAssocType || !this.typeNameIdMap.get(loweredName).assocOnly)) { | |
| 2597 | // @ts-expect-error | |
| 2598 | elem.id = this.typeNameIdMap.get(loweredName).id; | |
| 2599 | } else if (!parsedQuery.literalSearch) { | |
| 2600 | let match = null; | |
| 2601 | let matchDist = maxEditDistance + 1; | |
| 2602 | let matchName = ""; | |
| 2603 | for (const [name, { id, assocOnly }] of this.typeNameIdMap) { | |
| 2604 | const dist = Math.min( | |
| 2605 | editDistance(name, loweredName, maxEditDistance), | |
| 2606 | editDistance(name, elem.normalizedPathLast, maxEditDistance), | |
| 2607 | ); | |
| 2608 | if (dist <= matchDist && dist <= maxEditDistance && | |
| 2609 | (isAssocType || !assocOnly)) { | |
| 2610 | if (dist === matchDist && matchName > name) { | |
| 2611 | continue; | |
| 2612 | } | |
| 2613 | match = id; | |
| 2614 | matchDist = dist; | |
| 2615 | matchName = name; | |
| 2616 | } | |
| 2617 | } | |
| 2618 | if (match !== null) { | |
| 2619 | parsedQuery.correction = matchName; | |
| 2620 | } | |
| 2621 | elem.id = match; | |
| 2622 | } | |
| 2623 | if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 | |
| 2624 | && elem.generics.length === 0 && elem.bindings.size === 0) | |
| 2625 | || elem.typeFilter === TY_GENERIC) { | |
| 2626 | const id = genericSymbols.get(elem.normalizedPathLast); | |
| 2627 | if (id !== undefined) { | |
| 2628 | elem.id = id; | |
| 2629 | } else { | |
| 2630 | elem.id = -(genericSymbols.size + 1); | |
| 2631 | genericSymbols.set(elem.normalizedPathLast, elem.id); | |
| 2632 | } | |
| 2633 | if (elem.typeFilter === -1 && elem.normalizedPathLast.length >= 3) { | |
| 2634 | // Silly heuristic to catch if the user probably meant | |
| 2635 | // to not write a generic parameter. We don't use it, | |
| 2636 | // just bring it up. | |
| 2637 | const maxPartDistance = Math.floor(elem.normalizedPathLast.length / 3); | |
| 2638 | let matchDist = maxPartDistance + 1; | |
| 2639 | let matchName = ""; | |
| 2640 | for (const name of this.typeNameIdMap.keys()) { | |
| 2641 | const dist = editDistance( | |
| 2642 | name, | |
| 2643 | elem.normalizedPathLast, | |
| 2644 | maxPartDistance, | |
| 2645 | ); | |
| 2646 | if (dist <= matchDist && dist <= maxPartDistance) { | |
| 2647 | if (dist === matchDist && matchName > name) { | |
| 2648 | continue; | |
| 2649 | } | |
| 2650 | matchDist = dist; | |
| 2651 | matchName = name; | |
| 2652 | } | |
| 2653 | } | |
| 2654 | if (matchName !== "") { | |
| 2655 | parsedQuery.proposeCorrectionFrom = elem.name; | |
| 2656 | parsedQuery.proposeCorrectionTo = matchName; | |
| 2657 | } | |
| 2658 | } | |
| 2659 | elem.typeFilter = TY_GENERIC; | |
| 2660 | } | |
| 2661 | if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { | |
| 2662 | // Rust does not have HKT | |
| 2663 | parsedQuery.error = [ | |
| 2664 | "Generic type parameter ", | |
| 2665 | elem.name, | |
| 2666 | " does not accept generic parameters", | |
| 2667 | ]; | |
| 2668 | } | |
| 2669 | for (const elem2 of elem.generics) { | |
| 2670 | convertNameToId(elem2); | |
| 2671 | } | |
| 2672 | elem.bindings = new Map(Array.from(elem.bindings.entries()) | |
| 2673 | .map(entry => { | |
| 2674 | const [name, constraints] = entry; | |
| 2675 | // @ts-expect-error | |
| 2676 | if (!this.typeNameIdMap.has(name)) { | |
| 2677 | parsedQuery.error = [ | |
| 2678 | "Type parameter ", | |
| 2679 | // @ts-expect-error | |
| 2680 | name, | |
| 2681 | " does not exist", | |
| 2682 | ]; | |
| 2683 | return [0, []]; | |
| 2684 | } | |
| 2685 | for (const elem2 of constraints) { | |
| 2686 | convertNameToId(elem2, false); | |
| 2687 | } | |
| 2688 | ||
| 2689 | // @ts-expect-error | |
| 2690 | return [this.typeNameIdMap.get(name).id, constraints]; | |
| 2691 | }), | |
| 2692 | ); | |
| 2693 | }; | |
| 2694 | ||
| 2695 | for (const elem of parsedQuery.elems) { | |
| 2696 | convertNameToId(elem, false); | |
| 2697 | this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); | |
| 2698 | } | |
| 2699 | for (const elem of parsedQuery.returned) { | |
| 2700 | convertNameToId(elem, false); | |
| 2701 | this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); | |
| 2702 | } | |
| 2703 | ||
| 2704 | 2024 | |
| 2705 | 2025 | /** |
| 2706 | * Creates the query results. | |
| 2707 | * | |
| 2708 | * @param {Array<rustdoc.ResultObject>} results_in_args | |
| 2709 | * @param {Array<rustdoc.ResultObject>} results_returned | |
| 2710 | * @param {Array<rustdoc.ResultObject>} results_others | |
| 2711 | * @param {rustdoc.ParsedQuery<rustdoc.QueryElement>} parsedQuery | |
| 2712 | * | |
| 2713 | * @return {rustdoc.ResultsTable} | |
| 2026 | * @param {rustdoc.Row} item | |
| 2027 | * @returns {[string, string, string]} | |
| 2714 | 2028 | */ |
| 2715 | function createQueryResults( | |
| 2716 | results_in_args, | |
| 2717 | results_returned, | |
| 2718 | results_others, | |
| 2719 | parsedQuery) { | |
| 2720 | return { | |
| 2721 | "in_args": results_in_args, | |
| 2722 | "returned": results_returned, | |
| 2723 | "others": results_others, | |
| 2724 | "query": parsedQuery, | |
| 2725 | }; | |
| 2726 | } | |
| 2727 | ||
| 2728 | // @ts-expect-error | |
| 2729 | 2029 | const buildHrefAndPath = item => { |
| 2730 | 2030 | let displayPath; |
| 2731 | 2031 | let href; |
| 2732 | if (item.is_alias) { | |
| 2733 | this.FOUND_ALIASES.add(item.word); | |
| 2734 | item = item.original; | |
| 2735 | } | |
| 2736 | 2032 | const type = itemTypes[item.ty]; |
| 2737 | 2033 | const name = item.name; |
| 2738 | let path = item.path; | |
| 2739 | let exactPath = item.exactPath; | |
| 2034 | let path = item.modulePath; | |
| 2035 | let exactPath = item.exactModulePath; | |
| 2740 | 2036 | |
| 2741 | 2037 | if (type === "mod") { |
| 2742 | 2038 | displayPath = path + "::"; |
| 2743 | 2039 | href = this.rootPath + path.replace(/::/g, "/") + "/" + |
| 2744 | 2040 | name + "/index.html"; |
| 2745 | 2041 | } else if (type === "import") { |
| 2746 | displayPath = item.path + "::"; | |
| 2747 | href = this.rootPath + item.path.replace(/::/g, "/") + | |
| 2042 | displayPath = item.modulePath + "::"; | |
| 2043 | href = this.rootPath + item.modulePath.replace(/::/g, "/") + | |
| 2748 | 2044 | "/index.html#reexport." + name; |
| 2749 | 2045 | } else if (type === "primitive" || type === "keyword") { |
| 2750 | 2046 | displayPath = ""; |
| ... | ... | @@ -2754,13 +2050,13 @@ class DocSearch { |
| 2754 | 2050 | } else if (type === "externcrate") { |
| 2755 | 2051 | displayPath = ""; |
| 2756 | 2052 | href = this.rootPath + name + "/index.html"; |
| 2757 | } else if (item.parent !== undefined) { | |
| 2053 | } else if (item.parent) { | |
| 2758 | 2054 | const myparent = item.parent; |
| 2759 | 2055 | let anchor = type + "." + name; |
| 2760 | const parentType = itemTypes[myparent.ty]; | |
| 2056 | const parentType = itemTypes[myparent.path.ty]; | |
| 2761 | 2057 | let pageType = parentType; |
| 2762 | 2058 | let pageName = myparent.name; |
| 2763 | exactPath = `${myparent.exactPath}::${myparent.name}`; | |
| 2059 | exactPath = `${myparent.path.exactModulePath}::${myparent.name}`; | |
| 2764 | 2060 | |
| 2765 | 2061 | if (parentType === "primitive") { |
| 2766 | 2062 | displayPath = myparent.name + "::"; |
| ... | ... | @@ -2768,9 +2064,9 @@ class DocSearch { |
| 2768 | 2064 | } else if (type === "structfield" && parentType === "variant") { |
| 2769 | 2065 | // Structfields belonging to variants are special: the |
| 2770 | 2066 | // final path element is the enum name. |
| 2771 | const enumNameIdx = item.path.lastIndexOf("::"); | |
| 2772 | const enumName = item.path.substr(enumNameIdx + 2); | |
| 2773 | path = item.path.substr(0, enumNameIdx); | |
| 2067 | const enumNameIdx = item.modulePath.lastIndexOf("::"); | |
| 2068 | const enumName = item.modulePath.substr(enumNameIdx + 2); | |
| 2069 | path = item.modulePath.substr(0, enumNameIdx); | |
| 2774 | 2070 | displayPath = path + "::" + enumName + "::" + myparent.name + "::"; |
| 2775 | 2071 | anchor = "variant." + myparent.name + ".field." + name; |
| 2776 | 2072 | pageType = "enum"; |
| ... | ... | @@ -2778,16 +2074,16 @@ class DocSearch { |
| 2778 | 2074 | } else { |
| 2779 | 2075 | displayPath = path + "::" + myparent.name + "::"; |
| 2780 | 2076 | } |
| 2781 | if (item.implDisambiguator !== null) { | |
| 2782 | anchor = item.implDisambiguator + "/" + anchor; | |
| 2077 | if (item.entry && item.entry.associatedItemDisambiguator !== null) { | |
| 2078 | anchor = item.entry.associatedItemDisambiguator + "/" + anchor; | |
| 2783 | 2079 | } |
| 2784 | 2080 | href = this.rootPath + path.replace(/::/g, "/") + |
| 2785 | 2081 | "/" + pageType + |
| 2786 | 2082 | "." + pageName + |
| 2787 | 2083 | ".html#" + anchor; |
| 2788 | 2084 | } else { |
| 2789 | displayPath = item.path + "::"; | |
| 2790 | href = this.rootPath + item.path.replace(/::/g, "/") + | |
| 2085 | displayPath = item.modulePath + "::"; | |
| 2086 | href = this.rootPath + item.modulePath.replace(/::/g, "/") + | |
| 2791 | 2087 | "/" + type + "." + name + ".html"; |
| 2792 | 2088 | } |
| 2793 | 2089 | return [displayPath, href, `${exactPath}::${name}`]; |
| ... | ... | @@ -2806,74 +2102,6 @@ class DocSearch { |
| 2806 | 2102 | return tmp; |
| 2807 | 2103 | } |
| 2808 | 2104 | |
| 2809 | /** | |
| 2810 | * Add extra data to result objects, and filter items that have been | |
| 2811 | * marked for removal. | |
| 2812 | * | |
| 2813 | * @param {rustdoc.ResultObject[]} results | |
| 2814 | * @param {"sig"|"elems"|"returned"|null} typeInfo | |
| 2815 | * @returns {rustdoc.ResultObject[]} | |
| 2816 | */ | |
| 2817 | const transformResults = (results, typeInfo) => { | |
| 2818 | const duplicates = new Set(); | |
| 2819 | const out = []; | |
| 2820 | ||
| 2821 | for (const result of results) { | |
| 2822 | if (result.id !== -1) { | |
| 2823 | const res = buildHrefAndPath(this.searchIndex[result.id]); | |
| 2824 | // many of these properties don't strictly need to be | |
| 2825 | // copied over, but copying them over satisfies tsc, | |
| 2826 | // and hopefully plays nice with the shape optimization | |
| 2827 | // of the browser engine. | |
| 2828 | /** @type {rustdoc.ResultObject} */ | |
| 2829 | const obj = Object.assign({ | |
| 2830 | parent: result.parent, | |
| 2831 | type: result.type, | |
| 2832 | dist: result.dist, | |
| 2833 | path_dist: result.path_dist, | |
| 2834 | index: result.index, | |
| 2835 | desc: result.desc, | |
| 2836 | item: result.item, | |
| 2837 | displayPath: pathSplitter(res[0]), | |
| 2838 | fullPath: "", | |
| 2839 | href: "", | |
| 2840 | displayTypeSignature: null, | |
| 2841 | }, this.searchIndex[result.id]); | |
| 2842 | ||
| 2843 | // To be sure than it some items aren't considered as duplicate. | |
| 2844 | obj.fullPath = res[2] + "|" + obj.ty; | |
| 2845 | ||
| 2846 | if (duplicates.has(obj.fullPath)) { | |
| 2847 | continue; | |
| 2848 | } | |
| 2849 | ||
| 2850 | // Exports are specifically not shown if the items they point at | |
| 2851 | // are already in the results. | |
| 2852 | if (obj.ty === TY_IMPORT && duplicates.has(res[2])) { | |
| 2853 | continue; | |
| 2854 | } | |
| 2855 | if (duplicates.has(res[2] + "|" + TY_IMPORT)) { | |
| 2856 | continue; | |
| 2857 | } | |
| 2858 | duplicates.add(obj.fullPath); | |
| 2859 | duplicates.add(res[2]); | |
| 2860 | ||
| 2861 | if (typeInfo !== null) { | |
| 2862 | obj.displayTypeSignature = | |
| 2863 | // @ts-expect-error | |
| 2864 | this.formatDisplayTypeSignature(obj, typeInfo); | |
| 2865 | } | |
| 2866 | ||
| 2867 | obj.href = res[1]; | |
| 2868 | out.push(obj); | |
| 2869 | if (out.length >= MAX_RESULTS) { | |
| 2870 | break; | |
| 2871 | } | |
| 2872 | } | |
| 2873 | } | |
| 2874 | return out; | |
| 2875 | }; | |
| 2876 | ||
| 2877 | 2105 | /** |
| 2878 | 2106 | * Add extra data to result objects, and filter items that have been |
| 2879 | 2107 | * marked for removal. |
| ... | ... | @@ -2883,9 +2111,11 @@ class DocSearch { |
| 2883 | 2111 | * |
| 2884 | 2112 | * @param {rustdoc.ResultObject} obj |
| 2885 | 2113 | * @param {"sig"|"elems"|"returned"|null} typeInfo |
| 2114 | * @param {rustdoc.QueryElement[]} elems | |
| 2115 | * @param {rustdoc.QueryElement[]} returned | |
| 2886 | 2116 | * @returns {Promise<rustdoc.DisplayTypeSignature>} |
| 2887 | 2117 | */ |
| 2888 | this.formatDisplayTypeSignature = async(obj, typeInfo) => { | |
| 2118 | const formatDisplayTypeSignature = async(obj, typeInfo, elems, returned) => { | |
| 2889 | 2119 | const objType = obj.type; |
| 2890 | 2120 | if (!objType) { |
| 2891 | 2121 | return {type: [], mappedNames: new Map(), whereClause: new Map()}; |
| ... | ... | @@ -2897,13 +2127,13 @@ class DocSearch { |
| 2897 | 2127 | if (typeInfo !== "elems" && typeInfo !== "returned") { |
| 2898 | 2128 | fnInputs = unifyFunctionTypes( |
| 2899 | 2129 | objType.inputs, |
| 2900 | parsedQuery.elems, | |
| 2130 | elems, | |
| 2901 | 2131 | objType.where_clause, |
| 2902 | 2132 | null, |
| 2903 | 2133 | mgensScratch => { |
| 2904 | 2134 | fnOutput = unifyFunctionTypes( |
| 2905 | 2135 | objType.output, |
| 2906 | parsedQuery.returned, | |
| 2136 | returned, | |
| 2907 | 2137 | objType.where_clause, |
| 2908 | 2138 | mgensScratch, |
| 2909 | 2139 | mgensOut => { |
| ... | ... | @@ -2917,10 +2147,9 @@ class DocSearch { |
| 2917 | 2147 | 0, |
| 2918 | 2148 | ); |
| 2919 | 2149 | } else { |
| 2920 | const arr = typeInfo === "elems" ? objType.inputs : objType.output; | |
| 2921 | 2150 | const highlighted = unifyFunctionTypes( |
| 2922 | arr, | |
| 2923 | parsedQuery.elems, | |
| 2151 | typeInfo === "elems" ? objType.inputs : objType.output, | |
| 2152 | typeInfo === "elems" ? elems : returned, | |
| 2924 | 2153 | objType.where_clause, |
| 2925 | 2154 | null, |
| 2926 | 2155 | mgensOut => { |
| ... | ... | @@ -2969,15 +2198,15 @@ class DocSearch { |
| 2969 | 2198 | } |
| 2970 | 2199 | }; |
| 2971 | 2200 | |
| 2972 | parsedQuery.elems.forEach(remapQuery); | |
| 2973 | parsedQuery.returned.forEach(remapQuery); | |
| 2201 | elems.forEach(remapQuery); | |
| 2202 | returned.forEach(remapQuery); | |
| 2974 | 2203 | |
| 2975 | 2204 | /** |
| 2976 | 2205 | * Write text to a highlighting array. |
| 2977 | 2206 | * Index 0 is not highlighted, index 1 is highlighted, |
| 2978 | 2207 | * index 2 is not highlighted, etc. |
| 2979 | 2208 | * |
| 2980 | * @param {{name?: string, highlighted?: boolean}} fnType - input | |
| 2209 | * @param {{name: string|null, highlighted?: boolean}} fnType - input | |
| 2981 | 2210 | * @param {string[]} result |
| 2982 | 2211 | */ |
| 2983 | 2212 | const pushText = (fnType, result) => { |
| ... | ... | @@ -3004,8 +2233,9 @@ class DocSearch { |
| 3004 | 2233 | * |
| 3005 | 2234 | * @param {rustdoc.HighlightedFunctionType} fnType - input |
| 3006 | 2235 | * @param {string[]} result |
| 2236 | * @returns {Promise<void>} | |
| 3007 | 2237 | */ |
| 3008 | const writeHof = (fnType, result) => { | |
| 2238 | const writeHof = async(fnType, result) => { | |
| 3009 | 2239 | const hofOutput = fnType.bindings.get(this.typeNameIdOfOutput) || []; |
| 3010 | 2240 | const hofInputs = fnType.generics; |
| 3011 | 2241 | pushText(fnType, result); |
| ... | ... | @@ -3016,7 +2246,7 @@ class DocSearch { |
| 3016 | 2246 | pushText({ name: ", ", highlighted: false }, result); |
| 3017 | 2247 | } |
| 3018 | 2248 | needsComma = true; |
| 3019 | writeFn(fnType, result); | |
| 2249 | await writeFn(fnType, result); | |
| 3020 | 2250 | } |
| 3021 | 2251 | pushText({ |
| 3022 | 2252 | name: hofOutput.length === 0 ? ")" : ") -> ", |
| ... | ... | @@ -3031,7 +2261,7 @@ class DocSearch { |
| 3031 | 2261 | pushText({ name: ", ", highlighted: false }, result); |
| 3032 | 2262 | } |
| 3033 | 2263 | needsComma = true; |
| 3034 | writeFn(fnType, result); | |
| 2264 | await writeFn(fnType, result); | |
| 3035 | 2265 | } |
| 3036 | 2266 | if (hofOutput.length > 1) { |
| 3037 | 2267 | pushText({name: ")", highlighted: false}, result); |
| ... | ... | @@ -3044,8 +2274,9 @@ class DocSearch { |
| 3044 | 2274 | * |
| 3045 | 2275 | * @param {rustdoc.HighlightedFunctionType} fnType |
| 3046 | 2276 | * @param {string[]} result |
| 2277 | * @returns {Promise<boolean>} | |
| 3047 | 2278 | */ |
| 3048 | const writeSpecialPrimitive = (fnType, result) => { | |
| 2279 | const writeSpecialPrimitive = async(fnType, result) => { | |
| 3049 | 2280 | if (fnType.id === this.typeNameIdOfArray || fnType.id === this.typeNameIdOfSlice || |
| 3050 | 2281 | fnType.id === this.typeNameIdOfTuple || fnType.id === this.typeNameIdOfUnit) { |
| 3051 | 2282 | const [ob, sb] = |
| ... | ... | @@ -3054,7 +2285,7 @@ class DocSearch { |
| 3054 | 2285 | ["[", "]"] : |
| 3055 | 2286 | ["(", ")"]; |
| 3056 | 2287 | pushText({ name: ob, highlighted: fnType.highlighted }, result); |
| 3057 | onEachBtwn( | |
| 2288 | await onEachBtwnAsync( | |
| 3058 | 2289 | fnType.generics, |
| 3059 | 2290 | nested => writeFn(nested, result), |
| 3060 | 2291 | // @ts-expect-error |
| ... | ... | @@ -3065,11 +2296,11 @@ class DocSearch { |
| 3065 | 2296 | } else if (fnType.id === this.typeNameIdOfReference) { |
| 3066 | 2297 | pushText({ name: "&", highlighted: fnType.highlighted }, result); |
| 3067 | 2298 | let prevHighlighted = false; |
| 3068 | onEachBtwn( | |
| 2299 | await onEachBtwnAsync( | |
| 3069 | 2300 | fnType.generics, |
| 3070 | value => { | |
| 2301 | async value => { | |
| 3071 | 2302 | prevHighlighted = !!value.highlighted; |
| 3072 | writeFn(value, result); | |
| 2303 | await writeFn(value, result); | |
| 3073 | 2304 | }, |
| 3074 | 2305 | // @ts-expect-error |
| 3075 | 2306 | value => pushText({ |
| ... | ... | @@ -3078,8 +2309,16 @@ class DocSearch { |
| 3078 | 2309 | }, result), |
| 3079 | 2310 | ); |
| 3080 | 2311 | return true; |
| 3081 | } else if (fnType.id === this.typeNameIdOfFn) { | |
| 3082 | writeHof(fnType, result); | |
| 2312 | } else if ( | |
| 2313 | fnType.id === this.typeNameIdOfFn || | |
| 2314 | fnType.id === this.typeNameIdOfFnMut || | |
| 2315 | fnType.id === this.typeNameIdOfFnOnce || | |
| 2316 | fnType.id === this.typeNameIdOfFnPtr | |
| 2317 | ) { | |
| 2318 | await writeHof(fnType, result); | |
| 2319 | return true; | |
| 2320 | } else if (fnType.id === this.typeNameIdOfNever) { | |
| 2321 | pushText({ name: "!", highlighted: fnType.highlighted }, result); | |
| 3083 | 2322 | return true; |
| 3084 | 2323 | } |
| 3085 | 2324 | return false; |
| ... | ... | @@ -3091,8 +2330,9 @@ class DocSearch { |
| 3091 | 2330 | * |
| 3092 | 2331 | * @param {rustdoc.HighlightedFunctionType} fnType |
| 3093 | 2332 | * @param {string[]} result |
| 2333 | * @returns {Promise<void>} | |
| 3094 | 2334 | */ |
| 3095 | const writeFn = (fnType, result) => { | |
| 2335 | const writeFn = async(fnType, result) => { | |
| 3096 | 2336 | if (fnType.id !== null && fnType.id < 0) { |
| 3097 | 2337 | if (fnParamNames[-1 - fnType.id] === "") { |
| 3098 | 2338 | // Normally, there's no need to shown an unhighlighted |
| ... | ... | @@ -3101,7 +2341,7 @@ class DocSearch { |
| 3101 | 2341 | fnType.generics : |
| 3102 | 2342 | objType.where_clause[-1 - fnType.id]; |
| 3103 | 2343 | for (const nested of generics) { |
| 3104 | writeFn(nested, result); | |
| 2344 | await writeFn(nested, result); | |
| 3105 | 2345 | } |
| 3106 | 2346 | return; |
| 3107 | 2347 | } else if (mgens) { |
| ... | ... | @@ -3120,7 +2360,7 @@ class DocSearch { |
| 3120 | 2360 | }, result); |
| 3121 | 2361 | /** @type{string[]} */ |
| 3122 | 2362 | const where = []; |
| 3123 | onEachBtwn( | |
| 2363 | await onEachBtwnAsync( | |
| 3124 | 2364 | fnType.generics, |
| 3125 | 2365 | nested => writeFn(nested, where), |
| 3126 | 2366 | // @ts-expect-error |
| ... | ... | @@ -3131,32 +2371,61 @@ class DocSearch { |
| 3131 | 2371 | } |
| 3132 | 2372 | } else { |
| 3133 | 2373 | if (fnType.ty === TY_PRIMITIVE) { |
| 3134 | if (writeSpecialPrimitive(fnType, result)) { | |
| 2374 | if (await writeSpecialPrimitive(fnType, result)) { | |
| 3135 | 2375 | return; |
| 3136 | 2376 | } |
| 3137 | 2377 | } else if (fnType.ty === TY_TRAIT && ( |
| 3138 | 2378 | fnType.id === this.typeNameIdOfFn || |
| 3139 | fnType.id === this.typeNameIdOfFnMut || | |
| 3140 | fnType.id === this.typeNameIdOfFnOnce)) { | |
| 3141 | writeHof(fnType, result); | |
| 2379 | fnType.id === this.typeNameIdOfFnMut || | |
| 2380 | fnType.id === this.typeNameIdOfFnOnce || | |
| 2381 | fnType.id === this.typeNameIdOfFnPtr | |
| 2382 | )) { | |
| 2383 | await writeHof(fnType, result); | |
| 2384 | return; | |
| 2385 | } else if (fnType.name === "" && | |
| 2386 | fnType.bindings.size === 0 && | |
| 2387 | fnType.generics.length !== 0 | |
| 2388 | ) { | |
| 2389 | pushText({ name: "impl ", highlighted: false }, result); | |
| 2390 | if (fnType.generics.length > 1) { | |
| 2391 | pushText({ name: "(", highlighted: false }, result); | |
| 2392 | } | |
| 2393 | await onEachBtwnAsync( | |
| 2394 | fnType.generics, | |
| 2395 | value => writeFn(value, result), | |
| 2396 | // @ts-expect-error | |
| 2397 | () => pushText({ name: ", ", highlighted: false }, result), | |
| 2398 | ); | |
| 2399 | if (fnType.generics.length > 1) { | |
| 2400 | pushText({ name: ")", highlighted: false }, result); | |
| 2401 | } | |
| 3142 | 2402 | return; |
| 3143 | 2403 | } |
| 3144 | 2404 | pushText(fnType, result); |
| 3145 | 2405 | let hasBindings = false; |
| 3146 | 2406 | if (fnType.bindings.size > 0) { |
| 3147 | onEachBtwn( | |
| 3148 | fnType.bindings, | |
| 3149 | ([key, values]) => { | |
| 3150 | const name = this.assocTypeIdNameMap.get(key); | |
| 2407 | await onEachBtwnAsync( | |
| 2408 | await Promise.all([...fnType.bindings.entries()].map( | |
| 2409 | /** | |
| 2410 | * @param {[number, rustdoc.HighlightedFunctionType[]]} param0 | |
| 2411 | * @returns {Promise<[ | |
| 2412 | * string|null, | |
| 2413 | * rustdoc.HighlightedFunctionType[], | |
| 2414 | * ]>} | |
| 2415 | */ | |
| 2416 | async([key, values]) => [await this.getName(key), values], | |
| 2417 | )), | |
| 2418 | async([name, values]) => { | |
| 3151 | 2419 | // @ts-expect-error |
| 3152 | 2420 | if (values.length === 1 && values[0].id < 0 && |
| 3153 | 2421 | // @ts-expect-error |
| 3154 | `${fnType.name}::${name}` === fnParamNames[-1 - values[0].id]) { | |
| 2422 | `${fnType.name}::${name}` === fnParamNames[-1 - values[0].id] | |
| 2423 | ) { | |
| 3155 | 2424 | // the internal `Item=Iterator::Item` type variable should be |
| 3156 | 2425 | // shown in the where clause and name mapping output, but is |
| 3157 | 2426 | // redundant in this spot |
| 3158 | 2427 | for (const value of values) { |
| 3159 | writeFn(value, []); | |
| 2428 | await writeFn(value, []); | |
| 3160 | 2429 | } |
| 3161 | 2430 | return true; |
| 3162 | 2431 | } |
| ... | ... | @@ -3169,7 +2438,7 @@ class DocSearch { |
| 3169 | 2438 | name: values.length !== 1 ? "=(" : "=", |
| 3170 | 2439 | highlighted: false, |
| 3171 | 2440 | }, result); |
| 3172 | onEachBtwn( | |
| 2441 | await onEachBtwnAsync( | |
| 3173 | 2442 | values || [], |
| 3174 | 2443 | value => writeFn(value, result), |
| 3175 | 2444 | // @ts-expect-error |
| ... | ... | @@ -3186,7 +2455,7 @@ class DocSearch { |
| 3186 | 2455 | if (fnType.generics.length > 0) { |
| 3187 | 2456 | pushText({ name: hasBindings ? ", " : "<", highlighted: false }, result); |
| 3188 | 2457 | } |
| 3189 | onEachBtwn( | |
| 2458 | await onEachBtwnAsync( | |
| 3190 | 2459 | fnType.generics, |
| 3191 | 2460 | value => writeFn(value, result), |
| 3192 | 2461 | // @ts-expect-error |
| ... | ... | @@ -3199,14 +2468,14 @@ class DocSearch { |
| 3199 | 2468 | }; |
| 3200 | 2469 | /** @type {string[]} */ |
| 3201 | 2470 | const type = []; |
| 3202 | onEachBtwn( | |
| 2471 | await onEachBtwnAsync( | |
| 3203 | 2472 | fnInputs, |
| 3204 | 2473 | fnType => writeFn(fnType, type), |
| 3205 | 2474 | // @ts-expect-error |
| 3206 | 2475 | () => pushText({ name: ", ", highlighted: false }, type), |
| 3207 | 2476 | ); |
| 3208 | 2477 | pushText({ name: " -> ", highlighted: false }, type); |
| 3209 | onEachBtwn( | |
| 2478 | await onEachBtwnAsync( | |
| 3210 | 2479 | fnOutput, |
| 3211 | 2480 | fnType => writeFn(fnType, type), |
| 3212 | 2481 | // @ts-expect-error |
| ... | ... | @@ -3217,176 +2486,252 @@ class DocSearch { |
| 3217 | 2486 | }; |
| 3218 | 2487 | |
| 3219 | 2488 | /** |
| 3220 | * This function takes a result map, and sorts it by various criteria, including edit | |
| 3221 | * distance, substring match, and the crate it comes from. | |
| 2489 | * Add extra data to result objects, and filter items that have been | |
| 2490 | * marked for removal. | |
| 3222 | 2491 | * |
| 3223 | * @param {rustdoc.Results} results | |
| 2492 | * @param {[rustdoc.PlainResultObject, rustdoc.Row][]} results | |
| 3224 | 2493 | * @param {"sig"|"elems"|"returned"|null} typeInfo |
| 3225 | * @param {string} preferredCrate | |
| 3226 | * @returns {Promise<rustdoc.ResultObject[]>} | |
| 2494 | * @param {Set<string>} duplicates | |
| 2495 | * @returns {rustdoc.ResultObject[]} | |
| 3227 | 2496 | */ |
| 3228 | const sortResults = async(results, typeInfo, preferredCrate) => { | |
| 3229 | const userQuery = parsedQuery.userQuery; | |
| 3230 | const normalizedUserQuery = parsedQuery.userQuery.toLowerCase(); | |
| 3231 | const isMixedCase = normalizedUserQuery !== userQuery; | |
| 3232 | const result_list = []; | |
| 3233 | const isReturnTypeQuery = parsedQuery.elems.length === 0 || | |
| 3234 | typeInfo === "returned"; | |
| 3235 | for (const result of results.values()) { | |
| 3236 | result.item = this.searchIndex[result.id]; | |
| 3237 | result.word = this.searchIndex[result.id].word; | |
| 3238 | if (isReturnTypeQuery) { | |
| 3239 | // We are doing a return-type based search, deprioritize "clone-like" results, | |
| 3240 | // ie. functions that also take the queried type as an argument. | |
| 3241 | const resultItemType = result.item && result.item.type; | |
| 3242 | if (!resultItemType) { | |
| 2497 | const transformResults = (results, typeInfo, duplicates) => { | |
| 2498 | const out = []; | |
| 2499 | ||
| 2500 | for (const [result, item] of results) { | |
| 2501 | if (item.id !== -1) { | |
| 2502 | const res = buildHrefAndPath(item); | |
| 2503 | // many of these properties don't strictly need to be | |
| 2504 | // copied over, but copying them over satisfies tsc, | |
| 2505 | // and hopefully plays nice with the shape optimization | |
| 2506 | // of the browser engine. | |
| 2507 | /** @type {rustdoc.ResultObject} */ | |
| 2508 | const obj = Object.assign({ | |
| 2509 | parent: item.parent ? { | |
| 2510 | path: item.parent.path.modulePath, | |
| 2511 | exactPath: item.parent.path.exactModulePath || | |
| 2512 | item.parent.path.modulePath, | |
| 2513 | name: item.parent.name, | |
| 2514 | ty: item.parent.path.ty, | |
| 2515 | } : undefined, | |
| 2516 | type: item.type && item.type.functionSignature ? | |
| 2517 | item.type.functionSignature : | |
| 2518 | undefined, | |
| 2519 | paramNames: item.type && item.type.paramNames ? | |
| 2520 | item.type.paramNames : | |
| 2521 | undefined, | |
| 2522 | dist: result.dist, | |
| 2523 | path_dist: result.path_dist, | |
| 2524 | index: result.index, | |
| 2525 | desc: this.getDesc(result.id), | |
| 2526 | item, | |
| 2527 | displayPath: pathSplitter(res[0]), | |
| 2528 | fullPath: "", | |
| 2529 | href: "", | |
| 2530 | displayTypeSignature: null, | |
| 2531 | }, result); | |
| 2532 | ||
| 2533 | // To be sure than it some items aren't considered as duplicate. | |
| 2534 | obj.fullPath = res[2] + "|" + obj.item.ty; | |
| 2535 | ||
| 2536 | if (duplicates.has(obj.fullPath)) { | |
| 2537 | continue; | |
| 2538 | } | |
| 2539 | ||
| 2540 | // Exports are specifically not shown if the items they point at | |
| 2541 | // are already in the results. | |
| 2542 | if (obj.item.ty === TY_IMPORT && duplicates.has(res[2])) { | |
| 2543 | continue; | |
| 2544 | } | |
| 2545 | if (duplicates.has(res[2] + "|" + TY_IMPORT)) { | |
| 3243 | 2546 | continue; |
| 3244 | 2547 | } |
| 3245 | const inputs = resultItemType.inputs; | |
| 3246 | const where_clause = resultItemType.where_clause; | |
| 3247 | if (containsTypeFromQuery(inputs, where_clause)) { | |
| 3248 | result.path_dist *= 100; | |
| 3249 | result.dist *= 100; | |
| 2548 | duplicates.add(obj.fullPath); | |
| 2549 | duplicates.add(res[2]); | |
| 2550 | ||
| 2551 | if (typeInfo !== null) { | |
| 2552 | obj.displayTypeSignature = formatDisplayTypeSignature( | |
| 2553 | obj, | |
| 2554 | typeInfo, | |
| 2555 | result.elems, | |
| 2556 | result.returned, | |
| 2557 | ); | |
| 2558 | } | |
| 2559 | ||
| 2560 | obj.href = res[1]; | |
| 2561 | out.push(obj); | |
| 2562 | if (out.length >= MAX_RESULTS) { | |
| 2563 | break; | |
| 3250 | 2564 | } |
| 3251 | 2565 | } |
| 3252 | result_list.push(result); | |
| 3253 | 2566 | } |
| 3254 | 2567 | |
| 3255 | result_list.sort((aaa, bbb) => { | |
| 3256 | /** @type {number} */ | |
| 3257 | let a; | |
| 3258 | /** @type {number} */ | |
| 3259 | let b; | |
| 2568 | return out; | |
| 2569 | }; | |
| 3260 | 2570 | |
| 3261 | // sort by exact case-sensitive match | |
| 3262 | if (isMixedCase) { | |
| 3263 | a = Number(aaa.item.name !== userQuery); | |
| 3264 | b = Number(bbb.item.name !== userQuery); | |
| 3265 | if (a !== b) { | |
| 3266 | return a - b; | |
| 2571 | const sortAndTransformResults = | |
| 2572 | /** | |
| 2573 | * @this {DocSearch} | |
| 2574 | * @param {Array<rustdoc.PlainResultObject|null>} results | |
| 2575 | * @param {"sig"|"elems"|"returned"|null} typeInfo | |
| 2576 | * @param {string} preferredCrate | |
| 2577 | * @param {Set<string>} duplicates | |
| 2578 | * @returns {AsyncGenerator<rustdoc.ResultObject, number>} | |
| 2579 | */ | |
| 2580 | async function*(results, typeInfo, preferredCrate, duplicates) { | |
| 2581 | const userQuery = parsedQuery.userQuery; | |
| 2582 | const normalizedUserQuery = parsedQuery.userQuery.toLowerCase(); | |
| 2583 | const isMixedCase = normalizedUserQuery !== userQuery; | |
| 2584 | /** | |
| 2585 | * @type {[rustdoc.PlainResultObject, rustdoc.Row][]} | |
| 2586 | */ | |
| 2587 | const result_list = []; | |
| 2588 | for (const result of results.values()) { | |
| 2589 | if (!result) { | |
| 2590 | continue; | |
| 2591 | } | |
| 2592 | /** | |
| 2593 | * @type {rustdoc.Row?} | |
| 2594 | */ | |
| 2595 | const item = await this.getRow(result.id); | |
| 2596 | if (!item) { | |
| 2597 | continue; | |
| 2598 | } | |
| 2599 | if (filterCrates !== null && item.crate !== filterCrates) { | |
| 2600 | continue; | |
| 2601 | } | |
| 2602 | if (item) { | |
| 2603 | result_list.push([result, item]); | |
| 2604 | } else { | |
| 2605 | continue; | |
| 3267 | 2606 | } |
| 3268 | 2607 | } |
| 3269 | 2608 | |
| 3270 | // sort by exact match with regard to the last word (mismatch goes later) | |
| 3271 | a = Number(aaa.word !== normalizedUserQuery); | |
| 3272 | b = Number(bbb.word !== normalizedUserQuery); | |
| 3273 | if (a !== b) { | |
| 3274 | return a - b; | |
| 3275 | } | |
| 2609 | result_list.sort(([aaa, aai], [bbb, bbi]) => { | |
| 2610 | /** @type {number} */ | |
| 2611 | let a; | |
| 2612 | /** @type {number} */ | |
| 2613 | let b; | |
| 2614 | ||
| 2615 | if (typeInfo === null) { | |
| 2616 | // in name based search... | |
| 2617 | ||
| 2618 | // sort by exact case-sensitive match | |
| 2619 | if (isMixedCase) { | |
| 2620 | a = Number(aai.name !== userQuery); | |
| 2621 | b = Number(bbi.name !== userQuery); | |
| 2622 | if (a !== b) { | |
| 2623 | return a - b; | |
| 2624 | } | |
| 2625 | } | |
| 3276 | 2626 | |
| 3277 | // sort by index of keyword in item name (no literal occurrence goes later) | |
| 3278 | a = Number(aaa.index < 0); | |
| 3279 | b = Number(bbb.index < 0); | |
| 3280 | if (a !== b) { | |
| 3281 | return a - b; | |
| 3282 | } | |
| 2627 | // sort by exact match with regard to the last word (mismatch goes later) | |
| 2628 | a = Number(aai.normalizedName !== normalizedUserQuery); | |
| 2629 | b = Number(bbi.normalizedName !== normalizedUserQuery); | |
| 2630 | if (a !== b) { | |
| 2631 | return a - b; | |
| 2632 | } | |
| 3283 | 2633 | |
| 3284 | // in type based search, put functions first | |
| 3285 | if (parsedQuery.hasReturnArrow) { | |
| 3286 | a = Number(!isFnLikeTy(aaa.item.ty)); | |
| 3287 | b = Number(!isFnLikeTy(bbb.item.ty)); | |
| 2634 | // sort by index of keyword in item name (no literal occurrence goes later) | |
| 2635 | a = Number(aaa.index < 0); | |
| 2636 | b = Number(bbb.index < 0); | |
| 2637 | if (a !== b) { | |
| 2638 | return a - b; | |
| 2639 | } | |
| 2640 | } | |
| 2641 | ||
| 2642 | // Sort by distance in the path part, if specified | |
| 2643 | // (less changes required to match means higher rankings) | |
| 2644 | a = Number(aaa.path_dist); | |
| 2645 | b = Number(bbb.path_dist); | |
| 3288 | 2646 | if (a !== b) { |
| 3289 | 2647 | return a - b; |
| 3290 | 2648 | } |
| 3291 | } | |
| 3292 | 2649 | |
| 3293 | // Sort by distance in the path part, if specified | |
| 3294 | // (less changes required to match means higher rankings) | |
| 3295 | a = Number(aaa.path_dist); | |
| 3296 | b = Number(bbb.path_dist); | |
| 3297 | if (a !== b) { | |
| 3298 | return a - b; | |
| 3299 | } | |
| 3300 | ||
| 3301 | // (later literal occurrence, if any, goes later) | |
| 3302 | a = Number(aaa.index); | |
| 3303 | b = Number(bbb.index); | |
| 3304 | if (a !== b) { | |
| 3305 | return a - b; | |
| 3306 | } | |
| 2650 | // (later literal occurrence, if any, goes later) | |
| 2651 | a = Number(aaa.index); | |
| 2652 | b = Number(bbb.index); | |
| 2653 | if (a !== b) { | |
| 2654 | return a - b; | |
| 2655 | } | |
| 3307 | 2656 | |
| 3308 | // Sort by distance in the name part, the last part of the path | |
| 3309 | // (less changes required to match means higher rankings) | |
| 3310 | a = Number(aaa.dist); | |
| 3311 | b = Number(bbb.dist); | |
| 3312 | if (a !== b) { | |
| 3313 | return a - b; | |
| 3314 | } | |
| 2657 | // Sort by distance in the name part, the last part of the path | |
| 2658 | // (less changes required to match means higher rankings) | |
| 2659 | a = Number(aaa.dist); | |
| 2660 | b = Number(bbb.dist); | |
| 2661 | if (a !== b) { | |
| 2662 | return a - b; | |
| 2663 | } | |
| 3315 | 2664 | |
| 3316 | // sort deprecated items later | |
| 3317 | a = Number( | |
| 3318 | // @ts-expect-error | |
| 3319 | this.searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex), | |
| 3320 | ); | |
| 3321 | b = Number( | |
| 3322 | // @ts-expect-error | |
| 3323 | this.searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex), | |
| 3324 | ); | |
| 3325 | if (a !== b) { | |
| 3326 | return a - b; | |
| 3327 | } | |
| 2665 | // sort aliases lower | |
| 2666 | a = Number(aaa.is_alias); | |
| 2667 | b = Number(bbb.is_alias); | |
| 2668 | if (a !== b) { | |
| 2669 | return a - b; | |
| 2670 | } | |
| 3328 | 2671 | |
| 3329 | // sort by crate (current crate comes first) | |
| 3330 | a = Number(aaa.item.crate !== preferredCrate); | |
| 3331 | b = Number(bbb.item.crate !== preferredCrate); | |
| 3332 | if (a !== b) { | |
| 3333 | return a - b; | |
| 3334 | } | |
| 2672 | // sort deprecated items later | |
| 2673 | a = Number(aai.deprecated); | |
| 2674 | b = Number(bbi.deprecated); | |
| 2675 | if (a !== b) { | |
| 2676 | return a - b; | |
| 2677 | } | |
| 3335 | 2678 | |
| 3336 | // sort by item name length (longer goes later) | |
| 3337 | a = Number(aaa.word.length); | |
| 3338 | b = Number(bbb.word.length); | |
| 3339 | if (a !== b) { | |
| 3340 | return a - b; | |
| 3341 | } | |
| 2679 | // sort by crate (current crate comes first) | |
| 2680 | a = Number(aai.crate !== preferredCrate); | |
| 2681 | b = Number(bbi.crate !== preferredCrate); | |
| 2682 | if (a !== b) { | |
| 2683 | return a - b; | |
| 2684 | } | |
| 3342 | 2685 | |
| 3343 | // sort doc alias items later | |
| 3344 | a = Number(aaa.item.is_alias === true); | |
| 3345 | b = Number(bbb.item.is_alias === true); | |
| 3346 | if (a !== b) { | |
| 3347 | return a - b; | |
| 3348 | } | |
| 2686 | // sort by item name length (longer goes later) | |
| 2687 | a = Number(aai.normalizedName.length); | |
| 2688 | b = Number(bbi.normalizedName.length); | |
| 2689 | if (a !== b) { | |
| 2690 | return a - b; | |
| 2691 | } | |
| 3349 | 2692 | |
| 3350 | // sort by item name (lexicographically larger goes later) | |
| 3351 | let aw = aaa.word; | |
| 3352 | let bw = bbb.word; | |
| 3353 | if (aw !== bw) { | |
| 3354 | return (aw > bw ? +1 : -1); | |
| 3355 | } | |
| 2693 | // sort by item name (lexicographically larger goes later) | |
| 2694 | let aw = aai.normalizedName; | |
| 2695 | let bw = bbi.normalizedName; | |
| 2696 | if (aw !== bw) { | |
| 2697 | return (aw > bw ? +1 : -1); | |
| 2698 | } | |
| 3356 | 2699 | |
| 3357 | // sort by description (no description goes later) | |
| 3358 | a = Number( | |
| 3359 | // @ts-expect-error | |
| 3360 | this.searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex), | |
| 3361 | ); | |
| 3362 | b = Number( | |
| 3363 | // @ts-expect-error | |
| 3364 | this.searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex), | |
| 3365 | ); | |
| 3366 | if (a !== b) { | |
| 3367 | return a - b; | |
| 3368 | } | |
| 2700 | // sort by description (no description goes later) | |
| 2701 | const di = this.database.getData("desc"); | |
| 2702 | if (di) { | |
| 2703 | a = Number(di.isEmpty(aaa.id)); | |
| 2704 | b = Number(di.isEmpty(bbb.id)); | |
| 2705 | if (a !== b) { | |
| 2706 | return a - b; | |
| 2707 | } | |
| 2708 | } | |
| 3369 | 2709 | |
| 3370 | // sort by type (later occurrence in `itemTypes` goes later) | |
| 3371 | a = Number(aaa.item.ty); | |
| 3372 | b = Number(bbb.item.ty); | |
| 3373 | if (a !== b) { | |
| 3374 | return a - b; | |
| 3375 | } | |
| 2710 | // sort by type (later occurrence in `itemTypes` goes later) | |
| 2711 | a = Number(aai.ty); | |
| 2712 | b = Number(bbi.ty); | |
| 2713 | if (a !== b) { | |
| 2714 | return a - b; | |
| 2715 | } | |
| 3376 | 2716 | |
| 3377 | // sort by path (lexicographically larger goes later) | |
| 3378 | aw = aaa.item.path; | |
| 3379 | bw = bbb.item.path; | |
| 3380 | if (aw !== bw) { | |
| 3381 | return (aw > bw ? +1 : -1); | |
| 3382 | } | |
| 2717 | // sort by path (lexicographically larger goes later) | |
| 2718 | const ap = aai.modulePath; | |
| 2719 | const bp = bbi.modulePath; | |
| 2720 | aw = ap === undefined ? "" : ap; | |
| 2721 | bw = bp === undefined ? "" : bp; | |
| 2722 | if (aw !== bw) { | |
| 2723 | return (aw > bw ? +1 : -1); | |
| 2724 | } | |
| 3383 | 2725 | |
| 3384 | // que sera, sera | |
| 3385 | return 0; | |
| 3386 | }); | |
| 2726 | // que sera, sera | |
| 2727 | return 0; | |
| 2728 | }); | |
| 3387 | 2729 | |
| 3388 | return transformResults(result_list, typeInfo); | |
| 3389 | }; | |
| 2730 | const transformed_result_list = transformResults(result_list, typeInfo, duplicates); | |
| 2731 | yield* transformed_result_list; | |
| 2732 | return transformed_result_list.length; | |
| 2733 | } | |
| 2734 | .bind(this); | |
| 3390 | 2735 | |
| 3391 | 2736 | /** |
| 3392 | 2737 | * This function checks if a list of search query `queryElems` can all be found in the |
| ... | ... | @@ -3938,6 +3283,8 @@ class DocSearch { |
| 3938 | 3283 | } |
| 3939 | 3284 | return true; |
| 3940 | 3285 | } else { |
| 3286 | // For these special cases, matching code need added to the inverted index. | |
| 3287 | // search_index.rs -> convert_render_type does this | |
| 3941 | 3288 | if (queryElem.id === this.typeNameIdOfArrayOrSlice && |
| 3942 | 3289 | (fnType.id === this.typeNameIdOfSlice || fnType.id === this.typeNameIdOfArray) |
| 3943 | 3290 | ) { |
| ... | ... | @@ -3948,10 +3295,12 @@ class DocSearch { |
| 3948 | 3295 | ) { |
| 3949 | 3296 | // () matches primitive:tuple or primitive:unit |
| 3950 | 3297 | // if it matches, then we're fine, and this is an appropriate match candidate |
| 3951 | } else if (queryElem.id === this.typeNameIdOfHof && | |
| 3952 | (fnType.id === this.typeNameIdOfFn || fnType.id === this.typeNameIdOfFnMut || | |
| 3953 | fnType.id === this.typeNameIdOfFnOnce) | |
| 3954 | ) { | |
| 3298 | } else if (queryElem.id === this.typeNameIdOfHof && ( | |
| 3299 | fnType.id === this.typeNameIdOfFn || | |
| 3300 | fnType.id === this.typeNameIdOfFnMut || | |
| 3301 | fnType.id === this.typeNameIdOfFnOnce || | |
| 3302 | fnType.id === this.typeNameIdOfFnPtr | |
| 3303 | )) { | |
| 3955 | 3304 | // -> matches fn, fnonce, and fnmut |
| 3956 | 3305 | // if it matches, then we're fine, and this is an appropriate match candidate |
| 3957 | 3306 | } else if (fnType.id !== queryElem.id || queryElem.id === null) { |
| ... | ... | @@ -4134,21 +3483,13 @@ class DocSearch { |
| 4134 | 3483 | * This function checks if the given list contains any |
| 4135 | 3484 | * (non-generic) types mentioned in the query. |
| 4136 | 3485 | * |
| 3486 | * @param {rustdoc.QueryElement[]} elems | |
| 4137 | 3487 | * @param {rustdoc.FunctionType[]} list - A list of function types. |
| 4138 | 3488 | * @param {rustdoc.FunctionType[][]} where_clause - Trait bounds for generic items. |
| 4139 | 3489 | */ |
| 4140 | function containsTypeFromQuery(list, where_clause) { | |
| 3490 | function containsTypeFromQuery(elems, list, where_clause) { | |
| 4141 | 3491 | if (!list) return false; |
| 4142 | for (const ty of parsedQuery.returned) { | |
| 4143 | // negative type ids are generics | |
| 4144 | if (ty.id !== null && ty.id < 0) { | |
| 4145 | continue; | |
| 4146 | } | |
| 4147 | if (checkIfInList(list, ty, where_clause, null, 0)) { | |
| 4148 | return true; | |
| 4149 | } | |
| 4150 | } | |
| 4151 | for (const ty of parsedQuery.elems) { | |
| 3492 | for (const ty of elems) { | |
| 4152 | 3493 | if (ty.id !== null && ty.id < 0) { |
| 4153 | 3494 | continue; |
| 4154 | 3495 | } |
| ... | ... | @@ -4240,10 +3581,10 @@ class DocSearch { |
| 4240 | 3581 | /** |
| 4241 | 3582 | * Compute an "edit distance" that ignores missing path elements. |
| 4242 | 3583 | * @param {string[]} contains search query path |
| 4243 | * @param {rustdoc.Row} ty indexed item | |
| 3584 | * @param {string[]} path indexed page path | |
| 4244 | 3585 | * @returns {null|number} edit distance |
| 4245 | 3586 | */ |
| 4246 | function checkPath(contains, ty) { | |
| 3587 | function checkPath(contains, path) { | |
| 4247 | 3588 | if (contains.length === 0) { |
| 4248 | 3589 | return 0; |
| 4249 | 3590 | } |
| ... | ... | @@ -4251,11 +3592,6 @@ class DocSearch { |
| 4251 | 3592 | contains.reduce((acc, next) => acc + next.length, 0) / 3, |
| 4252 | 3593 | ); |
| 4253 | 3594 | let ret_dist = maxPathEditDistance + 1; |
| 4254 | const path = ty.path.split("::"); | |
| 4255 | ||
| 4256 | if (ty.parent && ty.parent.name) { | |
| 4257 | path.push(ty.parent.name.toLowerCase()); | |
| 4258 | } | |
| 4259 | 3595 | |
| 4260 | 3596 | const length = path.length; |
| 4261 | 3597 | const clength = contains.length; |
| ... | ... | @@ -4281,7 +3617,32 @@ class DocSearch { |
| 4281 | 3617 | return ret_dist > maxPathEditDistance ? null : ret_dist; |
| 4282 | 3618 | } |
| 4283 | 3619 | |
| 4284 | // @ts-expect-error | |
| 3620 | /** | |
| 3621 | * Compute an "edit distance" that ignores missing path elements. | |
| 3622 | * @param {string[]} contains search query path | |
| 3623 | * @param {rustdoc.Row} row indexed item | |
| 3624 | * @returns {null|number} edit distance | |
| 3625 | */ | |
| 3626 | function checkRowPath(contains, row) { | |
| 3627 | if (contains.length === 0) { | |
| 3628 | return 0; | |
| 3629 | } | |
| 3630 | ||
| 3631 | const path = row.modulePath.split("::"); | |
| 3632 | ||
| 3633 | if (row.parent && row.parent.name) { | |
| 3634 | path.push(row.parent.name.toLowerCase()); | |
| 3635 | } | |
| 3636 | ||
| 3637 | return checkPath(contains, path); | |
| 3638 | } | |
| 3639 | ||
| 3640 | /** | |
| 3641 | * | |
| 3642 | * @param {number} filter | |
| 3643 | * @param {rustdoc.ItemType} type | |
| 3644 | * @returns | |
| 3645 | */ | |
| 4285 | 3646 | function typePassesFilter(filter, type) { |
| 4286 | 3647 | // No filter or Exact mach |
| 4287 | 3648 | if (filter <= NO_TYPE_FILTER || filter === type) return true; |
| ... | ... | @@ -4303,366 +3664,839 @@ class DocSearch { |
| 4303 | 3664 | return false; |
| 4304 | 3665 | } |
| 4305 | 3666 | |
| 4306 | // @ts-expect-error | |
| 4307 | const handleAliases = async(ret, query, filterCrates, currentCrate) => { | |
| 4308 | const lowerQuery = query.toLowerCase(); | |
| 4309 | if (this.FOUND_ALIASES.has(lowerQuery)) { | |
| 4310 | return; | |
| 4311 | } | |
| 4312 | this.FOUND_ALIASES.add(lowerQuery); | |
| 4313 | // We separate aliases and crate aliases because we want to have current crate | |
| 4314 | // aliases to be before the others in the displayed results. | |
| 4315 | // @ts-expect-error | |
| 4316 | const aliases = []; | |
| 4317 | // @ts-expect-error | |
| 4318 | const crateAliases = []; | |
| 4319 | if (filterCrates !== null) { | |
| 4320 | if (this.ALIASES.has(filterCrates) | |
| 4321 | && this.ALIASES.get(filterCrates).has(lowerQuery)) { | |
| 4322 | const query_aliases = this.ALIASES.get(filterCrates).get(lowerQuery); | |
| 4323 | for (const alias of query_aliases) { | |
| 4324 | aliases.push(alias); | |
| 4325 | } | |
| 3667 | const innerRunNameQuery = | |
| 3668 | /** | |
| 3669 | * @this {DocSearch} | |
| 3670 | * @param {string} currentCrate | |
| 3671 | * @returns {AsyncGenerator<rustdoc.ResultObject>} | |
| 3672 | */ | |
| 3673 | async function*(currentCrate) { | |
| 3674 | const index = this.database.getIndex("normalizedName"); | |
| 3675 | if (!index) { | |
| 3676 | return; | |
| 4326 | 3677 | } |
| 4327 | } else { | |
| 4328 | for (const [crate, crateAliasesIndex] of this.ALIASES) { | |
| 4329 | if (crateAliasesIndex.has(lowerQuery)) { | |
| 4330 | // @ts-expect-error | |
| 4331 | const pushTo = crate === currentCrate ? crateAliases : aliases; | |
| 4332 | const query_aliases = crateAliasesIndex.get(lowerQuery); | |
| 4333 | for (const alias of query_aliases) { | |
| 4334 | pushTo.push(alias); | |
| 3678 | const idDuplicates = new Set(); | |
| 3679 | const pathDuplicates = new Set(); | |
| 3680 | let count = 0; | |
| 3681 | const prefixResults = []; | |
| 3682 | const normalizedUserQuery = parsedQuery.userQuery | |
| 3683 | .replace(/[_"]/g, "") | |
| 3684 | .toLowerCase(); | |
| 3685 | /** | |
| 3686 | * @param {string} name | |
| 3687 | * @param {number} alias | |
| 3688 | * @param {number} dist | |
| 3689 | * @param {number} index | |
| 3690 | * @returns {Promise<rustdoc.PlainResultObject?>} | |
| 3691 | */ | |
| 3692 | const handleAlias = async(name, alias, dist, index) => { | |
| 3693 | return { | |
| 3694 | id: alias, | |
| 3695 | dist, | |
| 3696 | path_dist: 0, | |
| 3697 | index, | |
| 3698 | alias: name, | |
| 3699 | is_alias: true, | |
| 3700 | elems: [], // only used in type-based queries | |
| 3701 | returned: [], // only used in type-based queries | |
| 3702 | original: await this.getRow(alias), | |
| 3703 | }; | |
| 3704 | }; | |
| 3705 | /** | |
| 3706 | * @param {Promise<rustdoc.PlainResultObject|null>[]} data | |
| 3707 | * @returns {AsyncGenerator<rustdoc.ResultObject, boolean>} | |
| 3708 | */ | |
| 3709 | const flush = async function* (data) { | |
| 3710 | const satr = sortAndTransformResults( | |
| 3711 | await Promise.all(data), | |
| 3712 | null, | |
| 3713 | currentCrate, | |
| 3714 | pathDuplicates, | |
| 3715 | ); | |
| 3716 | data.length = 0; | |
| 3717 | for await (const processed of satr) { | |
| 3718 | yield processed; | |
| 3719 | count += 1; | |
| 3720 | if ((count & 0x7F) === 0) { | |
| 3721 | await yieldToBrowser(); | |
| 3722 | } | |
| 3723 | if (count >= MAX_RESULTS) { | |
| 3724 | return true; | |
| 4335 | 3725 | } |
| 4336 | 3726 | } |
| 4337 | } | |
| 4338 | } | |
| 4339 | ||
| 4340 | // @ts-expect-error | |
| 4341 | const sortFunc = (aaa, bbb) => { | |
| 4342 | if (aaa.original.path < bbb.original.path) { | |
| 4343 | return 1; | |
| 4344 | } else if (aaa.original.path === bbb.original.path) { | |
| 4345 | return 0; | |
| 4346 | } | |
| 4347 | return -1; | |
| 4348 | }; | |
| 4349 | // @ts-expect-error | |
| 4350 | crateAliases.sort(sortFunc); | |
| 4351 | aliases.sort(sortFunc); | |
| 4352 | ||
| 4353 | // @ts-expect-error | |
| 4354 | const pushFunc = alias => { | |
| 4355 | // Cloning `alias` to prevent its fields to be updated. | |
| 4356 | alias = {...alias}; | |
| 4357 | const res = buildHrefAndPath(alias); | |
| 4358 | alias.displayPath = pathSplitter(res[0]); | |
| 4359 | alias.fullPath = alias.displayPath + alias.name; | |
| 4360 | alias.href = res[1]; | |
| 4361 | ||
| 4362 | ret.others.unshift(alias); | |
| 4363 | if (ret.others.length > MAX_RESULTS) { | |
| 4364 | ret.others.pop(); | |
| 4365 | } | |
| 4366 | }; | |
| 4367 | ||
| 4368 | aliases.forEach(pushFunc); | |
| 4369 | // @ts-expect-error | |
| 4370 | crateAliases.forEach(pushFunc); | |
| 4371 | }; | |
| 4372 | ||
| 4373 | /** | |
| 4374 | * This function adds the given result into the provided `results` map if it matches the | |
| 4375 | * following condition: | |
| 4376 | * | |
| 4377 | * * If it is a "literal search" (`parsedQuery.literalSearch`), then `dist` must be 0. | |
| 4378 | * * If it is not a "literal search", `dist` must be <= `maxEditDistance`. | |
| 4379 | * | |
| 4380 | * The `results` map contains information which will be used to sort the search results: | |
| 4381 | * | |
| 4382 | * * `fullId` is an `integer`` used as the key of the object we use for the `results` map. | |
| 4383 | * * `id` is the index in the `searchIndex` array for this element. | |
| 4384 | * * `index` is an `integer`` used to sort by the position of the word in the item's name. | |
| 4385 | * * `dist` is the main metric used to sort the search results. | |
| 4386 | * * `path_dist` is zero if a single-component search query is used, otherwise it's the | |
| 4387 | * distance computed for everything other than the last path component. | |
| 4388 | * | |
| 4389 | * @param {rustdoc.Results} results | |
| 4390 | * @param {number} fullId | |
| 4391 | * @param {number} id | |
| 4392 | * @param {number} index | |
| 4393 | * @param {number} dist | |
| 4394 | * @param {number} path_dist | |
| 4395 | * @param {number} maxEditDistance | |
| 4396 | */ | |
| 4397 | function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) { | |
| 4398 | if (dist <= maxEditDistance || index !== -1) { | |
| 4399 | if (results.has(fullId)) { | |
| 4400 | const result = results.get(fullId); | |
| 4401 | if (result === undefined || result.dontValidate || result.dist <= dist) { | |
| 4402 | return; | |
| 3727 | return false; | |
| 3728 | }; | |
| 3729 | const aliasResults = await index.search(normalizedUserQuery); | |
| 3730 | if (aliasResults) { | |
| 3731 | for (const id of aliasResults.matches().entries()) { | |
| 3732 | const [name, alias] = await Promise.all([ | |
| 3733 | this.getName(id), | |
| 3734 | this.getAliasTarget(id), | |
| 3735 | ]); | |
| 3736 | if (name !== null && | |
| 3737 | alias !== null && | |
| 3738 | !idDuplicates.has(id) && | |
| 3739 | name.replace(/[_"]/g, "").toLowerCase() === normalizedUserQuery | |
| 3740 | ) { | |
| 3741 | prefixResults.push(handleAlias(name, alias, 0, 0)); | |
| 3742 | idDuplicates.add(id); | |
| 3743 | } | |
| 4403 | 3744 | } |
| 4404 | 3745 | } |
| 4405 | // @ts-expect-error | |
| 4406 | results.set(fullId, { | |
| 4407 | id: id, | |
| 4408 | index: index, | |
| 4409 | dontValidate: parsedQuery.literalSearch, | |
| 4410 | dist: dist, | |
| 4411 | path_dist: path_dist, | |
| 4412 | }); | |
| 4413 | } | |
| 4414 | } | |
| 4415 | ||
| 4416 | /** | |
| 4417 | * This function is called in case the query has more than one element. In this case, it'll | |
| 4418 | * try to match the items which validates all the elements. For `aa -> bb` will look for | |
| 4419 | * functions which have a parameter `aa` and has `bb` in its returned values. | |
| 4420 | * | |
| 4421 | * @param {rustdoc.Row} row | |
| 4422 | * @param {number} pos - Position in the `searchIndex`. | |
| 4423 | * @param {rustdoc.Results} results | |
| 4424 | */ | |
| 4425 | function handleArgs(row, pos, results) { | |
| 4426 | if (!row || (filterCrates !== null && row.crate !== filterCrates)) { | |
| 4427 | return; | |
| 4428 | } | |
| 4429 | const rowType = row.type; | |
| 4430 | if (!rowType) { | |
| 4431 | return; | |
| 4432 | } | |
| 4433 | ||
| 4434 | const tfpDist = compareTypeFingerprints( | |
| 4435 | row.id, | |
| 4436 | parsedQuery.typeFingerprint, | |
| 4437 | ); | |
| 4438 | if (tfpDist === null) { | |
| 4439 | return; | |
| 4440 | } | |
| 4441 | // @ts-expect-error | |
| 4442 | if (results.size >= MAX_RESULTS && tfpDist > results.max_dist) { | |
| 4443 | return; | |
| 4444 | } | |
| 4445 | ||
| 4446 | // If the result is too "bad", we return false and it ends this search. | |
| 4447 | if (!unifyFunctionTypes( | |
| 4448 | rowType.inputs, | |
| 4449 | parsedQuery.elems, | |
| 4450 | rowType.where_clause, | |
| 4451 | null, | |
| 4452 | // @ts-expect-error | |
| 4453 | mgens => { | |
| 4454 | return unifyFunctionTypes( | |
| 4455 | rowType.output, | |
| 4456 | parsedQuery.returned, | |
| 4457 | rowType.where_clause, | |
| 4458 | mgens, | |
| 4459 | checkTypeMgensForConflict, | |
| 4460 | 0, // unboxing depth | |
| 4461 | ); | |
| 4462 | }, | |
| 4463 | 0, // unboxing depth | |
| 4464 | )) { | |
| 4465 | return; | |
| 4466 | } | |
| 4467 | ||
| 4468 | results.max_dist = Math.max(results.max_dist || 0, tfpDist); | |
| 4469 | addIntoResults(results, row.id, pos, 0, tfpDist, 0, Number.MAX_VALUE); | |
| 4470 | } | |
| 4471 | ||
| 4472 | /** | |
| 4473 | * Compare the query fingerprint with the function fingerprint. | |
| 4474 | * | |
| 4475 | * @param {number} fullId - The function | |
| 4476 | * @param {Uint32Array} queryFingerprint - The query | |
| 4477 | * @returns {number|null} - Null if non-match, number if distance | |
| 4478 | * This function might return 0! | |
| 4479 | */ | |
| 4480 | const compareTypeFingerprints = (fullId, queryFingerprint) => { | |
| 4481 | const fh0 = this.functionTypeFingerprint[fullId * 4]; | |
| 4482 | const fh1 = this.functionTypeFingerprint[(fullId * 4) + 1]; | |
| 4483 | const fh2 = this.functionTypeFingerprint[(fullId * 4) + 2]; | |
| 4484 | const [qh0, qh1, qh2] = queryFingerprint; | |
| 4485 | // Approximate set intersection with bloom filters. | |
| 4486 | // This can be larger than reality, not smaller, because hashes have | |
| 4487 | // the property that if they've got the same value, they hash to the | |
| 4488 | // same thing. False positives exist, but not false negatives. | |
| 4489 | const [in0, in1, in2] = [fh0 & qh0, fh1 & qh1, fh2 & qh2]; | |
| 4490 | // Approximate the set of items in the query but not the function. | |
| 4491 | // This might be smaller than reality, but cannot be bigger. | |
| 4492 | // | |
| 4493 | // | in_ | qh_ | XOR | Meaning | | |
| 4494 | // | --- | --- | --- | ------------------------------------------------ | | |
| 4495 | // | 0 | 0 | 0 | Not present | | |
| 4496 | // | 1 | 0 | 1 | IMPOSSIBLE because `in_` is `fh_ & qh_` | | |
| 4497 | // | 1 | 1 | 0 | If one or both is false positive, false negative | | |
| 4498 | // | 0 | 1 | 1 | Since in_ has no false negatives, must be real | | |
| 4499 | if ((in0 ^ qh0) || (in1 ^ qh1) || (in2 ^ qh2)) { | |
| 4500 | return null; | |
| 4501 | } | |
| 4502 | return this.functionTypeFingerprint[(fullId * 4) + 3]; | |
| 4503 | }; | |
| 4504 | ||
| 4505 | ||
| 4506 | const innerRunQuery = () => { | |
| 4507 | if (parsedQuery.foundElems === 1 && !parsedQuery.hasReturnArrow) { | |
| 3746 | if (parsedQuery.error !== null || parsedQuery.elems.length === 0) { | |
| 3747 | yield* flush(prefixResults); | |
| 3748 | return; | |
| 3749 | } | |
| 4508 | 3750 | const elem = parsedQuery.elems[0]; |
| 4509 | // use arrow functions to preserve `this`. | |
| 4510 | /** @type {function(number): void} */ | |
| 4511 | const handleNameSearch = id => { | |
| 4512 | const row = this.searchIndex[id]; | |
| 4513 | if (!typePassesFilter(elem.typeFilter, row.ty) || | |
| 3751 | const typeFilter = itemTypeFromName(elem.typeFilter); | |
| 3752 | /** | |
| 3753 | * @param {number} id | |
| 3754 | * @returns {Promise<rustdoc.PlainResultObject?>} | |
| 3755 | */ | |
| 3756 | const handleNameSearch = async id => { | |
| 3757 | const row = await this.getRow(id); | |
| 3758 | if (!row || !row.entry) { | |
| 3759 | return null; | |
| 3760 | } | |
| 3761 | if (!typePassesFilter(typeFilter, row.ty) || | |
| 4514 | 3762 | (filterCrates !== null && row.crate !== filterCrates)) { |
| 4515 | return; | |
| 3763 | return null; | |
| 4516 | 3764 | } |
| 4517 | 3765 | |
| 3766 | /** @type {number|null} */ | |
| 4518 | 3767 | let pathDist = 0; |
| 4519 | 3768 | if (elem.fullPath.length > 1) { |
| 4520 | ||
| 4521 | const maybePathDist = checkPath(elem.pathWithoutLast, row); | |
| 4522 | if (maybePathDist === null) { | |
| 4523 | return; | |
| 3769 | pathDist = checkRowPath(elem.pathWithoutLast, row); | |
| 3770 | if (pathDist === null) { | |
| 3771 | return null; | |
| 4524 | 3772 | } |
| 4525 | pathDist = maybePathDist; | |
| 4526 | 3773 | } |
| 4527 | 3774 | |
| 4528 | 3775 | if (parsedQuery.literalSearch) { |
| 4529 | if (row.word === elem.pathLast) { | |
| 4530 | addIntoResults(results_others, row.id, id, 0, 0, pathDist, 0); | |
| 4531 | } | |
| 3776 | return row.name.toLowerCase() === elem.pathLast ? { | |
| 3777 | id, | |
| 3778 | dist: 0, | |
| 3779 | path_dist: 0, | |
| 3780 | index: 0, | |
| 3781 | elems: [], // only used in type-based queries | |
| 3782 | returned: [], // only used in type-based queries | |
| 3783 | is_alias: false, | |
| 3784 | } : null; | |
| 4532 | 3785 | } else { |
| 4533 | addIntoResults( | |
| 4534 | results_others, | |
| 4535 | row.id, | |
| 3786 | return { | |
| 4536 | 3787 | id, |
| 4537 | row.normalizedName.indexOf(elem.normalizedPathLast), | |
| 4538 | editDistance( | |
| 3788 | dist: editDistance( | |
| 4539 | 3789 | row.normalizedName, |
| 4540 | 3790 | elem.normalizedPathLast, |
| 4541 | 3791 | maxEditDistance, |
| 4542 | 3792 | ), |
| 4543 | pathDist, | |
| 4544 | maxEditDistance, | |
| 3793 | path_dist: pathDist, | |
| 3794 | index: row.normalizedName.indexOf(elem.normalizedPathLast), | |
| 3795 | elems: [], // only used in type-based queries | |
| 3796 | returned: [], // only used in type-based queries | |
| 3797 | is_alias: false, | |
| 3798 | }; | |
| 3799 | } | |
| 3800 | }; | |
| 3801 | if (elem.normalizedPathLast === "") { | |
| 3802 | // faster full-table scan for this specific case. | |
| 3803 | const nameData = this.database.getData("name"); | |
| 3804 | const l = nameData ? nameData.length : 0; | |
| 3805 | for (let id = 0; id < l; ++id) { | |
| 3806 | if (!idDuplicates.has(id)) { | |
| 3807 | idDuplicates.add(id); | |
| 3808 | prefixResults.push(handleNameSearch(id)); | |
| 3809 | } | |
| 3810 | if (yield* flush(prefixResults)) { | |
| 3811 | return; | |
| 3812 | } | |
| 3813 | } | |
| 3814 | return; | |
| 3815 | } | |
| 3816 | const results = await index.search(elem.normalizedPathLast); | |
| 3817 | if (results) { | |
| 3818 | for await (const result of results.prefixMatches()) { | |
| 3819 | for (const id of result.entries()) { | |
| 3820 | if (!idDuplicates.has(id)) { | |
| 3821 | idDuplicates.add(id); | |
| 3822 | prefixResults.push(handleNameSearch(id)); | |
| 3823 | const [name, alias] = await Promise.all([ | |
| 3824 | this.getName(id), | |
| 3825 | this.getAliasTarget(id), | |
| 3826 | ]); | |
| 3827 | if (name !== null && alias !== null) { | |
| 3828 | prefixResults.push(handleAlias(name, alias, 0, 0)); | |
| 3829 | } | |
| 3830 | } | |
| 3831 | } | |
| 3832 | if (yield* flush(prefixResults)) { | |
| 3833 | return; | |
| 3834 | } | |
| 3835 | } | |
| 3836 | if (yield* flush(prefixResults)) { | |
| 3837 | return; | |
| 3838 | } | |
| 3839 | } | |
| 3840 | const levSearchResults = index.searchLev(elem.normalizedPathLast); | |
| 3841 | const levResults = []; | |
| 3842 | for await (const levResult of levSearchResults) { | |
| 3843 | for (const id of levResult.matches().entries()) { | |
| 3844 | if (!idDuplicates.has(id)) { | |
| 3845 | idDuplicates.add(id); | |
| 3846 | levResults.push(handleNameSearch(id)); | |
| 3847 | const [name, alias] = await Promise.all([ | |
| 3848 | this.getName(id), | |
| 3849 | this.getAliasTarget(id), | |
| 3850 | ]); | |
| 3851 | if (name !== null && alias !== null) { | |
| 3852 | levResults.push(handleAlias( | |
| 3853 | name, | |
| 3854 | alias, | |
| 3855 | editDistance(elem.normalizedPathLast, name, maxEditDistance), | |
| 3856 | name.indexOf(elem.normalizedPathLast), | |
| 3857 | )); | |
| 3858 | } | |
| 3859 | } | |
| 3860 | } | |
| 3861 | } | |
| 3862 | yield* flush(levResults); | |
| 3863 | if (results) { | |
| 3864 | const substringResults = []; | |
| 3865 | for await (const result of results.substringMatches()) { | |
| 3866 | for (const id of result.entries()) { | |
| 3867 | if (!idDuplicates.has(id)) { | |
| 3868 | idDuplicates.add(id); | |
| 3869 | substringResults.push(handleNameSearch(id)); | |
| 3870 | const [name, alias] = await Promise.all([ | |
| 3871 | this.getName(id), | |
| 3872 | this.getAliasTarget(id), | |
| 3873 | ]); | |
| 3874 | if (name !== null && alias !== null) { | |
| 3875 | levResults.push(handleAlias( | |
| 3876 | name, | |
| 3877 | alias, | |
| 3878 | editDistance( | |
| 3879 | elem.normalizedPathLast, | |
| 3880 | name, | |
| 3881 | maxEditDistance, | |
| 3882 | ), | |
| 3883 | name.indexOf(elem.normalizedPathLast), | |
| 3884 | )); | |
| 3885 | } | |
| 3886 | } | |
| 3887 | } | |
| 3888 | if (yield* flush(substringResults)) { | |
| 3889 | return; | |
| 3890 | } | |
| 3891 | } | |
| 3892 | } | |
| 3893 | } | |
| 3894 | .bind(this); | |
| 3895 | ||
| 3896 | const innerRunTypeQuery = | |
| 3897 | /** | |
| 3898 | * @this {DocSearch} | |
| 3899 | * @param {rustdoc.ParserQueryElement[]} inputs | |
| 3900 | * @param {rustdoc.ParserQueryElement[]} output | |
| 3901 | * @param {"sig"|"elems"|"returned"|null} typeInfo | |
| 3902 | * @param {string} currentCrate | |
| 3903 | * @returns {AsyncGenerator<rustdoc.ResultObject>} | |
| 3904 | */ | |
| 3905 | async function*(inputs, output, typeInfo, currentCrate) { | |
| 3906 | const index = this.database.getIndex("normalizedName"); | |
| 3907 | if (!index) { | |
| 3908 | return; | |
| 3909 | } | |
| 3910 | /** @type {Map<string, number>} */ | |
| 3911 | const genericMap = new Map(); | |
| 3912 | /** | |
| 3913 | * @template Q | |
| 3914 | * @typedef {{ | |
| 3915 | * invertedIndex: stringdex.RoaringBitmap[], | |
| 3916 | * queryElem: Q, | |
| 3917 | * }} PostingsList | |
| 3918 | */ | |
| 3919 | /** @type {stringdex.RoaringBitmap[]} */ | |
| 3920 | const empty_inverted_index = []; | |
| 3921 | /** @type {PostingsList<any>[]} */ | |
| 3922 | const empty_postings_list = []; | |
| 3923 | /** @type {stringdex.RoaringBitmap[]} */ | |
| 3924 | const everything_inverted_index = []; | |
| 3925 | for (let i = 0; i < 64; ++i) { | |
| 3926 | everything_inverted_index.push(RoaringBitmap.everything()); | |
| 3927 | } | |
| 3928 | /** | |
| 3929 | * @type {PostingsList<rustdoc.QueryElement[]>} | |
| 3930 | */ | |
| 3931 | const everything_postings_list = { | |
| 3932 | invertedIndex: everything_inverted_index, | |
| 3933 | queryElem: [], | |
| 3934 | }; | |
| 3935 | /** | |
| 3936 | * @type {PostingsList<rustdoc.QueryElement[]>[]} | |
| 3937 | */ | |
| 3938 | const nested_everything_postings_list = [everything_postings_list]; | |
| 3939 | /** | |
| 3940 | * @param {...stringdex.RoaringBitmap[]} idx | |
| 3941 | * @returns {stringdex.RoaringBitmap[]} | |
| 3942 | */ | |
| 3943 | const intersectInvertedIndexes = (...idx) => { | |
| 3944 | let i = 0; | |
| 3945 | const l = idx.length; | |
| 3946 | while (i < l - 1 && idx[i] === everything_inverted_index) { | |
| 3947 | i += 1; | |
| 3948 | } | |
| 3949 | const result = [...idx[i]]; | |
| 3950 | for (; i < l; ++i) { | |
| 3951 | if (idx[i] === everything_inverted_index) { | |
| 3952 | continue; | |
| 3953 | } | |
| 3954 | if (idx[i].length < result.length) { | |
| 3955 | result.length = idx[i].length; | |
| 3956 | } | |
| 3957 | for (let j = 0; j < result.length; ++j) { | |
| 3958 | result[j] = result[j].intersection(idx[i][j]); | |
| 3959 | } | |
| 3960 | } | |
| 3961 | return result; | |
| 3962 | }; | |
| 3963 | /** | |
| 3964 | * Fetch a bitmap of potentially-matching functions, | |
| 3965 | * plus a list of query elements annotated with the correct IDs. | |
| 3966 | * | |
| 3967 | * More than one ID can exist because, for example, q=`Iter` can match | |
| 3968 | * `std::vec::Iter`, or `std::btree_set::Iter`, or anything else, and those | |
| 3969 | * items different IDs. What's worse, q=`Iter<Iter>` has N**2 possible | |
| 3970 | * matches, because it could be `vec::Iter<btree_set::Iter>`, | |
| 3971 | * `btree_set::Iter<vec::Iter>`, `vec::Iter<vec::Iter>`, | |
| 3972 | * `btree_set::Iter<btree_set::Iter>`, | |
| 3973 | * or anything else. This function returns all possible permutations. | |
| 3974 | * | |
| 3975 | * @param {rustdoc.ParserQueryElement|null} elem | |
| 3976 | * @returns {Promise<PostingsList<rustdoc.QueryElement>[]>} | |
| 3977 | */ | |
| 3978 | const unpackPostingsList = async elem => { | |
| 3979 | if (!elem) { | |
| 3980 | return empty_postings_list; | |
| 3981 | } | |
| 3982 | const typeFilter = itemTypeFromName(elem.typeFilter); | |
| 3983 | const searchResults = await index.search(elem.normalizedPathLast); | |
| 3984 | /** | |
| 3985 | * @type {Promise<[ | |
| 3986 | * number, | |
| 3987 | * string|null, | |
| 3988 | * rustdoc.TypeData|null, | |
| 3989 | * rustdoc.PathData|null, | |
| 3990 | * ]>[]} | |
| 3991 | * */ | |
| 3992 | const typePromises = []; | |
| 3993 | if (typeFilter !== TY_GENERIC && searchResults) { | |
| 3994 | for (const id of searchResults.matches().entries()) { | |
| 3995 | typePromises.push(Promise.all([ | |
| 3996 | this.getName(id), | |
| 3997 | this.getTypeData(id), | |
| 3998 | this.getPathData(id), | |
| 3999 | ]).then(([name, typeData, pathData]) => | |
| 4000 | [id, name, typeData, pathData])); | |
| 4001 | } | |
| 4002 | } | |
| 4003 | const types = (await Promise.all(typePromises)) | |
| 4004 | .filter(([_id, name, ty, path]) => | |
| 4005 | name !== null && name.toLowerCase() === elem.pathLast && | |
| 4006 | ty && !ty.invertedFunctionSignatureIndex.every(bitmap => { | |
| 4007 | return bitmap.isEmpty(); | |
| 4008 | }) && | |
| 4009 | path && path.ty !== TY_ASSOCTYPE && | |
| 4010 | (elem.pathWithoutLast.length === 0 || | |
| 4011 | checkPath( | |
| 4012 | elem.pathWithoutLast, | |
| 4013 | path.modulePath.split("::"), | |
| 4014 | ) === 0), | |
| 4015 | ); | |
| 4016 | if (types.length === 0) { | |
| 4017 | const areGenericsAllowed = typeFilter === TY_GENERIC || ( | |
| 4018 | typeFilter === -1 && | |
| 4019 | (parsedQuery.totalElems > 1 || parsedQuery.hasReturnArrow) && | |
| 4020 | elem.pathWithoutLast.length === 0 && | |
| 4021 | elem.generics.length === 0 && | |
| 4022 | elem.bindings.size === 0 | |
| 4545 | 4023 | ); |
| 4024 | if (typeFilter !== TY_GENERIC && | |
| 4025 | (elem.name.length >= 3 || !areGenericsAllowed) | |
| 4026 | ) { | |
| 4027 | /** @type {string|null} */ | |
| 4028 | let chosenName = null; | |
| 4029 | /** @type {rustdoc.TypeData[]} */ | |
| 4030 | let chosenType = []; | |
| 4031 | /** @type {rustdoc.PathData[]} */ | |
| 4032 | let chosenPath = []; | |
| 4033 | /** @type {number[]} */ | |
| 4034 | let chosenId = []; | |
| 4035 | let chosenDist = Number.MAX_SAFE_INTEGER; | |
| 4036 | const levResults = index.searchLev(elem.normalizedPathLast); | |
| 4037 | for await (const searchResults of levResults) { | |
| 4038 | for (const id of searchResults.matches().entries()) { | |
| 4039 | const [name, ty, path] = await Promise.all([ | |
| 4040 | this.getName(id), | |
| 4041 | this.getTypeData(id), | |
| 4042 | this.getPathData(id), | |
| 4043 | ]); | |
| 4044 | if (name !== null && ty !== null && path !== null && | |
| 4045 | !ty.invertedFunctionSignatureIndex.every(bitmap => { | |
| 4046 | return bitmap.isEmpty(); | |
| 4047 | }) && | |
| 4048 | path.ty !== TY_ASSOCTYPE | |
| 4049 | ) { | |
| 4050 | let dist = editDistance( | |
| 4051 | name, | |
| 4052 | elem.pathLast, | |
| 4053 | maxEditDistance, | |
| 4054 | ); | |
| 4055 | if (elem.pathWithoutLast.length !== 0) { | |
| 4056 | const pathDist = checkPath( | |
| 4057 | elem.pathWithoutLast, | |
| 4058 | path.modulePath.split("::"), | |
| 4059 | ); | |
| 4060 | // guaranteed to be higher than the path limit | |
| 4061 | dist += pathDist === null ? | |
| 4062 | Number.MAX_SAFE_INTEGER : | |
| 4063 | pathDist; | |
| 4064 | } | |
| 4065 | if (name === chosenName) { | |
| 4066 | chosenId.push(id); | |
| 4067 | chosenType.push(ty); | |
| 4068 | chosenPath.push(path); | |
| 4069 | } else if (dist < chosenDist) { | |
| 4070 | chosenName = name; | |
| 4071 | chosenId = [id]; | |
| 4072 | chosenType = [ty]; | |
| 4073 | chosenPath = [path]; | |
| 4074 | chosenDist = dist; | |
| 4075 | } | |
| 4076 | } | |
| 4077 | } | |
| 4078 | if (chosenId.length !== 0) { | |
| 4079 | // searchLev returns results in order | |
| 4080 | // if we have working matches, we're done | |
| 4081 | break; | |
| 4082 | } | |
| 4083 | } | |
| 4084 | if (areGenericsAllowed) { | |
| 4085 | parsedQuery.proposeCorrectionFrom = elem.name; | |
| 4086 | parsedQuery.proposeCorrectionTo = chosenName; | |
| 4087 | } else { | |
| 4088 | parsedQuery.correction = chosenName; | |
| 4089 | for (let i = 0; i < chosenType.length; ++i) { | |
| 4090 | types.push([ | |
| 4091 | chosenId[i], | |
| 4092 | chosenName, | |
| 4093 | chosenType[i], | |
| 4094 | chosenPath[i], | |
| 4095 | ]); | |
| 4096 | } | |
| 4097 | } | |
| 4098 | } | |
| 4099 | if (areGenericsAllowed) { | |
| 4100 | let genericId = genericMap.get(elem.normalizedPathLast); | |
| 4101 | if (genericId === undefined) { | |
| 4102 | genericId = genericMap.size; | |
| 4103 | genericMap.set(elem.normalizedPathLast, genericId); | |
| 4104 | } | |
| 4105 | return [{ | |
| 4106 | invertedIndex: await this.getGenericInvertedIndex(genericId), | |
| 4107 | queryElem: { | |
| 4108 | name: elem.name, | |
| 4109 | id: (-genericId) - 1, | |
| 4110 | typeFilter: TY_GENERIC, | |
| 4111 | generics: [], | |
| 4112 | bindings: EMPTY_BINDINGS_MAP, | |
| 4113 | fullPath: elem.fullPath, | |
| 4114 | pathLast: elem.pathLast, | |
| 4115 | normalizedPathLast: elem.normalizedPathLast, | |
| 4116 | pathWithoutLast: elem.pathWithoutLast, | |
| 4117 | }, | |
| 4118 | }]; | |
| 4119 | } | |
| 4120 | } | |
| 4121 | types.sort(([_i, name1, _t, pathData1], [_i2, name2, _t2, pathData2]) => { | |
| 4122 | const p1 = !pathData1 ? "" : pathData1.modulePath; | |
| 4123 | const p2 = !pathData2 ? "" : pathData2.modulePath; | |
| 4124 | const n1 = name1 === null ? "" : name1; | |
| 4125 | const n2 = name2 === null ? "" : name2; | |
| 4126 | if (p1.length !== p2.length) { | |
| 4127 | return p1.length > p2.length ? +1 : -1; | |
| 4128 | } | |
| 4129 | if (n1.length !== n2.length) { | |
| 4130 | return n1.length > n2.length ? +1 : -1; | |
| 4131 | } | |
| 4132 | if (n1 !== n2) { | |
| 4133 | return n1 > n2 ? +1 : -1; | |
| 4134 | } | |
| 4135 | if (p1 !== p2) { | |
| 4136 | return p1 > p2 ? +1 : -1; | |
| 4137 | } | |
| 4138 | return 0; | |
| 4139 | }); | |
| 4140 | /** @type {PostingsList<rustdoc.QueryElement>[]} */ | |
| 4141 | const results = []; | |
| 4142 | for (const [id, _name, typeData] of types) { | |
| 4143 | if (!typeData || typeData.invertedFunctionSignatureIndex.every(bitmap => { | |
| 4144 | return bitmap.isEmpty(); | |
| 4145 | })) { | |
| 4146 | continue; | |
| 4147 | } | |
| 4148 | const upla = await unpackPostingsListAll(elem.generics); | |
| 4149 | const uplb = await unpackPostingsListBindings(elem.bindings); | |
| 4150 | for (const {invertedIndex: genericsIdx, queryElem: generics} of upla) { | |
| 4151 | for (const {invertedIndex: bindingsIdx, queryElem: bindings} of uplb) { | |
| 4152 | results.push({ | |
| 4153 | invertedIndex: intersectInvertedIndexes( | |
| 4154 | typeData.invertedFunctionSignatureIndex, | |
| 4155 | genericsIdx, | |
| 4156 | bindingsIdx, | |
| 4157 | ), | |
| 4158 | queryElem: { | |
| 4159 | name: elem.name, | |
| 4160 | id, | |
| 4161 | typeFilter, | |
| 4162 | generics, | |
| 4163 | bindings, | |
| 4164 | fullPath: elem.fullPath, | |
| 4165 | pathLast: elem.pathLast, | |
| 4166 | normalizedPathLast: elem.normalizedPathLast, | |
| 4167 | pathWithoutLast: elem.pathWithoutLast, | |
| 4168 | }, | |
| 4169 | }); | |
| 4170 | if ((results.length & 0x7F) === 0) { | |
| 4171 | await yieldToBrowser(); | |
| 4172 | } | |
| 4173 | } | |
| 4174 | } | |
| 4175 | } | |
| 4176 | return results; | |
| 4177 | }; | |
| 4178 | /** | |
| 4179 | * Fetch all possible matching permutations of a list of query elements. | |
| 4180 | * | |
| 4181 | * The empty list returns an "identity postings list", with a bitmap that | |
| 4182 | * matches everything and an empty list of elems. This allows you to safely | |
| 4183 | * take the intersection of this bitmap. | |
| 4184 | * | |
| 4185 | * @param {(rustdoc.ParserQueryElement|null)[]|null} elems | |
| 4186 | * @returns {Promise<PostingsList<rustdoc.QueryElement[]>[]>} | |
| 4187 | */ | |
| 4188 | const unpackPostingsListAll = async elems => { | |
| 4189 | if (!elems || elems.length === 0) { | |
| 4190 | return nested_everything_postings_list; | |
| 4191 | } | |
| 4192 | const [firstPostingsList, remainingAll] = await Promise.all([ | |
| 4193 | unpackPostingsList(elems[0]), | |
| 4194 | unpackPostingsListAll(elems.slice(1)), | |
| 4195 | ]); | |
| 4196 | /** @type {PostingsList<rustdoc.QueryElement[]>[]} */ | |
| 4197 | const results = []; | |
| 4198 | for (const { | |
| 4199 | invertedIndex: firstIdx, | |
| 4200 | queryElem: firstElem, | |
| 4201 | } of firstPostingsList) { | |
| 4202 | for (const { | |
| 4203 | invertedIndex: remainingIdx, | |
| 4204 | queryElem: remainingElems, | |
| 4205 | } of remainingAll) { | |
| 4206 | results.push({ | |
| 4207 | invertedIndex: intersectInvertedIndexes(firstIdx, remainingIdx), | |
| 4208 | queryElem: [firstElem, ...remainingElems], | |
| 4209 | }); | |
| 4210 | if ((results.length & 0x7F) === 0) { | |
| 4211 | await yieldToBrowser(); | |
| 4212 | } | |
| 4213 | } | |
| 4214 | } | |
| 4215 | return results; | |
| 4216 | }; | |
| 4217 | /** | |
| 4218 | * Fetch all possible matching permutations of a map query element bindings. | |
| 4219 | * | |
| 4220 | * The empty list returns an "identity postings list", with a bitmap that | |
| 4221 | * matches everything and an empty list of elems. This allows you to safely | |
| 4222 | * take the intersection of this bitmap. | |
| 4223 | * | |
| 4224 | * Heads up! This function mutates the Map that you provide. | |
| 4225 | * Before passing an actual parser item to it, make sure to clone the map. | |
| 4226 | * | |
| 4227 | * @param {Map<string, rustdoc.ParserQueryElement[]>} elems | |
| 4228 | * @returns {Promise<PostingsList< | |
| 4229 | * Map<number, rustdoc.QueryElement[]>, | |
| 4230 | * >[]>} | |
| 4231 | */ | |
| 4232 | const unpackPostingsListBindings = async elems => { | |
| 4233 | if (!elems) { | |
| 4234 | return [{ | |
| 4235 | invertedIndex: everything_inverted_index, | |
| 4236 | queryElem: new Map(), | |
| 4237 | }]; | |
| 4238 | } | |
| 4239 | const firstKey = elems.keys().next().value; | |
| 4240 | if (firstKey === undefined) { | |
| 4241 | return [{ | |
| 4242 | invertedIndex: everything_inverted_index, | |
| 4243 | queryElem: new Map(), | |
| 4244 | }]; | |
| 4245 | } | |
| 4246 | const firstList = elems.get(firstKey); | |
| 4247 | if (firstList === undefined) { | |
| 4248 | return [{ | |
| 4249 | invertedIndex: everything_inverted_index, | |
| 4250 | queryElem: new Map(), | |
| 4251 | }]; | |
| 4252 | } | |
| 4253 | const firstKeyIds = await index.search(firstKey); | |
| 4254 | if (!firstKeyIds) { | |
| 4255 | // User specified a non-existent key. | |
| 4256 | return [{ | |
| 4257 | invertedIndex: empty_inverted_index, | |
| 4258 | queryElem: new Map(), | |
| 4259 | }]; | |
| 4260 | } | |
| 4261 | elems.delete(firstKey); | |
| 4262 | const [firstPostingsList, remainingAll] = await Promise.all([ | |
| 4263 | unpackPostingsListAll(firstList), | |
| 4264 | unpackPostingsListBindings(elems), | |
| 4265 | ]); | |
| 4266 | /** @type {PostingsList<Map<number, rustdoc.QueryElement[]>>[]} */ | |
| 4267 | const results = []; | |
| 4268 | for (const keyId of firstKeyIds.matches().entries()) { | |
| 4269 | for (const { | |
| 4270 | invertedIndex: firstIdx, | |
| 4271 | queryElem: firstElem, | |
| 4272 | } of firstPostingsList) { | |
| 4273 | for (const { | |
| 4274 | invertedIndex: remainingIdx, | |
| 4275 | queryElem: remainingElems, | |
| 4276 | } of remainingAll) { | |
| 4277 | const elems = new Map(remainingElems); | |
| 4278 | elems.set(keyId, firstElem); | |
| 4279 | results.push({ | |
| 4280 | invertedIndex: intersectInvertedIndexes(firstIdx, remainingIdx), | |
| 4281 | queryElem: elems, | |
| 4282 | }); | |
| 4283 | if ((results.length & 0x7F) === 0) { | |
| 4284 | await yieldToBrowser(); | |
| 4285 | } | |
| 4286 | } | |
| 4287 | } | |
| 4546 | 4288 | } |
| 4289 | elems.set(firstKey, firstList); | |
| 4290 | if (results.length === 0) { | |
| 4291 | // User specified a non-existent key. | |
| 4292 | return [{ | |
| 4293 | invertedIndex: empty_inverted_index, | |
| 4294 | queryElem: new Map(), | |
| 4295 | }]; | |
| 4296 | } | |
| 4297 | return results; | |
| 4547 | 4298 | }; |
| 4548 | if (elem.normalizedPathLast !== "") { | |
| 4549 | const last = elem.normalizedPathLast; | |
| 4550 | for (const id of this.nameTrie.search(last, this.tailTable)) { | |
| 4551 | handleNameSearch(id); | |
| 4299 | ||
| 4300 | // finally, we can do the actual unification loop | |
| 4301 | const [allInputs, allOutput] = await Promise.all([ | |
| 4302 | unpackPostingsListAll(inputs), | |
| 4303 | unpackPostingsListAll(output), | |
| 4304 | ]); | |
| 4305 | let checkCounter = 0; | |
| 4306 | /** | |
| 4307 | * Finally, we can perform an incremental search, sorted by the number of | |
| 4308 | * entries that match a given query. | |
| 4309 | * | |
| 4310 | * The outer list gives the number of elements. The inner one is separate | |
| 4311 | * for each distinct name resolution. | |
| 4312 | * | |
| 4313 | * @type {{ | |
| 4314 | * bitmap: stringdex.RoaringBitmap, | |
| 4315 | * inputs: rustdoc.QueryElement[], | |
| 4316 | * output: rustdoc.QueryElement[], | |
| 4317 | * }[][]} | |
| 4318 | */ | |
| 4319 | const queryPlan = []; | |
| 4320 | for (const {invertedIndex: inputsIdx, queryElem: inputs} of allInputs) { | |
| 4321 | for (const {invertedIndex: outputIdx, queryElem: output} of allOutput) { | |
| 4322 | const invertedIndex = intersectInvertedIndexes(inputsIdx, outputIdx); | |
| 4323 | for (const [size, bitmap] of invertedIndex.entries()) { | |
| 4324 | checkCounter += 1; | |
| 4325 | if ((checkCounter & 0x7F) === 0) { | |
| 4326 | await yieldToBrowser(); | |
| 4327 | } | |
| 4328 | if (!queryPlan[size]) { | |
| 4329 | queryPlan[size] = []; | |
| 4330 | } | |
| 4331 | queryPlan[size].push({ | |
| 4332 | bitmap, inputs, output, | |
| 4333 | }); | |
| 4334 | } | |
| 4552 | 4335 | } |
| 4553 | 4336 | } |
| 4554 | const length = this.searchIndex.length; | |
| 4555 | ||
| 4556 | for (let i = 0, nSearchIndex = length; i < nSearchIndex; ++i) { | |
| 4557 | // queries that end in :: bypass the trie | |
| 4558 | if (elem.normalizedPathLast === "") { | |
| 4559 | handleNameSearch(i); | |
| 4560 | } | |
| 4561 | const row = this.searchIndex[i]; | |
| 4562 | if (filterCrates !== null && row.crate !== filterCrates) { | |
| 4563 | continue; | |
| 4564 | } | |
| 4565 | const tfpDist = compareTypeFingerprints( | |
| 4566 | row.id, | |
| 4567 | parsedQuery.typeFingerprint, | |
| 4568 | ); | |
| 4569 | if (tfpDist !== null) { | |
| 4570 | const in_args = row.type && row.type.inputs | |
| 4571 | && checkIfInList(row.type.inputs, elem, row.type.where_clause, null, 0); | |
| 4572 | const returned = row.type && row.type.output | |
| 4573 | && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); | |
| 4574 | if (in_args) { | |
| 4575 | results_in_args.max_dist = Math.max( | |
| 4576 | results_in_args.max_dist || 0, | |
| 4577 | tfpDist, | |
| 4578 | ); | |
| 4579 | const maxDist = results_in_args.size < MAX_RESULTS ? | |
| 4580 | (tfpDist + 1) : | |
| 4581 | results_in_args.max_dist; | |
| 4582 | addIntoResults(results_in_args, row.id, i, -1, tfpDist, 0, maxDist); | |
| 4337 | const resultPromises = []; | |
| 4338 | const dedup = new Set(); | |
| 4339 | let resultCounter = 0; | |
| 4340 | const isReturnTypeQuery = inputs.length === 0; | |
| 4341 | /** @type {rustdoc.PlainResultObject[]} */ | |
| 4342 | const pushToBottom = []; | |
| 4343 | plan: for (const queryStep of queryPlan) { | |
| 4344 | for (const {bitmap, inputs, output} of queryStep) { | |
| 4345 | for (const id of bitmap.entries()) { | |
| 4346 | checkCounter += 1; | |
| 4347 | if ((checkCounter & 0x7F) === 0) { | |
| 4348 | await yieldToBrowser(); | |
| 4349 | } | |
| 4350 | resultPromises.push(this.getFunctionData(id).then(async fnData => { | |
| 4351 | if (!fnData || !fnData.functionSignature) { | |
| 4352 | return null; | |
| 4353 | } | |
| 4354 | checkCounter += 1; | |
| 4355 | if ((checkCounter & 0x7F) === 0) { | |
| 4356 | await yieldToBrowser(); | |
| 4357 | } | |
| 4358 | const functionSignature = fnData.functionSignature; | |
| 4359 | if (!unifyFunctionTypes( | |
| 4360 | functionSignature.inputs, | |
| 4361 | inputs, | |
| 4362 | functionSignature.where_clause, | |
| 4363 | null, | |
| 4364 | mgens => { | |
| 4365 | return !!unifyFunctionTypes( | |
| 4366 | functionSignature.output, | |
| 4367 | output, | |
| 4368 | functionSignature.where_clause, | |
| 4369 | mgens, | |
| 4370 | checkTypeMgensForConflict, | |
| 4371 | 0, // unboxing depth | |
| 4372 | ); | |
| 4373 | }, | |
| 4374 | 0, // unboxing depth | |
| 4375 | )) { | |
| 4376 | return null; | |
| 4377 | } | |
| 4378 | const result = { | |
| 4379 | id, | |
| 4380 | dist: fnData.elemCount, | |
| 4381 | path_dist: 0, | |
| 4382 | index: -1, | |
| 4383 | elems: inputs, | |
| 4384 | returned: output, | |
| 4385 | is_alias: false, | |
| 4386 | }; | |
| 4387 | const entry = await this.getEntryData(id); | |
| 4388 | if ((entry && !isFnLikeTy(entry.ty)) || | |
| 4389 | (isReturnTypeQuery && | |
| 4390 | functionSignature && | |
| 4391 | containsTypeFromQuery( | |
| 4392 | output, | |
| 4393 | functionSignature.inputs, | |
| 4394 | functionSignature.where_clause, | |
| 4395 | ) | |
| 4396 | ) | |
| 4397 | ) { | |
| 4398 | pushToBottom.push(result); | |
| 4399 | return null; | |
| 4400 | } | |
| 4401 | return result; | |
| 4402 | })); | |
| 4583 | 4403 | } |
| 4584 | if (returned) { | |
| 4585 | results_returned.max_dist = Math.max( | |
| 4586 | results_returned.max_dist || 0, | |
| 4587 | tfpDist, | |
| 4588 | ); | |
| 4589 | const maxDist = results_returned.size < MAX_RESULTS ? | |
| 4590 | (tfpDist + 1) : | |
| 4591 | results_returned.max_dist; | |
| 4592 | addIntoResults(results_returned, row.id, i, -1, tfpDist, 0, maxDist); | |
| 4404 | } | |
| 4405 | for await (const result of sortAndTransformResults( | |
| 4406 | await Promise.all(resultPromises), | |
| 4407 | typeInfo, | |
| 4408 | currentCrate, | |
| 4409 | dedup, | |
| 4410 | )) { | |
| 4411 | if (resultCounter >= MAX_RESULTS) { | |
| 4412 | break plan; | |
| 4593 | 4413 | } |
| 4414 | yield result; | |
| 4415 | resultCounter += 1; | |
| 4594 | 4416 | } |
| 4417 | resultPromises.length = 0; | |
| 4595 | 4418 | } |
| 4596 | } else if (parsedQuery.foundElems > 0) { | |
| 4597 | // Sort input and output so that generic type variables go first and | |
| 4598 | // types with generic parameters go last. | |
| 4599 | // That's because of the way unification is structured: it eats off | |
| 4600 | // the end, and hits a fast path if the last item is a simple atom. | |
| 4601 | /** @type {function(rustdoc.QueryElement, rustdoc.QueryElement): number} */ | |
| 4602 | const sortQ = (a, b) => { | |
| 4603 | const ag = a.generics.length === 0 && a.bindings.size === 0; | |
| 4604 | const bg = b.generics.length === 0 && b.bindings.size === 0; | |
| 4605 | if (ag !== bg) { | |
| 4606 | // unary `+` converts booleans into integers. | |
| 4607 | return +ag - +bg; | |
| 4419 | if (resultCounter >= MAX_RESULTS) { | |
| 4420 | return; | |
| 4421 | } | |
| 4422 | for await (const result of sortAndTransformResults( | |
| 4423 | await Promise.all(pushToBottom), | |
| 4424 | typeInfo, | |
| 4425 | currentCrate, | |
| 4426 | dedup, | |
| 4427 | )) { | |
| 4428 | if (resultCounter >= MAX_RESULTS) { | |
| 4429 | break; | |
| 4608 | 4430 | } |
| 4609 | const ai = a.id !== null && a.id > 0; | |
| 4610 | const bi = b.id !== null && b.id > 0; | |
| 4611 | return +ai - +bi; | |
| 4612 | }; | |
| 4613 | parsedQuery.elems.sort(sortQ); | |
| 4614 | parsedQuery.returned.sort(sortQ); | |
| 4615 | for (let i = 0, nSearchIndex = this.searchIndex.length; i < nSearchIndex; ++i) { | |
| 4616 | handleArgs(this.searchIndex[i], i, results_others); | |
| 4431 | yield result; | |
| 4432 | resultCounter += 1; | |
| 4617 | 4433 | } |
| 4618 | 4434 | } |
| 4619 | }; | |
| 4620 | ||
| 4621 | if (parsedQuery.error === null) { | |
| 4622 | innerRunQuery(); | |
| 4623 | } | |
| 4624 | ||
| 4625 | const isType = parsedQuery.foundElems !== 1 || parsedQuery.hasReturnArrow; | |
| 4626 | const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ | |
| 4627 | sortResults(results_in_args, "elems", currentCrate), | |
| 4628 | sortResults(results_returned, "returned", currentCrate), | |
| 4629 | // @ts-expect-error | |
| 4630 | sortResults(results_others, (isType ? "query" : null), currentCrate), | |
| 4631 | ]); | |
| 4632 | const ret = createQueryResults( | |
| 4633 | sorted_in_args, | |
| 4634 | sorted_returned, | |
| 4635 | sorted_others, | |
| 4636 | parsedQuery); | |
| 4637 | await handleAliases(ret, parsedQuery.userQuery.replace(/"/g, ""), | |
| 4638 | filterCrates, currentCrate); | |
| 4639 | await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { | |
| 4640 | const descs = await Promise.all(list.map(result => { | |
| 4641 | // @ts-expect-error | |
| 4642 | return this.searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? | |
| 4643 | "" : | |
| 4644 | // @ts-expect-error | |
| 4645 | this.searchState.loadDesc(result); | |
| 4646 | })); | |
| 4647 | for (const [i, result] of list.entries()) { | |
| 4648 | // @ts-expect-error | |
| 4649 | result.desc = descs[i]; | |
| 4650 | } | |
| 4651 | })); | |
| 4652 | if (parsedQuery.error !== null && ret.others.length !== 0) { | |
| 4653 | // It means some doc aliases were found so let's "remove" the error! | |
| 4654 | ret.query.error = null; | |
| 4435 | .bind(this); | |
| 4436 | ||
| 4437 | if (parsedQuery.foundElems === 1 && !parsedQuery.hasReturnArrow) { | |
| 4438 | // We never want the main tab to delay behind the other two tabs. | |
| 4439 | // This is a bit of a hack (because JS's scheduler doesn't have much of an API), | |
| 4440 | // along with making innerRunTypeQuery yield to the UI thread. | |
| 4441 | const { | |
| 4442 | promise: donePromise, | |
| 4443 | resolve: doneResolve, | |
| 4444 | reject: doneReject, | |
| 4445 | } = Promise.withResolvers(); | |
| 4446 | const doneTimeout = timeout(250); | |
| 4447 | return { | |
| 4448 | "in_args": (async function*() { | |
| 4449 | await Promise.race([donePromise, doneTimeout]); | |
| 4450 | yield* innerRunTypeQuery(parsedQuery.elems, [], "elems", currentCrate); | |
| 4451 | })(), | |
| 4452 | "returned": (async function*() { | |
| 4453 | await Promise.race([donePromise, doneTimeout]); | |
| 4454 | yield* innerRunTypeQuery([], parsedQuery.elems, "returned", currentCrate); | |
| 4455 | })(), | |
| 4456 | "others": (async function*() { | |
| 4457 | try { | |
| 4458 | yield* innerRunNameQuery(currentCrate); | |
| 4459 | doneResolve(null); | |
| 4460 | } catch (e) { | |
| 4461 | doneReject(e); | |
| 4462 | throw e; | |
| 4463 | } | |
| 4464 | })(), | |
| 4465 | "query": parsedQuery, | |
| 4466 | }; | |
| 4467 | } else if (parsedQuery.error !== null) { | |
| 4468 | return { | |
| 4469 | "in_args": (async function*() {})(), | |
| 4470 | "returned": (async function*() {})(), | |
| 4471 | "others": innerRunNameQuery(currentCrate), | |
| 4472 | "query": parsedQuery, | |
| 4473 | }; | |
| 4474 | } else { | |
| 4475 | const typeInfo = parsedQuery.elems.length === 0 ? | |
| 4476 | "returned" : ( | |
| 4477 | parsedQuery.returned.length === 0 ? "elems" : "sig" | |
| 4478 | ); | |
| 4479 | return { | |
| 4480 | "in_args": (async function*() {})(), | |
| 4481 | "returned": (async function*() {})(), | |
| 4482 | "others": parsedQuery.foundElems === 0 ? | |
| 4483 | (async function*() {})() : | |
| 4484 | innerRunTypeQuery( | |
| 4485 | parsedQuery.elems, | |
| 4486 | parsedQuery.returned, | |
| 4487 | typeInfo, | |
| 4488 | currentCrate, | |
| 4489 | ), | |
| 4490 | "query": parsedQuery, | |
| 4491 | }; | |
| 4655 | 4492 | } |
| 4656 | return ret; | |
| 4657 | 4493 | } |
| 4658 | 4494 | } |
| 4659 | 4495 | |
| 4660 | 4496 | |
| 4661 | 4497 | // ==================== Core search logic end ==================== |
| 4662 | 4498 | |
| 4663 | /** @type {Map<string, rustdoc.RawSearchIndexCrate>} */ | |
| 4664 | let rawSearchIndex; | |
| 4665 | // @ts-expect-error | |
| 4499 | /** @type {DocSearch} */ | |
| 4666 | 4500 | let docSearch; |
| 4667 | 4501 | const longItemTypes = [ |
| 4668 | 4502 | "keyword", |
| ... | ... | @@ -4762,12 +4596,8 @@ function buildUrl(search, filterCrates) { |
| 4762 | 4596 | function getFilterCrates() { |
| 4763 | 4597 | const elem = document.getElementById("crate-search"); |
| 4764 | 4598 | |
| 4765 | if (elem && | |
| 4766 | // @ts-expect-error | |
| 4767 | elem.value !== "all crates" && | |
| 4768 | // @ts-expect-error | |
| 4769 | window.searchIndex.has(elem.value) | |
| 4770 | ) { | |
| 4599 | // @ts-expect-error | |
| 4600 | if (elem && elem.value !== "all crates") { | |
| 4771 | 4601 | // @ts-expect-error |
| 4772 | 4602 | return elem.value; |
| 4773 | 4603 | } |
| ... | ... | @@ -4777,8 +4607,7 @@ function getFilterCrates() { |
| 4777 | 4607 | // @ts-expect-error |
| 4778 | 4608 | function nextTab(direction) { |
| 4779 | 4609 | const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length; |
| 4780 | // @ts-expect-error | |
| 4781 | searchState.focusedByTab[searchState.currentTab] = document.activeElement; | |
| 4610 | window.searchState.focusedByTab[searchState.currentTab] = document.activeElement; | |
| 4782 | 4611 | printTab(next); |
| 4783 | 4612 | focusSearchResult(); |
| 4784 | 4613 | } |
| ... | ... | @@ -4790,133 +4619,182 @@ function focusSearchResult() { |
| 4790 | 4619 | document.querySelectorAll(".search-results.active a").item(0) || |
| 4791 | 4620 | document.querySelectorAll("#search-tabs button").item(searchState.currentTab); |
| 4792 | 4621 | searchState.focusedByTab[searchState.currentTab] = null; |
| 4793 | if (target) { | |
| 4794 | // @ts-expect-error | |
| 4622 | if (target && target instanceof HTMLElement) { | |
| 4795 | 4623 | target.focus(); |
| 4796 | 4624 | } |
| 4797 | 4625 | } |
| 4798 | 4626 | |
| 4799 | 4627 | /** |
| 4800 | 4628 | * Render a set of search results for a single tab. |
| 4801 | * @param {Array<?>} array - The search results for this tab | |
| 4802 | * @param {rustdoc.ParsedQuery<rustdoc.QueryElement>} query | |
| 4629 | * @param {AsyncGenerator<rustdoc.ResultObject>} results - The search results for this tab | |
| 4630 | * @param {rustdoc.ParsedQuery<rustdoc.ParserQueryElement>} query | |
| 4803 | 4631 | * @param {boolean} display - True if this is the active tab |
| 4632 | * @param {function(number, HTMLElement): any} finishedCallback | |
| 4633 | * @param {boolean} isTypeSearch | |
| 4634 | * @returns {Promise<HTMLElement>} | |
| 4804 | 4635 | */ |
| 4805 | async function addTab(array, query, display) { | |
| 4636 | async function addTab(results, query, display, finishedCallback, isTypeSearch) { | |
| 4806 | 4637 | const extraClass = display ? " active" : ""; |
| 4807 | 4638 | |
| 4808 | const output = document.createElement( | |
| 4809 | array.length === 0 && query.error === null ? "div" : "ul", | |
| 4810 | ); | |
| 4811 | if (array.length > 0) { | |
| 4812 | output.className = "search-results " + extraClass; | |
| 4639 | /** @type {HTMLElement} */ | |
| 4640 | let output = document.createElement("ul"); | |
| 4641 | output.className = "search-results " + extraClass; | |
| 4813 | 4642 | |
| 4814 | const lis = Promise.all(array.map(async item => { | |
| 4815 | const name = item.is_alias ? item.original.name : item.name; | |
| 4816 | const type = itemTypes[item.ty]; | |
| 4817 | const longType = longItemTypes[item.ty]; | |
| 4818 | const typeName = longType.length !== 0 ? `${longType}` : "?"; | |
| 4643 | let count = 0; | |
| 4819 | 4644 | |
| 4820 | const link = document.createElement("a"); | |
| 4821 | link.className = "result-" + type; | |
| 4822 | link.href = item.href; | |
| 4645 | /** @type {Promise<string|null>[]} */ | |
| 4646 | const descList = []; | |
| 4823 | 4647 | |
| 4824 | const resultName = document.createElement("span"); | |
| 4825 | resultName.className = "result-name"; | |
| 4648 | /** @param {rustdoc.ResultObject} obj */ | |
| 4649 | const addNextResultToOutput = async obj => { | |
| 4650 | count += 1; | |
| 4826 | 4651 | |
| 4827 | resultName.insertAdjacentHTML( | |
| 4828 | "beforeend", | |
| 4829 | `<span class="typename">${typeName}</span>`); | |
| 4830 | link.appendChild(resultName); | |
| 4652 | const name = obj.item.name; | |
| 4653 | const type = itemTypes[obj.item.ty]; | |
| 4654 | const longType = longItemTypes[obj.item.ty]; | |
| 4655 | const typeName = longType.length !== 0 ? `${longType}` : "?"; | |
| 4831 | 4656 | |
| 4832 | let alias = " "; | |
| 4833 | if (item.is_alias) { | |
| 4834 | alias = ` <div class="alias">\ | |
| 4835 | <b>${item.name}</b><i class="grey">&nbsp;- see&nbsp;</i>\ | |
| 4657 | const link = document.createElement("a"); | |
| 4658 | link.className = "result-" + type; | |
| 4659 | link.href = obj.href; | |
| 4660 | ||
| 4661 | const resultName = document.createElement("span"); | |
| 4662 | resultName.className = "result-name"; | |
| 4663 | ||
| 4664 | resultName.insertAdjacentHTML( | |
| 4665 | "beforeend", | |
| 4666 | `<span class="typename">${typeName}</span>`); | |
| 4667 | link.appendChild(resultName); | |
| 4668 | ||
| 4669 | let alias = " "; | |
| 4670 | if (obj.alias !== undefined) { | |
| 4671 | alias = ` <div class="alias">\ | |
| 4672 | <b>${obj.alias}</b><i class="grey">&nbsp;- see&nbsp;</i>\ | |
| 4836 | 4673 | </div>`; |
| 4837 | } | |
| 4838 | resultName.insertAdjacentHTML( | |
| 4839 | "beforeend", | |
| 4840 | `<div class="path">${alias}\ | |
| 4841 | ${item.displayPath}<span class="${type}">${name}</span>\ | |
| 4674 | } | |
| 4675 | resultName.insertAdjacentHTML( | |
| 4676 | "beforeend", | |
| 4677 | `<div class="path">${alias}\ | |
| 4678 | ${obj.displayPath}<span class="${type}">${name}</span>\ | |
| 4842 | 4679 | </div>`); |
| 4843 | 4680 | |
| 4844 | const description = document.createElement("div"); | |
| 4845 | description.className = "desc"; | |
| 4846 | description.insertAdjacentHTML("beforeend", item.desc); | |
| 4847 | if (item.displayTypeSignature) { | |
| 4848 | const {type, mappedNames, whereClause} = await item.displayTypeSignature; | |
| 4849 | const displayType = document.createElement("div"); | |
| 4850 | // @ts-expect-error | |
| 4851 | type.forEach((value, index) => { | |
| 4852 | if (index % 2 !== 0) { | |
| 4853 | const highlight = document.createElement("strong"); | |
| 4854 | highlight.appendChild(document.createTextNode(value)); | |
| 4855 | displayType.appendChild(highlight); | |
| 4856 | } else { | |
| 4857 | displayType.appendChild(document.createTextNode(value)); | |
| 4681 | const description = document.createElement("div"); | |
| 4682 | description.className = "desc"; | |
| 4683 | obj.desc.then(desc => { | |
| 4684 | if (desc !== null) { | |
| 4685 | description.insertAdjacentHTML("beforeend", desc); | |
| 4686 | } | |
| 4687 | }); | |
| 4688 | descList.push(obj.desc); | |
| 4689 | if (obj.displayTypeSignature) { | |
| 4690 | const {type, mappedNames, whereClause} = await obj.displayTypeSignature; | |
| 4691 | const displayType = document.createElement("div"); | |
| 4692 | type.forEach((value, index) => { | |
| 4693 | if (index % 2 !== 0) { | |
| 4694 | const highlight = document.createElement("strong"); | |
| 4695 | highlight.appendChild(document.createTextNode(value)); | |
| 4696 | displayType.appendChild(highlight); | |
| 4697 | } else { | |
| 4698 | displayType.appendChild(document.createTextNode(value)); | |
| 4699 | } | |
| 4700 | }); | |
| 4701 | if (mappedNames.size > 0 || whereClause.size > 0) { | |
| 4702 | let addWhereLineFn = () => { | |
| 4703 | const line = document.createElement("div"); | |
| 4704 | line.className = "where"; | |
| 4705 | line.appendChild(document.createTextNode("where")); | |
| 4706 | displayType.appendChild(line); | |
| 4707 | addWhereLineFn = () => {}; | |
| 4708 | }; | |
| 4709 | for (const [qname, name] of mappedNames) { | |
| 4710 | // don't care unless the generic name is different | |
| 4711 | if (name === qname) { | |
| 4712 | continue; | |
| 4858 | 4713 | } |
| 4859 | }); | |
| 4860 | if (mappedNames.size > 0 || whereClause.size > 0) { | |
| 4861 | let addWhereLineFn = () => { | |
| 4862 | const line = document.createElement("div"); | |
| 4863 | line.className = "where"; | |
| 4864 | line.appendChild(document.createTextNode("where")); | |
| 4865 | displayType.appendChild(line); | |
| 4866 | addWhereLineFn = () => {}; | |
| 4867 | }; | |
| 4868 | for (const [qname, name] of mappedNames) { | |
| 4869 | // don't care unless the generic name is different | |
| 4870 | if (name === qname) { | |
| 4871 | continue; | |
| 4872 | } | |
| 4873 | addWhereLineFn(); | |
| 4874 | const line = document.createElement("div"); | |
| 4875 | line.className = "where"; | |
| 4876 | line.appendChild(document.createTextNode(` ${qname} matches `)); | |
| 4877 | const lineStrong = document.createElement("strong"); | |
| 4878 | lineStrong.appendChild(document.createTextNode(name)); | |
| 4879 | line.appendChild(lineStrong); | |
| 4880 | displayType.appendChild(line); | |
| 4714 | addWhereLineFn(); | |
| 4715 | const line = document.createElement("div"); | |
| 4716 | line.className = "where"; | |
| 4717 | line.appendChild(document.createTextNode(` ${qname} matches `)); | |
| 4718 | const lineStrong = document.createElement("strong"); | |
| 4719 | lineStrong.appendChild(document.createTextNode(name)); | |
| 4720 | line.appendChild(lineStrong); | |
| 4721 | displayType.appendChild(line); | |
| 4722 | } | |
| 4723 | for (const [name, innerType] of whereClause) { | |
| 4724 | // don't care unless there's at least one highlighted entry | |
| 4725 | if (innerType.length <= 1) { | |
| 4726 | continue; | |
| 4881 | 4727 | } |
| 4882 | for (const [name, innerType] of whereClause) { | |
| 4883 | // don't care unless there's at least one highlighted entry | |
| 4884 | if (innerType.length <= 1) { | |
| 4885 | continue; | |
| 4728 | addWhereLineFn(); | |
| 4729 | const line = document.createElement("div"); | |
| 4730 | line.className = "where"; | |
| 4731 | line.appendChild(document.createTextNode(` ${name}: `)); | |
| 4732 | innerType.forEach((value, index) => { | |
| 4733 | if (index % 2 !== 0) { | |
| 4734 | const highlight = document.createElement("strong"); | |
| 4735 | highlight.appendChild(document.createTextNode(value)); | |
| 4736 | line.appendChild(highlight); | |
| 4737 | } else { | |
| 4738 | line.appendChild(document.createTextNode(value)); | |
| 4886 | 4739 | } |
| 4887 | addWhereLineFn(); | |
| 4888 | const line = document.createElement("div"); | |
| 4889 | line.className = "where"; | |
| 4890 | line.appendChild(document.createTextNode(` ${name}: `)); | |
| 4891 | // @ts-expect-error | |
| 4892 | innerType.forEach((value, index) => { | |
| 4893 | if (index % 2 !== 0) { | |
| 4894 | const highlight = document.createElement("strong"); | |
| 4895 | highlight.appendChild(document.createTextNode(value)); | |
| 4896 | line.appendChild(highlight); | |
| 4897 | } else { | |
| 4898 | line.appendChild(document.createTextNode(value)); | |
| 4899 | } | |
| 4900 | }); | |
| 4901 | displayType.appendChild(line); | |
| 4902 | } | |
| 4740 | }); | |
| 4741 | displayType.appendChild(line); | |
| 4903 | 4742 | } |
| 4904 | displayType.className = "type-signature"; | |
| 4905 | link.appendChild(displayType); | |
| 4906 | 4743 | } |
| 4744 | displayType.className = "type-signature"; | |
| 4745 | link.appendChild(displayType); | |
| 4746 | } | |
| 4747 | ||
| 4748 | link.appendChild(description); | |
| 4749 | output.appendChild(link); | |
| 4907 | 4750 | |
| 4908 | link.appendChild(description); | |
| 4909 | return link; | |
| 4910 | })); | |
| 4911 | lis.then(lis => { | |
| 4912 | for (const li of lis) { | |
| 4913 | output.appendChild(li); | |
| 4751 | results.next().then(async nextResult => { | |
| 4752 | if (nextResult.value) { | |
| 4753 | addNextResultToOutput(nextResult.value); | |
| 4754 | } else { | |
| 4755 | await Promise.all(descList); | |
| 4756 | // need to make sure the element is shown before | |
| 4757 | // running this callback | |
| 4758 | yieldToBrowser().then(() => finishedCallback(count, output)); | |
| 4914 | 4759 | } |
| 4915 | 4760 | }); |
| 4916 | } else if (query.error === null) { | |
| 4917 | const dlroChannel = `https://doc.rust-lang.org/${getVar("channel")}`; | |
| 4761 | }; | |
| 4762 | const firstResult = await results.next(); | |
| 4763 | let correctionOutput = ""; | |
| 4764 | if (query.correction !== null && isTypeSearch) { | |
| 4765 | const orig = query.returned.length > 0 | |
| 4766 | ? query.returned[0].name | |
| 4767 | : query.elems[0].name; | |
| 4768 | correctionOutput = "<h3 class=\"search-corrections\">" + | |
| 4769 | `Type "${orig}" not found. ` + | |
| 4770 | "Showing results for closest type name " + | |
| 4771 | `"${query.correction}" instead.</h3>`; | |
| 4772 | } | |
| 4773 | if (query.proposeCorrectionFrom !== null && isTypeSearch) { | |
| 4774 | const orig = query.proposeCorrectionFrom; | |
| 4775 | const targ = query.proposeCorrectionTo; | |
| 4776 | correctionOutput = "<h3 class=\"search-corrections\">" + | |
| 4777 | `Type "${orig}" not found and used as generic parameter. ` + | |
| 4778 | `Consider searching for "${targ}" instead.</h3>`; | |
| 4779 | } | |
| 4780 | if (firstResult.value) { | |
| 4781 | if (correctionOutput !== "") { | |
| 4782 | const h3 = document.createElement("h3"); | |
| 4783 | h3.innerHTML = correctionOutput; | |
| 4784 | output.appendChild(h3); | |
| 4785 | } | |
| 4786 | await addNextResultToOutput(firstResult.value); | |
| 4787 | } else { | |
| 4788 | output = document.createElement("div"); | |
| 4789 | if (correctionOutput !== "") { | |
| 4790 | const h3 = document.createElement("h3"); | |
| 4791 | h3.innerHTML = correctionOutput; | |
| 4792 | output.appendChild(h3); | |
| 4793 | } | |
| 4918 | 4794 | output.className = "search-failed" + extraClass; |
| 4919 | output.innerHTML = "No results :(<br/>" + | |
| 4795 | const dlroChannel = `https://doc.rust-lang.org/${getVar("channel")}`; | |
| 4796 | if (query.userQuery !== "") { | |
| 4797 | output.innerHTML += "No results :(<br/>" + | |
| 4920 | 4798 | "Try on <a href=\"https://duckduckgo.com/?q=" + |
| 4921 | 4799 | encodeURIComponent("rust " + query.userQuery) + |
| 4922 | 4800 | "\">DuckDuckGo</a>?<br/><br/>" + |
| ... | ... | @@ -4929,192 +4807,198 @@ ${item.displayPath}<span class="${type}">${name}</span>\ |
| 4929 | 4807 | "introductions to language features and the language itself.</li><li><a " + |
| 4930 | 4808 | "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" + |
| 4931 | 4809 | " <a href=\"https://crates.io/\">crates.io</a>.</li></ul>"; |
| 4810 | } | |
| 4811 | output.innerHTML += "Example searches:<ul>" + | |
| 4812 | "<li><a href=\"" + getNakedUrl() + "?search=std::vec\">std::vec</a></li>" + | |
| 4813 | "<li><a href=\"" + getNakedUrl() + "?search=u32+->+bool\">u32 -> bool</a></li>" + | |
| 4814 | "<li><a href=\"" + getNakedUrl() + "?search=Option<T>,+(T+->+U)+->+Option<U>\">" + | |
| 4815 | "Option&lt;T>, (T -> U) -> Option&lt;U></a></li>" + | |
| 4816 | "</ul>"; | |
| 4817 | // need to make sure the element is shown before | |
| 4818 | // running this callback | |
| 4819 | yieldToBrowser().then(() => finishedCallback(0, output)); | |
| 4932 | 4820 | } |
| 4933 | 4821 | return output; |
| 4934 | 4822 | } |
| 4935 | 4823 | |
| 4936 | // @ts-expect-error | |
| 4937 | function makeTabHeader(tabNb, text, nbElems) { | |
| 4938 | // https://blog.horizon-eda.org/misc/2020/02/19/ui.html | |
| 4939 | // | |
| 4940 | // CSS runs with `font-variant-numeric: tabular-nums` to ensure all | |
| 4941 | // digits are the same width. \u{2007} is a Unicode space character | |
| 4942 | // that is defined to be the same width as a digit. | |
| 4943 | const fmtNbElems = | |
| 4944 | nbElems < 10 ? `\u{2007}(${nbElems})\u{2007}\u{2007}` : | |
| 4945 | nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` : `\u{2007}(${nbElems})`; | |
| 4946 | if (searchState.currentTab === tabNb) { | |
| 4947 | return "<button class=\"selected\">" + text + | |
| 4948 | "<span class=\"count\">" + fmtNbElems + "</span></button>"; | |
| 4949 | } | |
| 4950 | return "<button>" + text + "<span class=\"count\">" + fmtNbElems + "</span></button>"; | |
| 4824 | /** | |
| 4825 | * returns [tab, output] | |
| 4826 | * @param {number} tabNb | |
| 4827 | * @param {string} text | |
| 4828 | * @param {AsyncGenerator<rustdoc.ResultObject>} results | |
| 4829 | * @param {rustdoc.ParsedQuery<rustdoc.ParserQueryElement>} query | |
| 4830 | * @param {boolean} isTypeSearch | |
| 4831 | * @param {boolean} goToFirst | |
| 4832 | * @returns {[HTMLElement, Promise<HTMLElement>]} | |
| 4833 | */ | |
| 4834 | function makeTab(tabNb, text, results, query, isTypeSearch, goToFirst) { | |
| 4835 | const isCurrentTab = window.searchState.currentTab === tabNb; | |
| 4836 | const tabButton = document.createElement("button"); | |
| 4837 | tabButton.appendChild(document.createTextNode(text)); | |
| 4838 | tabButton.className = isCurrentTab ? "selected" : ""; | |
| 4839 | const tabCount = document.createElement("span"); | |
| 4840 | tabCount.className = "count loading"; | |
| 4841 | tabCount.innerHTML = "\u{2007}(\u{2007})\u{2007}\u{2007}"; | |
| 4842 | tabButton.appendChild(tabCount); | |
| 4843 | return [ | |
| 4844 | tabButton, | |
| 4845 | addTab(results, query, isCurrentTab, (count, output) => { | |
| 4846 | const search = window.searchState.outputElement(); | |
| 4847 | const error = query.error; | |
| 4848 | if (count === 0 && error !== null && search) { | |
| 4849 | error.forEach((value, index) => { | |
| 4850 | value = value.split("<").join("&lt;").split(">").join("&gt;"); | |
| 4851 | if (index % 2 !== 0) { | |
| 4852 | error[index] = `<code>${value.replaceAll(" ", "&nbsp;")}</code>`; | |
| 4853 | } else { | |
| 4854 | error[index] = value; | |
| 4855 | } | |
| 4856 | }); | |
| 4857 | const errorReport = document.createElement("h3"); | |
| 4858 | errorReport.className = "error"; | |
| 4859 | errorReport.innerHTML = `Query parser error: "${error.join("")}".`; | |
| 4860 | search.insertBefore(errorReport, search.firstElementChild); | |
| 4861 | } else if (goToFirst || | |
| 4862 | (count === 1 && getSettingValue("go-to-only-result") === "true") | |
| 4863 | ) { | |
| 4864 | // Needed to force re-execution of JS when coming back to a page. Let's take this | |
| 4865 | // scenario as example: | |
| 4866 | // | |
| 4867 | // 1. You have the "Directly go to item in search if there is only one result" | |
| 4868 | // option enabled. | |
| 4869 | // 2. You make a search which results only one result, leading you automatically to | |
| 4870 | // this result. | |
| 4871 | // 3. You go back to previous page. | |
| 4872 | // | |
| 4873 | // Now, without the call below, the JS will not be re-executed and the previous | |
| 4874 | // state will be used, starting search again since the search input is not empty, | |
| 4875 | // leading you back to the previous page again. | |
| 4876 | window.onunload = () => { }; | |
| 4877 | window.searchState.removeQueryParameters(); | |
| 4878 | const a = output.querySelector("a"); | |
| 4879 | if (a) { | |
| 4880 | a.click(); | |
| 4881 | return; | |
| 4882 | } | |
| 4883 | } | |
| 4884 | ||
| 4885 | // https://blog.horizon-eda.org/misc/2020/02/19/ui.html | |
| 4886 | // | |
| 4887 | // CSS runs with `font-variant-numeric: tabular-nums` to ensure all | |
| 4888 | // digits are the same width. \u{2007} is a Unicode space character | |
| 4889 | // that is defined to be the same width as a digit. | |
| 4890 | const fmtNbElems = | |
| 4891 | count < 10 ? `\u{2007}(${count})\u{2007}\u{2007}` : | |
| 4892 | count < 100 ? `\u{2007}(${count})\u{2007}` : `\u{2007}(${count})`; | |
| 4893 | tabCount.innerHTML = fmtNbElems; | |
| 4894 | tabCount.className = "count"; | |
| 4895 | }, isTypeSearch), | |
| 4896 | ]; | |
| 4951 | 4897 | } |
| 4952 | 4898 | |
| 4953 | 4899 | /** |
| 4900 | * @param {DocSearch} docSearch | |
| 4954 | 4901 | * @param {rustdoc.ResultsTable} results |
| 4955 | * @param {boolean} go_to_first | |
| 4902 | * @param {boolean} goToFirst | |
| 4956 | 4903 | * @param {string} filterCrates |
| 4957 | 4904 | */ |
| 4958 | async function showResults(results, go_to_first, filterCrates) { | |
| 4959 | const search = searchState.outputElement(); | |
| 4960 | if (go_to_first || (results.others.length === 1 | |
| 4961 | && getSettingValue("go-to-only-result") === "true") | |
| 4962 | ) { | |
| 4963 | // Needed to force re-execution of JS when coming back to a page. Let's take this | |
| 4964 | // scenario as example: | |
| 4965 | // | |
| 4966 | // 1. You have the "Directly go to item in search if there is only one result" option | |
| 4967 | // enabled. | |
| 4968 | // 2. You make a search which results only one result, leading you automatically to | |
| 4969 | // this result. | |
| 4970 | // 3. You go back to previous page. | |
| 4971 | // | |
| 4972 | // Now, without the call below, the JS will not be re-executed and the previous state | |
| 4973 | // will be used, starting search again since the search input is not empty, leading you | |
| 4974 | // back to the previous page again. | |
| 4975 | window.onunload = () => { }; | |
| 4976 | searchState.removeQueryParameters(); | |
| 4977 | const elem = document.createElement("a"); | |
| 4978 | elem.href = results.others[0].href; | |
| 4979 | removeClass(elem, "active"); | |
| 4980 | // For firefox, we need the element to be in the DOM so it can be clicked. | |
| 4981 | document.body.appendChild(elem); | |
| 4982 | elem.click(); | |
| 4983 | return; | |
| 4984 | } | |
| 4985 | if (results.query === undefined) { | |
| 4986 | // @ts-expect-error | |
| 4987 | results.query = DocSearch.parseQuery(searchState.input.value); | |
| 4988 | } | |
| 4905 | async function showResults(docSearch, results, goToFirst, filterCrates) { | |
| 4906 | const search = window.searchState.outputElement(); | |
| 4989 | 4907 | |
| 4990 | currentResults = results.query.userQuery; | |
| 4991 | ||
| 4992 | // Navigate to the relevant tab if the current tab is empty, like in case users search | |
| 4993 | // for "-> String". If they had selected another tab previously, they have to click on | |
| 4994 | // it again. | |
| 4995 | let currentTab = searchState.currentTab; | |
| 4996 | if ((currentTab === 0 && results.others.length === 0) || | |
| 4997 | (currentTab === 1 && results.in_args.length === 0) || | |
| 4998 | (currentTab === 2 && results.returned.length === 0)) { | |
| 4999 | if (results.others.length !== 0) { | |
| 5000 | currentTab = 0; | |
| 5001 | } else if (results.in_args.length) { | |
| 5002 | currentTab = 1; | |
| 5003 | } else if (results.returned.length) { | |
| 5004 | currentTab = 2; | |
| 5005 | } | |
| 4908 | if (!search) { | |
| 4909 | return; | |
| 5006 | 4910 | } |
| 5007 | 4911 | |
| 5008 | 4912 | let crates = ""; |
| 5009 | if (rawSearchIndex.size > 1) { | |
| 5010 | crates = "<div class=\"sub-heading\"> in&nbsp;<div id=\"crate-search-div\">" + | |
| 4913 | const crateNames = await docSearch.getCrateNameList(); | |
| 4914 | if (crateNames.length > 1) { | |
| 4915 | crates = "&nbsp;in&nbsp;<div id=\"crate-search-div\">" + | |
| 5011 | 4916 | "<select id=\"crate-search\"><option value=\"all crates\">all crates</option>"; |
| 5012 | for (const c of rawSearchIndex.keys()) { | |
| 4917 | const l = crateNames.length; | |
| 4918 | for (let i = 0; i < l; i += 1) { | |
| 4919 | const c = crateNames[i]; | |
| 5013 | 4920 | crates += `<option value="${c}" ${c === filterCrates && "selected"}>${c}</option>`; |
| 5014 | 4921 | } |
| 5015 | crates += "</select></div></div>"; | |
| 4922 | crates += "</select></div>"; | |
| 5016 | 4923 | } |
| 4924 | nonnull(document.querySelector(".search-switcher")).innerHTML = `Search results${crates}`; | |
| 5017 | 4925 | |
| 5018 | let output = `<div class="main-heading">\ | |
| 5019 | <h1 class="search-results-title">Results</h1>${crates}</div>`; | |
| 4926 | /** @type {[HTMLElement, Promise<HTMLElement>][]} */ | |
| 4927 | const tabs = []; | |
| 4928 | searchState.currentTab = 0; | |
| 5020 | 4929 | if (results.query.error !== null) { |
| 5021 | const error = results.query.error; | |
| 5022 | // @ts-expect-error | |
| 5023 | error.forEach((value, index) => { | |
| 5024 | value = value.split("<").join("&lt;").split(">").join("&gt;"); | |
| 5025 | if (index % 2 !== 0) { | |
| 5026 | error[index] = `<code>${value.replaceAll(" ", "&nbsp;")}</code>`; | |
| 5027 | } else { | |
| 5028 | error[index] = value; | |
| 5029 | } | |
| 5030 | }); | |
| 5031 | output += `<h3 class="error">Query parser error: "${error.join("")}".</h3>`; | |
| 5032 | output += "<div id=\"search-tabs\">" + | |
| 5033 | makeTabHeader(0, "In Names", results.others.length) + | |
| 5034 | "</div>"; | |
| 5035 | currentTab = 0; | |
| 5036 | } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) { | |
| 5037 | output += "<div id=\"search-tabs\">" + | |
| 5038 | makeTabHeader(0, "In Names", results.others.length) + | |
| 5039 | makeTabHeader(1, "In Parameters", results.in_args.length) + | |
| 5040 | makeTabHeader(2, "In Return Types", results.returned.length) + | |
| 5041 | "</div>"; | |
| 4930 | tabs.push(makeTab(0, "In Names", results.others, results.query, false, goToFirst)); | |
| 4931 | } else if ( | |
| 4932 | results.query.foundElems <= 1 && | |
| 4933 | results.query.returned.length === 0 && | |
| 4934 | !results.query.hasReturnArrow | |
| 4935 | ) { | |
| 4936 | tabs.push(makeTab(0, "In Names", results.others, results.query, false, goToFirst)); | |
| 4937 | tabs.push(makeTab(1, "In Parameters", results.in_args, results.query, true, false)); | |
| 4938 | tabs.push(makeTab(2, "In Return Types", results.returned, results.query, true, false)); | |
| 5042 | 4939 | } else { |
| 5043 | 4940 | const signatureTabTitle = |
| 5044 | 4941 | results.query.elems.length === 0 ? "In Function Return Types" : |
| 5045 | 4942 | results.query.returned.length === 0 ? "In Function Parameters" : |
| 5046 | 4943 | "In Function Signatures"; |
| 5047 | output += "<div id=\"search-tabs\">" + | |
| 5048 | makeTabHeader(0, signatureTabTitle, results.others.length) + | |
| 5049 | "</div>"; | |
| 5050 | currentTab = 0; | |
| 5051 | } | |
| 5052 | ||
| 5053 | if (results.query.correction !== null) { | |
| 5054 | const orig = results.query.returned.length > 0 | |
| 5055 | ? results.query.returned[0].name | |
| 5056 | : results.query.elems[0].name; | |
| 5057 | output += "<h3 class=\"search-corrections\">" + | |
| 5058 | `Type "${orig}" not found. ` + | |
| 5059 | "Showing results for closest type name " + | |
| 5060 | `"${results.query.correction}" instead.</h3>`; | |
| 5061 | } | |
| 5062 | if (results.query.proposeCorrectionFrom !== null) { | |
| 5063 | const orig = results.query.proposeCorrectionFrom; | |
| 5064 | const targ = results.query.proposeCorrectionTo; | |
| 5065 | output += "<h3 class=\"search-corrections\">" + | |
| 5066 | `Type "${orig}" not found and used as generic parameter. ` + | |
| 5067 | `Consider searching for "${targ}" instead.</h3>`; | |
| 4944 | tabs.push(makeTab(0, signatureTabTitle, results.others, results.query, true, goToFirst)); | |
| 5068 | 4945 | } |
| 5069 | 4946 | |
| 5070 | const [ret_others, ret_in_args, ret_returned] = await Promise.all([ | |
| 5071 | addTab(results.others, results.query, currentTab === 0), | |
| 5072 | addTab(results.in_args, results.query, currentTab === 1), | |
| 5073 | addTab(results.returned, results.query, currentTab === 2), | |
| 5074 | ]); | |
| 4947 | const tabsElem = document.createElement("div"); | |
| 4948 | tabsElem.id = "search-tabs"; | |
| 5075 | 4949 | |
| 5076 | 4950 | const resultsElem = document.createElement("div"); |
| 5077 | 4951 | resultsElem.id = "results"; |
| 5078 | resultsElem.appendChild(ret_others); | |
| 5079 | resultsElem.appendChild(ret_in_args); | |
| 5080 | resultsElem.appendChild(ret_returned); | |
| 5081 | 4952 | |
| 5082 | // @ts-expect-error | |
| 5083 | search.innerHTML = output; | |
| 5084 | if (searchState.rustdocToolbar) { | |
| 5085 | // @ts-expect-error | |
| 5086 | search.querySelector(".main-heading").appendChild(searchState.rustdocToolbar); | |
| 4953 | search.innerHTML = ""; | |
| 4954 | for (const [tab, output] of tabs) { | |
| 4955 | tabsElem.appendChild(tab); | |
| 4956 | const placeholder = document.createElement("div"); | |
| 4957 | output.then(output => { | |
| 4958 | if (placeholder.parentElement) { | |
| 4959 | placeholder.parentElement.replaceChild(output, placeholder); | |
| 4960 | } | |
| 4961 | }); | |
| 4962 | resultsElem.appendChild(placeholder); | |
| 4963 | } | |
| 4964 | ||
| 4965 | if (window.searchState.rustdocToolbar) { | |
| 4966 | nonnull( | |
| 4967 | nonnull(window.searchState.containerElement()) | |
| 4968 | .querySelector(".main-heading"), | |
| 4969 | ).appendChild(window.searchState.rustdocToolbar); | |
| 5087 | 4970 | } |
| 5088 | 4971 | const crateSearch = document.getElementById("crate-search"); |
| 5089 | 4972 | if (crateSearch) { |
| 5090 | 4973 | crateSearch.addEventListener("input", updateCrate); |
| 5091 | 4974 | } |
| 5092 | // @ts-expect-error | |
| 4975 | search.appendChild(tabsElem); | |
| 5093 | 4976 | search.appendChild(resultsElem); |
| 5094 | 4977 | // Reset focused elements. |
| 5095 | searchState.showResults(search); | |
| 5096 | // @ts-expect-error | |
| 5097 | const elems = document.getElementById("search-tabs").childNodes; | |
| 5098 | // @ts-expect-error | |
| 5099 | searchState.focusedByTab = []; | |
| 4978 | window.searchState.showResults(); | |
| 4979 | window.searchState.focusedByTab = [null, null, null]; | |
| 5100 | 4980 | let i = 0; |
| 5101 | for (const elem of elems) { | |
| 4981 | for (const elem of tabsElem.childNodes) { | |
| 5102 | 4982 | const j = i; |
| 5103 | 4983 | // @ts-expect-error |
| 5104 | 4984 | elem.onclick = () => printTab(j); |
| 5105 | searchState.focusedByTab.push(null); | |
| 4985 | window.searchState.focusedByTab[i] = null; | |
| 5106 | 4986 | i += 1; |
| 5107 | 4987 | } |
| 5108 | printTab(currentTab); | |
| 4988 | printTab(0); | |
| 5109 | 4989 | } |
| 5110 | 4990 | |
| 5111 | 4991 | // @ts-expect-error |
| 5112 | 4992 | function updateSearchHistory(url) { |
| 4993 | const btn = document.querySelector("#search-button a"); | |
| 4994 | if (btn instanceof HTMLAnchorElement) { | |
| 4995 | btn.href = url; | |
| 4996 | } | |
| 5113 | 4997 | if (!browserSupportsHistoryApi()) { |
| 5114 | 4998 | return; |
| 5115 | 4999 | } |
| 5116 | 5000 | const params = searchState.getQueryStringParams(); |
| 5117 | if (!history.state && !params.search) { | |
| 5001 | if (!history.state && params.search === undefined) { | |
| 5118 | 5002 | history.pushState(null, "", url); |
| 5119 | 5003 | } else { |
| 5120 | 5004 | history.replaceState(null, "", url); |
| ... | ... | @@ -5127,8 +5011,8 @@ function updateSearchHistory(url) { |
| 5127 | 5011 | * @param {boolean} [forced] |
| 5128 | 5012 | */ |
| 5129 | 5013 | async function search(forced) { |
| 5130 | // @ts-expect-error | |
| 5131 | const query = DocSearch.parseQuery(searchState.input.value.trim()); | |
| 5014 | const query = DocSearch.parseQuery(nonnull(window.searchState.inputElement()).value.trim()); | |
| 5015 | ||
| 5132 | 5016 | let filterCrates = getFilterCrates(); |
| 5133 | 5017 | |
| 5134 | 5018 | // @ts-expect-error |
| ... | ... | @@ -5138,6 +5022,7 @@ async function search(forced) { |
| 5138 | 5022 | } |
| 5139 | 5023 | return; |
| 5140 | 5024 | } |
| 5025 | currentResults = query.userQuery; | |
| 5141 | 5026 | |
| 5142 | 5027 | searchState.setLoadingSearch(); |
| 5143 | 5028 | |
| ... | ... | @@ -5149,6 +5034,12 @@ async function search(forced) { |
| 5149 | 5034 | filterCrates = params["filter-crate"]; |
| 5150 | 5035 | } |
| 5151 | 5036 | |
| 5037 | if (filterCrates !== null && | |
| 5038 | (await docSearch.getCrateNameList()).indexOf(filterCrates) === -1 | |
| 5039 | ) { | |
| 5040 | filterCrates = null; | |
| 5041 | } | |
| 5042 | ||
| 5152 | 5043 | // Update document title to maintain a meaningful browser history |
| 5153 | 5044 | searchState.title = "\"" + query.userQuery + "\" Search - Rust"; |
| 5154 | 5045 | |
| ... | ... | @@ -5157,6 +5048,7 @@ async function search(forced) { |
| 5157 | 5048 | updateSearchHistory(buildUrl(query.userQuery, filterCrates)); |
| 5158 | 5049 | |
| 5159 | 5050 | await showResults( |
| 5051 | docSearch, | |
| 5160 | 5052 | // @ts-expect-error |
| 5161 | 5053 | await docSearch.execQuery(query, filterCrates, window.currentCrate), |
| 5162 | 5054 | params.go_to_first, |
| ... | ... | @@ -5176,16 +5068,14 @@ function onSearchSubmit(e) { |
| 5176 | 5068 | } |
| 5177 | 5069 | |
| 5178 | 5070 | function putBackSearch() { |
| 5179 | const search_input = searchState.input; | |
| 5180 | if (!searchState.input) { | |
| 5071 | const search_input = window.searchState.inputElement(); | |
| 5072 | if (!search_input) { | |
| 5181 | 5073 | return; |
| 5182 | 5074 | } |
| 5183 | // @ts-expect-error | |
| 5184 | 5075 | if (search_input.value !== "" && !searchState.isDisplayed()) { |
| 5185 | 5076 | searchState.showResults(); |
| 5186 | 5077 | if (browserSupportsHistoryApi()) { |
| 5187 | 5078 | history.replaceState(null, "", |
| 5188 | // @ts-expect-error | |
| 5189 | 5079 | buildUrl(search_input.value, getFilterCrates())); |
| 5190 | 5080 | } |
| 5191 | 5081 | document.title = searchState.title; |
| ... | ... | @@ -5199,30 +5089,21 @@ function registerSearchEvents() { |
| 5199 | 5089 | // but only if the input bar is empty. This avoid the obnoxious issue |
| 5200 | 5090 | // where you start trying to do a search, and the index loads, and |
| 5201 | 5091 | // suddenly your search is gone! |
| 5202 | // @ts-expect-error | |
| 5203 | if (searchState.input.value === "") { | |
| 5204 | // @ts-expect-error | |
| 5205 | searchState.input.value = params.search || ""; | |
| 5092 | const inputElement = nonnull(window.searchState.inputElement()); | |
| 5093 | if (inputElement.value === "") { | |
| 5094 | inputElement.value = params.search || ""; | |
| 5206 | 5095 | } |
| 5207 | 5096 | |
| 5208 | 5097 | const searchAfter500ms = () => { |
| 5209 | 5098 | searchState.clearInputTimeout(); |
| 5210 | // @ts-expect-error | |
| 5211 | if (searchState.input.value.length === 0) { | |
| 5212 | searchState.hideResults(); | |
| 5213 | } else { | |
| 5214 | // @ts-ignore | |
| 5215 | searchState.timeout = setTimeout(search, 500); | |
| 5216 | } | |
| 5099 | window.searchState.timeout = setTimeout(search, 500); | |
| 5217 | 5100 | }; |
| 5218 | // @ts-expect-error | |
| 5219 | searchState.input.onkeyup = searchAfter500ms; | |
| 5220 | // @ts-expect-error | |
| 5221 | searchState.input.oninput = searchAfter500ms; | |
| 5222 | // @ts-expect-error | |
| 5223 | document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit; | |
| 5224 | // @ts-expect-error | |
| 5225 | searchState.input.onchange = e => { | |
| 5101 | inputElement.onkeyup = searchAfter500ms; | |
| 5102 | inputElement.oninput = searchAfter500ms; | |
| 5103 | if (inputElement.form) { | |
| 5104 | inputElement.form.onsubmit = onSearchSubmit; | |
| 5105 | } | |
| 5106 | inputElement.onchange = e => { | |
| 5226 | 5107 | if (e.target !== document.activeElement) { |
| 5227 | 5108 | // To prevent doing anything when it's from a blur event. |
| 5228 | 5109 | return; |
| ... | ... | @@ -5234,11 +5115,13 @@ function registerSearchEvents() { |
| 5234 | 5115 | // change, though. |
| 5235 | 5116 | setTimeout(search, 0); |
| 5236 | 5117 | }; |
| 5237 | // @ts-expect-error | |
| 5238 | searchState.input.onpaste = searchState.input.onchange; | |
| 5118 | inputElement.onpaste = inputElement.onchange; | |
| 5239 | 5119 | |
| 5240 | 5120 | // @ts-expect-error |
| 5241 | 5121 | searchState.outputElement().addEventListener("keydown", e => { |
| 5122 | if (!(e instanceof KeyboardEvent)) { | |
| 5123 | return; | |
| 5124 | } | |
| 5242 | 5125 | // We only handle unmodified keystrokes here. We don't want to interfere with, |
| 5243 | 5126 | // for instance, alt-left and alt-right for history navigation. |
| 5244 | 5127 | if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { |
| ... | ... | @@ -5278,88 +5161,23 @@ function registerSearchEvents() { |
| 5278 | 5161 | } |
| 5279 | 5162 | }); |
| 5280 | 5163 | |
| 5281 | // @ts-expect-error | |
| 5282 | searchState.input.addEventListener("keydown", e => { | |
| 5164 | inputElement.addEventListener("keydown", e => { | |
| 5283 | 5165 | if (e.which === 40) { // down |
| 5284 | 5166 | focusSearchResult(); |
| 5285 | 5167 | e.preventDefault(); |
| 5286 | 5168 | } |
| 5287 | 5169 | }); |
| 5288 | 5170 | |
| 5289 | // @ts-expect-error | |
| 5290 | searchState.input.addEventListener("focus", () => { | |
| 5171 | inputElement.addEventListener("focus", () => { | |
| 5291 | 5172 | putBackSearch(); |
| 5292 | 5173 | }); |
| 5293 | ||
| 5294 | // @ts-expect-error | |
| 5295 | searchState.input.addEventListener("blur", () => { | |
| 5296 | if (window.searchState.input) { | |
| 5297 | window.searchState.input.placeholder = window.searchState.origPlaceholder; | |
| 5298 | } | |
| 5299 | }); | |
| 5300 | ||
| 5301 | // Push and pop states are used to add search results to the browser | |
| 5302 | // history. | |
| 5303 | if (browserSupportsHistoryApi()) { | |
| 5304 | // Store the previous <title> so we can revert back to it later. | |
| 5305 | const previousTitle = document.title; | |
| 5306 | ||
| 5307 | window.addEventListener("popstate", e => { | |
| 5308 | const params = searchState.getQueryStringParams(); | |
| 5309 | // Revert to the previous title manually since the History | |
| 5310 | // API ignores the title parameter. | |
| 5311 | document.title = previousTitle; | |
| 5312 | // When browsing forward to search results the previous | |
| 5313 | // search will be repeated, so the currentResults are | |
| 5314 | // cleared to ensure the search is successful. | |
| 5315 | currentResults = null; | |
| 5316 | // Synchronize search bar with query string state and | |
| 5317 | // perform the search. This will empty the bar if there's | |
| 5318 | // nothing there, which lets you really go back to a | |
| 5319 | // previous state with nothing in the bar. | |
| 5320 | if (params.search && params.search.length > 0) { | |
| 5321 | // @ts-expect-error | |
| 5322 | searchState.input.value = params.search; | |
| 5323 | // Some browsers fire "onpopstate" for every page load | |
| 5324 | // (Chrome), while others fire the event only when actually | |
| 5325 | // popping a state (Firefox), which is why search() is | |
| 5326 | // called both here and at the end of the startSearch() | |
| 5327 | // function. | |
| 5328 | e.preventDefault(); | |
| 5329 | search(); | |
| 5330 | } else { | |
| 5331 | // @ts-expect-error | |
| 5332 | searchState.input.value = ""; | |
| 5333 | // When browsing back from search results the main page | |
| 5334 | // visibility must be reset. | |
| 5335 | searchState.hideResults(); | |
| 5336 | } | |
| 5337 | }); | |
| 5338 | } | |
| 5339 | ||
| 5340 | // This is required in firefox to avoid this problem: Navigating to a search result | |
| 5341 | // with the keyboard, hitting enter, and then hitting back would take you back to | |
| 5342 | // the doc page, rather than the search that should overlay it. | |
| 5343 | // This was an interaction between the back-forward cache and our handlers | |
| 5344 | // that try to sync state between the URL and the search input. To work around it, | |
| 5345 | // do a small amount of re-init on page show. | |
| 5346 | window.onpageshow = () => { | |
| 5347 | const qSearch = searchState.getQueryStringParams().search; | |
| 5348 | // @ts-expect-error | |
| 5349 | if (searchState.input.value === "" && qSearch) { | |
| 5350 | // @ts-expect-error | |
| 5351 | searchState.input.value = qSearch; | |
| 5352 | } | |
| 5353 | search(); | |
| 5354 | }; | |
| 5355 | 5174 | } |
| 5356 | 5175 | |
| 5357 | 5176 | // @ts-expect-error |
| 5358 | 5177 | function updateCrate(ev) { |
| 5359 | 5178 | if (ev.target.value === "all crates") { |
| 5360 | 5179 | // If we don't remove it from the URL, it'll be picked up again by the search. |
| 5361 | // @ts-expect-error | |
| 5362 | const query = searchState.input.value.trim(); | |
| 5180 | const query = nonnull(window.searchState.inputElement()).value.trim(); | |
| 5363 | 5181 | updateSearchHistory(buildUrl(query, null)); |
| 5364 | 5182 | } |
| 5365 | 5183 | // In case you "cut" the entry from the search input, then change the crate filter |
| ... | ... | @@ -5369,522 +5187,91 @@ function updateCrate(ev) { |
| 5369 | 5187 | search(true); |
| 5370 | 5188 | } |
| 5371 | 5189 | |
| 5372 | // Parts of this code are based on Lucene, which is licensed under the | |
| 5373 | // Apache/2.0 license. | |
| 5374 | // More information found here: | |
| 5375 | // https://fossies.org/linux/lucene/lucene/core/src/java/org/apache/lucene/util/automaton/ | |
| 5376 | // LevenshteinAutomata.java | |
| 5377 | class ParametricDescription { | |
| 5378 | // @ts-expect-error | |
| 5379 | constructor(w, n, minErrors) { | |
| 5380 | this.w = w; | |
| 5381 | this.n = n; | |
| 5382 | this.minErrors = minErrors; | |
| 5383 | } | |
| 5384 | // @ts-expect-error | |
| 5385 | isAccept(absState) { | |
| 5386 | const state = Math.floor(absState / (this.w + 1)); | |
| 5387 | const offset = absState % (this.w + 1); | |
| 5388 | return this.w - offset + this.minErrors[state] <= this.n; | |
| 5389 | } | |
| 5390 | // @ts-expect-error | |
| 5391 | getPosition(absState) { | |
| 5392 | return absState % (this.w + 1); | |
| 5393 | } | |
| 5394 | // @ts-expect-error | |
| 5395 | getVector(name, charCode, pos, end) { | |
| 5396 | let vector = 0; | |
| 5397 | for (let i = pos; i < end; i += 1) { | |
| 5398 | vector = vector << 1; | |
| 5399 | if (name.charCodeAt(i) === charCode) { | |
| 5400 | vector |= 1; | |
| 5401 | } | |
| 5402 | } | |
| 5403 | return vector; | |
| 5404 | } | |
| 5405 | // @ts-expect-error | |
| 5406 | unpack(data, index, bitsPerValue) { | |
| 5407 | const bitLoc = (bitsPerValue * index); | |
| 5408 | const dataLoc = bitLoc >> 5; | |
| 5409 | const bitStart = bitLoc & 31; | |
| 5410 | if (bitStart + bitsPerValue <= 32) { | |
| 5411 | // not split | |
| 5412 | return ((data[dataLoc] >> bitStart) & this.MASKS[bitsPerValue - 1]); | |
| 5413 | } else { | |
| 5414 | // split | |
| 5415 | const part = 32 - bitStart; | |
| 5416 | return ~~(((data[dataLoc] >> bitStart) & this.MASKS[part - 1]) + | |
| 5417 | ((data[1 + dataLoc] & this.MASKS[bitsPerValue - part - 1]) << part)); | |
| 5418 | } | |
| 5190 | // eslint-disable-next-line max-len | |
| 5191 | // polyfill https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64 | |
| 5192 | /** | |
| 5193 | * @type {function(string): Uint8Array} base64 | |
| 5194 | */ | |
| 5195 | //@ts-expect-error | |
| 5196 | const makeUint8ArrayFromBase64 = Uint8Array.fromBase64 ? Uint8Array.fromBase64 : (string => { | |
| 5197 | const bytes_as_string = atob(string); | |
| 5198 | const l = bytes_as_string.length; | |
| 5199 | const bytes = new Uint8Array(l); | |
| 5200 | for (let i = 0; i < l; ++i) { | |
| 5201 | bytes[i] = bytes_as_string.charCodeAt(i); | |
| 5419 | 5202 | } |
| 5420 | } | |
| 5421 | ParametricDescription.prototype.MASKS = new Int32Array([ | |
| 5422 | 0x1, 0x3, 0x7, 0xF, | |
| 5423 | 0x1F, 0x3F, 0x7F, 0xFF, | |
| 5424 | 0x1FF, 0x3F, 0x7FF, 0xFFF, | |
| 5425 | 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, | |
| 5426 | 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, | |
| 5427 | 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, | |
| 5428 | 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, | |
| 5429 | 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, | |
| 5430 | ]); | |
| 5431 | ||
| 5432 | // The following code was generated with the moman/finenight pkg | |
| 5433 | // This package is available under the MIT License, see NOTICE.txt | |
| 5434 | // for more details. | |
| 5435 | // This class is auto-generated, Please do not modify it directly. | |
| 5436 | // You should modify the https://gitlab.com/notriddle/createAutomata.py instead. | |
| 5437 | // The following code was generated with the moman/finenight pkg | |
| 5438 | // This package is available under the MIT License, see NOTICE.txt | |
| 5439 | // for more details. | |
| 5440 | // This class is auto-generated, Please do not modify it directly. | |
| 5441 | // You should modify https://gitlab.com/notriddle/moman-rustdoc instead. | |
| 5442 | ||
| 5443 | class Lev2TParametricDescription extends ParametricDescription { | |
| 5444 | /** | |
| 5445 | * @param {number} absState | |
| 5446 | * @param {number} position | |
| 5447 | * @param {number} vector | |
| 5448 | * @returns {number} | |
| 5449 | */ | |
| 5450 | transition(absState, position, vector) { | |
| 5451 | let state = Math.floor(absState / (this.w + 1)); | |
| 5452 | let offset = absState % (this.w + 1); | |
| 5453 | ||
| 5454 | if (position === this.w) { | |
| 5455 | if (state < 3) { // eslint-disable-line no-lonely-if | |
| 5456 | const loc = Math.imul(vector, 3) + state; | |
| 5457 | offset += this.unpack(this.offsetIncrs0, loc, 1); | |
| 5458 | state = this.unpack(this.toStates0, loc, 2) - 1; | |
| 5459 | } | |
| 5460 | } else if (position === this.w - 1) { | |
| 5461 | if (state < 5) { // eslint-disable-line no-lonely-if | |
| 5462 | const loc = Math.imul(vector, 5) + state; | |
| 5463 | offset += this.unpack(this.offsetIncrs1, loc, 1); | |
| 5464 | state = this.unpack(this.toStates1, loc, 3) - 1; | |
| 5465 | } | |
| 5466 | } else if (position === this.w - 2) { | |
| 5467 | if (state < 13) { // eslint-disable-line no-lonely-if | |
| 5468 | const loc = Math.imul(vector, 13) + state; | |
| 5469 | offset += this.unpack(this.offsetIncrs2, loc, 2); | |
| 5470 | state = this.unpack(this.toStates2, loc, 4) - 1; | |
| 5471 | } | |
| 5472 | } else if (position === this.w - 3) { | |
| 5473 | if (state < 28) { // eslint-disable-line no-lonely-if | |
| 5474 | const loc = Math.imul(vector, 28) + state; | |
| 5475 | offset += this.unpack(this.offsetIncrs3, loc, 2); | |
| 5476 | state = this.unpack(this.toStates3, loc, 5) - 1; | |
| 5477 | } | |
| 5478 | } else if (position === this.w - 4) { | |
| 5479 | if (state < 45) { // eslint-disable-line no-lonely-if | |
| 5480 | const loc = Math.imul(vector, 45) + state; | |
| 5481 | offset += this.unpack(this.offsetIncrs4, loc, 3); | |
| 5482 | state = this.unpack(this.toStates4, loc, 6) - 1; | |
| 5483 | } | |
| 5484 | } else { | |
| 5485 | if (state < 45) { // eslint-disable-line no-lonely-if | |
| 5486 | const loc = Math.imul(vector, 45) + state; | |
| 5487 | offset += this.unpack(this.offsetIncrs5, loc, 3); | |
| 5488 | state = this.unpack(this.toStates5, loc, 6) - 1; | |
| 5489 | } | |
| 5490 | } | |
| 5203 | return bytes; | |
| 5204 | }); | |
| 5491 | 5205 | |
| 5492 | if (state === -1) { | |
| 5493 | // null state | |
| 5494 | return -1; | |
| 5495 | } else { | |
| 5496 | // translate back to abs | |
| 5497 | return Math.imul(state, this.w + 1) + offset; | |
| 5498 | } | |
| 5499 | } | |
| 5500 | 5206 | |
| 5501 | // state map | |
| 5502 | // 0 -> [(0, 0)] | |
| 5503 | // 1 -> [(0, 1)] | |
| 5504 | // 2 -> [(0, 2)] | |
| 5505 | // 3 -> [(0, 1), (1, 1)] | |
| 5506 | // 4 -> [(0, 2), (1, 2)] | |
| 5507 | // 5 -> [(0, 1), (1, 1), (2, 1)] | |
| 5508 | // 6 -> [(0, 2), (1, 2), (2, 2)] | |
| 5509 | // 7 -> [(0, 1), (2, 1)] | |
| 5510 | // 8 -> [(0, 1), (2, 2)] | |
| 5511 | // 9 -> [(0, 2), (2, 1)] | |
| 5512 | // 10 -> [(0, 2), (2, 2)] | |
| 5513 | // 11 -> [t(0, 1), (0, 1), (1, 1), (2, 1)] | |
| 5514 | // 12 -> [t(0, 2), (0, 2), (1, 2), (2, 2)] | |
| 5515 | // 13 -> [(0, 2), (1, 2), (2, 2), (3, 2)] | |
| 5516 | // 14 -> [(0, 1), (1, 1), (3, 2)] | |
| 5517 | // 15 -> [(0, 1), (2, 2), (3, 2)] | |
| 5518 | // 16 -> [(0, 1), (3, 2)] | |
| 5519 | // 17 -> [(0, 1), t(1, 2), (2, 2), (3, 2)] | |
| 5520 | // 18 -> [(0, 2), (1, 2), (3, 1)] | |
| 5521 | // 19 -> [(0, 2), (1, 2), (3, 2)] | |
| 5522 | // 20 -> [(0, 2), (1, 2), t(1, 2), (2, 2), (3, 2)] | |
| 5523 | // 21 -> [(0, 2), (2, 1), (3, 1)] | |
| 5524 | // 22 -> [(0, 2), (2, 2), (3, 2)] | |
| 5525 | // 23 -> [(0, 2), (3, 1)] | |
| 5526 | // 24 -> [(0, 2), (3, 2)] | |
| 5527 | // 25 -> [(0, 2), t(1, 2), (1, 2), (2, 2), (3, 2)] | |
| 5528 | // 26 -> [t(0, 2), (0, 2), (1, 2), (2, 2), (3, 2)] | |
| 5529 | // 27 -> [t(0, 2), (0, 2), (1, 2), (3, 1)] | |
| 5530 | // 28 -> [(0, 2), (1, 2), (2, 2), (3, 2), (4, 2)] | |
| 5531 | // 29 -> [(0, 2), (1, 2), (2, 2), (4, 2)] | |
| 5532 | // 30 -> [(0, 2), (1, 2), (2, 2), t(2, 2), (3, 2), (4, 2)] | |
| 5533 | // 31 -> [(0, 2), (1, 2), (3, 2), (4, 2)] | |
| 5534 | // 32 -> [(0, 2), (1, 2), (4, 2)] | |
| 5535 | // 33 -> [(0, 2), (1, 2), t(1, 2), (2, 2), (3, 2), (4, 2)] | |
| 5536 | // 34 -> [(0, 2), (1, 2), t(2, 2), (2, 2), (3, 2), (4, 2)] | |
| 5537 | // 35 -> [(0, 2), (2, 1), (4, 2)] | |
| 5538 | // 36 -> [(0, 2), (2, 2), (3, 2), (4, 2)] | |
| 5539 | // 37 -> [(0, 2), (2, 2), (4, 2)] | |
| 5540 | // 38 -> [(0, 2), (3, 2), (4, 2)] | |
| 5541 | // 39 -> [(0, 2), (4, 2)] | |
| 5542 | // 40 -> [(0, 2), t(1, 2), (1, 2), (2, 2), (3, 2), (4, 2)] | |
| 5543 | // 41 -> [(0, 2), t(2, 2), (2, 2), (3, 2), (4, 2)] | |
| 5544 | // 42 -> [t(0, 2), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2)] | |
| 5545 | // 43 -> [t(0, 2), (0, 2), (1, 2), (2, 2), (4, 2)] | |
| 5546 | // 44 -> [t(0, 2), (0, 2), (1, 2), (2, 2), t(2, 2), (3, 2), (4, 2)] | |
| 5547 | ||
| 5548 | ||
| 5549 | /** @param {number} w - length of word being checked */ | |
| 5550 | constructor(w) { | |
| 5551 | super(w, 2, new Int32Array([ | |
| 5552 | 0,1,2,0,1,-1,0,-1,0,-1,0,-1,0,-1,-1,-1,-1,-1,-2,-1,-1,-2,-1,-2, | |
| 5553 | -1,-1,-1,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2, | |
| 5554 | ])); | |
| 5207 | if (ROOT_PATH === null) { | |
| 5208 | return; | |
| 5209 | } | |
| 5210 | const database = await Stringdex.loadDatabase(hooks); | |
| 5211 | if (typeof window !== "undefined") { | |
| 5212 | docSearch = new DocSearch(ROOT_PATH, database); | |
| 5213 | await docSearch.buildIndex(); | |
| 5214 | onEachLazy(document.querySelectorAll( | |
| 5215 | ".search-form.loading", | |
| 5216 | ), form => { | |
| 5217 | removeClass(form, "loading"); | |
| 5218 | }); | |
| 5219 | registerSearchEvents(); | |
| 5220 | // If there's a search term in the URL, execute the search now. | |
| 5221 | if (window.searchState.getQueryStringParams().search !== undefined) { | |
| 5222 | search(); | |
| 5555 | 5223 | } |
| 5224 | } else if (typeof exports !== "undefined") { | |
| 5225 | docSearch = new DocSearch(ROOT_PATH, database); | |
| 5226 | await docSearch.buildIndex(); | |
| 5227 | return { docSearch, DocSearch }; | |
| 5556 | 5228 | } |
| 5229 | }; | |
| 5557 | 5230 | |
| 5558 | Lev2TParametricDescription.prototype.toStates0 = /*2 bits per value */ new Int32Array([ | |
| 5559 | 0xe, | |
| 5560 | ]); | |
| 5561 | Lev2TParametricDescription.prototype.offsetIncrs0 = /*1 bits per value */ new Int32Array([ | |
| 5562 | 0x0, | |
| 5563 | ]); | |
| 5564 | ||
| 5565 | Lev2TParametricDescription.prototype.toStates1 = /*3 bits per value */ new Int32Array([ | |
| 5566 | 0x1a688a2c, | |
| 5567 | ]); | |
| 5568 | Lev2TParametricDescription.prototype.offsetIncrs1 = /*1 bits per value */ new Int32Array([ | |
| 5569 | 0x3e0, | |
| 5570 | ]); | |
| 5571 | ||
| 5572 | Lev2TParametricDescription.prototype.toStates2 = /*4 bits per value */ new Int32Array([ | |
| 5573 | 0x70707054,0xdc07035,0x3dd3a3a,0x2323213a, | |
| 5574 | 0x15435223,0x22545432,0x5435, | |
| 5575 | ]); | |
| 5576 | Lev2TParametricDescription.prototype.offsetIncrs2 = /*2 bits per value */ new Int32Array([ | |
| 5577 | 0x80000,0x55582088,0x55555555,0x55, | |
| 5578 | ]); | |
| 5579 | ||
| 5580 | Lev2TParametricDescription.prototype.toStates3 = /*5 bits per value */ new Int32Array([ | |
| 5581 | 0x1c0380a4,0x700a570,0xca529c0,0x180a00, | |
| 5582 | 0xa80af180,0xc5498e60,0x5a546398,0x8c4300e8, | |
| 5583 | 0xac18c601,0xd8d43501,0x863500ad,0x51976d6a, | |
| 5584 | 0x8ca0180a,0xc3501ac2,0xb0c5be16,0x76dda8a5, | |
| 5585 | 0x18c4519,0xc41294a,0xe248d231,0x1086520c, | |
| 5586 | 0xce31ac42,0x13946358,0x2d0348c4,0x6732d494, | |
| 5587 | 0x1ad224a5,0xd635ad4b,0x520c4139,0xce24948, | |
| 5588 | 0x22110a52,0x58ce729d,0xc41394e3,0x941cc520, | |
| 5589 | 0x90e732d4,0x4729d224,0x39ce35ad, | |
| 5590 | ]); | |
| 5591 | Lev2TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new Int32Array([ | |
| 5592 | 0x80000,0xc0c830,0x300f3c30,0x2200fcff, | |
| 5593 | 0xcaa00a08,0x3c2200a8,0xa8fea00a,0x55555555, | |
| 5594 | 0x55555555,0x55555555,0x55555555,0x55555555, | |
| 5595 | 0x55555555,0x55555555, | |
| 5596 | ]); | |
| 5597 | ||
| 5598 | Lev2TParametricDescription.prototype.toStates4 = /*6 bits per value */ new Int32Array([ | |
| 5599 | 0x801c0144,0x1453803,0x14700038,0xc0005145, | |
| 5600 | 0x1401,0x14,0x140000,0x0, | |
| 5601 | 0x510000,0x6301f007,0x301f00d1,0xa186178, | |
| 5602 | 0xc20ca0c3,0xc20c30,0xc30030c,0xc00c00cd, | |
| 5603 | 0xf0c00c30,0x4c054014,0xc30944c3,0x55150c34, | |
| 5604 | 0x8300550,0x430c0143,0x50c31,0xc30850c, | |
| 5605 | 0xc3143000,0x50053c50,0x5130d301,0x850d30c2, | |
| 5606 | 0x30a08608,0xc214414,0x43142145,0x21450031, | |
| 5607 | 0x1400c314,0x4c143145,0x32832803,0x28014d6c, | |
| 5608 | 0xcd34a0c3,0x1c50c76,0x1c314014,0x430c30c3, | |
| 5609 | 0x1431,0xc300500,0xca00d303,0xd36d0e40, | |
| 5610 | 0x90b0e400,0xcb2abb2c,0x70c20ca1,0x2c32ca2c, | |
| 5611 | 0xcd2c70cb,0x31c00c00,0x34c2c32c,0x5583280, | |
| 5612 | 0x558309b7,0x6cd6ca14,0x430850c7,0x51c51401, | |
| 5613 | 0x1430c714,0xc3087,0x71451450,0xca00d30, | |
| 5614 | 0xc26dc156,0xb9071560,0x1cb2abb2,0xc70c2144, | |
| 5615 | 0xb1c51ca1,0x1421c70c,0xc51c00c3,0x30811c51, | |
| 5616 | 0x24324308,0xc51031c2,0x70820820,0x5c33830d, | |
| 5617 | 0xc33850c3,0x30c30c30,0xc30c31c,0x451450c3, | |
| 5618 | 0x20c20c20,0xda0920d,0x5145914f,0x36596114, | |
| 5619 | 0x51965865,0xd9643653,0x365a6590,0x51964364, | |
| 5620 | 0x43081505,0x920b2032,0x2c718b28,0xd7242249, | |
| 5621 | 0x35cb28b0,0x2cb3872c,0x972c30d7,0xb0c32cb2, | |
| 5622 | 0x4e1c75c,0xc80c90c2,0x62ca2482,0x4504171c, | |
| 5623 | 0xd65d9610,0x33976585,0xd95cb5d,0x4b5ca5d7, | |
| 5624 | 0x73975c36,0x10308138,0xc2245105,0x41451031, | |
| 5625 | 0x14e24208,0xc35c3387,0x51453851,0x1c51c514, | |
| 5626 | 0xc70c30c3,0x20451450,0x14f1440c,0x4f0da092, | |
| 5627 | 0x4513d41,0x6533944d,0x1350e658,0xe1545055, | |
| 5628 | 0x64365a50,0x5519383,0x51030815,0x28920718, | |
| 5629 | 0x441c718b,0x714e2422,0x1c35cb28,0x4e1c7387, | |
| 5630 | 0xb28e1c51,0x5c70c32c,0xc204e1c7,0x81c61440, | |
| 5631 | 0x1c62ca24,0xd04503ce,0x85d63944,0x39338e65, | |
| 5632 | 0x8e154387,0x364b5ca3,0x38739738, | |
| 5633 | ]); | |
| 5634 | Lev2TParametricDescription.prototype.offsetIncrs4 = /*3 bits per value */ new Int32Array([ | |
| 5635 | 0x10000000,0xc00000,0x60061,0x400, | |
| 5636 | 0x0,0x80010008,0x249248a4,0x8229048, | |
| 5637 | 0x2092,0x6c3603,0xb61b6c30,0x6db6036d, | |
| 5638 | 0xdb6c0,0x361b0180,0x91b72000,0xdb11b71b, | |
| 5639 | 0x6db6236,0x1008200,0x12480012,0x24924906, | |
| 5640 | 0x48200049,0x80410002,0x24000900,0x4924a489, | |
| 5641 | 0x10822492,0x20800125,0x48360,0x9241b692, | |
| 5642 | 0x6da4924,0x40009268,0x241b010,0x291b4900, | |
| 5643 | 0x6d249249,0x49493423,0x92492492,0x24924924, | |
| 5644 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 5645 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 5646 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 5647 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 5648 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 5649 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 5650 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 5651 | 0x92492492,0x24924924,0x49249249,0x2492, | |
| 5652 | ]); | |
| 5653 | ||
| 5654 | Lev2TParametricDescription.prototype.toStates5 = /*6 bits per value */ new Int32Array([ | |
| 5655 | 0x801c0144,0x1453803,0x14700038,0xc0005145, | |
| 5656 | 0x1401,0x14,0x140000,0x0, | |
| 5657 | 0x510000,0x4e00e007,0xe0051,0x3451451c, | |
| 5658 | 0xd015000,0x30cd0000,0xc30c30c,0xc30c30d4, | |
| 5659 | 0x40c30c30,0x7c01c014,0xc03458c0,0x185e0c07, | |
| 5660 | 0x2830c286,0x830c3083,0xc30030,0x33430c, | |
| 5661 | 0x30c3003,0x70051030,0x16301f00,0x8301f00d, | |
| 5662 | 0x30a18617,0xc20ca0c,0x431420c3,0xb1450c51, | |
| 5663 | 0x14314315,0x4f143145,0x34c05401,0x4c30944c, | |
| 5664 | 0x55150c3,0x30830055,0x1430c014,0xc00050c3, | |
| 5665 | 0xc30850,0xc314300,0x150053c5,0x25130d30, | |
| 5666 | 0x5430d30c,0xc0354154,0x300d0c90,0x1cb2cd0c, | |
| 5667 | 0xc91cb0c3,0x72c30cb2,0x14f1cb2c,0xc34c0540, | |
| 5668 | 0x34c30944,0x82182214,0x851050c2,0x50851430, | |
| 5669 | 0x1400c50c,0x30c5085,0x50c51450,0x150053c, | |
| 5670 | 0xc25130d3,0x8850d30,0x1430a086,0x450c2144, | |
| 5671 | 0x51cb1c21,0x1c91c70c,0xc71c314b,0x34c1cb1, | |
| 5672 | 0x6c328328,0xc328014d,0x76cd34a0,0x1401c50c, | |
| 5673 | 0xc31c3140,0x31430c30,0x14,0x30c3005, | |
| 5674 | 0xa0ca00d3,0x535b0c,0x4d2830ca,0x514369b3, | |
| 5675 | 0xc500d01,0x5965965a,0x30d46546,0x6435030c, | |
| 5676 | 0x8034c659,0xdb439032,0x2c390034,0xcaaecb24, | |
| 5677 | 0x30832872,0xcb28b1c,0x4b1c32cb,0x70030033, | |
| 5678 | 0x30b0cb0c,0xe40ca00d,0x400d36d0,0xb2c90b0e, | |
| 5679 | 0xca1cb2ab,0xa2c70c20,0x6575d95c,0x4315b5ce, | |
| 5680 | 0x95c53831,0x28034c5d,0x9b705583,0xa1455830, | |
| 5681 | 0xc76cd6c,0x40143085,0x71451c51,0x871430c, | |
| 5682 | 0x450000c3,0xd3071451,0x1560ca00,0x560c26dc, | |
| 5683 | 0xb35b2851,0xc914369,0x1a14500d,0x46593945, | |
| 5684 | 0xcb2c939,0x94507503,0x328034c3,0x9b70558, | |
| 5685 | 0xe41c5583,0x72caaeca,0x1c308510,0xc7147287, | |
| 5686 | 0x50871c32,0x1470030c,0xd307147,0xc1560ca0, | |
| 5687 | 0x1560c26d,0xabb2b907,0x21441cb2,0x38a1c70c, | |
| 5688 | 0x8e657394,0x314b1c93,0x39438738,0x43083081, | |
| 5689 | 0x31c22432,0x820c510,0x830d7082,0x50c35c33, | |
| 5690 | 0xc30c338,0xc31c30c3,0x50c30c30,0xc204514, | |
| 5691 | 0x890c90c2,0x31440c70,0xa8208208,0xea0df0c3, | |
| 5692 | 0x8a231430,0xa28a28a2,0x28a28a1e,0x1861868a, | |
| 5693 | 0x48308308,0xc3682483,0x14516453,0x4d965845, | |
| 5694 | 0xd4659619,0x36590d94,0xd969964,0x546590d9, | |
| 5695 | 0x20c20541,0x920d20c,0x5914f0da,0x96114514, | |
| 5696 | 0x65865365,0xe89d3519,0x99e7a279,0x9e89e89e, | |
| 5697 | 0x81821827,0xb2032430,0x18b28920,0x422492c7, | |
| 5698 | 0xb28b0d72,0x3872c35c,0xc30d72cb,0x32cb2972, | |
| 5699 | 0x1c75cb0c,0xc90c204e,0xa2482c80,0x24b1c62c, | |
| 5700 | 0xc3a89089,0xb0ea2e42,0x9669a31c,0xa4966a28, | |
| 5701 | 0x59a8a269,0x8175e7a,0xb203243,0x718b2892, | |
| 5702 | 0x4114105c,0x17597658,0x74ce5d96,0x5c36572d, | |
| 5703 | 0xd92d7297,0xe1ce5d70,0xc90c204,0xca2482c8, | |
| 5704 | 0x4171c62,0x5d961045,0x976585d6,0x79669533, | |
| 5705 | 0x964965a2,0x659689e6,0x308175e7,0x24510510, | |
| 5706 | 0x451031c2,0xe2420841,0x5c338714,0x453851c3, | |
| 5707 | 0x51c51451,0xc30c31c,0x451450c7,0x41440c20, | |
| 5708 | 0xc708914,0x82105144,0xf1c58c90,0x1470ea0d, | |
| 5709 | 0x61861863,0x8a1e85e8,0x8687a8a2,0x3081861, | |
| 5710 | 0x24853c51,0x5053c368,0x1341144f,0x96194ce5, | |
| 5711 | 0x1544d439,0x94385514,0xe0d90d96,0x5415464, | |
| 5712 | 0x4f1440c2,0xf0da0921,0x4513d414,0x533944d0, | |
| 5713 | 0x350e6586,0x86082181,0xe89e981d,0x18277689, | |
| 5714 | 0x10308182,0x89207185,0x41c718b2,0x14e24224, | |
| 5715 | 0xc35cb287,0xe1c73871,0x28e1c514,0xc70c32cb, | |
| 5716 | 0x204e1c75,0x1c61440c,0xc62ca248,0x90891071, | |
| 5717 | 0x2e41c58c,0xa31c70ea,0xe86175e7,0xa269a475, | |
| 5718 | 0x5e7a57a8,0x51030817,0x28920718,0xf38718b, | |
| 5719 | 0xe5134114,0x39961758,0xe1ce4ce,0x728e3855, | |
| 5720 | 0x5ce0d92d,0xc204e1ce,0x81c61440,0x1c62ca24, | |
| 5721 | 0xd04503ce,0x85d63944,0x75338e65,0x5d86075e, | |
| 5722 | 0x89e69647,0x75e76576, | |
| 5723 | ]); | |
| 5724 | Lev2TParametricDescription.prototype.offsetIncrs5 = /*3 bits per value */ new Int32Array([ | |
| 5725 | 0x10000000,0xc00000,0x60061,0x400, | |
| 5726 | 0x0,0x60000008,0x6b003080,0xdb6ab6db, | |
| 5727 | 0x2db6,0x800400,0x49245240,0x11482412, | |
| 5728 | 0x104904,0x40020000,0x92292000,0xa4b25924, | |
| 5729 | 0x9649658,0xd80c000,0xdb0c001b,0x80db6d86, | |
| 5730 | 0x6db01b6d,0xc0600003,0x86000d86,0x6db6c36d, | |
| 5731 | 0xddadb6ed,0x300001b6,0x6c360,0xe37236e4, | |
| 5732 | 0x46db6236,0xdb6c,0x361b018,0xb91b7200, | |
| 5733 | 0x6dbb1b71,0x6db763,0x20100820,0x61248001, | |
| 5734 | 0x92492490,0x24820004,0x8041000,0x92400090, | |
| 5735 | 0x24924830,0x555b6a49,0x2080012,0x20004804, | |
| 5736 | 0x49252449,0x84112492,0x4000928,0x240201, | |
| 5737 | 0x92922490,0x58924924,0x49456,0x120d8082, | |
| 5738 | 0x6da4800,0x69249249,0x249a01b,0x6c04100, | |
| 5739 | 0x6d240009,0x92492483,0x24d5adb4,0x60208001, | |
| 5740 | 0x92000483,0x24925236,0x6846da49,0x10400092, | |
| 5741 | 0x241b0,0x49291b49,0x636d2492,0x92494935, | |
| 5742 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 5743 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 5744 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 5745 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 5746 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 5747 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 5748 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 5749 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 5750 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 5751 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 5752 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 5753 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 5754 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 5755 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 5756 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 5757 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 5758 | 0x49249249,0x92492492,0x24924924, | |
| 5759 | ]); | |
| 5760 | ||
| 5761 | class Lev1TParametricDescription extends ParametricDescription { | |
| 5762 | /** | |
| 5763 | * @param {number} absState | |
| 5764 | * @param {number} position | |
| 5765 | * @param {number} vector | |
| 5766 | * @returns {number} | |
| 5767 | */ | |
| 5768 | transition(absState, position, vector) { | |
| 5769 | let state = Math.floor(absState / (this.w + 1)); | |
| 5770 | let offset = absState % (this.w + 1); | |
| 5771 | ||
| 5772 | if (position === this.w) { | |
| 5773 | if (state < 2) { // eslint-disable-line no-lonely-if | |
| 5774 | const loc = Math.imul(vector, 2) + state; | |
| 5775 | offset += this.unpack(this.offsetIncrs0, loc, 1); | |
| 5776 | state = this.unpack(this.toStates0, loc, 2) - 1; | |
| 5777 | } | |
| 5778 | } else if (position === this.w - 1) { | |
| 5779 | if (state < 3) { // eslint-disable-line no-lonely-if | |
| 5780 | const loc = Math.imul(vector, 3) + state; | |
| 5781 | offset += this.unpack(this.offsetIncrs1, loc, 1); | |
| 5782 | state = this.unpack(this.toStates1, loc, 2) - 1; | |
| 5783 | } | |
| 5784 | } else if (position === this.w - 2) { | |
| 5785 | if (state < 6) { // eslint-disable-line no-lonely-if | |
| 5786 | const loc = Math.imul(vector, 6) + state; | |
| 5787 | offset += this.unpack(this.offsetIncrs2, loc, 2); | |
| 5788 | state = this.unpack(this.toStates2, loc, 3) - 1; | |
| 5231 | if (typeof window !== "undefined") { | |
| 5232 | const ROOT_PATH = window.rootPath; | |
| 5233 | /** @type {stringdex.Callbacks|null} */ | |
| 5234 | let databaseCallbacks = null; | |
| 5235 | initSearch(window.Stringdex, window.RoaringBitmap, { | |
| 5236 | loadRoot: callbacks => { | |
| 5237 | for (const key in callbacks) { | |
| 5238 | if (Object.hasOwn(callbacks, key)) { | |
| 5239 | // @ts-ignore | |
| 5240 | window[key] = callbacks[key]; | |
| 5241 | } | |
| 5789 | 5242 | } |
| 5790 | } else { | |
| 5791 | if (state < 6) { // eslint-disable-line no-lonely-if | |
| 5792 | const loc = Math.imul(vector, 6) + state; | |
| 5793 | offset += this.unpack(this.offsetIncrs3, loc, 2); | |
| 5794 | state = this.unpack(this.toStates3, loc, 3) - 1; | |
| 5243 | databaseCallbacks = callbacks; | |
| 5244 | // search.index/root is loaded by main.js, so | |
| 5245 | // this script doesn't need to launch it, but | |
| 5246 | // must pick it up | |
| 5247 | // @ts-ignore | |
| 5248 | if (window.searchIndex) { | |
| 5249 | // @ts-ignore | |
| 5250 | window.rr_(window.searchIndex); | |
| 5795 | 5251 | } |
| 5796 | } | |
| 5797 | ||
| 5798 | if (state === -1) { | |
| 5799 | // null state | |
| 5800 | return -1; | |
| 5801 | } else { | |
| 5802 | // translate back to abs | |
| 5803 | return Math.imul(state, this.w + 1) + offset; | |
| 5804 | } | |
| 5805 | } | |
| 5806 | ||
| 5807 | // state map | |
| 5808 | // 0 -> [(0, 0)] | |
| 5809 | // 1 -> [(0, 1)] | |
| 5810 | // 2 -> [(0, 1), (1, 1)] | |
| 5811 | // 3 -> [(0, 1), (1, 1), (2, 1)] | |
| 5812 | // 4 -> [(0, 1), (2, 1)] | |
| 5813 | // 5 -> [t(0, 1), (0, 1), (1, 1), (2, 1)] | |
| 5814 | ||
| 5815 | ||
| 5816 | /** @param {number} w - length of word being checked */ | |
| 5817 | constructor(w) { | |
| 5818 | super(w, 1, new Int32Array([0,1,0,-1,-1,-1])); | |
| 5819 | } | |
| 5820 | } | |
| 5821 | ||
| 5822 | Lev1TParametricDescription.prototype.toStates0 = /*2 bits per value */ new Int32Array([ | |
| 5823 | 0x2, | |
| 5824 | ]); | |
| 5825 | Lev1TParametricDescription.prototype.offsetIncrs0 = /*1 bits per value */ new Int32Array([ | |
| 5826 | 0x0, | |
| 5827 | ]); | |
| 5828 | ||
| 5829 | Lev1TParametricDescription.prototype.toStates1 = /*2 bits per value */ new Int32Array([ | |
| 5830 | 0xa43, | |
| 5831 | ]); | |
| 5832 | Lev1TParametricDescription.prototype.offsetIncrs1 = /*1 bits per value */ new Int32Array([ | |
| 5833 | 0x38, | |
| 5834 | ]); | |
| 5835 | ||
| 5836 | Lev1TParametricDescription.prototype.toStates2 = /*3 bits per value */ new Int32Array([ | |
| 5837 | 0x12180003,0xb45a4914,0x69, | |
| 5838 | ]); | |
| 5839 | Lev1TParametricDescription.prototype.offsetIncrs2 = /*2 bits per value */ new Int32Array([ | |
| 5840 | 0x558a0000,0x5555, | |
| 5841 | ]); | |
| 5842 | ||
| 5843 | Lev1TParametricDescription.prototype.toStates3 = /*3 bits per value */ new Int32Array([ | |
| 5844 | 0x900c0003,0xa1904864,0x45a49169,0x5a6d196a, | |
| 5845 | 0x9634, | |
| 5846 | ]); | |
| 5847 | Lev1TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new Int32Array([ | |
| 5848 | 0xa0fc0000,0x5555ba08,0x55555555, | |
| 5849 | ]); | |
| 5850 | ||
| 5851 | // ==================== | |
| 5852 | // WARNING: Nothing should be added below this comment: we need the `initSearch` function to | |
| 5853 | // be called ONLY when the whole file has been parsed and loaded. | |
| 5854 | ||
| 5855 | // @ts-expect-error | |
| 5856 | function initSearch(searchIndex) { | |
| 5857 | rawSearchIndex = searchIndex; | |
| 5858 | if (typeof window !== "undefined") { | |
| 5859 | // @ts-expect-error | |
| 5860 | docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); | |
| 5861 | registerSearchEvents(); | |
| 5862 | // If there's a search term in the URL, execute the search now. | |
| 5863 | if (window.searchState.getQueryStringParams().search) { | |
| 5864 | search(); | |
| 5865 | } | |
| 5866 | } else if (typeof exports !== "undefined") { | |
| 5867 | // @ts-expect-error | |
| 5868 | docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); | |
| 5869 | exports.docSearch = docSearch; | |
| 5870 | exports.parseQuery = DocSearch.parseQuery; | |
| 5871 | } | |
| 5872 | } | |
| 5873 | ||
| 5874 | if (typeof exports !== "undefined") { | |
| 5252 | }, | |
| 5253 | loadTreeByHash: hashHex => { | |
| 5254 | const script = document.createElement("script"); | |
| 5255 | script.src = `${ROOT_PATH}/search.index/${hashHex}.js`; | |
| 5256 | script.onerror = e => { | |
| 5257 | if (databaseCallbacks) { | |
| 5258 | databaseCallbacks.err_rn_(hashHex, e); | |
| 5259 | } | |
| 5260 | }; | |
| 5261 | document.documentElement.appendChild(script); | |
| 5262 | }, | |
| 5263 | loadDataByNameAndHash: (name, hashHex) => { | |
| 5264 | const script = document.createElement("script"); | |
| 5265 | script.src = `${ROOT_PATH}/search.index/${name}/${hashHex}.js`; | |
| 5266 | script.onerror = e => { | |
| 5267 | if (databaseCallbacks) { | |
| 5268 | databaseCallbacks.err_rd_(hashHex, e); | |
| 5269 | } | |
| 5270 | }; | |
| 5271 | document.documentElement.appendChild(script); | |
| 5272 | }, | |
| 5273 | }); | |
| 5274 | } else if (typeof exports !== "undefined") { | |
| 5275 | // eslint-disable-next-line no-undef | |
| 5875 | 5276 | exports.initSearch = initSearch; |
| 5876 | 5277 | } |
| 5877 | ||
| 5878 | if (typeof window !== "undefined") { | |
| 5879 | // @ts-expect-error | |
| 5880 | window.initSearch = initSearch; | |
| 5881 | // @ts-expect-error | |
| 5882 | if (window.searchIndex !== undefined) { | |
| 5883 | // @ts-expect-error | |
| 5884 | initSearch(window.searchIndex); | |
| 5885 | } | |
| 5886 | } else { | |
| 5887 | // Running in Node, not a browser. Run initSearch just to produce the | |
| 5888 | // exports. | |
| 5889 | initSearch(new Map()); | |
| 5890 | } |
src/librustdoc/html/static/js/settings.js+42-44| ... | ... | @@ -1,25 +1,13 @@ |
| 1 | 1 | // Local js definitions: |
| 2 | 2 | /* global getSettingValue, updateLocalStorage, updateTheme */ |
| 3 | 3 | /* global addClass, removeClass, onEach, onEachLazy */ |
| 4 | /* global MAIN_ID, getVar, getSettingsButton, getHelpButton, nonnull */ | |
| 4 | /* global MAIN_ID, getVar, nonnull */ | |
| 5 | 5 | |
| 6 | 6 | "use strict"; |
| 7 | 7 | |
| 8 | 8 | (function() { |
| 9 | 9 | const isSettingsPage = window.location.pathname.endsWith("/settings.html"); |
| 10 | 10 | |
| 11 | /** | |
| 12 | * @param {Element} elem | |
| 13 | * @param {EventTarget|null} target | |
| 14 | */ | |
| 15 | function elemContainsTarget(elem, target) { | |
| 16 | if (target instanceof Node) { | |
| 17 | return elem.contains(target); | |
| 18 | } else { | |
| 19 | return false; | |
| 20 | } | |
| 21 | } | |
| 22 | ||
| 23 | 11 | /** |
| 24 | 12 | * @overload {"theme"|"preferred-dark-theme"|"preferred-light-theme"} |
| 25 | 13 | * @param {string} settingName |
| ... | ... | @@ -305,10 +293,12 @@ |
| 305 | 293 | } |
| 306 | 294 | } else { |
| 307 | 295 | el.setAttribute("tabindex", "-1"); |
| 308 | const settingsBtn = getSettingsButton(); | |
| 309 | if (settingsBtn !== null) { | |
| 310 | settingsBtn.appendChild(el); | |
| 311 | } | |
| 296 | onEachLazy(document.querySelectorAll(".settings-menu"), menu => { | |
| 297 | if (menu.offsetWidth !== 0) { | |
| 298 | menu.appendChild(el); | |
| 299 | return true; | |
| 300 | } | |
| 301 | }); | |
| 312 | 302 | } |
| 313 | 303 | return el; |
| 314 | 304 | } |
| ... | ... | @@ -317,6 +307,15 @@ |
| 317 | 307 | |
| 318 | 308 | function displaySettings() { |
| 319 | 309 | settingsMenu.style.display = ""; |
| 310 | onEachLazy(document.querySelectorAll(".settings-menu"), menu => { | |
| 311 | if (menu.offsetWidth !== 0) { | |
| 312 | if (!menu.contains(settingsMenu) && settingsMenu.parentElement) { | |
| 313 | settingsMenu.parentElement.removeChild(settingsMenu); | |
| 314 | menu.appendChild(settingsMenu); | |
| 315 | } | |
| 316 | return true; | |
| 317 | } | |
| 318 | }); | |
| 320 | 319 | onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"), el => { |
| 321 | 320 | const val = getSettingValue(el.id); |
| 322 | 321 | const checked = val === "true"; |
| ... | ... | @@ -330,40 +329,37 @@ |
| 330 | 329 | * @param {FocusEvent} event |
| 331 | 330 | */ |
| 332 | 331 | function settingsBlurHandler(event) { |
| 333 | const helpBtn = getHelpButton(); | |
| 334 | const settingsBtn = getSettingsButton(); | |
| 335 | const helpUnfocused = helpBtn === null || | |
| 336 | (!helpBtn.contains(document.activeElement) && | |
| 337 | !elemContainsTarget(helpBtn, event.relatedTarget)); | |
| 338 | const settingsUnfocused = settingsBtn === null || | |
| 339 | (!settingsBtn.contains(document.activeElement) && | |
| 340 | !elemContainsTarget(settingsBtn, event.relatedTarget)); | |
| 341 | if (helpUnfocused && settingsUnfocused) { | |
| 332 | const isInPopover = onEachLazy( | |
| 333 | document.querySelectorAll(".settings-menu, .help-menu"), | |
| 334 | menu => { | |
| 335 | return menu.contains(document.activeElement) || menu.contains(event.relatedTarget); | |
| 336 | }, | |
| 337 | ); | |
| 338 | if (!isInPopover) { | |
| 342 | 339 | window.hidePopoverMenus(); |
| 343 | 340 | } |
| 344 | 341 | } |
| 345 | 342 | |
| 346 | 343 | if (!isSettingsPage) { |
| 347 | 344 | // We replace the existing "onclick" callback. |
| 348 | // These elements must exist, as (outside of the settings page) | |
| 349 | // `settings.js` is only loaded after the settings button is clicked. | |
| 350 | const settingsButton = nonnull(getSettingsButton()); | |
| 351 | 345 | const settingsMenu = nonnull(document.getElementById("settings")); |
| 352 | settingsButton.onclick = event => { | |
| 353 | if (elemContainsTarget(settingsMenu, event.target)) { | |
| 354 | return; | |
| 355 | } | |
| 356 | event.preventDefault(); | |
| 357 | const shouldDisplaySettings = settingsMenu.style.display === "none"; | |
| 346 | onEachLazy(document.querySelectorAll(".settings-menu"), settingsButton => { | |
| 347 | /** @param {MouseEvent} event */ | |
| 348 | settingsButton.querySelector("a").onclick = event => { | |
| 349 | if (!(event.target instanceof Element) || settingsMenu.contains(event.target)) { | |
| 350 | return; | |
| 351 | } | |
| 352 | event.preventDefault(); | |
| 353 | const shouldDisplaySettings = settingsMenu.style.display === "none"; | |
| 358 | 354 | |
| 359 | window.hideAllModals(false); | |
| 360 | if (shouldDisplaySettings) { | |
| 361 | displaySettings(); | |
| 362 | } | |
| 363 | }; | |
| 364 | settingsButton.onblur = settingsBlurHandler; | |
| 365 | // the settings button should always have a link in it | |
| 366 | nonnull(settingsButton.querySelector("a")).onblur = settingsBlurHandler; | |
| 355 | window.hideAllModals(false); | |
| 356 | if (shouldDisplaySettings) { | |
| 357 | displaySettings(); | |
| 358 | } | |
| 359 | }; | |
| 360 | settingsButton.onblur = settingsBlurHandler; | |
| 361 | settingsButton.querySelector("a").onblur = settingsBlurHandler; | |
| 362 | }); | |
| 367 | 363 | onEachLazy(settingsMenu.querySelectorAll("input"), el => { |
| 368 | 364 | el.onblur = settingsBlurHandler; |
| 369 | 365 | }); |
| ... | ... | @@ -377,6 +373,8 @@ |
| 377 | 373 | if (!isSettingsPage) { |
| 378 | 374 | displaySettings(); |
| 379 | 375 | } |
| 380 | removeClass(getSettingsButton(), "rotate"); | |
| 376 | onEachLazy(document.querySelectorAll(".settings-menu"), settingsButton => { | |
| 377 | removeClass(settingsButton, "rotate"); | |
| 378 | }); | |
| 381 | 379 | }, 0); |
| 382 | 380 | })(); |
src/librustdoc/html/static/js/storage.js+37-30| ... | ... | @@ -7,6 +7,7 @@ |
| 7 | 7 | |
| 8 | 8 | /** |
| 9 | 9 | * @import * as rustdoc from "./rustdoc.d.ts"; |
| 10 | * @import * as stringdex from "./stringdex.d.ts"; | |
| 10 | 11 | */ |
| 11 | 12 | |
| 12 | 13 | const builtinThemes = ["light", "dark", "ayu"]; |
| ... | ... | @@ -172,7 +173,7 @@ function updateLocalStorage(name, value) { |
| 172 | 173 | } else { |
| 173 | 174 | window.localStorage.setItem("rustdoc-" + name, value); |
| 174 | 175 | } |
| 175 | } catch (e) { | |
| 176 | } catch { | |
| 176 | 177 | // localStorage is not accessible, do nothing |
| 177 | 178 | } |
| 178 | 179 | } |
| ... | ... | @@ -189,7 +190,7 @@ function updateLocalStorage(name, value) { |
| 189 | 190 | function getCurrentValue(name) { |
| 190 | 191 | try { |
| 191 | 192 | return window.localStorage.getItem("rustdoc-" + name); |
| 192 | } catch (e) { | |
| 193 | } catch { | |
| 193 | 194 | return null; |
| 194 | 195 | } |
| 195 | 196 | } |
| ... | ... | @@ -375,32 +376,6 @@ window.addEventListener("pageshow", ev => { |
| 375 | 376 | // That's also why this is in storage.js and not main.js. |
| 376 | 377 | // |
| 377 | 378 | // [parser]: https://html.spec.whatwg.org/multipage/parsing.html |
| 378 | class RustdocSearchElement extends HTMLElement { | |
| 379 | constructor() { | |
| 380 | super(); | |
| 381 | } | |
| 382 | connectedCallback() { | |
| 383 | const rootPath = getVar("root-path"); | |
| 384 | const currentCrate = getVar("current-crate"); | |
| 385 | this.innerHTML = `<nav class="sub"> | |
| 386 | <form class="search-form"> | |
| 387 | <span></span> <!-- This empty span is a hacky fix for Safari - See #93184 --> | |
| 388 | <div id="sidebar-button" tabindex="-1"> | |
| 389 | <a href="${rootPath}${currentCrate}/all.html" title="show sidebar"></a> | |
| 390 | </div> | |
| 391 | <input | |
| 392 | class="search-input" | |
| 393 | name="search" | |
| 394 | aria-label="Run search in the documentation" | |
| 395 | autocomplete="off" | |
| 396 | spellcheck="false" | |
| 397 | placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…" | |
| 398 | type="search"> | |
| 399 | </form> | |
| 400 | </nav>`; | |
| 401 | } | |
| 402 | } | |
| 403 | window.customElements.define("rustdoc-search", RustdocSearchElement); | |
| 404 | 379 | class RustdocToolbarElement extends HTMLElement { |
| 405 | 380 | constructor() { |
| 406 | 381 | super(); |
| ... | ... | @@ -411,11 +386,15 @@ class RustdocToolbarElement extends HTMLElement { |
| 411 | 386 | return; |
| 412 | 387 | } |
| 413 | 388 | const rootPath = getVar("root-path"); |
| 389 | const currentUrl = window.location.href.split("?")[0].split("#")[0]; | |
| 414 | 390 | this.innerHTML = ` |
| 415 | <div id="settings-menu" tabindex="-1"> | |
| 391 | <div id="search-button" tabindex="-1"> | |
| 392 | <a href="${currentUrl}?search="><span class="label">Search</span></a> | |
| 393 | </div> | |
| 394 | <div class="settings-menu" tabindex="-1"> | |
| 416 | 395 | <a href="${rootPath}settings.html"><span class="label">Settings</span></a> |
| 417 | 396 | </div> |
| 418 | <div id="help-button" tabindex="-1"> | |
| 397 | <div class="help-menu" tabindex="-1"> | |
| 419 | 398 | <a href="${rootPath}help.html"><span class="label">Help</span></a> |
| 420 | 399 | </div> |
| 421 | 400 | <button id="toggle-all-docs" |
| ... | ... | @@ -424,3 +403,31 @@ class="label">Summary</span></button>`; |
| 424 | 403 | } |
| 425 | 404 | } |
| 426 | 405 | window.customElements.define("rustdoc-toolbar", RustdocToolbarElement); |
| 406 | class RustdocTopBarElement extends HTMLElement { | |
| 407 | constructor() { | |
| 408 | super(); | |
| 409 | } | |
| 410 | connectedCallback() { | |
| 411 | const rootPath = getVar("root-path"); | |
| 412 | const tmplt = document.createElement("template"); | |
| 413 | tmplt.innerHTML = ` | |
| 414 | <slot name="sidebar-menu-toggle"></slot> | |
| 415 | <slot></slot> | |
| 416 | <slot name="settings-menu"></slot> | |
| 417 | <slot name="help-menu"></slot> | |
| 418 | `; | |
| 419 | const shadow = this.attachShadow({ mode: "open" }); | |
| 420 | shadow.appendChild(tmplt.content.cloneNode(true)); | |
| 421 | this.innerHTML += ` | |
| 422 | <button class="sidebar-menu-toggle" slot="sidebar-menu-toggle" title="show sidebar"> | |
| 423 | </button> | |
| 424 | <div class="settings-menu" slot="settings-menu" tabindex="-1"> | |
| 425 | <a href="${rootPath}settings.html"><span class="label">Settings</span></a> | |
| 426 | </div> | |
| 427 | <div class="help-menu" slot="help-menu" tabindex="-1"> | |
| 428 | <a href="${rootPath}help.html"><span class="label">Help</span></a> | |
| 429 | </div> | |
| 430 | `; | |
| 431 | } | |
| 432 | } | |
| 433 | window.customElements.define("rustdoc-topbar", RustdocTopBarElement); |
src/librustdoc/html/static/js/stringdex.d.ts created+165| ... | ... | @@ -0,0 +1,165 @@ |
| 1 | export = stringdex; | |
| 2 | ||
| 3 | declare namespace stringdex { | |
| 4 | /** | |
| 5 | * The client interface to Stringdex. | |
| 6 | */ | |
| 7 | interface Database { | |
| 8 | getIndex(colname: string): SearchTree|undefined; | |
| 9 | getData(colname: string): DataColumn|undefined; | |
| 10 | } | |
| 11 | /** | |
| 12 | * A search index file. | |
| 13 | */ | |
| 14 | interface SearchTree { | |
| 15 | trie(): Trie; | |
| 16 | search(name: Uint8Array|string): Promise<Trie?>; | |
| 17 | searchLev(name: Uint8Array|string): AsyncGenerator<Trie>; | |
| 18 | } | |
| 19 | /** | |
| 20 | * A compressed node in the search tree. | |
| 21 | * | |
| 22 | * This object logically addresses two interleaved trees: | |
| 23 | * a "prefix tree", and a "suffix tree". If you ask for | |
| 24 | * generic matches, you get both, but if you ask for one | |
| 25 | * that excludes suffix-only entries, you'll get prefixes | |
| 26 | * alone. | |
| 27 | */ | |
| 28 | interface Trie { | |
| 29 | matches(): RoaringBitmap; | |
| 30 | substringMatches(): AsyncGenerator<RoaringBitmap>; | |
| 31 | prefixMatches(): AsyncGenerator<RoaringBitmap>; | |
| 32 | keys(): Uint8Array; | |
| 33 | keysExcludeSuffixOnly(): Uint8Array; | |
| 34 | children(): [number, Promise<Trie>][]; | |
| 35 | childrenExcludeSuffixOnly(): [number, Promise<Trie>][]; | |
| 36 | child(id: number): Promise<Trie>?; | |
| 37 | } | |
| 38 | /** | |
| 39 | * The client interface to Stringdex. | |
| 40 | */ | |
| 41 | interface DataColumn { | |
| 42 | isEmpty(id: number): boolean; | |
| 43 | at(id: number): Promise<Uint8Array|undefined>; | |
| 44 | length: number, | |
| 45 | } | |
| 46 | /** | |
| 47 | * Callbacks for a host application and VFS backend. | |
| 48 | * | |
| 49 | * These functions are calleb with mostly-raw data, | |
| 50 | * except the JSONP wrapper is removed. For example, | |
| 51 | * a file with the contents `rr_('{"A":"B"}')` should, | |
| 52 | * after being pulled in, result in the `rr_` callback | |
| 53 | * being invoked. | |
| 54 | * | |
| 55 | * The success callbacks don't need to supply the name of | |
| 56 | * the file that succeeded, but, if you want successful error | |
| 57 | * reporting, you'll need to remember which files are | |
| 58 | * in flight and report the filename as the first parameter. | |
| 59 | */ | |
| 60 | interface Callbacks { | |
| 61 | /** | |
| 62 | * Load the root of the search database | |
| 63 | * @param {string} dataString | |
| 64 | */ | |
| 65 | rr_: function(string); | |
| 66 | err_rr_: function(any); | |
| 67 | /** | |
| 68 | * Load a nodefile in the search tree. | |
| 69 | * A node file may contain multiple nodes; | |
| 70 | * each node has five fields, separated by newlines. | |
| 71 | * @param {string} inputBase64 | |
| 72 | */ | |
| 73 | rn_: function(string); | |
| 74 | err_rn_: function(string, any); | |
| 75 | /** | |
| 76 | * Load a database column partition from a string | |
| 77 | * @param {string} dataString | |
| 78 | */ | |
| 79 | rd_: function(string); | |
| 80 | err_rd_: function(string, any); | |
| 81 | /** | |
| 82 | * Load a database column partition from base64 | |
| 83 | * @param {string} dataString | |
| 84 | */ | |
| 85 | rb_: function(string); | |
| 86 | err_rb_: function(string, any); | |
| 87 | }; | |
| 88 | /** | |
| 89 | * Hooks that a VFS layer must provide for stringdex to load data. | |
| 90 | * | |
| 91 | * When the root is loaded, the Callbacks object is provided. These | |
| 92 | * functions should result in callback functions being called with | |
| 93 | * the contents of the file, or in error callbacks being invoked with | |
| 94 | * the failed-to-load filename. | |
| 95 | */ | |
| 96 | interface Hooks { | |
| 97 | /** | |
| 98 | * The first function invoked as part of loading a search database. | |
| 99 | * This function must, eventually, invoke `rr_` with the string | |
| 100 | * representation of the root file (the function call wrapper, | |
| 101 | * `rr_('` and `')`, must be removed). | |
| 102 | * | |
| 103 | * The supplied callbacks object is used to feed search data back | |
| 104 | * to the search engine core. You have to store it, so that | |
| 105 | * loadTreeByHash and loadDataByNameAndHash can use it. | |
| 106 | * | |
| 107 | * If this fails, either throw an exception, or call `err_rr_` | |
| 108 | * with the error object. | |
| 109 | */ | |
| 110 | loadRoot: function(Callbacks); | |
| 111 | /** | |
| 112 | * Load a subtree file from the search index. | |
| 113 | * | |
| 114 | * If this function succeeds, call `rn_` on the callbacks | |
| 115 | * object. If it fails, call `err_rn_(hashHex, error)`. | |
| 116 | * | |
| 117 | * @param {string} hashHex | |
| 118 | */ | |
| 119 | loadTreeByHash: function(string); | |
| 120 | /** | |
| 121 | * Load a column partition from the search database. | |
| 122 | * | |
| 123 | * If this function succeeds, call `rd_` or `rb_` on the callbacks | |
| 124 | * object. If it fails, call `err_rd_(hashHex, error)`. or `err_rb_`. | |
| 125 | * To determine which one, the wrapping function call in the js file | |
| 126 | * specifies it. | |
| 127 | * | |
| 128 | * @param {string} columnName | |
| 129 | * @param {string} hashHex | |
| 130 | */ | |
| 131 | loadDataByNameAndHash: function(string, string); | |
| 132 | }; | |
| 133 | class RoaringBitmap { | |
| 134 | constructor(array: Uint8Array|null, start?: number); | |
| 135 | static makeSingleton(number: number); | |
| 136 | static everything(): RoaringBitmap; | |
| 137 | static empty(): RoaringBitmap; | |
| 138 | isEmpty(): boolean; | |
| 139 | union(that: RoaringBitmap): RoaringBitmap; | |
| 140 | intersection(that: RoaringBitmap): RoaringBitmap; | |
| 141 | contains(number: number): boolean; | |
| 142 | entries(): Generator<number>; | |
| 143 | first(): number|null; | |
| 144 | consumed_len_bytes: number; | |
| 145 | }; | |
| 146 | ||
| 147 | type Stringdex = { | |
| 148 | /** | |
| 149 | * Initialize Stringdex with VFS hooks. | |
| 150 | * Returns a database that you can use. | |
| 151 | */ | |
| 152 | loadDatabase: function(Hooks): Promise<Database>, | |
| 153 | }; | |
| 154 | ||
| 155 | const Stringdex: Stringdex; | |
| 156 | const RoaringBitmap: Class<stringdex.RoaringBitmap>; | |
| 157 | } | |
| 158 | ||
| 159 | declare global { | |
| 160 | interface Window { | |
| 161 | Stringdex: stringdex.Stringdex; | |
| 162 | RoaringBitmap: Class<stringdex.RoaringBitmap>; | |
| 163 | StringdexOnload: Array<function(stringdex.Stringdex): any>?; | |
| 164 | }; | |
| 165 | } | |
| \ No newline at end of file |
src/librustdoc/html/static/js/stringdex.js created+3217| ... | ... | @@ -0,0 +1,3217 @@ |
| 1 | /** | |
| 2 | * @import * as stringdex from "./stringdex.d.ts" | |
| 3 | */ | |
| 4 | ||
| 5 | const EMPTY_UINT8 = new Uint8Array(); | |
| 6 | ||
| 7 | /** | |
| 8 | * @property {Uint8Array} keysAndCardinalities | |
| 9 | * @property {Uint8Array[]} containers | |
| 10 | */ | |
| 11 | class RoaringBitmap { | |
| 12 | /** | |
| 13 | * @param {Uint8Array|null} u8array | |
| 14 | * @param {number} [startingOffset] | |
| 15 | */ | |
| 16 | constructor(u8array, startingOffset) { | |
| 17 | const start = startingOffset ? startingOffset : 0; | |
| 18 | let i = start; | |
| 19 | /** @type {Uint8Array} */ | |
| 20 | this.keysAndCardinalities = EMPTY_UINT8; | |
| 21 | /** @type {(RoaringBitmapArray|RoaringBitmapBits|RoaringBitmapRun)[]} */ | |
| 22 | this.containers = []; | |
| 23 | /** @type {number} */ | |
| 24 | this.consumed_len_bytes = 0; | |
| 25 | if (u8array === null || u8array.length === i || u8array[i] === 0) { | |
| 26 | return this; | |
| 27 | } else if (u8array[i] > 0xf0) { | |
| 28 | // Special representation of tiny sets that are close together | |
| 29 | const lspecial = u8array[i] & 0x0f; | |
| 30 | this.keysAndCardinalities = new Uint8Array(lspecial * 4); | |
| 31 | let pspecial = i + 1; | |
| 32 | let key = u8array[pspecial + 2] | (u8array[pspecial + 3] << 8); | |
| 33 | let value = u8array[pspecial] | (u8array[pspecial + 1] << 8); | |
| 34 | let entry = (key << 16) | value; | |
| 35 | let container; | |
| 36 | container = new RoaringBitmapArray(1, new Uint8Array(4)); | |
| 37 | container.array[0] = value & 0xFF; | |
| 38 | container.array[1] = (value >> 8) & 0xFF; | |
| 39 | this.containers.push(container); | |
| 40 | this.keysAndCardinalities[0] = key; | |
| 41 | this.keysAndCardinalities[1] = key >> 8; | |
| 42 | pspecial += 4; | |
| 43 | for (let ispecial = 1; ispecial < lspecial; ispecial += 1) { | |
| 44 | entry += u8array[pspecial] | (u8array[pspecial + 1] << 8); | |
| 45 | value = entry & 0xffff; | |
| 46 | key = entry >> 16; | |
| 47 | container = this.addToArrayAt(key); | |
| 48 | const cardinalityOld = container.cardinality; | |
| 49 | container.array[cardinalityOld * 2] = value & 0xFF; | |
| 50 | container.array[(cardinalityOld * 2) + 1] = (value >> 8) & 0xFF; | |
| 51 | container.cardinality = cardinalityOld + 1; | |
| 52 | pspecial += 2; | |
| 53 | } | |
| 54 | this.consumed_len_bytes = pspecial - i; | |
| 55 | return this; | |
| 56 | } else if (u8array[i] < 0x3a) { | |
| 57 | // Special representation of tiny sets with arbitrary 32-bit integers | |
| 58 | const lspecial = u8array[i]; | |
| 59 | this.keysAndCardinalities = new Uint8Array(lspecial * 4); | |
| 60 | let pspecial = i + 1; | |
| 61 | for (let ispecial = 0; ispecial < lspecial; ispecial += 1) { | |
| 62 | const key = u8array[pspecial + 2] | (u8array[pspecial + 3] << 8); | |
| 63 | const value = u8array[pspecial] | (u8array[pspecial + 1] << 8); | |
| 64 | const container = this.addToArrayAt(key); | |
| 65 | const cardinalityOld = container.cardinality; | |
| 66 | container.array[cardinalityOld * 2] = value & 0xFF; | |
| 67 | container.array[(cardinalityOld * 2) + 1] = (value >> 8) & 0xFF; | |
| 68 | container.cardinality = cardinalityOld + 1; | |
| 69 | pspecial += 4; | |
| 70 | } | |
| 71 | this.consumed_len_bytes = pspecial - i; | |
| 72 | return this; | |
| 73 | } | |
| 74 | // https://github.com/RoaringBitmap/RoaringFormatSpec | |
| 75 | // | |
| 76 | // Roaring bitmaps are used for flags that can be kept in their | |
| 77 | // compressed form, even when loaded into memory. This decoder | |
| 78 | // turns the containers into objects, but uses byte array | |
| 79 | // slices of the original format for the data payload. | |
| 80 | const has_runs = u8array[i] === 0x3b; | |
| 81 | if (u8array[i] !== 0x3a && u8array[i] !== 0x3b) { | |
| 82 | throw new Error("not a roaring bitmap: " + u8array[i]); | |
| 83 | } | |
| 84 | const size = has_runs ? | |
| 85 | ((u8array[i + 2] | (u8array[i + 3] << 8)) + 1) : | |
| 86 | ((u8array[i + 4] | (u8array[i + 5] << 8) | | |
| 87 | (u8array[i + 6] << 16) | (u8array[i + 7] << 24))); | |
| 88 | i += has_runs ? 4 : 8; | |
| 89 | let is_run; | |
| 90 | if (has_runs) { | |
| 91 | const is_run_len = (size + 7) >> 3; | |
| 92 | is_run = new Uint8Array(u8array.buffer, i + u8array.byteOffset, is_run_len); | |
| 93 | i += is_run_len; | |
| 94 | } else { | |
| 95 | is_run = EMPTY_UINT8; | |
| 96 | } | |
| 97 | this.keysAndCardinalities = u8array.subarray(i, i + (size * 4)); | |
| 98 | i += size * 4; | |
| 99 | let offsets = null; | |
| 100 | if (!has_runs || size >= 4) { | |
| 101 | offsets = []; | |
| 102 | for (let j = 0; j < size; ++j) { | |
| 103 | offsets.push(u8array[i] | (u8array[i + 1] << 8) | (u8array[i + 2] << 16) | | |
| 104 | (u8array[i + 3] << 24)); | |
| 105 | i += 4; | |
| 106 | } | |
| 107 | } | |
| 108 | for (let j = 0; j < size; ++j) { | |
| 109 | if (offsets && offsets[j] !== i - start) { | |
| 110 | throw new Error(`corrupt bitmap ${j}: ${i - start} / ${offsets[j]}`); | |
| 111 | } | |
| 112 | const cardinality = (this.keysAndCardinalities[(j * 4) + 2] | | |
| 113 | (this.keysAndCardinalities[(j * 4) + 3] << 8)) + 1; | |
| 114 | if (is_run[j >> 3] & (1 << (j & 0x7))) { | |
| 115 | const runcount = (u8array[i] | (u8array[i + 1] << 8)); | |
| 116 | i += 2; | |
| 117 | this.containers.push(new RoaringBitmapRun( | |
| 118 | runcount, | |
| 119 | new Uint8Array(u8array.buffer, i + u8array.byteOffset, runcount * 4), | |
| 120 | )); | |
| 121 | i += runcount * 4; | |
| 122 | } else if (cardinality >= 4096) { | |
| 123 | this.containers.push(new RoaringBitmapBits(new Uint8Array( | |
| 124 | u8array.buffer, | |
| 125 | i + u8array.byteOffset, 8192, | |
| 126 | ))); | |
| 127 | i += 8192; | |
| 128 | } else { | |
| 129 | const end = cardinality * 2; | |
| 130 | this.containers.push(new RoaringBitmapArray( | |
| 131 | cardinality, | |
| 132 | new Uint8Array(u8array.buffer, i + u8array.byteOffset, end), | |
| 133 | )); | |
| 134 | i += end; | |
| 135 | } | |
| 136 | } | |
| 137 | this.consumed_len_bytes = i - start; | |
| 138 | } | |
| 139 | /** | |
| 140 | * @param {number} number | |
| 141 | * @returns {RoaringBitmap} | |
| 142 | */ | |
| 143 | static makeSingleton(number) { | |
| 144 | const result = new RoaringBitmap(null, 0); | |
| 145 | result.keysAndCardinalities = Uint8Array.of( | |
| 146 | (number >> 16), (number >> 24), | |
| 147 | 0, 0, // keysAndCardinalities stores the true cardinality minus 1 | |
| 148 | ); | |
| 149 | result.containers.push(new RoaringBitmapArray( | |
| 150 | 1, | |
| 151 | Uint8Array.of(number, number >> 8), | |
| 152 | )); | |
| 153 | return result; | |
| 154 | } | |
| 155 | /** @returns {RoaringBitmap} */ | |
| 156 | static everything() { | |
| 157 | if (EVERYTHING_BITMAP.isEmpty()) { | |
| 158 | let i = 0; | |
| 159 | const l = 1 << 16; | |
| 160 | const everything_range = new RoaringBitmapRun(1, Uint8Array.of(0, 0, 0xff, 0xff)); | |
| 161 | EVERYTHING_BITMAP.keysAndCardinalities = new Uint8Array(l * 4); | |
| 162 | while (i < l) { | |
| 163 | EVERYTHING_BITMAP.containers.push(everything_range); | |
| 164 | // key | |
| 165 | EVERYTHING_BITMAP.keysAndCardinalities[(i * 4) + 0] = i; | |
| 166 | EVERYTHING_BITMAP.keysAndCardinalities[(i * 4) + 1] = i >> 8; | |
| 167 | // cardinality (minus one) | |
| 168 | EVERYTHING_BITMAP.keysAndCardinalities[(i * 4) + 2] = 0xff; | |
| 169 | EVERYTHING_BITMAP.keysAndCardinalities[(i * 4) + 3] = 0xff; | |
| 170 | i += 1; | |
| 171 | } | |
| 172 | } | |
| 173 | return EVERYTHING_BITMAP; | |
| 174 | } | |
| 175 | /** @returns {RoaringBitmap} */ | |
| 176 | static empty() { | |
| 177 | return EMPTY_BITMAP; | |
| 178 | } | |
| 179 | /** @returns {boolean} */ | |
| 180 | isEmpty() { | |
| 181 | return this.containers.length === 0; | |
| 182 | } | |
| 183 | /** | |
| 184 | * Helper function used when constructing bitmaps from lists. | |
| 185 | * Returns an array container with at least two free byte slots | |
| 186 | * and bumps `this.cardinalities`. | |
| 187 | * @param {number} key | |
| 188 | * @returns {RoaringBitmapArray} | |
| 189 | */ | |
| 190 | addToArrayAt(key) { | |
| 191 | let mid = this.getContainerId(key); | |
| 192 | /** @type {RoaringBitmapArray|RoaringBitmapBits|RoaringBitmapRun} */ | |
| 193 | let container; | |
| 194 | if (mid === -1) { | |
| 195 | container = new RoaringBitmapArray(0, new Uint8Array(2)); | |
| 196 | mid = this.containers.length; | |
| 197 | this.containers.push(container); | |
| 198 | if (mid * 4 > this.keysAndCardinalities.length) { | |
| 199 | const keysAndContainers = new Uint8Array(mid * 8); | |
| 200 | keysAndContainers.set(this.keysAndCardinalities); | |
| 201 | this.keysAndCardinalities = keysAndContainers; | |
| 202 | } | |
| 203 | this.keysAndCardinalities[(mid * 4) + 0] = key; | |
| 204 | this.keysAndCardinalities[(mid * 4) + 1] = key >> 8; | |
| 205 | } else { | |
| 206 | container = this.containers[mid]; | |
| 207 | const cardinalityOld = | |
| 208 | this.keysAndCardinalities[(mid * 4) + 2] | | |
| 209 | (this.keysAndCardinalities[(mid * 4) + 3] << 8); | |
| 210 | const cardinality = cardinalityOld + 1; | |
| 211 | this.keysAndCardinalities[(mid * 4) + 2] = cardinality; | |
| 212 | this.keysAndCardinalities[(mid * 4) + 3] = cardinality >> 8; | |
| 213 | } | |
| 214 | // the logic for handing this number is annoying, because keysAndCardinalities stores | |
| 215 | // the cardinality *minus one*, so that it can count up to 65536 with only two bytes | |
| 216 | // (because empty containers are never stored). | |
| 217 | // | |
| 218 | // So, if this is a new container, the stored cardinality contains `0 0`, which is | |
| 219 | // the proper value of the old cardinality (an imaginary empty container existed). | |
| 220 | // If this is adding to an existing container, then the above `else` branch bumps it | |
| 221 | // by one, leaving us with a proper value of `cardinality - 1`. | |
| 222 | const cardinalityOld = | |
| 223 | this.keysAndCardinalities[(mid * 4) + 2] | | |
| 224 | (this.keysAndCardinalities[(mid * 4) + 3] << 8); | |
| 225 | if (!(container instanceof RoaringBitmapArray) || | |
| 226 | container.array.byteLength < ((cardinalityOld + 1) * 2) | |
| 227 | ) { | |
| 228 | const newBuf = new Uint8Array((cardinalityOld + 1) * 4); | |
| 229 | let idx = 0; | |
| 230 | for (const cvalue of container.values()) { | |
| 231 | newBuf[idx] = cvalue & 0xFF; | |
| 232 | newBuf[idx + 1] = (cvalue >> 8) & 0xFF; | |
| 233 | idx += 2; | |
| 234 | } | |
| 235 | if (container instanceof RoaringBitmapArray) { | |
| 236 | container.cardinality = cardinalityOld; | |
| 237 | container.array = newBuf; | |
| 238 | return container; | |
| 239 | } | |
| 240 | const newcontainer = new RoaringBitmapArray(cardinalityOld, newBuf); | |
| 241 | this.containers[mid] = newcontainer; | |
| 242 | return newcontainer; | |
| 243 | } else { | |
| 244 | return container; | |
| 245 | } | |
| 246 | } | |
| 247 | /** | |
| 248 | * @param {RoaringBitmap} that | |
| 249 | * @returns {RoaringBitmap} | |
| 250 | */ | |
| 251 | union(that) { | |
| 252 | if (this.isEmpty()) { | |
| 253 | return that; | |
| 254 | } | |
| 255 | if (that.isEmpty()) { | |
| 256 | return this; | |
| 257 | } | |
| 258 | if (this === RoaringBitmap.everything() || that === RoaringBitmap.everything()) { | |
| 259 | return RoaringBitmap.everything(); | |
| 260 | } | |
| 261 | let i = 0; | |
| 262 | const il = this.containers.length; | |
| 263 | let j = 0; | |
| 264 | const jl = that.containers.length; | |
| 265 | const result = new RoaringBitmap(null, 0); | |
| 266 | result.keysAndCardinalities = new Uint8Array((il + jl) * 4); | |
| 267 | while (i < il || j < jl) { | |
| 268 | const ik = i * 4; | |
| 269 | const jk = j * 4; | |
| 270 | const k = result.containers.length * 4; | |
| 271 | if (j >= jl || (i < il && ( | |
| 272 | (this.keysAndCardinalities[ik + 1] < that.keysAndCardinalities[jk + 1]) || | |
| 273 | (this.keysAndCardinalities[ik + 1] === that.keysAndCardinalities[jk + 1] && | |
| 274 | this.keysAndCardinalities[ik] < that.keysAndCardinalities[jk]) | |
| 275 | ))) { | |
| 276 | result.keysAndCardinalities[k + 0] = this.keysAndCardinalities[ik + 0]; | |
| 277 | result.keysAndCardinalities[k + 1] = this.keysAndCardinalities[ik + 1]; | |
| 278 | result.keysAndCardinalities[k + 2] = this.keysAndCardinalities[ik + 2]; | |
| 279 | result.keysAndCardinalities[k + 3] = this.keysAndCardinalities[ik + 3]; | |
| 280 | result.containers.push(this.containers[i]); | |
| 281 | i += 1; | |
| 282 | } else if (i >= il || (j < jl && ( | |
| 283 | (that.keysAndCardinalities[jk + 1] < this.keysAndCardinalities[ik + 1]) || | |
| 284 | (that.keysAndCardinalities[jk + 1] === this.keysAndCardinalities[ik + 1] && | |
| 285 | that.keysAndCardinalities[jk] < this.keysAndCardinalities[ik]) | |
| 286 | ))) { | |
| 287 | result.keysAndCardinalities[k + 0] = that.keysAndCardinalities[jk + 0]; | |
| 288 | result.keysAndCardinalities[k + 1] = that.keysAndCardinalities[jk + 1]; | |
| 289 | result.keysAndCardinalities[k + 2] = that.keysAndCardinalities[jk + 2]; | |
| 290 | result.keysAndCardinalities[k + 3] = that.keysAndCardinalities[jk + 3]; | |
| 291 | result.containers.push(that.containers[j]); | |
| 292 | j += 1; | |
| 293 | } else { | |
| 294 | // this key is not smaller than that key | |
| 295 | // that key is not smaller than this key | |
| 296 | // they must be equal | |
| 297 | const thisContainer = this.containers[i]; | |
| 298 | const thatContainer = that.containers[j]; | |
| 299 | let card = 0; | |
| 300 | if (thisContainer instanceof RoaringBitmapBits && | |
| 301 | thatContainer instanceof RoaringBitmapBits | |
| 302 | ) { | |
| 303 | const resultArray = new Uint8Array( | |
| 304 | thisContainer.array.length > thatContainer.array.length ? | |
| 305 | thisContainer.array.length : | |
| 306 | thatContainer.array.length, | |
| 307 | ); | |
| 308 | let k = 0; | |
| 309 | const kl = resultArray.length; | |
| 310 | while (k < kl) { | |
| 311 | const c = thisContainer.array[k] | thatContainer.array[k]; | |
| 312 | resultArray[k] = c; | |
| 313 | card += bitCount(c); | |
| 314 | k += 1; | |
| 315 | } | |
| 316 | result.containers.push(new RoaringBitmapBits(resultArray)); | |
| 317 | } else { | |
| 318 | const thisValues = thisContainer.values(); | |
| 319 | const thatValues = thatContainer.values(); | |
| 320 | let thisResult = thisValues.next(); | |
| 321 | let thatResult = thatValues.next(); | |
| 322 | /** @type {Array<number>} */ | |
| 323 | const resultValues = []; | |
| 324 | while (!thatResult.done || !thisResult.done) { | |
| 325 | // generator will definitely implement the iterator protocol correctly | |
| 326 | /** @type {number} */ | |
| 327 | const thisValue = thisResult.value; | |
| 328 | /** @type {number} */ | |
| 329 | const thatValue = thatResult.value; | |
| 330 | if (thatResult.done || thisValue < thatValue) { | |
| 331 | resultValues.push(thisValue); | |
| 332 | thisResult = thisValues.next(); | |
| 333 | } else if (thisResult.done || thatValue < thisValue) { | |
| 334 | resultValues.push(thatValue); | |
| 335 | thatResult = thatValues.next(); | |
| 336 | } else { | |
| 337 | // this value is not smaller than that value | |
| 338 | // that value is not smaller than this value | |
| 339 | // they must be equal | |
| 340 | resultValues.push(thisValue); | |
| 341 | thisResult = thisValues.next(); | |
| 342 | thatResult = thatValues.next(); | |
| 343 | } | |
| 344 | } | |
| 345 | const resultArray = new Uint8Array(resultValues.length * 2); | |
| 346 | let k = 0; | |
| 347 | for (const value of resultValues) { | |
| 348 | // roaring bitmap is little endian | |
| 349 | resultArray[k] = value & 0xFF; | |
| 350 | resultArray[k + 1] = (value >> 8) & 0xFF; | |
| 351 | k += 2; | |
| 352 | } | |
| 353 | result.containers.push(new RoaringBitmapArray( | |
| 354 | resultValues.length, | |
| 355 | resultArray, | |
| 356 | )); | |
| 357 | card = resultValues.length; | |
| 358 | } | |
| 359 | result.keysAndCardinalities[k + 0] = this.keysAndCardinalities[ik + 0]; | |
| 360 | result.keysAndCardinalities[k + 1] = this.keysAndCardinalities[ik + 1]; | |
| 361 | card -= 1; | |
| 362 | result.keysAndCardinalities[k + 2] = card; | |
| 363 | result.keysAndCardinalities[k + 3] = card >> 8; | |
| 364 | i += 1; | |
| 365 | j += 1; | |
| 366 | } | |
| 367 | } | |
| 368 | return result; | |
| 369 | } | |
| 370 | /** | |
| 371 | * @param {RoaringBitmap} that | |
| 372 | * @returns {RoaringBitmap} | |
| 373 | */ | |
| 374 | intersection(that) { | |
| 375 | if (this.isEmpty() || that.isEmpty()) { | |
| 376 | return EMPTY_BITMAP; | |
| 377 | } | |
| 378 | if (this === RoaringBitmap.everything()) { | |
| 379 | return that; | |
| 380 | } | |
| 381 | if (that === RoaringBitmap.everything()) { | |
| 382 | return this; | |
| 383 | } | |
| 384 | let i = 0; | |
| 385 | const il = this.containers.length; | |
| 386 | let j = 0; | |
| 387 | const jl = that.containers.length; | |
| 388 | const result = new RoaringBitmap(null, 0); | |
| 389 | result.keysAndCardinalities = new Uint8Array((il > jl ? il : jl) * 4); | |
| 390 | while (i < il && j < jl) { | |
| 391 | const ik = i * 4; | |
| 392 | const jk = j * 4; | |
| 393 | const k = result.containers.length * 4; | |
| 394 | if (j >= jl || (i < il && ( | |
| 395 | (this.keysAndCardinalities[ik + 1] < that.keysAndCardinalities[jk + 1]) || | |
| 396 | (this.keysAndCardinalities[ik + 1] === that.keysAndCardinalities[jk + 1] && | |
| 397 | this.keysAndCardinalities[ik] < that.keysAndCardinalities[jk]) | |
| 398 | ))) { | |
| 399 | i += 1; | |
| 400 | } else if (i >= il || (j < jl && ( | |
| 401 | (that.keysAndCardinalities[jk + 1] < this.keysAndCardinalities[ik + 1]) || | |
| 402 | (that.keysAndCardinalities[jk + 1] === this.keysAndCardinalities[ik + 1] && | |
| 403 | that.keysAndCardinalities[jk] < this.keysAndCardinalities[ik]) | |
| 404 | ))) { | |
| 405 | j += 1; | |
| 406 | } else { | |
| 407 | // this key is not smaller than that key | |
| 408 | // that key is not smaller than this key | |
| 409 | // they must be equal | |
| 410 | const thisContainer = this.containers[i]; | |
| 411 | const thatContainer = that.containers[j]; | |
| 412 | let card = 0; | |
| 413 | if (thisContainer instanceof RoaringBitmapBits && | |
| 414 | thatContainer instanceof RoaringBitmapBits | |
| 415 | ) { | |
| 416 | const resultArray = new Uint8Array( | |
| 417 | thisContainer.array.length > thatContainer.array.length ? | |
| 418 | thisContainer.array.length : | |
| 419 | thatContainer.array.length, | |
| 420 | ); | |
| 421 | let k = 0; | |
| 422 | const kl = resultArray.length; | |
| 423 | while (k < kl) { | |
| 424 | const c = thisContainer.array[k] & thatContainer.array[k]; | |
| 425 | resultArray[k] = c; | |
| 426 | card += bitCount(c); | |
| 427 | k += 1; | |
| 428 | } | |
| 429 | if (card !== 0) { | |
| 430 | result.containers.push(new RoaringBitmapBits(resultArray)); | |
| 431 | } | |
| 432 | } else { | |
| 433 | const thisValues = thisContainer.values(); | |
| 434 | const thatValues = thatContainer.values(); | |
| 435 | let thisValue = thisValues.next(); | |
| 436 | let thatValue = thatValues.next(); | |
| 437 | const resultValues = []; | |
| 438 | while (!thatValue.done && !thisValue.done) { | |
| 439 | if (thisValue.value < thatValue.value) { | |
| 440 | thisValue = thisValues.next(); | |
| 441 | } else if (thatValue.value < thisValue.value) { | |
| 442 | thatValue = thatValues.next(); | |
| 443 | } else { | |
| 444 | // this value is not smaller than that value | |
| 445 | // that value is not smaller than this value | |
| 446 | // they must be equal | |
| 447 | resultValues.push(thisValue.value); | |
| 448 | thisValue = thisValues.next(); | |
| 449 | thatValue = thatValues.next(); | |
| 450 | } | |
| 451 | } | |
| 452 | card = resultValues.length; | |
| 453 | if (card !== 0) { | |
| 454 | const resultArray = new Uint8Array(resultValues.length * 2); | |
| 455 | let k = 0; | |
| 456 | for (const value of resultValues) { | |
| 457 | // roaring bitmap is little endian | |
| 458 | resultArray[k] = value & 0xFF; | |
| 459 | resultArray[k + 1] = (value >> 8) & 0xFF; | |
| 460 | k += 2; | |
| 461 | } | |
| 462 | result.containers.push(new RoaringBitmapArray( | |
| 463 | resultValues.length, | |
| 464 | resultArray, | |
| 465 | )); | |
| 466 | } | |
| 467 | } | |
| 468 | if (card !== 0) { | |
| 469 | result.keysAndCardinalities[k + 0] = this.keysAndCardinalities[ik + 0]; | |
| 470 | result.keysAndCardinalities[k + 1] = this.keysAndCardinalities[ik + 1]; | |
| 471 | card -= 1; | |
| 472 | result.keysAndCardinalities[k + 2] = card; | |
| 473 | result.keysAndCardinalities[k + 3] = card >> 8; | |
| 474 | } | |
| 475 | i += 1; | |
| 476 | j += 1; | |
| 477 | } | |
| 478 | } | |
| 479 | return result; | |
| 480 | } | |
| 481 | /** @param {number} keyvalue */ | |
| 482 | contains(keyvalue) { | |
| 483 | const key = keyvalue >> 16; | |
| 484 | const value = keyvalue & 0xFFFF; | |
| 485 | const mid = this.getContainerId(key); | |
| 486 | return mid === -1 ? false : this.containers[mid].contains(value); | |
| 487 | } | |
| 488 | /** | |
| 489 | * @param {number} key | |
| 490 | * @returns {number} | |
| 491 | */ | |
| 492 | getContainerId(key) { | |
| 493 | // Binary search algorithm copied from | |
| 494 | // https://en.wikipedia.org/wiki/Binary_search#Procedure | |
| 495 | // | |
| 496 | // Format is required by specification to be sorted. | |
| 497 | // Because keys are 16 bits and unique, length can't be | |
| 498 | // bigger than 2**16, and because we have 32 bits of safe int, | |
| 499 | // left + right can't overflow. | |
| 500 | let left = 0; | |
| 501 | let right = this.containers.length - 1; | |
| 502 | while (left <= right) { | |
| 503 | const mid = Math.floor((left + right) / 2); | |
| 504 | const x = this.keysAndCardinalities[(mid * 4)] | | |
| 505 | (this.keysAndCardinalities[(mid * 4) + 1] << 8); | |
| 506 | if (x < key) { | |
| 507 | left = mid + 1; | |
| 508 | } else if (x > key) { | |
| 509 | right = mid - 1; | |
| 510 | } else { | |
| 511 | return mid; | |
| 512 | } | |
| 513 | } | |
| 514 | return -1; | |
| 515 | } | |
| 516 | * entries() { | |
| 517 | const l = this.containers.length; | |
| 518 | for (let i = 0; i < l; ++i) { | |
| 519 | const key = this.keysAndCardinalities[i * 4] | | |
| 520 | (this.keysAndCardinalities[(i * 4) + 1] << 8); | |
| 521 | for (const value of this.containers[i].values()) { | |
| 522 | yield (key << 16) | value; | |
| 523 | } | |
| 524 | } | |
| 525 | } | |
| 526 | /** | |
| 527 | * @returns {number|null} | |
| 528 | */ | |
| 529 | first() { | |
| 530 | for (const entry of this.entries()) { | |
| 531 | return entry; | |
| 532 | } | |
| 533 | return null; | |
| 534 | } | |
| 535 | /** | |
| 536 | * @returns {number} | |
| 537 | */ | |
| 538 | cardinality() { | |
| 539 | let result = 0; | |
| 540 | const l = this.containers.length; | |
| 541 | for (let i = 0; i < l; ++i) { | |
| 542 | const card = this.keysAndCardinalities[(i * 4) + 2] | | |
| 543 | (this.keysAndCardinalities[(i * 4) + 3] << 8); | |
| 544 | result += card + 1; | |
| 545 | } | |
| 546 | return result; | |
| 547 | } | |
| 548 | } | |
| 549 | ||
| 550 | class RoaringBitmapRun { | |
| 551 | /** | |
| 552 | * @param {number} runcount | |
| 553 | * @param {Uint8Array} array | |
| 554 | */ | |
| 555 | constructor(runcount, array) { | |
| 556 | this.runcount = runcount; | |
| 557 | this.array = array; | |
| 558 | } | |
| 559 | /** @param {number} value */ | |
| 560 | contains(value) { | |
| 561 | // Binary search algorithm copied from | |
| 562 | // https://en.wikipedia.org/wiki/Binary_search#Procedure | |
| 563 | // | |
| 564 | // Since runcount is stored as 16 bits, left + right | |
| 565 | // can't overflow. | |
| 566 | let left = 0; | |
| 567 | let right = this.runcount - 1; | |
| 568 | while (left <= right) { | |
| 569 | const mid = (left + right) >> 1; | |
| 570 | const i = mid * 4; | |
| 571 | const start = this.array[i] | (this.array[i + 1] << 8); | |
| 572 | const lenm1 = this.array[i + 2] | (this.array[i + 3] << 8); | |
| 573 | if ((start + lenm1) < value) { | |
| 574 | left = mid + 1; | |
| 575 | } else if (start > value) { | |
| 576 | right = mid - 1; | |
| 577 | } else { | |
| 578 | return true; | |
| 579 | } | |
| 580 | } | |
| 581 | return false; | |
| 582 | } | |
| 583 | * values() { | |
| 584 | let i = 0; | |
| 585 | while (i < this.runcount) { | |
| 586 | const start = this.array[i * 4] | (this.array[(i * 4) + 1] << 8); | |
| 587 | const lenm1 = this.array[(i * 4) + 2] | (this.array[(i * 4) + 3] << 8); | |
| 588 | let value = start; | |
| 589 | let j = 0; | |
| 590 | while (j <= lenm1) { | |
| 591 | yield value; | |
| 592 | value += 1; | |
| 593 | j += 1; | |
| 594 | } | |
| 595 | i += 1; | |
| 596 | } | |
| 597 | } | |
| 598 | } | |
| 599 | class RoaringBitmapArray { | |
| 600 | /** | |
| 601 | * @param {number} cardinality | |
| 602 | * @param {Uint8Array} array | |
| 603 | */ | |
| 604 | constructor(cardinality, array) { | |
| 605 | this.cardinality = cardinality; | |
| 606 | this.array = array; | |
| 607 | } | |
| 608 | /** @param {number} value */ | |
| 609 | contains(value) { | |
| 610 | // Binary search algorithm copied from | |
| 611 | // https://en.wikipedia.org/wiki/Binary_search#Procedure | |
| 612 | // | |
| 613 | // Since cardinality can't be higher than 4096, left + right | |
| 614 | // cannot overflow. | |
| 615 | let left = 0; | |
| 616 | let right = this.cardinality - 1; | |
| 617 | while (left <= right) { | |
| 618 | const mid = (left + right) >> 1; | |
| 619 | const i = mid * 2; | |
| 620 | const x = this.array[i] | (this.array[i + 1] << 8); | |
| 621 | if (x < value) { | |
| 622 | left = mid + 1; | |
| 623 | } else if (x > value) { | |
| 624 | right = mid - 1; | |
| 625 | } else { | |
| 626 | return true; | |
| 627 | } | |
| 628 | } | |
| 629 | return false; | |
| 630 | } | |
| 631 | /** @returns {Generator<number>} */ | |
| 632 | * values() { | |
| 633 | let i = 0; | |
| 634 | const l = this.cardinality * 2; | |
| 635 | while (i < l) { | |
| 636 | yield this.array[i] | (this.array[i + 1] << 8); | |
| 637 | i += 2; | |
| 638 | } | |
| 639 | } | |
| 640 | } | |
| 641 | class RoaringBitmapBits { | |
| 642 | /** | |
| 643 | * @param {Uint8Array} array | |
| 644 | */ | |
| 645 | constructor(array) { | |
| 646 | this.array = array; | |
| 647 | } | |
| 648 | /** @param {number} value */ | |
| 649 | contains(value) { | |
| 650 | return !!(this.array[value >> 3] & (1 << (value & 7))); | |
| 651 | } | |
| 652 | * values() { | |
| 653 | let i = 0; | |
| 654 | const l = this.array.length << 3; | |
| 655 | while (i < l) { | |
| 656 | if (this.contains(i)) { | |
| 657 | yield i; | |
| 658 | } | |
| 659 | i += 1; | |
| 660 | } | |
| 661 | } | |
| 662 | } | |
| 663 | ||
| 664 | const EMPTY_BITMAP = new RoaringBitmap(null, 0); | |
| 665 | EMPTY_BITMAP.consumed_len_bytes = 0; | |
| 666 | const EMPTY_BITMAP1 = new RoaringBitmap(null, 0); | |
| 667 | EMPTY_BITMAP1.consumed_len_bytes = 1; | |
| 668 | const EVERYTHING_BITMAP = new RoaringBitmap(null, 0); | |
| 669 | ||
| 670 | /** | |
| 671 | * A mapping from six byte nodeids to an arbitrary value. | |
| 672 | * We don't just use `Map` because that requires double hashing. | |
| 673 | * @template T | |
| 674 | * @property {Uint8Array} keys | |
| 675 | * @property {T[]} values | |
| 676 | * @property {number} size | |
| 677 | * @property {number} capacityClass | |
| 678 | */ | |
| 679 | class HashTable { | |
| 680 | /** | |
| 681 | * Construct an empty hash table. | |
| 682 | */ | |
| 683 | constructor() { | |
| 684 | this.keys = EMPTY_UINT8; | |
| 685 | /** @type {(T|undefined)[]} */ | |
| 686 | this.values = []; | |
| 687 | this.size = 0; | |
| 688 | this.capacityClass = 0; | |
| 689 | } | |
| 690 | /** | |
| 691 | * @returns {Generator<[Uint8Array, T]>} | |
| 692 | */ | |
| 693 | * entries() { | |
| 694 | const keys = this.keys; | |
| 695 | const values = this.values; | |
| 696 | const l = this.values.length; | |
| 697 | for (let i = 0; i < l; i += 1) { | |
| 698 | const value = values[i]; | |
| 699 | if (value !== undefined) { | |
| 700 | yield [keys.subarray(i * 6, (i + 1) * 6), value]; | |
| 701 | } | |
| 702 | } | |
| 703 | } | |
| 704 | /** | |
| 705 | * Add a value to the hash table. | |
| 706 | * @param {Uint8Array} key | |
| 707 | * @param {T} value | |
| 708 | */ | |
| 709 | set(key, value) { | |
| 710 | // 90 % load factor | |
| 711 | if (this.size * 10 >= this.values.length * 9) { | |
| 712 | const keys = this.keys; | |
| 713 | const values = this.values; | |
| 714 | const l = values.length; | |
| 715 | this.capacityClass += 1; | |
| 716 | const capacity = 1 << this.capacityClass; | |
| 717 | this.keys = new Uint8Array(capacity * 6); | |
| 718 | this.values = []; | |
| 719 | for (let i = 0; i < capacity; i += 1) { | |
| 720 | this.values.push(undefined); | |
| 721 | } | |
| 722 | this.size = 0; | |
| 723 | for (let i = 0; i < l; i += 1) { | |
| 724 | const oldValue = values[i]; | |
| 725 | if (oldValue !== undefined) { | |
| 726 | this.setNoGrow(keys, i * 6, oldValue); | |
| 727 | } | |
| 728 | } | |
| 729 | } | |
| 730 | this.setNoGrow(key, 0, value); | |
| 731 | } | |
| 732 | /** | |
| 733 | * @param {Uint8Array} key | |
| 734 | * @param {number} start | |
| 735 | * @param {T} value | |
| 736 | */ | |
| 737 | setNoGrow(key, start, value) { | |
| 738 | const mask = ~(0xffffffff << this.capacityClass); | |
| 739 | const keys = this.keys; | |
| 740 | const values = this.values; | |
| 741 | const l = 1 << this.capacityClass; | |
| 742 | // because we know that our values are already hashed, | |
| 743 | // just chop off the lower four bytes | |
| 744 | let slot = ( | |
| 745 | (key[start + 2] << 24) | | |
| 746 | (key[start + 3] << 16) | | |
| 747 | (key[start + 4] << 8) | | |
| 748 | key[start + 5] | |
| 749 | ) & mask; | |
| 750 | for (let distance = 0; distance < l; ) { | |
| 751 | const j = slot * 6; | |
| 752 | const otherValue = values[slot]; | |
| 753 | if (otherValue === undefined) { | |
| 754 | values[slot] = value; | |
| 755 | const keysStart = slot * 6; | |
| 756 | keys[keysStart + 0] = key[start + 0]; | |
| 757 | keys[keysStart + 1] = key[start + 1]; | |
| 758 | keys[keysStart + 2] = key[start + 2]; | |
| 759 | keys[keysStart + 3] = key[start + 3]; | |
| 760 | keys[keysStart + 4] = key[start + 4]; | |
| 761 | keys[keysStart + 5] = key[start + 5]; | |
| 762 | this.size += 1; | |
| 763 | break; | |
| 764 | } else if ( | |
| 765 | key[start + 0] === keys[j + 0] && | |
| 766 | key[start + 1] === keys[j + 1] && | |
| 767 | key[start + 2] === keys[j + 2] && | |
| 768 | key[start + 3] === keys[j + 3] && | |
| 769 | key[start + 4] === keys[j + 4] && | |
| 770 | key[start + 5] === keys[j + 5] | |
| 771 | ) { | |
| 772 | values[slot] = value; | |
| 773 | break; | |
| 774 | } else { | |
| 775 | const otherPreferredSlot = ( | |
| 776 | (keys[j + 2] << 24) | (keys[j + 3] << 16) | | |
| 777 | (keys[j + 4] << 8) | keys[j + 5] | |
| 778 | ) & mask; | |
| 779 | const otherDistance = otherPreferredSlot <= slot ? | |
| 780 | slot - otherPreferredSlot : | |
| 781 | (l - otherPreferredSlot) + slot; | |
| 782 | if (distance > otherDistance) { | |
| 783 | // if the other key is closer to its preferred slot than this one, | |
| 784 | // then insert our node in its place and swap | |
| 785 | // | |
| 786 | // https://cglab.ca/~abeinges/blah/robinhood-part-1/ | |
| 787 | const otherKey = keys.slice(j, j + 6); | |
| 788 | values[slot] = value; | |
| 789 | value = otherValue; | |
| 790 | keys[j + 0] = key[start + 0]; | |
| 791 | keys[j + 1] = key[start + 1]; | |
| 792 | keys[j + 2] = key[start + 2]; | |
| 793 | keys[j + 3] = key[start + 3]; | |
| 794 | keys[j + 4] = key[start + 4]; | |
| 795 | keys[j + 5] = key[start + 5]; | |
| 796 | key = otherKey; | |
| 797 | start = 0; | |
| 798 | distance = otherDistance; | |
| 799 | } | |
| 800 | distance += 1; | |
| 801 | slot = (slot + 1) & mask; | |
| 802 | } | |
| 803 | } | |
| 804 | } | |
| 805 | /** | |
| 806 | * Retrieve a value | |
| 807 | * @param {Uint8Array} key | |
| 808 | * @returns {T|undefined} | |
| 809 | */ | |
| 810 | get(key) { | |
| 811 | if (key.length !== 6) { | |
| 812 | throw "invalid key"; | |
| 813 | } | |
| 814 | return this.getWithOffsetKey(key, 0); | |
| 815 | } | |
| 816 | /** | |
| 817 | * Retrieve a value | |
| 818 | * @param {Uint8Array} key | |
| 819 | * @param {number} start | |
| 820 | * @returns {T|undefined} | |
| 821 | */ | |
| 822 | getWithOffsetKey(key, start) { | |
| 823 | const mask = ~(0xffffffff << this.capacityClass); | |
| 824 | const keys = this.keys; | |
| 825 | const values = this.values; | |
| 826 | const l = 1 << this.capacityClass; | |
| 827 | // because we know that our values are already hashed, | |
| 828 | // just chop off the lower four bytes | |
| 829 | let slot = ( | |
| 830 | (key[start + 2] << 24) | | |
| 831 | (key[start + 3] << 16) | | |
| 832 | (key[start + 4] << 8) | | |
| 833 | key[start + 5] | |
| 834 | ) & mask; | |
| 835 | for (let distance = 0; distance < l; distance += 1) { | |
| 836 | const j = slot * 6; | |
| 837 | const value = values[slot]; | |
| 838 | if (value === undefined) { | |
| 839 | break; | |
| 840 | } else if ( | |
| 841 | key[start + 0] === keys[j + 0] && | |
| 842 | key[start + 1] === keys[j + 1] && | |
| 843 | key[start + 2] === keys[j + 2] && | |
| 844 | key[start + 3] === keys[j + 3] && | |
| 845 | key[start + 4] === keys[j + 4] && | |
| 846 | key[start + 5] === keys[j + 5] | |
| 847 | ) { | |
| 848 | return value; | |
| 849 | } else { | |
| 850 | const otherPreferredSlot = ( | |
| 851 | (keys[j + 2] << 24) | (keys[j + 3] << 16) | | |
| 852 | (keys[j + 4] << 8) | keys[j + 5] | |
| 853 | ) & mask; | |
| 854 | const otherDistance = otherPreferredSlot <= slot ? | |
| 855 | slot - otherPreferredSlot : | |
| 856 | (l - otherPreferredSlot) + slot; | |
| 857 | if (distance > otherDistance) { | |
| 858 | break; | |
| 859 | } | |
| 860 | } | |
| 861 | slot = (slot + 1) & mask; | |
| 862 | } | |
| 863 | return undefined; | |
| 864 | } | |
| 865 | } | |
| 866 | ||
| 867 | /*eslint-disable */ | |
| 868 | // ignore-tidy-linelength | |
| 869 | /** <https://stackoverflow.com/questions/43122082/efficiently-count-the-number-of-bits-in-an-integer-in-javascript> | |
| 870 | * @param {number} n | |
| 871 | * @returns {number} | |
| 872 | */ | |
| 873 | function bitCount(n) { | |
| 874 | n = (~~n) - ((n >> 1) & 0x55555555); | |
| 875 | n = (n & 0x33333333) + ((n >> 2) & 0x33333333); | |
| 876 | return ((n + (n >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; | |
| 877 | } | |
| 878 | /*eslint-enable */ | |
| 879 | ||
| 880 | /** | |
| 881 | * @param {stringdex.Hooks} hooks | |
| 882 | * @returns {Promise<stringdex.Database>} | |
| 883 | */ | |
| 884 | function loadDatabase(hooks) { | |
| 885 | /** @type {stringdex.Callbacks} */ | |
| 886 | const callbacks = { | |
| 887 | rr_: function(data) { | |
| 888 | const dataObj = JSON.parse(data); | |
| 889 | for (const colName of Object.keys(dataObj)) { | |
| 890 | if (Object.hasOwn(dataObj[colName], "I")) { | |
| 891 | registry.searchTreeRoots.set( | |
| 892 | colName, | |
| 893 | makeSearchTreeFromBase64(dataObj[colName].I)[1], | |
| 894 | ); | |
| 895 | } | |
| 896 | if (Object.hasOwn(dataObj[colName], "N")) { | |
| 897 | const counts = []; | |
| 898 | const countsstring = dataObj[colName]["N"]; | |
| 899 | let i = 0; | |
| 900 | const l = countsstring.length; | |
| 901 | while (i < l) { | |
| 902 | let n = 0; | |
| 903 | let c = countsstring.charCodeAt(i); | |
| 904 | while (c < 96) { // 96 = "`" | |
| 905 | n = (n << 4) | (c & 0xF); | |
| 906 | i += 1; | |
| 907 | c = countsstring.charCodeAt(i); | |
| 908 | } | |
| 909 | n = (n << 4) | (c & 0xF); | |
| 910 | counts.push(n); | |
| 911 | i += 1; | |
| 912 | } | |
| 913 | registry.dataColumns.set(colName, new DataColumn( | |
| 914 | counts, | |
| 915 | makeUint8ArrayFromBase64(dataObj[colName]["H"]), | |
| 916 | new RoaringBitmap(makeUint8ArrayFromBase64(dataObj[colName]["E"]), 0), | |
| 917 | colName, | |
| 918 | )); | |
| 919 | } | |
| 920 | } | |
| 921 | const cb = registry.searchTreeRootCallback; | |
| 922 | if (cb) { | |
| 923 | cb(null, new Database(registry.searchTreeRoots, registry.dataColumns)); | |
| 924 | } | |
| 925 | }, | |
| 926 | err_rr_: function(err) { | |
| 927 | const cb = registry.searchTreeRootCallback; | |
| 928 | if (cb) { | |
| 929 | cb(err, null); | |
| 930 | } | |
| 931 | }, | |
| 932 | rd_: function(dataString) { | |
| 933 | const l = dataString.length; | |
| 934 | const data = new Uint8Array(l); | |
| 935 | for (let i = 0; i < l; ++i) { | |
| 936 | data[i] = dataString.charCodeAt(i); | |
| 937 | } | |
| 938 | loadColumnFromBytes(data); | |
| 939 | }, | |
| 940 | err_rd_: function(filename, err) { | |
| 941 | const nodeid = makeUint8ArrayFromHex(filename); | |
| 942 | const cb = registry.dataColumnLoadPromiseCallbacks.get(nodeid); | |
| 943 | if (cb) { | |
| 944 | cb(err, null); | |
| 945 | } | |
| 946 | }, | |
| 947 | rb_: function(dataString64) { | |
| 948 | loadColumnFromBytes(makeUint8ArrayFromBase64(dataString64)); | |
| 949 | }, | |
| 950 | err_rb_: function(filename, err) { | |
| 951 | const nodeid = makeUint8ArrayFromHex(filename); | |
| 952 | const cb = registry.dataColumnLoadPromiseCallbacks.get(nodeid); | |
| 953 | if (cb) { | |
| 954 | cb(err, null); | |
| 955 | } | |
| 956 | }, | |
| 957 | rn_: function(inputBase64) { | |
| 958 | const [nodeid, tree] = makeSearchTreeFromBase64(inputBase64); | |
| 959 | const cb = registry.searchTreeLoadPromiseCallbacks.get(nodeid); | |
| 960 | if (cb) { | |
| 961 | cb(null, tree); | |
| 962 | registry.searchTreeLoadPromiseCallbacks.set(nodeid, null); | |
| 963 | } | |
| 964 | }, | |
| 965 | err_rn_: function(filename, err) { | |
| 966 | const nodeid = makeUint8ArrayFromHex(filename); | |
| 967 | const cb = registry.searchTreeLoadPromiseCallbacks.get(nodeid); | |
| 968 | if (cb) { | |
| 969 | cb(err, null); | |
| 970 | } | |
| 971 | }, | |
| 972 | }; | |
| 973 | ||
| 974 | /** | |
| 975 | * @type {{ | |
| 976 | * searchTreeRoots: Map<string, SearchTree>; | |
| 977 | * searchTreeLoadPromiseCallbacks: HashTable<(function(any, SearchTree?): any)|null>; | |
| 978 | * searchTreePromises: HashTable<Promise<SearchTree>>; | |
| 979 | * dataColumnLoadPromiseCallbacks: HashTable<function(any, Uint8Array[]?): any>; | |
| 980 | * dataColumns: Map<string, DataColumn>; | |
| 981 | * dataColumnsBuckets: Map<string, HashTable<Promise<Uint8Array[]>>>; | |
| 982 | * searchTreeLoadByNodeID: function(Uint8Array): Promise<SearchTree>; | |
| 983 | * searchTreeRootCallback?: function(any, Database?): any; | |
| 984 | * dataLoadByNameAndHash: function(string, Uint8Array): Promise<Uint8Array[]>; | |
| 985 | * }} | |
| 986 | */ | |
| 987 | const registry = { | |
| 988 | searchTreeRoots: new Map(), | |
| 989 | searchTreeLoadPromiseCallbacks: new HashTable(), | |
| 990 | searchTreePromises: new HashTable(), | |
| 991 | dataColumnLoadPromiseCallbacks: new HashTable(), | |
| 992 | dataColumns: new Map(), | |
| 993 | dataColumnsBuckets: new Map(), | |
| 994 | searchTreeLoadByNodeID: function(nodeid) { | |
| 995 | const existingPromise = registry.searchTreePromises.get(nodeid); | |
| 996 | if (existingPromise) { | |
| 997 | return existingPromise; | |
| 998 | } | |
| 999 | /** @type {Promise<SearchTree>} */ | |
| 1000 | let newPromise; | |
| 1001 | if ((nodeid[0] & 0x80) !== 0) { | |
| 1002 | const isWhole = (nodeid[0] & 0x40) !== 0; | |
| 1003 | let leaves; | |
| 1004 | if ((nodeid[0] & 0x10) !== 0) { | |
| 1005 | let id1 = (nodeid[2] << 8) | nodeid[3]; | |
| 1006 | if ((nodeid[0] & 0x20) !== 0) { | |
| 1007 | // when data is present, id1 can be up to 20 bits | |
| 1008 | id1 |= ((nodeid[1] & 0x0f) << 16); | |
| 1009 | } else { | |
| 1010 | // otherwise, we fit in 28 | |
| 1011 | id1 |= ((nodeid[0] & 0x0f) << 24) | (nodeid[1] << 16); | |
| 1012 | } | |
| 1013 | const id2 = id1 + ((nodeid[4] << 8) | nodeid[5]); | |
| 1014 | leaves = RoaringBitmap.makeSingleton(id1) | |
| 1015 | .union(RoaringBitmap.makeSingleton(id2)); | |
| 1016 | } else { | |
| 1017 | leaves = RoaringBitmap.makeSingleton( | |
| 1018 | (nodeid[2] << 24) | (nodeid[3] << 16) | | |
| 1019 | (nodeid[4] << 8) | nodeid[5], | |
| 1020 | ); | |
| 1021 | } | |
| 1022 | const data = (nodeid[0] & 0x20) !== 0 ? | |
| 1023 | Uint8Array.of(((nodeid[0] & 0x0f) << 4) | (nodeid[1] >> 4)) : | |
| 1024 | EMPTY_UINT8; | |
| 1025 | newPromise = Promise.resolve(new SearchTree( | |
| 1026 | EMPTY_SEARCH_TREE_BRANCHES, | |
| 1027 | EMPTY_SEARCH_TREE_BRANCHES, | |
| 1028 | data, | |
| 1029 | isWhole ? leaves : EMPTY_BITMAP, | |
| 1030 | isWhole ? EMPTY_BITMAP : leaves, | |
| 1031 | )); | |
| 1032 | } else { | |
| 1033 | const hashHex = makeHexFromUint8Array(nodeid); | |
| 1034 | newPromise = new Promise((resolve, reject) => { | |
| 1035 | const cb = registry.searchTreeLoadPromiseCallbacks.get(nodeid); | |
| 1036 | if (cb) { | |
| 1037 | registry.searchTreeLoadPromiseCallbacks.set(nodeid, (err, data) => { | |
| 1038 | cb(err, data); | |
| 1039 | if (data) { | |
| 1040 | resolve(data); | |
| 1041 | } else { | |
| 1042 | reject(err); | |
| 1043 | } | |
| 1044 | }); | |
| 1045 | } else { | |
| 1046 | registry.searchTreeLoadPromiseCallbacks.set(nodeid, (err, data) => { | |
| 1047 | if (data) { | |
| 1048 | resolve(data); | |
| 1049 | } else { | |
| 1050 | reject(err); | |
| 1051 | } | |
| 1052 | }); | |
| 1053 | hooks.loadTreeByHash(hashHex); | |
| 1054 | } | |
| 1055 | }); | |
| 1056 | } | |
| 1057 | registry.searchTreePromises.set(nodeid, newPromise); | |
| 1058 | return newPromise; | |
| 1059 | }, | |
| 1060 | dataLoadByNameAndHash: function(name, hash) { | |
| 1061 | let dataColumnBuckets = registry.dataColumnsBuckets.get(name); | |
| 1062 | if (dataColumnBuckets === undefined) { | |
| 1063 | dataColumnBuckets = new HashTable(); | |
| 1064 | registry.dataColumnsBuckets.set(name, dataColumnBuckets); | |
| 1065 | } | |
| 1066 | const existingBucket = dataColumnBuckets.get(hash); | |
| 1067 | if (existingBucket) { | |
| 1068 | return existingBucket; | |
| 1069 | } | |
| 1070 | const hashHex = makeHexFromUint8Array(hash); | |
| 1071 | /** @type {Promise<Uint8Array[]>} */ | |
| 1072 | const newBucket = new Promise((resolve, reject) => { | |
| 1073 | const cb = registry.dataColumnLoadPromiseCallbacks.get(hash); | |
| 1074 | if (cb) { | |
| 1075 | registry.dataColumnLoadPromiseCallbacks.set(hash, (err, data) => { | |
| 1076 | cb(err, data); | |
| 1077 | if (data) { | |
| 1078 | resolve(data); | |
| 1079 | } else { | |
| 1080 | reject(err); | |
| 1081 | } | |
| 1082 | }); | |
| 1083 | } else { | |
| 1084 | registry.dataColumnLoadPromiseCallbacks.set(hash, (err, data) => { | |
| 1085 | if (data) { | |
| 1086 | resolve(data); | |
| 1087 | } else { | |
| 1088 | reject(err); | |
| 1089 | } | |
| 1090 | }); | |
| 1091 | hooks.loadDataByNameAndHash(name, hashHex); | |
| 1092 | } | |
| 1093 | }); | |
| 1094 | dataColumnBuckets.set(hash, newBucket); | |
| 1095 | return newBucket; | |
| 1096 | }, | |
| 1097 | }; | |
| 1098 | ||
| 1099 | /** | |
| 1100 | * The set of child subtrees. | |
| 1101 | * @type {{ | |
| 1102 | * nodeids: Uint8Array, | |
| 1103 | * subtrees: Array<Promise<SearchTree>|null>, | |
| 1104 | * }} | |
| 1105 | */ | |
| 1106 | class SearchTreeBranches { | |
| 1107 | /** | |
| 1108 | * Construct the subtree list with `length` nulls | |
| 1109 | * @param {number} length | |
| 1110 | * @param {Uint8Array} nodeids | |
| 1111 | */ | |
| 1112 | constructor(length, nodeids) { | |
| 1113 | this.nodeids = nodeids; | |
| 1114 | this.subtrees = []; | |
| 1115 | for (let i = 0; i < length; ++i) { | |
| 1116 | this.subtrees.push(null); | |
| 1117 | } | |
| 1118 | } | |
| 1119 | /** | |
| 1120 | * @param {number} i | |
| 1121 | * @returns {Uint8Array} | |
| 1122 | */ | |
| 1123 | getNodeID(i) { | |
| 1124 | return new Uint8Array( | |
| 1125 | this.nodeids.buffer, | |
| 1126 | this.nodeids.byteOffset + (i * 6), | |
| 1127 | 6, | |
| 1128 | ); | |
| 1129 | } | |
| 1130 | // https://github.com/microsoft/TypeScript/issues/17227 | |
| 1131 | /** @returns {Generator<[number, Promise<SearchTree>|null]>} */ | |
| 1132 | entries() { | |
| 1133 | throw new Error(); | |
| 1134 | } | |
| 1135 | /** | |
| 1136 | * @param {number} _k | |
| 1137 | * @returns {number} | |
| 1138 | */ | |
| 1139 | getIndex(_k) { | |
| 1140 | throw new Error(); | |
| 1141 | } | |
| 1142 | /** | |
| 1143 | * @param {number} _i | |
| 1144 | * @returns {number} | |
| 1145 | */ | |
| 1146 | getKey(_i) { | |
| 1147 | throw new Error(); | |
| 1148 | } | |
| 1149 | /** | |
| 1150 | * @returns {Uint8Array} | |
| 1151 | */ | |
| 1152 | getKeys() { | |
| 1153 | throw new Error(); | |
| 1154 | } | |
| 1155 | } | |
| 1156 | ||
| 1157 | /** | |
| 1158 | * A sorted array of search tree branches. | |
| 1159 | * | |
| 1160 | * @type {{ | |
| 1161 | * keys: Uint8Array, | |
| 1162 | * nodeids: Uint8Array, | |
| 1163 | * subtrees: Array<Promise<SearchTree>|null>, | |
| 1164 | * }} | |
| 1165 | */ | |
| 1166 | class SearchTreeBranchesArray extends SearchTreeBranches { | |
| 1167 | /** | |
| 1168 | * @param {Uint8Array} keys | |
| 1169 | * @param {Uint8Array} nodeids | |
| 1170 | */ | |
| 1171 | constructor(keys, nodeids) { | |
| 1172 | super(keys.length, nodeids); | |
| 1173 | this.keys = keys; | |
| 1174 | let i = 1; | |
| 1175 | while (i < this.keys.length) { | |
| 1176 | if (this.keys[i - 1] >= this.keys[i]) { | |
| 1177 | throw new Error("HERE"); | |
| 1178 | } | |
| 1179 | i += 1; | |
| 1180 | } | |
| 1181 | } | |
| 1182 | /** @returns {Generator<[number, Promise<SearchTree>|null]>} */ | |
| 1183 | * entries() { | |
| 1184 | let i = 0; | |
| 1185 | const l = this.keys.length; | |
| 1186 | while (i < l) { | |
| 1187 | yield [this.keys[i], this.subtrees[i]]; | |
| 1188 | i += 1; | |
| 1189 | } | |
| 1190 | } | |
| 1191 | /** | |
| 1192 | * @param {number} k | |
| 1193 | * @returns {number} | |
| 1194 | */ | |
| 1195 | getIndex(k) { | |
| 1196 | // Since length can't be bigger than 256, | |
| 1197 | // left + right can't overflow. | |
| 1198 | let left = 0; | |
| 1199 | let right = this.keys.length - 1; | |
| 1200 | while (left <= right) { | |
| 1201 | const mid = (left + right) >> 1; | |
| 1202 | if (this.keys[mid] < k) { | |
| 1203 | left = mid + 1; | |
| 1204 | } else if (this.keys[mid] > k) { | |
| 1205 | right = mid - 1; | |
| 1206 | } else { | |
| 1207 | return mid; | |
| 1208 | } | |
| 1209 | } | |
| 1210 | return -1; | |
| 1211 | } | |
| 1212 | /** | |
| 1213 | * @param {number} i | |
| 1214 | * @returns {number} | |
| 1215 | */ | |
| 1216 | getKey(i) { | |
| 1217 | return this.keys[i]; | |
| 1218 | } | |
| 1219 | /** | |
| 1220 | * @returns {Uint8Array} | |
| 1221 | */ | |
| 1222 | getKeys() { | |
| 1223 | return this.keys; | |
| 1224 | } | |
| 1225 | } | |
| 1226 | ||
| 1227 | const EMPTY_SEARCH_TREE_BRANCHES = new SearchTreeBranchesArray( | |
| 1228 | EMPTY_UINT8, | |
| 1229 | EMPTY_UINT8, | |
| 1230 | ); | |
| 1231 | ||
| 1232 | /** @type {number[]} */ | |
| 1233 | const SHORT_ALPHABITMAP_CHARS = []; | |
| 1234 | for (let i = 0x61; i <= 0x7A; ++i) { | |
| 1235 | if (i === 0x76 || i === 0x71) { | |
| 1236 | // 24 entries, 26 letters, so we skip q and v | |
| 1237 | continue; | |
| 1238 | } | |
| 1239 | SHORT_ALPHABITMAP_CHARS.push(i); | |
| 1240 | } | |
| 1241 | ||
| 1242 | /** @type {number[]} */ | |
| 1243 | const LONG_ALPHABITMAP_CHARS = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36]; | |
| 1244 | for (let i = 0x61; i <= 0x7A; ++i) { | |
| 1245 | LONG_ALPHABITMAP_CHARS.push(i); | |
| 1246 | } | |
| 1247 | ||
| 1248 | /** | |
| 1249 | * @param {number[]} alphabitmap_chars | |
| 1250 | * @param {number} width | |
| 1251 | * @return {(typeof SearchTreeBranches)&{"ALPHABITMAP_CHARS": number[], "width": number}} | |
| 1252 | */ | |
| 1253 | function makeSearchTreeBranchesAlphaBitmapClass(alphabitmap_chars, width) { | |
| 1254 | const bitwidth = width * 8; | |
| 1255 | const cls = class SearchTreeBranchesAlphaBitmap extends SearchTreeBranches { | |
| 1256 | /** | |
| 1257 | * @param {number} bitmap | |
| 1258 | * @param {Uint8Array} nodeids | |
| 1259 | */ | |
| 1260 | constructor(bitmap, nodeids) { | |
| 1261 | super(nodeids.length / 6, nodeids); | |
| 1262 | if (nodeids.length / 6 !== bitCount(bitmap)) { | |
| 1263 | throw new Error(`mismatch ${bitmap} ${nodeids}`); | |
| 1264 | } | |
| 1265 | this.bitmap = bitmap; | |
| 1266 | this.nodeids = nodeids; | |
| 1267 | } | |
| 1268 | /** @returns {Generator<[number, Promise<SearchTree>|null]>} */ | |
| 1269 | * entries() { | |
| 1270 | let i = 0; | |
| 1271 | let j = 0; | |
| 1272 | while (i < bitwidth) { | |
| 1273 | if (this.bitmap & (1 << i)) { | |
| 1274 | yield [alphabitmap_chars[i], this.subtrees[j]]; | |
| 1275 | j += 1; | |
| 1276 | } | |
| 1277 | i += 1; | |
| 1278 | } | |
| 1279 | } | |
| 1280 | /** | |
| 1281 | * @param {number} k | |
| 1282 | * @returns {number} | |
| 1283 | */ | |
| 1284 | getIndex(k) { | |
| 1285 | //return this.getKeys().indexOf(k); | |
| 1286 | const ix = alphabitmap_chars.indexOf(k); | |
| 1287 | if (ix < 0) { | |
| 1288 | return ix; | |
| 1289 | } | |
| 1290 | const result = bitCount(~(0xffffffff << ix) & this.bitmap); | |
| 1291 | return result >= this.subtrees.length ? -1 : result; | |
| 1292 | } | |
| 1293 | /** | |
| 1294 | * @param {number} branch_index | |
| 1295 | * @returns {number} | |
| 1296 | */ | |
| 1297 | getKey(branch_index) { | |
| 1298 | //return this.getKeys()[branch_index]; | |
| 1299 | let alpha_index = 0; | |
| 1300 | while (branch_index >= 0) { | |
| 1301 | if (this.bitmap & (1 << alpha_index)) { | |
| 1302 | branch_index -= 1; | |
| 1303 | } | |
| 1304 | alpha_index += 1; | |
| 1305 | } | |
| 1306 | return alphabitmap_chars[alpha_index]; | |
| 1307 | } | |
| 1308 | /** | |
| 1309 | * @returns {Uint8Array} | |
| 1310 | */ | |
| 1311 | getKeys() { | |
| 1312 | const length = bitCount(this.bitmap); | |
| 1313 | const result = new Uint8Array(length); | |
| 1314 | let result_index = 0; | |
| 1315 | for (let alpha_index = 0; alpha_index < bitwidth; ++alpha_index) { | |
| 1316 | if (this.bitmap & (1 << alpha_index)) { | |
| 1317 | result[result_index] = alphabitmap_chars[alpha_index]; | |
| 1318 | result_index += 1; | |
| 1319 | } | |
| 1320 | } | |
| 1321 | return result; | |
| 1322 | } | |
| 1323 | }; | |
| 1324 | cls.ALPHABITMAP_CHARS = alphabitmap_chars; | |
| 1325 | cls.width = width; | |
| 1326 | return cls; | |
| 1327 | } | |
| 1328 | ||
| 1329 | const SearchTreeBranchesShortAlphaBitmap = | |
| 1330 | makeSearchTreeBranchesAlphaBitmapClass(SHORT_ALPHABITMAP_CHARS, 3); | |
| 1331 | ||
| 1332 | const SearchTreeBranchesLongAlphaBitmap = | |
| 1333 | makeSearchTreeBranchesAlphaBitmapClass(LONG_ALPHABITMAP_CHARS, 4); | |
| 1334 | ||
| 1335 | /** | |
| 1336 | * A [suffix tree], used for name-based search. | |
| 1337 | * | |
| 1338 | * This data structure is used to drive substring matches, | |
| 1339 | * such as matching the query "link" to `LinkedList`, | |
| 1340 | * and Lev-distance matches, such as matching the | |
| 1341 | * query "hahsmap" to `HashMap`. | |
| 1342 | * | |
| 1343 | * [suffix tree]: https://en.wikipedia.org/wiki/Suffix_tree | |
| 1344 | * | |
| 1345 | * branches | |
| 1346 | * : A sorted-array map of subtrees. | |
| 1347 | * | |
| 1348 | * data | |
| 1349 | * : The substring represented by this node. The root node | |
| 1350 | * is always empty. | |
| 1351 | * | |
| 1352 | * leaves_suffix | |
| 1353 | * : The IDs of every entry that matches. Levenshtein matches | |
| 1354 | * won't include these. | |
| 1355 | * | |
| 1356 | * leaves_whole | |
| 1357 | * : The IDs of every entry that matches exactly. Levenshtein matches | |
| 1358 | * will include these. | |
| 1359 | * | |
| 1360 | * @type {{ | |
| 1361 | * might_have_prefix_branches: SearchTreeBranches, | |
| 1362 | * branches: SearchTreeBranches, | |
| 1363 | * data: Uint8Array, | |
| 1364 | * leaves_suffix: RoaringBitmap, | |
| 1365 | * leaves_whole: RoaringBitmap, | |
| 1366 | * }} | |
| 1367 | */ | |
| 1368 | class SearchTree { | |
| 1369 | /** | |
| 1370 | * @param {SearchTreeBranches} branches | |
| 1371 | * @param {SearchTreeBranches} might_have_prefix_branches | |
| 1372 | * @param {Uint8Array} data | |
| 1373 | * @param {RoaringBitmap} leaves_whole | |
| 1374 | * @param {RoaringBitmap} leaves_suffix | |
| 1375 | */ | |
| 1376 | constructor( | |
| 1377 | branches, | |
| 1378 | might_have_prefix_branches, | |
| 1379 | data, | |
| 1380 | leaves_whole, | |
| 1381 | leaves_suffix, | |
| 1382 | ) { | |
| 1383 | this.might_have_prefix_branches = might_have_prefix_branches; | |
| 1384 | this.branches = branches; | |
| 1385 | this.data = data; | |
| 1386 | this.leaves_suffix = leaves_suffix; | |
| 1387 | this.leaves_whole = leaves_whole; | |
| 1388 | } | |
| 1389 | /** | |
| 1390 | * Returns the Trie for the root node. | |
| 1391 | * | |
| 1392 | * A Trie pointer refers to a single node in a logical decompressed search tree | |
| 1393 | * (the real search tree is compressed). | |
| 1394 | * | |
| 1395 | * @return {Trie} | |
| 1396 | */ | |
| 1397 | trie() { | |
| 1398 | return new Trie(this, 0); | |
| 1399 | } | |
| 1400 | ||
| 1401 | /** | |
| 1402 | * Return the trie representing `name` | |
| 1403 | * @param {Uint8Array|string} name | |
| 1404 | * @returns {Promise<Trie?>} | |
| 1405 | */ | |
| 1406 | async search(name) { | |
| 1407 | if (typeof name === "string") { | |
| 1408 | const utf8encoder = new TextEncoder(); | |
| 1409 | name = utf8encoder.encode(name); | |
| 1410 | } | |
| 1411 | let trie = this.trie(); | |
| 1412 | for (const datum of name) { | |
| 1413 | // code point definitely exists | |
| 1414 | const newTrie = trie.child(datum); | |
| 1415 | if (newTrie) { | |
| 1416 | trie = await newTrie; | |
| 1417 | } else { | |
| 1418 | return null; | |
| 1419 | } | |
| 1420 | } | |
| 1421 | return trie; | |
| 1422 | } | |
| 1423 | ||
| 1424 | /** | |
| 1425 | * @param {Uint8Array|string} name | |
| 1426 | * @returns {AsyncGenerator<Trie>} | |
| 1427 | */ | |
| 1428 | async* searchLev(name) { | |
| 1429 | if (typeof name === "string") { | |
| 1430 | const utf8encoder = new TextEncoder(); | |
| 1431 | name = utf8encoder.encode(name); | |
| 1432 | } | |
| 1433 | const w = name.length; | |
| 1434 | if (w < 3) { | |
| 1435 | const trie = await this.search(name); | |
| 1436 | if (trie !== null) { | |
| 1437 | yield trie; | |
| 1438 | } | |
| 1439 | return; | |
| 1440 | } | |
| 1441 | const levParams = w >= 6 ? | |
| 1442 | new Lev2TParametricDescription(w) : | |
| 1443 | new Lev1TParametricDescription(w); | |
| 1444 | /** @type {Array<[Promise<Trie>, number]>} */ | |
| 1445 | const stack = [[Promise.resolve(this.trie()), 0]]; | |
| 1446 | const n = levParams.n; | |
| 1447 | while (stack.length !== 0) { | |
| 1448 | // It's not empty | |
| 1449 | /** @type {[Promise<Trie>, number]} */ | |
| 1450 | //@ts-expect-error | |
| 1451 | const [triePromise, levState] = stack.pop(); | |
| 1452 | const trie = await triePromise; | |
| 1453 | for (const byte of trie.keysExcludeSuffixOnly()) { | |
| 1454 | const levPos = levParams.getPosition(levState); | |
| 1455 | const vector = levParams.getVector( | |
| 1456 | name, | |
| 1457 | byte, | |
| 1458 | levPos, | |
| 1459 | Math.min(w, levPos + (2 * n) + 1), | |
| 1460 | ); | |
| 1461 | const newLevState = levParams.transition( | |
| 1462 | levState, | |
| 1463 | levPos, | |
| 1464 | vector, | |
| 1465 | ); | |
| 1466 | if (newLevState >= 0) { | |
| 1467 | const child = trie.child(byte); | |
| 1468 | if (child) { | |
| 1469 | stack.push([child, newLevState]); | |
| 1470 | if (levParams.isAccept(newLevState)) { | |
| 1471 | yield child; | |
| 1472 | } | |
| 1473 | } | |
| 1474 | } | |
| 1475 | } | |
| 1476 | } | |
| 1477 | } | |
| 1478 | } | |
| 1479 | ||
| 1480 | /** | |
| 1481 | * A representation of a set of strings in the search index, | |
| 1482 | * as a subset of the entire tree. | |
| 1483 | */ | |
| 1484 | class Trie { | |
| 1485 | /** | |
| 1486 | * @param {SearchTree} tree | |
| 1487 | * @param {number} offset | |
| 1488 | */ | |
| 1489 | constructor(tree, offset) { | |
| 1490 | this.tree = tree; | |
| 1491 | this.offset = offset; | |
| 1492 | } | |
| 1493 | ||
| 1494 | /** | |
| 1495 | * All exact matches for the string represented by this node. | |
| 1496 | * @returns {RoaringBitmap} | |
| 1497 | */ | |
| 1498 | matches() { | |
| 1499 | if (this.offset === this.tree.data.length) { | |
| 1500 | return this.tree.leaves_whole; | |
| 1501 | } else { | |
| 1502 | return EMPTY_BITMAP; | |
| 1503 | } | |
| 1504 | } | |
| 1505 | ||
| 1506 | /** | |
| 1507 | * All matches for strings that contain the string represented by this node. | |
| 1508 | * @returns {AsyncGenerator<RoaringBitmap>} | |
| 1509 | */ | |
| 1510 | async* substringMatches() { | |
| 1511 | /** @type {Promise<SearchTree>[]} */ | |
| 1512 | let layer = [Promise.resolve(this.tree)]; | |
| 1513 | while (layer.length) { | |
| 1514 | const current_layer = layer; | |
| 1515 | layer = []; | |
| 1516 | for await (const tree of current_layer) { | |
| 1517 | yield tree.leaves_whole.union(tree.leaves_suffix); | |
| 1518 | } | |
| 1519 | /** @type {HashTable<[number, SearchTree][]>} */ | |
| 1520 | const subnodes = new HashTable(); | |
| 1521 | for await (const node of current_layer) { | |
| 1522 | const branches = node.branches; | |
| 1523 | const l = branches.subtrees.length; | |
| 1524 | for (let i = 0; i < l; ++i) { | |
| 1525 | const subtree = branches.subtrees[i]; | |
| 1526 | if (subtree) { | |
| 1527 | layer.push(subtree); | |
| 1528 | } else if (subtree === null) { | |
| 1529 | const byte = branches.getKey(i); | |
| 1530 | const newnode = branches.getNodeID(i); | |
| 1531 | if (!newnode) { | |
| 1532 | throw new Error(`malformed tree; no node for key ${byte}`); | |
| 1533 | } else { | |
| 1534 | let subnode_list = subnodes.get(newnode); | |
| 1535 | if (!subnode_list) { | |
| 1536 | subnode_list = [[byte, node]]; | |
| 1537 | subnodes.set(newnode, subnode_list); | |
| 1538 | } else { | |
| 1539 | subnode_list.push([byte, node]); | |
| 1540 | } | |
| 1541 | } | |
| 1542 | } else { | |
| 1543 | throw new Error(`malformed tree; index ${i} does not exist`); | |
| 1544 | } | |
| 1545 | } | |
| 1546 | } | |
| 1547 | for (const [newnode, subnode_list] of subnodes.entries()) { | |
| 1548 | const res = registry.searchTreeLoadByNodeID(newnode); | |
| 1549 | for (const [byte, node] of subnode_list) { | |
| 1550 | const branches = node.branches; | |
| 1551 | const might_have_prefix_branches = node.might_have_prefix_branches; | |
| 1552 | const i = branches.getIndex(byte); | |
| 1553 | branches.subtrees[i] = res; | |
| 1554 | const mhpI = might_have_prefix_branches.getIndex(byte); | |
| 1555 | if (mhpI !== -1) { | |
| 1556 | might_have_prefix_branches.subtrees[mhpI] = res; | |
| 1557 | } | |
| 1558 | } | |
| 1559 | layer.push(res); | |
| 1560 | } | |
| 1561 | } | |
| 1562 | } | |
| 1563 | ||
| 1564 | /** | |
| 1565 | * All matches for strings that start with the string represented by this node. | |
| 1566 | * @returns {AsyncGenerator<RoaringBitmap>} | |
| 1567 | */ | |
| 1568 | async* prefixMatches() { | |
| 1569 | /** @type {{node: Promise<SearchTree>, len: number}[]} */ | |
| 1570 | let layer = [{node: Promise.resolve(this.tree), len: 0}]; | |
| 1571 | // https://en.wikipedia.org/wiki/Heap_(data_structure)#Implementation_using_arrays | |
| 1572 | /** @type {{bitmap: RoaringBitmap, length: number}[]} */ | |
| 1573 | const backlog = []; | |
| 1574 | while (layer.length !== 0 || backlog.length !== 0) { | |
| 1575 | const current_layer = layer; | |
| 1576 | layer = []; | |
| 1577 | let minLength = null; | |
| 1578 | // push every entry in the current layer into the backlog, | |
| 1579 | // a min-heap of result entries | |
| 1580 | // we then yield the smallest ones (can't yield bigger ones | |
| 1581 | // if we want to do them in order) | |
| 1582 | for (const {node, len} of current_layer) { | |
| 1583 | const tree = await node; | |
| 1584 | const length = len + tree.data.length; | |
| 1585 | if (minLength === null || length < minLength) { | |
| 1586 | minLength = length; | |
| 1587 | } | |
| 1588 | let backlogSlot = backlog.length; | |
| 1589 | backlog.push({bitmap: tree.leaves_whole, length}); | |
| 1590 | while (backlogSlot > 0 && | |
| 1591 | backlog[backlogSlot].length < backlog[(backlogSlot - 1) >> 1].length | |
| 1592 | ) { | |
| 1593 | const parentSlot = (backlogSlot - 1) >> 1; | |
| 1594 | const parent = backlog[parentSlot]; | |
| 1595 | backlog[parentSlot] = backlog[backlogSlot]; | |
| 1596 | backlog[backlogSlot] = parent; | |
| 1597 | backlogSlot = parentSlot; | |
| 1598 | } | |
| 1599 | } | |
| 1600 | // yield nodes in length order, smallest one first | |
| 1601 | // we know that, whatever the smallest item is | |
| 1602 | // every child will be bigger than that | |
| 1603 | while (backlog.length !== 0) { | |
| 1604 | const backlogEntry = backlog[0]; | |
| 1605 | if (minLength !== null && backlogEntry.length > minLength) { | |
| 1606 | break; | |
| 1607 | } | |
| 1608 | if (!backlogEntry.bitmap.isEmpty()) { | |
| 1609 | yield backlogEntry.bitmap; | |
| 1610 | } | |
| 1611 | backlog[0] = backlog[backlog.length - 1]; | |
| 1612 | backlog.length -= 1; | |
| 1613 | let backlogSlot = 0; | |
| 1614 | const backlogLength = backlog.length; | |
| 1615 | while (backlogSlot < backlogLength) { | |
| 1616 | const leftSlot = (backlogSlot << 1) + 1; | |
| 1617 | const rightSlot = (backlogSlot << 1) + 2; | |
| 1618 | let smallest = backlogSlot; | |
| 1619 | if (leftSlot < backlogLength && | |
| 1620 | backlog[leftSlot].length < backlog[smallest].length | |
| 1621 | ) { | |
| 1622 | smallest = leftSlot; | |
| 1623 | } | |
| 1624 | if (rightSlot < backlogLength && | |
| 1625 | backlog[rightSlot].length < backlog[smallest].length | |
| 1626 | ) { | |
| 1627 | smallest = rightSlot; | |
| 1628 | } | |
| 1629 | if (smallest === backlogSlot) { | |
| 1630 | break; | |
| 1631 | } else { | |
| 1632 | const tmp = backlog[backlogSlot]; | |
| 1633 | backlog[backlogSlot] = backlog[smallest]; | |
| 1634 | backlog[smallest] = tmp; | |
| 1635 | backlogSlot = smallest; | |
| 1636 | } | |
| 1637 | } | |
| 1638 | } | |
| 1639 | // if we still have more subtrees to walk, then keep going | |
| 1640 | /** @type {HashTable<{byte: number, tree: SearchTree, len: number}[]>} */ | |
| 1641 | const subnodes = new HashTable(); | |
| 1642 | for await (const {node, len} of current_layer) { | |
| 1643 | const tree = await node; | |
| 1644 | const length = len + tree.data.length; | |
| 1645 | const mhp_branches = tree.might_have_prefix_branches; | |
| 1646 | const l = mhp_branches.subtrees.length; | |
| 1647 | for (let i = 0; i < l; ++i) { | |
| 1648 | const len = length + 1; | |
| 1649 | const subtree = mhp_branches.subtrees[i]; | |
| 1650 | if (subtree) { | |
| 1651 | layer.push({node: subtree, len}); | |
| 1652 | } else if (subtree === null) { | |
| 1653 | const byte = mhp_branches.getKey(i); | |
| 1654 | const newnode = mhp_branches.getNodeID(i); | |
| 1655 | if (!newnode) { | |
| 1656 | throw new Error(`malformed tree; no node for key ${byte}`); | |
| 1657 | } else { | |
| 1658 | let subnode_list = subnodes.get(newnode); | |
| 1659 | if (!subnode_list) { | |
| 1660 | subnode_list = [{byte, tree, len}]; | |
| 1661 | subnodes.set(newnode, subnode_list); | |
| 1662 | } else { | |
| 1663 | subnode_list.push({byte, tree, len}); | |
| 1664 | } | |
| 1665 | } | |
| 1666 | } else { | |
| 1667 | throw new Error(`malformed tree; index ${i} does not exist`); | |
| 1668 | } | |
| 1669 | } | |
| 1670 | } | |
| 1671 | for (const [newnode, subnode_list] of subnodes.entries()) { | |
| 1672 | const res = registry.searchTreeLoadByNodeID(newnode); | |
| 1673 | let len = Number.MAX_SAFE_INTEGER; | |
| 1674 | for (const {byte, tree, len: subtreelen} of subnode_list) { | |
| 1675 | if (subtreelen < len) { | |
| 1676 | len = subtreelen; | |
| 1677 | } | |
| 1678 | const mhp_branches = tree.might_have_prefix_branches; | |
| 1679 | const i = mhp_branches.getIndex(byte); | |
| 1680 | mhp_branches.subtrees[i] = res; | |
| 1681 | const branches = tree.branches; | |
| 1682 | const bi = branches.getIndex(byte); | |
| 1683 | branches.subtrees[bi] = res; | |
| 1684 | } | |
| 1685 | layer.push({node: res, len}); | |
| 1686 | } | |
| 1687 | } | |
| 1688 | } | |
| 1689 | ||
| 1690 | /** | |
| 1691 | * Returns all keys that are children of this node. | |
| 1692 | * @returns {Uint8Array} | |
| 1693 | */ | |
| 1694 | keys() { | |
| 1695 | const data = this.tree.data; | |
| 1696 | if (this.offset === data.length) { | |
| 1697 | return this.tree.branches.getKeys(); | |
| 1698 | } else { | |
| 1699 | return Uint8Array.of(data[this.offset]); | |
| 1700 | } | |
| 1701 | } | |
| 1702 | ||
| 1703 | /** | |
| 1704 | * Returns all nodes that are direct children of this node. | |
| 1705 | * @returns {[number, Promise<Trie>][]} | |
| 1706 | */ | |
| 1707 | children() { | |
| 1708 | const data = this.tree.data; | |
| 1709 | if (this.offset === data.length) { | |
| 1710 | /** @type {[number, Promise<Trie>][]} */ | |
| 1711 | const nodes = []; | |
| 1712 | let i = 0; | |
| 1713 | for (const [k, v] of this.tree.branches.entries()) { | |
| 1714 | /** @type {Promise<SearchTree>} */ | |
| 1715 | let node; | |
| 1716 | if (v) { | |
| 1717 | node = v; | |
| 1718 | } else { | |
| 1719 | const newnode = this.tree.branches.getNodeID(i); | |
| 1720 | if (!newnode) { | |
| 1721 | throw new Error(`malformed tree; no hash for key ${k}: ${newnode} \ | |
| 1722 | ${this.tree.branches.nodeids} ${this.tree.branches.getKeys()}`); | |
| 1723 | } | |
| 1724 | node = registry.searchTreeLoadByNodeID(newnode); | |
| 1725 | this.tree.branches.subtrees[i] = node; | |
| 1726 | const mhpI = this.tree.might_have_prefix_branches.getIndex(k); | |
| 1727 | if (mhpI !== -1) { | |
| 1728 | this.tree.might_have_prefix_branches.subtrees[mhpI] = node; | |
| 1729 | } | |
| 1730 | } | |
| 1731 | nodes.push([k, node.then(node => node.trie())]); | |
| 1732 | i += 1; | |
| 1733 | } | |
| 1734 | return nodes; | |
| 1735 | } else { | |
| 1736 | /** @type {number} */ | |
| 1737 | const codePoint = data[this.offset]; | |
| 1738 | const trie = new Trie(this.tree, this.offset + 1); | |
| 1739 | return [[codePoint, Promise.resolve(trie)]]; | |
| 1740 | } | |
| 1741 | } | |
| 1742 | ||
| 1743 | /** | |
| 1744 | * Returns all keys that are children of this node. | |
| 1745 | * @returns {Uint8Array} | |
| 1746 | */ | |
| 1747 | keysExcludeSuffixOnly() { | |
| 1748 | const data = this.tree.data; | |
| 1749 | if (this.offset === data.length) { | |
| 1750 | return this.tree.might_have_prefix_branches.getKeys(); | |
| 1751 | } else { | |
| 1752 | return Uint8Array.of(data[this.offset]); | |
| 1753 | } | |
| 1754 | } | |
| 1755 | ||
| 1756 | /** | |
| 1757 | * Returns all nodes that are direct children of this node. | |
| 1758 | * @returns {[number, Promise<Trie>][]} | |
| 1759 | */ | |
| 1760 | childrenExcludeSuffixOnly() { | |
| 1761 | const data = this.tree.data; | |
| 1762 | if (this.offset === data.length) { | |
| 1763 | /** @type {[number, Promise<Trie>][]} */ | |
| 1764 | const nodes = []; | |
| 1765 | let i = 0; | |
| 1766 | for (const [k, v] of this.tree.might_have_prefix_branches.entries()) { | |
| 1767 | /** @type {Promise<SearchTree>} */ | |
| 1768 | let node; | |
| 1769 | if (v) { | |
| 1770 | node = v; | |
| 1771 | } else { | |
| 1772 | const newnode = this.tree.might_have_prefix_branches.getNodeID(i); | |
| 1773 | if (!newnode) { | |
| 1774 | throw new Error(`malformed tree; no node for key ${k}`); | |
| 1775 | } | |
| 1776 | node = registry.searchTreeLoadByNodeID(newnode); | |
| 1777 | this.tree.might_have_prefix_branches.subtrees[i] = node; | |
| 1778 | this.tree.branches.subtrees[this.tree.branches.getIndex(k)] = node; | |
| 1779 | } | |
| 1780 | nodes.push([k, node.then(node => node.trie())]); | |
| 1781 | i += 1; | |
| 1782 | } | |
| 1783 | return nodes; | |
| 1784 | } else { | |
| 1785 | /** @type {number} */ | |
| 1786 | const codePoint = data[this.offset]; | |
| 1787 | const trie = new Trie(this.tree, this.offset + 1); | |
| 1788 | return [[codePoint, Promise.resolve(trie)]]; | |
| 1789 | } | |
| 1790 | } | |
| 1791 | ||
| 1792 | /** | |
| 1793 | * Returns a single node that is a direct child of this node. | |
| 1794 | * @param {number} byte | |
| 1795 | * @returns {Promise<Trie>?} | |
| 1796 | */ | |
| 1797 | child(byte) { | |
| 1798 | if (this.offset === this.tree.data.length) { | |
| 1799 | const i = this.tree.branches.getIndex(byte); | |
| 1800 | if (i !== -1) { | |
| 1801 | let branch = this.tree.branches.subtrees[i]; | |
| 1802 | if (branch === null) { | |
| 1803 | const newnode = this.tree.branches.getNodeID(i); | |
| 1804 | if (!newnode) { | |
| 1805 | throw new Error(`malformed tree; no node for key ${byte}`); | |
| 1806 | } | |
| 1807 | branch = registry.searchTreeLoadByNodeID(newnode); | |
| 1808 | this.tree.branches.subtrees[i] = branch; | |
| 1809 | const mhpI = this.tree.might_have_prefix_branches.getIndex(byte); | |
| 1810 | if (mhpI !== -1) { | |
| 1811 | this.tree.might_have_prefix_branches.subtrees[mhpI] = branch; | |
| 1812 | } | |
| 1813 | } | |
| 1814 | return branch.then(branch => branch.trie()); | |
| 1815 | } | |
| 1816 | } else if (this.tree.data[this.offset] === byte) { | |
| 1817 | return Promise.resolve(new Trie(this.tree, this.offset + 1)); | |
| 1818 | } | |
| 1819 | return null; | |
| 1820 | } | |
| 1821 | } | |
| 1822 | ||
| 1823 | class DataColumn { | |
| 1824 | /** | |
| 1825 | * Construct the wrapper object for a data column. | |
| 1826 | * @param {number[]} counts | |
| 1827 | * @param {Uint8Array} hashes | |
| 1828 | * @param {RoaringBitmap} emptyset | |
| 1829 | * @param {string} name | |
| 1830 | */ | |
| 1831 | constructor(counts, hashes, emptyset, name) { | |
| 1832 | this.hashes = hashes; | |
| 1833 | this.emptyset = emptyset; | |
| 1834 | this.name = name; | |
| 1835 | /** @type {{"hash": Uint8Array, "data": Promise<Uint8Array[]>?, "end": number}[]} */ | |
| 1836 | this.buckets = []; | |
| 1837 | this.bucket_keys = []; | |
| 1838 | const l = counts.length; | |
| 1839 | let k = 0; | |
| 1840 | let totalLength = 0; | |
| 1841 | for (let i = 0; i < l; ++i) { | |
| 1842 | const count = counts[i]; | |
| 1843 | totalLength += count; | |
| 1844 | const start = k; | |
| 1845 | for (let j = 0; j < count; ++j) { | |
| 1846 | if (emptyset.contains(k)) { | |
| 1847 | j -= 1; | |
| 1848 | } | |
| 1849 | k += 1; | |
| 1850 | } | |
| 1851 | const end = k; | |
| 1852 | const bucket = {hash: hashes.subarray(i * 6, (i + 1) * 6), data: null, end, count}; | |
| 1853 | this.buckets.push(bucket); | |
| 1854 | this.bucket_keys.push(start); | |
| 1855 | } | |
| 1856 | this.length = totalLength; | |
| 1857 | } | |
| 1858 | /** | |
| 1859 | * Check if a cell contains the empty string. | |
| 1860 | * @param {number} id | |
| 1861 | * @returns {boolean} | |
| 1862 | */ | |
| 1863 | isEmpty(id) { | |
| 1864 | return this.emptyset.contains(id); | |
| 1865 | } | |
| 1866 | /** | |
| 1867 | * Look up a cell by row ID. | |
| 1868 | * @param {number} id | |
| 1869 | * @returns {Promise<Uint8Array|undefined>} | |
| 1870 | */ | |
| 1871 | async at(id) { | |
| 1872 | if (this.emptyset.contains(id)) { | |
| 1873 | return Promise.resolve(EMPTY_UINT8); | |
| 1874 | } else { | |
| 1875 | let idx = -1; | |
| 1876 | while (this.bucket_keys[idx + 1] <= id) { | |
| 1877 | idx += 1; | |
| 1878 | } | |
| 1879 | if (idx === -1 || idx >= this.bucket_keys.length) { | |
| 1880 | return Promise.resolve(undefined); | |
| 1881 | } else { | |
| 1882 | const start = this.bucket_keys[idx]; | |
| 1883 | const {hash, end} = this.buckets[idx]; | |
| 1884 | let data = this.buckets[idx].data; | |
| 1885 | if (data === null) { | |
| 1886 | const dataSansEmptyset = await registry.dataLoadByNameAndHash( | |
| 1887 | this.name, | |
| 1888 | hash, | |
| 1889 | ); | |
| 1890 | // After the `await` resolves, another task might fill | |
| 1891 | // in the data. If so, we should use that. | |
| 1892 | data = this.buckets[idx].data; | |
| 1893 | if (data !== null) { | |
| 1894 | return (await data)[id - start]; | |
| 1895 | } | |
| 1896 | /** @type {(Uint8Array[])|null} */ | |
| 1897 | let dataWithEmptyset = null; | |
| 1898 | let pos = start; | |
| 1899 | let insertCount = 0; | |
| 1900 | while (pos < end) { | |
| 1901 | if (this.emptyset.contains(pos)) { | |
| 1902 | if (dataWithEmptyset === null) { | |
| 1903 | dataWithEmptyset = dataSansEmptyset.splice(0, insertCount); | |
| 1904 | } else if (insertCount !== 0) { | |
| 1905 | dataWithEmptyset.push( | |
| 1906 | ...dataSansEmptyset.splice(0, insertCount), | |
| 1907 | ); | |
| 1908 | } | |
| 1909 | insertCount = 0; | |
| 1910 | dataWithEmptyset.push(EMPTY_UINT8); | |
| 1911 | } else { | |
| 1912 | insertCount += 1; | |
| 1913 | } | |
| 1914 | pos += 1; | |
| 1915 | } | |
| 1916 | data = Promise.resolve( | |
| 1917 | dataWithEmptyset === null ? | |
| 1918 | dataSansEmptyset : | |
| 1919 | dataWithEmptyset.concat(dataSansEmptyset), | |
| 1920 | ); | |
| 1921 | this.buckets[idx].data = data; | |
| 1922 | } | |
| 1923 | return (await data)[id - start]; | |
| 1924 | } | |
| 1925 | } | |
| 1926 | } | |
| 1927 | } | |
| 1928 | ||
| 1929 | class Database { | |
| 1930 | /** | |
| 1931 | * The primary frontend for accessing data in this index. | |
| 1932 | * | |
| 1933 | * @param {Map<string, SearchTree>} searchTreeRoots | |
| 1934 | * @param {Map<string, DataColumn>} dataColumns | |
| 1935 | */ | |
| 1936 | constructor(searchTreeRoots, dataColumns) { | |
| 1937 | this.searchTreeRoots = searchTreeRoots; | |
| 1938 | this.dataColumns = dataColumns; | |
| 1939 | } | |
| 1940 | /** | |
| 1941 | * Search a column by name, returning verbatim matched IDs. | |
| 1942 | * @param {string} colname | |
| 1943 | * @returns {SearchTree|undefined} | |
| 1944 | */ | |
| 1945 | getIndex(colname) { | |
| 1946 | return this.searchTreeRoots.get(colname); | |
| 1947 | } | |
| 1948 | /** | |
| 1949 | * Look up a cell by column ID and row ID. | |
| 1950 | * @param {string} colname | |
| 1951 | * @returns {DataColumn|undefined} | |
| 1952 | */ | |
| 1953 | getData(colname) { | |
| 1954 | return this.dataColumns.get(colname); | |
| 1955 | } | |
| 1956 | } | |
| 1957 | ||
| 1958 | /** | |
| 1959 | * Load a data column. | |
| 1960 | * @param {Uint8Array} data | |
| 1961 | */ | |
| 1962 | function loadColumnFromBytes(data) { | |
| 1963 | const hashBuf = Uint8Array.of(0, 0, 0, 0, 0, 0, 0, 0); | |
| 1964 | const truncatedHash = hashBuf.subarray(2, 8); | |
| 1965 | siphashOfBytes(data, 0, 0, 0, 0, hashBuf); | |
| 1966 | const cb = registry.dataColumnLoadPromiseCallbacks.get(truncatedHash); | |
| 1967 | if (cb) { | |
| 1968 | const backrefs = []; | |
| 1969 | const dataSansEmptyset = []; | |
| 1970 | let i = 0; | |
| 1971 | const l = data.length; | |
| 1972 | while (i < l) { | |
| 1973 | let c = data[i]; | |
| 1974 | if (c >= 48 && c <= 63) { // 48 = "0", 63 = "?" | |
| 1975 | dataSansEmptyset.push(backrefs[c - 48]); | |
| 1976 | i += 1; | |
| 1977 | } else { | |
| 1978 | let n = 0; | |
| 1979 | while (c < 96) { // 96 = "`" | |
| 1980 | n = (n << 4) | (c & 0xF); | |
| 1981 | i += 1; | |
| 1982 | c = data[i]; | |
| 1983 | } | |
| 1984 | n = (n << 4) | (c & 0xF); | |
| 1985 | i += 1; | |
| 1986 | const item = data.subarray(i, i + n); | |
| 1987 | dataSansEmptyset.push(item); | |
| 1988 | i += n; | |
| 1989 | backrefs.unshift(item); | |
| 1990 | if (backrefs.length > 16) { | |
| 1991 | backrefs.pop(); | |
| 1992 | } | |
| 1993 | } | |
| 1994 | } | |
| 1995 | cb(null, dataSansEmptyset); | |
| 1996 | } | |
| 1997 | } | |
| 1998 | ||
| 1999 | /** | |
| 2000 | * @param {string} inputBase64 | |
| 2001 | * @returns {[Uint8Array, SearchTree]} | |
| 2002 | */ | |
| 2003 | function makeSearchTreeFromBase64(inputBase64) { | |
| 2004 | const input = makeUint8ArrayFromBase64(inputBase64); | |
| 2005 | let i = 0; | |
| 2006 | const l = input.length; | |
| 2007 | /** @type {HashTable<SearchTree>} */ | |
| 2008 | const stash = new HashTable(); | |
| 2009 | const hash = Uint8Array.of(0, 0, 0, 0, 0, 0, 0, 0); | |
| 2010 | const truncatedHash = new Uint8Array(hash.buffer, 2, 6); | |
| 2011 | // used for handling compressed (that is, relative-offset) nodes | |
| 2012 | /** @type {{hash: Uint8Array, used: boolean}[]} */ | |
| 2013 | const hash_history = []; | |
| 2014 | /** @type {Uint8Array[]} */ | |
| 2015 | const data_history = []; | |
| 2016 | let canonical = EMPTY_UINT8; | |
| 2017 | /** @type {SearchTree} */ | |
| 2018 | let tree = new SearchTree( | |
| 2019 | EMPTY_SEARCH_TREE_BRANCHES, | |
| 2020 | EMPTY_SEARCH_TREE_BRANCHES, | |
| 2021 | EMPTY_UINT8, | |
| 2022 | EMPTY_BITMAP, | |
| 2023 | EMPTY_BITMAP, | |
| 2024 | ); | |
| 2025 | /** | |
| 2026 | * @param {Uint8Array} input | |
| 2027 | * @param {number} i | |
| 2028 | * @param {number} compression_tag | |
| 2029 | * @returns {{ | |
| 2030 | * "cpbranches": Uint8Array, | |
| 2031 | * "csbranches": Uint8Array, | |
| 2032 | * "might_have_prefix_branches": SearchTreeBranches, | |
| 2033 | * "branches": SearchTreeBranches, | |
| 2034 | * "cpnodes": Uint8Array, | |
| 2035 | * "csnodes": Uint8Array, | |
| 2036 | * "consumed_len_bytes": number, | |
| 2037 | * }} | |
| 2038 | */ | |
| 2039 | function makeBranchesFromBinaryData( | |
| 2040 | input, | |
| 2041 | i, | |
| 2042 | compression_tag, | |
| 2043 | ) { | |
| 2044 | const is_pure_suffixes_only_node = (compression_tag & 0x01) !== 0x00; | |
| 2045 | const is_stack_compressed = (compression_tag & 0x02) !== 0; | |
| 2046 | const is_long_compressed = (compression_tag & 0x04) !== 0; | |
| 2047 | const all_children_are_compressed = | |
| 2048 | (compression_tag & 0xF0) === 0xF0 && !is_long_compressed; | |
| 2049 | const any_children_are_compressed = | |
| 2050 | (compression_tag & 0xF0) !== 0x00 || is_long_compressed; | |
| 2051 | const start_point = i; | |
| 2052 | let cplen; | |
| 2053 | let cslen; | |
| 2054 | let alphabitmap = null; | |
| 2055 | if (is_pure_suffixes_only_node) { | |
| 2056 | cplen = 0; | |
| 2057 | cslen = input[i]; | |
| 2058 | i += 1; | |
| 2059 | if (cslen >= 0xc0) { | |
| 2060 | alphabitmap = SearchTreeBranchesLongAlphaBitmap; | |
| 2061 | cslen = cslen & 0x3F; | |
| 2062 | } else if (cslen >= 0x80) { | |
| 2063 | alphabitmap = SearchTreeBranchesShortAlphaBitmap; | |
| 2064 | cslen = cslen & 0x7F; | |
| 2065 | } | |
| 2066 | } else { | |
| 2067 | cplen = input[i]; | |
| 2068 | i += 1; | |
| 2069 | cslen = input[i]; | |
| 2070 | i += 1; | |
| 2071 | if (cplen === 0xff && cslen === 0xff) { | |
| 2072 | cplen = 0x100; | |
| 2073 | cslen = 0; | |
| 2074 | } else if (cplen >= 0xc0 && cslen >= 0xc0) { | |
| 2075 | alphabitmap = SearchTreeBranchesLongAlphaBitmap; | |
| 2076 | cplen = cplen & 0x3F; | |
| 2077 | cslen = cslen & 0x3F; | |
| 2078 | } else if (cplen >= 0x80 && cslen >= 0x80) { | |
| 2079 | alphabitmap = SearchTreeBranchesShortAlphaBitmap; | |
| 2080 | cplen = cplen & 0x7F; | |
| 2081 | cslen = cslen & 0x7F; | |
| 2082 | } | |
| 2083 | } | |
| 2084 | let j = 0; | |
| 2085 | /** @type {Uint8Array} */ | |
| 2086 | let cpnodes; | |
| 2087 | if (any_children_are_compressed) { | |
| 2088 | cpnodes = cplen === 0 ? EMPTY_UINT8 : new Uint8Array(cplen * 6); | |
| 2089 | while (j < cplen) { | |
| 2090 | const is_compressed = all_children_are_compressed || | |
| 2091 | ((0x10 << j) & compression_tag) !== 0; | |
| 2092 | if (is_compressed) { | |
| 2093 | let slot = hash_history.length - 1; | |
| 2094 | if (is_stack_compressed) { | |
| 2095 | while (hash_history[slot].used) { | |
| 2096 | slot -= 1; | |
| 2097 | } | |
| 2098 | } else { | |
| 2099 | slot -= input[i]; | |
| 2100 | i += 1; | |
| 2101 | } | |
| 2102 | hash_history[slot].used = true; | |
| 2103 | cpnodes.set( | |
| 2104 | hash_history[slot].hash, | |
| 2105 | j * 6, | |
| 2106 | ); | |
| 2107 | } else { | |
| 2108 | const joff = j * 6; | |
| 2109 | cpnodes[joff + 0] = input[i + 0]; | |
| 2110 | cpnodes[joff + 1] = input[i + 1]; | |
| 2111 | cpnodes[joff + 2] = input[i + 2]; | |
| 2112 | cpnodes[joff + 3] = input[i + 3]; | |
| 2113 | cpnodes[joff + 4] = input[i + 4]; | |
| 2114 | cpnodes[joff + 5] = input[i + 5]; | |
| 2115 | i += 6; | |
| 2116 | } | |
| 2117 | j += 1; | |
| 2118 | } | |
| 2119 | } else { | |
| 2120 | cpnodes = cplen === 0 ? EMPTY_UINT8 : input.subarray(i, i + (cplen * 6)); | |
| 2121 | i += cplen * 6; | |
| 2122 | } | |
| 2123 | j = 0; | |
| 2124 | /** @type {Uint8Array} */ | |
| 2125 | let csnodes; | |
| 2126 | if (any_children_are_compressed) { | |
| 2127 | csnodes = cslen === 0 ? EMPTY_UINT8 : new Uint8Array(cslen * 6); | |
| 2128 | while (j < cslen) { | |
| 2129 | const is_compressed = all_children_are_compressed || | |
| 2130 | ((0x10 << (cplen + j)) & compression_tag) !== 0; | |
| 2131 | if (is_compressed) { | |
| 2132 | let slot = hash_history.length - 1; | |
| 2133 | if (is_stack_compressed) { | |
| 2134 | while (hash_history[slot].used) { | |
| 2135 | slot -= 1; | |
| 2136 | } | |
| 2137 | } else { | |
| 2138 | slot -= input[i]; | |
| 2139 | i += 1; | |
| 2140 | } | |
| 2141 | hash_history[slot].used = true; | |
| 2142 | csnodes.set( | |
| 2143 | hash_history[slot].hash, | |
| 2144 | j * 6, | |
| 2145 | ); | |
| 2146 | } else { | |
| 2147 | const joff = j * 6; | |
| 2148 | csnodes[joff + 0] = input[i + 0]; | |
| 2149 | csnodes[joff + 1] = input[i + 1]; | |
| 2150 | csnodes[joff + 2] = input[i + 2]; | |
| 2151 | csnodes[joff + 3] = input[i + 3]; | |
| 2152 | csnodes[joff + 4] = input[i + 4]; | |
| 2153 | csnodes[joff + 5] = input[i + 5]; | |
| 2154 | i += 6; | |
| 2155 | } | |
| 2156 | j += 1; | |
| 2157 | } | |
| 2158 | } else { | |
| 2159 | csnodes = cslen === 0 ? EMPTY_UINT8 : input.subarray(i, i + (cslen * 6)); | |
| 2160 | i += cslen * 6; | |
| 2161 | } | |
| 2162 | let cpbranches; | |
| 2163 | let might_have_prefix_branches; | |
| 2164 | if (cplen === 0) { | |
| 2165 | cpbranches = EMPTY_UINT8; | |
| 2166 | might_have_prefix_branches = EMPTY_SEARCH_TREE_BRANCHES; | |
| 2167 | } else if (alphabitmap) { | |
| 2168 | cpbranches = new Uint8Array(input.buffer, i + input.byteOffset, alphabitmap.width); | |
| 2169 | const branchset = (alphabitmap.width === 4 ? (input[i + 3] << 24) : 0) | | |
| 2170 | (input[i + 2] << 16) | | |
| 2171 | (input[i + 1] << 8) | | |
| 2172 | input[i]; | |
| 2173 | might_have_prefix_branches = new alphabitmap(branchset, cpnodes); | |
| 2174 | i += alphabitmap.width; | |
| 2175 | } else { | |
| 2176 | cpbranches = new Uint8Array(input.buffer, i + input.byteOffset, cplen); | |
| 2177 | might_have_prefix_branches = new SearchTreeBranchesArray(cpbranches, cpnodes); | |
| 2178 | i += cplen; | |
| 2179 | } | |
| 2180 | let csbranches; | |
| 2181 | let branches; | |
| 2182 | if (cslen === 0) { | |
| 2183 | csbranches = EMPTY_UINT8; | |
| 2184 | branches = might_have_prefix_branches; | |
| 2185 | } else if (alphabitmap) { | |
| 2186 | csbranches = new Uint8Array(input.buffer, i + input.byteOffset, alphabitmap.width); | |
| 2187 | const branchset = (alphabitmap.width === 4 ? (input[i + 3] << 24) : 0) | | |
| 2188 | (input[i + 2] << 16) | | |
| 2189 | (input[i + 1] << 8) | | |
| 2190 | input[i]; | |
| 2191 | if (cplen === 0) { | |
| 2192 | branches = new alphabitmap(branchset, csnodes); | |
| 2193 | } else { | |
| 2194 | const cpoffset = i - alphabitmap.width; | |
| 2195 | const cpbranchset = | |
| 2196 | (alphabitmap.width === 4 ? (input[cpoffset + 3] << 24) : 0) | | |
| 2197 | (input[cpoffset + 2] << 16) | | |
| 2198 | (input[cpoffset + 1] << 8) | | |
| 2199 | input[cpoffset]; | |
| 2200 | const hashes = new Uint8Array((cplen + cslen) * 6); | |
| 2201 | let cpi = 0; | |
| 2202 | let csi = 0; | |
| 2203 | let j = 0; | |
| 2204 | for (let k = 0; k < alphabitmap.ALPHABITMAP_CHARS.length; k += 1) { | |
| 2205 | if (branchset & (1 << k)) { | |
| 2206 | hashes[j + 0] = csnodes[csi + 0]; | |
| 2207 | hashes[j + 1] = csnodes[csi + 1]; | |
| 2208 | hashes[j + 2] = csnodes[csi + 2]; | |
| 2209 | hashes[j + 3] = csnodes[csi + 3]; | |
| 2210 | hashes[j + 4] = csnodes[csi + 4]; | |
| 2211 | hashes[j + 5] = csnodes[csi + 5]; | |
| 2212 | j += 6; | |
| 2213 | csi += 6; | |
| 2214 | } else if (cpbranchset & (1 << k)) { | |
| 2215 | hashes[j + 0] = cpnodes[cpi + 0]; | |
| 2216 | hashes[j + 1] = cpnodes[cpi + 1]; | |
| 2217 | hashes[j + 2] = cpnodes[cpi + 2]; | |
| 2218 | hashes[j + 3] = cpnodes[cpi + 3]; | |
| 2219 | hashes[j + 4] = cpnodes[cpi + 4]; | |
| 2220 | hashes[j + 5] = cpnodes[cpi + 5]; | |
| 2221 | j += 6; | |
| 2222 | cpi += 6; | |
| 2223 | } | |
| 2224 | } | |
| 2225 | branches = new alphabitmap(branchset | cpbranchset, hashes); | |
| 2226 | } | |
| 2227 | i += alphabitmap.width; | |
| 2228 | } else { | |
| 2229 | csbranches = new Uint8Array(input.buffer, i + input.byteOffset, cslen); | |
| 2230 | if (cplen === 0) { | |
| 2231 | branches = new SearchTreeBranchesArray(csbranches, csnodes); | |
| 2232 | } else { | |
| 2233 | const branchset = new Uint8Array(cplen + cslen); | |
| 2234 | const hashes = new Uint8Array((cplen + cslen) * 6); | |
| 2235 | let cpi = 0; | |
| 2236 | let csi = 0; | |
| 2237 | let j = 0; | |
| 2238 | while (cpi < cplen || csi < cslen) { | |
| 2239 | if (cpi >= cplen || (csi < cslen && cpbranches[cpi] > csbranches[csi])) { | |
| 2240 | branchset[j] = csbranches[csi]; | |
| 2241 | const joff = j * 6; | |
| 2242 | const csioff = csi * 6; | |
| 2243 | hashes[joff + 0] = csnodes[csioff + 0]; | |
| 2244 | hashes[joff + 1] = csnodes[csioff + 1]; | |
| 2245 | hashes[joff + 2] = csnodes[csioff + 2]; | |
| 2246 | hashes[joff + 3] = csnodes[csioff + 3]; | |
| 2247 | hashes[joff + 4] = csnodes[csioff + 4]; | |
| 2248 | hashes[joff + 5] = csnodes[csioff + 5]; | |
| 2249 | csi += 1; | |
| 2250 | } else { | |
| 2251 | branchset[j] = cpbranches[cpi]; | |
| 2252 | const joff = j * 6; | |
| 2253 | const cpioff = cpi * 6; | |
| 2254 | hashes[joff + 0] = cpnodes[cpioff + 0]; | |
| 2255 | hashes[joff + 1] = cpnodes[cpioff + 1]; | |
| 2256 | hashes[joff + 2] = cpnodes[cpioff + 2]; | |
| 2257 | hashes[joff + 3] = cpnodes[cpioff + 3]; | |
| 2258 | hashes[joff + 4] = cpnodes[cpioff + 4]; | |
| 2259 | hashes[joff + 5] = cpnodes[cpioff + 5]; | |
| 2260 | cpi += 1; | |
| 2261 | } | |
| 2262 | j += 1; | |
| 2263 | } | |
| 2264 | branches = new SearchTreeBranchesArray(branchset, hashes); | |
| 2265 | } | |
| 2266 | i += cslen; | |
| 2267 | } | |
| 2268 | return { | |
| 2269 | consumed_len_bytes: i - start_point, | |
| 2270 | cpbranches, | |
| 2271 | csbranches, | |
| 2272 | cpnodes, | |
| 2273 | csnodes, | |
| 2274 | branches, | |
| 2275 | might_have_prefix_branches, | |
| 2276 | }; | |
| 2277 | } | |
| 2278 | while (i < l) { | |
| 2279 | const start = i; | |
| 2280 | let data; | |
| 2281 | // compression_tag = 1 means pure-suffixes-only, | |
| 2282 | // which is not considered "compressed" for the purposes of this loop | |
| 2283 | // because that's the canonical, hashed version of the data | |
| 2284 | let compression_tag = input[i]; | |
| 2285 | const is_pure_suffixes_only_node = (compression_tag & 0x01) !== 0; | |
| 2286 | if (compression_tag > 1) { | |
| 2287 | // compressed node | |
| 2288 | const is_long_compressed = (compression_tag & 0x04) !== 0; | |
| 2289 | const is_data_compressed = (compression_tag & 0x08) !== 0; | |
| 2290 | i += 1; | |
| 2291 | if (is_long_compressed) { | |
| 2292 | compression_tag |= input[i] << 8; | |
| 2293 | i += 1; | |
| 2294 | compression_tag |= input[i] << 16; | |
| 2295 | i += 1; | |
| 2296 | } | |
| 2297 | let dlen = input[i]; | |
| 2298 | i += 1; | |
| 2299 | if (is_data_compressed) { | |
| 2300 | data = data_history[data_history.length - dlen - 1]; | |
| 2301 | dlen = data.length; | |
| 2302 | } else { | |
| 2303 | data = dlen === 0 ? | |
| 2304 | EMPTY_UINT8 : | |
| 2305 | new Uint8Array(input.buffer, i + input.byteOffset, dlen); | |
| 2306 | i += dlen; | |
| 2307 | } | |
| 2308 | const coffset = i; | |
| 2309 | const { | |
| 2310 | cpbranches, | |
| 2311 | csbranches, | |
| 2312 | cpnodes, | |
| 2313 | csnodes, | |
| 2314 | consumed_len_bytes: branches_consumed_len_bytes, | |
| 2315 | branches, | |
| 2316 | might_have_prefix_branches, | |
| 2317 | } = makeBranchesFromBinaryData(input, i, compression_tag); | |
| 2318 | i += branches_consumed_len_bytes; | |
| 2319 | let whole; | |
| 2320 | let suffix; | |
| 2321 | if (is_pure_suffixes_only_node) { | |
| 2322 | whole = EMPTY_BITMAP; | |
| 2323 | suffix = input[i] === 0 ? | |
| 2324 | EMPTY_BITMAP1 : | |
| 2325 | new RoaringBitmap(input, i); | |
| 2326 | i += suffix.consumed_len_bytes; | |
| 2327 | } else if (input[i] === 0xff) { | |
| 2328 | whole = EMPTY_BITMAP; | |
| 2329 | suffix = EMPTY_BITMAP1; | |
| 2330 | i += 1; | |
| 2331 | } else { | |
| 2332 | whole = input[i] === 0 ? | |
| 2333 | EMPTY_BITMAP1 : | |
| 2334 | new RoaringBitmap(input, i); | |
| 2335 | i += whole.consumed_len_bytes; | |
| 2336 | suffix = input[i] === 0 ? | |
| 2337 | EMPTY_BITMAP1 : | |
| 2338 | new RoaringBitmap(input, i); | |
| 2339 | i += suffix.consumed_len_bytes; | |
| 2340 | } | |
| 2341 | tree = new SearchTree( | |
| 2342 | branches, | |
| 2343 | might_have_prefix_branches, | |
| 2344 | data, | |
| 2345 | whole, | |
| 2346 | suffix, | |
| 2347 | ); | |
| 2348 | const clen = ( | |
| 2349 | (is_pure_suffixes_only_node ? 3 : 4) + // lengths of children and data | |
| 2350 | dlen + | |
| 2351 | cpnodes.length + csnodes.length + | |
| 2352 | cpbranches.length + csbranches.length + | |
| 2353 | whole.consumed_len_bytes + | |
| 2354 | suffix.consumed_len_bytes | |
| 2355 | ); | |
| 2356 | if (canonical.length < clen) { | |
| 2357 | canonical = new Uint8Array(clen); | |
| 2358 | } | |
| 2359 | let ci = 0; | |
| 2360 | canonical[ci] = is_pure_suffixes_only_node ? 1 : 0; | |
| 2361 | ci += 1; | |
| 2362 | canonical[ci] = dlen; | |
| 2363 | ci += 1; | |
| 2364 | canonical.set(data, ci); | |
| 2365 | ci += dlen; | |
| 2366 | canonical[ci] = input[coffset]; | |
| 2367 | ci += 1; | |
| 2368 | if (!is_pure_suffixes_only_node) { | |
| 2369 | canonical[ci] = input[coffset + 1]; | |
| 2370 | ci += 1; | |
| 2371 | } | |
| 2372 | canonical.set(cpnodes, ci); | |
| 2373 | ci += cpnodes.length; | |
| 2374 | canonical.set(csnodes, ci); | |
| 2375 | ci += csnodes.length; | |
| 2376 | canonical.set(cpbranches, ci); | |
| 2377 | ci += cpbranches.length; | |
| 2378 | canonical.set(csbranches, ci); | |
| 2379 | ci += csbranches.length; | |
| 2380 | const leavesOffset = i - whole.consumed_len_bytes - suffix.consumed_len_bytes; | |
| 2381 | for (let j = leavesOffset; j < i; j += 1) { | |
| 2382 | canonical[ci + j - leavesOffset] = input[j]; | |
| 2383 | } | |
| 2384 | siphashOfBytes(canonical.subarray(0, clen), 0, 0, 0, 0, hash); | |
| 2385 | hash[2] &= 0x7f; | |
| 2386 | } else { | |
| 2387 | // uncompressed node | |
| 2388 | const dlen = input [i + 1]; | |
| 2389 | i += 2; | |
| 2390 | if (dlen === 0) { | |
| 2391 | data = EMPTY_UINT8; | |
| 2392 | } else { | |
| 2393 | data = new Uint8Array(input.buffer, i + input.byteOffset, dlen); | |
| 2394 | } | |
| 2395 | i += dlen; | |
| 2396 | const { | |
| 2397 | consumed_len_bytes: branches_consumed_len_bytes, | |
| 2398 | branches, | |
| 2399 | might_have_prefix_branches, | |
| 2400 | } = makeBranchesFromBinaryData(input, i, compression_tag); | |
| 2401 | i += branches_consumed_len_bytes; | |
| 2402 | let whole; | |
| 2403 | let suffix; | |
| 2404 | if (is_pure_suffixes_only_node) { | |
| 2405 | whole = EMPTY_BITMAP; | |
| 2406 | suffix = input[i] === 0 ? | |
| 2407 | EMPTY_BITMAP1 : | |
| 2408 | new RoaringBitmap(input, i); | |
| 2409 | i += suffix.consumed_len_bytes; | |
| 2410 | } else if (input[i] === 0xff) { | |
| 2411 | whole = EMPTY_BITMAP; | |
| 2412 | suffix = EMPTY_BITMAP; | |
| 2413 | i += 1; | |
| 2414 | } else { | |
| 2415 | whole = input[i] === 0 ? | |
| 2416 | EMPTY_BITMAP1 : | |
| 2417 | new RoaringBitmap(input, i); | |
| 2418 | i += whole.consumed_len_bytes; | |
| 2419 | suffix = input[i] === 0 ? | |
| 2420 | EMPTY_BITMAP1 : | |
| 2421 | new RoaringBitmap(input, i); | |
| 2422 | i += suffix.consumed_len_bytes; | |
| 2423 | } | |
| 2424 | siphashOfBytes(new Uint8Array( | |
| 2425 | input.buffer, | |
| 2426 | start + input.byteOffset, | |
| 2427 | i - start, | |
| 2428 | ), 0, 0, 0, 0, hash); | |
| 2429 | hash[2] &= 0x7f; | |
| 2430 | tree = new SearchTree( | |
| 2431 | branches, | |
| 2432 | might_have_prefix_branches, | |
| 2433 | data, | |
| 2434 | whole, | |
| 2435 | suffix, | |
| 2436 | ); | |
| 2437 | } | |
| 2438 | hash_history.push({hash: truncatedHash.slice(), used: false}); | |
| 2439 | if (data.length !== 0) { | |
| 2440 | data_history.push(data); | |
| 2441 | } | |
| 2442 | const tree_branch_nodeids = tree.branches.nodeids; | |
| 2443 | const tree_branch_subtrees = tree.branches.subtrees; | |
| 2444 | let j = 0; | |
| 2445 | let lb = tree.branches.subtrees.length; | |
| 2446 | while (j < lb) { | |
| 2447 | // node id with a 1 in its most significant bit is inlined, and, so | |
| 2448 | // it won't be in the stash | |
| 2449 | if ((tree_branch_nodeids[j * 6] & 0x80) === 0) { | |
| 2450 | const subtree = stash.getWithOffsetKey(tree_branch_nodeids, j * 6); | |
| 2451 | if (subtree !== undefined) { | |
| 2452 | tree_branch_subtrees[j] = Promise.resolve(subtree); | |
| 2453 | } | |
| 2454 | } | |
| 2455 | j += 1; | |
| 2456 | } | |
| 2457 | const tree_mhp_branch_nodeids = tree.might_have_prefix_branches.nodeids; | |
| 2458 | const tree_mhp_branch_subtrees = tree.might_have_prefix_branches.subtrees; | |
| 2459 | j = 0; | |
| 2460 | lb = tree.might_have_prefix_branches.subtrees.length; | |
| 2461 | while (j < lb) { | |
| 2462 | // node id with a 1 in its most significant bit is inlined, and, so | |
| 2463 | // it won't be in the stash | |
| 2464 | if ((tree_mhp_branch_nodeids[j * 6] & 0x80) === 0) { | |
| 2465 | const subtree = stash.getWithOffsetKey(tree_mhp_branch_nodeids, j * 6); | |
| 2466 | if (subtree !== undefined) { | |
| 2467 | tree_mhp_branch_subtrees[j] = Promise.resolve(subtree); | |
| 2468 | } | |
| 2469 | } | |
| 2470 | j += 1; | |
| 2471 | } | |
| 2472 | if (i !== l) { | |
| 2473 | stash.set(truncatedHash, tree); | |
| 2474 | } | |
| 2475 | } | |
| 2476 | return [truncatedHash, tree]; | |
| 2477 | } | |
| 2478 | ||
| 2479 | return new Promise((resolve, reject) => { | |
| 2480 | registry.searchTreeRootCallback = (error, data) => { | |
| 2481 | if (data) { | |
| 2482 | resolve(data); | |
| 2483 | } else { | |
| 2484 | reject(error); | |
| 2485 | } | |
| 2486 | }; | |
| 2487 | hooks.loadRoot(callbacks); | |
| 2488 | }); | |
| 2489 | } | |
| 2490 | ||
| 2491 | if (typeof window !== "undefined") { | |
| 2492 | window.Stringdex = { | |
| 2493 | loadDatabase, | |
| 2494 | }; | |
| 2495 | window.RoaringBitmap = RoaringBitmap; | |
| 2496 | if (window.StringdexOnload) { | |
| 2497 | window.StringdexOnload.forEach(cb => cb(window.Stringdex)); | |
| 2498 | } | |
| 2499 | } else { | |
| 2500 | /** @type {stringdex.Stringdex} */ | |
| 2501 | // eslint-disable-next-line no-undef | |
| 2502 | module.exports.Stringdex = { | |
| 2503 | loadDatabase, | |
| 2504 | }; | |
| 2505 | /** @type {stringdex.RoaringBitmap} */ | |
| 2506 | // eslint-disable-next-line no-undef | |
| 2507 | module.exports.RoaringBitmap = RoaringBitmap; | |
| 2508 | } | |
| 2509 | ||
| 2510 | // eslint-disable-next-line max-len | |
| 2511 | // polyfill https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64 | |
| 2512 | /** | |
| 2513 | * @type {function(string): Uint8Array} base64 | |
| 2514 | */ | |
| 2515 | //@ts-expect-error | |
| 2516 | const makeUint8ArrayFromBase64 = Uint8Array.fromBase64 ? Uint8Array.fromBase64 : (string => { | |
| 2517 | const bytes_as_string = atob(string); | |
| 2518 | const l = bytes_as_string.length; | |
| 2519 | const bytes = new Uint8Array(l); | |
| 2520 | for (let i = 0; i < l; ++i) { | |
| 2521 | bytes[i] = bytes_as_string.charCodeAt(i); | |
| 2522 | } | |
| 2523 | return bytes; | |
| 2524 | }); | |
| 2525 | /** | |
| 2526 | * @type {function(string): Uint8Array} base64 | |
| 2527 | */ | |
| 2528 | //@ts-expect-error | |
| 2529 | const makeUint8ArrayFromHex = Uint8Array.fromHex ? Uint8Array.fromHex : (string => { | |
| 2530 | /** @type {Object<string, number>} */ | |
| 2531 | const alpha = { | |
| 2532 | "0": 0, "1": 1, | |
| 2533 | "2": 2, "3": 3, | |
| 2534 | "4": 4, "5": 5, | |
| 2535 | "6": 6, "7": 7, | |
| 2536 | "8": 8, "9": 9, | |
| 2537 | "a": 10, "b": 11, | |
| 2538 | "A": 10, "B": 11, | |
| 2539 | "c": 12, "d": 13, | |
| 2540 | "C": 12, "D": 13, | |
| 2541 | "e": 14, "f": 15, | |
| 2542 | "E": 14, "F": 15, | |
| 2543 | }; | |
| 2544 | const l = string.length >> 1; | |
| 2545 | const bytes = new Uint8Array(l); | |
| 2546 | for (let i = 0; i < l; i += 1) { | |
| 2547 | const top = string[i << 1]; | |
| 2548 | const bottom = string[(i << 1) + 1]; | |
| 2549 | bytes[i] = (alpha[top] << 4) | alpha[bottom]; | |
| 2550 | } | |
| 2551 | return bytes; | |
| 2552 | }); | |
| 2553 | ||
| 2554 | /** | |
| 2555 | * @type {function(Uint8Array): string} base64 | |
| 2556 | */ | |
| 2557 | //@ts-expect-error | |
| 2558 | const makeHexFromUint8Array = Uint8Array.prototype.toHex ? (array => array.toHex()) : (array => { | |
| 2559 | /** @type {string[]} */ | |
| 2560 | const alpha = [ | |
| 2561 | "0", "1", | |
| 2562 | "2", "3", | |
| 2563 | "4", "5", | |
| 2564 | "6", "7", | |
| 2565 | "8", "9", | |
| 2566 | "a", "b", | |
| 2567 | "c", "d", | |
| 2568 | "e", "f", | |
| 2569 | ]; | |
| 2570 | const l = array.length; | |
| 2571 | const v = []; | |
| 2572 | for (let i = 0; i < l; ++i) { | |
| 2573 | v.push(alpha[array[i] >> 4]); | |
| 2574 | v.push(alpha[array[i] & 0xf]); | |
| 2575 | } | |
| 2576 | return v.join(""); | |
| 2577 | }); | |
| 2578 | ||
| 2579 | ////////////// | |
| 2580 | ||
| 2581 | /** | |
| 2582 | * SipHash 1-3 | |
| 2583 | * @param {Uint8Array} input data to be hashed; all codepoints in string should be less than 256 | |
| 2584 | * @param {number} k0lo first word of key | |
| 2585 | * @param {number} k0hi second word of key | |
| 2586 | * @param {number} k1lo third word of key | |
| 2587 | * @param {number} k1hi fourth word of key | |
| 2588 | * @param {Uint8Array} output the data to write (clobber the first eight bytes) | |
| 2589 | */ | |
| 2590 | function siphashOfBytes(input, k0lo, k0hi, k1lo, k1hi, output) { | |
| 2591 | // hash state | |
| 2592 | // While siphash uses 64 bit state, js only has native support | |
| 2593 | // for 32 bit numbers. BigInt, unfortunately, doesn't count. | |
| 2594 | // It's too slow. | |
| 2595 | let v0lo = k0lo ^ 0x70736575; | |
| 2596 | let v0hi = k0hi ^ 0x736f6d65; | |
| 2597 | let v1lo = k1lo ^ 0x6e646f6d; | |
| 2598 | let v1hi = k1hi ^ 0x646f7261; | |
| 2599 | let v2lo = k0lo ^ 0x6e657261; | |
| 2600 | let v2hi = k0hi ^ 0x6c796765; | |
| 2601 | let v3lo = k1lo ^ 0x79746573; | |
| 2602 | let v3hi = k1hi ^ 0x74656462; | |
| 2603 | const inputLength = input.length; | |
| 2604 | let inputI = 0; | |
| 2605 | // main hash loop | |
| 2606 | const left = inputLength & 0x7; | |
| 2607 | let milo = 0; | |
| 2608 | let mihi = 0; | |
| 2609 | while (inputI < inputLength - left) { | |
| 2610 | u8ToU64le(inputI, inputI + 8); | |
| 2611 | v3lo ^= milo; | |
| 2612 | v3hi ^= mihi; | |
| 2613 | siphashCompress(); | |
| 2614 | v0lo ^= milo; | |
| 2615 | v0hi ^= mihi; | |
| 2616 | inputI += 8; | |
| 2617 | } | |
| 2618 | u8ToU64le(inputI, inputI + left); | |
| 2619 | // finish | |
| 2620 | const blo = milo; | |
| 2621 | const bhi = ((inputLength & 0xff) << 24) | mihi; | |
| 2622 | v3lo ^= blo; | |
| 2623 | v3hi ^= bhi; | |
| 2624 | siphashCompress(); | |
| 2625 | v0lo ^= blo; | |
| 2626 | v0hi ^= bhi; | |
| 2627 | v2lo ^= 0xff; | |
| 2628 | siphashCompress(); | |
| 2629 | siphashCompress(); | |
| 2630 | siphashCompress(); | |
| 2631 | output[7] = (v0lo ^ v1lo ^ v2lo ^ v3lo) & 0xff; | |
| 2632 | output[6] = (v0lo ^ v1lo ^ v2lo ^ v3lo) >>> 8; | |
| 2633 | output[5] = (v0lo ^ v1lo ^ v2lo ^ v3lo) >>> 16; | |
| 2634 | output[4] = (v0lo ^ v1lo ^ v2lo ^ v3lo) >>> 24; | |
| 2635 | output[3] = (v0hi ^ v1hi ^ v2hi ^ v3hi) & 0xff; | |
| 2636 | output[2] = (v0hi ^ v1hi ^ v2hi ^ v3hi) >>> 8; | |
| 2637 | output[1] = (v0hi ^ v1hi ^ v2hi ^ v3hi) >>> 16; | |
| 2638 | output[0] = (v0hi ^ v1hi ^ v2hi ^ v3hi) >>> 24; | |
| 2639 | /** | |
| 2640 | * Convert eight bytes to a single 64-bit number | |
| 2641 | * @param {number} offset | |
| 2642 | * @param {number} length | |
| 2643 | */ | |
| 2644 | function u8ToU64le(offset, length) { | |
| 2645 | const n0 = offset < length ? input[offset] & 0xff : 0; | |
| 2646 | const n1 = offset + 1 < length ? input[offset + 1] & 0xff : 0; | |
| 2647 | const n2 = offset + 2 < length ? input[offset + 2] & 0xff : 0; | |
| 2648 | const n3 = offset + 3 < length ? input[offset + 3] & 0xff : 0; | |
| 2649 | const n4 = offset + 4 < length ? input[offset + 4] & 0xff : 0; | |
| 2650 | const n5 = offset + 5 < length ? input[offset + 5] & 0xff : 0; | |
| 2651 | const n6 = offset + 6 < length ? input[offset + 6] & 0xff : 0; | |
| 2652 | const n7 = offset + 7 < length ? input[offset + 7] & 0xff : 0; | |
| 2653 | milo = n0 | (n1 << 8) | (n2 << 16) | (n3 << 24); | |
| 2654 | mihi = n4 | (n5 << 8) | (n6 << 16) | (n7 << 24); | |
| 2655 | } | |
| 2656 | function siphashCompress() { | |
| 2657 | // v0 += v1; | |
| 2658 | v0hi = (v0hi + v1hi + (((v0lo >>> 0) + (v1lo >>> 0) > 0xffffffff) ? 1 : 0)) | 0; | |
| 2659 | v0lo = (v0lo + v1lo) | 0; | |
| 2660 | // rotl(v1, 13) | |
| 2661 | let v1lo_ = v1lo; | |
| 2662 | let v1hi_ = v1hi; | |
| 2663 | v1lo = (v1lo_ << 13) | (v1hi_ >>> 19); | |
| 2664 | v1hi = (v1hi_ << 13) | (v1lo_ >>> 19); | |
| 2665 | // v1 ^= v0 | |
| 2666 | v1lo ^= v0lo; | |
| 2667 | v1hi ^= v0hi; | |
| 2668 | // rotl(v0, 32) | |
| 2669 | const v0lo_ = v0lo; | |
| 2670 | const v0hi_ = v0hi; | |
| 2671 | v0lo = v0hi_; | |
| 2672 | v0hi = v0lo_; | |
| 2673 | // v2 += v3 | |
| 2674 | v2hi = (v2hi + v3hi + (((v2lo >>> 0) + (v3lo >>> 0) > 0xffffffff) ? 1 : 0)) | 0; | |
| 2675 | v2lo = (v2lo + v3lo) | 0; | |
| 2676 | // rotl(v3, 16) | |
| 2677 | let v3lo_ = v3lo; | |
| 2678 | let v3hi_ = v3hi; | |
| 2679 | v3lo = (v3lo_ << 16) | (v3hi_ >>> 16); | |
| 2680 | v3hi = (v3hi_ << 16) | (v3lo_ >>> 16); | |
| 2681 | // v3 ^= v2 | |
| 2682 | v3lo ^= v2lo; | |
| 2683 | v3hi ^= v2hi; | |
| 2684 | // v0 += v3 | |
| 2685 | v0hi = (v0hi + v3hi + (((v0lo >>> 0) + (v3lo >>> 0) > 0xffffffff) ? 1 : 0)) | 0; | |
| 2686 | v0lo = (v0lo + v3lo) | 0; | |
| 2687 | // rotl(v3, 21) | |
| 2688 | v3lo_ = v3lo; | |
| 2689 | v3hi_ = v3hi; | |
| 2690 | v3lo = (v3lo_ << 21) | (v3hi_ >>> 11); | |
| 2691 | v3hi = (v3hi_ << 21) | (v3lo_ >>> 11); | |
| 2692 | // v3 ^= v0 | |
| 2693 | v3lo ^= v0lo; | |
| 2694 | v3hi ^= v0hi; | |
| 2695 | // v2 += v1 | |
| 2696 | v2hi = (v2hi + v1hi + (((v2lo >>> 0) + (v1lo >>> 0) > 0xffffffff) ? 1 : 0)) | 0; | |
| 2697 | v2lo = (v2lo + v1lo) | 0; | |
| 2698 | // rotl(v1, 17) | |
| 2699 | v1lo_ = v1lo; | |
| 2700 | v1hi_ = v1hi; | |
| 2701 | v1lo = (v1lo_ << 17) | (v1hi_ >>> 15); | |
| 2702 | v1hi = (v1hi_ << 17) | (v1lo_ >>> 15); | |
| 2703 | // v1 ^= v2 | |
| 2704 | v1lo ^= v2lo; | |
| 2705 | v1hi ^= v2hi; | |
| 2706 | // rotl(v2, 32) | |
| 2707 | const v2lo_ = v2lo; | |
| 2708 | const v2hi_ = v2hi; | |
| 2709 | v2lo = v2hi_; | |
| 2710 | v2hi = v2lo_; | |
| 2711 | } | |
| 2712 | } | |
| 2713 | ||
| 2714 | ////////////// | |
| 2715 | ||
| 2716 | ||
| 2717 | // Parts of this code are based on Lucene, which is licensed under the | |
| 2718 | // Apache/2.0 license. | |
| 2719 | // More information found here: | |
| 2720 | // https://fossies.org/linux/lucene/lucene/core/src/java/org/apache/lucene/util/automaton/ | |
| 2721 | // LevenshteinAutomata.java | |
| 2722 | class ParametricDescription { | |
| 2723 | /** | |
| 2724 | * @param {number} w | |
| 2725 | * @param {number} n | |
| 2726 | * @param {Int32Array} minErrors | |
| 2727 | */ | |
| 2728 | constructor(w, n, minErrors) { | |
| 2729 | this.w = w; | |
| 2730 | this.n = n; | |
| 2731 | this.minErrors = minErrors; | |
| 2732 | } | |
| 2733 | /** | |
| 2734 | * @param {number} absState | |
| 2735 | * @returns {boolean} | |
| 2736 | */ | |
| 2737 | isAccept(absState) { | |
| 2738 | const state = Math.floor(absState / (this.w + 1)); | |
| 2739 | const offset = absState % (this.w + 1); | |
| 2740 | return this.w - offset + this.minErrors[state] <= this.n; | |
| 2741 | } | |
| 2742 | /** | |
| 2743 | * @param {number} absState | |
| 2744 | * @returns {number} | |
| 2745 | */ | |
| 2746 | getPosition(absState) { | |
| 2747 | return absState % (this.w + 1); | |
| 2748 | } | |
| 2749 | /** | |
| 2750 | * @param {Uint8Array} name | |
| 2751 | * @param {number} charCode | |
| 2752 | * @param {number} pos | |
| 2753 | * @param {number} end | |
| 2754 | * @returns {number} | |
| 2755 | */ | |
| 2756 | getVector(name, charCode, pos, end) { | |
| 2757 | let vector = 0; | |
| 2758 | for (let i = pos; i < end; i += 1) { | |
| 2759 | vector = vector << 1; | |
| 2760 | if (name[i] === charCode) { | |
| 2761 | vector |= 1; | |
| 2762 | } | |
| 2763 | } | |
| 2764 | return vector; | |
| 2765 | } | |
| 2766 | /** | |
| 2767 | * @param {Int32Array} data | |
| 2768 | * @param {number} index | |
| 2769 | * @param {number} bitsPerValue | |
| 2770 | * @returns {number} | |
| 2771 | */ | |
| 2772 | unpack(data, index, bitsPerValue) { | |
| 2773 | const bitLoc = (bitsPerValue * index); | |
| 2774 | const dataLoc = bitLoc >> 5; | |
| 2775 | const bitStart = bitLoc & 31; | |
| 2776 | if (bitStart + bitsPerValue <= 32) { | |
| 2777 | // not split | |
| 2778 | return ((data[dataLoc] >> bitStart) & this.MASKS[bitsPerValue - 1]); | |
| 2779 | } else { | |
| 2780 | // split | |
| 2781 | const part = 32 - bitStart; | |
| 2782 | return ~~(((data[dataLoc] >> bitStart) & this.MASKS[part - 1]) + | |
| 2783 | ((data[1 + dataLoc] & this.MASKS[bitsPerValue - part - 1]) << part)); | |
| 2784 | } | |
| 2785 | } | |
| 2786 | } | |
| 2787 | ParametricDescription.prototype.MASKS = new Int32Array([ | |
| 2788 | 0x1, 0x3, 0x7, 0xF, | |
| 2789 | 0x1F, 0x3F, 0x7F, 0xFF, | |
| 2790 | 0x1FF, 0x3F, 0x7FF, 0xFFF, | |
| 2791 | 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, | |
| 2792 | 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, | |
| 2793 | 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, | |
| 2794 | 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, | |
| 2795 | 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, | |
| 2796 | ]); | |
| 2797 | ||
| 2798 | // The following code was generated with the moman/finenight pkg | |
| 2799 | // This package is available under the MIT License, see NOTICE.txt | |
| 2800 | // for more details. | |
| 2801 | // This class is auto-generated, Please do not modify it directly. | |
| 2802 | // You should modify the https://gitlab.com/notriddle/createAutomata.py instead. | |
| 2803 | // The following code was generated with the moman/finenight pkg | |
| 2804 | // This package is available under the MIT License, see NOTICE.txt | |
| 2805 | // for more details. | |
| 2806 | // This class is auto-generated, Please do not modify it directly. | |
| 2807 | // You should modify https://gitlab.com/notriddle/moman-rustdoc instead. | |
| 2808 | ||
| 2809 | class Lev2TParametricDescription extends ParametricDescription { | |
| 2810 | /** | |
| 2811 | * @param {number} absState | |
| 2812 | * @param {number} position | |
| 2813 | * @param {number} vector | |
| 2814 | * @returns {number} | |
| 2815 | */ | |
| 2816 | transition(absState, position, vector) { | |
| 2817 | let state = Math.floor(absState / (this.w + 1)); | |
| 2818 | let offset = absState % (this.w + 1); | |
| 2819 | ||
| 2820 | if (position === this.w) { | |
| 2821 | if (state < 3) { | |
| 2822 | const loc = Math.imul(vector, 3) + state; | |
| 2823 | offset += this.unpack(this.offsetIncrs0, loc, 1); | |
| 2824 | state = this.unpack(this.toStates0, loc, 2) - 1; | |
| 2825 | } | |
| 2826 | } else if (position === this.w - 1) { | |
| 2827 | if (state < 5) { | |
| 2828 | const loc = Math.imul(vector, 5) + state; | |
| 2829 | offset += this.unpack(this.offsetIncrs1, loc, 1); | |
| 2830 | state = this.unpack(this.toStates1, loc, 3) - 1; | |
| 2831 | } | |
| 2832 | } else if (position === this.w - 2) { | |
| 2833 | if (state < 13) { | |
| 2834 | const loc = Math.imul(vector, 13) + state; | |
| 2835 | offset += this.unpack(this.offsetIncrs2, loc, 2); | |
| 2836 | state = this.unpack(this.toStates2, loc, 4) - 1; | |
| 2837 | } | |
| 2838 | } else if (position === this.w - 3) { | |
| 2839 | if (state < 28) { | |
| 2840 | const loc = Math.imul(vector, 28) + state; | |
| 2841 | offset += this.unpack(this.offsetIncrs3, loc, 2); | |
| 2842 | state = this.unpack(this.toStates3, loc, 5) - 1; | |
| 2843 | } | |
| 2844 | } else if (position === this.w - 4) { | |
| 2845 | if (state < 45) { | |
| 2846 | const loc = Math.imul(vector, 45) + state; | |
| 2847 | offset += this.unpack(this.offsetIncrs4, loc, 3); | |
| 2848 | state = this.unpack(this.toStates4, loc, 6) - 1; | |
| 2849 | } | |
| 2850 | } else { | |
| 2851 | // eslint-disable-next-line no-lonely-if | |
| 2852 | if (state < 45) { | |
| 2853 | const loc = Math.imul(vector, 45) + state; | |
| 2854 | offset += this.unpack(this.offsetIncrs5, loc, 3); | |
| 2855 | state = this.unpack(this.toStates5, loc, 6) - 1; | |
| 2856 | } | |
| 2857 | } | |
| 2858 | ||
| 2859 | if (state === -1) { | |
| 2860 | // null state | |
| 2861 | return -1; | |
| 2862 | } else { | |
| 2863 | // translate back to abs | |
| 2864 | return Math.imul(state, this.w + 1) + offset; | |
| 2865 | } | |
| 2866 | } | |
| 2867 | ||
| 2868 | // state map | |
| 2869 | // 0 -> [(0, 0)] | |
| 2870 | // 1 -> [(0, 1)] | |
| 2871 | // 2 -> [(0, 2)] | |
| 2872 | // 3 -> [(0, 1), (1, 1)] | |
| 2873 | // 4 -> [(0, 2), (1, 2)] | |
| 2874 | // 5 -> [(0, 1), (1, 1), (2, 1)] | |
| 2875 | // 6 -> [(0, 2), (1, 2), (2, 2)] | |
| 2876 | // 7 -> [(0, 1), (2, 1)] | |
| 2877 | // 8 -> [(0, 1), (2, 2)] | |
| 2878 | // 9 -> [(0, 2), (2, 1)] | |
| 2879 | // 10 -> [(0, 2), (2, 2)] | |
| 2880 | // 11 -> [t(0, 1), (0, 1), (1, 1), (2, 1)] | |
| 2881 | // 12 -> [t(0, 2), (0, 2), (1, 2), (2, 2)] | |
| 2882 | // 13 -> [(0, 2), (1, 2), (2, 2), (3, 2)] | |
| 2883 | // 14 -> [(0, 1), (1, 1), (3, 2)] | |
| 2884 | // 15 -> [(0, 1), (2, 2), (3, 2)] | |
| 2885 | // 16 -> [(0, 1), (3, 2)] | |
| 2886 | // 17 -> [(0, 1), t(1, 2), (2, 2), (3, 2)] | |
| 2887 | // 18 -> [(0, 2), (1, 2), (3, 1)] | |
| 2888 | // 19 -> [(0, 2), (1, 2), (3, 2)] | |
| 2889 | // 20 -> [(0, 2), (1, 2), t(1, 2), (2, 2), (3, 2)] | |
| 2890 | // 21 -> [(0, 2), (2, 1), (3, 1)] | |
| 2891 | // 22 -> [(0, 2), (2, 2), (3, 2)] | |
| 2892 | // 23 -> [(0, 2), (3, 1)] | |
| 2893 | // 24 -> [(0, 2), (3, 2)] | |
| 2894 | // 25 -> [(0, 2), t(1, 2), (1, 2), (2, 2), (3, 2)] | |
| 2895 | // 26 -> [t(0, 2), (0, 2), (1, 2), (2, 2), (3, 2)] | |
| 2896 | // 27 -> [t(0, 2), (0, 2), (1, 2), (3, 1)] | |
| 2897 | // 28 -> [(0, 2), (1, 2), (2, 2), (3, 2), (4, 2)] | |
| 2898 | // 29 -> [(0, 2), (1, 2), (2, 2), (4, 2)] | |
| 2899 | // 30 -> [(0, 2), (1, 2), (2, 2), t(2, 2), (3, 2), (4, 2)] | |
| 2900 | // 31 -> [(0, 2), (1, 2), (3, 2), (4, 2)] | |
| 2901 | // 32 -> [(0, 2), (1, 2), (4, 2)] | |
| 2902 | // 33 -> [(0, 2), (1, 2), t(1, 2), (2, 2), (3, 2), (4, 2)] | |
| 2903 | // 34 -> [(0, 2), (1, 2), t(2, 2), (2, 2), (3, 2), (4, 2)] | |
| 2904 | // 35 -> [(0, 2), (2, 1), (4, 2)] | |
| 2905 | // 36 -> [(0, 2), (2, 2), (3, 2), (4, 2)] | |
| 2906 | // 37 -> [(0, 2), (2, 2), (4, 2)] | |
| 2907 | // 38 -> [(0, 2), (3, 2), (4, 2)] | |
| 2908 | // 39 -> [(0, 2), (4, 2)] | |
| 2909 | // 40 -> [(0, 2), t(1, 2), (1, 2), (2, 2), (3, 2), (4, 2)] | |
| 2910 | // 41 -> [(0, 2), t(2, 2), (2, 2), (3, 2), (4, 2)] | |
| 2911 | // 42 -> [t(0, 2), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2)] | |
| 2912 | // 43 -> [t(0, 2), (0, 2), (1, 2), (2, 2), (4, 2)] | |
| 2913 | // 44 -> [t(0, 2), (0, 2), (1, 2), (2, 2), t(2, 2), (3, 2), (4, 2)] | |
| 2914 | ||
| 2915 | ||
| 2916 | /** @param {number} w - length of word being checked */ | |
| 2917 | constructor(w) { | |
| 2918 | super(w, 2, new Int32Array([ | |
| 2919 | 0,1,2,0,1,-1,0,-1,0,-1,0,-1,0,-1,-1,-1,-1,-1,-2,-1,-1,-2,-1,-2, | |
| 2920 | -1,-1,-1,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2, | |
| 2921 | ])); | |
| 2922 | } | |
| 2923 | } | |
| 2924 | ||
| 2925 | Lev2TParametricDescription.prototype.toStates0 = /*2 bits per value */ new Int32Array([ | |
| 2926 | 0xe, | |
| 2927 | ]); | |
| 2928 | Lev2TParametricDescription.prototype.offsetIncrs0 = /*1 bits per value */ new Int32Array([ | |
| 2929 | 0x0, | |
| 2930 | ]); | |
| 2931 | ||
| 2932 | Lev2TParametricDescription.prototype.toStates1 = /*3 bits per value */ new Int32Array([ | |
| 2933 | 0x1a688a2c, | |
| 2934 | ]); | |
| 2935 | Lev2TParametricDescription.prototype.offsetIncrs1 = /*1 bits per value */ new Int32Array([ | |
| 2936 | 0x3e0, | |
| 2937 | ]); | |
| 2938 | ||
| 2939 | Lev2TParametricDescription.prototype.toStates2 = /*4 bits per value */ new Int32Array([ | |
| 2940 | 0x70707054,0xdc07035,0x3dd3a3a,0x2323213a, | |
| 2941 | 0x15435223,0x22545432,0x5435, | |
| 2942 | ]); | |
| 2943 | Lev2TParametricDescription.prototype.offsetIncrs2 = /*2 bits per value */ new Int32Array([ | |
| 2944 | 0x80000,0x55582088,0x55555555,0x55, | |
| 2945 | ]); | |
| 2946 | ||
| 2947 | Lev2TParametricDescription.prototype.toStates3 = /*5 bits per value */ new Int32Array([ | |
| 2948 | 0x1c0380a4,0x700a570,0xca529c0,0x180a00, | |
| 2949 | 0xa80af180,0xc5498e60,0x5a546398,0x8c4300e8, | |
| 2950 | 0xac18c601,0xd8d43501,0x863500ad,0x51976d6a, | |
| 2951 | 0x8ca0180a,0xc3501ac2,0xb0c5be16,0x76dda8a5, | |
| 2952 | 0x18c4519,0xc41294a,0xe248d231,0x1086520c, | |
| 2953 | 0xce31ac42,0x13946358,0x2d0348c4,0x6732d494, | |
| 2954 | 0x1ad224a5,0xd635ad4b,0x520c4139,0xce24948, | |
| 2955 | 0x22110a52,0x58ce729d,0xc41394e3,0x941cc520, | |
| 2956 | 0x90e732d4,0x4729d224,0x39ce35ad, | |
| 2957 | ]); | |
| 2958 | Lev2TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new Int32Array([ | |
| 2959 | 0x80000,0xc0c830,0x300f3c30,0x2200fcff, | |
| 2960 | 0xcaa00a08,0x3c2200a8,0xa8fea00a,0x55555555, | |
| 2961 | 0x55555555,0x55555555,0x55555555,0x55555555, | |
| 2962 | 0x55555555,0x55555555, | |
| 2963 | ]); | |
| 2964 | ||
| 2965 | Lev2TParametricDescription.prototype.toStates4 = /*6 bits per value */ new Int32Array([ | |
| 2966 | 0x801c0144,0x1453803,0x14700038,0xc0005145, | |
| 2967 | 0x1401,0x14,0x140000,0x0, | |
| 2968 | 0x510000,0x6301f007,0x301f00d1,0xa186178, | |
| 2969 | 0xc20ca0c3,0xc20c30,0xc30030c,0xc00c00cd, | |
| 2970 | 0xf0c00c30,0x4c054014,0xc30944c3,0x55150c34, | |
| 2971 | 0x8300550,0x430c0143,0x50c31,0xc30850c, | |
| 2972 | 0xc3143000,0x50053c50,0x5130d301,0x850d30c2, | |
| 2973 | 0x30a08608,0xc214414,0x43142145,0x21450031, | |
| 2974 | 0x1400c314,0x4c143145,0x32832803,0x28014d6c, | |
| 2975 | 0xcd34a0c3,0x1c50c76,0x1c314014,0x430c30c3, | |
| 2976 | 0x1431,0xc300500,0xca00d303,0xd36d0e40, | |
| 2977 | 0x90b0e400,0xcb2abb2c,0x70c20ca1,0x2c32ca2c, | |
| 2978 | 0xcd2c70cb,0x31c00c00,0x34c2c32c,0x5583280, | |
| 2979 | 0x558309b7,0x6cd6ca14,0x430850c7,0x51c51401, | |
| 2980 | 0x1430c714,0xc3087,0x71451450,0xca00d30, | |
| 2981 | 0xc26dc156,0xb9071560,0x1cb2abb2,0xc70c2144, | |
| 2982 | 0xb1c51ca1,0x1421c70c,0xc51c00c3,0x30811c51, | |
| 2983 | 0x24324308,0xc51031c2,0x70820820,0x5c33830d, | |
| 2984 | 0xc33850c3,0x30c30c30,0xc30c31c,0x451450c3, | |
| 2985 | 0x20c20c20,0xda0920d,0x5145914f,0x36596114, | |
| 2986 | 0x51965865,0xd9643653,0x365a6590,0x51964364, | |
| 2987 | 0x43081505,0x920b2032,0x2c718b28,0xd7242249, | |
| 2988 | 0x35cb28b0,0x2cb3872c,0x972c30d7,0xb0c32cb2, | |
| 2989 | 0x4e1c75c,0xc80c90c2,0x62ca2482,0x4504171c, | |
| 2990 | 0xd65d9610,0x33976585,0xd95cb5d,0x4b5ca5d7, | |
| 2991 | 0x73975c36,0x10308138,0xc2245105,0x41451031, | |
| 2992 | 0x14e24208,0xc35c3387,0x51453851,0x1c51c514, | |
| 2993 | 0xc70c30c3,0x20451450,0x14f1440c,0x4f0da092, | |
| 2994 | 0x4513d41,0x6533944d,0x1350e658,0xe1545055, | |
| 2995 | 0x64365a50,0x5519383,0x51030815,0x28920718, | |
| 2996 | 0x441c718b,0x714e2422,0x1c35cb28,0x4e1c7387, | |
| 2997 | 0xb28e1c51,0x5c70c32c,0xc204e1c7,0x81c61440, | |
| 2998 | 0x1c62ca24,0xd04503ce,0x85d63944,0x39338e65, | |
| 2999 | 0x8e154387,0x364b5ca3,0x38739738, | |
| 3000 | ]); | |
| 3001 | Lev2TParametricDescription.prototype.offsetIncrs4 = /*3 bits per value */ new Int32Array([ | |
| 3002 | 0x10000000,0xc00000,0x60061,0x400, | |
| 3003 | 0x0,0x80010008,0x249248a4,0x8229048, | |
| 3004 | 0x2092,0x6c3603,0xb61b6c30,0x6db6036d, | |
| 3005 | 0xdb6c0,0x361b0180,0x91b72000,0xdb11b71b, | |
| 3006 | 0x6db6236,0x1008200,0x12480012,0x24924906, | |
| 3007 | 0x48200049,0x80410002,0x24000900,0x4924a489, | |
| 3008 | 0x10822492,0x20800125,0x48360,0x9241b692, | |
| 3009 | 0x6da4924,0x40009268,0x241b010,0x291b4900, | |
| 3010 | 0x6d249249,0x49493423,0x92492492,0x24924924, | |
| 3011 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 3012 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 3013 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 3014 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 3015 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 3016 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 3017 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 3018 | 0x92492492,0x24924924,0x49249249,0x2492, | |
| 3019 | ]); | |
| 3020 | ||
| 3021 | Lev2TParametricDescription.prototype.toStates5 = /*6 bits per value */ new Int32Array([ | |
| 3022 | 0x801c0144,0x1453803,0x14700038,0xc0005145, | |
| 3023 | 0x1401,0x14,0x140000,0x0, | |
| 3024 | 0x510000,0x4e00e007,0xe0051,0x3451451c, | |
| 3025 | 0xd015000,0x30cd0000,0xc30c30c,0xc30c30d4, | |
| 3026 | 0x40c30c30,0x7c01c014,0xc03458c0,0x185e0c07, | |
| 3027 | 0x2830c286,0x830c3083,0xc30030,0x33430c, | |
| 3028 | 0x30c3003,0x70051030,0x16301f00,0x8301f00d, | |
| 3029 | 0x30a18617,0xc20ca0c,0x431420c3,0xb1450c51, | |
| 3030 | 0x14314315,0x4f143145,0x34c05401,0x4c30944c, | |
| 3031 | 0x55150c3,0x30830055,0x1430c014,0xc00050c3, | |
| 3032 | 0xc30850,0xc314300,0x150053c5,0x25130d30, | |
| 3033 | 0x5430d30c,0xc0354154,0x300d0c90,0x1cb2cd0c, | |
| 3034 | 0xc91cb0c3,0x72c30cb2,0x14f1cb2c,0xc34c0540, | |
| 3035 | 0x34c30944,0x82182214,0x851050c2,0x50851430, | |
| 3036 | 0x1400c50c,0x30c5085,0x50c51450,0x150053c, | |
| 3037 | 0xc25130d3,0x8850d30,0x1430a086,0x450c2144, | |
| 3038 | 0x51cb1c21,0x1c91c70c,0xc71c314b,0x34c1cb1, | |
| 3039 | 0x6c328328,0xc328014d,0x76cd34a0,0x1401c50c, | |
| 3040 | 0xc31c3140,0x31430c30,0x14,0x30c3005, | |
| 3041 | 0xa0ca00d3,0x535b0c,0x4d2830ca,0x514369b3, | |
| 3042 | 0xc500d01,0x5965965a,0x30d46546,0x6435030c, | |
| 3043 | 0x8034c659,0xdb439032,0x2c390034,0xcaaecb24, | |
| 3044 | 0x30832872,0xcb28b1c,0x4b1c32cb,0x70030033, | |
| 3045 | 0x30b0cb0c,0xe40ca00d,0x400d36d0,0xb2c90b0e, | |
| 3046 | 0xca1cb2ab,0xa2c70c20,0x6575d95c,0x4315b5ce, | |
| 3047 | 0x95c53831,0x28034c5d,0x9b705583,0xa1455830, | |
| 3048 | 0xc76cd6c,0x40143085,0x71451c51,0x871430c, | |
| 3049 | 0x450000c3,0xd3071451,0x1560ca00,0x560c26dc, | |
| 3050 | 0xb35b2851,0xc914369,0x1a14500d,0x46593945, | |
| 3051 | 0xcb2c939,0x94507503,0x328034c3,0x9b70558, | |
| 3052 | 0xe41c5583,0x72caaeca,0x1c308510,0xc7147287, | |
| 3053 | 0x50871c32,0x1470030c,0xd307147,0xc1560ca0, | |
| 3054 | 0x1560c26d,0xabb2b907,0x21441cb2,0x38a1c70c, | |
| 3055 | 0x8e657394,0x314b1c93,0x39438738,0x43083081, | |
| 3056 | 0x31c22432,0x820c510,0x830d7082,0x50c35c33, | |
| 3057 | 0xc30c338,0xc31c30c3,0x50c30c30,0xc204514, | |
| 3058 | 0x890c90c2,0x31440c70,0xa8208208,0xea0df0c3, | |
| 3059 | 0x8a231430,0xa28a28a2,0x28a28a1e,0x1861868a, | |
| 3060 | 0x48308308,0xc3682483,0x14516453,0x4d965845, | |
| 3061 | 0xd4659619,0x36590d94,0xd969964,0x546590d9, | |
| 3062 | 0x20c20541,0x920d20c,0x5914f0da,0x96114514, | |
| 3063 | 0x65865365,0xe89d3519,0x99e7a279,0x9e89e89e, | |
| 3064 | 0x81821827,0xb2032430,0x18b28920,0x422492c7, | |
| 3065 | 0xb28b0d72,0x3872c35c,0xc30d72cb,0x32cb2972, | |
| 3066 | 0x1c75cb0c,0xc90c204e,0xa2482c80,0x24b1c62c, | |
| 3067 | 0xc3a89089,0xb0ea2e42,0x9669a31c,0xa4966a28, | |
| 3068 | 0x59a8a269,0x8175e7a,0xb203243,0x718b2892, | |
| 3069 | 0x4114105c,0x17597658,0x74ce5d96,0x5c36572d, | |
| 3070 | 0xd92d7297,0xe1ce5d70,0xc90c204,0xca2482c8, | |
| 3071 | 0x4171c62,0x5d961045,0x976585d6,0x79669533, | |
| 3072 | 0x964965a2,0x659689e6,0x308175e7,0x24510510, | |
| 3073 | 0x451031c2,0xe2420841,0x5c338714,0x453851c3, | |
| 3074 | 0x51c51451,0xc30c31c,0x451450c7,0x41440c20, | |
| 3075 | 0xc708914,0x82105144,0xf1c58c90,0x1470ea0d, | |
| 3076 | 0x61861863,0x8a1e85e8,0x8687a8a2,0x3081861, | |
| 3077 | 0x24853c51,0x5053c368,0x1341144f,0x96194ce5, | |
| 3078 | 0x1544d439,0x94385514,0xe0d90d96,0x5415464, | |
| 3079 | 0x4f1440c2,0xf0da0921,0x4513d414,0x533944d0, | |
| 3080 | 0x350e6586,0x86082181,0xe89e981d,0x18277689, | |
| 3081 | 0x10308182,0x89207185,0x41c718b2,0x14e24224, | |
| 3082 | 0xc35cb287,0xe1c73871,0x28e1c514,0xc70c32cb, | |
| 3083 | 0x204e1c75,0x1c61440c,0xc62ca248,0x90891071, | |
| 3084 | 0x2e41c58c,0xa31c70ea,0xe86175e7,0xa269a475, | |
| 3085 | 0x5e7a57a8,0x51030817,0x28920718,0xf38718b, | |
| 3086 | 0xe5134114,0x39961758,0xe1ce4ce,0x728e3855, | |
| 3087 | 0x5ce0d92d,0xc204e1ce,0x81c61440,0x1c62ca24, | |
| 3088 | 0xd04503ce,0x85d63944,0x75338e65,0x5d86075e, | |
| 3089 | 0x89e69647,0x75e76576, | |
| 3090 | ]); | |
| 3091 | Lev2TParametricDescription.prototype.offsetIncrs5 = /*3 bits per value */ new Int32Array([ | |
| 3092 | 0x10000000,0xc00000,0x60061,0x400, | |
| 3093 | 0x0,0x60000008,0x6b003080,0xdb6ab6db, | |
| 3094 | 0x2db6,0x800400,0x49245240,0x11482412, | |
| 3095 | 0x104904,0x40020000,0x92292000,0xa4b25924, | |
| 3096 | 0x9649658,0xd80c000,0xdb0c001b,0x80db6d86, | |
| 3097 | 0x6db01b6d,0xc0600003,0x86000d86,0x6db6c36d, | |
| 3098 | 0xddadb6ed,0x300001b6,0x6c360,0xe37236e4, | |
| 3099 | 0x46db6236,0xdb6c,0x361b018,0xb91b7200, | |
| 3100 | 0x6dbb1b71,0x6db763,0x20100820,0x61248001, | |
| 3101 | 0x92492490,0x24820004,0x8041000,0x92400090, | |
| 3102 | 0x24924830,0x555b6a49,0x2080012,0x20004804, | |
| 3103 | 0x49252449,0x84112492,0x4000928,0x240201, | |
| 3104 | 0x92922490,0x58924924,0x49456,0x120d8082, | |
| 3105 | 0x6da4800,0x69249249,0x249a01b,0x6c04100, | |
| 3106 | 0x6d240009,0x92492483,0x24d5adb4,0x60208001, | |
| 3107 | 0x92000483,0x24925236,0x6846da49,0x10400092, | |
| 3108 | 0x241b0,0x49291b49,0x636d2492,0x92494935, | |
| 3109 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 3110 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 3111 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 3112 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 3113 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 3114 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 3115 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 3116 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 3117 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 3118 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 3119 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 3120 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 3121 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 3122 | 0x49249249,0x92492492,0x24924924,0x49249249, | |
| 3123 | 0x92492492,0x24924924,0x49249249,0x92492492, | |
| 3124 | 0x24924924,0x49249249,0x92492492,0x24924924, | |
| 3125 | 0x49249249,0x92492492,0x24924924, | |
| 3126 | ]); | |
| 3127 | ||
| 3128 | class Lev1TParametricDescription extends ParametricDescription { | |
| 3129 | /** | |
| 3130 | * @param {number} absState | |
| 3131 | * @param {number} position | |
| 3132 | * @param {number} vector | |
| 3133 | * @returns {number} | |
| 3134 | */ | |
| 3135 | transition(absState, position, vector) { | |
| 3136 | let state = Math.floor(absState / (this.w + 1)); | |
| 3137 | let offset = absState % (this.w + 1); | |
| 3138 | ||
| 3139 | if (position === this.w) { | |
| 3140 | if (state < 2) { | |
| 3141 | const loc = Math.imul(vector, 2) + state; | |
| 3142 | offset += this.unpack(this.offsetIncrs0, loc, 1); | |
| 3143 | state = this.unpack(this.toStates0, loc, 2) - 1; | |
| 3144 | } | |
| 3145 | } else if (position === this.w - 1) { | |
| 3146 | if (state < 3) { | |
| 3147 | const loc = Math.imul(vector, 3) + state; | |
| 3148 | offset += this.unpack(this.offsetIncrs1, loc, 1); | |
| 3149 | state = this.unpack(this.toStates1, loc, 2) - 1; | |
| 3150 | } | |
| 3151 | } else if (position === this.w - 2) { | |
| 3152 | if (state < 6) { | |
| 3153 | const loc = Math.imul(vector, 6) + state; | |
| 3154 | offset += this.unpack(this.offsetIncrs2, loc, 2); | |
| 3155 | state = this.unpack(this.toStates2, loc, 3) - 1; | |
| 3156 | } | |
| 3157 | } else { | |
| 3158 | // eslint-disable-next-line no-lonely-if | |
| 3159 | if (state < 6) { | |
| 3160 | const loc = Math.imul(vector, 6) + state; | |
| 3161 | offset += this.unpack(this.offsetIncrs3, loc, 2); | |
| 3162 | state = this.unpack(this.toStates3, loc, 3) - 1; | |
| 3163 | } | |
| 3164 | } | |
| 3165 | ||
| 3166 | if (state === -1) { | |
| 3167 | // null state | |
| 3168 | return -1; | |
| 3169 | } else { | |
| 3170 | // translate back to abs | |
| 3171 | return Math.imul(state, this.w + 1) + offset; | |
| 3172 | } | |
| 3173 | } | |
| 3174 | ||
| 3175 | // state map | |
| 3176 | // 0 -> [(0, 0)] | |
| 3177 | // 1 -> [(0, 1)] | |
| 3178 | // 2 -> [(0, 1), (1, 1)] | |
| 3179 | // 3 -> [(0, 1), (1, 1), (2, 1)] | |
| 3180 | // 4 -> [(0, 1), (2, 1)] | |
| 3181 | // 5 -> [t(0, 1), (0, 1), (1, 1), (2, 1)] | |
| 3182 | ||
| 3183 | ||
| 3184 | /** @param {number} w - length of word being checked */ | |
| 3185 | constructor(w) { | |
| 3186 | super(w, 1, new Int32Array([0,1,0,-1,-1,-1])); | |
| 3187 | } | |
| 3188 | } | |
| 3189 | ||
| 3190 | Lev1TParametricDescription.prototype.toStates0 = /*2 bits per value */ new Int32Array([ | |
| 3191 | 0x2, | |
| 3192 | ]); | |
| 3193 | Lev1TParametricDescription.prototype.offsetIncrs0 = /*1 bits per value */ new Int32Array([ | |
| 3194 | 0x0, | |
| 3195 | ]); | |
| 3196 | ||
| 3197 | Lev1TParametricDescription.prototype.toStates1 = /*2 bits per value */ new Int32Array([ | |
| 3198 | 0xa43, | |
| 3199 | ]); | |
| 3200 | Lev1TParametricDescription.prototype.offsetIncrs1 = /*1 bits per value */ new Int32Array([ | |
| 3201 | 0x38, | |
| 3202 | ]); | |
| 3203 | ||
| 3204 | Lev1TParametricDescription.prototype.toStates2 = /*3 bits per value */ new Int32Array([ | |
| 3205 | 0x12180003,0xb45a4914,0x69, | |
| 3206 | ]); | |
| 3207 | Lev1TParametricDescription.prototype.offsetIncrs2 = /*2 bits per value */ new Int32Array([ | |
| 3208 | 0x558a0000,0x5555, | |
| 3209 | ]); | |
| 3210 | ||
| 3211 | Lev1TParametricDescription.prototype.toStates3 = /*3 bits per value */ new Int32Array([ | |
| 3212 | 0x900c0003,0xa1904864,0x45a49169,0x5a6d196a, | |
| 3213 | 0x9634, | |
| 3214 | ]); | |
| 3215 | Lev1TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new Int32Array([ | |
| 3216 | 0xa0fc0000,0x5555ba08,0x55555555, | |
| 3217 | ]); |
src/librustdoc/html/static/js/tsconfig.json+1-1| ... | ... | @@ -10,6 +10,6 @@ |
| 10 | 10 | "skipLibCheck": true |
| 11 | 11 | }, |
| 12 | 12 | "typeAcquisition": { |
| 13 | "include": ["./rustdoc.d.ts"] | |
| 13 | "include": ["./rustdoc.d.ts", "./stringdex.d.ts"] | |
| 14 | 14 | } |
| 15 | 15 | } |
src/librustdoc/html/static_files.rs+1| ... | ... | @@ -80,6 +80,7 @@ static_files! { |
| 80 | 80 | normalize_css => "static/css/normalize.css", |
| 81 | 81 | main_js => "static/js/main.js", |
| 82 | 82 | search_js => "static/js/search.js", |
| 83 | stringdex_js => "static/js/stringdex.js", | |
| 83 | 84 | settings_js => "static/js/settings.js", |
| 84 | 85 | src_script_js => "static/js/src-script.js", |
| 85 | 86 | storage_js => "static/js/storage.js", |
src/librustdoc/html/templates/page.html+4-15| ... | ... | @@ -29,6 +29,7 @@ |
| 29 | 29 | data-rustdoc-version="{{rustdoc_version}}" {#+ #} |
| 30 | 30 | data-channel="{{rust_channel}}" {#+ #} |
| 31 | 31 | data-search-js="{{files.search_js}}" {#+ #} |
| 32 | data-stringdex-js="{{files.stringdex_js}}" {#+ #} | |
| 32 | 33 | data-settings-js="{{files.settings_js}}" {#+ #} |
| 33 | 34 | > {# #} |
| 34 | 35 | <script src="{{static_root_path|safe}}{{files.storage_js}}"></script> |
| ... | ... | @@ -72,18 +73,9 @@ |
| 72 | 73 | <![endif]--> |
| 73 | 74 | {{ layout.external_html.before_content|safe }} |
| 74 | 75 | {% if page.css_class != "src" %} |
| 75 | <nav class="mobile-topbar"> {# #} | |
| 76 | <button class="sidebar-menu-toggle" title="show sidebar"></button> | |
| 77 | {% if !layout.logo.is_empty() || page.rust_logo %} | |
| 78 | <a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> | |
| 79 | {% if page.rust_logo %} | |
| 80 | <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt=""> | |
| 81 | {% else if !layout.logo.is_empty() %} | |
| 82 | <img src="{{layout.logo}}" alt=""> | |
| 83 | {% endif %} | |
| 84 | </a> | |
| 85 | {% endif %} | |
| 86 | </nav> | |
| 76 | <rustdoc-topbar> {# #} | |
| 77 | <h2><a href="#">{{page.short_title}}</a></h2> {# #} | |
| 78 | </rustdoc-topbar> | |
| 87 | 79 | {% endif %} |
| 88 | 80 | <nav class="sidebar"> |
| 89 | 81 | {% if page.css_class != "src" %} |
| ... | ... | @@ -117,9 +109,6 @@ |
| 117 | 109 | <div class="sidebar-resizer" title="Drag to resize sidebar"></div> {# #} |
| 118 | 110 | <main> |
| 119 | 111 | {% if page.css_class != "src" %}<div class="width-limiter">{% endif %} |
| 120 | {# defined in storage.js to avoid duplicating complex UI across every page #} | |
| 121 | {# and because the search form only works if JS is enabled anyway #} | |
| 122 | <rustdoc-search></rustdoc-search> {# #} | |
| 123 | 112 | <section id="main-content" class="content">{{ content|safe }}</section> |
| 124 | 113 | {% if page.css_class != "src" %}</div>{% endif %} |
| 125 | 114 | </main> |
src/librustdoc/html/templates/print_item.html+2-2| ... | ... | @@ -12,8 +12,8 @@ |
| 12 | 12 | <h1> |
| 13 | 13 | {{typ}} |
| 14 | 14 | <span{% if item_type != "mod" +%} class="{{item_type}}"{% endif %}> |
| 15 | {{name}} | |
| 16 | </span> {# #} | |
| 15 | {{name|wrapped|safe}} | |
| 16 | </span>&nbsp;{# #} | |
| 17 | 17 | <button id="copy-path" title="Copy item path to clipboard"> {# #} |
| 18 | 18 | Copy item path {# #} |
| 19 | 19 | </button> {# #} |
src/tools/compiletest/src/runtest.rs+1-1| ... | ... | @@ -2151,7 +2151,7 @@ impl<'test> TestCx<'test> { |
| 2151 | 2151 | |
| 2152 | 2152 | #[rustfmt::skip] |
| 2153 | 2153 | let tidy_args = [ |
| 2154 | "--new-blocklevel-tags", "rustdoc-search,rustdoc-toolbar", | |
| 2154 | "--new-blocklevel-tags", "rustdoc-search,rustdoc-toolbar,rustdoc-topbar", | |
| 2155 | 2155 | "--indent", "yes", |
| 2156 | 2156 | "--indent-spaces", "2", |
| 2157 | 2157 | "--wrap", "0", |
src/tools/html-checker/main.rs+1-1| ... | ... | @@ -31,7 +31,7 @@ fn check_html_file(file: &Path) -> usize { |
| 31 | 31 | .arg("--mute-id") // this option is useful in case we want to mute more warnings |
| 32 | 32 | .arg("yes") |
| 33 | 33 | .arg("--new-blocklevel-tags") |
| 34 | .arg("rustdoc-search,rustdoc-toolbar") // custom elements | |
| 34 | .arg("rustdoc-search,rustdoc-toolbar,rustdoc-topbar") // custom elements | |
| 35 | 35 | .arg("--mute") |
| 36 | 36 | .arg(&to_mute_s) |
| 37 | 37 | .arg(file); |
src/tools/rustdoc-js/tester.js+97-90| ... | ... | @@ -1,7 +1,8 @@ |
| 1 | 1 | /* global globalThis */ |
| 2 | ||
| 2 | 3 | const fs = require("fs"); |
| 3 | 4 | const path = require("path"); |
| 4 | ||
| 5 | const { isGeneratorObject } = require("util/types"); | |
| 5 | 6 | |
| 6 | 7 | function arrayToCode(array) { |
| 7 | 8 | return array.map((value, index) => { |
| ... | ... | @@ -45,23 +46,16 @@ function shouldIgnoreField(fieldName) { |
| 45 | 46 | } |
| 46 | 47 | |
| 47 | 48 | function valueMapper(key, testOutput) { |
| 48 | const isAlias = testOutput["is_alias"]; | |
| 49 | 49 | let value = testOutput[key]; |
| 50 | 50 | // To make our life easier, if there is a "parent" type, we add it to the path. |
| 51 | 51 | if (key === "path") { |
| 52 | if (testOutput["parent"] !== undefined) { | |
| 52 | if (testOutput["parent"]) { | |
| 53 | 53 | if (value.length > 0) { |
| 54 | 54 | value += "::" + testOutput["parent"]["name"]; |
| 55 | 55 | } else { |
| 56 | 56 | value = testOutput["parent"]["name"]; |
| 57 | 57 | } |
| 58 | } else if (testOutput["is_alias"]) { | |
| 59 | value = valueMapper(key, testOutput["original"]); | |
| 60 | 58 | } |
| 61 | } else if (isAlias && key === "alias") { | |
| 62 | value = testOutput["name"]; | |
| 63 | } else if (isAlias && ["name"].includes(key)) { | |
| 64 | value = testOutput["original"][key]; | |
| 65 | 59 | } |
| 66 | 60 | return value; |
| 67 | 61 | } |
| ... | ... | @@ -237,7 +231,7 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) { |
| 237 | 231 | const ignore_order = loadedFile.ignore_order; |
| 238 | 232 | const exact_check = loadedFile.exact_check; |
| 239 | 233 | |
| 240 | const results = await doSearch(query, loadedFile.FILTER_CRATE); | |
| 234 | const { resultsTable } = await doSearch(query, loadedFile.FILTER_CRATE); | |
| 241 | 235 | const error_text = []; |
| 242 | 236 | |
| 243 | 237 | for (const key in expected) { |
| ... | ... | @@ -247,37 +241,38 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) { |
| 247 | 241 | if (!Object.prototype.hasOwnProperty.call(expected, key)) { |
| 248 | 242 | continue; |
| 249 | 243 | } |
| 250 | if (!Object.prototype.hasOwnProperty.call(results, key)) { | |
| 244 | if (!Object.prototype.hasOwnProperty.call(resultsTable, key)) { | |
| 251 | 245 | error_text.push("==> Unknown key \"" + key + "\""); |
| 252 | 246 | break; |
| 253 | 247 | } |
| 254 | 248 | const entry = expected[key]; |
| 255 | 249 | |
| 256 | if (exact_check && entry.length !== results[key].length) { | |
| 250 | if (exact_check && entry.length !== resultsTable[key].length) { | |
| 257 | 251 | error_text.push(queryName + "==> Expected exactly " + entry.length + |
| 258 | " results but found " + results[key].length + " in '" + key + "'"); | |
| 252 | " results but found " + resultsTable[key].length + " in '" + key + "'"); | |
| 259 | 253 | } |
| 260 | 254 | |
| 261 | 255 | let prev_pos = -1; |
| 262 | 256 | for (const [index, elem] of entry.entries()) { |
| 263 | const entry_pos = lookForEntry(elem, results[key]); | |
| 257 | const entry_pos = lookForEntry(elem, resultsTable[key]); | |
| 264 | 258 | if (entry_pos === -1) { |
| 265 | 259 | error_text.push(queryName + "==> Result not found in '" + key + "': '" + |
| 266 | 260 | JSON.stringify(elem) + "'"); |
| 267 | 261 | // By default, we just compare the two first items. |
| 268 | 262 | let item_to_diff = 0; |
| 269 | if ((!ignore_order || exact_check) && index < results[key].length) { | |
| 263 | if ((!ignore_order || exact_check) && index < resultsTable[key].length) { | |
| 270 | 264 | item_to_diff = index; |
| 271 | 265 | } |
| 272 | 266 | error_text.push("Diff of first error:\n" + |
| 273 | betterLookingDiff(elem, results[key][item_to_diff])); | |
| 267 | betterLookingDiff(elem, resultsTable[key][item_to_diff])); | |
| 274 | 268 | } else if (exact_check === true && prev_pos + 1 !== entry_pos) { |
| 275 | 269 | error_text.push(queryName + "==> Exact check failed at position " + (prev_pos + 1) + |
| 276 | 270 | ": expected '" + JSON.stringify(elem) + "' but found '" + |
| 277 | JSON.stringify(results[key][index]) + "'"); | |
| 271 | JSON.stringify(resultsTable[key][index]) + "'"); | |
| 278 | 272 | } else if (ignore_order === false && entry_pos < prev_pos) { |
| 279 | error_text.push(queryName + "==> '" + JSON.stringify(elem) + "' was supposed " + | |
| 280 | "to be before '" + JSON.stringify(results[key][prev_pos]) + "'"); | |
| 273 | error_text.push(queryName + "==> '" + | |
| 274 | JSON.stringify(elem) + "' was supposed to be before '" + | |
| 275 | JSON.stringify(resultsTable[key][prev_pos]) + "'"); | |
| 281 | 276 | } else { |
| 282 | 277 | prev_pos = entry_pos; |
| 283 | 278 | } |
| ... | ... | @@ -286,19 +281,20 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) { |
| 286 | 281 | return error_text; |
| 287 | 282 | } |
| 288 | 283 | |
| 289 | async function runCorrections(query, corrections, getCorrections, loadedFile) { | |
| 290 | const qc = await getCorrections(query, loadedFile.FILTER_CRATE); | |
| 284 | async function runCorrections(query, corrections, doSearch, loadedFile) { | |
| 285 | const { parsedQuery } = await doSearch(query, loadedFile.FILTER_CRATE); | |
| 286 | const qc = parsedQuery.correction; | |
| 291 | 287 | const error_text = []; |
| 292 | 288 | |
| 293 | 289 | if (corrections === null) { |
| 294 | 290 | if (qc !== null) { |
| 295 | error_text.push(`==> expected = null, found = ${qc}`); | |
| 291 | error_text.push(`==> [correction] expected = null, found = ${qc}`); | |
| 296 | 292 | } |
| 297 | 293 | return error_text; |
| 298 | 294 | } |
| 299 | 295 | |
| 300 | if (qc !== corrections.toLowerCase()) { | |
| 301 | error_text.push(`==> expected = ${corrections}, found = ${qc}`); | |
| 296 | if (qc.toLowerCase() !== corrections.toLowerCase()) { | |
| 297 | error_text.push(`==> [correction] expected = ${corrections}, found = ${qc}`); | |
| 302 | 298 | } |
| 303 | 299 | |
| 304 | 300 | return error_text; |
| ... | ... | @@ -320,7 +316,7 @@ function checkResult(error_text, loadedFile, displaySuccess) { |
| 320 | 316 | return 1; |
| 321 | 317 | } |
| 322 | 318 | |
| 323 | async function runCheckInner(callback, loadedFile, entry, getCorrections, extra) { | |
| 319 | async function runCheckInner(callback, loadedFile, entry, extra, doSearch) { | |
| 324 | 320 | if (typeof entry.query !== "string") { |
| 325 | 321 | console.log("FAILED"); |
| 326 | 322 | console.log("==> Missing `query` field"); |
| ... | ... | @@ -338,7 +334,7 @@ async function runCheckInner(callback, loadedFile, entry, getCorrections, extra) |
| 338 | 334 | error_text = await runCorrections( |
| 339 | 335 | entry.query, |
| 340 | 336 | entry.correction, |
| 341 | getCorrections, | |
| 337 | doSearch, | |
| 342 | 338 | loadedFile, |
| 343 | 339 | ); |
| 344 | 340 | if (checkResult(error_text, loadedFile, false) !== 0) { |
| ... | ... | @@ -348,16 +344,16 @@ async function runCheckInner(callback, loadedFile, entry, getCorrections, extra) |
| 348 | 344 | return true; |
| 349 | 345 | } |
| 350 | 346 | |
| 351 | async function runCheck(loadedFile, key, getCorrections, callback) { | |
| 347 | async function runCheck(loadedFile, key, doSearch, callback) { | |
| 352 | 348 | const expected = loadedFile[key]; |
| 353 | 349 | |
| 354 | 350 | if (Array.isArray(expected)) { |
| 355 | 351 | for (const entry of expected) { |
| 356 | if (!await runCheckInner(callback, loadedFile, entry, getCorrections, true)) { | |
| 352 | if (!await runCheckInner(callback, loadedFile, entry, true, doSearch)) { | |
| 357 | 353 | return 1; |
| 358 | 354 | } |
| 359 | 355 | } |
| 360 | } else if (!await runCheckInner(callback, loadedFile, expected, getCorrections, false)) { | |
| 356 | } else if (!await runCheckInner(callback, loadedFile, expected, false, doSearch)) { | |
| 361 | 357 | return 1; |
| 362 | 358 | } |
| 363 | 359 | console.log("OK"); |
| ... | ... | @@ -368,7 +364,7 @@ function hasCheck(content, checkName) { |
| 368 | 364 | return content.startsWith(`const ${checkName}`) || content.includes(`\nconst ${checkName}`); |
| 369 | 365 | } |
| 370 | 366 | |
| 371 | async function runChecks(testFile, doSearch, parseQuery, getCorrections) { | |
| 367 | async function runChecks(testFile, doSearch, parseQuery) { | |
| 372 | 368 | let checkExpected = false; |
| 373 | 369 | let checkParsed = false; |
| 374 | 370 | let testFileContent = readFile(testFile); |
| ... | ... | @@ -397,12 +393,12 @@ async function runChecks(testFile, doSearch, parseQuery, getCorrections) { |
| 397 | 393 | let res = 0; |
| 398 | 394 | |
| 399 | 395 | if (checkExpected) { |
| 400 | res += await runCheck(loadedFile, "EXPECTED", getCorrections, (query, expected, text) => { | |
| 396 | res += await runCheck(loadedFile, "EXPECTED", doSearch, (query, expected, text) => { | |
| 401 | 397 | return runSearch(query, expected, doSearch, loadedFile, text); |
| 402 | 398 | }); |
| 403 | 399 | } |
| 404 | 400 | if (checkParsed) { |
| 405 | res += await runCheck(loadedFile, "PARSED", getCorrections, (query, expected, text) => { | |
| 401 | res += await runCheck(loadedFile, "PARSED", doSearch, (query, expected, text) => { | |
| 406 | 402 | return runParser(query, expected, parseQuery, text); |
| 407 | 403 | }); |
| 408 | 404 | } |
| ... | ... | @@ -416,71 +412,89 @@ async function runChecks(testFile, doSearch, parseQuery, getCorrections) { |
| 416 | 412 | * @param {string} resource_suffix - Version number between filename and .js, e.g. "1.59.0" |
| 417 | 413 | * @returns {Object} - Object containing keys: `doSearch`, which runs a search |
| 418 | 414 | * with the loaded index and returns a table of results; `parseQuery`, which is the |
| 419 | * `parseQuery` function exported from the search module; and `getCorrections`, which runs | |
| 415 | * `parseQuery` function exported from the search module, which runs | |
| 420 | 416 | * a search but returns type name corrections instead of results. |
| 421 | 417 | */ |
| 422 | function loadSearchJS(doc_folder, resource_suffix) { | |
| 423 | const searchIndexJs = path.join(doc_folder, "search-index" + resource_suffix + ".js"); | |
| 424 | const searchIndex = require(searchIndexJs); | |
| 425 | ||
| 426 | globalThis.searchState = { | |
| 427 | descShards: new Map(), | |
| 428 | loadDesc: async function({descShard, descIndex}) { | |
| 429 | if (descShard.promise === null) { | |
| 430 | descShard.promise = new Promise((resolve, reject) => { | |
| 431 | descShard.resolve = resolve; | |
| 432 | const ds = descShard; | |
| 433 | const fname = `${ds.crate}-desc-${ds.shard}-${resource_suffix}.js`; | |
| 434 | fs.readFile( | |
| 435 | `${doc_folder}/search.desc/${descShard.crate}/${fname}`, | |
| 436 | (err, data) => { | |
| 437 | if (err) { | |
| 438 | reject(err); | |
| 439 | } else { | |
| 440 | eval(data.toString("utf8")); | |
| 441 | } | |
| 442 | }, | |
| 443 | ); | |
| 444 | }); | |
| 445 | } | |
| 446 | const list = await descShard.promise; | |
| 447 | return list[descIndex]; | |
| 448 | }, | |
| 449 | loadedDescShard: function(crate, shard, data) { | |
| 450 | this.descShards.get(crate)[shard].resolve(data.split("\n")); | |
| 451 | }, | |
| 452 | }; | |
| 453 | ||
| 418 | async function loadSearchJS(doc_folder, resource_suffix) { | |
| 454 | 419 | const staticFiles = path.join(doc_folder, "static.files"); |
| 420 | const stringdexJs = fs.readdirSync(staticFiles).find(f => f.match(/stringdex.*\.js$/)); | |
| 421 | const stringdexModule = require(path.join(staticFiles, stringdexJs)); | |
| 455 | 422 | const searchJs = fs.readdirSync(staticFiles).find(f => f.match(/search.*\.js$/)); |
| 456 | 423 | const searchModule = require(path.join(staticFiles, searchJs)); |
| 457 | searchModule.initSearch(searchIndex.searchIndex); | |
| 458 | const docSearch = searchModule.docSearch; | |
| 424 | globalThis.nonnull = (x, msg) => { | |
| 425 | if (x === null) { | |
| 426 | throw (msg || "unexpected null value!"); | |
| 427 | } else { | |
| 428 | return x; | |
| 429 | } | |
| 430 | }; | |
| 431 | const { docSearch, DocSearch } = await searchModule.initSearch( | |
| 432 | stringdexModule.Stringdex, | |
| 433 | stringdexModule.RoaringBitmap, | |
| 434 | { | |
| 435 | loadRoot: callbacks => { | |
| 436 | for (const key in callbacks) { | |
| 437 | if (Object.hasOwn(callbacks, key)) { | |
| 438 | globalThis[key] = callbacks[key]; | |
| 439 | } | |
| 440 | } | |
| 441 | const rootJs = readFile(path.join(doc_folder, "search.index/root" + | |
| 442 | resource_suffix + ".js")); | |
| 443 | eval(rootJs); | |
| 444 | }, | |
| 445 | loadTreeByHash: hashHex => { | |
| 446 | const shardJs = readFile(path.join(doc_folder, "search.index/" + hashHex + ".js")); | |
| 447 | eval(shardJs); | |
| 448 | }, | |
| 449 | loadDataByNameAndHash: (name, hashHex) => { | |
| 450 | const shardJs = readFile(path.join(doc_folder, "search.index/" + name + "/" + | |
| 451 | hashHex + ".js")); | |
| 452 | eval(shardJs); | |
| 453 | }, | |
| 454 | }, | |
| 455 | ); | |
| 459 | 456 | return { |
| 460 | 457 | doSearch: async function(queryStr, filterCrate, currentCrate) { |
| 461 | const result = await docSearch.execQuery(searchModule.parseQuery(queryStr), | |
| 462 | filterCrate, currentCrate); | |
| 458 | const parsedQuery = DocSearch.parseQuery(queryStr); | |
| 459 | const result = await docSearch.execQuery(parsedQuery, filterCrate, currentCrate); | |
| 460 | const resultsTable = {}; | |
| 463 | 461 | for (const tab in result) { |
| 464 | 462 | if (!Object.prototype.hasOwnProperty.call(result, tab)) { |
| 465 | 463 | continue; |
| 466 | 464 | } |
| 467 | if (!(result[tab] instanceof Array)) { | |
| 465 | if (!isGeneratorObject(result[tab])) { | |
| 468 | 466 | continue; |
| 469 | 467 | } |
| 470 | for (const entry of result[tab]) { | |
| 468 | resultsTable[tab] = []; | |
| 469 | for await (const entry of result[tab]) { | |
| 470 | const flatEntry = Object.assign({ | |
| 471 | crate: entry.item.crate, | |
| 472 | name: entry.item.name, | |
| 473 | path: entry.item.modulePath, | |
| 474 | exactPath: entry.item.exactModulePath, | |
| 475 | ty: entry.item.ty, | |
| 476 | }, entry); | |
| 471 | 477 | for (const key in entry) { |
| 472 | 478 | if (!Object.prototype.hasOwnProperty.call(entry, key)) { |
| 473 | 479 | continue; |
| 474 | 480 | } |
| 475 | if (key === "displayTypeSignature" && entry.displayTypeSignature !== null) { | |
| 476 | const {type, mappedNames, whereClause} = | |
| 477 | await entry.displayTypeSignature; | |
| 478 | entry.displayType = arrayToCode(type); | |
| 479 | entry.displayMappedNames = [...mappedNames.entries()] | |
| 481 | if (key === "desc" && entry.desc !== null) { | |
| 482 | flatEntry.desc = await entry.desc; | |
| 483 | } else if (key === "displayTypeSignature" && | |
| 484 | entry.displayTypeSignature !== null | |
| 485 | ) { | |
| 486 | flatEntry.displayTypeSignature = await entry.displayTypeSignature; | |
| 487 | const { | |
| 488 | type, | |
| 489 | mappedNames, | |
| 490 | whereClause, | |
| 491 | } = flatEntry.displayTypeSignature; | |
| 492 | flatEntry.displayType = arrayToCode(type); | |
| 493 | flatEntry.displayMappedNames = [...mappedNames.entries()] | |
| 480 | 494 | .map(([name, qname]) => { |
| 481 | 495 | return `${name} = ${qname}`; |
| 482 | 496 | }).join(", "); |
| 483 | entry.displayWhereClause = [...whereClause.entries()] | |
| 497 | flatEntry.displayWhereClause = [...whereClause.entries()] | |
| 484 | 498 | .flatMap(([name, value]) => { |
| 485 | 499 | if (value.length === 0) { |
| 486 | 500 | return []; |
| ... | ... | @@ -489,16 +503,12 @@ function loadSearchJS(doc_folder, resource_suffix) { |
| 489 | 503 | }).join(", "); |
| 490 | 504 | } |
| 491 | 505 | } |
| 506 | resultsTable[tab].push(flatEntry); | |
| 492 | 507 | } |
| 493 | 508 | } |
| 494 | return result; | |
| 509 | return { resultsTable, parsedQuery }; | |
| 495 | 510 | }, |
| 496 | getCorrections: function(queryStr, filterCrate, currentCrate) { | |
| 497 | const parsedQuery = searchModule.parseQuery(queryStr); | |
| 498 | docSearch.execQuery(parsedQuery, filterCrate, currentCrate); | |
| 499 | return parsedQuery.correction; | |
| 500 | }, | |
| 501 | parseQuery: searchModule.parseQuery, | |
| 511 | parseQuery: DocSearch.parseQuery, | |
| 502 | 512 | }; |
| 503 | 513 | } |
| 504 | 514 | |
| ... | ... | @@ -570,7 +580,7 @@ async function main(argv) { |
| 570 | 580 | return 1; |
| 571 | 581 | } |
| 572 | 582 | |
| 573 | const parseAndSearch = loadSearchJS( | |
| 583 | const parseAndSearch = await loadSearchJS( | |
| 574 | 584 | opts["doc_folder"], |
| 575 | 585 | opts["resource_suffix"], |
| 576 | 586 | ); |
| ... | ... | @@ -579,14 +589,11 @@ async function main(argv) { |
| 579 | 589 | const doSearch = function(queryStr, filterCrate) { |
| 580 | 590 | return parseAndSearch.doSearch(queryStr, filterCrate, opts["crate_name"]); |
| 581 | 591 | }; |
| 582 | const getCorrections = function(queryStr, filterCrate) { | |
| 583 | return parseAndSearch.getCorrections(queryStr, filterCrate, opts["crate_name"]); | |
| 584 | }; | |
| 585 | 592 | |
| 586 | 593 | if (opts["test_file"].length !== 0) { |
| 587 | 594 | for (const file of opts["test_file"]) { |
| 588 | 595 | process.stdout.write(`Testing ${file} ... `); |
| 589 | errors += await runChecks(file, doSearch, parseAndSearch.parseQuery, getCorrections); | |
| 596 | errors += await runChecks(file, doSearch, parseAndSearch.parseQuery); | |
| 590 | 597 | } |
| 591 | 598 | } else if (opts["test_folder"].length !== 0) { |
| 592 | 599 | for (const file of fs.readdirSync(opts["test_folder"])) { |
| ... | ... | @@ -595,7 +602,7 @@ async function main(argv) { |
| 595 | 602 | } |
| 596 | 603 | process.stdout.write(`Testing ${file} ... `); |
| 597 | 604 | errors += await runChecks(path.join(opts["test_folder"], file), doSearch, |
| 598 | parseAndSearch.parseQuery, getCorrections); | |
| 605 | parseAndSearch.parseQuery); | |
| 599 | 606 | } |
| 600 | 607 | } |
| 601 | 608 | return errors > 0 ? 1 : 0; |
tests/codegen-llvm/enum/enum-aggregate.rs+4-4| ... | ... | @@ -27,7 +27,7 @@ fn make_none_bool() -> Option<bool> { |
| 27 | 27 | |
| 28 | 28 | #[no_mangle] |
| 29 | 29 | fn make_some_ordering(x: Ordering) -> Option<Ordering> { |
| 30 | // CHECK-LABEL: i8 @make_some_ordering(i8 %x) | |
| 30 | // CHECK-LABEL: i8 @make_some_ordering(i8{{( signext)?}} %x) | |
| 31 | 31 | // CHECK-NEXT: start: |
| 32 | 32 | // CHECK-NEXT: ret i8 %x |
| 33 | 33 | Some(x) |
| ... | ... | @@ -35,7 +35,7 @@ fn make_some_ordering(x: Ordering) -> Option<Ordering> { |
| 35 | 35 | |
| 36 | 36 | #[no_mangle] |
| 37 | 37 | fn make_some_u16(x: u16) -> Option<u16> { |
| 38 | // CHECK-LABEL: { i16, i16 } @make_some_u16(i16 %x) | |
| 38 | // CHECK-LABEL: { i16, i16 } @make_some_u16(i16{{( zeroext)?}} %x) | |
| 39 | 39 | // CHECK-NEXT: start: |
| 40 | 40 | // CHECK-NEXT: %0 = insertvalue { i16, i16 } { i16 1, i16 poison }, i16 %x, 1 |
| 41 | 41 | // CHECK-NEXT: ret { i16, i16 } %0 |
| ... | ... | @@ -52,7 +52,7 @@ fn make_none_u16() -> Option<u16> { |
| 52 | 52 | |
| 53 | 53 | #[no_mangle] |
| 54 | 54 | fn make_some_nzu32(x: NonZero<u32>) -> Option<NonZero<u32>> { |
| 55 | // CHECK-LABEL: i32 @make_some_nzu32(i32 %x) | |
| 55 | // CHECK-LABEL: i32 @make_some_nzu32(i32{{( signext)?}} %x) | |
| 56 | 56 | // CHECK-NEXT: start: |
| 57 | 57 | // CHECK-NEXT: ret i32 %x |
| 58 | 58 | Some(x) |
| ... | ... | @@ -114,7 +114,7 @@ fn make_uninhabited_err_indirectly(n: Never) -> Result<u32, Never> { |
| 114 | 114 | fn make_fully_uninhabited_result(v: u32, n: Never) -> Result<(u32, Never), (Never, u32)> { |
| 115 | 115 | // Actually reaching this would be UB, so we don't actually build a result. |
| 116 | 116 | |
| 117 | // CHECK-LABEL: { i32, i32 } @make_fully_uninhabited_result(i32 %v) | |
| 117 | // CHECK-LABEL: { i32, i32 } @make_fully_uninhabited_result(i32{{( signext)?}} %v) | |
| 118 | 118 | // CHECK-NEXT: start: |
| 119 | 119 | // CHECK-NEXT: call void @llvm.trap() |
| 120 | 120 | // CHECK-NEXT: call void @llvm.trap() |
tests/codegen-llvm/enum/enum-match.rs+1-1| ... | ... | @@ -739,7 +739,7 @@ pub enum Tricky { |
| 739 | 739 | |
| 740 | 740 | const _: () = assert!(std::intrinsics::discriminant_value(&Tricky::V100) == 100); |
| 741 | 741 | |
| 742 | // CHECK-LABEL: define noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @discriminant6(i8 noundef %e) | |
| 742 | // CHECK-LABEL: define noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @discriminant6(i8 noundef{{( zeroext)?}} %e) | |
| 743 | 743 | // CHECK-NEXT: start: |
| 744 | 744 | // CHECK-NEXT: %[[REL_VAR:.+]] = add i8 %e, -66 |
| 745 | 745 | // CHECK-NEXT: %[[IS_NICHE:.+]] = icmp ult i8 %[[REL_VAR]], -56 |
tests/codegen-llvm/enum/enum-transparent-extract.rs+1-1| ... | ... | @@ -9,7 +9,7 @@ pub enum Never {} |
| 9 | 9 | |
| 10 | 10 | #[no_mangle] |
| 11 | 11 | pub fn make_unmake_result_never(x: i32) -> i32 { |
| 12 | // CHECK-LABEL: define i32 @make_unmake_result_never(i32 %x) | |
| 12 | // CHECK-LABEL: define i32 @make_unmake_result_never(i32{{( signext)?}} %x) | |
| 13 | 13 | // CHECK: start: |
| 14 | 14 | // CHECK-NEXT: ret i32 %x |
| 15 | 15 |
tests/codegen-llvm/repeat-operand-zero-len.rs+2-2| ... | ... | @@ -11,7 +11,7 @@ |
| 11 | 11 | #[repr(transparent)] |
| 12 | 12 | pub struct Wrapper<T, const N: usize>([T; N]); |
| 13 | 13 | |
| 14 | // CHECK-LABEL: define {{.+}}do_repeat{{.+}}(i32 noundef %x) | |
| 14 | // CHECK-LABEL: define {{.+}}do_repeat{{.+}}(i32 noundef{{( signext)?}} %x) | |
| 15 | 15 | // CHECK-NEXT: start: |
| 16 | 16 | // CHECK-NOT: alloca |
| 17 | 17 | // CHECK-NEXT: ret void |
| ... | ... | @@ -23,6 +23,6 @@ pub fn do_repeat<T: Copy, const N: usize>(x: T) -> Wrapper<T, N> { |
| 23 | 23 | // CHECK-LABEL: @trigger_repeat_zero_len |
| 24 | 24 | #[no_mangle] |
| 25 | 25 | pub fn trigger_repeat_zero_len() -> Wrapper<u32, 0> { |
| 26 | // CHECK: call void {{.+}}do_repeat{{.+}}(i32 noundef 4) | |
| 26 | // CHECK: call void {{.+}}do_repeat{{.+}}(i32 noundef{{( signext)?}} 4) | |
| 27 | 27 | do_repeat(4) |
| 28 | 28 | } |
tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs deleted-18| ... | ... | @@ -1,18 +0,0 @@ |
| 1 | // Verifies that pointer type membership tests for indirect calls are omitted. | |
| 2 | // | |
| 3 | //@ needs-sanitizer-cfi | |
| 4 | //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 | |
| 5 | ||
| 6 | #![crate_type = "lib"] | |
| 7 | #![feature(no_sanitize)] | |
| 8 | ||
| 9 | #[no_sanitize(cfi)] | |
| 10 | pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { | |
| 11 | // CHECK-LABEL: emit_type_checks_attr_no_sanitize::foo | |
| 12 | // CHECK: Function Attrs: {{.*}} | |
| 13 | // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} | |
| 14 | // CHECK: start: | |
| 15 | // CHECK-NEXT: {{%.+}} = call i32 %f(i32{{.*}} %arg) | |
| 16 | // CHECK-NEXT: ret i32 {{%.+}} | |
| 17 | f(arg) | |
| 18 | } |
tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-sanitize-off.rs created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | // Verifies that pointer type membership tests for indirect calls are omitted. | |
| 2 | // | |
| 3 | //@ needs-sanitizer-cfi | |
| 4 | //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 | |
| 5 | ||
| 6 | #![crate_type = "lib"] | |
| 7 | #![feature(sanitize)] | |
| 8 | ||
| 9 | #[sanitize(cfi = "off")] | |
| 10 | pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { | |
| 11 | // CHECK-LABEL: emit_type_checks_attr_sanitize_off::foo | |
| 12 | // CHECK: Function Attrs: {{.*}} | |
| 13 | // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} | |
| 14 | // CHECK: start: | |
| 15 | // CHECK-NEXT: {{%.+}} = call i32 %f(i32{{.*}} %arg) | |
| 16 | // CHECK-NEXT: ret i32 {{%.+}} | |
| 17 | f(arg) | |
| 18 | } |
tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs+2-2| ... | ... | @@ -13,7 +13,7 @@ |
| 13 | 13 | //@[x86_64] needs-llvm-components: x86 |
| 14 | 14 | |
| 15 | 15 | #![crate_type = "rlib"] |
| 16 | #![feature(no_core, no_sanitize, lang_items)] | |
| 16 | #![feature(no_core, sanitize, lang_items)] | |
| 17 | 17 | #![no_core] |
| 18 | 18 | |
| 19 | 19 | extern crate minicore; |
| ... | ... | @@ -25,7 +25,7 @@ use minicore::*; |
| 25 | 25 | // CHECK: start: |
| 26 | 26 | // CHECK-NOT: call void @__asan_report_load |
| 27 | 27 | // CHECK: } |
| 28 | #[no_sanitize(address)] | |
| 28 | #[sanitize(address = "off")] | |
| 29 | 29 | pub fn unsanitized(b: &mut u8) -> u8 { |
| 30 | 30 | *b |
| 31 | 31 | } |
tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs deleted-27| ... | ... | @@ -1,27 +0,0 @@ |
| 1 | // Verifies that KCFI operand bundles are omitted. | |
| 2 | // | |
| 3 | //@ add-core-stubs | |
| 4 | //@ revisions: aarch64 x86_64 | |
| 5 | //@ [aarch64] compile-flags: --target aarch64-unknown-none | |
| 6 | //@ [aarch64] needs-llvm-components: aarch64 | |
| 7 | //@ [x86_64] compile-flags: --target x86_64-unknown-none | |
| 8 | //@ [x86_64] needs-llvm-components: x86 | |
| 9 | //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 | |
| 10 | ||
| 11 | #![crate_type = "lib"] | |
| 12 | #![feature(no_core, no_sanitize, lang_items)] | |
| 13 | #![no_core] | |
| 14 | ||
| 15 | extern crate minicore; | |
| 16 | use minicore::*; | |
| 17 | ||
| 18 | #[no_sanitize(kcfi)] | |
| 19 | pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { | |
| 20 | // CHECK-LABEL: emit_kcfi_operand_bundle_attr_no_sanitize::foo | |
| 21 | // CHECK: Function Attrs: {{.*}} | |
| 22 | // CHECK-LABEL: define{{.*}}foo{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} | |
| 23 | // CHECK: start: | |
| 24 | // CHECK-NOT: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 {{[-0-9]+}}) ] | |
| 25 | // CHECK: ret i32 {{%.+}} | |
| 26 | f(arg) | |
| 27 | } |
tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs created+27| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | // Verifies that KCFI operand bundles are omitted. | |
| 2 | // | |
| 3 | //@ add-core-stubs | |
| 4 | //@ revisions: aarch64 x86_64 | |
| 5 | //@ [aarch64] compile-flags: --target aarch64-unknown-none | |
| 6 | //@ [aarch64] needs-llvm-components: aarch64 | |
| 7 | //@ [x86_64] compile-flags: --target x86_64-unknown-none | |
| 8 | //@ [x86_64] needs-llvm-components: x86 | |
| 9 | //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 | |
| 10 | ||
| 11 | #![crate_type = "lib"] | |
| 12 | #![feature(no_core, sanitize, lang_items)] | |
| 13 | #![no_core] | |
| 14 | ||
| 15 | extern crate minicore; | |
| 16 | use minicore::*; | |
| 17 | ||
| 18 | #[sanitize(kcfi = "off")] | |
| 19 | pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { | |
| 20 | // CHECK-LABEL: emit_kcfi_operand_bundle_attr_sanitize_off::foo | |
| 21 | // CHECK: Function Attrs: {{.*}} | |
| 22 | // CHECK-LABEL: define{{.*}}foo{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}} | |
| 23 | // CHECK: start: | |
| 24 | // CHECK-NOT: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 {{[-0-9]+}}) ] | |
| 25 | // CHECK: ret i32 {{%.+}} | |
| 26 | f(arg) | |
| 27 | } |
tests/codegen-llvm/sanitizer/no-sanitize-inlining.rs deleted-31| ... | ... | @@ -1,31 +0,0 @@ |
| 1 | // Verifies that no_sanitize attribute prevents inlining when | |
| 2 | // given sanitizer is enabled, but has no effect on inlining otherwise. | |
| 3 | // | |
| 4 | //@ needs-sanitizer-address | |
| 5 | //@ needs-sanitizer-leak | |
| 6 | //@ revisions: ASAN LSAN | |
| 7 | //@ compile-flags: -Copt-level=3 -Zmir-opt-level=4 -Ctarget-feature=-crt-static | |
| 8 | //@[ASAN] compile-flags: -Zsanitizer=address | |
| 9 | //@[LSAN] compile-flags: -Zsanitizer=leak | |
| 10 | ||
| 11 | #![crate_type = "lib"] | |
| 12 | #![feature(no_sanitize)] | |
| 13 | ||
| 14 | // ASAN-LABEL: define void @test | |
| 15 | // ASAN: call {{.*}} @random_inline | |
| 16 | // ASAN: } | |
| 17 | // | |
| 18 | // LSAN-LABEL: define void @test | |
| 19 | // LSAN-NOT: call | |
| 20 | // LSAN: } | |
| 21 | #[no_mangle] | |
| 22 | pub fn test(n: &mut u32) { | |
| 23 | random_inline(n); | |
| 24 | } | |
| 25 | ||
| 26 | #[no_sanitize(address)] | |
| 27 | #[inline] | |
| 28 | #[no_mangle] | |
| 29 | pub fn random_inline(n: &mut u32) { | |
| 30 | *n = 42; | |
| 31 | } |
tests/codegen-llvm/sanitizer/no-sanitize.rs deleted-39| ... | ... | @@ -1,39 +0,0 @@ |
| 1 | // Verifies that no_sanitize attribute can be used to | |
| 2 | // selectively disable sanitizer instrumentation. | |
| 3 | // | |
| 4 | //@ needs-sanitizer-address | |
| 5 | //@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0 | |
| 6 | ||
| 7 | #![crate_type = "lib"] | |
| 8 | #![feature(no_sanitize)] | |
| 9 | ||
| 10 | // CHECK: @UNSANITIZED = constant{{.*}} no_sanitize_address | |
| 11 | // CHECK-NOT: @__asan_global_UNSANITIZED | |
| 12 | #[no_mangle] | |
| 13 | #[no_sanitize(address)] | |
| 14 | pub static UNSANITIZED: u32 = 0; | |
| 15 | ||
| 16 | // CHECK: @__asan_global_SANITIZED | |
| 17 | #[no_mangle] | |
| 18 | pub static SANITIZED: u32 = 0; | |
| 19 | ||
| 20 | // CHECK-LABEL: ; no_sanitize::unsanitized | |
| 21 | // CHECK-NEXT: ; Function Attrs: | |
| 22 | // CHECK-NOT: sanitize_address | |
| 23 | // CHECK: start: | |
| 24 | // CHECK-NOT: call void @__asan_report_load | |
| 25 | // CHECK: } | |
| 26 | #[no_sanitize(address)] | |
| 27 | pub fn unsanitized(b: &mut u8) -> u8 { | |
| 28 | *b | |
| 29 | } | |
| 30 | ||
| 31 | // CHECK-LABEL: ; no_sanitize::sanitized | |
| 32 | // CHECK-NEXT: ; Function Attrs: | |
| 33 | // CHECK: sanitize_address | |
| 34 | // CHECK: start: | |
| 35 | // CHECK: call void @__asan_report_load | |
| 36 | // CHECK: } | |
| 37 | pub fn sanitized(b: &mut u8) -> u8 { | |
| 38 | *b | |
| 39 | } |
tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs created+42| ... | ... | @@ -0,0 +1,42 @@ |
| 1 | // Verifies that the `#[sanitize(address = "off")]` attribute also turns off | |
| 2 | // the kernel address sanitizer. | |
| 3 | // | |
| 4 | //@ add-core-stubs | |
| 5 | //@ compile-flags: -Zsanitizer=kernel-address -Ctarget-feature=-crt-static -Copt-level=0 | |
| 6 | //@ revisions: aarch64 riscv64imac riscv64gc x86_64 | |
| 7 | //@[aarch64] compile-flags: --target aarch64-unknown-none | |
| 8 | //@[aarch64] needs-llvm-components: aarch64 | |
| 9 | //@[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf | |
| 10 | //@[riscv64imac] needs-llvm-components: riscv | |
| 11 | //@[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf | |
| 12 | //@[riscv64gc] needs-llvm-components: riscv | |
| 13 | //@[x86_64] compile-flags: --target x86_64-unknown-none | |
| 14 | //@[x86_64] needs-llvm-components: x86 | |
| 15 | ||
| 16 | #![crate_type = "rlib"] | |
| 17 | #![feature(no_core, sanitize, lang_items)] | |
| 18 | #![no_core] | |
| 19 | ||
| 20 | extern crate minicore; | |
| 21 | use minicore::*; | |
| 22 | ||
| 23 | // CHECK-LABEL: ; sanitize_off_asan_kasan::unsanitized | |
| 24 | // CHECK-NEXT: ; Function Attrs: | |
| 25 | // CHECK-NOT: sanitize_address | |
| 26 | // CHECK: start: | |
| 27 | // CHECK-NOT: call void @__asan_report_load | |
| 28 | // CHECK: } | |
| 29 | #[sanitize(address = "off")] | |
| 30 | pub fn unsanitized(b: &mut u8) -> u8 { | |
| 31 | *b | |
| 32 | } | |
| 33 | ||
| 34 | // CHECK-LABEL: ; sanitize_off_asan_kasan::sanitized | |
| 35 | // CHECK-NEXT: ; Function Attrs: | |
| 36 | // CHECK: sanitize_address | |
| 37 | // CHECK: start: | |
| 38 | // CHECK: call void @__asan_report_load | |
| 39 | // CHECK: } | |
| 40 | pub fn sanitized(b: &mut u8) -> u8 { | |
| 41 | *b | |
| 42 | } |
tests/codegen-llvm/sanitizer/sanitize-off-inlining.rs created+31| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | // Verifies that sanitize(xyz = "off") attribute prevents inlining when | |
| 2 | // given sanitizer is enabled, but has no effect on inlining otherwise. | |
| 3 | // | |
| 4 | //@ needs-sanitizer-address | |
| 5 | //@ needs-sanitizer-leak | |
| 6 | //@ revisions: ASAN LSAN | |
| 7 | //@ compile-flags: -Copt-level=3 -Zmir-opt-level=4 -Ctarget-feature=-crt-static | |
| 8 | //@[ASAN] compile-flags: -Zsanitizer=address | |
| 9 | //@[LSAN] compile-flags: -Zsanitizer=leak | |
| 10 | ||
| 11 | #![crate_type = "lib"] | |
| 12 | #![feature(sanitize)] | |
| 13 | ||
| 14 | // ASAN-LABEL: define void @test | |
| 15 | // ASAN: call {{.*}} @random_inline | |
| 16 | // ASAN: } | |
| 17 | // | |
| 18 | // LSAN-LABEL: define void @test | |
| 19 | // LSAN-NOT: call | |
| 20 | // LSAN: } | |
| 21 | #[no_mangle] | |
| 22 | pub fn test(n: &mut u32) { | |
| 23 | random_inline(n); | |
| 24 | } | |
| 25 | ||
| 26 | #[sanitize(address = "off")] | |
| 27 | #[inline] | |
| 28 | #[no_mangle] | |
| 29 | pub fn random_inline(n: &mut u32) { | |
| 30 | *n = 42; | |
| 31 | } |
tests/codegen-llvm/sanitizer/sanitize-off-kasan-asan.rs created+29| ... | ... | @@ -0,0 +1,29 @@ |
| 1 | // Verifies that the `#[sanitize(kernel_address = "off")]` attribute also turns off | |
| 2 | // the address sanitizer. | |
| 3 | // | |
| 4 | //@ needs-sanitizer-address | |
| 5 | //@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0 | |
| 6 | ||
| 7 | #![crate_type = "lib"] | |
| 8 | #![feature(sanitize)] | |
| 9 | ||
| 10 | // CHECK-LABEL: ; sanitize_off_kasan_asan::unsanitized | |
| 11 | // CHECK-NEXT: ; Function Attrs: | |
| 12 | // CHECK-NOT: sanitize_address | |
| 13 | // CHECK: start: | |
| 14 | // CHECK-NOT: call void @__asan_report_load | |
| 15 | // CHECK: } | |
| 16 | #[sanitize(kernel_address = "off")] | |
| 17 | pub fn unsanitized(b: &mut u8) -> u8 { | |
| 18 | *b | |
| 19 | } | |
| 20 | ||
| 21 | // CHECK-LABEL: ; sanitize_off_kasan_asan::sanitized | |
| 22 | // CHECK-NEXT: ; Function Attrs: | |
| 23 | // CHECK: sanitize_address | |
| 24 | // CHECK: start: | |
| 25 | // CHECK: call void @__asan_report_load | |
| 26 | // CHECK: } | |
| 27 | pub fn sanitized(b: &mut u8) -> u8 { | |
| 28 | *b | |
| 29 | } |
tests/codegen-llvm/sanitizer/sanitize-off.rs created+138| ... | ... | @@ -0,0 +1,138 @@ |
| 1 | // Verifies that the `#[sanitize(address = "off")]` attribute can be used to | |
| 2 | // selectively disable sanitizer instrumentation. | |
| 3 | // | |
| 4 | //@ needs-sanitizer-address | |
| 5 | //@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0 | |
| 6 | ||
| 7 | #![crate_type = "lib"] | |
| 8 | #![feature(sanitize)] | |
| 9 | ||
| 10 | // CHECK: @UNSANITIZED = constant{{.*}} no_sanitize_address | |
| 11 | // CHECK-NOT: @__asan_global_SANITIZED | |
| 12 | #[no_mangle] | |
| 13 | #[sanitize(address = "off")] | |
| 14 | pub static UNSANITIZED: u32 = 0; | |
| 15 | ||
| 16 | // CHECK: @__asan_global_SANITIZED | |
| 17 | #[no_mangle] | |
| 18 | pub static SANITIZED: u32 = 0; | |
| 19 | ||
| 20 | // CHECK-LABEL: ; sanitize_off::unsanitized | |
| 21 | // CHECK-NEXT: ; Function Attrs: | |
| 22 | // CHECK-NOT: sanitize_address | |
| 23 | // CHECK: start: | |
| 24 | // CHECK-NOT: call void @__asan_report_load | |
| 25 | // CHECK: } | |
| 26 | #[sanitize(address = "off")] | |
| 27 | pub fn unsanitized(b: &mut u8) -> u8 { | |
| 28 | *b | |
| 29 | } | |
| 30 | ||
| 31 | // CHECK-LABEL: ; sanitize_off::sanitized | |
| 32 | // CHECK-NEXT: ; Function Attrs: | |
| 33 | // CHECK: sanitize_address | |
| 34 | // CHECK: start: | |
| 35 | // CHECK: call void @__asan_report_load | |
| 36 | // CHECK: } | |
| 37 | pub fn sanitized(b: &mut u8) -> u8 { | |
| 38 | *b | |
| 39 | } | |
| 40 | ||
| 41 | #[sanitize(address = "off")] | |
| 42 | pub mod foo { | |
| 43 | // CHECK-LABEL: ; sanitize_off::foo::unsanitized | |
| 44 | // CHECK-NEXT: ; Function Attrs: | |
| 45 | // CHECK-NOT: sanitize_address | |
| 46 | // CHECK: start: | |
| 47 | // CHECK-NOT: call void @__asan_report_load | |
| 48 | // CHECK: } | |
| 49 | pub fn unsanitized(b: &mut u8) -> u8 { | |
| 50 | *b | |
| 51 | } | |
| 52 | ||
| 53 | // CHECK-LABEL: ; sanitize_off::foo::sanitized | |
| 54 | // CHECK-NEXT: ; Function Attrs: | |
| 55 | // CHECK: sanitize_address | |
| 56 | // CHECK: start: | |
| 57 | // CHECK: call void @__asan_report_load | |
| 58 | // CHECK: } | |
| 59 | #[sanitize(address = "on")] | |
| 60 | pub fn sanitized(b: &mut u8) -> u8 { | |
| 61 | *b | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | pub trait MyTrait { | |
| 66 | fn unsanitized(&self, b: &mut u8) -> u8; | |
| 67 | fn sanitized(&self, b: &mut u8) -> u8; | |
| 68 | ||
| 69 | // CHECK-LABEL: ; sanitize_off::MyTrait::unsanitized_default | |
| 70 | // CHECK-NEXT: ; Function Attrs: | |
| 71 | // CHECK-NOT: sanitize_address | |
| 72 | // CHECK: start: | |
| 73 | // CHECK-NOT: call void @__asan_report_load | |
| 74 | // CHECK: } | |
| 75 | #[sanitize(address = "off")] | |
| 76 | fn unsanitized_default(&self, b: &mut u8) -> u8 { | |
| 77 | *b | |
| 78 | } | |
| 79 | ||
| 80 | // CHECK-LABEL: ; sanitize_off::MyTrait::sanitized_default | |
| 81 | // CHECK-NEXT: ; Function Attrs: | |
| 82 | // CHECK: sanitize_address | |
| 83 | // CHECK: start: | |
| 84 | // CHECK: call void @__asan_report_load | |
| 85 | // CHECK: } | |
| 86 | fn sanitized_default(&self, b: &mut u8) -> u8 { | |
| 87 | *b | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | #[sanitize(address = "off")] | |
| 92 | impl MyTrait for () { | |
| 93 | // CHECK-LABEL: ; <() as sanitize_off::MyTrait>::unsanitized | |
| 94 | // CHECK-NEXT: ; Function Attrs: | |
| 95 | // CHECK-NOT: sanitize_address | |
| 96 | // CHECK: start: | |
| 97 | // CHECK-NOT: call void @__asan_report_load | |
| 98 | // CHECK: } | |
| 99 | fn unsanitized(&self, b: &mut u8) -> u8 { | |
| 100 | *b | |
| 101 | } | |
| 102 | ||
| 103 | // CHECK-LABEL: ; <() as sanitize_off::MyTrait>::sanitized | |
| 104 | // CHECK-NEXT: ; Function Attrs: | |
| 105 | // CHECK: sanitize_address | |
| 106 | // CHECK: start: | |
| 107 | // CHECK: call void @__asan_report_load | |
| 108 | // CHECK: } | |
| 109 | #[sanitize(address = "on")] | |
| 110 | fn sanitized(&self, b: &mut u8) -> u8 { | |
| 111 | *b | |
| 112 | } | |
| 113 | } | |
| 114 | ||
| 115 | pub fn expose_trait(b: &mut u8) -> u8 { | |
| 116 | <() as MyTrait>::unsanitized_default(&(), b); | |
| 117 | <() as MyTrait>::sanitized_default(&(), b) | |
| 118 | } | |
| 119 | ||
| 120 | #[sanitize(address = "off")] | |
| 121 | pub mod outer { | |
| 122 | #[sanitize(thread = "off")] | |
| 123 | pub mod inner { | |
| 124 | // CHECK-LABEL: ; sanitize_off::outer::inner::unsanitized | |
| 125 | // CHECK-NEXT: ; Function Attrs: | |
| 126 | // CHECK-NOT: sanitize_address | |
| 127 | // CHECK: start: | |
| 128 | // CHECK-NOT: call void @__asan_report_load | |
| 129 | // CHECK: } | |
| 130 | pub fn unsanitized() { | |
| 131 | let xs = [0, 1, 2, 3]; | |
| 132 | // Avoid optimizing everything out. | |
| 133 | let xs = std::hint::black_box(xs.as_ptr()); | |
| 134 | let code = unsafe { *xs.offset(4) }; | |
| 135 | std::process::exit(code); | |
| 136 | } | |
| 137 | } | |
| 138 | } |
tests/codegen-llvm/sanitizer/scs-attr-check.rs+2-2| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | //@ compile-flags: -Zsanitizer=shadow-call-stack |
| 6 | 6 | |
| 7 | 7 | #![crate_type = "lib"] |
| 8 | #![feature(no_sanitize)] | |
| 8 | #![feature(sanitize)] | |
| 9 | 9 | |
| 10 | 10 | // CHECK: ; sanitizer_scs_attr_check::scs |
| 11 | 11 | // CHECK-NEXT: ; Function Attrs:{{.*}}shadowcallstack |
| ... | ... | @@ -13,5 +13,5 @@ pub fn scs() {} |
| 13 | 13 | |
| 14 | 14 | // CHECK: ; sanitizer_scs_attr_check::no_scs |
| 15 | 15 | // CHECK-NOT: ; Function Attrs:{{.*}}shadowcallstack |
| 16 | #[no_sanitize(shadow_call_stack)] | |
| 16 | #[sanitize(shadow_call_stack = "off")] | |
| 17 | 17 | pub fn no_scs() {} |
tests/codegen-llvm/transmute-scalar.rs+3-10| ... | ... | @@ -1,8 +1,9 @@ |
| 1 | 1 | //@ add-core-stubs |
| 2 | //@ compile-flags: -C opt-level=0 -C no-prepopulate-passes | |
| 2 | //@ compile-flags: -C opt-level=0 -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu | |
| 3 | //@ needs-llvm-components: x86 | |
| 3 | 4 | |
| 4 | 5 | #![crate_type = "lib"] |
| 5 | #![feature(no_core, repr_simd, arm_target_feature, mips_target_feature, s390x_target_feature)] | |
| 6 | #![feature(no_core, repr_simd)] | |
| 6 | 7 | #![no_core] |
| 7 | 8 | extern crate minicore; |
| 8 | 9 | |
| ... | ... | @@ -117,11 +118,7 @@ struct S([i64; 1]); |
| 117 | 118 | // CHECK-NEXT: %[[TEMP:.+]] = load i64, ptr %[[RET]] |
| 118 | 119 | // CHECK-NEXT: ret i64 %[[TEMP]] |
| 119 | 120 | #[no_mangle] |
| 120 | #[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] | |
| 121 | #[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] | |
| 122 | 121 | #[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] |
| 123 | #[cfg_attr(target_arch = "mips", target_feature(enable = "msa"))] | |
| 124 | #[cfg_attr(target_arch = "s390x", target_feature(enable = "vector"))] | |
| 125 | 122 | pub extern "C" fn single_element_simd_to_scalar(b: S) -> i64 { |
| 126 | 123 | unsafe { mem::transmute(b) } |
| 127 | 124 | } |
| ... | ... | @@ -133,11 +130,7 @@ pub extern "C" fn single_element_simd_to_scalar(b: S) -> i64 { |
| 133 | 130 | // CHECK-NEXT: %[[TEMP:.+]] = load <1 x i64>, ptr %[[RET]] |
| 134 | 131 | // CHECK-NEXT: ret <1 x i64> %[[TEMP]] |
| 135 | 132 | #[no_mangle] |
| 136 | #[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] | |
| 137 | #[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] | |
| 138 | 133 | #[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] |
| 139 | #[cfg_attr(target_arch = "mips", target_feature(enable = "msa"))] | |
| 140 | #[cfg_attr(target_arch = "s390x", target_feature(enable = "vector"))] | |
| 141 | 134 | pub extern "C" fn scalar_to_single_element_simd(b: i64) -> S { |
| 142 | 135 | unsafe { mem::transmute(b) } |
| 143 | 136 | } |
tests/codegen-llvm/uninhabited-transparent-return-abi.rs+1-1| ... | ... | @@ -36,7 +36,7 @@ pub fn test_uninhabited_ret_by_ref() { |
| 36 | 36 | pub fn test_uninhabited_ret_by_ref_with_arg(rsi: u32) { |
| 37 | 37 | // CHECK: %_2 = alloca [24 x i8], align {{8|4}} |
| 38 | 38 | // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 24, )?}}ptr nonnull %_2) |
| 39 | // CHECK-NEXT: call void @opaque_with_arg({{.*}} sret([24 x i8]) {{.*}} %_2, i32 noundef %rsi) #2 | |
| 39 | // CHECK-NEXT: call void @opaque_with_arg({{.*}} sret([24 x i8]) {{.*}} %_2, i32 noundef{{( signext)?}} %rsi) #2 | |
| 40 | 40 | // CHECK-NEXT: unreachable |
| 41 | 41 | unsafe { |
| 42 | 42 | opaque_with_arg(rsi); |
tests/mir-opt/inline/inline_compatibility.rs+11-11| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | //@ compile-flags: -Cpanic=abort |
| 4 | 4 | |
| 5 | 5 | #![crate_type = "lib"] |
| 6 | #![feature(no_sanitize)] | |
| 6 | #![feature(sanitize)] | |
| 7 | 7 | #![feature(c_variadic)] |
| 8 | 8 | |
| 9 | 9 | #[inline] |
| ... | ... | @@ -37,22 +37,22 @@ pub unsafe fn f2() { |
| 37 | 37 | } |
| 38 | 38 | |
| 39 | 39 | #[inline] |
| 40 | #[no_sanitize(address)] | |
| 41 | pub unsafe fn no_sanitize() {} | |
| 40 | #[sanitize(address = "off")] | |
| 41 | pub unsafe fn sanitize_off() {} | |
| 42 | 42 | |
| 43 | // CHECK-LABEL: fn inlined_no_sanitize() | |
| 43 | // CHECK-LABEL: fn inlined_sanitize_off() | |
| 44 | 44 | // CHECK: bb0: { |
| 45 | 45 | // CHECK-NEXT: return; |
| 46 | #[no_sanitize(address)] | |
| 47 | pub unsafe fn inlined_no_sanitize() { | |
| 48 | no_sanitize(); | |
| 46 | #[sanitize(address = "off")] | |
| 47 | pub unsafe fn inlined_sanitize_off() { | |
| 48 | sanitize_off(); | |
| 49 | 49 | } |
| 50 | 50 | |
| 51 | // CHECK-LABEL: fn not_inlined_no_sanitize() | |
| 51 | // CHECK-LABEL: fn not_inlined_sanitize_off() | |
| 52 | 52 | // CHECK: bb0: { |
| 53 | // CHECK-NEXT: no_sanitize() | |
| 54 | pub unsafe fn not_inlined_no_sanitize() { | |
| 55 | no_sanitize(); | |
| 53 | // CHECK-NEXT: sanitize_off() | |
| 54 | pub unsafe fn not_inlined_sanitize_off() { | |
| 55 | sanitize_off(); | |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | // CHECK-LABEL: fn not_inlined_c_variadic() |
tests/run-make/emit-shared-files/rmake.rs+1-1| ... | ... | @@ -19,7 +19,7 @@ fn main() { |
| 19 | 19 | .args(&["--extend-css", "z.css"]) |
| 20 | 20 | .input("x.rs") |
| 21 | 21 | .run(); |
| 22 | assert!(path("invocation-only/search-index-xxx.js").exists()); | |
| 22 | assert!(path("invocation-only/search.index/root-xxx.js").exists()); | |
| 23 | 23 | assert!(path("invocation-only/crates-xxx.js").exists()); |
| 24 | 24 | assert!(path("invocation-only/settings.html").exists()); |
| 25 | 25 | assert!(path("invocation-only/x/all.html").exists()); |
tests/run-make/rustdoc-determinism/rmake.rs+2-2| ... | ... | @@ -15,7 +15,7 @@ fn main() { |
| 15 | 15 | rustdoc().input("foo.rs").out_dir(&bar_first).run(); |
| 16 | 16 | |
| 17 | 17 | diff() |
| 18 | .expected_file(foo_first.join("search-index.js")) | |
| 19 | .actual_file(bar_first.join("search-index.js")) | |
| 18 | .expected_file(foo_first.join("search.index/root.js")) | |
| 19 | .actual_file(bar_first.join("search.index/root.js")) | |
| 20 | 20 | .run(); |
| 21 | 21 | } |
tests/run-make/rustdoc-scrape-examples-paths/rmake.rs+8-8| ... | ... | @@ -1,16 +1,16 @@ |
| 1 | 1 | //! Test to ensure that the rustdoc `scrape-examples` feature is not panicking. |
| 2 | 2 | //! Regression test for <https://github.com/rust-lang/rust/issues/144752>. |
| 3 | 3 | |
| 4 | use run_make_support::{cargo, path, rfs}; | |
| 4 | use run_make_support::cargo; | |
| 5 | use run_make_support::scoped_run::run_in_tmpdir; | |
| 5 | 6 | |
| 6 | 7 | fn main() { |
| 7 | 8 | // We copy the crate to be documented "outside" to prevent documenting |
| 8 | 9 | // the whole compiler. |
| 9 | let tmp = std::env::temp_dir(); | |
| 10 | let test_crate = tmp.join("foo"); | |
| 11 | rfs::copy_dir_all(path("foo"), &test_crate); | |
| 12 | ||
| 13 | // The `scrape-examples` feature is also implemented in `cargo` so instead of reproducing | |
| 14 | // what `cargo` does, better to just let `cargo` do it. | |
| 15 | cargo().current_dir(&test_crate).args(["doc", "-p", "foo", "-Zrustdoc-scrape-examples"]).run(); | |
| 10 | std::env::set_current_dir("foo").unwrap(); | |
| 11 | run_in_tmpdir(|| { | |
| 12 | // The `scrape-examples` feature is also implemented in `cargo` so instead of reproducing | |
| 13 | // what `cargo` does, better to just let `cargo` do it. | |
| 14 | cargo().args(["doc", "-p", "foo", "-Zrustdoc-scrape-examples"]).run(); | |
| 15 | }) | |
| 16 | 16 | } |
tests/rustdoc-gui/code-example-buttons.goml+4-4| ... | ... | @@ -5,25 +5,25 @@ include: "utils.goml" |
| 5 | 5 | // First we check we "hover". |
| 6 | 6 | move-cursor-to: ".example-wrap" |
| 7 | 7 | assert-css: (".example-wrap .copy-button", { "visibility": "visible" }) |
| 8 | move-cursor-to: ".search-input" | |
| 8 | move-cursor-to: "#search-button" | |
| 9 | 9 | assert-css: (".example-wrap .copy-button", { "visibility": "hidden" }) |
| 10 | 10 | |
| 11 | 11 | // Now we check the click. |
| 12 | 12 | assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0) |
| 13 | 13 | click: ".example-wrap" |
| 14 | move-cursor-to: ".search-input" | |
| 14 | move-cursor-to: "#search-button" | |
| 15 | 15 | // It should have a new class and be visible. |
| 16 | 16 | wait-for-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 1) |
| 17 | 17 | wait-for-css: (".example-wrap:not(:hover) .button-holder.keep-visible", { "visibility": "visible" }) |
| 18 | 18 | // Clicking again will remove the class. |
| 19 | 19 | click: ".example-wrap" |
| 20 | move-cursor-to: ".search-input" | |
| 20 | move-cursor-to: "rustdoc-toolbar #search-button" | |
| 21 | 21 | assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0) |
| 22 | 22 | assert-css: (".example-wrap .copy-button", { "visibility": "hidden" }) |
| 23 | 23 | |
| 24 | 24 | // Clicking on the "copy code" button shouldn't make the buttons stick. |
| 25 | 25 | click: ".example-wrap .copy-button" |
| 26 | move-cursor-to: ".search-input" | |
| 26 | move-cursor-to: "#search-button" | |
| 27 | 27 | assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0) |
| 28 | 28 | assert-css: (".example-wrap .copy-button", { "visibility": "hidden" }) |
| 29 | 29 | // Since we clicked on the copy button, the clipboard content should have been updated. |
tests/rustdoc-gui/copy-code.goml+1-1| ... | ... | @@ -12,7 +12,7 @@ define-function: ( |
| 12 | 12 | assert-count: (".example-wrap .copy-button", 1) |
| 13 | 13 | // We now ensure it's only displayed when the example is hovered. |
| 14 | 14 | assert-css: (".example-wrap .copy-button", { "visibility": "visible" }) |
| 15 | move-cursor-to: ".search-input" | |
| 15 | move-cursor-to: "rustdoc-toolbar #search-button" | |
| 16 | 16 | assert-css: (".example-wrap .copy-button", { "visibility": "hidden" }) |
| 17 | 17 | // Checking that the copy button has the same size as the "copy path" button. |
| 18 | 18 | compare-elements-size: ( |
tests/rustdoc-gui/cursor.goml+2-5| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | // This test ensures that several clickable items actually have the pointer cursor. |
| 2 | include: "utils.goml" | |
| 2 | 3 | go-to: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html" |
| 3 | 4 | |
| 4 | 5 | // the `[+]/[-]` button |
| ... | ... | @@ -8,11 +9,7 @@ assert-css: ("#toggle-all-docs", {"cursor": "pointer"}) |
| 8 | 9 | assert-css: ("#copy-path", {"cursor": "pointer"}) |
| 9 | 10 | |
| 10 | 11 | // the search tabs |
| 11 | write-into: (".search-input", "Foo") | |
| 12 | // To be SURE that the search will be run. | |
| 13 | press-key: 'Enter' | |
| 14 | // Waiting for the search results to appear... | |
| 15 | wait-for: "#search-tabs" | |
| 12 | call-function: ("perform-search", {"query": "Foo"}) | |
| 16 | 13 | assert-css: ("#search-tabs > button", {"cursor": "pointer"}) |
| 17 | 14 | |
| 18 | 15 | // mobile sidebar toggle button |
tests/rustdoc-gui/docblock-code-block-line-number.goml+4-4| ... | ... | @@ -69,7 +69,7 @@ call-function: ("check-colors", { |
| 69 | 69 | // and make sure it goes away. |
| 70 | 70 | |
| 71 | 71 | // First, open the settings menu. |
| 72 | click: "#settings-menu" | |
| 72 | click: "rustdoc-toolbar .settings-menu" | |
| 73 | 73 | wait-for: "#settings" |
| 74 | 74 | assert-css: ("#settings", {"display": "block"}) |
| 75 | 75 | |
| ... | ... | @@ -121,7 +121,7 @@ call-function: ("check-padding", { |
| 121 | 121 | define-function: ("check-line-numbers-existence", [], block { |
| 122 | 122 | assert-local-storage: {"rustdoc-line-numbers": "true" } |
| 123 | 123 | assert-false: ".example-line-numbers" |
| 124 | click: "#settings-menu" | |
| 124 | click: "rustdoc-toolbar .settings-menu" | |
| 125 | 125 | wait-for: "#settings" |
| 126 | 126 | |
| 127 | 127 | // Then, click the toggle button. |
| ... | ... | @@ -137,7 +137,7 @@ define-function: ("check-line-numbers-existence", [], block { |
| 137 | 137 | // Line numbers should still be there. |
| 138 | 138 | assert-css: ("[data-nosnippet]", { "display": "block"}) |
| 139 | 139 | // Closing settings menu. |
| 140 | click: "#settings-menu" | |
| 140 | click: "rustdoc-toolbar .settings-menu" | |
| 141 | 141 | wait-for-css: ("#settings", {"display": "none"}) |
| 142 | 142 | }) |
| 143 | 143 | |
| ... | ... | @@ -168,7 +168,7 @@ assert: ".example-wrap > pre.rust" |
| 168 | 168 | assert-count: (".example-wrap", 2) |
| 169 | 169 | assert-count: (".example-wrap.digits-1", 2) |
| 170 | 170 | |
| 171 | click: "#settings-menu" | |
| 171 | click: "rustdoc-toolbar .settings-menu" | |
| 172 | 172 | wait-for: "#settings" |
| 173 | 173 | |
| 174 | 174 | // Then, click the toggle button. |
tests/rustdoc-gui/escape-key.goml+4-7| ... | ... | @@ -1,13 +1,10 @@ |
| 1 | 1 | // This test ensures that the "Escape" shortcut is handled correctly based on the |
| 2 | 2 | // current content displayed. |
| 3 | include: "utils.goml" | |
| 3 | 4 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 4 | 5 | // First, we check that the search results are hidden when the Escape key is pressed. |
| 5 | write-into: (".search-input", "test") | |
| 6 | // To be SURE that the search will be run. | |
| 7 | press-key: 'Enter' | |
| 8 | wait-for: "#search h1" // The search element is empty before the first search | |
| 6 | call-function: ("perform-search", {"query": "test"}) | |
| 9 | 7 | // Check that the currently displayed element is search. |
| 10 | wait-for: "#alternative-display #search" | |
| 11 | 8 | assert-attribute: ("#main-content", {"class": "content hidden"}) |
| 12 | 9 | assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH) |
| 13 | 10 | press-key: "Escape" |
| ... | ... | @@ -17,8 +14,8 @@ assert-false: "#alternative-display #search" |
| 17 | 14 | assert-attribute: ("#main-content", {"class": "content"}) |
| 18 | 15 | assert-document-property: ({"URL": "index.html"}, [ENDS_WITH]) |
| 19 | 16 | |
| 20 | // Check that focusing the search input brings back the search results | |
| 21 | focus: ".search-input" | |
| 17 | // Check that clicking the search button brings back the search results | |
| 18 | click: "#search-button" | |
| 22 | 19 | wait-for: "#alternative-display #search" |
| 23 | 20 | assert-attribute: ("#main-content", {"class": "content hidden"}) |
| 24 | 21 | assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH) |
tests/rustdoc-gui/font-serif-change.goml+2-2| ... | ... | @@ -8,7 +8,7 @@ assert-css: ("body", {"font-family": |serif_font|}) |
| 8 | 8 | assert-css: ("p code", {"font-family": |serif_code_font|}) |
| 9 | 9 | |
| 10 | 10 | // We now switch to the sans serif font |
| 11 | click: "#settings-menu" | |
| 11 | click: "main .settings-menu" | |
| 12 | 12 | wait-for: "#sans-serif-fonts" |
| 13 | 13 | click: "#sans-serif-fonts" |
| 14 | 14 | |
| ... | ... | @@ -23,7 +23,7 @@ assert-css: ("body", {"font-family": |font|}) |
| 23 | 23 | assert-css: ("p code", {"font-family": |code_font|}) |
| 24 | 24 | |
| 25 | 25 | // We switch back to the serif font |
| 26 | click: "#settings-menu" | |
| 26 | click: "main .settings-menu" | |
| 27 | 27 | wait-for: "#sans-serif-fonts" |
| 28 | 28 | click: "#sans-serif-fonts" |
| 29 | 29 |
tests/rustdoc-gui/globals.goml+2-3| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | // Make sure search stores its data in `window` |
| 2 | 2 | // It needs to use a global to avoid racing on search-index.js and search.js |
| 3 | 3 | // https://github.com/rust-lang/rust/pull/118961 |
| 4 | include: "utils.goml" | |
| 4 | 5 | |
| 5 | 6 | // URL query |
| 6 | 7 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds" |
| ... | ... | @@ -9,9 +10,7 @@ assert-window-property-false: {"searchIndex": null} |
| 9 | 10 | |
| 10 | 11 | // Form input |
| 11 | 12 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 12 | write-into: (".search-input", "Foo") | |
| 13 | press-key: 'Enter' | |
| 14 | wait-for: "#search-tabs" | |
| 13 | call-function: ("perform-search", {"query": "Foo"}) | |
| 15 | 14 | assert-window-property-false: {"searchIndex": null} |
| 16 | 15 | |
| 17 | 16 | // source sidebar |
tests/rustdoc-gui/help-page.goml+8-10| ... | ... | @@ -6,12 +6,12 @@ assert-css: ("#help", {"display": "block"}) |
| 6 | 6 | assert-css: ("#help dd", {"font-size": "16px"}) |
| 7 | 7 | assert-false: "#help-button > a" |
| 8 | 8 | assert-css: ("#help", {"display": "block"}) |
| 9 | compare-elements-property: (".sub", "#help", ["offsetWidth"]) | |
| 10 | compare-elements-position: (".sub", "#help", ["x"]) | |
| 9 | compare-elements-property: (".main-heading", "#help", ["offsetWidth"]) | |
| 10 | compare-elements-position: (".main-heading", "#help", ["x"]) | |
| 11 | 11 | set-window-size: (500, 1000) // Try mobile next. |
| 12 | 12 | assert-css: ("#help", {"display": "block"}) |
| 13 | compare-elements-property: (".sub", "#help", ["offsetWidth"]) | |
| 14 | compare-elements-position: (".sub", "#help", ["x"]) | |
| 13 | compare-elements-property: (".main-heading", "#help", ["offsetWidth"]) | |
| 14 | compare-elements-position: (".main-heading", "#help", ["x"]) | |
| 15 | 15 | |
| 16 | 16 | // Checking the color of the elements of the help menu. |
| 17 | 17 | show-text: true |
| ... | ... | @@ -54,19 +54,17 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a" |
| 54 | 54 | wait-for: "#search-tabs" // Waiting for the search.js to load. |
| 55 | 55 | set-window-size: (1000, 1000) // Only supported on desktop. |
| 56 | 56 | assert-false: "#help" |
| 57 | click: "#help-button > a" | |
| 57 | click: "rustdoc-toolbar .help-menu > a" | |
| 58 | 58 | assert-css: ("#help", {"display": "block"}) |
| 59 | 59 | assert-css: ("#help dd", {"font-size": "16px"}) |
| 60 | click: "#help-button > a" | |
| 61 | assert-css: ("#help", {"display": "none"}) | |
| 62 | compare-elements-property-false: (".sub", "#help", ["offsetWidth"]) | |
| 63 | compare-elements-position-false: (".sub", "#help", ["x"]) | |
| 60 | click: "rustdoc-toolbar .help-menu > a" | |
| 61 | assert-false: "#help" | |
| 64 | 62 | |
| 65 | 63 | // This test ensures that the "the rustdoc book" anchor link within the help popover works. |
| 66 | 64 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a" |
| 67 | 65 | wait-for: "#search-tabs" // Waiting for the search.js to load. |
| 68 | 66 | set-window-size: (1000, 1000) // Popover only appears when the screen width is >700px. |
| 69 | 67 | assert-false: "#help" |
| 70 | click: "#help-button > a" | |
| 68 | click: "rustdoc-toolbar .help-menu > a" | |
| 71 | 69 | click: "//*[@id='help']//a[text()='the rustdoc book']" |
| 72 | 70 | wait-for-document-property: ({"URL": "https://doc.rust-lang.org/"}, STARTS_WITH) |
tests/rustdoc-gui/hide-mobile-topbar.goml+4-5| ... | ... | @@ -1,20 +1,19 @@ |
| 1 | 1 | // Checks sidebar resizing stays synced with the setting |
| 2 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" | |
| 2 | go-to: "file://" + |DOC_PATH| + "/settings.html" | |
| 3 | 3 | set-window-size: (400, 600) |
| 4 | 4 | |
| 5 | 5 | // Verify that the "hide" option is unchecked |
| 6 | click: "#settings-menu" | |
| 7 | 6 | wait-for: "#settings" |
| 8 | 7 | assert-css: ("#settings", {"display": "block"}) |
| 9 | 8 | assert-property: ("#hide-sidebar", {"checked": "false"}) |
| 10 | assert-css: (".mobile-topbar", {"display": "flex"}) | |
| 9 | assert-css: ("rustdoc-topbar", {"display": "flex"}) | |
| 11 | 10 | |
| 12 | 11 | // Toggle it |
| 13 | 12 | click: "#hide-sidebar" |
| 14 | 13 | assert-property: ("#hide-sidebar", {"checked": "true"}) |
| 15 | assert-css: (".mobile-topbar", {"display": "none"}) | |
| 14 | assert-css: ("rustdoc-topbar", {"display": "none"}) | |
| 16 | 15 | |
| 17 | 16 | // Toggle it again |
| 18 | 17 | click: "#hide-sidebar" |
| 19 | 18 | assert-property: ("#hide-sidebar", {"checked": "false"}) |
| 20 | assert-css: (".mobile-topbar", {"display": "flex"}) | |
| 19 | assert-css: ("rustdoc-topbar", {"display": "flex"}) |
tests/rustdoc-gui/huge-logo.goml-5| ... | ... | @@ -8,8 +8,3 @@ assert-property: (".sidebar-crate .logo-container", {"offsetWidth": "96", "offse |
| 8 | 8 | // offsetWidth = width of sidebar, offsetHeight = height + top padding |
| 9 | 9 | assert-property: (".sidebar-crate .logo-container img", {"offsetWidth": "48", "offsetHeight": 64}) |
| 10 | 10 | assert-css: (".sidebar-crate .logo-container img", {"border-top-width": "16px", "margin-top": "-16px"}) |
| 11 | ||
| 12 | set-window-size: (400, 600) | |
| 13 | // offset = size + margin | |
| 14 | assert-property: (".mobile-topbar .logo-container", {"offsetWidth": "55", "offsetHeight": 45}) | |
| 15 | assert-property: (".mobile-topbar .logo-container img", {"offsetWidth": "35", "offsetHeight": 35}) |
tests/rustdoc-gui/item-info.goml+1-1| ... | ... | @@ -20,7 +20,7 @@ store-position: ( |
| 20 | 20 | {"x": second_line_x, "y": second_line_y}, |
| 21 | 21 | ) |
| 22 | 22 | assert: |first_line_x| != |second_line_x| && |first_line_x| == 521 && |second_line_x| == 277 |
| 23 | assert: |first_line_y| != |second_line_y| && |first_line_y| == 718 && |second_line_y| == 741 | |
| 23 | assert: |first_line_y| != |second_line_y| && |first_line_y| == 676 && |second_line_y| == 699 | |
| 24 | 24 | |
| 25 | 25 | // Now we ensure that they're not rendered on the same line. |
| 26 | 26 | set-window-size: (1100, 800) |
tests/rustdoc-gui/mobile-crate-name.goml+7-7| ... | ... | @@ -5,18 +5,18 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 5 | 5 | // First we change the title to make it big. |
| 6 | 6 | set-window-size: (350, 800) |
| 7 | 7 | // We ensure that the "format" of the title is the same as the one we'll use. |
| 8 | assert-text: (".mobile-topbar .location a", "test_docs") | |
| 8 | assert-text: ("rustdoc-topbar h2 a", "Crate test_docs") | |
| 9 | 9 | // We store the height we know is correct. |
| 10 | store-property: (".mobile-topbar .location", {"offsetHeight": height}) | |
| 10 | store-property: ("rustdoc-topbar h2", {"offsetHeight": height}) | |
| 11 | 11 | // We change the crate name to something longer. |
| 12 | set-text: (".mobile-topbar .location a", "cargo_packager_resource_resolver") | |
| 12 | set-text: ("rustdoc-topbar h2 a", "cargo_packager_resource_resolver") | |
| 13 | 13 | // And we check that the size remained the same. |
| 14 | assert-property: (".mobile-topbar .location", {"offsetHeight": |height|}) | |
| 14 | assert-property: ("rustdoc-topbar h2", {"offsetHeight": |height|}) | |
| 15 | 15 | |
| 16 | 16 | // Now we check if it works for the non-crate pages as well. |
| 17 | 17 | go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" |
| 18 | 18 | // We store the height we know is correct. |
| 19 | store-property: (".mobile-topbar .location", {"offsetHeight": height}) | |
| 20 | set-text: (".mobile-topbar .location a", "Something_incredibly_long_because") | |
| 19 | store-property: ("rustdoc-topbar h2", {"offsetHeight": height}) | |
| 20 | set-text: ("rustdoc-topbar h2 a", "Something_incredibly_long_because") | |
| 21 | 21 | // And we check that the size remained the same. |
| 22 | assert-property: (".mobile-topbar .location", {"offsetHeight": |height|}) | |
| 22 | assert-property: ("rustdoc-topbar h2", {"offsetHeight": |height|}) |
tests/rustdoc-gui/mobile.goml+1-1| ... | ... | @@ -5,7 +5,7 @@ set-window-size: (400, 600) |
| 5 | 5 | set-font-size: 18 |
| 6 | 6 | wait-for: 100 // wait a bit for the resize and the font-size change to be fully taken into account. |
| 7 | 7 | |
| 8 | assert-property: (".mobile-topbar h2", {"offsetHeight": 33}) | |
| 8 | assert-property: ("rustdoc-topbar h2", {"offsetHeight": 33}) | |
| 9 | 9 | |
| 10 | 10 | // On the settings page, the theme buttons should not line-wrap. Instead, they should |
| 11 | 11 | // all be placed as a group on a line below the setting name "Theme." |
tests/rustdoc-gui/notable-trait.goml+13-13| ... | ... | @@ -82,15 +82,6 @@ call-function: ("check-notable-tooltip-position", { |
| 82 | 82 | "i_x": 528, |
| 83 | 83 | }) |
| 84 | 84 | |
| 85 | // Checking on mobile now. | |
| 86 | set-window-size: (650, 600) | |
| 87 | wait-for-size: ("body", {"width": 650}) | |
| 88 | call-function: ("check-notable-tooltip-position-complete", { | |
| 89 | "x": 26, | |
| 90 | "i_x": 305, | |
| 91 | "popover_x": 0, | |
| 92 | }) | |
| 93 | ||
| 94 | 85 | // Now check the colors. |
| 95 | 86 | define-function: ( |
| 96 | 87 | "check-colors", |
| ... | ... | @@ -176,6 +167,15 @@ call-function: ( |
| 176 | 167 | }, |
| 177 | 168 | ) |
| 178 | 169 | |
| 170 | // Checking on mobile now. | |
| 171 | set-window-size: (650, 600) | |
| 172 | wait-for-size: ("body", {"width": 650}) | |
| 173 | call-function: ("check-notable-tooltip-position-complete", { | |
| 174 | "x": 26, | |
| 175 | "i_x": 305, | |
| 176 | "popover_x": 0, | |
| 177 | }) | |
| 178 | ||
| 179 | 179 | reload: |
| 180 | 180 | |
| 181 | 181 | // Check that pressing escape works |
| ... | ... | @@ -189,7 +189,7 @@ assert: "#method\.create_an_iterator_from_read .tooltip:focus" |
| 189 | 189 | // Check that clicking outside works. |
| 190 | 190 | click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" |
| 191 | 191 | assert-count: ("//*[@class='tooltip popover']", 1) |
| 192 | click: ".search-input" | |
| 192 | click: ".main-heading h1" | |
| 193 | 193 | assert-count: ("//*[@class='tooltip popover']", 0) |
| 194 | 194 | assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" |
| 195 | 195 | |
| ... | ... | @@ -219,14 +219,14 @@ define-function: ( |
| 219 | 219 | store-window-property: {"scrollY": scroll} |
| 220 | 220 | click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" |
| 221 | 221 | wait-for: "//*[@class='tooltip popover']" |
| 222 | click: "#settings-menu a" | |
| 222 | click: ".main-heading h1" | |
| 223 | 223 | } |
| 224 | 224 | ) |
| 225 | 225 | |
| 226 | 226 | // Now we check that the focus isn't given back to the wrong item when opening |
| 227 | 227 | // another popover. |
| 228 | 228 | call-function: ("setup-popup", {}) |
| 229 | click: ".search-input" | |
| 229 | click: ".main-heading h1" | |
| 230 | 230 | // We ensure we didn't come back to the previous focused item. |
| 231 | 231 | assert-window-property-false: {"scrollY": |scroll|} |
| 232 | 232 | |
| ... | ... | @@ -251,7 +251,7 @@ reload: |
| 251 | 251 | assert-count: ("//*[@class='tooltip popover']", 0) |
| 252 | 252 | click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" |
| 253 | 253 | assert-count: ("//*[@class='tooltip popover']", 1) |
| 254 | click: "#settings-menu a" | |
| 254 | click: "rustdoc-toolbar .settings-menu a" | |
| 255 | 255 | wait-for: "#settings" |
| 256 | 256 | assert-count: ("//*[@class='tooltip popover']", 0) |
| 257 | 257 | assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" |
tests/rustdoc-gui/pocket-menu.goml+44-23| ... | ... | @@ -3,33 +3,33 @@ include: "utils.goml" |
| 3 | 3 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test" |
| 4 | 4 | wait-for: "#crate-search" |
| 5 | 5 | // First we check that the help menu doesn't exist yet. |
| 6 | assert-false: "#help-button .popover" | |
| 6 | assert-false: "rustdoc-toolbar .help-menu .popover" | |
| 7 | 7 | // Then we display the help menu. |
| 8 | click: "#help-button" | |
| 9 | assert: "#help-button .popover" | |
| 10 | assert-css: ("#help-button .popover", {"display": "block"}) | |
| 8 | click: "rustdoc-toolbar .help-menu" | |
| 9 | assert: "rustdoc-toolbar .help-menu .popover" | |
| 10 | assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"}) | |
| 11 | 11 | |
| 12 | 12 | // Now we click somewhere else on the page to ensure it is handling the blur event |
| 13 | 13 | // correctly. |
| 14 | 14 | click: ".sidebar" |
| 15 | assert-css: ("#help-button .popover", {"display": "none"}) | |
| 15 | assert-false: "rustdoc-toolbar .help-menu .popover" | |
| 16 | 16 | |
| 17 | 17 | // Now we will check that we cannot have two "pocket menus" displayed at the same time. |
| 18 | click: "#help-button" | |
| 19 | assert-css: ("#help-button .popover", {"display": "block"}) | |
| 20 | click: "#settings-menu" | |
| 21 | assert-css: ("#help-button .popover", {"display": "none"}) | |
| 22 | assert-css: ("#settings-menu .popover", {"display": "block"}) | |
| 18 | click: "rustdoc-toolbar .help-menu" | |
| 19 | assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"}) | |
| 20 | click: "rustdoc-toolbar .settings-menu" | |
| 21 | assert-false: "rustdoc-toolbar .help-menu .popover" | |
| 22 | assert-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"}) | |
| 23 | 23 | |
| 24 | 24 | // Now the other way. |
| 25 | click: "#help-button" | |
| 26 | assert-css: ("#help-button .popover", {"display": "block"}) | |
| 27 | assert-css: ("#settings-menu .popover", {"display": "none"}) | |
| 25 | click: "rustdoc-toolbar .help-menu" | |
| 26 | assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"}) | |
| 27 | assert-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"}) | |
| 28 | 28 | |
| 29 | 29 | // Now verify that clicking the help menu again closes it. |
| 30 | click: "#help-button" | |
| 31 | assert-css: ("#help-button .popover", {"display": "none"}) | |
| 32 | assert-css: ("#settings-menu .popover", {"display": "none"}) | |
| 30 | click: "rustdoc-toolbar .help-menu" | |
| 31 | assert-false: "rustdoc-toolbar .help-menu .popover" | |
| 32 | assert-css: (".settings-menu .popover", {"display": "none"}) | |
| 33 | 33 | |
| 34 | 34 | define-function: ( |
| 35 | 35 | "check-popover-colors", |
| ... | ... | @@ -37,13 +37,21 @@ define-function: ( |
| 37 | 37 | block { |
| 38 | 38 | call-function: ("switch-theme", {"theme": |theme|}) |
| 39 | 39 | |
| 40 | click: "#help-button" | |
| 40 | click: "rustdoc-toolbar .help-menu" | |
| 41 | 41 | assert-css: ( |
| 42 | "#help-button .popover", | |
| 42 | "rustdoc-toolbar .help-menu .popover", | |
| 43 | 43 | {"display": "block", "border-color": |border_color|}, |
| 44 | 44 | ) |
| 45 | compare-elements-css: ("#help-button .popover", "#help-button .top", ["border-color"]) | |
| 46 | compare-elements-css: ("#help-button .popover", "#help-button .bottom", ["border-color"]) | |
| 45 | compare-elements-css: ( | |
| 46 | "rustdoc-toolbar .help-menu .popover", | |
| 47 | "rustdoc-toolbar .help-menu .top", | |
| 48 | ["border-color"], | |
| 49 | ) | |
| 50 | compare-elements-css: ( | |
| 51 | "rustdoc-toolbar .help-menu .popover", | |
| 52 | "rustdoc-toolbar .help-menu .bottom", | |
| 53 | ["border-color"], | |
| 54 | ) | |
| 47 | 55 | } |
| 48 | 56 | ) |
| 49 | 57 | |
| ... | ... | @@ -63,8 +71,21 @@ call-function: ("check-popover-colors", { |
| 63 | 71 | |
| 64 | 72 | // Opening the mobile sidebar should close the settings popover. |
| 65 | 73 | set-window-size: (650, 600) |
| 66 | click: "#settings-menu a" | |
| 67 | assert-css: ("#settings-menu .popover", {"display": "block"}) | |
| 74 | click: "rustdoc-topbar .settings-menu a" | |
| 75 | assert-css: ("rustdoc-topbar .settings-menu .popover", {"display": "block"}) | |
| 76 | click: ".sidebar-menu-toggle" | |
| 77 | assert: "//*[@class='sidebar shown']" | |
| 78 | assert-css: ("rustdoc-topbar .settings-menu .popover", {"display": "none"}) | |
| 79 | // Opening the settings popover should close the sidebar. | |
| 80 | click: ".settings-menu a" | |
| 81 | assert-css: ("rustdoc-topbar .settings-menu .popover", {"display": "block"}) | |
| 82 | assert-false: "//*[@class='sidebar shown']" | |
| 83 | ||
| 84 | // Opening the settings popover at start (which async loads stuff) should also close. | |
| 85 | reload: | |
| 68 | 86 | click: ".sidebar-menu-toggle" |
| 69 | 87 | assert: "//*[@class='sidebar shown']" |
| 70 | assert-css: ("#settings-menu .popover", {"display": "none"}) | |
| 88 | assert-false: "rustdoc-topbar .settings-menu .popover" | |
| 89 | click: "rustdoc-topbar .settings-menu a" | |
| 90 | assert-false: "//*[@class='sidebar shown']" | |
| 91 | wait-for: "rustdoc-topbar .settings-menu .popover" |
tests/rustdoc-gui/scrape-examples-color.goml+1-1| ... | ... | @@ -27,7 +27,7 @@ define-function: ( |
| 27 | 27 | "color": |help_hover_color|, |
| 28 | 28 | }) |
| 29 | 29 | // Moving the cursor to another item to not break next runs. |
| 30 | move-cursor-to: ".search-input" | |
| 30 | move-cursor-to: "#search-button" | |
| 31 | 31 | } |
| 32 | 32 | ) |
| 33 | 33 |
tests/rustdoc-gui/scrape-examples-layout.goml+4-4| ... | ... | @@ -64,8 +64,8 @@ assert-size: (".more-scraped-examples .scraped-example .example-wrap", { |
| 64 | 64 | store-value: (offset_y, 4) |
| 65 | 65 | |
| 66 | 66 | // First with desktop |
| 67 | assert-position: (".scraped-example", {"y": 256}) | |
| 68 | assert-position: (".scraped-example .prev", {"y": 256 + |offset_y|}) | |
| 67 | assert-position: (".scraped-example", {"y": 214}) | |
| 68 | assert-position: (".scraped-example .prev", {"y": 214 + |offset_y|}) | |
| 69 | 69 | |
| 70 | 70 | // Gradient background should be at the top of the code block. |
| 71 | 71 | assert-css: (".scraped-example .example-wrap::before", {"top": "0px"}) |
| ... | ... | @@ -74,8 +74,8 @@ assert-css: (".scraped-example .example-wrap::after", {"bottom": "0px"}) |
| 74 | 74 | // Then with mobile |
| 75 | 75 | set-window-size: (600, 600) |
| 76 | 76 | store-size: (".scraped-example .scraped-example-title", {"height": title_height}) |
| 77 | assert-position: (".scraped-example", {"y": 291}) | |
| 78 | assert-position: (".scraped-example .prev", {"y": 291 + |offset_y| + |title_height|}) | |
| 77 | assert-position: (".scraped-example", {"y": 249}) | |
| 78 | assert-position: (".scraped-example .prev", {"y": 249 + |offset_y| + |title_height|}) | |
| 79 | 79 | |
| 80 | 80 | define-function: ( |
| 81 | 81 | "check_title_and_code_position", |
tests/rustdoc-gui/scrape-examples-toggle.goml+1-1| ... | ... | @@ -25,7 +25,7 @@ define-function: ( |
| 25 | 25 | // We put the toggle in the original state. |
| 26 | 26 | click: ".more-examples-toggle" |
| 27 | 27 | // Moving cursor away from the toggle line to prevent disrupting next test. |
| 28 | move-cursor-to: ".search-input" | |
| 28 | move-cursor-to: "rustdoc-toolbar #search-button" | |
| 29 | 29 | }, |
| 30 | 30 | ) |
| 31 | 31 |
tests/rustdoc-gui/search-about-this-result.goml+2| ... | ... | @@ -7,6 +7,7 @@ focus: ".search-input" |
| 7 | 7 | press-key: "Enter" |
| 8 | 8 | |
| 9 | 9 | wait-for: "#search-tabs" |
| 10 | wait-for-false: "#search-tabs .count.loading" | |
| 10 | 11 | assert-count: ("#search-tabs button", 1) |
| 11 | 12 | assert-count: (".search-results > a", 1) |
| 12 | 13 | |
| ... | ... | @@ -32,6 +33,7 @@ focus: ".search-input" |
| 32 | 33 | press-key: "Enter" |
| 33 | 34 | |
| 34 | 35 | wait-for: "#search-tabs" |
| 36 | wait-for-false: "#search-tabs .count.loading" | |
| 35 | 37 | assert-text: ("//div[@class='type-signature']", "F -> WhereWhitespace<T>") |
| 36 | 38 | assert-count: ("#search-tabs button", 1) |
| 37 | 39 | assert-count: (".search-results > a", 1) |
tests/rustdoc-gui/search-corrections.goml+17-58| ... | ... | @@ -1,101 +1,60 @@ |
| 1 | 1 | // ignore-tidy-linelength |
| 2 | include: "utils.goml" | |
| 2 | 3 | |
| 3 | 4 | // Checks that the search tab result tell the user about corrections |
| 4 | 5 | // First, try a search-by-name |
| 5 | 6 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 6 | // Intentionally wrong spelling of "NotableStructWithLongName" | |
| 7 | write-into: (".search-input", "NotableStructWithLongNamr") | |
| 8 | // To be SURE that the search will be run. | |
| 9 | press-key: 'Enter' | |
| 10 | // Waiting for the search results to appear... | |
| 11 | wait-for: "#search-tabs" | |
| 7 | call-function: ("perform-search", {"query": "NotableStructWithLongNamr"}) | |
| 12 | 8 | |
| 13 | 9 | // Corrections aren't shown on the "In Names" tab. |
| 14 | 10 | assert: "#search-tabs button.selected:first-child" |
| 15 | assert-css: (".search-corrections", { | |
| 16 | "display": "none" | |
| 17 | }) | |
| 11 | assert-false: ".search-results:nth-child(1) .search-corrections" | |
| 18 | 12 | |
| 19 | 13 | // Corrections do get shown on the "In Parameters" tab. |
| 20 | 14 | click: "#search-tabs button:nth-child(2)" |
| 21 | 15 | assert: "#search-tabs button.selected:nth-child(2)" |
| 22 | assert-css: (".search-corrections", { | |
| 23 | "display": "block" | |
| 24 | }) | |
| 25 | 16 | assert-text: ( |
| 26 | ".search-corrections", | |
| 27 | "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." | |
| 17 | ".search-results:nth-child(2) .search-corrections", | |
| 18 | "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"NotableStructWithLongName\" instead." | |
| 28 | 19 | ) |
| 29 | 20 | |
| 30 | 21 | // Corrections do get shown on the "In Return Type" tab. |
| 31 | 22 | click: "#search-tabs button:nth-child(3)" |
| 32 | 23 | assert: "#search-tabs button.selected:nth-child(3)" |
| 33 | assert-css: (".search-corrections", { | |
| 34 | "display": "block" | |
| 35 | }) | |
| 36 | 24 | assert-text: ( |
| 37 | ".search-corrections", | |
| 38 | "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." | |
| 25 | ".search-results:nth-child(3) .search-corrections", | |
| 26 | "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"NotableStructWithLongName\" instead." | |
| 39 | 27 | ) |
| 40 | 28 | |
| 41 | 29 | // Now, explicit return values |
| 42 | 30 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 43 | // Intentionally wrong spelling of "NotableStructWithLongName" | |
| 44 | write-into: (".search-input", "-> NotableStructWithLongNamr") | |
| 45 | // To be SURE that the search will be run. | |
| 46 | press-key: 'Enter' | |
| 47 | // Waiting for the search results to appear... | |
| 48 | wait-for: "#search-tabs" | |
| 31 | call-function: ("perform-search", {"query": "-> NotableStructWithLongNamr"}) | |
| 49 | 32 | |
| 50 | assert-css: (".search-corrections", { | |
| 51 | "display": "block" | |
| 52 | }) | |
| 53 | 33 | assert-text: ( |
| 54 | ".search-corrections", | |
| 55 | "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." | |
| 34 | ".search-results.active .search-corrections", | |
| 35 | "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"NotableStructWithLongName\" instead." | |
| 56 | 36 | ) |
| 57 | 37 | |
| 58 | 38 | // Now, generic correction |
| 59 | 39 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 60 | // Intentionally wrong spelling of "NotableStructWithLongName" | |
| 61 | write-into: (".search-input", "NotableStructWithLongNamr, NotableStructWithLongNamr") | |
| 62 | // To be SURE that the search will be run. | |
| 63 | press-key: 'Enter' | |
| 64 | // Waiting for the search results to appear... | |
| 65 | wait-for: "#search-tabs" | |
| 40 | call-function: ("perform-search", {"query": "NotableStructWithLongNamr, NotableStructWithLongNamr"}) | |
| 66 | 41 | |
| 67 | assert-css: (".search-corrections", { | |
| 68 | "display": "block" | |
| 69 | }) | |
| 70 | 42 | assert-text: ( |
| 71 | ".search-corrections", | |
| 72 | "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead." | |
| 43 | ".search-failed.active .search-corrections", | |
| 44 | "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"NotableStructWithLongName\" instead." | |
| 73 | 45 | ) |
| 74 | 46 | |
| 75 | 47 | // Now, generic correction plus error |
| 76 | 48 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 77 | // Intentionally wrong spelling of "NotableStructWithLongName" | |
| 78 | write-into: (".search-input", "Foo<NotableStructWithLongNamr>,y") | |
| 79 | // To be SURE that the search will be run. | |
| 80 | press-key: 'Enter' | |
| 81 | // Waiting for the search results to appear... | |
| 82 | wait-for: "#search-tabs" | |
| 49 | call-function: ("perform-search", {"query": "Foo<NotableStructWithLongNamr>,y"}) | |
| 83 | 50 | |
| 84 | assert-css: (".search-corrections", { | |
| 85 | "display": "block" | |
| 86 | }) | |
| 87 | 51 | assert-text: ( |
| 88 | ".search-corrections", | |
| 89 | "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead." | |
| 52 | ".search-failed.active .search-corrections", | |
| 53 | "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"NotableStructWithLongName\" instead." | |
| 90 | 54 | ) |
| 91 | 55 | |
| 92 | 56 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 93 | // Intentionally wrong spelling of "NotableStructWithLongName" | |
| 94 | write-into: (".search-input", "generic:NotableStructWithLongNamr<x>,y") | |
| 95 | // To be SURE that the search will be run. | |
| 96 | press-key: 'Enter' | |
| 97 | // Waiting for the search results to appear... | |
| 98 | wait-for: "#search-tabs" | |
| 57 | call-function: ("perform-search", {"query": "generic:NotableStructWithLongNamr<x>,y"}) | |
| 99 | 58 | |
| 100 | 59 | assert-css: (".error", { |
| 101 | 60 | "display": "block" |
tests/rustdoc-gui/search-error.goml+1| ... | ... | @@ -8,6 +8,7 @@ define-function: ( |
| 8 | 8 | [theme, error_background], |
| 9 | 9 | block { |
| 10 | 10 | call-function: ("switch-theme", {"theme": |theme|}) |
| 11 | wait-for-false: "#search-tabs .count.loading" | |
| 11 | 12 | wait-for: "#search .error code" |
| 12 | 13 | assert-css: ("#search .error code", {"background-color": |error_background|}) |
| 13 | 14 | } |
tests/rustdoc-gui/search-filter.goml+7-9| ... | ... | @@ -2,11 +2,7 @@ |
| 2 | 2 | include: "utils.goml" |
| 3 | 3 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 4 | 4 | show-text: true |
| 5 | write-into: (".search-input", "test") | |
| 6 | // To be SURE that the search will be run. | |
| 7 | press-key: 'Enter' | |
| 8 | // Waiting for the search results to appear... | |
| 9 | wait-for: "#search-tabs" | |
| 5 | call-function: ("perform-search", {"query": "test"}) | |
| 10 | 6 | assert-text: ("#results .externcrate", "test_docs") |
| 11 | 7 | |
| 12 | 8 | wait-for: "#crate-search" |
| ... | ... | @@ -21,6 +17,7 @@ press-key: "ArrowDown" |
| 21 | 17 | press-key: "Enter" |
| 22 | 18 | // Waiting for the search results to appear... |
| 23 | 19 | wait-for: "#search-tabs" |
| 20 | wait-for-false: "#search-tabs .count.loading" | |
| 24 | 21 | assert-document-property: ({"URL": "&filter-crate="}, CONTAINS) |
| 25 | 22 | // We check that there is no more "test_docs" appearing. |
| 26 | 23 | assert-false: "#results .externcrate" |
| ... | ... | @@ -31,7 +28,8 @@ assert-property: ("#crate-search", {"value": "lib2"}) |
| 31 | 28 | // crate filtering. |
| 32 | 29 | press-key: "Escape" |
| 33 | 30 | wait-for-css: ("#main-content", {"display": "block"}) |
| 34 | focus: ".search-input" | |
| 31 | click: "#search-button" | |
| 32 | wait-for: ".search-input" | |
| 35 | 33 | wait-for-css: ("#main-content", {"display": "none"}) |
| 36 | 34 | // We check that there is no more "test_docs" appearing. |
| 37 | 35 | assert-false: "#results .externcrate" |
| ... | ... | @@ -47,6 +45,7 @@ press-key: "ArrowUp" |
| 47 | 45 | press-key: "Enter" |
| 48 | 46 | // Waiting for the search results to appear... |
| 49 | 47 | wait-for: "#search-tabs" |
| 48 | wait-for-false: "#search-tabs .count.loading" | |
| 50 | 49 | assert-property: ("#crate-search", {"value": "all crates"}) |
| 51 | 50 | |
| 52 | 51 | // Checking that the URL parameter is taken into account for crate filtering. |
| ... | ... | @@ -56,8 +55,7 @@ assert-property: ("#crate-search", {"value": "lib2"}) |
| 56 | 55 | assert-false: "#results .externcrate" |
| 57 | 56 | |
| 58 | 57 | // Checking that the text for the "title" is correct (the "all crates" comes from the "<select>"). |
| 59 | assert-text: (".search-results-title", "Results", STARTS_WITH) | |
| 60 | assert-text: (".search-results-title + .sub-heading", " in all crates", STARTS_WITH) | |
| 58 | assert-text: (".search-switcher", "Search results in all crates", STARTS_WITH) | |
| 61 | 59 | |
| 62 | 60 | // Checking the display of the crate filter. |
| 63 | 61 | // We start with the light theme. |
| ... | ... | @@ -72,7 +70,7 @@ assert-css: ("#crate-search", { |
| 72 | 70 | }) |
| 73 | 71 | |
| 74 | 72 | // We now check the dark theme. |
| 75 | click: "#settings-menu" | |
| 73 | click: "rustdoc-toolbar .settings-menu" | |
| 76 | 74 | wait-for: "#settings" |
| 77 | 75 | click: "#theme-dark" |
| 78 | 76 | wait-for-css: ("#crate-search", { |
tests/rustdoc-gui/search-form-elements.goml+11-8| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | include: "utils.goml" |
| 3 | 3 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test" |
| 4 | 4 | wait-for: "#search-tabs" // Waiting for the search.js to load. |
| 5 | wait-for-false: "#search-tabs .count.loading" | |
| 5 | 6 | show-text: true |
| 6 | 7 | |
| 7 | 8 | define-function: ( |
| ... | ... | @@ -31,7 +32,7 @@ define-function: ( |
| 31 | 32 | }, |
| 32 | 33 | ) |
| 33 | 34 | assert-css: ( |
| 34 | "#help-button > a", | |
| 35 | "rustdoc-toolbar .help-menu > a", | |
| 35 | 36 | { |
| 36 | 37 | "color": |menu_button_a_color|, |
| 37 | 38 | "border-color": "transparent", |
| ... | ... | @@ -39,9 +40,9 @@ define-function: ( |
| 39 | 40 | }, |
| 40 | 41 | ) |
| 41 | 42 | // Hover help button. |
| 42 | move-cursor-to: "#help-button" | |
| 43 | move-cursor-to: "rustdoc-toolbar .help-menu" | |
| 43 | 44 | assert-css: ( |
| 44 | "#help-button > a", | |
| 45 | "rustdoc-toolbar .help-menu > a", | |
| 45 | 46 | { |
| 46 | 47 | "color": |menu_button_a_color|, |
| 47 | 48 | "border-color": |menu_button_a_border_hover|, |
| ... | ... | @@ -49,15 +50,15 @@ define-function: ( |
| 49 | 50 | }, |
| 50 | 51 | ) |
| 51 | 52 | // Link color inside |
| 52 | click: "#help-button" | |
| 53 | click: "rustdoc-toolbar .help-menu" | |
| 53 | 54 | assert-css: ( |
| 54 | "#help a", | |
| 55 | "rustdoc-toolbar #help a", | |
| 55 | 56 | { |
| 56 | 57 | "color": |menu_a_color|, |
| 57 | 58 | }, |
| 58 | 59 | ) |
| 59 | 60 | assert-css: ( |
| 60 | "#settings-menu > a", | |
| 61 | "rustdoc-toolbar .settings-menu > a", | |
| 61 | 62 | { |
| 62 | 63 | "color": |menu_button_a_color|, |
| 63 | 64 | "border-color": "transparent", |
| ... | ... | @@ -65,9 +66,9 @@ define-function: ( |
| 65 | 66 | }, |
| 66 | 67 | ) |
| 67 | 68 | // Hover settings menu. |
| 68 | move-cursor-to: "#settings-menu" | |
| 69 | move-cursor-to: "rustdoc-toolbar .settings-menu" | |
| 69 | 70 | assert-css: ( |
| 70 | "#settings-menu:hover > a", | |
| 71 | "rustdoc-toolbar .settings-menu:hover > a", | |
| 71 | 72 | { |
| 72 | 73 | "color": |menu_button_a_color|, |
| 73 | 74 | "border-color": |menu_button_a_border_hover|, |
| ... | ... | @@ -120,8 +121,10 @@ call-function: ( |
| 120 | 121 | // Check that search input correctly decodes form encoding. |
| 121 | 122 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a+b" |
| 122 | 123 | wait-for: "#search-tabs" // Waiting for the search.js to load. |
| 124 | wait-for-false: "#search-tabs .count.loading" | |
| 123 | 125 | assert-property: (".search-input", { "value": "a b" }) |
| 124 | 126 | // Check that literal + is not treated as space. |
| 125 | 127 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a%2Bb" |
| 126 | 128 | wait-for: "#search-tabs" // Waiting for the search.js to load. |
| 129 | wait-for-false: "#search-tabs .count.loading" | |
| 127 | 130 | assert-property: (".search-input", { "value": "a+b" }) |
tests/rustdoc-gui/search-input-mobile.goml+8-5| ... | ... | @@ -2,10 +2,13 @@ |
| 2 | 2 | // The PR which fixed it is: https://github.com/rust-lang/rust/pull/81592 |
| 3 | 3 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 4 | 4 | set-window-size: (463, 700) |
| 5 | // We first check that the search input isn't already focused. | |
| 6 | assert-false: ("input.search-input:focus") | |
| 7 | click: "input.search-input" | |
| 5 | click: "#search-button" | |
| 6 | wait-for: ".search-input" | |
| 7 | assert: "input.search-input:focus" | |
| 8 | ||
| 9 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" | |
| 8 | 10 | reload: |
| 9 | 11 | set-window-size: (750, 700) |
| 10 | click: "input.search-input" | |
| 11 | assert: ("input.search-input:focus") | |
| 12 | click: "#search-button" | |
| 13 | wait-for: ".search-input" | |
| 14 | assert: "input.search-input:focus" |
tests/rustdoc-gui/search-keyboard.goml+9-12| ... | ... | @@ -1,28 +1,25 @@ |
| 1 | 1 | // Checks that the search tab results work correctly with function signature syntax |
| 2 | 2 | // First, try a search-by-name |
| 3 | include: "utils.goml" | |
| 3 | 4 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 4 | write-into: (".search-input", "Foo") | |
| 5 | // To be SURE that the search will be run. | |
| 6 | press-key: 'Enter' | |
| 7 | // Waiting for the search results to appear... | |
| 8 | wait-for: "#search-tabs" | |
| 5 | call-function: ("perform-search", {"query": "Foo"}) | |
| 9 | 6 | |
| 10 | 7 | // Now use the keyboard commands to switch to the third result. |
| 11 | 8 | press-key: "ArrowDown" |
| 12 | 9 | press-key: "ArrowDown" |
| 13 | 10 | press-key: "ArrowDown" |
| 14 | assert: ".search-results.active > a:focus:nth-of-type(3)" | |
| 11 | wait-for: ".search-results.active > a:focus:nth-of-type(3)" | |
| 15 | 12 | |
| 16 | 13 | // Now switch to the second tab, then back to the first one, then arrow back up. |
| 17 | 14 | press-key: "ArrowRight" |
| 18 | assert: ".search-results.active:nth-of-type(2) > a:focus:nth-of-type(1)" | |
| 15 | wait-for: ".search-results.active:nth-of-type(2) > a:focus:nth-of-type(1)" | |
| 19 | 16 | press-key: "ArrowLeft" |
| 20 | assert: ".search-results.active:nth-of-type(1) > a:focus:nth-of-type(3)" | |
| 17 | wait-for: ".search-results.active:nth-of-type(1) > a:focus:nth-of-type(3)" | |
| 21 | 18 | press-key: "ArrowUp" |
| 22 | assert: ".search-results.active > a:focus:nth-of-type(2)" | |
| 19 | wait-for: ".search-results.active > a:focus:nth-of-type(2)" | |
| 23 | 20 | press-key: "ArrowUp" |
| 24 | assert: ".search-results.active > a:focus:nth-of-type(1)" | |
| 21 | wait-for: ".search-results.active > a:focus:nth-of-type(1)" | |
| 25 | 22 | press-key: "ArrowUp" |
| 26 | assert: ".search-input:focus" | |
| 23 | wait-for: ".search-input:focus" | |
| 27 | 24 | press-key: "ArrowDown" |
| 28 | assert: ".search-results.active > a:focus:nth-of-type(1)" | |
| 25 | wait-for: ".search-results.active > a:focus:nth-of-type(1)" |
tests/rustdoc-gui/search-reexport.goml+4-7| ... | ... | @@ -6,10 +6,8 @@ call-function: ("switch-theme", {"theme": "dark"}) |
| 6 | 6 | // First we check that the reexport has the correct ID and no background color. |
| 7 | 7 | assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;") |
| 8 | 8 | assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"}) |
| 9 | write-into: (".search-input", "TheStdReexport") | |
| 10 | // To be SURE that the search will be run. | |
| 11 | press-key: 'Enter' | |
| 12 | wait-for: "//a[@class='result-import']" | |
| 9 | call-function: ("perform-search", {"query": "TheStdReexport"}) | |
| 10 | assert: "//a[@class='result-import']" | |
| 13 | 11 | assert-attribute: ( |
| 14 | 12 | "//a[@class='result-import']", |
| 15 | 13 | {"href": "../test_docs/index.html#reexport.TheStdReexport"}, |
| ... | ... | @@ -21,9 +19,8 @@ wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "#494a |
| 21 | 19 | |
| 22 | 20 | // We now check that the alias is working as well on the reexport. |
| 23 | 21 | // To be SURE that the search will be run. |
| 24 | press-key: 'Enter' | |
| 25 | write-into: (".search-input", "AliasForTheStdReexport") | |
| 26 | wait-for: "//a[@class='result-import']" | |
| 22 | call-function: ("perform-search", {"query": "AliasForTheStdReexport"}) | |
| 23 | assert: "//a[@class='result-import']" | |
| 27 | 24 | assert-text: ( |
| 28 | 25 | "a.result-import .result-name", |
| 29 | 26 | "re-export AliasForTheStdReexport - see test_docs::TheStdReexport", |
tests/rustdoc-gui/search-result-color.goml+2-5| ... | ... | @@ -14,6 +14,7 @@ define-function: ( |
| 14 | 14 | |
| 15 | 15 | // Waiting for the search results to appear... |
| 16 | 16 | wait-for: "#search-tabs" |
| 17 | wait-for-false: "#search-tabs .count.loading" | |
| 17 | 18 | assert-css: ( |
| 18 | 19 | "#search-tabs > button > .count", |
| 19 | 20 | {"color": |count_color|}, |
| ... | ... | @@ -212,11 +213,7 @@ call-function: ("check-search-color", { |
| 212 | 213 | // Check the alias. |
| 213 | 214 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 214 | 215 | |
| 215 | write-into: (".search-input", "thisisanalias") | |
| 216 | // To be SURE that the search will be run. | |
| 217 | press-key: 'Enter' | |
| 218 | // Waiting for the search results to appear... | |
| 219 | wait-for: "#search-tabs" | |
| 216 | call-function: ("perform-search", {"query": "thisisanalias"}) | |
| 220 | 217 | |
| 221 | 218 | define-function: ( |
| 222 | 219 | "check-alias", |
tests/rustdoc-gui/search-result-description.goml+1| ... | ... | @@ -2,4 +2,5 @@ |
| 2 | 2 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=some_more_function" |
| 3 | 3 | // Waiting for the search results to appear... |
| 4 | 4 | wait-for: "#search-tabs" |
| 5 | wait-for-false: "#search-tabs .count.loading" | |
| 5 | 6 | assert-text: (".search-results .desc code", "format!") |
tests/rustdoc-gui/search-result-display.goml+6-3| ... | ... | @@ -7,6 +7,7 @@ write-into: (".search-input", "test") |
| 7 | 7 | // To be SURE that the search will be run. |
| 8 | 8 | press-key: 'Enter' |
| 9 | 9 | wait-for: "#crate-search" |
| 10 | wait-for-false: "#search-tabs .count.loading" | |
| 10 | 11 | // The width is returned by "getComputedStyle" which returns the exact number instead of the |
| 11 | 12 | // CSS rule which is "50%"... |
| 12 | 13 | assert-size: (".search-results div.desc", {"width": 248}) |
| ... | ... | @@ -34,6 +35,7 @@ assert: |new_width| < |width| - 10 |
| 34 | 35 | // Check that if the search is too long on mobile, it'll go under the "typename". |
| 35 | 36 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName" |
| 36 | 37 | wait-for: "#crate-search" |
| 38 | wait-for-false: "#search-tabs .count.loading" | |
| 37 | 39 | compare-elements-position-near: ( |
| 38 | 40 | ".search-results .result-name .typename", |
| 39 | 41 | ".search-results .result-name .path", |
| ... | ... | @@ -51,7 +53,7 @@ set-window-size: (900, 900) |
| 51 | 53 | |
| 52 | 54 | // First we check the current width, height and position. |
| 53 | 55 | assert-css: ("#crate-search", {"width": "159px"}) |
| 54 | store-size: (".search-results-title", { | |
| 56 | store-size: (".search-switcher", { | |
| 55 | 57 | "height": search_results_title_height, |
| 56 | 58 | "width": search_results_title_width, |
| 57 | 59 | }) |
| ... | ... | @@ -64,8 +66,8 @@ set-text: ( |
| 64 | 66 | ) |
| 65 | 67 | |
| 66 | 68 | // Then we compare again to confirm the height didn't change. |
| 67 | assert-size: ("#crate-search", {"width": 370}) | |
| 68 | assert-size: (".search-results-title", { | |
| 69 | assert-size: ("#crate-search", {"width": 185}) | |
| 70 | assert-size: (".search-switcher", { | |
| 69 | 71 | "height": |search_results_title_height|, |
| 70 | 72 | }) |
| 71 | 73 | assert-css: ("#search", {"width": "640px"}) |
| ... | ... | @@ -79,6 +81,7 @@ define-function: ( |
| 79 | 81 | block { |
| 80 | 82 | call-function: ("switch-theme", {"theme": |theme|}) |
| 81 | 83 | wait-for: "#crate-search" |
| 84 | wait-for-false: "#search-tabs .count.loading" | |
| 82 | 85 | assert-css: ("#crate-search", {"border": "1px solid " + |border|}) |
| 83 | 86 | assert-css: ("#crate-search-div::after", {"filter": |filter|}) |
| 84 | 87 | move-cursor-to: "#crate-search" |
tests/rustdoc-gui/search-result-go-to-first.goml+2-1| ... | ... | @@ -9,6 +9,7 @@ assert-text-false: (".main-heading h1", "Struct test_docs::FooCopy item path") |
| 9 | 9 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo" |
| 10 | 10 | // Waiting for the search results to appear... |
| 11 | 11 | wait-for: "#search-tabs" |
| 12 | wait-for-false: "#search-tabs .count.loading" | |
| 12 | 13 | assert-text-false: (".main-heading h1", "Struct test_docs::FooCopy item path") |
| 13 | 14 | // Ensure that the search results are displayed, not the "normal" content. |
| 14 | 15 | assert-css: ("#main-content", {"display": "none"}) |
| ... | ... | @@ -17,4 +18,4 @@ assert-css: ("#main-content", {"display": "none"}) |
| 17 | 18 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo&go_to_first=true" |
| 18 | 19 | // Waiting for the page to load... |
| 19 | 20 | wait-for-text: (".main-heading .rustdoc-breadcrumbs", "test_docs") |
| 20 | wait-for-text: (".main-heading h1", "Struct FooCopy item path") | |
| 21 | wait-for-text: (".main-heading h1", "Struct Foo Copy item path") |
tests/rustdoc-gui/search-result-impl-disambiguation.goml+5-10| ... | ... | @@ -1,15 +1,12 @@ |
| 1 | 1 | // ignore-tidy-linelength |
| 2 | include: "utils.goml" | |
| 2 | 3 | |
| 3 | 4 | // Checks that, if a type has two methods with the same name, they both get |
| 4 | 5 | // linked correctly. |
| 5 | 6 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 6 | 7 | |
| 7 | 8 | // This should link to the inherent impl |
| 8 | write-into: (".search-input", "ZyxwvutMethodDisambiguation -> bool") | |
| 9 | // To be SURE that the search will be run. | |
| 10 | press-key: 'Enter' | |
| 11 | // Waiting for the search results to appear... | |
| 12 | wait-for: "#search-tabs" | |
| 9 | call-function: ("perform-search", {"query": "ZyxwvutMethodDisambiguation -> bool"}) | |
| 13 | 10 | // Check the disambiguated link. |
| 14 | 11 | assert-count: ("a.result-method", 1) |
| 15 | 12 | assert-attribute: ("a.result-method", { |
| ... | ... | @@ -25,11 +22,7 @@ assert: "section:target" |
| 25 | 22 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 26 | 23 | |
| 27 | 24 | // This should link to the trait impl |
| 28 | write-into: (".search-input", "ZyxwvutMethodDisambiguation, usize -> usize") | |
| 29 | // To be SURE that the search will be run. | |
| 30 | press-key: 'Enter' | |
| 31 | // Waiting for the search results to appear... | |
| 32 | wait-for: "#search-tabs" | |
| 25 | call-function: ("perform-search", {"query": "ZyxwvutMethodDisambiguation, usize -> usize"}) | |
| 33 | 26 | // Check the disambiguated link. |
| 34 | 27 | assert-count: ("a.result-method", 1) |
| 35 | 28 | assert-attribute: ("a.result-method", { |
| ... | ... | @@ -47,6 +40,7 @@ assert: "section:target" |
| 47 | 40 | // impl block's disambiguator is also acted upon. |
| 48 | 41 | go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->bool" |
| 49 | 42 | wait-for: "#search-tabs" |
| 43 | wait-for-false: "#search-tabs .count.loading" | |
| 50 | 44 | assert-count: ("a.result-method", 1) |
| 51 | 45 | assert-attribute: ("a.result-method", { |
| 52 | 46 | "href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockStruct/method.second_fn" |
| ... | ... | @@ -56,6 +50,7 @@ wait-for: "details:has(summary > #impl-MultiImplBlockStruct-1) > div section[id= |
| 56 | 50 | |
| 57 | 51 | go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->u32" |
| 58 | 52 | wait-for: "#search-tabs" |
| 53 | wait-for-false: "#search-tabs .count.loading" | |
| 59 | 54 | assert-count: ("a.result-method", 1) |
| 60 | 55 | assert-attribute: ("a.result-method", { |
| 61 | 56 | "href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockTrait-for-MultiImplBlockStruct/method.second_fn" |
tests/rustdoc-gui/search-result-keyword.goml+2-5| ... | ... | @@ -1,8 +1,5 @@ |
| 1 | 1 | // Checks that the "keyword" results have the expected text alongside them. |
| 2 | include: "utils.goml" | |
| 2 | 3 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 3 | write-into: (".search-input", "for") | |
| 4 | // To be SURE that the search will be run. | |
| 5 | press-key: 'Enter' | |
| 6 | // Waiting for the search results to appear... | |
| 7 | wait-for: "#search-tabs" | |
| 4 | call-function: ("perform-search", {"query": "for"}) | |
| 8 | 5 | assert-text: (".result-keyword .result-name", "keyword for") |
tests/rustdoc-gui/search-tab-change-title-fn-sig.goml+7-25| ... | ... | @@ -1,11 +1,9 @@ |
| 1 | 1 | // Checks that the search tab results work correctly with function signature syntax |
| 2 | 2 | // First, try a search-by-name |
| 3 | include: "utils.goml" | |
| 3 | 4 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 4 | write-into: (".search-input", "Foo") | |
| 5 | // To be SURE that the search will be run. | |
| 6 | press-key: 'Enter' | |
| 7 | // Waiting for the search results to appear... | |
| 8 | wait-for: "#search-tabs" | |
| 5 | call-function: ("perform-search", {"query": "Foo"}) | |
| 6 | ||
| 9 | 7 | assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) |
| 10 | 8 | assert-text: ("#search-tabs > button:nth-of-type(1)", "In Names", STARTS_WITH) |
| 11 | 9 | assert: "input.search-input:focus" |
| ... | ... | @@ -23,11 +21,7 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(3)", {"class": "selected |
| 23 | 21 | |
| 24 | 22 | // Now try search-by-return |
| 25 | 23 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 26 | write-into: (".search-input", "-> String") | |
| 27 | // To be SURE that the search will be run. | |
| 28 | press-key: 'Enter' | |
| 29 | // Waiting for the search results to appear... | |
| 30 | wait-for: "#search-tabs" | |
| 24 | call-function: ("perform-search", {"query": "-> String"}) | |
| 31 | 25 | assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) |
| 32 | 26 | assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH) |
| 33 | 27 | assert: "input.search-input:focus" |
| ... | ... | @@ -45,30 +39,18 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected |
| 45 | 39 | |
| 46 | 40 | // Try with a search-by-return with no results |
| 47 | 41 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 48 | write-into: (".search-input", "-> Something") | |
| 49 | // To be SURE that the search will be run. | |
| 50 | press-key: 'Enter' | |
| 51 | // Waiting for the search results to appear... | |
| 52 | wait-for: "#search-tabs" | |
| 42 | call-function: ("perform-search", {"query": "-> Something"}) | |
| 53 | 43 | assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) |
| 54 | 44 | assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH) |
| 55 | 45 | |
| 56 | 46 | // Try with a search-by-parameter |
| 57 | 47 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 58 | write-into: (".search-input", "usize,pattern") | |
| 59 | // To be SURE that the search will be run. | |
| 60 | press-key: 'Enter' | |
| 61 | // Waiting for the search results to appear... | |
| 62 | wait-for: "#search-tabs" | |
| 48 | call-function: ("perform-search", {"query": "usize,pattern"}) | |
| 63 | 49 | assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) |
| 64 | 50 | assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH) |
| 65 | 51 | |
| 66 | 52 | // Try with a search-by-parameter-and-return |
| 67 | 53 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 68 | write-into: (".search-input", "pattern -> str") | |
| 69 | // To be SURE that the search will be run. | |
| 70 | press-key: 'Enter' | |
| 71 | // Waiting for the search results to appear... | |
| 72 | wait-for: "#search-tabs" | |
| 54 | call-function: ("perform-search", {"query": "pattern -> str"}) | |
| 73 | 55 | assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) |
| 74 | 56 | assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH) |
tests/rustdoc-gui/search-tab.goml+2-1| ... | ... | @@ -15,7 +15,8 @@ define-function: ( |
| 15 | 15 | focus: ".search-input" |
| 16 | 16 | press-key: "Enter" |
| 17 | 17 | |
| 18 | wait-for: "#search-tabs" | |
| 18 | wait-for: "#search-tabs .count" | |
| 19 | wait-for-false: "#search-tabs .count.loading" | |
| 19 | 20 | assert-css: ("#search-tabs > button:not(.selected)", { |
| 20 | 21 | "background-color": |background|, |
| 21 | 22 | "border-bottom": |border_bottom|, |
tests/rustdoc-gui/search-title.goml+2-4| ... | ... | @@ -5,10 +5,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 5 | 5 | store-value: (title, "test_docs - Rust") |
| 6 | 6 | assert-document-property: {"title": |title|} |
| 7 | 7 | |
| 8 | write-into: (".search-input", "test") | |
| 9 | // To be SURE that the search will be run. | |
| 10 | press-key: 'Enter' | |
| 11 | wait-for: "#crate-search" | |
| 8 | call-function: ("perform-search", {"query": "test"}) | |
| 12 | 9 | |
| 13 | 10 | assert-document-property: {"title": '"test" Search - Rust'} |
| 14 | 11 | |
| ... | ... | @@ -16,6 +13,7 @@ set-property: (".search-input", {"value": "another one"}) |
| 16 | 13 | // To be SURE that the search will be run. |
| 17 | 14 | press-key: 'Enter' |
| 18 | 15 | wait-for: "#crate-search" |
| 16 | wait-for-false: "#search-tabs .count.loading" | |
| 19 | 17 | |
| 20 | 18 | assert-document-property: {"title": '"another one" Search - Rust'} |
| 21 | 19 |
tests/rustdoc-gui/setting-auto-hide-content-large-items.goml+1-1| ... | ... | @@ -9,7 +9,7 @@ define-function: ( |
| 9 | 9 | [storage_value, setting_attribute_value, toggle_attribute_value], |
| 10 | 10 | block { |
| 11 | 11 | assert-local-storage: {"rustdoc-auto-hide-large-items": |storage_value|} |
| 12 | click: "#settings-menu" | |
| 12 | click: "rustdoc-toolbar .settings-menu" | |
| 13 | 13 | wait-for: "#settings" |
| 14 | 14 | assert-property: ("#auto-hide-large-items", {"checked": |setting_attribute_value|}) |
| 15 | 15 | assert-attribute: (".item-decl .type-contents-toggle", {"open": |toggle_attribute_value|}) |
tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml+1-1| ... | ... | @@ -6,7 +6,7 @@ define-function: ( |
| 6 | 6 | [storage_value, setting_attribute_value, toggle_attribute_value], |
| 7 | 7 | block { |
| 8 | 8 | assert-local-storage: {"rustdoc-auto-hide-method-docs": |storage_value|} |
| 9 | click: "#settings-menu" | |
| 9 | click: "rustdoc-toolbar .settings-menu" | |
| 10 | 10 | wait-for: "#settings" |
| 11 | 11 | assert-property: ("#auto-hide-method-docs", {"checked": |setting_attribute_value|}) |
| 12 | 12 | assert-attribute: (".toggle.method-toggle", {"open": |toggle_attribute_value|}) |
tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml+1-1| ... | ... | @@ -5,7 +5,7 @@ define-function: ( |
| 5 | 5 | [storage_value, setting_attribute_value, toggle_attribute_value], |
| 6 | 6 | block { |
| 7 | 7 | assert-local-storage: {"rustdoc-auto-hide-trait-implementations": |storage_value|} |
| 8 | click: "#settings-menu" | |
| 8 | click: "rustdoc-toolbar .settings-menu" | |
| 9 | 9 | wait-for: "#settings" |
| 10 | 10 | assert-property: ("#auto-hide-trait-implementations", {"checked": |setting_attribute_value|}) |
| 11 | 11 | assert-attribute: ("#trait-implementations-list > details", {"open": |toggle_attribute_value|}, ALL) |
tests/rustdoc-gui/setting-go-to-only-result.goml+2-2| ... | ... | @@ -5,7 +5,7 @@ define-function: ( |
| 5 | 5 | [storage_value, setting_attribute_value], |
| 6 | 6 | block { |
| 7 | 7 | assert-local-storage: {"rustdoc-go-to-only-result": |storage_value|} |
| 8 | click: "#settings-menu" | |
| 8 | click: "rustdoc-toolbar .settings-menu" | |
| 9 | 9 | wait-for: "#settings" |
| 10 | 10 | assert-property: ("#go-to-only-result", {"checked": |setting_attribute_value|}) |
| 11 | 11 | } |
| ... | ... | @@ -25,7 +25,7 @@ wait-for: "#search" |
| 25 | 25 | assert-document-property: ({"URL": "/lib2/index.html"}, CONTAINS) |
| 26 | 26 | |
| 27 | 27 | // Now we change its value. |
| 28 | click: "#settings-menu" | |
| 28 | click: "rustdoc-toolbar .settings-menu" | |
| 29 | 29 | wait-for: "#settings" |
| 30 | 30 | click: "#go-to-only-result" |
| 31 | 31 | assert-local-storage: {"rustdoc-go-to-only-result": "true"} |
tests/rustdoc-gui/settings-button.goml+1-1| ... | ... | @@ -9,7 +9,7 @@ define-function: ( |
| 9 | 9 | [theme, filter], |
| 10 | 10 | block { |
| 11 | 11 | call-function: ("switch-theme", {"theme": |theme|}) |
| 12 | assert-css: ("#settings-menu > a::before", { | |
| 12 | assert-css: ("rustdoc-toolbar .settings-menu > a::before", { | |
| 13 | 13 | "filter": |filter|, |
| 14 | 14 | "width": "18px", |
| 15 | 15 | "height": "18px", |
tests/rustdoc-gui/settings.goml+21-21| ... | ... | @@ -5,7 +5,7 @@ show-text: true // needed when we check for colors below. |
| 5 | 5 | // First, we check that the settings page doesn't exist. |
| 6 | 6 | assert-false: "#settings" |
| 7 | 7 | // We now click on the settings button. |
| 8 | click: "#settings-menu" | |
| 8 | click: "rustdoc-toolbar .settings-menu" | |
| 9 | 9 | wait-for: "#settings" |
| 10 | 10 | assert-css: ("#settings", {"display": "block"}) |
| 11 | 11 | |
| ... | ... | @@ -13,11 +13,11 @@ assert-css: ("#settings", {"display": "block"}) |
| 13 | 13 | store-css: (".setting-line", {"margin": setting_line_margin}) |
| 14 | 14 | |
| 15 | 15 | // Let's close it by clicking on the same button. |
| 16 | click: "#settings-menu" | |
| 16 | click: "rustdoc-toolbar .settings-menu" | |
| 17 | 17 | wait-for-css: ("#settings", {"display": "none"}) |
| 18 | 18 | |
| 19 | 19 | // Let's check that pressing "ESCAPE" is closing it. |
| 20 | click: "#settings-menu" | |
| 20 | click: "rustdoc-toolbar .settings-menu" | |
| 21 | 21 | wait-for-css: ("#settings", {"display": "block"}) |
| 22 | 22 | press-key: "Escape" |
| 23 | 23 | wait-for-css: ("#settings", {"display": "none"}) |
| ... | ... | @@ -28,7 +28,7 @@ write: "test" |
| 28 | 28 | // To be SURE that the search will be run. |
| 29 | 29 | press-key: 'Enter' |
| 30 | 30 | wait-for: "#alternative-display #search" |
| 31 | click: "#settings-menu" | |
| 31 | click: "rustdoc-toolbar .settings-menu" | |
| 32 | 32 | wait-for-css: ("#settings", {"display": "block"}) |
| 33 | 33 | // Ensure that the search is still displayed. |
| 34 | 34 | wait-for: "#alternative-display #search" |
| ... | ... | @@ -41,7 +41,7 @@ set-local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false" |
| 41 | 41 | // We reload the page so the local storage settings are being used. |
| 42 | 42 | reload: |
| 43 | 43 | |
| 44 | click: "#settings-menu" | |
| 44 | click: "rustdoc-toolbar .settings-menu" | |
| 45 | 45 | wait-for: "#settings" |
| 46 | 46 | |
| 47 | 47 | // We check that the "Use system theme" is disabled. |
| ... | ... | @@ -55,7 +55,7 @@ assert: "#preferred-light-theme.setting-line.hidden" |
| 55 | 55 | assert-property: ("#theme .setting-radio-choices #theme-dark", {"checked": "true"}) |
| 56 | 56 | |
| 57 | 57 | // Some style checks... |
| 58 | move-cursor-to: "#settings-menu > a" | |
| 58 | move-cursor-to: "rustdoc-toolbar .settings-menu > a" | |
| 59 | 59 | // First we check the "default" display for radio buttons. |
| 60 | 60 | assert-css: ( |
| 61 | 61 | "#theme-dark", |
| ... | ... | @@ -194,7 +194,7 @@ assert-css: ( |
| 194 | 194 | "border-width": "2px", |
| 195 | 195 | }, |
| 196 | 196 | ) |
| 197 | move-cursor-to: "#settings-menu > a" | |
| 197 | move-cursor-to: "rustdoc-toolbar .settings-menu > a" | |
| 198 | 198 | // Let's now check with the focus for toggles. |
| 199 | 199 | focus: "#auto-hide-large-items" |
| 200 | 200 | assert-css: ( |
| ... | ... | @@ -273,43 +273,43 @@ assert-local-storage: {"rustdoc-disable-shortcuts": "true"} |
| 273 | 273 | press-key: "Escape" |
| 274 | 274 | press-key: "?" |
| 275 | 275 | assert-false: "#help-button .popover" |
| 276 | wait-for-css: ("#settings-menu .popover", {"display": "block"}) | |
| 276 | wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"}) | |
| 277 | 277 | |
| 278 | 278 | // Now turn keyboard shortcuts back on, and see if they work. |
| 279 | 279 | click: "#disable-shortcuts" |
| 280 | 280 | assert-local-storage: {"rustdoc-disable-shortcuts": "false"} |
| 281 | 281 | press-key: "Escape" |
| 282 | 282 | press-key: "?" |
| 283 | wait-for-css: ("#help-button .popover", {"display": "block"}) | |
| 284 | assert-css: ("#settings-menu .popover", {"display": "none"}) | |
| 283 | wait-for-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"}) | |
| 284 | assert-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"}) | |
| 285 | 285 | |
| 286 | 286 | // Now switch back to the settings popover, and make sure the keyboard |
| 287 | 287 | // shortcut works when a check box is selected. |
| 288 | click: "#settings-menu > a" | |
| 289 | wait-for-css: ("#settings-menu .popover", {"display": "block"}) | |
| 288 | click: "rustdoc-toolbar .settings-menu > a" | |
| 289 | wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"}) | |
| 290 | 290 | focus: "#auto-hide-large-items" |
| 291 | 291 | press-key: "?" |
| 292 | wait-for-css: ("#settings-menu .popover", {"display": "none"}) | |
| 293 | wait-for-css: ("#help-button .popover", {"display": "block"}) | |
| 292 | wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"}) | |
| 293 | wait-for-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"}) | |
| 294 | 294 | |
| 295 | 295 | // Now switch back to the settings popover, and make sure the keyboard |
| 296 | 296 | // shortcut works when a check box is selected. |
| 297 | click: "#settings-menu > a" | |
| 298 | wait-for-css: ("#settings-menu .popover", {"display": "block"}) | |
| 299 | wait-for-css: ("#help-button .popover", {"display": "none"}) | |
| 297 | click: "rustdoc-toolbar .settings-menu > a" | |
| 298 | wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"}) | |
| 299 | assert-false: "rustdoc-toolbar .help-menu .popover" | |
| 300 | 300 | focus: "#theme-system-preference" |
| 301 | 301 | press-key: "?" |
| 302 | wait-for-css: ("#settings-menu .popover", {"display": "none"}) | |
| 303 | wait-for-css: ("#help-button .popover", {"display": "block"}) | |
| 302 | wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"}) | |
| 303 | wait-for-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"}) | |
| 304 | 304 | |
| 305 | 305 | // Now we go to the settings page to check that the CSS is loaded as expected. |
| 306 | 306 | go-to: "file://" + |DOC_PATH| + "/settings.html" |
| 307 | 307 | wait-for: "#settings" |
| 308 | assert-false: "#settings-menu" | |
| 308 | assert-false: "rustdoc-toolbar .settings-menu" | |
| 309 | 309 | assert-css: (".setting-radio", {"cursor": "pointer"}) |
| 310 | 310 | |
| 311 | 311 | assert-attribute-false: ("#settings", {"class": "popover"}, CONTAINS) |
| 312 | compare-elements-position: (".sub form", "#settings", ["x"]) | |
| 312 | compare-elements-position: (".main-heading", "#settings", ["x"]) | |
| 313 | 313 | |
| 314 | 314 | // Check that setting-line has the same margin in this mode as in the popover. |
| 315 | 315 | assert-css: (".setting-line", {"margin": |setting_line_margin|}) |
tests/rustdoc-gui/shortcuts.goml+2-2| ... | ... | @@ -8,9 +8,9 @@ press-key: "Escape" |
| 8 | 8 | assert-false: "input.search-input:focus" |
| 9 | 9 | // We now check for the help popup. |
| 10 | 10 | press-key: "?" |
| 11 | assert-css: ("#help-button .popover", {"display": "block"}) | |
| 11 | assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"}) | |
| 12 | 12 | press-key: "Escape" |
| 13 | assert-css: ("#help-button .popover", {"display": "none"}) | |
| 13 | assert-false: "rustdoc-toolbar .help-menu .popover" | |
| 14 | 14 | // Checking doc collapse and expand. |
| 15 | 15 | // It should be displaying a "-": |
| 16 | 16 | assert-text: ("#toggle-all-docs", "Summary") |
tests/rustdoc-gui/sidebar-mobile.goml+4-4| ... | ... | @@ -17,7 +17,7 @@ assert-css: (".sidebar", {"display": "block", "left": "-1000px"}) |
| 17 | 17 | focus: ".sidebar-elems h3 a" |
| 18 | 18 | assert-css: (".sidebar", {"display": "block", "left": "0px"}) |
| 19 | 19 | // When we tab out of the sidebar, close it. |
| 20 | focus: ".search-input" | |
| 20 | focus: "#search-button" | |
| 21 | 21 | assert-css: (".sidebar", {"display": "block", "left": "-1000px"}) |
| 22 | 22 | |
| 23 | 23 | // Open the sidebar menu. |
| ... | ... | @@ -43,7 +43,7 @@ press-key: "Escape" |
| 43 | 43 | assert-css: (".sidebar", {"display": "block", "left": "-1000px"}) |
| 44 | 44 | |
| 45 | 45 | // Check that the topbar is visible |
| 46 | assert-property: (".mobile-topbar", {"clientHeight": "45"}) | |
| 46 | assert-property: ("rustdoc-topbar", {"clientHeight": "45"}) | |
| 47 | 47 | |
| 48 | 48 | // Check that clicking an element from the sidebar scrolls to the right place |
| 49 | 49 | // so the target is not obscured by the topbar. |
| ... | ... | @@ -54,7 +54,7 @@ assert-position: ("#method\.must_use", {"y": 46}) |
| 54 | 54 | // Check that the bottom-most item on the sidebar menu can be scrolled fully into view. |
| 55 | 55 | click: ".sidebar-menu-toggle" |
| 56 | 56 | scroll-to: ".block.keyword li:nth-child(1)" |
| 57 | compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 544}) | |
| 57 | compare-elements-position-near: (".block.keyword li:nth-child(1)", "rustdoc-topbar", {"y": 544}) | |
| 58 | 58 | |
| 59 | 59 | // Now checking the background color of the sidebar. |
| 60 | 60 | // Close the sidebar menu. |
| ... | ... | @@ -65,7 +65,7 @@ define-function: ( |
| 65 | 65 | "check-colors", |
| 66 | 66 | [theme, color, background], |
| 67 | 67 | block { |
| 68 | call-function: ("switch-theme", {"theme": |theme|}) | |
| 68 | call-function: ("switch-theme-mobile", {"theme": |theme|}) | |
| 69 | 69 | reload: |
| 70 | 70 | |
| 71 | 71 | // Open the sidebar menu. |
tests/rustdoc-gui/sidebar-resize-close-popover.goml+2-2| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 3 | 3 | assert-property: (".sidebar", {"clientWidth": "199"}) |
| 4 | 4 | show-text: true |
| 5 | click: "#settings-menu" | |
| 5 | click: "rustdoc-toolbar .settings-menu" | |
| 6 | 6 | wait-for: "#settings" |
| 7 | 7 | assert-css: ("#settings", {"display": "block"}) |
| 8 | 8 | // normal resizing |
| ... | ... | @@ -12,7 +12,7 @@ assert-css: ("#settings", {"display": "none"}) |
| 12 | 12 | |
| 13 | 13 | // Now same thing, but for source code |
| 14 | 14 | go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" |
| 15 | click: "#settings-menu" | |
| 15 | click: "rustdoc-toolbar .settings-menu" | |
| 16 | 16 | wait-for: "#settings" |
| 17 | 17 | assert-css: ("#settings", {"display": "block"}) |
| 18 | 18 | assert-property: (".sidebar", {"clientWidth": "49"}) |
tests/rustdoc-gui/sidebar-resize-setting.goml+12-12| ... | ... | @@ -4,7 +4,7 @@ assert-property: (".sidebar", {"clientWidth": "199"}) |
| 4 | 4 | show-text: true |
| 5 | 5 | |
| 6 | 6 | // Verify that the "hide" option is unchecked |
| 7 | click: "#settings-menu" | |
| 7 | click: "rustdoc-toolbar .settings-menu" | |
| 8 | 8 | wait-for: "#settings" |
| 9 | 9 | assert-css: ("#settings", {"display": "block"}) |
| 10 | 10 | assert-property: ("#hide-sidebar", {"checked": "false"}) |
| ... | ... | @@ -15,7 +15,7 @@ drag-and-drop: ((205, 100), (5, 100)) |
| 15 | 15 | assert-css: (".sidebar", {"display": "none"}) |
| 16 | 16 | |
| 17 | 17 | // Verify that the "hide" option is checked |
| 18 | focus: "#settings-menu a" | |
| 18 | focus: "rustdoc-toolbar .settings-menu a" | |
| 19 | 19 | press-key: "Enter" |
| 20 | 20 | wait-for-css: ("#settings", {"display": "block"}) |
| 21 | 21 | assert-property: ("#hide-sidebar", {"checked": "true"}) |
| ... | ... | @@ -24,28 +24,28 @@ wait-for-css: (".sidebar", {"display": "block"}) |
| 24 | 24 | |
| 25 | 25 | // Verify that hiding the sidebar hides the source sidebar |
| 26 | 26 | // and puts the button in static position mode on mobile |
| 27 | go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" | |
| 27 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" | |
| 28 | 28 | set-window-size: (600, 600) |
| 29 | focus: "#settings-menu a" | |
| 29 | focus: "rustdoc-topbar .settings-menu a" | |
| 30 | 30 | press-key: "Enter" |
| 31 | 31 | wait-for-css: ("#settings", {"display": "block"}) |
| 32 | wait-for-css: ("#sidebar-button", {"position": "static"}) | |
| 33 | assert-property: ("#hide-sidebar", {"checked": "false"}) | |
| 34 | click: "#hide-sidebar" | |
| 35 | wait-for-css: (".sidebar", {"display": "none"}) | |
| 32 | 36 | wait-for-css: ("#sidebar-button", {"position": "fixed"}) |
| 33 | 37 | store-position: ("#sidebar-button", { |
| 34 | 38 | "y": sidebar_button_y, |
| 35 | 39 | "x": sidebar_button_x, |
| 36 | 40 | }) |
| 37 | assert-property: ("#hide-sidebar", {"checked": "false"}) | |
| 38 | click: "#hide-sidebar" | |
| 39 | wait-for-css: (".sidebar", {"display": "none"}) | |
| 40 | wait-for-css: ("#sidebar-button", {"position": "static"}) | |
| 41 | assert-position: ("#sidebar-button", { | |
| 42 | "y": |sidebar_button_y|, | |
| 43 | "x": |sidebar_button_x|, | |
| 44 | }) | |
| 45 | 41 | assert-property: ("#hide-sidebar", {"checked": "true"}) |
| 46 | 42 | press-key: "Escape" |
| 47 | 43 | // Clicking the sidebar button should work, and implicitly re-enable |
| 48 | 44 | // the persistent navigation bar |
| 49 | 45 | wait-for-css: ("#settings", {"display": "none"}) |
| 46 | assert-position: ("#sidebar-button", { | |
| 47 | "y": |sidebar_button_y|, | |
| 48 | "x": |sidebar_button_x|, | |
| 49 | }) | |
| 50 | 50 | click: "#sidebar-button" |
| 51 | 51 | wait-for-css: (".sidebar", {"display": "block"}) |
tests/rustdoc-gui/sidebar-source-code-display.goml+1-1| ... | ... | @@ -141,7 +141,7 @@ click: "#sidebar-button" |
| 141 | 141 | wait-for-css: (".src .sidebar > *", {"visibility": "hidden"}) |
| 142 | 142 | // We scroll to line 117 to change the scroll position. |
| 143 | 143 | scroll-to: '//*[@id="117"]' |
| 144 | store-value: (y_offset, "2578") | |
| 144 | store-value: (y_offset, "2567") | |
| 145 | 145 | assert-window-property: {"pageYOffset": |y_offset|} |
| 146 | 146 | // Expanding the sidebar... |
| 147 | 147 | click: "#sidebar-button" |
tests/rustdoc-gui/sidebar-source-code.goml+1-1| ... | ... | @@ -85,4 +85,4 @@ assert-false: ".src-sidebar-expanded" |
| 85 | 85 | assert: "nav.sidebar" |
| 86 | 86 | |
| 87 | 87 | // Check that the topbar is not visible |
| 88 | assert-false: ".mobile-topbar" | |
| 88 | assert-false: "rustdoc-topbar" |
tests/rustdoc-gui/sidebar.goml+3-3| ... | ... | @@ -200,7 +200,7 @@ drag-and-drop: ((205, 100), (108, 100)) |
| 200 | 200 | assert-position: (".sidebar-crate > h2 > a", {"x": -3}) |
| 201 | 201 | |
| 202 | 202 | // Check that the mobile sidebar and the source sidebar use the same icon. |
| 203 | store-css: (".mobile-topbar .sidebar-menu-toggle::before", {"content": image_url}) | |
| 203 | store-css: ("rustdoc-topbar .sidebar-menu-toggle::before", {"content": image_url}) | |
| 204 | 204 | // Then we go to a source page. |
| 205 | 205 | click: ".main-heading .src" |
| 206 | 206 | assert-css: ("#sidebar-button a::before", {"content": |image_url|}) |
| ... | ... | @@ -212,7 +212,7 @@ assert: |sidebar_background| != |sidebar_background_hover| |
| 212 | 212 | click: "#sidebar-button a" |
| 213 | 213 | wait-for: "html.src-sidebar-expanded" |
| 214 | 214 | assert-css: ("#sidebar-button a:hover", {"background-color": |sidebar_background_hover|}) |
| 215 | move-cursor-to: "#settings-menu" | |
| 215 | move-cursor-to: "#search-button" | |
| 216 | 216 | assert-css: ("#sidebar-button a:not(:hover)", {"background-color": |sidebar_background|}) |
| 217 | 217 | // Closing sidebar. |
| 218 | 218 | click: "#sidebar-button a" |
| ... | ... | @@ -220,7 +220,7 @@ wait-for: "html:not(.src-sidebar-expanded)" |
| 220 | 220 | // Now we check the same when the sidebar button is moved alongside the search. |
| 221 | 221 | set-window-size: (500, 500) |
| 222 | 222 | store-css: ("#sidebar-button a:hover", {"background-color": not_sidebar_background_hover}) |
| 223 | move-cursor-to: "#settings-menu" | |
| 223 | move-cursor-to: "rustdoc-toolbar #search-button" | |
| 224 | 224 | store-css: ("#sidebar-button a:not(:hover)", {"background-color": not_sidebar_background}) |
| 225 | 225 | // The sidebar background is supposed to be the same as the main background. |
| 226 | 226 | assert-css: ("body", {"background-color": |not_sidebar_background|}) |
tests/rustdoc-gui/source-anchor-scroll.goml+4-4| ... | ... | @@ -8,13 +8,13 @@ set-window-size: (600, 800) |
| 8 | 8 | assert-property: ("html", {"scrollTop": "0"}) |
| 9 | 9 | |
| 10 | 10 | click: '//a[text() = "barbar" and @href="#5-7"]' |
| 11 | assert-property: ("html", {"scrollTop": "206"}) | |
| 11 | assert-property: ("html", {"scrollTop": "195"}) | |
| 12 | 12 | click: '//a[text() = "bar" and @href="#28-36"]' |
| 13 | assert-property: ("html", {"scrollTop": "239"}) | |
| 13 | assert-property: ("html", {"scrollTop": "228"}) | |
| 14 | 14 | click: '//a[normalize-space() = "sub_fn" and @href="#2-4"]' |
| 15 | assert-property: ("html", {"scrollTop": "134"}) | |
| 15 | assert-property: ("html", {"scrollTop": "123"}) | |
| 16 | 16 | |
| 17 | 17 | // We now check that clicking on lines doesn't change the scroll |
| 18 | 18 | // Extra information: the "sub_fn" function header is on line 1. |
| 19 | 19 | click: '//*[@id="6"]' |
| 20 | assert-property: ("html", {"scrollTop": "134"}) | |
| 20 | assert-property: ("html", {"scrollTop": "123"}) |
tests/rustdoc-gui/source-code-page.goml+5-27| ... | ... | @@ -89,9 +89,9 @@ assert-css: ("a[data-nosnippet]", {"text-align": "right"}, ALL) |
| 89 | 89 | // do anything (and certainly not add a `#NaN` to the URL!). |
| 90 | 90 | go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" |
| 91 | 91 | // We use this assert-position to know where we will click. |
| 92 | assert-position: ("//*[@id='1']", {"x": 81, "y": 169}) | |
| 93 | // We click on the left of the "1" anchor but still in the `a[data-nosnippet]`. | |
| 94 | click: (77, 163) | |
| 92 | assert-position: ("//*[@id='1']", {"x": 81, "y": 141}) | |
| 93 | // We click on the left of the "1" anchor but still in the "src-line-number" `<pre>`. | |
| 94 | click: (135, 77) | |
| 95 | 95 | assert-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH) |
| 96 | 96 | |
| 97 | 97 | // Checking the source code sidebar. |
| ... | ... | @@ -156,27 +156,8 @@ call-function: ("check-sidebar-dir-entry", { |
| 156 | 156 | "y": |source_sidebar_title_y| + |source_sidebar_title_height| + 7, |
| 157 | 157 | }) |
| 158 | 158 | |
| 159 | // Check the search form | |
| 160 | assert-css: ("nav.sub", {"flex-direction": "row"}) | |
| 161 | // The goal of this test is to ensure the search input is perfectly centered | |
| 162 | // between the top of the page and the header. | |
| 163 | // To check this, we maintain the invariant: | |
| 164 | // | |
| 165 | // offsetTop[nav.sub form] = offsetTop[#main-content] - offsetHeight[nav.sub form] - offsetTop[nav.sub form] | |
| 166 | assert-position: ("nav.sub form", {"y": 15}) | |
| 167 | assert-property: ("nav.sub form", {"offsetHeight": 34}) | |
| 168 | assert-position: ("h1", {"y": 68}) | |
| 169 | // 15 = 64 - 34 - 15 | |
| 170 | ||
| 171 | // Now do the same check on moderately-sized, tablet mobile. | |
| 172 | set-window-size: (700, 700) | |
| 173 | assert-css: ("nav.sub", {"flex-direction": "row"}) | |
| 174 | assert-position: ("nav.sub form", {"y": 8}) | |
| 175 | assert-property: ("nav.sub form", {"offsetHeight": 34}) | |
| 176 | assert-position: ("h1", {"y": 54}) | |
| 177 | // 8 = 50 - 34 - 8 | |
| 178 | ||
| 179 | 159 | // Check the sidebar directory entries have a marker and spacing (tablet). |
| 160 | set-window-size: (700, 700) | |
| 180 | 161 | store-property: (".src-sidebar-title", { |
| 181 | 162 | "offsetHeight": source_sidebar_title_height, |
| 182 | 163 | "offsetTop": source_sidebar_title_y, |
| ... | ... | @@ -187,11 +168,8 @@ call-function: ("check-sidebar-dir-entry", { |
| 187 | 168 | "y": |source_sidebar_title_y| + |source_sidebar_title_height| + 7, |
| 188 | 169 | }) |
| 189 | 170 | |
| 190 | // Tiny, phone mobile gets a different display where the logo is stacked on top. | |
| 191 | set-window-size: (450, 700) | |
| 192 | assert-css: ("nav.sub", {"flex-direction": "column"}) | |
| 193 | ||
| 194 | 171 | // Check the sidebar directory entries have a marker and spacing (phone). |
| 172 | set-window-size: (450, 700) | |
| 195 | 173 | store-property: (".src-sidebar-title", { |
| 196 | 174 | "offsetHeight": source_sidebar_title_height, |
| 197 | 175 | "offsetTop": source_sidebar_title_y, |
tests/rustdoc-gui/source-code-wrapping.goml+2-2| ... | ... | @@ -13,7 +13,7 @@ define-function: ( |
| 13 | 13 | ) |
| 14 | 14 | |
| 15 | 15 | store-size: (".rust code", {"width": width, "height": height}) |
| 16 | click: "#settings-menu" | |
| 16 | click: "main .settings-menu" | |
| 17 | 17 | wait-for: "#settings" |
| 18 | 18 | call-function: ("click-code-wrapping", {"expected": "true"}) |
| 19 | 19 | wait-for-size-false: (".rust code", {"width": |width|, "height": |height|}) |
| ... | ... | @@ -28,7 +28,7 @@ assert-size: (".rust code", {"width": |width|, "height": |height|}) |
| 28 | 28 | |
| 29 | 29 | // Now let's check in docs code examples. |
| 30 | 30 | go-to: "file://" + |DOC_PATH| + "/test_docs/trait_bounds/index.html" |
| 31 | click: "#settings-menu" | |
| 31 | click: "main .settings-menu" | |
| 32 | 32 | wait-for: "#settings" |
| 33 | 33 | |
| 34 | 34 | store-property: (".example-wrap .rust code", {"scrollWidth": rust_width, "scrollHeight": rust_height}) |
tests/rustdoc-gui/theme-change.goml+2-2| ... | ... | @@ -7,7 +7,7 @@ store-value: (background_light, "white") |
| 7 | 7 | store-value: (background_dark, "#353535") |
| 8 | 8 | store-value: (background_ayu, "#0f1419") |
| 9 | 9 | |
| 10 | click: "#settings-menu" | |
| 10 | click: "rustdoc-toolbar .settings-menu" | |
| 11 | 11 | wait-for: "#theme-ayu" |
| 12 | 12 | click: "#theme-ayu" |
| 13 | 13 | // should be the ayu theme so let's check the color. |
| ... | ... | @@ -75,7 +75,7 @@ store-value: (background_dark, "#353535") |
| 75 | 75 | store-value: (background_ayu, "#0f1419") |
| 76 | 76 | store-value: (background_custom_theme, "red") |
| 77 | 77 | |
| 78 | click: "#settings-menu" | |
| 78 | click: "rustdoc-toolbar .settings-menu" | |
| 79 | 79 | wait-for: "#theme-ayu" |
| 80 | 80 | click: "#theme-ayu" |
| 81 | 81 | // should be the ayu theme so let's check the color. |
tests/rustdoc-gui/theme-defaults.goml+2-2| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | // Ensure that the theme picker always starts with the actual defaults. |
| 2 | 2 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 3 | click: "#settings-menu" | |
| 3 | click: "rustdoc-toolbar .settings-menu" | |
| 4 | 4 | wait-for: "#theme-system-preference" |
| 5 | 5 | assert: "#theme-system-preference:checked" |
| 6 | 6 | assert: "#preferred-light-theme-light:checked" |
| ... | ... | @@ -16,7 +16,7 @@ set-local-storage: { |
| 16 | 16 | "rustdoc-theme": "ayu" |
| 17 | 17 | } |
| 18 | 18 | go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" |
| 19 | click: "#settings-menu" | |
| 19 | click: "rustdoc-toolbar .settings-menu" | |
| 20 | 20 | wait-for: "#theme-system-preference" |
| 21 | 21 | assert: "#theme-system-preference:checked" |
| 22 | 22 | assert: "#preferred-light-theme-light:checked" |
tests/rustdoc-gui/toggle-click-deadspace.goml+1-1| ... | ... | @@ -13,4 +13,4 @@ assert-attribute-false: (".impl-items .toggle", {"open": ""}) |
| 13 | 13 | // Click the "Trait" part of "impl Trait" and verify it navigates. |
| 14 | 14 | click: "#impl-Trait-for-Foo h3 a:first-of-type" |
| 15 | 15 | assert-text: (".main-heading .rustdoc-breadcrumbs", "lib2") |
| 16 | assert-text: (".main-heading h1", "Trait TraitCopy item path") | |
| 16 | assert-text: (".main-heading h1", "Trait Trait Copy item path") |
tests/rustdoc-gui/toggle-docs-mobile.goml+6-6| ... | ... | @@ -3,12 +3,12 @@ |
| 3 | 3 | go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" |
| 4 | 4 | set-window-size: (433, 600) |
| 5 | 5 | assert-attribute: (".top-doc", {"open": ""}) |
| 6 | click: (4, 270) // This is the position of the top doc comment toggle | |
| 6 | click: (4, 230) // This is the position of the top doc comment toggle | |
| 7 | 7 | assert-attribute-false: (".top-doc", {"open": ""}) |
| 8 | click: (4, 270) | |
| 8 | click: (4, 230) | |
| 9 | 9 | assert-attribute: (".top-doc", {"open": ""}) |
| 10 | 10 | // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. |
| 11 | click: (3, 270) | |
| 11 | click: (3, 230) | |
| 12 | 12 | assert-attribute: (".top-doc", {"open": ""}) |
| 13 | 13 | |
| 14 | 14 | // Assert the position of the toggle on the top doc block. |
| ... | ... | @@ -24,12 +24,12 @@ assert-position: ( |
| 24 | 24 | // Now we do the same but with a little bigger width |
| 25 | 25 | set-window-size: (600, 600) |
| 26 | 26 | assert-attribute: (".top-doc", {"open": ""}) |
| 27 | click: (4, 270) // New Y position since all search elements are back on one line. | |
| 27 | click: (4, 230) // New Y position since all search elements are back on one line. | |
| 28 | 28 | assert-attribute-false: (".top-doc", {"open": ""}) |
| 29 | click: (4, 270) | |
| 29 | click: (4, 230) | |
| 30 | 30 | assert-attribute: (".top-doc", {"open": ""}) |
| 31 | 31 | // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. |
| 32 | click: (3, 270) | |
| 32 | click: (3, 230) | |
| 33 | 33 | assert-attribute: (".top-doc", {"open": ""}) |
| 34 | 34 | |
| 35 | 35 | // Same check on trait items. |
tests/rustdoc-gui/toggle-docs.goml+1-1| ... | ... | @@ -64,7 +64,7 @@ define-function: ( |
| 64 | 64 | "filter": |filter|, |
| 65 | 65 | }) |
| 66 | 66 | // moving the cursor somewhere else to not mess with next function calls. |
| 67 | move-cursor-to: ".search-input" | |
| 67 | move-cursor-to: "#search-button" | |
| 68 | 68 | }, |
| 69 | 69 | ) |
| 70 | 70 |
tests/rustdoc-gui/type-declation-overflow.goml+11-11| ... | ... | @@ -47,27 +47,27 @@ assert-property: ("pre.item-decl", {"scrollWidth": "950"}) |
| 47 | 47 | set-window-size: (600, 600) |
| 48 | 48 | go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" |
| 49 | 49 | // It shouldn't have an overflow in the topbar either. |
| 50 | store-property: (".mobile-topbar", {"scrollWidth": scrollWidth}) | |
| 51 | assert-property: (".mobile-topbar", {"clientWidth": |scrollWidth|}) | |
| 52 | assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"}) | |
| 50 | store-property: ("rustdoc-topbar", {"scrollWidth": scrollWidth}) | |
| 51 | assert-property: ("rustdoc-topbar", {"clientWidth": |scrollWidth|}, NEAR) | |
| 52 | assert-css: ("rustdoc-topbar h2", {"overflow-x": "hidden"}) | |
| 53 | 53 | |
| 54 | 54 | // Check that main heading and toolbar go side-by-side, both on desktop and on mobile. |
| 55 | 55 | set-window-size: (1100, 800) |
| 56 | 56 | go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" |
| 57 | compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"]) | |
| 58 | compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 550}) | |
| 57 | compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", ["y"]) | |
| 58 | compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", {"x": 300}) | |
| 59 | 59 | go-to: "file://" + |DOC_PATH| + "/lib2/index.html" |
| 60 | compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"]) | |
| 61 | compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 550}) | |
| 60 | compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", ["y"]) | |
| 61 | compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", {"x": 300}) | |
| 62 | 62 | |
| 63 | 63 | // On mobile, they always wrap. |
| 64 | 64 | set-window-size: (600, 600) |
| 65 | 65 | go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" |
| 66 | compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"]) | |
| 67 | compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 200}) | |
| 66 | compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", ["y"]) | |
| 67 | compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", {"x": 200}) | |
| 68 | 68 | go-to: "file://" + |DOC_PATH| + "/lib2/index.html" |
| 69 | compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"]) | |
| 70 | compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 200}) | |
| 69 | compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", ["y"]) | |
| 70 | compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", {"x": 200}) | |
| 71 | 71 | |
| 72 | 72 | // Now we will check that the scrolling is working. |
| 73 | 73 | // First on an item with "hidden methods". |
tests/rustdoc-gui/utils.goml+35-2| ... | ... | @@ -5,14 +5,47 @@ define-function: ( |
| 5 | 5 | block { |
| 6 | 6 | // Set the theme. |
| 7 | 7 | // Open the settings menu. |
| 8 | click: "#settings-menu" | |
| 8 | click: "rustdoc-toolbar .settings-menu" | |
| 9 | 9 | // Wait for the popover to appear... |
| 10 | 10 | wait-for: "#settings" |
| 11 | 11 | // Change the setting. |
| 12 | 12 | click: "#theme-"+ |theme| |
| 13 | 13 | // Close the popover. |
| 14 | click: "#settings-menu" | |
| 14 | click: "rustdoc-toolbar .settings-menu" | |
| 15 | 15 | // Ensure that the local storage was correctly updated. |
| 16 | 16 | assert-local-storage: {"rustdoc-theme": |theme|} |
| 17 | 17 | }, |
| 18 | 18 | ) |
| 19 | ||
| 20 | define-function: ( | |
| 21 | "switch-theme-mobile", | |
| 22 | [theme], | |
| 23 | block { | |
| 24 | // Set the theme. | |
| 25 | // Open the settings menu. | |
| 26 | click: "rustdoc-topbar .settings-menu" | |
| 27 | // Wait for the popover to appear... | |
| 28 | wait-for: "#settings" | |
| 29 | // Change the setting. | |
| 30 | click: "#theme-"+ |theme| | |
| 31 | // Close the popover. | |
| 32 | click: "rustdoc-topbar .settings-menu" | |
| 33 | // Ensure that the local storage was correctly updated. | |
| 34 | assert-local-storage: {"rustdoc-theme": |theme|} | |
| 35 | }, | |
| 36 | ) | |
| 37 | ||
| 38 | define-function: ( | |
| 39 | "perform-search", | |
| 40 | [query], | |
| 41 | block { | |
| 42 | click: "#search-button" | |
| 43 | wait-for: ".search-input" | |
| 44 | write-into: (".search-input", |query|) | |
| 45 | press-key: 'Enter' | |
| 46 | // wait for the search to start | |
| 47 | wait-for: "#search-tabs" | |
| 48 | // then wait for it to finish | |
| 49 | wait-for-false: "#search-tabs .count.loading" | |
| 50 | } | |
| 51 | ) |
tests/rustdoc-js-std/alias-1.js+5| ... | ... | @@ -6,5 +6,10 @@ const EXPECTED = { |
| 6 | 6 | 'name': 'reference', |
| 7 | 7 | 'desc': "References, <code>&amp;T</code> and <code>&amp;mut T</code>.", |
| 8 | 8 | }, |
| 9 | { | |
| 10 | 'path': 'std::ops', | |
| 11 | 'name': 'BitAnd', | |
| 12 | 'desc': "The bitwise AND operator <code>&amp;</code>.", | |
| 13 | }, | |
| 9 | 14 | ], |
| 10 | 15 | }; |
tests/rustdoc-js-std/alias-2.js+1-3| ... | ... | @@ -1,9 +1,7 @@ |
| 1 | 1 | const EXPECTED = { |
| 2 | 2 | 'query': '+', |
| 3 | 3 | 'others': [ |
| 4 | { 'path': 'std::ops', 'name': 'AddAssign' }, | |
| 5 | 4 | { 'path': 'std::ops', 'name': 'Add' }, |
| 6 | { 'path': 'core::ops', 'name': 'AddAssign' }, | |
| 7 | { 'path': 'core::ops', 'name': 'Add' }, | |
| 5 | { 'path': 'std::ops', 'name': 'AddAssign' }, | |
| 8 | 6 | ], |
| 9 | 7 | }; |
tests/rustdoc-js-std/basic.js+1-1| ... | ... | @@ -9,6 +9,6 @@ const EXPECTED = { |
| 9 | 9 | { 'path': 'std::str', 'name': 'eq' }, |
| 10 | 10 | ], |
| 11 | 11 | 'returned': [ |
| 12 | { 'path': 'std::string::String', 'name': 'add' }, | |
| 12 | { 'path': 'std::string::String', 'name': 'new' }, | |
| 13 | 13 | ], |
| 14 | 14 | }; |
tests/rustdoc-js-std/parser-bindings.js+14-14| ... | ... | @@ -20,12 +20,12 @@ const PARSED = [ |
| 20 | 20 | pathLast: "c", |
| 21 | 21 | normalizedPathLast: "c", |
| 22 | 22 | generics: [], |
| 23 | typeFilter: -1, | |
| 23 | typeFilter: null, | |
| 24 | 24 | }, |
| 25 | 25 | ] |
| 26 | 26 | ], |
| 27 | 27 | ], |
| 28 | typeFilter: -1, | |
| 28 | typeFilter: null, | |
| 29 | 29 | }, |
| 30 | 30 | ], |
| 31 | 31 | foundElems: 1, |
| ... | ... | @@ -51,11 +51,11 @@ const PARSED = [ |
| 51 | 51 | pathWithoutLast: [], |
| 52 | 52 | pathLast: "c", |
| 53 | 53 | generics: [], |
| 54 | typeFilter: -1, | |
| 54 | typeFilter: null, | |
| 55 | 55 | }] |
| 56 | 56 | ], |
| 57 | 57 | ], |
| 58 | typeFilter: -1, | |
| 58 | typeFilter: null, | |
| 59 | 59 | }, |
| 60 | 60 | ], |
| 61 | 61 | foundElems: 1, |
| ... | ... | @@ -81,11 +81,11 @@ const PARSED = [ |
| 81 | 81 | pathWithoutLast: [], |
| 82 | 82 | pathLast: "never", |
| 83 | 83 | generics: [], |
| 84 | typeFilter: 1, | |
| 84 | typeFilter: "primitive", | |
| 85 | 85 | }] |
| 86 | 86 | ], |
| 87 | 87 | ], |
| 88 | typeFilter: -1, | |
| 88 | typeFilter: null, | |
| 89 | 89 | }, |
| 90 | 90 | ], |
| 91 | 91 | foundElems: 1, |
| ... | ... | @@ -111,11 +111,11 @@ const PARSED = [ |
| 111 | 111 | pathWithoutLast: [], |
| 112 | 112 | pathLast: "[]", |
| 113 | 113 | generics: [], |
| 114 | typeFilter: 1, | |
| 114 | typeFilter: "primitive", | |
| 115 | 115 | }] |
| 116 | 116 | ], |
| 117 | 117 | ], |
| 118 | typeFilter: -1, | |
| 118 | typeFilter: null, | |
| 119 | 119 | }, |
| 120 | 120 | ], |
| 121 | 121 | foundElems: 1, |
| ... | ... | @@ -147,14 +147,14 @@ const PARSED = [ |
| 147 | 147 | pathWithoutLast: [], |
| 148 | 148 | pathLast: "never", |
| 149 | 149 | generics: [], |
| 150 | typeFilter: 1, | |
| 150 | typeFilter: "primitive", | |
| 151 | 151 | }, |
| 152 | 152 | ], |
| 153 | typeFilter: 1, | |
| 153 | typeFilter: "primitive", | |
| 154 | 154 | }] |
| 155 | 155 | ], |
| 156 | 156 | ], |
| 157 | typeFilter: -1, | |
| 157 | typeFilter: null, | |
| 158 | 158 | }, |
| 159 | 159 | ], |
| 160 | 160 | foundElems: 1, |
| ... | ... | @@ -213,7 +213,7 @@ const PARSED = [ |
| 213 | 213 | pathWithoutLast: [], |
| 214 | 214 | pathLast: "c", |
| 215 | 215 | generics: [], |
| 216 | typeFilter: -1, | |
| 216 | typeFilter: null, | |
| 217 | 217 | }, |
| 218 | 218 | { |
| 219 | 219 | name: "X", |
| ... | ... | @@ -221,12 +221,12 @@ const PARSED = [ |
| 221 | 221 | pathWithoutLast: [], |
| 222 | 222 | pathLast: "x", |
| 223 | 223 | generics: [], |
| 224 | typeFilter: -1, | |
| 224 | typeFilter: null, | |
| 225 | 225 | }, |
| 226 | 226 | ], |
| 227 | 227 | ], |
| 228 | 228 | ], |
| 229 | typeFilter: -1, | |
| 229 | typeFilter: null, | |
| 230 | 230 | }, |
| 231 | 231 | ], |
| 232 | 232 | foundElems: 1, |
tests/rustdoc-js-std/parser-errors.js+9-9| ... | ... | @@ -406,10 +406,10 @@ const PARSED = [ |
| 406 | 406 | pathWithoutLast: [], |
| 407 | 407 | pathLast: "x", |
| 408 | 408 | generics: [], |
| 409 | typeFilter: -1, | |
| 409 | typeFilter: null, | |
| 410 | 410 | }, |
| 411 | 411 | ], |
| 412 | typeFilter: -1, | |
| 412 | typeFilter: null, | |
| 413 | 413 | }, |
| 414 | 414 | { |
| 415 | 415 | name: "y", |
| ... | ... | @@ -417,7 +417,7 @@ const PARSED = [ |
| 417 | 417 | pathWithoutLast: [], |
| 418 | 418 | pathLast: "y", |
| 419 | 419 | generics: [], |
| 420 | typeFilter: -1, | |
| 420 | typeFilter: null, | |
| 421 | 421 | }, |
| 422 | 422 | ], |
| 423 | 423 | foundElems: 2, |
| ... | ... | @@ -440,7 +440,7 @@ const PARSED = [ |
| 440 | 440 | pathWithoutLast: [], |
| 441 | 441 | pathLast: "x", |
| 442 | 442 | generics: [], |
| 443 | typeFilter: -1, | |
| 443 | typeFilter: null, | |
| 444 | 444 | }, |
| 445 | 445 | { |
| 446 | 446 | name: "y", |
| ... | ... | @@ -448,10 +448,10 @@ const PARSED = [ |
| 448 | 448 | pathWithoutLast: [], |
| 449 | 449 | pathLast: "y", |
| 450 | 450 | generics: [], |
| 451 | typeFilter: -1, | |
| 451 | typeFilter: null, | |
| 452 | 452 | }, |
| 453 | 453 | ], |
| 454 | typeFilter: -1, | |
| 454 | typeFilter: null, | |
| 455 | 455 | }, |
| 456 | 456 | ], |
| 457 | 457 | foundElems: 1, |
| ... | ... | @@ -468,7 +468,7 @@ const PARSED = [ |
| 468 | 468 | pathWithoutLast: [], |
| 469 | 469 | pathLast: "p", |
| 470 | 470 | generics: [], |
| 471 | typeFilter: -1, | |
| 471 | typeFilter: null, | |
| 472 | 472 | }, |
| 473 | 473 | { |
| 474 | 474 | name: "x", |
| ... | ... | @@ -476,7 +476,7 @@ const PARSED = [ |
| 476 | 476 | pathWithoutLast: [], |
| 477 | 477 | pathLast: "x", |
| 478 | 478 | generics: [], |
| 479 | typeFilter: -1, | |
| 479 | typeFilter: null, | |
| 480 | 480 | }, |
| 481 | 481 | { |
| 482 | 482 | name: "y", |
| ... | ... | @@ -484,7 +484,7 @@ const PARSED = [ |
| 484 | 484 | pathWithoutLast: [], |
| 485 | 485 | pathLast: "y", |
| 486 | 486 | generics: [], |
| 487 | typeFilter: -1, | |
| 487 | typeFilter: null, | |
| 488 | 488 | }, |
| 489 | 489 | ], |
| 490 | 490 | foundElems: 3, |
tests/rustdoc-js-std/parser-filter.js+11-11| ... | ... | @@ -7,7 +7,7 @@ const PARSED = [ |
| 7 | 7 | pathWithoutLast: [], |
| 8 | 8 | pathLast: "foo", |
| 9 | 9 | generics: [], |
| 10 | typeFilter: 7, | |
| 10 | typeFilter: "fn", | |
| 11 | 11 | }], |
| 12 | 12 | foundElems: 1, |
| 13 | 13 | userQuery: "fn:foo", |
| ... | ... | @@ -22,7 +22,7 @@ const PARSED = [ |
| 22 | 22 | pathWithoutLast: [], |
| 23 | 23 | pathLast: "foo", |
| 24 | 24 | generics: [], |
| 25 | typeFilter: 6, | |
| 25 | typeFilter: "enum", | |
| 26 | 26 | }], |
| 27 | 27 | foundElems: 1, |
| 28 | 28 | userQuery: "enum : foo", |
| ... | ... | @@ -45,7 +45,7 @@ const PARSED = [ |
| 45 | 45 | pathWithoutLast: [], |
| 46 | 46 | pathLast: "macro", |
| 47 | 47 | generics: [], |
| 48 | typeFilter: 16, | |
| 48 | typeFilter: "macro", | |
| 49 | 49 | }], |
| 50 | 50 | foundElems: 1, |
| 51 | 51 | userQuery: "macro!", |
| ... | ... | @@ -60,7 +60,7 @@ const PARSED = [ |
| 60 | 60 | pathWithoutLast: [], |
| 61 | 61 | pathLast: "mac", |
| 62 | 62 | generics: [], |
| 63 | typeFilter: 16, | |
| 63 | typeFilter: "macro", | |
| 64 | 64 | }], |
| 65 | 65 | foundElems: 1, |
| 66 | 66 | userQuery: "macro:mac!", |
| ... | ... | @@ -75,7 +75,7 @@ const PARSED = [ |
| 75 | 75 | pathWithoutLast: ["a"], |
| 76 | 76 | pathLast: "mac", |
| 77 | 77 | generics: [], |
| 78 | typeFilter: 16, | |
| 78 | typeFilter: "macro", | |
| 79 | 79 | }], |
| 80 | 80 | foundElems: 1, |
| 81 | 81 | userQuery: "a::mac!", |
| ... | ... | @@ -93,7 +93,7 @@ const PARSED = [ |
| 93 | 93 | pathWithoutLast: [], |
| 94 | 94 | pathLast: "foo", |
| 95 | 95 | generics: [], |
| 96 | typeFilter: 7, | |
| 96 | typeFilter: "fn", | |
| 97 | 97 | }], |
| 98 | 98 | error: null, |
| 99 | 99 | }, |
| ... | ... | @@ -114,10 +114,10 @@ const PARSED = [ |
| 114 | 114 | pathWithoutLast: [], |
| 115 | 115 | pathLast: "bar", |
| 116 | 116 | generics: [], |
| 117 | typeFilter: 7, | |
| 117 | typeFilter: "fn", | |
| 118 | 118 | } |
| 119 | 119 | ], |
| 120 | typeFilter: 7, | |
| 120 | typeFilter: "fn", | |
| 121 | 121 | }], |
| 122 | 122 | error: null, |
| 123 | 123 | }, |
| ... | ... | @@ -138,7 +138,7 @@ const PARSED = [ |
| 138 | 138 | pathWithoutLast: [], |
| 139 | 139 | pathLast: "bar", |
| 140 | 140 | generics: [], |
| 141 | typeFilter: 7, | |
| 141 | typeFilter: "fn", | |
| 142 | 142 | }, |
| 143 | 143 | { |
| 144 | 144 | name: "baz::fuzz", |
| ... | ... | @@ -146,10 +146,10 @@ const PARSED = [ |
| 146 | 146 | pathWithoutLast: ["baz"], |
| 147 | 147 | pathLast: "fuzz", |
| 148 | 148 | generics: [], |
| 149 | typeFilter: 6, | |
| 149 | typeFilter: "enum", | |
| 150 | 150 | }, |
| 151 | 151 | ], |
| 152 | typeFilter: 7, | |
| 152 | typeFilter: "fn", | |
| 153 | 153 | }], |
| 154 | 154 | error: null, |
| 155 | 155 | }, |
tests/rustdoc-js-std/parser-generics.js+6-6| ... | ... | @@ -16,7 +16,7 @@ const PARSED = [ |
| 16 | 16 | pathWithoutLast: [], |
| 17 | 17 | pathLast: "p", |
| 18 | 18 | generics: [], |
| 19 | typeFilter: -1, | |
| 19 | typeFilter: null, | |
| 20 | 20 | }, |
| 21 | 21 | { |
| 22 | 22 | name: "u8", |
| ... | ... | @@ -24,7 +24,7 @@ const PARSED = [ |
| 24 | 24 | pathWithoutLast: [], |
| 25 | 25 | pathLast: "u8", |
| 26 | 26 | generics: [], |
| 27 | typeFilter: -1, | |
| 27 | typeFilter: null, | |
| 28 | 28 | }, |
| 29 | 29 | ], |
| 30 | 30 | foundElems: 2, |
| ... | ... | @@ -49,7 +49,7 @@ const PARSED = [ |
| 49 | 49 | generics: [], |
| 50 | 50 | }, |
| 51 | 51 | ], |
| 52 | typeFilter: -1, | |
| 52 | typeFilter: null, | |
| 53 | 53 | }, |
| 54 | 54 | ], |
| 55 | 55 | foundElems: 1, |
| ... | ... | @@ -82,7 +82,7 @@ const PARSED = [ |
| 82 | 82 | ], |
| 83 | 83 | }, |
| 84 | 84 | ], |
| 85 | typeFilter: -1, | |
| 85 | typeFilter: null, | |
| 86 | 86 | }, |
| 87 | 87 | ], |
| 88 | 88 | foundElems: 1, |
| ... | ... | @@ -122,7 +122,7 @@ const PARSED = [ |
| 122 | 122 | generics: [], |
| 123 | 123 | }, |
| 124 | 124 | ], |
| 125 | typeFilter: -1, | |
| 125 | typeFilter: null, | |
| 126 | 126 | }, |
| 127 | 127 | ], |
| 128 | 128 | foundElems: 1, |
| ... | ... | @@ -162,7 +162,7 @@ const PARSED = [ |
| 162 | 162 | ], |
| 163 | 163 | }, |
| 164 | 164 | ], |
| 165 | typeFilter: -1, | |
| 165 | typeFilter: null, | |
| 166 | 166 | }, |
| 167 | 167 | ], |
| 168 | 168 | foundElems: 1, |
tests/rustdoc-js-std/parser-hof.js+53-53| ... | ... | @@ -25,11 +25,11 @@ const PARSED = [ |
| 25 | 25 | generics: [], |
| 26 | 26 | }, |
| 27 | 27 | ], |
| 28 | typeFilter: -1, | |
| 28 | typeFilter: null, | |
| 29 | 29 | }], |
| 30 | 30 | ], |
| 31 | 31 | ], |
| 32 | typeFilter: -1, | |
| 32 | typeFilter: null, | |
| 33 | 33 | }], |
| 34 | 34 | foundElems: 1, |
| 35 | 35 | userQuery: "(-> F<P>)", |
| ... | ... | @@ -53,11 +53,11 @@ const PARSED = [ |
| 53 | 53 | pathWithoutLast: [], |
| 54 | 54 | pathLast: "p", |
| 55 | 55 | generics: [], |
| 56 | typeFilter: -1, | |
| 56 | typeFilter: null, | |
| 57 | 57 | }], |
| 58 | 58 | ], |
| 59 | 59 | ], |
| 60 | typeFilter: -1, | |
| 60 | typeFilter: null, | |
| 61 | 61 | }], |
| 62 | 62 | foundElems: 1, |
| 63 | 63 | userQuery: "(-> P)", |
| ... | ... | @@ -81,11 +81,11 @@ const PARSED = [ |
| 81 | 81 | pathWithoutLast: [], |
| 82 | 82 | pathLast: "a", |
| 83 | 83 | generics: [], |
| 84 | typeFilter: -1, | |
| 84 | typeFilter: null, | |
| 85 | 85 | }], |
| 86 | 86 | ], |
| 87 | 87 | ], |
| 88 | typeFilter: -1, | |
| 88 | typeFilter: null, | |
| 89 | 89 | }], |
| 90 | 90 | foundElems: 1, |
| 91 | 91 | userQuery: "(->,a)", |
| ... | ... | @@ -113,7 +113,7 @@ const PARSED = [ |
| 113 | 113 | generics: [], |
| 114 | 114 | }, |
| 115 | 115 | ], |
| 116 | typeFilter: -1, | |
| 116 | typeFilter: null, | |
| 117 | 117 | }], |
| 118 | 118 | bindings: [ |
| 119 | 119 | [ |
| ... | ... | @@ -121,7 +121,7 @@ const PARSED = [ |
| 121 | 121 | [], |
| 122 | 122 | ], |
| 123 | 123 | ], |
| 124 | typeFilter: -1, | |
| 124 | typeFilter: null, | |
| 125 | 125 | }], |
| 126 | 126 | foundElems: 1, |
| 127 | 127 | userQuery: "(F<P> ->)", |
| ... | ... | @@ -141,7 +141,7 @@ const PARSED = [ |
| 141 | 141 | pathWithoutLast: [], |
| 142 | 142 | pathLast: "p", |
| 143 | 143 | generics: [], |
| 144 | typeFilter: -1, | |
| 144 | typeFilter: null, | |
| 145 | 145 | }], |
| 146 | 146 | bindings: [ |
| 147 | 147 | [ |
| ... | ... | @@ -149,7 +149,7 @@ const PARSED = [ |
| 149 | 149 | [], |
| 150 | 150 | ], |
| 151 | 151 | ], |
| 152 | typeFilter: -1, | |
| 152 | typeFilter: null, | |
| 153 | 153 | }], |
| 154 | 154 | foundElems: 1, |
| 155 | 155 | userQuery: "(P ->)", |
| ... | ... | @@ -169,7 +169,7 @@ const PARSED = [ |
| 169 | 169 | pathWithoutLast: [], |
| 170 | 170 | pathLast: "a", |
| 171 | 171 | generics: [], |
| 172 | typeFilter: -1, | |
| 172 | typeFilter: null, | |
| 173 | 173 | }], |
| 174 | 174 | bindings: [ |
| 175 | 175 | [ |
| ... | ... | @@ -177,7 +177,7 @@ const PARSED = [ |
| 177 | 177 | [], |
| 178 | 178 | ], |
| 179 | 179 | ], |
| 180 | typeFilter: -1, | |
| 180 | typeFilter: null, | |
| 181 | 181 | }], |
| 182 | 182 | foundElems: 1, |
| 183 | 183 | userQuery: "(,a->)", |
| ... | ... | @@ -197,7 +197,7 @@ const PARSED = [ |
| 197 | 197 | pathWithoutLast: [], |
| 198 | 198 | pathLast: "aaaaa", |
| 199 | 199 | generics: [], |
| 200 | typeFilter: -1, | |
| 200 | typeFilter: null, | |
| 201 | 201 | }], |
| 202 | 202 | bindings: [ |
| 203 | 203 | [ |
| ... | ... | @@ -208,11 +208,11 @@ const PARSED = [ |
| 208 | 208 | pathWithoutLast: [], |
| 209 | 209 | pathLast: "a", |
| 210 | 210 | generics: [], |
| 211 | typeFilter: -1, | |
| 211 | typeFilter: null, | |
| 212 | 212 | }], |
| 213 | 213 | ], |
| 214 | 214 | ], |
| 215 | typeFilter: -1, | |
| 215 | typeFilter: null, | |
| 216 | 216 | }], |
| 217 | 217 | foundElems: 1, |
| 218 | 218 | userQuery: "(aaaaa->a)", |
| ... | ... | @@ -233,7 +233,7 @@ const PARSED = [ |
| 233 | 233 | pathWithoutLast: [], |
| 234 | 234 | pathLast: "aaaaa", |
| 235 | 235 | generics: [], |
| 236 | typeFilter: -1, | |
| 236 | typeFilter: null, | |
| 237 | 237 | }, |
| 238 | 238 | { |
| 239 | 239 | name: "b", |
| ... | ... | @@ -241,7 +241,7 @@ const PARSED = [ |
| 241 | 241 | pathWithoutLast: [], |
| 242 | 242 | pathLast: "b", |
| 243 | 243 | generics: [], |
| 244 | typeFilter: -1, | |
| 244 | typeFilter: null, | |
| 245 | 245 | }, |
| 246 | 246 | ], |
| 247 | 247 | bindings: [ |
| ... | ... | @@ -253,11 +253,11 @@ const PARSED = [ |
| 253 | 253 | pathWithoutLast: [], |
| 254 | 254 | pathLast: "a", |
| 255 | 255 | generics: [], |
| 256 | typeFilter: -1, | |
| 256 | typeFilter: null, | |
| 257 | 257 | }], |
| 258 | 258 | ], |
| 259 | 259 | ], |
| 260 | typeFilter: -1, | |
| 260 | typeFilter: null, | |
| 261 | 261 | }], |
| 262 | 262 | foundElems: 1, |
| 263 | 263 | userQuery: "(aaaaa, b -> a)", |
| ... | ... | @@ -278,7 +278,7 @@ const PARSED = [ |
| 278 | 278 | pathWithoutLast: [], |
| 279 | 279 | pathLast: "aaaaa", |
| 280 | 280 | generics: [], |
| 281 | typeFilter: -1, | |
| 281 | typeFilter: null, | |
| 282 | 282 | }, |
| 283 | 283 | { |
| 284 | 284 | name: "b", |
| ... | ... | @@ -286,7 +286,7 @@ const PARSED = [ |
| 286 | 286 | pathWithoutLast: [], |
| 287 | 287 | pathLast: "b", |
| 288 | 288 | generics: [], |
| 289 | typeFilter: -1, | |
| 289 | typeFilter: null, | |
| 290 | 290 | }, |
| 291 | 291 | ], |
| 292 | 292 | bindings: [ |
| ... | ... | @@ -298,11 +298,11 @@ const PARSED = [ |
| 298 | 298 | pathWithoutLast: [], |
| 299 | 299 | pathLast: "a", |
| 300 | 300 | generics: [], |
| 301 | typeFilter: -1, | |
| 301 | typeFilter: null, | |
| 302 | 302 | }], |
| 303 | 303 | ], |
| 304 | 304 | ], |
| 305 | typeFilter: 1, | |
| 305 | typeFilter: "primitive", | |
| 306 | 306 | }], |
| 307 | 307 | foundElems: 1, |
| 308 | 308 | userQuery: "primitive:(aaaaa, b -> a)", |
| ... | ... | @@ -318,7 +318,7 @@ const PARSED = [ |
| 318 | 318 | pathWithoutLast: [], |
| 319 | 319 | pathLast: "x", |
| 320 | 320 | generics: [], |
| 321 | typeFilter: -1, | |
| 321 | typeFilter: null, | |
| 322 | 322 | }, |
| 323 | 323 | { |
| 324 | 324 | name: "->", |
| ... | ... | @@ -332,7 +332,7 @@ const PARSED = [ |
| 332 | 332 | pathWithoutLast: [], |
| 333 | 333 | pathLast: "aaaaa", |
| 334 | 334 | generics: [], |
| 335 | typeFilter: -1, | |
| 335 | typeFilter: null, | |
| 336 | 336 | }, |
| 337 | 337 | { |
| 338 | 338 | name: "b", |
| ... | ... | @@ -340,7 +340,7 @@ const PARSED = [ |
| 340 | 340 | pathWithoutLast: [], |
| 341 | 341 | pathLast: "b", |
| 342 | 342 | generics: [], |
| 343 | typeFilter: -1, | |
| 343 | typeFilter: null, | |
| 344 | 344 | }, |
| 345 | 345 | ], |
| 346 | 346 | bindings: [ |
| ... | ... | @@ -352,11 +352,11 @@ const PARSED = [ |
| 352 | 352 | pathWithoutLast: [], |
| 353 | 353 | pathLast: "a", |
| 354 | 354 | generics: [], |
| 355 | typeFilter: -1, | |
| 355 | typeFilter: null, | |
| 356 | 356 | }], |
| 357 | 357 | ], |
| 358 | 358 | ], |
| 359 | typeFilter: 10, | |
| 359 | typeFilter: "trait", | |
| 360 | 360 | } |
| 361 | 361 | ], |
| 362 | 362 | foundElems: 2, |
| ... | ... | @@ -390,11 +390,11 @@ const PARSED = [ |
| 390 | 390 | generics: [], |
| 391 | 391 | }, |
| 392 | 392 | ], |
| 393 | typeFilter: -1, | |
| 393 | typeFilter: null, | |
| 394 | 394 | }], |
| 395 | 395 | ], |
| 396 | 396 | ], |
| 397 | typeFilter: -1, | |
| 397 | typeFilter: null, | |
| 398 | 398 | }], |
| 399 | 399 | foundElems: 1, |
| 400 | 400 | userQuery: "Fn () -> F<P>", |
| ... | ... | @@ -418,11 +418,11 @@ const PARSED = [ |
| 418 | 418 | pathWithoutLast: [], |
| 419 | 419 | pathLast: "p", |
| 420 | 420 | generics: [], |
| 421 | typeFilter: -1, | |
| 421 | typeFilter: null, | |
| 422 | 422 | }], |
| 423 | 423 | ], |
| 424 | 424 | ], |
| 425 | typeFilter: -1, | |
| 425 | typeFilter: null, | |
| 426 | 426 | }], |
| 427 | 427 | foundElems: 1, |
| 428 | 428 | userQuery: "FnMut() -> P", |
| ... | ... | @@ -446,11 +446,11 @@ const PARSED = [ |
| 446 | 446 | pathWithoutLast: [], |
| 447 | 447 | pathLast: "p", |
| 448 | 448 | generics: [], |
| 449 | typeFilter: -1, | |
| 449 | typeFilter: null, | |
| 450 | 450 | }], |
| 451 | 451 | ], |
| 452 | 452 | ], |
| 453 | typeFilter: -1, | |
| 453 | typeFilter: null, | |
| 454 | 454 | }], |
| 455 | 455 | foundElems: 1, |
| 456 | 456 | userQuery: "(FnMut() -> P)", |
| ... | ... | @@ -478,7 +478,7 @@ const PARSED = [ |
| 478 | 478 | generics: [], |
| 479 | 479 | }, |
| 480 | 480 | ], |
| 481 | typeFilter: -1, | |
| 481 | typeFilter: null, | |
| 482 | 482 | }], |
| 483 | 483 | bindings: [ |
| 484 | 484 | [ |
| ... | ... | @@ -486,7 +486,7 @@ const PARSED = [ |
| 486 | 486 | [], |
| 487 | 487 | ], |
| 488 | 488 | ], |
| 489 | typeFilter: -1, | |
| 489 | typeFilter: null, | |
| 490 | 490 | }], |
| 491 | 491 | foundElems: 1, |
| 492 | 492 | userQuery: "Fn(F<P>)", |
| ... | ... | @@ -507,7 +507,7 @@ const PARSED = [ |
| 507 | 507 | pathWithoutLast: [], |
| 508 | 508 | pathLast: "aaaaa", |
| 509 | 509 | generics: [], |
| 510 | typeFilter: -1, | |
| 510 | typeFilter: null, | |
| 511 | 511 | }, |
| 512 | 512 | { |
| 513 | 513 | name: "b", |
| ... | ... | @@ -515,7 +515,7 @@ const PARSED = [ |
| 515 | 515 | pathWithoutLast: [], |
| 516 | 516 | pathLast: "b", |
| 517 | 517 | generics: [], |
| 518 | typeFilter: -1, | |
| 518 | typeFilter: null, | |
| 519 | 519 | }, |
| 520 | 520 | ], |
| 521 | 521 | bindings: [ |
| ... | ... | @@ -527,11 +527,11 @@ const PARSED = [ |
| 527 | 527 | pathWithoutLast: [], |
| 528 | 528 | pathLast: "a", |
| 529 | 529 | generics: [], |
| 530 | typeFilter: -1, | |
| 530 | typeFilter: null, | |
| 531 | 531 | }], |
| 532 | 532 | ], |
| 533 | 533 | ], |
| 534 | typeFilter: 1, | |
| 534 | typeFilter: "primitive", | |
| 535 | 535 | }], |
| 536 | 536 | foundElems: 1, |
| 537 | 537 | userQuery: "primitive:fnonce(aaaaa, b) -> a", |
| ... | ... | @@ -552,7 +552,7 @@ const PARSED = [ |
| 552 | 552 | pathWithoutLast: [], |
| 553 | 553 | pathLast: "aaaaa", |
| 554 | 554 | generics: [], |
| 555 | typeFilter: -1, | |
| 555 | typeFilter: null, | |
| 556 | 556 | }, |
| 557 | 557 | { |
| 558 | 558 | name: "b", |
| ... | ... | @@ -560,7 +560,7 @@ const PARSED = [ |
| 560 | 560 | pathWithoutLast: [], |
| 561 | 561 | pathLast: "b", |
| 562 | 562 | generics: [], |
| 563 | typeFilter: 0, | |
| 563 | typeFilter: "keyword", | |
| 564 | 564 | }, |
| 565 | 565 | ], |
| 566 | 566 | bindings: [ |
| ... | ... | @@ -572,11 +572,11 @@ const PARSED = [ |
| 572 | 572 | pathWithoutLast: [], |
| 573 | 573 | pathLast: "a", |
| 574 | 574 | generics: [], |
| 575 | typeFilter: 10, | |
| 575 | typeFilter: "trait", | |
| 576 | 576 | }], |
| 577 | 577 | ], |
| 578 | 578 | ], |
| 579 | typeFilter: 1, | |
| 579 | typeFilter: "primitive", | |
| 580 | 580 | }], |
| 581 | 581 | foundElems: 1, |
| 582 | 582 | userQuery: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", |
| ... | ... | @@ -592,7 +592,7 @@ const PARSED = [ |
| 592 | 592 | pathWithoutLast: [], |
| 593 | 593 | pathLast: "x", |
| 594 | 594 | generics: [], |
| 595 | typeFilter: -1, | |
| 595 | typeFilter: null, | |
| 596 | 596 | }, |
| 597 | 597 | { |
| 598 | 598 | name: "fn", |
| ... | ... | @@ -612,7 +612,7 @@ const PARSED = [ |
| 612 | 612 | pathWithoutLast: [], |
| 613 | 613 | pathLast: "aaaaa", |
| 614 | 614 | generics: [], |
| 615 | typeFilter: -1, | |
| 615 | typeFilter: null, | |
| 616 | 616 | }, |
| 617 | 617 | { |
| 618 | 618 | name: "b", |
| ... | ... | @@ -620,7 +620,7 @@ const PARSED = [ |
| 620 | 620 | pathWithoutLast: [], |
| 621 | 621 | pathLast: "b", |
| 622 | 622 | generics: [], |
| 623 | typeFilter: -1, | |
| 623 | typeFilter: null, | |
| 624 | 624 | }, |
| 625 | 625 | ], |
| 626 | 626 | bindings: [ |
| ... | ... | @@ -632,11 +632,11 @@ const PARSED = [ |
| 632 | 632 | pathWithoutLast: [], |
| 633 | 633 | pathLast: "a", |
| 634 | 634 | generics: [], |
| 635 | typeFilter: -1, | |
| 635 | typeFilter: null, | |
| 636 | 636 | }], |
| 637 | 637 | ], |
| 638 | 638 | ], |
| 639 | typeFilter: -1, | |
| 639 | typeFilter: null, | |
| 640 | 640 | }, |
| 641 | 641 | ], |
| 642 | 642 | bindings: [ |
| ... | ... | @@ -645,7 +645,7 @@ const PARSED = [ |
| 645 | 645 | [], |
| 646 | 646 | ] |
| 647 | 647 | ], |
| 648 | typeFilter: 10, | |
| 648 | typeFilter: "trait", | |
| 649 | 649 | } |
| 650 | 650 | ], |
| 651 | 651 | foundElems: 2, |
| ... | ... | @@ -662,7 +662,7 @@ const PARSED = [ |
| 662 | 662 | pathWithoutLast: [], |
| 663 | 663 | pathLast: "a", |
| 664 | 664 | generics: [], |
| 665 | typeFilter: -1, | |
| 665 | typeFilter: null, | |
| 666 | 666 | }, |
| 667 | 667 | { |
| 668 | 668 | name: "b", |
| ... | ... | @@ -675,7 +675,7 @@ const PARSED = [ |
| 675 | 675 | pathWithoutLast: [], |
| 676 | 676 | pathLast: "c", |
| 677 | 677 | generics: [], |
| 678 | typeFilter: -1, | |
| 678 | typeFilter: null, | |
| 679 | 679 | }], |
| 680 | 680 | bindings: [ |
| 681 | 681 | [ |
| ... | ... | @@ -683,7 +683,7 @@ const PARSED = [ |
| 683 | 683 | [], |
| 684 | 684 | ] |
| 685 | 685 | ], |
| 686 | typeFilter: -1, | |
| 686 | typeFilter: null, | |
| 687 | 687 | } |
| 688 | 688 | ], |
| 689 | 689 | foundElems: 2, |
tests/rustdoc-js-std/parser-ident.js+7-7| ... | ... | @@ -13,10 +13,10 @@ const PARSED = [ |
| 13 | 13 | pathWithoutLast: [], |
| 14 | 14 | pathLast: "never", |
| 15 | 15 | generics: [], |
| 16 | typeFilter: 1, | |
| 16 | typeFilter: "primitive", | |
| 17 | 17 | }, |
| 18 | 18 | ], |
| 19 | typeFilter: -1, | |
| 19 | typeFilter: null, | |
| 20 | 20 | }], |
| 21 | 21 | foundElems: 1, |
| 22 | 22 | userQuery: "R<!>", |
| ... | ... | @@ -31,7 +31,7 @@ const PARSED = [ |
| 31 | 31 | pathWithoutLast: [], |
| 32 | 32 | pathLast: "never", |
| 33 | 33 | generics: [], |
| 34 | typeFilter: 1, | |
| 34 | typeFilter: "primitive", | |
| 35 | 35 | }], |
| 36 | 36 | foundElems: 1, |
| 37 | 37 | userQuery: "!", |
| ... | ... | @@ -46,7 +46,7 @@ const PARSED = [ |
| 46 | 46 | pathWithoutLast: [], |
| 47 | 47 | pathLast: "a", |
| 48 | 48 | generics: [], |
| 49 | typeFilter: 16, | |
| 49 | typeFilter: "macro", | |
| 50 | 50 | }], |
| 51 | 51 | foundElems: 1, |
| 52 | 52 | userQuery: "a!", |
| ... | ... | @@ -77,7 +77,7 @@ const PARSED = [ |
| 77 | 77 | pathWithoutLast: ["never"], |
| 78 | 78 | pathLast: "b", |
| 79 | 79 | generics: [], |
| 80 | typeFilter: -1, | |
| 80 | typeFilter: null, | |
| 81 | 81 | }], |
| 82 | 82 | foundElems: 1, |
| 83 | 83 | userQuery: "!::b", |
| ... | ... | @@ -122,10 +122,10 @@ const PARSED = [ |
| 122 | 122 | pathWithoutLast: [], |
| 123 | 123 | pathLast: "t", |
| 124 | 124 | generics: [], |
| 125 | typeFilter: -1, | |
| 125 | typeFilter: null, | |
| 126 | 126 | } |
| 127 | 127 | ], |
| 128 | typeFilter: -1, | |
| 128 | typeFilter: null, | |
| 129 | 129 | }], |
| 130 | 130 | foundElems: 1, |
| 131 | 131 | userQuery: "!::b<T>", |
tests/rustdoc-js-std/parser-literal.js+1-1| ... | ... | @@ -15,7 +15,7 @@ const PARSED = [ |
| 15 | 15 | generics: [], |
| 16 | 16 | }, |
| 17 | 17 | ], |
| 18 | typeFilter: -1, | |
| 18 | typeFilter: null, | |
| 19 | 19 | }], |
| 20 | 20 | foundElems: 1, |
| 21 | 21 | userQuery: "R<P>", |
tests/rustdoc-js-std/parser-paths.js+9-9| ... | ... | @@ -7,7 +7,7 @@ const PARSED = [ |
| 7 | 7 | pathWithoutLast: ["a"], |
| 8 | 8 | pathLast: "b", |
| 9 | 9 | generics: [], |
| 10 | typeFilter: -1, | |
| 10 | typeFilter: null, | |
| 11 | 11 | }], |
| 12 | 12 | foundElems: 1, |
| 13 | 13 | userQuery: "A::B", |
| ... | ... | @@ -22,7 +22,7 @@ const PARSED = [ |
| 22 | 22 | pathWithoutLast: ["a"], |
| 23 | 23 | pathLast: "a", |
| 24 | 24 | generics: [], |
| 25 | typeFilter: -1, | |
| 25 | typeFilter: null, | |
| 26 | 26 | }], |
| 27 | 27 | foundElems: 1, |
| 28 | 28 | userQuery: 'a:: a', |
| ... | ... | @@ -37,7 +37,7 @@ const PARSED = [ |
| 37 | 37 | pathWithoutLast: ["a"], |
| 38 | 38 | pathLast: "a", |
| 39 | 39 | generics: [], |
| 40 | typeFilter: -1, | |
| 40 | typeFilter: null, | |
| 41 | 41 | }], |
| 42 | 42 | foundElems: 1, |
| 43 | 43 | userQuery: 'a ::a', |
| ... | ... | @@ -52,7 +52,7 @@ const PARSED = [ |
| 52 | 52 | pathWithoutLast: ["a"], |
| 53 | 53 | pathLast: "a", |
| 54 | 54 | generics: [], |
| 55 | typeFilter: -1, | |
| 55 | typeFilter: null, | |
| 56 | 56 | }], |
| 57 | 57 | foundElems: 1, |
| 58 | 58 | userQuery: 'a :: a', |
| ... | ... | @@ -68,7 +68,7 @@ const PARSED = [ |
| 68 | 68 | pathWithoutLast: ["a"], |
| 69 | 69 | pathLast: "b", |
| 70 | 70 | generics: [], |
| 71 | typeFilter: -1, | |
| 71 | typeFilter: null, | |
| 72 | 72 | }, |
| 73 | 73 | { |
| 74 | 74 | name: "C", |
| ... | ... | @@ -76,7 +76,7 @@ const PARSED = [ |
| 76 | 76 | pathWithoutLast: [], |
| 77 | 77 | pathLast: "c", |
| 78 | 78 | generics: [], |
| 79 | typeFilter: -1, | |
| 79 | typeFilter: null, | |
| 80 | 80 | }, |
| 81 | 81 | ], |
| 82 | 82 | foundElems: 2, |
| ... | ... | @@ -101,7 +101,7 @@ const PARSED = [ |
| 101 | 101 | generics: [], |
| 102 | 102 | }, |
| 103 | 103 | ], |
| 104 | typeFilter: -1, | |
| 104 | typeFilter: null, | |
| 105 | 105 | }, |
| 106 | 106 | { |
| 107 | 107 | name: "C", |
| ... | ... | @@ -109,7 +109,7 @@ const PARSED = [ |
| 109 | 109 | pathWithoutLast: [], |
| 110 | 110 | pathLast: "c", |
| 111 | 111 | generics: [], |
| 112 | typeFilter: -1, | |
| 112 | typeFilter: null, | |
| 113 | 113 | }, |
| 114 | 114 | ], |
| 115 | 115 | foundElems: 2, |
| ... | ... | @@ -125,7 +125,7 @@ const PARSED = [ |
| 125 | 125 | pathWithoutLast: ["mod"], |
| 126 | 126 | pathLast: "a", |
| 127 | 127 | generics: [], |
| 128 | typeFilter: -1, | |
| 128 | typeFilter: null, | |
| 129 | 129 | }], |
| 130 | 130 | foundElems: 1, |
| 131 | 131 | userQuery: "mod::a", |
tests/rustdoc-js-std/parser-quote.js+2-2| ... | ... | @@ -10,7 +10,7 @@ const PARSED = [ |
| 10 | 10 | pathWithoutLast: [], |
| 11 | 11 | pathLast: "p", |
| 12 | 12 | generics: [], |
| 13 | typeFilter: -1, | |
| 13 | typeFilter: null, | |
| 14 | 14 | }], |
| 15 | 15 | error: null, |
| 16 | 16 | }, |
| ... | ... | @@ -22,7 +22,7 @@ const PARSED = [ |
| 22 | 22 | pathWithoutLast: [], |
| 23 | 23 | pathLast: "p", |
| 24 | 24 | generics: [], |
| 25 | typeFilter: -1, | |
| 25 | typeFilter: null, | |
| 26 | 26 | }], |
| 27 | 27 | foundElems: 1, |
| 28 | 28 | userQuery: '"p",', |
tests/rustdoc-js-std/parser-reference.js+42-42| ... | ... | @@ -42,16 +42,16 @@ const PARSED = [ |
| 42 | 42 | pathWithoutLast: [], |
| 43 | 43 | pathLast: "d", |
| 44 | 44 | generics: [], |
| 45 | typeFilter: -1, | |
| 45 | typeFilter: null, | |
| 46 | 46 | }, |
| 47 | 47 | ], |
| 48 | typeFilter: 1, | |
| 48 | typeFilter: "primitive", | |
| 49 | 49 | }, |
| 50 | 50 | ], |
| 51 | typeFilter: 1, | |
| 51 | typeFilter: "primitive", | |
| 52 | 52 | }, |
| 53 | 53 | ], |
| 54 | typeFilter: 1, | |
| 54 | typeFilter: "primitive", | |
| 55 | 55 | }, |
| 56 | 56 | { |
| 57 | 57 | name: "[]", |
| ... | ... | @@ -59,7 +59,7 @@ const PARSED = [ |
| 59 | 59 | pathWithoutLast: [], |
| 60 | 60 | pathLast: "[]", |
| 61 | 61 | generics: [], |
| 62 | typeFilter: 1, | |
| 62 | typeFilter: "primitive", | |
| 63 | 63 | }, |
| 64 | 64 | ], |
| 65 | 65 | foundElems: 2, |
| ... | ... | @@ -100,19 +100,19 @@ const PARSED = [ |
| 100 | 100 | pathWithoutLast: [], |
| 101 | 101 | pathLast: "d", |
| 102 | 102 | generics: [], |
| 103 | typeFilter: -1, | |
| 103 | typeFilter: null, | |
| 104 | 104 | }, |
| 105 | 105 | ], |
| 106 | typeFilter: 1, | |
| 106 | typeFilter: "primitive", | |
| 107 | 107 | }, |
| 108 | 108 | ], |
| 109 | typeFilter: 1, | |
| 109 | typeFilter: "primitive", | |
| 110 | 110 | }, |
| 111 | 111 | ], |
| 112 | typeFilter: 1, | |
| 112 | typeFilter: "primitive", | |
| 113 | 113 | }, |
| 114 | 114 | ], |
| 115 | typeFilter: 1, | |
| 115 | typeFilter: "primitive", | |
| 116 | 116 | }, |
| 117 | 117 | ], |
| 118 | 118 | foundElems: 1, |
| ... | ... | @@ -129,7 +129,7 @@ const PARSED = [ |
| 129 | 129 | pathWithoutLast: [], |
| 130 | 130 | pathLast: "reference", |
| 131 | 131 | generics: [], |
| 132 | typeFilter: 1, | |
| 132 | typeFilter: "primitive", | |
| 133 | 133 | }, |
| 134 | 134 | ], |
| 135 | 135 | foundElems: 1, |
| ... | ... | @@ -152,10 +152,10 @@ const PARSED = [ |
| 152 | 152 | pathWithoutLast: [], |
| 153 | 153 | pathLast: "mut", |
| 154 | 154 | generics: [], |
| 155 | typeFilter: 0, | |
| 155 | typeFilter: "keyword", | |
| 156 | 156 | }, |
| 157 | 157 | ], |
| 158 | typeFilter: 1, | |
| 158 | typeFilter: "primitive", | |
| 159 | 159 | }, |
| 160 | 160 | ], |
| 161 | 161 | foundElems: 1, |
| ... | ... | @@ -172,7 +172,7 @@ const PARSED = [ |
| 172 | 172 | pathWithoutLast: [], |
| 173 | 173 | pathLast: "reference", |
| 174 | 174 | generics: [], |
| 175 | typeFilter: 1, | |
| 175 | typeFilter: "primitive", | |
| 176 | 176 | }, |
| 177 | 177 | { |
| 178 | 178 | name: "u8", |
| ... | ... | @@ -180,7 +180,7 @@ const PARSED = [ |
| 180 | 180 | pathWithoutLast: [], |
| 181 | 181 | pathLast: "u8", |
| 182 | 182 | generics: [], |
| 183 | typeFilter: -1, | |
| 183 | typeFilter: null, | |
| 184 | 184 | }, |
| 185 | 185 | ], |
| 186 | 186 | foundElems: 2, |
| ... | ... | @@ -203,10 +203,10 @@ const PARSED = [ |
| 203 | 203 | pathWithoutLast: [], |
| 204 | 204 | pathLast: "mut", |
| 205 | 205 | generics: [], |
| 206 | typeFilter: 0, | |
| 206 | typeFilter: "keyword", | |
| 207 | 207 | }, |
| 208 | 208 | ], |
| 209 | typeFilter: 1, | |
| 209 | typeFilter: "primitive", | |
| 210 | 210 | }, |
| 211 | 211 | { |
| 212 | 212 | name: "u8", |
| ... | ... | @@ -214,7 +214,7 @@ const PARSED = [ |
| 214 | 214 | pathWithoutLast: [], |
| 215 | 215 | pathLast: "u8", |
| 216 | 216 | generics: [], |
| 217 | typeFilter: -1, | |
| 217 | typeFilter: null, | |
| 218 | 218 | }, |
| 219 | 219 | ], |
| 220 | 220 | foundElems: 2, |
| ... | ... | @@ -237,10 +237,10 @@ const PARSED = [ |
| 237 | 237 | pathWithoutLast: [], |
| 238 | 238 | pathLast: "u8", |
| 239 | 239 | generics: [], |
| 240 | typeFilter: -1, | |
| 240 | typeFilter: null, | |
| 241 | 241 | }, |
| 242 | 242 | ], |
| 243 | typeFilter: 1, | |
| 243 | typeFilter: "primitive", | |
| 244 | 244 | }, |
| 245 | 245 | ], |
| 246 | 246 | foundElems: 1, |
| ... | ... | @@ -269,13 +269,13 @@ const PARSED = [ |
| 269 | 269 | pathWithoutLast: [], |
| 270 | 270 | pathLast: "u8", |
| 271 | 271 | generics: [], |
| 272 | typeFilter: -1, | |
| 272 | typeFilter: null, | |
| 273 | 273 | }, |
| 274 | 274 | ], |
| 275 | typeFilter: -1, | |
| 275 | typeFilter: null, | |
| 276 | 276 | }, |
| 277 | 277 | ], |
| 278 | typeFilter: 1, | |
| 278 | typeFilter: "primitive", | |
| 279 | 279 | }, |
| 280 | 280 | ], |
| 281 | 281 | foundElems: 1, |
| ... | ... | @@ -304,13 +304,13 @@ const PARSED = [ |
| 304 | 304 | pathWithoutLast: [], |
| 305 | 305 | pathLast: "u8", |
| 306 | 306 | generics: [], |
| 307 | typeFilter: -1, | |
| 307 | typeFilter: null, | |
| 308 | 308 | }, |
| 309 | 309 | ], |
| 310 | typeFilter: 1, | |
| 310 | typeFilter: "primitive", | |
| 311 | 311 | }, |
| 312 | 312 | ], |
| 313 | typeFilter: -1, | |
| 313 | typeFilter: null, | |
| 314 | 314 | }, |
| 315 | 315 | ], |
| 316 | 316 | foundElems: 1, |
| ... | ... | @@ -339,10 +339,10 @@ const PARSED = [ |
| 339 | 339 | pathWithoutLast: [], |
| 340 | 340 | pathLast: "u8", |
| 341 | 341 | generics: [], |
| 342 | typeFilter: -1, | |
| 342 | typeFilter: null, | |
| 343 | 343 | }, |
| 344 | 344 | ], |
| 345 | typeFilter: 1, | |
| 345 | typeFilter: "primitive", | |
| 346 | 346 | }, |
| 347 | 347 | { |
| 348 | 348 | name: "u8", |
| ... | ... | @@ -350,10 +350,10 @@ const PARSED = [ |
| 350 | 350 | pathWithoutLast: [], |
| 351 | 351 | pathLast: "u8", |
| 352 | 352 | generics: [], |
| 353 | typeFilter: -1, | |
| 353 | typeFilter: null, | |
| 354 | 354 | }, |
| 355 | 355 | ], |
| 356 | typeFilter: -1, | |
| 356 | typeFilter: null, | |
| 357 | 357 | }, |
| 358 | 358 | ], |
| 359 | 359 | foundElems: 1, |
| ... | ... | @@ -382,13 +382,13 @@ const PARSED = [ |
| 382 | 382 | pathWithoutLast: [], |
| 383 | 383 | pathLast: "u8", |
| 384 | 384 | generics: [], |
| 385 | typeFilter: -1, | |
| 385 | typeFilter: null, | |
| 386 | 386 | }, |
| 387 | 387 | ], |
| 388 | typeFilter: 1, | |
| 388 | typeFilter: "primitive", | |
| 389 | 389 | }, |
| 390 | 390 | ], |
| 391 | typeFilter: -1, | |
| 391 | typeFilter: null, | |
| 392 | 392 | }, |
| 393 | 393 | ], |
| 394 | 394 | foundElems: 1, |
| ... | ... | @@ -417,7 +417,7 @@ const PARSED = [ |
| 417 | 417 | pathWithoutLast: [], |
| 418 | 418 | pathLast: "mut", |
| 419 | 419 | generics: [], |
| 420 | typeFilter: 0, | |
| 420 | typeFilter: "keyword", | |
| 421 | 421 | }, |
| 422 | 422 | { |
| 423 | 423 | name: "u8", |
| ... | ... | @@ -425,10 +425,10 @@ const PARSED = [ |
| 425 | 425 | pathWithoutLast: [], |
| 426 | 426 | pathLast: "u8", |
| 427 | 427 | generics: [], |
| 428 | typeFilter: -1, | |
| 428 | typeFilter: null, | |
| 429 | 429 | }, |
| 430 | 430 | ], |
| 431 | typeFilter: 1, | |
| 431 | typeFilter: "primitive", | |
| 432 | 432 | }, |
| 433 | 433 | { |
| 434 | 434 | name: "u8", |
| ... | ... | @@ -436,10 +436,10 @@ const PARSED = [ |
| 436 | 436 | pathWithoutLast: [], |
| 437 | 437 | pathLast: "u8", |
| 438 | 438 | generics: [], |
| 439 | typeFilter: -1, | |
| 439 | typeFilter: null, | |
| 440 | 440 | }, |
| 441 | 441 | ], |
| 442 | typeFilter: -1, | |
| 442 | typeFilter: null, | |
| 443 | 443 | }, |
| 444 | 444 | ], |
| 445 | 445 | foundElems: 1, |
| ... | ... | @@ -462,10 +462,10 @@ const PARSED = [ |
| 462 | 462 | pathWithoutLast: [], |
| 463 | 463 | pathLast: "u8", |
| 464 | 464 | generics: [], |
| 465 | typeFilter: -1, | |
| 465 | typeFilter: null, | |
| 466 | 466 | }, |
| 467 | 467 | ], |
| 468 | typeFilter: 1, | |
| 468 | typeFilter: "primitive", | |
| 469 | 469 | }, |
| 470 | 470 | ], |
| 471 | 471 | foundElems: 1, |
| ... | ... | @@ -496,10 +496,10 @@ const PARSED = [ |
| 496 | 496 | pathWithoutLast: [], |
| 497 | 497 | pathLast: "u8", |
| 498 | 498 | generics: [], |
| 499 | typeFilter: 16, | |
| 499 | typeFilter: "macro", | |
| 500 | 500 | }, |
| 501 | 501 | ], |
| 502 | typeFilter: 1, | |
| 502 | typeFilter: "primitive", | |
| 503 | 503 | }, |
| 504 | 504 | ], |
| 505 | 505 | foundElems: 1, |
tests/rustdoc-js-std/parser-returned.js+10-10| ... | ... | @@ -18,7 +18,7 @@ const PARSED = [ |
| 18 | 18 | generics: [], |
| 19 | 19 | }, |
| 20 | 20 | ], |
| 21 | typeFilter: -1, | |
| 21 | typeFilter: null, | |
| 22 | 22 | }], |
| 23 | 23 | error: null, |
| 24 | 24 | }, |
| ... | ... | @@ -33,7 +33,7 @@ const PARSED = [ |
| 33 | 33 | pathWithoutLast: [], |
| 34 | 34 | pathLast: "p", |
| 35 | 35 | generics: [], |
| 36 | typeFilter: -1, | |
| 36 | typeFilter: null, | |
| 37 | 37 | }], |
| 38 | 38 | error: null, |
| 39 | 39 | }, |
| ... | ... | @@ -48,7 +48,7 @@ const PARSED = [ |
| 48 | 48 | pathWithoutLast: [], |
| 49 | 49 | pathLast: "a", |
| 50 | 50 | generics: [], |
| 51 | typeFilter: -1, | |
| 51 | typeFilter: null, | |
| 52 | 52 | }], |
| 53 | 53 | error: null, |
| 54 | 54 | }, |
| ... | ... | @@ -60,7 +60,7 @@ const PARSED = [ |
| 60 | 60 | pathWithoutLast: [], |
| 61 | 61 | pathLast: "aaaaa", |
| 62 | 62 | generics: [], |
| 63 | typeFilter: -1, | |
| 63 | typeFilter: null, | |
| 64 | 64 | }], |
| 65 | 65 | foundElems: 2, |
| 66 | 66 | userQuery: "aaaaa->a", |
| ... | ... | @@ -70,7 +70,7 @@ const PARSED = [ |
| 70 | 70 | pathWithoutLast: [], |
| 71 | 71 | pathLast: "a", |
| 72 | 72 | generics: [], |
| 73 | typeFilter: -1, | |
| 73 | typeFilter: null, | |
| 74 | 74 | }], |
| 75 | 75 | error: null, |
| 76 | 76 | }, |
| ... | ... | @@ -85,7 +85,7 @@ const PARSED = [ |
| 85 | 85 | pathWithoutLast: [], |
| 86 | 86 | pathLast: "never", |
| 87 | 87 | generics: [], |
| 88 | typeFilter: 1, | |
| 88 | typeFilter: "primitive", | |
| 89 | 89 | }], |
| 90 | 90 | error: null, |
| 91 | 91 | }, |
| ... | ... | @@ -97,7 +97,7 @@ const PARSED = [ |
| 97 | 97 | pathWithoutLast: [], |
| 98 | 98 | pathLast: "a", |
| 99 | 99 | generics: [], |
| 100 | typeFilter: -1, | |
| 100 | typeFilter: null, | |
| 101 | 101 | }], |
| 102 | 102 | foundElems: 1, |
| 103 | 103 | userQuery: "a->", |
| ... | ... | @@ -113,7 +113,7 @@ const PARSED = [ |
| 113 | 113 | pathWithoutLast: [], |
| 114 | 114 | pathLast: "never", |
| 115 | 115 | generics: [], |
| 116 | typeFilter: 1, | |
| 116 | typeFilter: "primitive", | |
| 117 | 117 | }], |
| 118 | 118 | foundElems: 1, |
| 119 | 119 | userQuery: "!->", |
| ... | ... | @@ -129,7 +129,7 @@ const PARSED = [ |
| 129 | 129 | pathWithoutLast: [], |
| 130 | 130 | pathLast: "never", |
| 131 | 131 | generics: [], |
| 132 | typeFilter: 1, | |
| 132 | typeFilter: "primitive", | |
| 133 | 133 | }], |
| 134 | 134 | foundElems: 1, |
| 135 | 135 | userQuery: "! ->", |
| ... | ... | @@ -145,7 +145,7 @@ const PARSED = [ |
| 145 | 145 | pathWithoutLast: [], |
| 146 | 146 | pathLast: "never", |
| 147 | 147 | generics: [], |
| 148 | typeFilter: 1, | |
| 148 | typeFilter: "primitive", | |
| 149 | 149 | }], |
| 150 | 150 | foundElems: 1, |
| 151 | 151 | userQuery: "primitive:!->", |
tests/rustdoc-js-std/parser-separators.js+10-10| ... | ... | @@ -10,7 +10,7 @@ const PARSED = [ |
| 10 | 10 | pathWithoutLast: ['aaaaaa'], |
| 11 | 11 | pathLast: 'b', |
| 12 | 12 | generics: [], |
| 13 | typeFilter: -1, | |
| 13 | typeFilter: null, | |
| 14 | 14 | }, |
| 15 | 15 | ], |
| 16 | 16 | foundElems: 1, |
| ... | ... | @@ -27,7 +27,7 @@ const PARSED = [ |
| 27 | 27 | pathWithoutLast: [], |
| 28 | 28 | pathLast: 'aaaaaa', |
| 29 | 29 | generics: [], |
| 30 | typeFilter: -1, | |
| 30 | typeFilter: null, | |
| 31 | 31 | }, |
| 32 | 32 | { |
| 33 | 33 | name: 'b', |
| ... | ... | @@ -35,7 +35,7 @@ const PARSED = [ |
| 35 | 35 | pathWithoutLast: [], |
| 36 | 36 | pathLast: 'b', |
| 37 | 37 | generics: [], |
| 38 | typeFilter: -1, | |
| 38 | typeFilter: null, | |
| 39 | 39 | }, |
| 40 | 40 | ], |
| 41 | 41 | foundElems: 2, |
| ... | ... | @@ -52,7 +52,7 @@ const PARSED = [ |
| 52 | 52 | pathWithoutLast: ['a'], |
| 53 | 53 | pathLast: 'b', |
| 54 | 54 | generics: [], |
| 55 | typeFilter: -1, | |
| 55 | typeFilter: null, | |
| 56 | 56 | }, |
| 57 | 57 | ], |
| 58 | 58 | foundElems: 1, |
| ... | ... | @@ -69,7 +69,7 @@ const PARSED = [ |
| 69 | 69 | pathWithoutLast: [], |
| 70 | 70 | pathLast: 'a', |
| 71 | 71 | generics: [], |
| 72 | typeFilter: -1, | |
| 72 | typeFilter: null, | |
| 73 | 73 | }, |
| 74 | 74 | { |
| 75 | 75 | name: 'b', |
| ... | ... | @@ -77,7 +77,7 @@ const PARSED = [ |
| 77 | 77 | pathWithoutLast: [], |
| 78 | 78 | pathLast: 'b', |
| 79 | 79 | generics: [], |
| 80 | typeFilter: -1, | |
| 80 | typeFilter: null, | |
| 81 | 81 | }, |
| 82 | 82 | ], |
| 83 | 83 | foundElems: 2, |
| ... | ... | @@ -94,7 +94,7 @@ const PARSED = [ |
| 94 | 94 | pathWithoutLast: ['a'], |
| 95 | 95 | pathLast: 'b', |
| 96 | 96 | generics: [], |
| 97 | typeFilter: -1, | |
| 97 | typeFilter: null, | |
| 98 | 98 | }, |
| 99 | 99 | ], |
| 100 | 100 | foundElems: 1, |
| ... | ... | @@ -119,7 +119,7 @@ const PARSED = [ |
| 119 | 119 | generics: [], |
| 120 | 120 | }, |
| 121 | 121 | ], |
| 122 | typeFilter: -1, | |
| 122 | typeFilter: null, | |
| 123 | 123 | }, |
| 124 | 124 | ], |
| 125 | 125 | foundElems: 1, |
| ... | ... | @@ -151,7 +151,7 @@ const PARSED = [ |
| 151 | 151 | generics: [], |
| 152 | 152 | }, |
| 153 | 153 | ], |
| 154 | typeFilter: -1, | |
| 154 | typeFilter: null, | |
| 155 | 155 | }, |
| 156 | 156 | ], |
| 157 | 157 | foundElems: 1, |
| ... | ... | @@ -176,7 +176,7 @@ const PARSED = [ |
| 176 | 176 | generics: [], |
| 177 | 177 | }, |
| 178 | 178 | ], |
| 179 | typeFilter: -1, | |
| 179 | typeFilter: null, | |
| 180 | 180 | }, |
| 181 | 181 | ], |
| 182 | 182 | foundElems: 1, |
tests/rustdoc-js-std/parser-slice-array.js+18-18| ... | ... | @@ -34,7 +34,7 @@ const PARSED = [ |
| 34 | 34 | pathWithoutLast: [], |
| 35 | 35 | pathLast: "d", |
| 36 | 36 | generics: [], |
| 37 | typeFilter: -1, | |
| 37 | typeFilter: null, | |
| 38 | 38 | }, |
| 39 | 39 | { |
| 40 | 40 | name: "[]", |
| ... | ... | @@ -42,16 +42,16 @@ const PARSED = [ |
| 42 | 42 | pathWithoutLast: [], |
| 43 | 43 | pathLast: "[]", |
| 44 | 44 | generics: [], |
| 45 | typeFilter: 1, | |
| 45 | typeFilter: "primitive", | |
| 46 | 46 | }, |
| 47 | 47 | ], |
| 48 | typeFilter: 1, | |
| 48 | typeFilter: "primitive", | |
| 49 | 49 | }, |
| 50 | 50 | ], |
| 51 | typeFilter: 1, | |
| 51 | typeFilter: "primitive", | |
| 52 | 52 | }, |
| 53 | 53 | ], |
| 54 | typeFilter: 1, | |
| 54 | typeFilter: "primitive", | |
| 55 | 55 | }, |
| 56 | 56 | ], |
| 57 | 57 | foundElems: 1, |
| ... | ... | @@ -68,7 +68,7 @@ const PARSED = [ |
| 68 | 68 | pathWithoutLast: [], |
| 69 | 69 | pathLast: "[]", |
| 70 | 70 | generics: [], |
| 71 | typeFilter: 1, | |
| 71 | typeFilter: "primitive", | |
| 72 | 72 | }, |
| 73 | 73 | { |
| 74 | 74 | name: "u8", |
| ... | ... | @@ -76,7 +76,7 @@ const PARSED = [ |
| 76 | 76 | pathWithoutLast: [], |
| 77 | 77 | pathLast: "u8", |
| 78 | 78 | generics: [], |
| 79 | typeFilter: -1, | |
| 79 | typeFilter: null, | |
| 80 | 80 | }, |
| 81 | 81 | ], |
| 82 | 82 | foundElems: 2, |
| ... | ... | @@ -99,10 +99,10 @@ const PARSED = [ |
| 99 | 99 | pathWithoutLast: [], |
| 100 | 100 | pathLast: "u8", |
| 101 | 101 | generics: [], |
| 102 | typeFilter: -1, | |
| 102 | typeFilter: null, | |
| 103 | 103 | }, |
| 104 | 104 | ], |
| 105 | typeFilter: 1, | |
| 105 | typeFilter: "primitive", | |
| 106 | 106 | }, |
| 107 | 107 | ], |
| 108 | 108 | foundElems: 1, |
| ... | ... | @@ -125,7 +125,7 @@ const PARSED = [ |
| 125 | 125 | pathWithoutLast: [], |
| 126 | 126 | pathLast: "u8", |
| 127 | 127 | generics: [], |
| 128 | typeFilter: -1, | |
| 128 | typeFilter: null, | |
| 129 | 129 | }, |
| 130 | 130 | { |
| 131 | 131 | name: "u8", |
| ... | ... | @@ -133,10 +133,10 @@ const PARSED = [ |
| 133 | 133 | pathWithoutLast: [], |
| 134 | 134 | pathLast: "u8", |
| 135 | 135 | generics: [], |
| 136 | typeFilter: -1, | |
| 136 | typeFilter: null, | |
| 137 | 137 | }, |
| 138 | 138 | ], |
| 139 | typeFilter: 1, | |
| 139 | typeFilter: "primitive", | |
| 140 | 140 | }, |
| 141 | 141 | ], |
| 142 | 142 | foundElems: 1, |
| ... | ... | @@ -165,13 +165,13 @@ const PARSED = [ |
| 165 | 165 | pathWithoutLast: [], |
| 166 | 166 | pathLast: "u8", |
| 167 | 167 | generics: [], |
| 168 | typeFilter: -1, | |
| 168 | typeFilter: null, | |
| 169 | 169 | }, |
| 170 | 170 | ], |
| 171 | typeFilter: -1, | |
| 171 | typeFilter: null, | |
| 172 | 172 | }, |
| 173 | 173 | ], |
| 174 | typeFilter: 1, | |
| 174 | typeFilter: "primitive", | |
| 175 | 175 | }, |
| 176 | 176 | ], |
| 177 | 177 | foundElems: 1, |
| ... | ... | @@ -188,7 +188,7 @@ const PARSED = [ |
| 188 | 188 | pathWithoutLast: [], |
| 189 | 189 | pathLast: "[]", |
| 190 | 190 | generics: [], |
| 191 | typeFilter: 1, | |
| 191 | typeFilter: "primitive", | |
| 192 | 192 | }, |
| 193 | 193 | ], |
| 194 | 194 | foundElems: 1, |
| ... | ... | @@ -283,10 +283,10 @@ const PARSED = [ |
| 283 | 283 | pathWithoutLast: [], |
| 284 | 284 | pathLast: "u8", |
| 285 | 285 | generics: [], |
| 286 | typeFilter: -1, | |
| 286 | typeFilter: null, | |
| 287 | 287 | }, |
| 288 | 288 | ], |
| 289 | typeFilter: 1, | |
| 289 | typeFilter: "primitive", | |
| 290 | 290 | }, |
| 291 | 291 | ], |
| 292 | 292 | foundElems: 1, |
tests/rustdoc-js-std/parser-tuple.js+19-19| ... | ... | @@ -22,7 +22,7 @@ const PARSED = [ |
| 22 | 22 | pathWithoutLast: [], |
| 23 | 23 | pathLast: "d", |
| 24 | 24 | generics: [], |
| 25 | typeFilter: -1, | |
| 25 | typeFilter: null, | |
| 26 | 26 | }, |
| 27 | 27 | { |
| 28 | 28 | name: "()", |
| ... | ... | @@ -30,10 +30,10 @@ const PARSED = [ |
| 30 | 30 | pathWithoutLast: [], |
| 31 | 31 | pathLast: "()", |
| 32 | 32 | generics: [], |
| 33 | typeFilter: 1, | |
| 33 | typeFilter: "primitive", | |
| 34 | 34 | }, |
| 35 | 35 | ], |
| 36 | typeFilter: 1, | |
| 36 | typeFilter: "primitive", | |
| 37 | 37 | } |
| 38 | 38 | ], |
| 39 | 39 | foundElems: 1, |
| ... | ... | @@ -50,7 +50,7 @@ const PARSED = [ |
| 50 | 50 | pathWithoutLast: [], |
| 51 | 51 | pathLast: "()", |
| 52 | 52 | generics: [], |
| 53 | typeFilter: 1, | |
| 53 | typeFilter: "primitive", | |
| 54 | 54 | }, |
| 55 | 55 | { |
| 56 | 56 | name: "u8", |
| ... | ... | @@ -58,7 +58,7 @@ const PARSED = [ |
| 58 | 58 | pathWithoutLast: [], |
| 59 | 59 | pathLast: "u8", |
| 60 | 60 | generics: [], |
| 61 | typeFilter: -1, | |
| 61 | typeFilter: null, | |
| 62 | 62 | }, |
| 63 | 63 | ], |
| 64 | 64 | foundElems: 2, |
| ... | ... | @@ -81,7 +81,7 @@ const PARSED = [ |
| 81 | 81 | pathWithoutLast: [], |
| 82 | 82 | pathLast: "u8", |
| 83 | 83 | generics: [], |
| 84 | typeFilter: -1, | |
| 84 | typeFilter: null, | |
| 85 | 85 | }, |
| 86 | 86 | ], |
| 87 | 87 | foundElems: 1, |
| ... | ... | @@ -104,10 +104,10 @@ const PARSED = [ |
| 104 | 104 | pathWithoutLast: [], |
| 105 | 105 | pathLast: "u8", |
| 106 | 106 | generics: [], |
| 107 | typeFilter: -1, | |
| 107 | typeFilter: null, | |
| 108 | 108 | }, |
| 109 | 109 | ], |
| 110 | typeFilter: 1, | |
| 110 | typeFilter: "primitive", | |
| 111 | 111 | }, |
| 112 | 112 | ], |
| 113 | 113 | foundElems: 1, |
| ... | ... | @@ -130,10 +130,10 @@ const PARSED = [ |
| 130 | 130 | pathWithoutLast: [], |
| 131 | 131 | pathLast: "u8", |
| 132 | 132 | generics: [], |
| 133 | typeFilter: -1, | |
| 133 | typeFilter: null, | |
| 134 | 134 | }, |
| 135 | 135 | ], |
| 136 | typeFilter: 1, | |
| 136 | typeFilter: "primitive", | |
| 137 | 137 | }, |
| 138 | 138 | ], |
| 139 | 139 | foundElems: 1, |
| ... | ... | @@ -156,10 +156,10 @@ const PARSED = [ |
| 156 | 156 | pathWithoutLast: [], |
| 157 | 157 | pathLast: "u8", |
| 158 | 158 | generics: [], |
| 159 | typeFilter: -1, | |
| 159 | typeFilter: null, | |
| 160 | 160 | }, |
| 161 | 161 | ], |
| 162 | typeFilter: 1, | |
| 162 | typeFilter: "primitive", | |
| 163 | 163 | }, |
| 164 | 164 | ], |
| 165 | 165 | foundElems: 1, |
| ... | ... | @@ -176,7 +176,7 @@ const PARSED = [ |
| 176 | 176 | pathWithoutLast: [], |
| 177 | 177 | pathLast: "u8", |
| 178 | 178 | generics: [], |
| 179 | typeFilter: 1, | |
| 179 | typeFilter: "primitive", | |
| 180 | 180 | }, |
| 181 | 181 | ], |
| 182 | 182 | foundElems: 1, |
| ... | ... | @@ -199,7 +199,7 @@ const PARSED = [ |
| 199 | 199 | pathWithoutLast: [], |
| 200 | 200 | pathLast: "u8", |
| 201 | 201 | generics: [], |
| 202 | typeFilter: -1, | |
| 202 | typeFilter: null, | |
| 203 | 203 | }, |
| 204 | 204 | { |
| 205 | 205 | name: "u8", |
| ... | ... | @@ -207,10 +207,10 @@ const PARSED = [ |
| 207 | 207 | pathWithoutLast: [], |
| 208 | 208 | pathLast: "u8", |
| 209 | 209 | generics: [], |
| 210 | typeFilter: -1, | |
| 210 | typeFilter: null, | |
| 211 | 211 | }, |
| 212 | 212 | ], |
| 213 | typeFilter: 1, | |
| 213 | typeFilter: "primitive", | |
| 214 | 214 | }, |
| 215 | 215 | ], |
| 216 | 216 | foundElems: 1, |
| ... | ... | @@ -233,10 +233,10 @@ const PARSED = [ |
| 233 | 233 | pathWithoutLast: [], |
| 234 | 234 | pathLast: "u8", |
| 235 | 235 | generics: [], |
| 236 | typeFilter: -1, | |
| 236 | typeFilter: null, | |
| 237 | 237 | }, |
| 238 | 238 | ], |
| 239 | typeFilter: -1, | |
| 239 | typeFilter: null, | |
| 240 | 240 | }, |
| 241 | 241 | ], |
| 242 | 242 | foundElems: 1, |
| ... | ... | @@ -253,7 +253,7 @@ const PARSED = [ |
| 253 | 253 | pathWithoutLast: [], |
| 254 | 254 | pathLast: "()", |
| 255 | 255 | generics: [], |
| 256 | typeFilter: 1, | |
| 256 | typeFilter: "primitive", | |
| 257 | 257 | }, |
| 258 | 258 | ], |
| 259 | 259 | foundElems: 1, |
tests/rustdoc-js-std/path-end-empty.js+2-1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | const FILTER_CRATE = "std"; | |
| 1 | 2 | const EXPECTED = { |
| 2 | 3 | 'query': 'Option::', |
| 3 | 4 | 'others': [ |
| 4 | { 'path': 'std::option::Option', 'name': 'get_or_insert_default' }, | |
| 5 | { 'path': 'std::option::Option', 'name': 'eq' }, | |
| 5 | 6 | ], |
| 6 | 7 | } |
tests/rustdoc-js-std/path-maxeditdistance.js+3-3| ... | ... | @@ -10,15 +10,15 @@ const EXPECTED = [ |
| 10 | 10 | query: 'vec::iter', |
| 11 | 11 | others: [ |
| 12 | 12 | // std::net::ToSocketAttrs::iter should not show up here |
| 13 | { 'path': 'std::vec', 'name': 'IntoIter' }, | |
| 13 | { 'path': 'std::collections::VecDeque', 'name': 'iter' }, | |
| 14 | { 'path': 'std::collections::VecDeque', 'name': 'iter_mut' }, | |
| 14 | 15 | { 'path': 'std::vec::Vec', 'name': 'from_iter' }, |
| 16 | { 'path': 'std::vec', 'name': 'IntoIter' }, | |
| 15 | 17 | { 'path': 'std::vec::Vec', 'name': 'into_iter' }, |
| 16 | 18 | { 'path': 'std::vec::ExtractIf', 'name': 'into_iter' }, |
| 17 | 19 | { 'path': 'std::vec::Drain', 'name': 'into_iter' }, |
| 18 | 20 | { 'path': 'std::vec::IntoIter', 'name': 'into_iter' }, |
| 19 | 21 | { 'path': 'std::vec::Splice', 'name': 'into_iter' }, |
| 20 | { 'path': 'std::collections::VecDeque', 'name': 'iter' }, | |
| 21 | { 'path': 'std::collections::VecDeque', 'name': 'iter_mut' }, | |
| 22 | 22 | { 'path': 'std::collections::VecDeque', 'name': 'from_iter' }, |
| 23 | 23 | { 'path': 'std::collections::VecDeque', 'name': 'into_iter' }, |
| 24 | 24 | ], |
tests/rustdoc-js-std/return-specific-literal.js+1-1| ... | ... | @@ -4,6 +4,6 @@ const EXPECTED = { |
| 4 | 4 | { 'path': 'std::string::String', 'name': 'ne' }, |
| 5 | 5 | ], |
| 6 | 6 | 'returned': [ |
| 7 | { 'path': 'std::string::String', 'name': 'add' }, | |
| 7 | { 'path': 'std::string::String', 'name': 'new' }, | |
| 8 | 8 | ], |
| 9 | 9 | }; |
tests/rustdoc-js-std/return-specific.js+1-1| ... | ... | @@ -4,6 +4,6 @@ const EXPECTED = { |
| 4 | 4 | { 'path': 'std::string::String', 'name': 'ne' }, |
| 5 | 5 | ], |
| 6 | 6 | 'returned': [ |
| 7 | { 'path': 'std::string::String', 'name': 'add' }, | |
| 7 | { 'path': 'std::string::String', 'name': 'new' }, | |
| 8 | 8 | ], |
| 9 | 9 | }; |
tests/rustdoc-js/doc-alias.js+6-7| ... | ... | @@ -231,6 +231,12 @@ const EXPECTED = [ |
| 231 | 231 | { |
| 232 | 232 | 'query': 'UnionItem', |
| 233 | 233 | 'others': [ |
| 234 | { | |
| 235 | 'path': 'doc_alias::Union', | |
| 236 | 'name': 'union_item', | |
| 237 | 'desc': 'Doc for <code>Union::union_item</code>', | |
| 238 | 'href': '../doc_alias/union.Union.html#structfield.union_item' | |
| 239 | }, | |
| 234 | 240 | { |
| 235 | 241 | 'path': 'doc_alias', |
| 236 | 242 | 'name': 'Union', |
| ... | ... | @@ -239,13 +245,6 @@ const EXPECTED = [ |
| 239 | 245 | 'href': '../doc_alias/union.Union.html', |
| 240 | 246 | 'is_alias': true |
| 241 | 247 | }, |
| 242 | // Not an alias! | |
| 243 | { | |
| 244 | 'path': 'doc_alias::Union', | |
| 245 | 'name': 'union_item', | |
| 246 | 'desc': 'Doc for <code>Union::union_item</code>', | |
| 247 | 'href': '../doc_alias/union.Union.html#structfield.union_item' | |
| 248 | }, | |
| 249 | 248 | ], |
| 250 | 249 | }, |
| 251 | 250 | { |
tests/rustdoc-js/generics-trait.js+19-2| ... | ... | @@ -25,8 +25,25 @@ const EXPECTED = [ |
| 25 | 25 | }, |
| 26 | 26 | { |
| 27 | 27 | 'query': 'Resulx<SomeTrait>', |
| 28 | 'in_args': [], | |
| 29 | 'returned': [], | |
| 28 | 'correction': 'Result', | |
| 29 | 'in_args': [ | |
| 30 | { | |
| 31 | 'path': 'generics_trait', | |
| 32 | 'name': 'beta', | |
| 33 | 'displayType': '`Result`<`T`, ()> -> ()', | |
| 34 | 'displayMappedNames': '', | |
| 35 | 'displayWhereClause': 'T: `SomeTrait`', | |
| 36 | }, | |
| 37 | ], | |
| 38 | 'returned': [ | |
| 39 | { | |
| 40 | 'path': 'generics_trait', | |
| 41 | 'name': 'bet', | |
| 42 | 'displayType': ' -> `Result`<`T`, ()>', | |
| 43 | 'displayMappedNames': '', | |
| 44 | 'displayWhereClause': 'T: `SomeTrait`', | |
| 45 | }, | |
| 46 | ], | |
| 30 | 47 | }, |
| 31 | 48 | { |
| 32 | 49 | 'query': 'Result<SomeTraiz>', |
tests/rustdoc-js/non-english-identifier.js+4-4| ... | ... | @@ -7,7 +7,7 @@ const PARSED = [ |
| 7 | 7 | pathWithoutLast: [], |
| 8 | 8 | pathLast: "中文", |
| 9 | 9 | generics: [], |
| 10 | typeFilter: -1, | |
| 10 | typeFilter: null, | |
| 11 | 11 | }], |
| 12 | 12 | returned: [], |
| 13 | 13 | foundElems: 1, |
| ... | ... | @@ -23,7 +23,7 @@ const PARSED = [ |
| 23 | 23 | pathLast: "_0mixed中英文", |
| 24 | 24 | normalizedPathLast: "0mixed中英文", |
| 25 | 25 | generics: [], |
| 26 | typeFilter: -1, | |
| 26 | typeFilter: null, | |
| 27 | 27 | }], |
| 28 | 28 | foundElems: 1, |
| 29 | 29 | userQuery: "_0Mixed中英文", |
| ... | ... | @@ -38,7 +38,7 @@ const PARSED = [ |
| 38 | 38 | pathWithoutLast: ["my_crate"], |
| 39 | 39 | pathLast: "中文api", |
| 40 | 40 | generics: [], |
| 41 | typeFilter: -1, | |
| 41 | typeFilter: null, | |
| 42 | 42 | }], |
| 43 | 43 | foundElems: 1, |
| 44 | 44 | userQuery: "my_crate::中文API", |
| ... | ... | @@ -94,7 +94,7 @@ const PARSED = [ |
| 94 | 94 | pathWithoutLast: ["my_crate"], |
| 95 | 95 | pathLast: "中文宏", |
| 96 | 96 | generics: [], |
| 97 | typeFilter: 16, | |
| 97 | typeFilter: "macro", | |
| 98 | 98 | }], |
| 99 | 99 | foundElems: 1, |
| 100 | 100 | userQuery: "my_crate 中文宏!", |
tests/rustdoc-js/ordering.js created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const EXPECTED = [ | |
| 2 | { | |
| 3 | 'query': 'Entry', | |
| 4 | 'others': [ | |
| 5 | { 'path': 'ordering', 'name': 'Entry1a' }, | |
| 6 | { 'path': 'ordering', 'name': 'Entry2ab' }, | |
| 7 | ], | |
| 8 | }, | |
| 9 | ]; |
tests/rustdoc-js/ordering.rs created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | pub struct Entry1a; | |
| 2 | pub struct Entry1b; | |
| 3 | pub struct Entry2ab; |
tests/rustdoc-js/type-parameters.js+2-4| ... | ... | @@ -4,8 +4,8 @@ const EXPECTED = [ |
| 4 | 4 | { |
| 5 | 5 | query: '-> trait:Some', |
| 6 | 6 | others: [ |
| 7 | { path: 'foo', name: 'alpha' }, | |
| 8 | 7 | { path: 'foo', name: 'alef' }, |
| 8 | { path: 'foo', name: 'alpha' }, | |
| 9 | 9 | ], |
| 10 | 10 | }, |
| 11 | 11 | { |
| ... | ... | @@ -75,10 +75,8 @@ const EXPECTED = [ |
| 75 | 75 | { |
| 76 | 76 | query: 'Other', |
| 77 | 77 | in_args: [ |
| 78 | // because function is called "other", it's sorted first | |
| 79 | // even though it has higher type distance | |
| 80 | { path: 'foo', name: 'other' }, | |
| 81 | 78 | { path: 'foo', name: 'alternate' }, |
| 79 | { path: 'foo', name: 'other' }, | |
| 82 | 80 | ], |
| 83 | 81 | }, |
| 84 | 82 | { |
tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs+3-3| ... | ... | @@ -5,9 +5,9 @@ |
| 5 | 5 | //@ has t/trait.Tango.html |
| 6 | 6 | //@ hasraw s/struct.Sierra.html 'Tango' |
| 7 | 7 | //@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html' |
| 8 | //@ hasraw search-index.js 'Tango' | |
| 9 | //@ hasraw search-index.js 'Sierra' | |
| 10 | //@ hasraw search-index.js 'Quebec' | |
| 8 | //@ hasraw search.index/name/*.js 'Tango' | |
| 9 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 10 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 11 | 11 | |
| 12 | 12 | // We document multiple crates into the same output directory, which |
| 13 | 13 | // merges the cross-crate information. Everything is available. |
tests/rustdoc/cross-crate-info/cargo-transitive/s.rs+3-3| ... | ... | @@ -13,9 +13,9 @@ |
| 13 | 13 | //@ has t/trait.Tango.html |
| 14 | 14 | //@ hasraw s/struct.Sierra.html 'Tango' |
| 15 | 15 | //@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html' |
| 16 | //@ hasraw search-index.js 'Tango' | |
| 17 | //@ hasraw search-index.js 'Sierra' | |
| 18 | //@ hasraw search-index.js 'Quebec' | |
| 16 | //@ hasraw search.index/name/*.js 'Tango' | |
| 17 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 18 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 19 | 19 | |
| 20 | 20 | // We document multiple crates into the same output directory, which |
| 21 | 21 | // merges the cross-crate information. Everything is available. |
tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs+2-2| ... | ... | @@ -4,8 +4,8 @@ |
| 4 | 4 | //@ has f/trait.Foxtrot.html |
| 5 | 5 | //@ hasraw e/enum.Echo.html 'Foxtrot' |
| 6 | 6 | //@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html' |
| 7 | //@ hasraw search-index.js 'Foxtrot' | |
| 8 | //@ hasraw search-index.js 'Echo' | |
| 7 | //@ hasraw search.index/name/*.js 'Foxtrot' | |
| 8 | //@ hasraw search.index/name/*.js 'Echo' | |
| 9 | 9 | |
| 10 | 10 | // document two crates in the same way that cargo does. do not provide |
| 11 | 11 | // --enable-index-page |
tests/rustdoc/cross-crate-info/cargo-two/e.rs+2-2| ... | ... | @@ -11,8 +11,8 @@ |
| 11 | 11 | //@ has f/trait.Foxtrot.html |
| 12 | 12 | //@ hasraw e/enum.Echo.html 'Foxtrot' |
| 13 | 13 | //@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html' |
| 14 | //@ hasraw search-index.js 'Foxtrot' | |
| 15 | //@ hasraw search-index.js 'Echo' | |
| 14 | //@ hasraw search.index/name/*.js 'Foxtrot' | |
| 15 | //@ hasraw search.index/name/*.js 'Echo' | |
| 16 | 16 | |
| 17 | 17 | // document two crates in the same way that cargo does, writing them both |
| 18 | 18 | // into the same output directory |
tests/rustdoc/cross-crate-info/index-on-last/e.rs+2-2| ... | ... | @@ -11,8 +11,8 @@ |
| 11 | 11 | //@ has f/trait.Foxtrot.html |
| 12 | 12 | //@ hasraw e/enum.Echo.html 'Foxtrot' |
| 13 | 13 | //@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html' |
| 14 | //@ hasraw search-index.js 'Foxtrot' | |
| 15 | //@ hasraw search-index.js 'Echo' | |
| 14 | //@ hasraw search.index/name/*.js 'Foxtrot' | |
| 15 | //@ hasraw search.index/name/*.js 'Echo' | |
| 16 | 16 | |
| 17 | 17 | // only declare --enable-index-page to the last rustdoc invocation |
| 18 | 18 | extern crate f; |
tests/rustdoc/cross-crate-info/kitchen-sink/i.rs+4-4| ... | ... | @@ -19,10 +19,10 @@ |
| 19 | 19 | //@ has t/trait.Tango.html |
| 20 | 20 | //@ hasraw s/struct.Sierra.html 'Tango' |
| 21 | 21 | //@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html' |
| 22 | //@ hasraw search-index.js 'Quebec' | |
| 23 | //@ hasraw search-index.js 'Romeo' | |
| 24 | //@ hasraw search-index.js 'Sierra' | |
| 25 | //@ hasraw search-index.js 'Tango' | |
| 22 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 23 | //@ hasraw search.index/name/*.js 'Romeo' | |
| 24 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 25 | //@ hasraw search.index/name/*.js 'Tango' | |
| 26 | 26 | //@ has type.impl/s/struct.Sierra.js |
| 27 | 27 | //@ hasraw type.impl/s/struct.Sierra.js 'Tango' |
| 28 | 28 | //@ hasraw type.impl/s/struct.Sierra.js 'Romeo' |
tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs+1-1| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | //@ has index.html '//h1' 'List of all crates' |
| 7 | 7 | //@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q' |
| 8 | 8 | //@ has q/struct.Quebec.html |
| 9 | //@ hasraw search-index.js 'Quebec' | |
| 9 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 10 | 10 | |
| 11 | 11 | // there's nothing cross-crate going on here |
| 12 | 12 | pub struct Quebec; |
tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | //@ build-aux-docs |
| 2 | 2 | //@ has q/struct.Quebec.html |
| 3 | //@ hasraw search-index.js 'Quebec' | |
| 3 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 4 | 4 | |
| 5 | 5 | // there's nothing cross-crate going on here |
| 6 | 6 | pub struct Quebec; |
tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs+2-2| ... | ... | @@ -4,8 +4,8 @@ |
| 4 | 4 | //@ !has f/trait.Foxtrot.html |
| 5 | 5 | //@ hasraw e/enum.Echo.html 'Foxtrot' |
| 6 | 6 | //@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html' |
| 7 | //@ !hasraw search-index.js 'Foxtrot' | |
| 8 | //@ hasraw search-index.js 'Echo' | |
| 7 | //@ !hasraw search.index/name/*.js 'Foxtrot' | |
| 8 | //@ hasraw search.index/name/*.js 'Echo' | |
| 9 | 9 | |
| 10 | 10 | // test the fact that our test runner will document this crate somewhere |
| 11 | 11 | // else |
tests/rustdoc/masked.rs+1-1| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | #[doc(masked)] |
| 8 | 8 | extern crate masked; |
| 9 | 9 | |
| 10 | //@ !hasraw 'search-index.js' 'masked_method' | |
| 10 | //@ !hasraw 'search.index/name/*.js' 'masked_method' | |
| 11 | 11 | |
| 12 | 12 | //@ !hasraw 'foo/struct.String.html' 'MaskedTrait' |
| 13 | 13 | //@ !hasraw 'foo/struct.String.html' 'MaskedBlanketTrait' |
tests/rustdoc/merge-cross-crate-info/cargo-transitive-read-write/sierra.rs+3-3| ... | ... | @@ -14,9 +14,9 @@ |
| 14 | 14 | //@ has tango/trait.Tango.html |
| 15 | 15 | //@ hasraw sierra/struct.Sierra.html 'Tango' |
| 16 | 16 | //@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html' |
| 17 | //@ hasraw search-index.js 'Tango' | |
| 18 | //@ hasraw search-index.js 'Sierra' | |
| 19 | //@ hasraw search-index.js 'Quebec' | |
| 17 | //@ hasraw search.index/name/*.js 'Tango' | |
| 18 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 19 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 20 | 20 | |
| 21 | 21 | // similar to cargo-workflow-transitive, but we use --merge=read-write, |
| 22 | 22 | // which is the default. |
tests/rustdoc/merge-cross-crate-info/kitchen-sink-separate-dirs/indigo.rs+4-4| ... | ... | @@ -23,10 +23,10 @@ |
| 23 | 23 | //@ !has sierra/struct.Sierra.html |
| 24 | 24 | //@ !has tango/trait.Tango.html |
| 25 | 25 | //@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html' |
| 26 | //@ hasraw search-index.js 'Quebec' | |
| 27 | //@ hasraw search-index.js 'Romeo' | |
| 28 | //@ hasraw search-index.js 'Sierra' | |
| 29 | //@ hasraw search-index.js 'Tango' | |
| 26 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 27 | //@ hasraw search.index/name/*.js 'Romeo' | |
| 28 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 29 | //@ hasraw search.index/name/*.js 'Tango' | |
| 30 | 30 | //@ has type.impl/sierra/struct.Sierra.js |
| 31 | 31 | //@ hasraw type.impl/sierra/struct.Sierra.js 'Tango' |
| 32 | 32 | //@ hasraw type.impl/sierra/struct.Sierra.js 'Romeo' |
tests/rustdoc/merge-cross-crate-info/no-merge-separate/sierra.rs+1-1| ... | ... | @@ -8,7 +8,7 @@ |
| 8 | 8 | //@ has sierra/struct.Sierra.html |
| 9 | 9 | //@ hasraw sierra/struct.Sierra.html 'Tango' |
| 10 | 10 | //@ !has trait.impl/tango/trait.Tango.js |
| 11 | //@ !has search-index.js | |
| 11 | //@ !has search.index/name/*.js | |
| 12 | 12 | |
| 13 | 13 | // we don't generate any cross-crate info if --merge=none, even if we |
| 14 | 14 | // document crates separately |
tests/rustdoc/merge-cross-crate-info/no-merge-write-anyway/sierra.rs+1-1| ... | ... | @@ -10,7 +10,7 @@ |
| 10 | 10 | //@ has tango/trait.Tango.html |
| 11 | 11 | //@ hasraw sierra/struct.Sierra.html 'Tango' |
| 12 | 12 | //@ !has trait.impl/tango/trait.Tango.js |
| 13 | //@ !has search-index.js | |
| 13 | //@ !has search.index/name/*.js | |
| 14 | 14 | |
| 15 | 15 | // we --merge=none, so --parts-out-dir doesn't do anything |
| 16 | 16 | extern crate tango; |
tests/rustdoc/merge-cross-crate-info/overwrite-but-include/sierra.rs+3-3| ... | ... | @@ -10,9 +10,9 @@ |
| 10 | 10 | //@ has tango/trait.Tango.html |
| 11 | 11 | //@ hasraw sierra/struct.Sierra.html 'Tango' |
| 12 | 12 | //@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html' |
| 13 | //@ hasraw search-index.js 'Tango' | |
| 14 | //@ hasraw search-index.js 'Sierra' | |
| 15 | //@ !hasraw search-index.js 'Quebec' | |
| 13 | //@ hasraw search.index/name/*.js 'Tango' | |
| 14 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 15 | //@ !hasraw search.index/name/*.js 'Quebec' | |
| 16 | 16 | |
| 17 | 17 | // we overwrite quebec and tango's cross-crate information, but we |
| 18 | 18 | // include the info from tango meaning that it should appear in the out |
tests/rustdoc/merge-cross-crate-info/overwrite-but-separate/sierra.rs+3-3| ... | ... | @@ -13,9 +13,9 @@ |
| 13 | 13 | //@ has index.html '//ul[@class="all-items"]//a[@href="tango/index.html"]' 'tango' |
| 14 | 14 | //@ has sierra/struct.Sierra.html |
| 15 | 15 | //@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html' |
| 16 | //@ hasraw search-index.js 'Tango' | |
| 17 | //@ hasraw search-index.js 'Sierra' | |
| 18 | //@ hasraw search-index.js 'Quebec' | |
| 16 | //@ hasraw search.index/name/*.js 'Tango' | |
| 17 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 18 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 19 | 19 | |
| 20 | 20 | // If these were documeted into the same directory, the info would be |
| 21 | 21 | // overwritten. However, since they are merged, we can still recover all |
tests/rustdoc/merge-cross-crate-info/overwrite/sierra.rs+3-3| ... | ... | @@ -9,9 +9,9 @@ |
| 9 | 9 | //@ has tango/trait.Tango.html |
| 10 | 10 | //@ hasraw sierra/struct.Sierra.html 'Tango' |
| 11 | 11 | //@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html' |
| 12 | //@ hasraw search-index.js 'Tango' | |
| 13 | //@ hasraw search-index.js 'Sierra' | |
| 14 | //@ !hasraw search-index.js 'Quebec' | |
| 12 | //@ hasraw search.index/name/*.js 'Tango' | |
| 13 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 14 | //@ !hasraw search.index/name/*.js 'Quebec' | |
| 15 | 15 | |
| 16 | 16 | // since tango is documented with --merge=finalize, we overwrite q's |
| 17 | 17 | // cross-crate information |
tests/rustdoc/merge-cross-crate-info/single-crate-finalize/quebec.rs+1-1| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | //@ has index.html '//h1' 'List of all crates' |
| 7 | 7 | //@ has index.html '//ul[@class="all-items"]//a[@href="quebec/index.html"]' 'quebec' |
| 8 | 8 | //@ has quebec/struct.Quebec.html |
| 9 | //@ hasraw search-index.js 'Quebec' | |
| 9 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 10 | 10 | |
| 11 | 11 | // there is nothing to read from the output directory if we use a single |
| 12 | 12 | // crate |
tests/rustdoc/merge-cross-crate-info/single-crate-read-write/quebec.rs+1-1| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | //@ has index.html '//h1' 'List of all crates' |
| 7 | 7 | //@ has index.html '//ul[@class="all-items"]//a[@href="quebec/index.html"]' 'quebec' |
| 8 | 8 | //@ has quebec/struct.Quebec.html |
| 9 | //@ hasraw search-index.js 'Quebec' | |
| 9 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 10 | 10 | |
| 11 | 11 | // read-write is the default and this does the same as `single-crate` |
| 12 | 12 | pub struct Quebec; |
tests/rustdoc/merge-cross-crate-info/single-crate-write-anyway/quebec.rs+1-1| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | //@ has index.html '//h1' 'List of all crates' |
| 7 | 7 | //@ has index.html '//ul[@class="all-items"]//a[@href="quebec/index.html"]' 'quebec' |
| 8 | 8 | //@ has quebec/struct.Quebec.html |
| 9 | //@ hasraw search-index.js 'Quebec' | |
| 9 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 10 | 10 | |
| 11 | 11 | // we can --parts-out-dir, but that doesn't do anything other than create |
| 12 | 12 | // the file |
tests/rustdoc/merge-cross-crate-info/single-merge-none-useless-write/quebec.rs+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | |
| 6 | 6 | //@ !has index.html |
| 7 | 7 | //@ has quebec/struct.Quebec.html |
| 8 | //@ !has search-index.js | |
| 8 | //@ !has search.index/name/*.js | |
| 9 | 9 | |
| 10 | 10 | // --merge=none doesn't write anything, despite --parts-out-dir |
| 11 | 11 | pub struct Quebec; |
tests/rustdoc/merge-cross-crate-info/transitive-finalize/sierra.rs+1-1| ... | ... | @@ -12,7 +12,7 @@ |
| 12 | 12 | //@ has tango/trait.Tango.html |
| 13 | 13 | //@ hasraw sierra/struct.Sierra.html 'Tango' |
| 14 | 14 | //@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html' |
| 15 | //@ hasraw search-index.js 'Sierra' | |
| 15 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 16 | 16 | |
| 17 | 17 | // write only overwrites stuff in the output directory |
| 18 | 18 | extern crate tango; |
tests/rustdoc/merge-cross-crate-info/transitive-merge-none/sierra.rs+3-3| ... | ... | @@ -16,9 +16,9 @@ |
| 16 | 16 | //@ has tango/trait.Tango.html |
| 17 | 17 | //@ hasraw sierra/struct.Sierra.html 'Tango' |
| 18 | 18 | //@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html' |
| 19 | //@ hasraw search-index.js 'Tango' | |
| 20 | //@ hasraw search-index.js 'Sierra' | |
| 21 | //@ hasraw search-index.js 'Quebec' | |
| 19 | //@ hasraw search.index/name/*.js 'Tango' | |
| 20 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 21 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 22 | 22 | |
| 23 | 23 | // We avoid writing any cross-crate information, preferring to include it |
| 24 | 24 | // with --include-parts-dir. |
tests/rustdoc/merge-cross-crate-info/transitive-merge-read-write/sierra.rs+3-3| ... | ... | @@ -14,9 +14,9 @@ |
| 14 | 14 | //@ has tango/trait.Tango.html |
| 15 | 15 | //@ hasraw sierra/struct.Sierra.html 'Tango' |
| 16 | 16 | //@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html' |
| 17 | //@ hasraw search-index.js 'Tango' | |
| 18 | //@ hasraw search-index.js 'Sierra' | |
| 19 | //@ hasraw search-index.js 'Quebec' | |
| 17 | //@ hasraw search.index/name/*.js 'Tango' | |
| 18 | //@ hasraw search.index/name/*.js 'Sierra' | |
| 19 | //@ hasraw search.index/name/*.js 'Quebec' | |
| 20 | 20 | |
| 21 | 21 | // We can use read-write to emulate the default behavior of rustdoc, when |
| 22 | 22 | // --merge is left out. |
tests/rustdoc/merge-cross-crate-info/transitive-no-info/sierra.rs+1-1| ... | ... | @@ -9,7 +9,7 @@ |
| 9 | 9 | //@ has tango/trait.Tango.html |
| 10 | 10 | //@ hasraw sierra/struct.Sierra.html 'Tango' |
| 11 | 11 | //@ !has trait.impl/tango/trait.Tango.js |
| 12 | //@ !has search-index.js | |
| 12 | //@ !has search.index/name/*.js | |
| 13 | 13 | |
| 14 | 14 | // --merge=none on all crates does not generate any cross-crate info |
| 15 | 15 | extern crate tango; |
tests/rustdoc/merge-cross-crate-info/two-separate-out-dir/echo.rs+2-2| ... | ... | @@ -6,8 +6,8 @@ |
| 6 | 6 | //@ has echo/enum.Echo.html |
| 7 | 7 | //@ hasraw echo/enum.Echo.html 'Foxtrot' |
| 8 | 8 | //@ hasraw trait.impl/foxtrot/trait.Foxtrot.js 'enum.Echo.html' |
| 9 | //@ hasraw search-index.js 'Foxtrot' | |
| 10 | //@ hasraw search-index.js 'Echo' | |
| 9 | //@ hasraw search.index/name/*.js 'Foxtrot' | |
| 10 | //@ hasraw search.index/name/*.js 'Echo' | |
| 11 | 11 | |
| 12 | 12 | // document two crates in different places, and merge their docs after |
| 13 | 13 | // they are generated |
tests/rustdoc/no-unit-struct-field.rs+5-4| ... | ... | @@ -1,10 +1,11 @@ |
| 1 | 1 | // This test ensures that the tuple struct fields are not generated in the |
| 2 | 2 | // search index. |
| 3 | 3 | |
| 4 | //@ !hasraw search-index.js '"0"' | |
| 5 | //@ !hasraw search-index.js '"1"' | |
| 6 | //@ hasraw search-index.js '"foo_a"' | |
| 7 | //@ hasraw search-index.js '"bar_a"' | |
| 4 | // vlqhex encoding ` = 0, a = 1, e = 5 | |
| 5 | //@ !hasraw search.index/name/*.js 'a0' | |
| 6 | //@ !hasraw search.index/name/*.js 'a1' | |
| 7 | //@ hasraw search.index/name/*.js 'efoo_a' | |
| 8 | //@ hasraw search.index/name/*.js 'ebar_a' | |
| 8 | 9 | |
| 9 | 10 | pub struct Bar(pub u32, pub u8); |
| 10 | 11 | pub struct Foo { |
tests/rustdoc/primitive/search-index-primitive-inherent-method-23511.rs+1-1| ... | ... | @@ -9,7 +9,7 @@ pub mod str { |
| 9 | 9 | #![rustc_doc_primitive = "str"] |
| 10 | 10 | |
| 11 | 11 | impl str { |
| 12 | //@ hasraw search-index.js foo | |
| 12 | //@ hasraw search.index/name/*.js foo | |
| 13 | 13 | #[rustc_allow_incoherent_impl] |
| 14 | 14 | pub fn foo(&self) {} |
| 15 | 15 | } |
tests/rustdoc/search-index-summaries.rs+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | #![crate_name = "foo"] |
| 2 | 2 | |
| 3 | //@ hasraw 'search.desc/foo/foo-desc-0-.js' 'Foo short link.' | |
| 3 | //@ hasraw 'search.index/desc/*.js' 'Foo short link.' | |
| 4 | 4 | //@ !hasraw - 'www.example.com' |
| 5 | 5 | //@ !hasraw - 'More Foo.' |
| 6 | 6 |
tests/rustdoc/search-index.rs+2-2| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | use std::ops::Deref; |
| 4 | 4 | |
| 5 | //@ hasraw search-index.js Foo | |
| 5 | //@ hasraw search.index/name/*.js Foo | |
| 6 | 6 | pub use private::Foo; |
| 7 | 7 | |
| 8 | 8 | mod private { |
| ... | ... | @@ -20,7 +20,7 @@ mod private { |
| 20 | 20 | pub struct Bar; |
| 21 | 21 | |
| 22 | 22 | impl Deref for Bar { |
| 23 | //@ !hasraw search-index.js Target | |
| 23 | //@ !hasraw search.index/name/*.js Target | |
| 24 | 24 | type Target = Bar; |
| 25 | 25 | fn deref(&self) -> &Bar { self } |
| 26 | 26 | } |
tests/ui/array-slice-vec/slice-mut-2.stderr+3-3| ... | ... | @@ -4,10 +4,10 @@ error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference |
| 4 | 4 | LL | let _ = &mut x[2..4]; |
| 5 | 5 | | ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable |
| 6 | 6 | | |
| 7 | help: consider changing this to be a mutable reference | |
| 7 | help: consider changing this binding's type | |
| 8 | 8 | | |
| 9 | LL | let x: &[isize] = &mut [1, 2, 3, 4, 5]; | |
| 10 | | +++ | |
| 9 | LL | let x: &mut [isize] = &[1, 2, 3, 4, 5]; | |
| 10 | | +++ | |
| 11 | 11 | |
| 12 | 12 | error: aborting due to 1 previous error |
| 13 | 13 |
tests/ui/attributes/lint_on_root.rs+1-1| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | // NOTE: this used to panic in debug builds (by a sanity assertion) |
| 2 | 2 | // and not emit any lint on release builds. See https://github.com/rust-lang/rust/issues/142891. |
| 3 | 3 | #![inline = ""] |
| 4 | //~^ ERROR: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` [ill_formed_attribute_input] | |
| 4 | //~^ ERROR: valid forms for the attribute are `#![inline(always)]`, `#![inline(never)]`, and `#![inline]` [ill_formed_attribute_input] | |
| 5 | 5 | //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! |
| 6 | 6 | //~| ERROR attribute cannot be used on |
| 7 | 7 |
tests/ui/attributes/lint_on_root.stderr+2-2| ... | ... | @@ -6,7 +6,7 @@ LL | #![inline = ""] |
| 6 | 6 | | |
| 7 | 7 | = help: `#[inline]` can only be applied to functions |
| 8 | 8 | |
| 9 | error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` | |
| 9 | error: valid forms for the attribute are `#![inline(always)]`, `#![inline(never)]`, and `#![inline]` | |
| 10 | 10 | --> $DIR/lint_on_root.rs:3:1 |
| 11 | 11 | | |
| 12 | 12 | LL | #![inline = ""] |
| ... | ... | @@ -19,7 +19,7 @@ LL | #![inline = ""] |
| 19 | 19 | error: aborting due to 2 previous errors |
| 20 | 20 | |
| 21 | 21 | Future incompatibility report: Future breakage diagnostic: |
| 22 | error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` | |
| 22 | error: valid forms for the attribute are `#![inline(always)]`, `#![inline(never)]`, and `#![inline]` | |
| 23 | 23 | --> $DIR/lint_on_root.rs:3:1 |
| 24 | 24 | | |
| 25 | 25 | LL | #![inline = ""] |
tests/ui/attributes/malformed-attrs.rs+2-2| ... | ... | @@ -12,7 +12,7 @@ |
| 12 | 12 | #![feature(min_generic_const_args)] |
| 13 | 13 | #![feature(ffi_const, ffi_pure)] |
| 14 | 14 | #![feature(coverage_attribute)] |
| 15 | #![feature(no_sanitize)] | |
| 15 | #![feature(sanitize)] | |
| 16 | 16 | #![feature(marker_trait_attr)] |
| 17 | 17 | #![feature(thread_local)] |
| 18 | 18 | #![feature(must_not_suspend)] |
| ... | ... | @@ -89,7 +89,7 @@ |
| 89 | 89 | //~^ ERROR malformed |
| 90 | 90 | #[coverage] |
| 91 | 91 | //~^ ERROR malformed `coverage` attribute input |
| 92 | #[no_sanitize] | |
| 92 | #[sanitize] | |
| 93 | 93 | //~^ ERROR malformed |
| 94 | 94 | #[ignore()] |
| 95 | 95 | //~^ ERROR valid forms for the attribute are |
tests/ui/attributes/malformed-attrs.stderr+15-3| ... | ... | @@ -49,11 +49,23 @@ LL | #[crate_name] |
| 49 | 49 | | |
| 50 | 50 | = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute> |
| 51 | 51 | |
| 52 | error: malformed `no_sanitize` attribute input | |
| 52 | error: malformed `sanitize` attribute input | |
| 53 | 53 | --> $DIR/malformed-attrs.rs:92:1 |
| 54 | 54 | | |
| 55 | LL | #[no_sanitize] | |
| 56 | | ^^^^^^^^^^^^^^ help: must be of the form: `#[no_sanitize(address, kcfi, memory, thread)]` | |
| 55 | LL | #[sanitize] | |
| 56 | | ^^^^^^^^^^^ | |
| 57 | | | |
| 58 | help: the following are the possible correct uses | |
| 59 | | | |
| 60 | LL | #[sanitize(address = "on|off")] | |
| 61 | | ++++++++++++++++++++ | |
| 62 | LL | #[sanitize(cfi = "on|off")] | |
| 63 | | ++++++++++++++++ | |
| 64 | LL | #[sanitize(hwaddress = "on|off")] | |
| 65 | | ++++++++++++++++++++++ | |
| 66 | LL | #[sanitize(kcfi = "on|off")] | |
| 67 | | +++++++++++++++++ | |
| 68 | = and 5 other candidates | |
| 57 | 69 | |
| 58 | 70 | error: malformed `instruction_set` attribute input |
| 59 | 71 | --> $DIR/malformed-attrs.rs:106:1 |
tests/ui/attributes/malformed-reprs.stderr+8-12| ... | ... | @@ -7,18 +7,14 @@ LL | #![repr] |
| 7 | 7 | = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations> |
| 8 | 8 | help: try changing it to one of the following valid forms of the attribute |
| 9 | 9 | | |
| 10 | LL - #![repr] | |
| 11 | LL + #[repr(<integer type>)] | |
| 12 | | | |
| 13 | LL - #![repr] | |
| 14 | LL + #[repr(C)] | |
| 15 | | | |
| 16 | LL - #![repr] | |
| 17 | LL + #[repr(Rust)] | |
| 18 | | | |
| 19 | LL - #![repr] | |
| 20 | LL + #[repr(align(...))] | |
| 21 | | | |
| 10 | LL | #![repr(<integer type>)] | |
| 11 | | ++++++++++++++++ | |
| 12 | LL | #![repr(C)] | |
| 13 | | +++ | |
| 14 | LL | #![repr(Rust)] | |
| 15 | | ++++++ | |
| 16 | LL | #![repr(align(...))] | |
| 17 | | ++++++++++++ | |
| 22 | 18 | = and 2 other candidates |
| 23 | 19 | |
| 24 | 20 | error[E0589]: invalid `repr(align)` attribute: not a power of two |
tests/ui/attributes/no-sanitize.rs deleted-45| ... | ... | @@ -1,45 +0,0 @@ |
| 1 | #![feature(no_sanitize)] | |
| 2 | #![feature(stmt_expr_attributes)] | |
| 3 | #![deny(unused_attributes)] | |
| 4 | #![allow(dead_code)] | |
| 5 | ||
| 6 | fn invalid() { | |
| 7 | #[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function | |
| 8 | { | |
| 9 | 1 | |
| 10 | }; | |
| 11 | } | |
| 12 | ||
| 13 | #[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function | |
| 14 | type InvalidTy = (); | |
| 15 | ||
| 16 | #[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function | |
| 17 | mod invalid_module {} | |
| 18 | ||
| 19 | fn main() { | |
| 20 | let _ = #[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function | |
| 21 | (|| 1); | |
| 22 | } | |
| 23 | ||
| 24 | #[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function | |
| 25 | struct F; | |
| 26 | ||
| 27 | #[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function | |
| 28 | impl F { | |
| 29 | #[no_sanitize(memory)] | |
| 30 | fn valid(&self) {} | |
| 31 | } | |
| 32 | ||
| 33 | #[no_sanitize(address, memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function | |
| 34 | static INVALID : i32 = 0; | |
| 35 | ||
| 36 | #[no_sanitize(memory)] | |
| 37 | fn valid() {} | |
| 38 | ||
| 39 | #[no_sanitize(address)] | |
| 40 | static VALID : i32 = 0; | |
| 41 | ||
| 42 | #[no_sanitize("address")] | |
| 43 | //~^ ERROR `#[no_sanitize(...)]` should be applied to a function | |
| 44 | //~| ERROR invalid argument for `no_sanitize` | |
| 45 | static VALID2 : i32 = 0; |
tests/ui/attributes/no-sanitize.stderr deleted-80| ... | ... | @@ -1,80 +0,0 @@ |
| 1 | error: `#[no_sanitize(memory)]` should be applied to a function | |
| 2 | --> $DIR/no-sanitize.rs:7:19 | |
| 3 | | | |
| 4 | LL | #[no_sanitize(memory)] | |
| 5 | | ^^^^^^ | |
| 6 | LL | / { | |
| 7 | LL | | 1 | |
| 8 | LL | | }; | |
| 9 | | |_____- not a function | |
| 10 | ||
| 11 | error: `#[no_sanitize(memory)]` should be applied to a function | |
| 12 | --> $DIR/no-sanitize.rs:13:15 | |
| 13 | | | |
| 14 | LL | #[no_sanitize(memory)] | |
| 15 | | ^^^^^^ | |
| 16 | LL | type InvalidTy = (); | |
| 17 | | -------------------- not a function | |
| 18 | ||
| 19 | error: `#[no_sanitize(memory)]` should be applied to a function | |
| 20 | --> $DIR/no-sanitize.rs:16:15 | |
| 21 | | | |
| 22 | LL | #[no_sanitize(memory)] | |
| 23 | | ^^^^^^ | |
| 24 | LL | mod invalid_module {} | |
| 25 | | --------------------- not a function | |
| 26 | ||
| 27 | error: `#[no_sanitize(memory)]` should be applied to a function | |
| 28 | --> $DIR/no-sanitize.rs:20:27 | |
| 29 | | | |
| 30 | LL | let _ = #[no_sanitize(memory)] | |
| 31 | | ^^^^^^ | |
| 32 | LL | (|| 1); | |
| 33 | | ------ not a function | |
| 34 | ||
| 35 | error: `#[no_sanitize(memory)]` should be applied to a function | |
| 36 | --> $DIR/no-sanitize.rs:24:15 | |
| 37 | | | |
| 38 | LL | #[no_sanitize(memory)] | |
| 39 | | ^^^^^^ | |
| 40 | LL | struct F; | |
| 41 | | --------- not a function | |
| 42 | ||
| 43 | error: `#[no_sanitize(memory)]` should be applied to a function | |
| 44 | --> $DIR/no-sanitize.rs:27:15 | |
| 45 | | | |
| 46 | LL | #[no_sanitize(memory)] | |
| 47 | | ^^^^^^ | |
| 48 | LL | / impl F { | |
| 49 | LL | | #[no_sanitize(memory)] | |
| 50 | LL | | fn valid(&self) {} | |
| 51 | LL | | } | |
| 52 | | |_- not a function | |
| 53 | ||
| 54 | error: `#[no_sanitize(memory)]` should be applied to a function | |
| 55 | --> $DIR/no-sanitize.rs:33:24 | |
| 56 | | | |
| 57 | LL | #[no_sanitize(address, memory)] | |
| 58 | | ^^^^^^ | |
| 59 | LL | static INVALID : i32 = 0; | |
| 60 | | ------------------------- not a function | |
| 61 | ||
| 62 | error: `#[no_sanitize(...)]` should be applied to a function | |
| 63 | --> $DIR/no-sanitize.rs:42:15 | |
| 64 | | | |
| 65 | LL | #[no_sanitize("address")] | |
| 66 | | ^^^^^^^^^ | |
| 67 | ... | |
| 68 | LL | static VALID2 : i32 = 0; | |
| 69 | | ------------------------ not a function | |
| 70 | ||
| 71 | error: invalid argument for `no_sanitize` | |
| 72 | --> $DIR/no-sanitize.rs:42:15 | |
| 73 | | | |
| 74 | LL | #[no_sanitize("address")] | |
| 75 | | ^^^^^^^^^ | |
| 76 | | | |
| 77 | = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` | |
| 78 | ||
| 79 | error: aborting due to 9 previous errors | |
| 80 |
tests/ui/borrowck/borrow-raw-address-of-deref-mutability.stderr+3-3| ... | ... | @@ -15,10 +15,10 @@ error[E0596]: cannot borrow `*x` as mutable, as it is behind a `*const` pointer |
| 15 | 15 | LL | let q = &raw mut *x; |
| 16 | 16 | | ^^^^^^^^^^^ `x` is a `*const` pointer, so the data it refers to cannot be borrowed as mutable |
| 17 | 17 | | |
| 18 | help: consider changing this to be a mutable pointer | |
| 18 | help: consider specifying this binding's type | |
| 19 | 19 | | |
| 20 | LL | let x = &mut 0 as *const i32; | |
| 21 | | +++ | |
| 20 | LL | let x: *mut i32 = &0 as *const i32; | |
| 21 | | ++++++++++ | |
| 22 | 22 | |
| 23 | 23 | error: aborting due to 2 previous errors |
| 24 | 24 |
tests/ui/borrowck/borrowck-access-permissions.stderr+4-3| ... | ... | @@ -43,10 +43,11 @@ error[E0596]: cannot borrow `*ptr_x` as mutable, as it is behind a `*const` poin |
| 43 | 43 | LL | let _y1 = &mut *ptr_x; |
| 44 | 44 | | ^^^^^^^^^^^ `ptr_x` is a `*const` pointer, so the data it refers to cannot be borrowed as mutable |
| 45 | 45 | | |
| 46 | help: consider changing this to be a mutable pointer | |
| 46 | help: consider changing this binding's type | |
| 47 | | | |
| 48 | LL - let ptr_x: *const _ = &x; | |
| 49 | LL + let ptr_x: *mut i32 = &x; | |
| 47 | 50 | | |
| 48 | LL | let ptr_x: *const _ = &mut x; | |
| 49 | | +++ | |
| 50 | 51 | |
| 51 | 52 | error[E0596]: cannot borrow `*foo_ref.f` as mutable, as it is behind a `&` reference |
| 52 | 53 | --> $DIR/borrowck-access-permissions.rs:59:18 |
tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr-6| ... | ... | @@ -22,12 +22,6 @@ LL | force_send(async_load(&not_static)); |
| 22 | 22 | ... |
| 23 | 23 | LL | } |
| 24 | 24 | | - `not_static` dropped here while still borrowed |
| 25 | | | |
| 26 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 27 | --> $DIR/implementation-not-general-enough-ice-133252.rs:16:18 | |
| 28 | | | |
| 29 | LL | fn force_send<T: Send>(_: T) {} | |
| 30 | | ^^^^ | |
| 31 | 25 | |
| 32 | 26 | error: aborting due to 2 previous errors |
| 33 | 27 |
tests/ui/borrowck/suggestions/overloaded-index-not-mut-but-should-be-mut.fixed created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | //@ run-rustfix | |
| 2 | fn main() { | |
| 3 | let mut map = std::collections::BTreeMap::new(); | |
| 4 | map.insert(0, "string".to_owned()); | |
| 5 | ||
| 6 | let string = map.get_mut(&0).unwrap(); | |
| 7 | string.push_str("test"); | |
| 8 | //~^ ERROR cannot borrow `*string` as mutable, as it is behind a `&` reference | |
| 9 | ||
| 10 | let mut map = std::collections::HashMap::new(); | |
| 11 | map.insert(0, "string".to_owned()); | |
| 12 | ||
| 13 | let string = map.get_mut(&0).unwrap(); | |
| 14 | string.push_str("test"); | |
| 15 | //~^ ERROR cannot borrow `*string` as mutable, as it is behind a `&` reference | |
| 16 | ||
| 17 | let mut vec = vec![String::new(), String::new()]; | |
| 18 | let string = &mut vec[0]; | |
| 19 | string.push_str("test"); | |
| 20 | //~^ ERROR cannot borrow `*string` as mutable, as it is behind a `&` reference | |
| 21 | } |
tests/ui/borrowck/suggestions/overloaded-index-not-mut-but-should-be-mut.rs created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | //@ run-rustfix | |
| 2 | fn main() { | |
| 3 | let mut map = std::collections::BTreeMap::new(); | |
| 4 | map.insert(0, "string".to_owned()); | |
| 5 | ||
| 6 | let string = &map[&0]; | |
| 7 | string.push_str("test"); | |
| 8 | //~^ ERROR cannot borrow `*string` as mutable, as it is behind a `&` reference | |
| 9 | ||
| 10 | let mut map = std::collections::HashMap::new(); | |
| 11 | map.insert(0, "string".to_owned()); | |
| 12 | ||
| 13 | let string = &map[&0]; | |
| 14 | string.push_str("test"); | |
| 15 | //~^ ERROR cannot borrow `*string` as mutable, as it is behind a `&` reference | |
| 16 | ||
| 17 | let mut vec = vec![String::new(), String::new()]; | |
| 18 | let string = &vec[0]; | |
| 19 | string.push_str("test"); | |
| 20 | //~^ ERROR cannot borrow `*string` as mutable, as it is behind a `&` reference | |
| 21 | } |
tests/ui/borrowck/suggestions/overloaded-index-not-mut-but-should-be-mut.stderr created+38| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | error[E0596]: cannot borrow `*string` as mutable, as it is behind a `&` reference | |
| 2 | --> $DIR/overloaded-index-not-mut-but-should-be-mut.rs:7:5 | |
| 3 | | | |
| 4 | LL | string.push_str("test"); | |
| 5 | | ^^^^^^ `string` is a `&` reference, so the data it refers to cannot be borrowed as mutable | |
| 6 | | | |
| 7 | help: consider using `get_mut` | |
| 8 | | | |
| 9 | LL - let string = &map[&0]; | |
| 10 | LL + let string = map.get_mut(&0).unwrap(); | |
| 11 | | | |
| 12 | ||
| 13 | error[E0596]: cannot borrow `*string` as mutable, as it is behind a `&` reference | |
| 14 | --> $DIR/overloaded-index-not-mut-but-should-be-mut.rs:14:5 | |
| 15 | | | |
| 16 | LL | string.push_str("test"); | |
| 17 | | ^^^^^^ `string` is a `&` reference, so the data it refers to cannot be borrowed as mutable | |
| 18 | | | |
| 19 | help: consider using `get_mut` | |
| 20 | | | |
| 21 | LL - let string = &map[&0]; | |
| 22 | LL + let string = map.get_mut(&0).unwrap(); | |
| 23 | | | |
| 24 | ||
| 25 | error[E0596]: cannot borrow `*string` as mutable, as it is behind a `&` reference | |
| 26 | --> $DIR/overloaded-index-not-mut-but-should-be-mut.rs:19:5 | |
| 27 | | | |
| 28 | LL | string.push_str("test"); | |
| 29 | | ^^^^^^ `string` is a `&` reference, so the data it refers to cannot be borrowed as mutable | |
| 30 | | | |
| 31 | help: consider changing this to be a mutable reference | |
| 32 | | | |
| 33 | LL | let string = &mut vec[0]; | |
| 34 | | +++ | |
| 35 | ||
| 36 | error: aborting due to 3 previous errors | |
| 37 | ||
| 38 | For more information about this error, try `rustc --explain E0596`. |
tests/ui/borrowck/suggestions/overloaded-index-without-indexmut.rs created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | use std::ops::Index; | |
| 2 | ||
| 3 | struct MyType; | |
| 4 | impl Index<usize> for MyType { | |
| 5 | type Output = String; | |
| 6 | fn index(&self, _idx: usize) -> &String { | |
| 7 | const { &String::new() } | |
| 8 | } | |
| 9 | } | |
| 10 | ||
| 11 | fn main() { | |
| 12 | let x = MyType; | |
| 13 | let y = &x[0]; | |
| 14 | y.push_str(""); | |
| 15 | //~^ ERROR cannot borrow `*y` as mutable, as it is behind a `&` reference | |
| 16 | } |
tests/ui/borrowck/suggestions/overloaded-index-without-indexmut.stderr created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | error[E0596]: cannot borrow `*y` as mutable, as it is behind a `&` reference | |
| 2 | --> $DIR/overloaded-index-without-indexmut.rs:14:5 | |
| 3 | | | |
| 4 | LL | y.push_str(""); | |
| 5 | | ^ `y` is a `&` reference, so the data it refers to cannot be borrowed as mutable | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 | ||
| 9 | For more information about this error, try `rustc --explain E0596`. |
tests/ui/coverage-attr/name-value.stderr+2-2| ... | ... | @@ -22,10 +22,10 @@ LL | #![coverage = "off"] |
| 22 | 22 | help: try changing it to one of the following valid forms of the attribute |
| 23 | 23 | | |
| 24 | 24 | LL - #![coverage = "off"] |
| 25 | LL + #[coverage(off)] | |
| 25 | LL + #![coverage(off)] | |
| 26 | 26 | | |
| 27 | 27 | LL - #![coverage = "off"] |
| 28 | LL + #[coverage(on)] | |
| 28 | LL + #![coverage(on)] | |
| 29 | 29 | | |
| 30 | 30 | |
| 31 | 31 | error[E0539]: malformed `coverage` attribute input |
tests/ui/coverage-attr/word-only.stderr+4-6| ... | ... | @@ -19,12 +19,10 @@ LL | #![coverage] |
| 19 | 19 | | |
| 20 | 20 | help: try changing it to one of the following valid forms of the attribute |
| 21 | 21 | | |
| 22 | LL - #![coverage] | |
| 23 | LL + #[coverage(off)] | |
| 24 | | | |
| 25 | LL - #![coverage] | |
| 26 | LL + #[coverage(on)] | |
| 27 | | | |
| 22 | LL | #![coverage(off)] | |
| 23 | | +++++ | |
| 24 | LL | #![coverage(on)] | |
| 25 | | ++++ | |
| 28 | 26 | |
| 29 | 27 | error[E0539]: malformed `coverage` attribute input |
| 30 | 28 | --> $DIR/word-only.rs:21:1 |
tests/ui/deriving/deriving-all-codegen.rs+15-3| ... | ... | @@ -18,6 +18,8 @@ |
| 18 | 18 | #![allow(deprecated)] |
| 19 | 19 | #![feature(derive_from)] |
| 20 | 20 | |
| 21 | use std::from::From; | |
| 22 | ||
| 21 | 23 | // Empty struct. |
| 22 | 24 | #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] |
| 23 | 25 | struct Empty; |
| ... | ... | @@ -51,7 +53,14 @@ struct SingleField { |
| 51 | 53 | // `clone` implemention that just does `*self`. |
| 52 | 54 | #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] |
| 53 | 55 | struct Big { |
| 54 | b1: u32, b2: u32, b3: u32, b4: u32, b5: u32, b6: u32, b7: u32, b8: u32, | |
| 56 | b1: u32, | |
| 57 | b2: u32, | |
| 58 | b3: u32, | |
| 59 | b4: u32, | |
| 60 | b5: u32, | |
| 61 | b6: u32, | |
| 62 | b7: u32, | |
| 63 | b8: u32, | |
| 55 | 64 | } |
| 56 | 65 | |
| 57 | 66 | // It is more efficient to compare scalar types before non-scalar types. |
| ... | ... | @@ -126,7 +135,7 @@ enum Enum0 {} |
| 126 | 135 | // A single-variant enum. |
| 127 | 136 | #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] |
| 128 | 137 | enum Enum1 { |
| 129 | Single { x: u32 } | |
| 138 | Single { x: u32 }, | |
| 130 | 139 | } |
| 131 | 140 | |
| 132 | 141 | // A C-like, fieldless enum with a single variant. |
| ... | ... | @@ -152,7 +161,10 @@ enum Mixed { |
| 152 | 161 | P, |
| 153 | 162 | Q, |
| 154 | 163 | R(u32), |
| 155 | S { d1: Option<u32>, d2: Option<i32> }, | |
| 164 | S { | |
| 165 | d1: Option<u32>, | |
| 166 | d2: Option<i32>, | |
| 167 | }, | |
| 156 | 168 | } |
| 157 | 169 | |
| 158 | 170 | // When comparing enum variant it is more efficient to compare scalar types before non-scalar types. |
tests/ui/deriving/deriving-all-codegen.stdout+2| ... | ... | @@ -23,6 +23,8 @@ extern crate std; |
| 23 | 23 | #[prelude_import] |
| 24 | 24 | use std::prelude::rust_2021::*; |
| 25 | 25 | |
| 26 | use std::from::From; | |
| 27 | ||
| 26 | 28 | // Empty struct. |
| 27 | 29 | struct Empty; |
| 28 | 30 | #[automatically_derived] |
tests/ui/deriving/deriving-from-wrong-target.rs+2-1| ... | ... | @@ -1,9 +1,10 @@ |
| 1 | //@ edition: 2021 | |
| 2 | 1 | //@ check-fail |
| 3 | 2 | |
| 4 | 3 | #![feature(derive_from)] |
| 5 | 4 | #![allow(dead_code)] |
| 6 | 5 | |
| 6 | use std::from::From; | |
| 7 | ||
| 7 | 8 | #[derive(From)] |
| 8 | 9 | //~^ ERROR `#[derive(From)]` used on a struct with no fields |
| 9 | 10 | struct S1; |
tests/ui/deriving/deriving-from-wrong-target.stderr+9-9| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | error: `#[derive(From)]` used on a struct with no fields |
| 2 | --> $DIR/deriving-from-wrong-target.rs:7:10 | |
| 2 | --> $DIR/deriving-from-wrong-target.rs:8:10 | |
| 3 | 3 | | |
| 4 | 4 | LL | #[derive(From)] |
| 5 | 5 | | ^^^^ |
| ... | ... | @@ -10,7 +10,7 @@ LL | struct S1; |
| 10 | 10 | = note: `#[derive(From)]` can only be used on structs with exactly one field |
| 11 | 11 | |
| 12 | 12 | error: `#[derive(From)]` used on a struct with no fields |
| 13 | --> $DIR/deriving-from-wrong-target.rs:11:10 | |
| 13 | --> $DIR/deriving-from-wrong-target.rs:12:10 | |
| 14 | 14 | | |
| 15 | 15 | LL | #[derive(From)] |
| 16 | 16 | | ^^^^ |
| ... | ... | @@ -21,7 +21,7 @@ LL | struct S2 {} |
| 21 | 21 | = note: `#[derive(From)]` can only be used on structs with exactly one field |
| 22 | 22 | |
| 23 | 23 | error: `#[derive(From)]` used on a struct with multiple fields |
| 24 | --> $DIR/deriving-from-wrong-target.rs:15:10 | |
| 24 | --> $DIR/deriving-from-wrong-target.rs:16:10 | |
| 25 | 25 | | |
| 26 | 26 | LL | #[derive(From)] |
| 27 | 27 | | ^^^^ |
| ... | ... | @@ -32,7 +32,7 @@ LL | struct S3(u32, bool); |
| 32 | 32 | = note: `#[derive(From)]` can only be used on structs with exactly one field |
| 33 | 33 | |
| 34 | 34 | error: `#[derive(From)]` used on a struct with multiple fields |
| 35 | --> $DIR/deriving-from-wrong-target.rs:19:10 | |
| 35 | --> $DIR/deriving-from-wrong-target.rs:20:10 | |
| 36 | 36 | | |
| 37 | 37 | LL | #[derive(From)] |
| 38 | 38 | | ^^^^ |
| ... | ... | @@ -43,7 +43,7 @@ LL | struct S4 { |
| 43 | 43 | = note: `#[derive(From)]` can only be used on structs with exactly one field |
| 44 | 44 | |
| 45 | 45 | error: `#[derive(From)]` used on an enum |
| 46 | --> $DIR/deriving-from-wrong-target.rs:26:10 | |
| 46 | --> $DIR/deriving-from-wrong-target.rs:27:10 | |
| 47 | 47 | | |
| 48 | 48 | LL | #[derive(From)] |
| 49 | 49 | | ^^^^ |
| ... | ... | @@ -54,7 +54,7 @@ LL | enum E1 {} |
| 54 | 54 | = note: `#[derive(From)]` can only be used on structs with exactly one field |
| 55 | 55 | |
| 56 | 56 | error[E0277]: the size for values of type `T` cannot be known at compilation time |
| 57 | --> $DIR/deriving-from-wrong-target.rs:30:10 | |
| 57 | --> $DIR/deriving-from-wrong-target.rs:31:10 | |
| 58 | 58 | | |
| 59 | 59 | LL | #[derive(From)] |
| 60 | 60 | | ^^^^ doesn't have a size known at compile-time |
| ... | ... | @@ -71,7 +71,7 @@ LL + struct SUnsizedField<T> { |
| 71 | 71 | | |
| 72 | 72 | |
| 73 | 73 | error[E0277]: the size for values of type `T` cannot be known at compilation time |
| 74 | --> $DIR/deriving-from-wrong-target.rs:30:10 | |
| 74 | --> $DIR/deriving-from-wrong-target.rs:31:10 | |
| 75 | 75 | | |
| 76 | 76 | LL | #[derive(From)] |
| 77 | 77 | | ^^^^ doesn't have a size known at compile-time |
| ... | ... | @@ -80,7 +80,7 @@ LL | struct SUnsizedField<T: ?Sized> { |
| 80 | 80 | | - this type parameter needs to be `Sized` |
| 81 | 81 | | |
| 82 | 82 | note: required because it appears within the type `SUnsizedField<T>` |
| 83 | --> $DIR/deriving-from-wrong-target.rs:33:8 | |
| 83 | --> $DIR/deriving-from-wrong-target.rs:34:8 | |
| 84 | 84 | | |
| 85 | 85 | LL | struct SUnsizedField<T: ?Sized> { |
| 86 | 86 | | ^^^^^^^^^^^^^ |
| ... | ... | @@ -92,7 +92,7 @@ LL + struct SUnsizedField<T> { |
| 92 | 92 | | |
| 93 | 93 | |
| 94 | 94 | error[E0277]: the size for values of type `T` cannot be known at compilation time |
| 95 | --> $DIR/deriving-from-wrong-target.rs:34:11 | |
| 95 | --> $DIR/deriving-from-wrong-target.rs:35:11 | |
| 96 | 96 | | |
| 97 | 97 | LL | struct SUnsizedField<T: ?Sized> { |
| 98 | 98 | | - this type parameter needs to be `Sized` |
tests/ui/deriving/deriving-from.rs+2| ... | ... | @@ -3,6 +3,8 @@ |
| 3 | 3 | |
| 4 | 4 | #![feature(derive_from)] |
| 5 | 5 | |
| 6 | use core::from::From; | |
| 7 | ||
| 6 | 8 | #[derive(From)] |
| 7 | 9 | struct TupleSimple(u32); |
| 8 | 10 |
tests/ui/feature-gates/feature-gate-derive-from.rs+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | //@ edition: 2021 | |
| 1 | use std::from::From; //~ ERROR use of unstable library feature `derive_from | |
| 2 | 2 | |
| 3 | 3 | #[derive(From)] //~ ERROR use of unstable library feature `derive_from` |
| 4 | 4 | struct Foo(u32); |
tests/ui/feature-gates/feature-gate-derive-from.stderr+11-1| ... | ... | @@ -8,6 +8,16 @@ LL | #[derive(From)] |
| 8 | 8 | = help: add `#![feature(derive_from)]` to the crate attributes to enable |
| 9 | 9 | = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date |
| 10 | 10 | |
| 11 | error: aborting due to 1 previous error | |
| 11 | error[E0658]: use of unstable library feature `derive_from` | |
| 12 | --> $DIR/feature-gate-derive-from.rs:1:5 | |
| 13 | | | |
| 14 | LL | use std::from::From; | |
| 15 | | ^^^^^^^^^^^^^^^ | |
| 16 | | | |
| 17 | = note: see issue #144889 <https://github.com/rust-lang/rust/issues/144889> for more information | |
| 18 | = help: add `#![feature(derive_from)]` to the crate attributes to enable | |
| 19 | = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date | |
| 20 | ||
| 21 | error: aborting due to 2 previous errors | |
| 12 | 22 | |
| 13 | 23 | For more information about this error, try `rustc --explain E0658`. |
tests/ui/feature-gates/feature-gate-no_sanitize.rs deleted-4| ... | ... | @@ -1,4 +0,0 @@ |
| 1 | #[no_sanitize(address)] | |
| 2 | //~^ ERROR the `#[no_sanitize]` attribute is an experimental feature | |
| 3 | fn main() { | |
| 4 | } |
tests/ui/feature-gates/feature-gate-no_sanitize.stderr deleted-13| ... | ... | @@ -1,13 +0,0 @@ |
| 1 | error[E0658]: the `#[no_sanitize]` attribute is an experimental feature | |
| 2 | --> $DIR/feature-gate-no_sanitize.rs:1:1 | |
| 3 | | | |
| 4 | LL | #[no_sanitize(address)] | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | | | |
| 7 | = note: see issue #39699 <https://github.com/rust-lang/rust/issues/39699> for more information | |
| 8 | = help: add `#![feature(no_sanitize)]` to the crate attributes to enable | |
| 9 | = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date | |
| 10 | ||
| 11 | error: aborting due to 1 previous error | |
| 12 | ||
| 13 | For more information about this error, try `rustc --explain E0658`. |
tests/ui/feature-gates/feature-gate-sanitize.rs created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | //@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION" | |
| 2 | #![feature(no_sanitize)] //~ ERROR feature has been removed | |
| 3 | ||
| 4 | #[sanitize(address = "on")] | |
| 5 | //~^ ERROR the `#[sanitize]` attribute is an experimental feature | |
| 6 | fn main() { | |
| 7 | } |
tests/ui/feature-gates/feature-gate-sanitize.stderr created+23| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | error[E0557]: feature has been removed | |
| 2 | --> $DIR/feature-gate-sanitize.rs:2:12 | |
| 3 | | | |
| 4 | LL | #![feature(no_sanitize)] | |
| 5 | | ^^^^^^^^^^^ feature has been removed | |
| 6 | | | |
| 7 | = note: removed in CURRENT_RUSTC_VERSION; see <https://github.com/rust-lang/rust/pull/142681> for more information | |
| 8 | = note: renamed to sanitize(xyz = "on|off") | |
| 9 | ||
| 10 | error[E0658]: the `#[sanitize]` attribute is an experimental feature | |
| 11 | --> $DIR/feature-gate-sanitize.rs:4:1 | |
| 12 | | | |
| 13 | LL | #[sanitize(address = "on")] | |
| 14 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 15 | | | |
| 16 | = note: see issue #39699 <https://github.com/rust-lang/rust/issues/39699> for more information | |
| 17 | = help: add `#![feature(sanitize)]` to the crate attributes to enable | |
| 18 | = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date | |
| 19 | ||
| 20 | error: aborting due to 2 previous errors | |
| 21 | ||
| 22 | Some errors have detailed explanations: E0557, E0658. | |
| 23 | For more information about an error, try `rustc --explain E0557`. |
tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr+3-3| ... | ... | @@ -9,11 +9,11 @@ LL | print_items::<WindowsMut<'_>>(windows); |
| 9 | 9 | LL | } |
| 10 | 10 | | - temporary value is freed at the end of this statement |
| 11 | 11 | | |
| 12 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 13 | --> $DIR/hrtb-implied-1.rs:26:26 | |
| 12 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 13 | --> $DIR/hrtb-implied-1.rs:26:5 | |
| 14 | 14 | | |
| 15 | 15 | LL | for<'a> I::Item<'a>: Debug, |
| 16 | | ^^^^^ | |
| 16 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 17 | 17 | |
| 18 | 18 | error: aborting due to 1 previous error |
| 19 | 19 |
tests/ui/generic-associated-types/bugs/hrtb-implied-2.stderr+5-1| ... | ... | @@ -15,7 +15,11 @@ LL | let _next = iter2.next(); |
| 15 | 15 | = note: requirement occurs because of a mutable reference to `Eat<&mut I, F>` |
| 16 | 16 | = note: mutable references are invariant over their type parameter |
| 17 | 17 | = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance |
| 18 | = note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 18 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 19 | --> $DIR/hrtb-implied-2.rs:31:8 | |
| 20 | | | |
| 21 | LL | F: FnMut(I::Item<'_>), | |
| 22 | | ^^^^^^^^^^^^^^^^^^ | |
| 19 | 23 | |
| 20 | 24 | error: aborting due to 1 previous error |
| 21 | 25 |
tests/ui/generic-associated-types/bugs/hrtb-implied-3.stderr+3-3| ... | ... | @@ -11,11 +11,11 @@ LL | trivial_bound(iter); |
| 11 | 11 | | `iter` escapes the function body here |
| 12 | 12 | | argument requires that `'1` must outlive `'static` |
| 13 | 13 | | |
| 14 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 15 | --> $DIR/hrtb-implied-3.rs:14:26 | |
| 14 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 15 | --> $DIR/hrtb-implied-3.rs:14:5 | |
| 16 | 16 | | |
| 17 | 17 | LL | for<'a> I::Item<'a>: Sized, |
| 18 | | ^^^^^ | |
| 18 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 19 | 19 | |
| 20 | 20 | error: aborting due to 1 previous error |
| 21 | 21 |
tests/ui/generic-associated-types/collectivity-regression.stderr+1-1| ... | ... | @@ -7,7 +7,7 @@ LL | | let _x = x; |
| 7 | 7 | LL | | }; |
| 8 | 8 | | |_____^ |
| 9 | 9 | | |
| 10 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 10 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 11 | 11 | --> $DIR/collectivity-regression.rs:11:16 |
| 12 | 12 | | |
| 13 | 13 | LL | for<'a> T: Get<Value<'a> = ()>, |
tests/ui/generic-associated-types/extended/lending_iterator.stderr+6| ... | ... | @@ -12,6 +12,12 @@ error: `Self` does not live long enough |
| 12 | 12 | | |
| 13 | 13 | LL | <B as FromLendingIterator<A>>::from_iter(self) |
| 14 | 14 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 15 | | | |
| 16 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 17 | --> $DIR/lending_iterator.rs:4:21 | |
| 18 | | | |
| 19 | LL | fn from_iter<T: for<'x> LendingIterator<Item<'x> = A>>(iter: T) -> Self; | |
| 20 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 15 | 21 | |
| 16 | 22 | error: aborting due to 2 previous errors |
| 17 | 23 |
tests/ui/higher-ranked/trait-bounds/hrtb-just-for-static.stderr+1-1| ... | ... | @@ -15,7 +15,7 @@ LL | fn give_some<'a>() { |
| 15 | 15 | LL | want_hrtb::<&'a u32>() |
| 16 | 16 | | ^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` |
| 17 | 17 | | |
| 18 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 18 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 19 | 19 | --> $DIR/hrtb-just-for-static.rs:9:15 |
| 20 | 20 | | |
| 21 | 21 | LL | where T : for<'a> Foo<&'a isize> |
tests/ui/higher-ranked/trait-bounds/hrtb-perfect-forwarding.stderr+1-1| ... | ... | @@ -47,7 +47,7 @@ LL | fn foo_hrtb_bar_not<'b, T>(mut t: T) |
| 47 | 47 | LL | foo_hrtb_bar_not(&mut t); |
| 48 | 48 | | ^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static` |
| 49 | 49 | | |
| 50 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 50 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 51 | 51 | --> $DIR/hrtb-perfect-forwarding.rs:37:8 |
| 52 | 52 | | |
| 53 | 53 | LL | T: for<'a> Foo<&'a isize> + Bar<&'b isize>, |
tests/ui/implied-bounds/normalization-placeholder-leak.fail.stderr+12| ... | ... | @@ -30,6 +30,12 @@ LL | fn test_lifetime<'lt, T: Trait>(_: Foo<&'lt u8>) {} |
| 30 | 30 | | | | |
| 31 | 31 | | | lifetime `'lt` defined here |
| 32 | 32 | | requires that `'lt` must outlive `'static` |
| 33 | | | |
| 34 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 35 | --> $DIR/normalization-placeholder-leak.rs:19:5 | |
| 36 | | | |
| 37 | LL | for<'x> T::Ty<'x>: Sized; | |
| 38 | | ^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 33 | 39 | |
| 34 | 40 | error: lifetime may not live long enough |
| 35 | 41 | --> $DIR/normalization-placeholder-leak.rs:38:5 |
| ... | ... | @@ -39,6 +45,12 @@ LL | fn test_alias<'lt, T: AnotherTrait>(_: Foo<T::Ty2::<'lt>>) {} |
| 39 | 45 | | | | |
| 40 | 46 | | | lifetime `'lt` defined here |
| 41 | 47 | | requires that `'lt` must outlive `'static` |
| 48 | | | |
| 49 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 50 | --> $DIR/normalization-placeholder-leak.rs:19:5 | |
| 51 | | | |
| 52 | LL | for<'x> T::Ty<'x>: Sized; | |
| 53 | | ^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 42 | 54 | |
| 43 | 55 | error: aborting due to 6 previous errors |
| 44 | 56 |
tests/ui/inference/issue-72616.stderr+4-8| ... | ... | @@ -6,14 +6,10 @@ LL | if String::from("a") == "a".try_into().unwrap() {} |
| 6 | 6 | | | |
| 7 | 7 | | type must be known at this point |
| 8 | 8 | | |
| 9 | = note: cannot satisfy `String: PartialEq<_>` | |
| 10 | = help: the following types implement trait `PartialEq<Rhs>`: | |
| 11 | `String` implements `PartialEq<&str>` | |
| 12 | `String` implements `PartialEq<ByteStr>` | |
| 13 | `String` implements `PartialEq<ByteString>` | |
| 14 | `String` implements `PartialEq<Cow<'_, str>>` | |
| 15 | `String` implements `PartialEq<str>` | |
| 16 | `String` implements `PartialEq` | |
| 9 | = note: multiple `impl`s satisfying `String: PartialEq<_>` found in the following crates: `alloc`, `std`: | |
| 10 | - impl PartialEq for String; | |
| 11 | - impl PartialEq<Path> for String; | |
| 12 | - impl PartialEq<PathBuf> for String; | |
| 17 | 13 | help: try using a fully qualified path to specify the expected types |
| 18 | 14 | | |
| 19 | 15 | LL - if String::from("a") == "a".try_into().unwrap() {} |
tests/ui/invalid/invalid-no-sanitize.rs deleted-5| ... | ... | @@ -1,5 +0,0 @@ |
| 1 | #![feature(no_sanitize)] | |
| 2 | ||
| 3 | #[no_sanitize(brontosaurus)] //~ ERROR invalid argument | |
| 4 | fn main() { | |
| 5 | } |
tests/ui/invalid/invalid-no-sanitize.stderr deleted-10| ... | ... | @@ -1,10 +0,0 @@ |
| 1 | error: invalid argument for `no_sanitize` | |
| 2 | --> $DIR/invalid-no-sanitize.rs:3:15 | |
| 3 | | | |
| 4 | LL | #[no_sanitize(brontosaurus)] | |
| 5 | | ^^^^^^^^^^^^ | |
| 6 | | | |
| 7 | = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` | |
| 8 | ||
| 9 | error: aborting due to 1 previous error | |
| 10 |
tests/ui/issues/issue-26217.stderr+3-3| ... | ... | @@ -6,11 +6,11 @@ LL | fn bar<'a>() { |
| 6 | 6 | LL | foo::<&'a i32>(); |
| 7 | 7 | | ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` |
| 8 | 8 | | |
| 9 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 10 | --> $DIR/issue-26217.rs:1:30 | |
| 9 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 10 | --> $DIR/issue-26217.rs:1:19 | |
| 11 | 11 | | |
| 12 | 12 | LL | fn foo<T>() where for<'a> T: 'a {} |
| 13 | | ^^ | |
| 13 | | ^^^^^^^^^^^^^ | |
| 14 | 14 | |
| 15 | 15 | error: aborting due to 1 previous error |
| 16 | 16 |
tests/ui/lifetimes/issue-105507.fixed+2-2| ... | ... | @@ -25,8 +25,8 @@ impl<T> ProjectedMyTrait for T |
| 25 | 25 | where |
| 26 | 26 | T: Project, |
| 27 | 27 | for<'a> T::Projected<'a>: MyTrait, |
| 28 | //~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 29 | //~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 28 | //~^ NOTE due to a current limitation of the type system, this implies a `'static` lifetime | |
| 29 | //~| NOTE due to a current limitation of the type system, this implies a `'static` lifetime | |
| 30 | 30 | {} |
| 31 | 31 | |
| 32 | 32 | fn require_trait<T: MyTrait>(_: T) {} |
tests/ui/lifetimes/issue-105507.rs+2-2| ... | ... | @@ -25,8 +25,8 @@ impl<T> ProjectedMyTrait for T |
| 25 | 25 | where |
| 26 | 26 | T: Project, |
| 27 | 27 | for<'a> T::Projected<'a>: MyTrait, |
| 28 | //~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 29 | //~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 28 | //~^ NOTE due to a current limitation of the type system, this implies a `'static` lifetime | |
| 29 | //~| NOTE due to a current limitation of the type system, this implies a `'static` lifetime | |
| 30 | 30 | {} |
| 31 | 31 | |
| 32 | 32 | fn require_trait<T: MyTrait>(_: T) {} |
tests/ui/lifetimes/issue-105507.stderr+2-2| ... | ... | @@ -4,7 +4,7 @@ error: `T` does not live long enough |
| 4 | 4 | LL | require_trait(wrap); |
| 5 | 5 | | ^^^^^^^^^^^^^^^^^^^ |
| 6 | 6 | | |
| 7 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 7 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 8 | 8 | --> $DIR/issue-105507.rs:27:35 |
| 9 | 9 | | |
| 10 | 10 | LL | for<'a> T::Projected<'a>: MyTrait, |
| ... | ... | @@ -20,7 +20,7 @@ error: `U` does not live long enough |
| 20 | 20 | LL | require_trait(wrap1); |
| 21 | 21 | | ^^^^^^^^^^^^^^^^^^^^ |
| 22 | 22 | | |
| 23 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 23 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 24 | 24 | --> $DIR/issue-105507.rs:27:35 |
| 25 | 25 | | |
| 26 | 26 | LL | for<'a> T::Projected<'a>: MyTrait, |
tests/ui/mismatched_types/closure-arg-type-mismatch.stderr+1-1| ... | ... | @@ -57,7 +57,7 @@ LL | baz(f); |
| 57 | 57 | = note: requirement occurs because of a mutable pointer to `&u32` |
| 58 | 58 | = note: mutable pointers are invariant over their type parameter |
| 59 | 59 | = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance |
| 60 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 60 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 61 | 61 | --> $DIR/closure-arg-type-mismatch.rs:8:11 |
| 62 | 62 | | |
| 63 | 63 | LL | fn baz<F: Fn(*mut &u32)>(_: F) {} |
tests/ui/nll/local-outlives-static-via-hrtb.stderr+6-6| ... | ... | @@ -12,11 +12,11 @@ LL | assert_static_via_hrtb_with_assoc_type(&&local); |
| 12 | 12 | LL | } |
| 13 | 13 | | - `local` dropped here while still borrowed |
| 14 | 14 | | |
| 15 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 16 | --> $DIR/local-outlives-static-via-hrtb.rs:15:53 | |
| 15 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 16 | --> $DIR/local-outlives-static-via-hrtb.rs:15:42 | |
| 17 | 17 | | |
| 18 | 18 | LL | fn assert_static_via_hrtb<G>(_: G) where for<'a> G: Outlives<'a> {} |
| 19 | | ^^^^^^^^^^^^ | |
| 19 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 20 | 20 | |
| 21 | 21 | error[E0597]: `local` does not live long enough |
| 22 | 22 | --> $DIR/local-outlives-static-via-hrtb.rs:25:45 |
| ... | ... | @@ -32,11 +32,11 @@ LL | assert_static_via_hrtb_with_assoc_type(&&local); |
| 32 | 32 | LL | } |
| 33 | 33 | | - `local` dropped here while still borrowed |
| 34 | 34 | | |
| 35 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 36 | --> $DIR/local-outlives-static-via-hrtb.rs:19:20 | |
| 35 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 36 | --> $DIR/local-outlives-static-via-hrtb.rs:19:5 | |
| 37 | 37 | | |
| 38 | 38 | LL | for<'a> &'a T: Reference<AssociatedType = &'a ()>, |
| 39 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 39 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 40 | 40 | |
| 41 | 41 | error: aborting due to 2 previous errors |
| 42 | 42 |
tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr+3-3| ... | ... | @@ -13,11 +13,11 @@ LL | let b = |_| &a; |
| 13 | 13 | LL | } |
| 14 | 14 | | - `a` dropped here while still borrowed |
| 15 | 15 | | |
| 16 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 17 | --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 | |
| 16 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 17 | --> $DIR/location-insensitive-scopes-issue-117146.rs:20:11 | |
| 18 | 18 | | |
| 19 | 19 | LL | fn bad<F: Fn(&()) -> &()>(_: F) {} |
| 20 | | ^^^ | |
| 20 | | ^^^^^^^^^^^^^^ | |
| 21 | 21 | |
| 22 | 22 | error: implementation of `Fn` is not general enough |
| 23 | 23 | --> $DIR/location-insensitive-scopes-issue-117146.rs:13:5 |
tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr+3-3| ... | ... | @@ -13,11 +13,11 @@ LL | let b = |_| &a; |
| 13 | 13 | LL | } |
| 14 | 14 | | - `a` dropped here while still borrowed |
| 15 | 15 | | |
| 16 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 17 | --> $DIR/location-insensitive-scopes-issue-117146.rs:20:22 | |
| 16 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 17 | --> $DIR/location-insensitive-scopes-issue-117146.rs:20:11 | |
| 18 | 18 | | |
| 19 | 19 | LL | fn bad<F: Fn(&()) -> &()>(_: F) {} |
| 20 | | ^^^ | |
| 20 | | ^^^^^^^^^^^^^^ | |
| 21 | 21 | |
| 22 | 22 | error: implementation of `Fn` is not general enough |
| 23 | 23 | --> $DIR/location-insensitive-scopes-issue-117146.rs:13:5 |
tests/ui/nll/type-test-universe.stderr+3-3| ... | ... | @@ -12,11 +12,11 @@ LL | fn test2<'a>() { |
| 12 | 12 | LL | outlives_forall::<Value<'a>>(); |
| 13 | 13 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` |
| 14 | 14 | | |
| 15 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 16 | --> $DIR/type-test-universe.rs:6:16 | |
| 15 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 16 | --> $DIR/type-test-universe.rs:6:5 | |
| 17 | 17 | | |
| 18 | 18 | LL | for<'u> T: 'u, |
| 19 | | ^^ | |
| 19 | | ^^^^^^^^^^^^^ | |
| 20 | 20 | |
| 21 | 21 | error: aborting due to 2 previous errors |
| 22 | 22 |
tests/ui/reachable/expr_cast.rs+16-8| ... | ... | @@ -1,13 +1,21 @@ |
| 1 | #![allow(unused_variables)] | |
| 2 | #![allow(unused_assignments)] | |
| 3 | #![allow(dead_code)] | |
| 1 | //@ check-pass | |
| 2 | //@ edition: 2024 | |
| 3 | // | |
| 4 | // Check that we don't warn on `as` casts of never to any as unreachable. | |
| 5 | // While they *are* unreachable, sometimes they are required to appeal typeck. | |
| 4 | 6 | #![deny(unreachable_code)] |
| 5 | #![feature(never_type, type_ascription)] | |
| 6 | 7 | |
| 7 | 8 | fn a() { |
| 8 | // the cast is unreachable: | |
| 9 | let x = {return} as !; //~ ERROR unreachable | |
| 10 | //~| ERROR non-primitive cast | |
| 9 | _ = {return} as u32; | |
| 11 | 10 | } |
| 12 | 11 | |
| 13 | fn main() { } | |
| 12 | fn b() { | |
| 13 | (return) as u32; | |
| 14 | } | |
| 15 | ||
| 16 | // example that needs an explicit never-to-any `as` cast | |
| 17 | fn example() -> impl Iterator<Item = u8> { | |
| 18 | todo!() as std::iter::Empty<_> | |
| 19 | } | |
| 20 | ||
| 21 | fn main() {} |
tests/ui/reachable/expr_cast.stderr deleted-24| ... | ... | @@ -1,24 +0,0 @@ |
| 1 | error: unreachable expression | |
| 2 | --> $DIR/expr_cast.rs:9:13 | |
| 3 | | | |
| 4 | LL | let x = {return} as !; | |
| 5 | | ^------^^^^^^ | |
| 6 | | || | |
| 7 | | |any code following this expression is unreachable | |
| 8 | | unreachable expression | |
| 9 | | | |
| 10 | note: the lint level is defined here | |
| 11 | --> $DIR/expr_cast.rs:4:9 | |
| 12 | | | |
| 13 | LL | #![deny(unreachable_code)] | |
| 14 | | ^^^^^^^^^^^^^^^^ | |
| 15 | ||
| 16 | error[E0605]: non-primitive cast: `()` as `!` | |
| 17 | --> $DIR/expr_cast.rs:9:13 | |
| 18 | | | |
| 19 | LL | let x = {return} as !; | |
| 20 | | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object | |
| 21 | ||
| 22 | error: aborting due to 2 previous errors | |
| 23 | ||
| 24 | For more information about this error, try `rustc --explain E0605`. |
tests/ui/reachable/unreachable-try-pattern.rs+1-1| ... | ... | @@ -18,7 +18,7 @@ fn bar(x: Result<!, i32>) -> Result<u32, i32> { |
| 18 | 18 | fn foo(x: Result<!, i32>) -> Result<u32, i32> { |
| 19 | 19 | let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?; |
| 20 | 20 | //~^ WARN unreachable pattern |
| 21 | //~| WARN unreachable expression | |
| 21 | //~| WARN unreachable call | |
| 22 | 22 | Ok(y) |
| 23 | 23 | } |
| 24 | 24 |
tests/ui/reachable/unreachable-try-pattern.stderr+5-6| ... | ... | @@ -1,11 +1,10 @@ |
| 1 | warning: unreachable expression | |
| 2 | --> $DIR/unreachable-try-pattern.rs:19:36 | |
| 1 | warning: unreachable call | |
| 2 | --> $DIR/unreachable-try-pattern.rs:19:33 | |
| 3 | 3 | | |
| 4 | 4 | LL | let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?; |
| 5 | | -^^^^^^^ | |
| 6 | | | | |
| 7 | | unreachable expression | |
| 8 | | any code following this expression is unreachable | |
| 5 | | ^^ - any code following this expression is unreachable | |
| 6 | | | | |
| 7 | | unreachable call | |
| 9 | 8 | | |
| 10 | 9 | note: the lint level is defined here |
| 11 | 10 | --> $DIR/unreachable-try-pattern.rs:3:9 |
tests/ui/resolve/path-attr-in-const-block.stderr+1-1| ... | ... | @@ -11,7 +11,7 @@ LL | #![path = foo!()] |
| 11 | 11 | | ^^^^^^^^^^------^ |
| 12 | 12 | | | | |
| 13 | 13 | | | expected a string literal here |
| 14 | | help: must be of the form: `#[path = "file"]` | |
| 14 | | help: must be of the form: `#![path = "file"]` | |
| 15 | 15 | | |
| 16 | 16 | = note: for more information, visit <https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute> |
| 17 | 17 |
tests/ui/sanitize-attr/invalid-sanitize.rs created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | #![feature(sanitize)] | |
| 2 | ||
| 3 | #[sanitize(brontosaurus = "off")] //~ ERROR invalid argument | |
| 4 | fn main() { | |
| 5 | } | |
| 6 | ||
| 7 | #[sanitize(address = "off")] //~ ERROR multiple `sanitize` attributes | |
| 8 | #[sanitize(address = "off")] | |
| 9 | fn multiple_consistent() {} | |
| 10 | ||
| 11 | #[sanitize(address = "on")] //~ ERROR multiple `sanitize` attributes | |
| 12 | #[sanitize(address = "off")] | |
| 13 | fn multiple_inconsistent() {} | |
| 14 | ||
| 15 | #[sanitize(address = "bogus")] //~ ERROR invalid argument for `sanitize` | |
| 16 | fn wrong_value() {} | |
| 17 | ||
| 18 | #[sanitize = "off"] //~ ERROR malformed `sanitize` attribute input | |
| 19 | fn name_value () {} | |
| 20 | ||
| 21 | #[sanitize] //~ ERROR malformed `sanitize` attribute input | |
| 22 | fn just_word() {} |
tests/ui/sanitize-attr/invalid-sanitize.stderr created+82| ... | ... | @@ -0,0 +1,82 @@ |
| 1 | error: malformed `sanitize` attribute input | |
| 2 | --> $DIR/invalid-sanitize.rs:18:1 | |
| 3 | | | |
| 4 | LL | #[sanitize = "off"] | |
| 5 | | ^^^^^^^^^^^^^^^^^^^ | |
| 6 | | | |
| 7 | help: the following are the possible correct uses | |
| 8 | | | |
| 9 | LL - #[sanitize = "off"] | |
| 10 | LL + #[sanitize(address = "on|off")] | |
| 11 | | | |
| 12 | LL - #[sanitize = "off"] | |
| 13 | LL + #[sanitize(cfi = "on|off")] | |
| 14 | | | |
| 15 | LL - #[sanitize = "off"] | |
| 16 | LL + #[sanitize(hwaddress = "on|off")] | |
| 17 | | | |
| 18 | LL - #[sanitize = "off"] | |
| 19 | LL + #[sanitize(kcfi = "on|off")] | |
| 20 | | | |
| 21 | = and 5 other candidates | |
| 22 | ||
| 23 | error: malformed `sanitize` attribute input | |
| 24 | --> $DIR/invalid-sanitize.rs:21:1 | |
| 25 | | | |
| 26 | LL | #[sanitize] | |
| 27 | | ^^^^^^^^^^^ | |
| 28 | | | |
| 29 | help: the following are the possible correct uses | |
| 30 | | | |
| 31 | LL | #[sanitize(address = "on|off")] | |
| 32 | | ++++++++++++++++++++ | |
| 33 | LL | #[sanitize(cfi = "on|off")] | |
| 34 | | ++++++++++++++++ | |
| 35 | LL | #[sanitize(hwaddress = "on|off")] | |
| 36 | | ++++++++++++++++++++++ | |
| 37 | LL | #[sanitize(kcfi = "on|off")] | |
| 38 | | +++++++++++++++++ | |
| 39 | = and 5 other candidates | |
| 40 | ||
| 41 | error: multiple `sanitize` attributes | |
| 42 | --> $DIR/invalid-sanitize.rs:7:1 | |
| 43 | | | |
| 44 | LL | #[sanitize(address = "off")] | |
| 45 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | |
| 46 | | | |
| 47 | note: attribute also specified here | |
| 48 | --> $DIR/invalid-sanitize.rs:8:1 | |
| 49 | | | |
| 50 | LL | #[sanitize(address = "off")] | |
| 51 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 52 | ||
| 53 | error: multiple `sanitize` attributes | |
| 54 | --> $DIR/invalid-sanitize.rs:11:1 | |
| 55 | | | |
| 56 | LL | #[sanitize(address = "on")] | |
| 57 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | |
| 58 | | | |
| 59 | note: attribute also specified here | |
| 60 | --> $DIR/invalid-sanitize.rs:12:1 | |
| 61 | | | |
| 62 | LL | #[sanitize(address = "off")] | |
| 63 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 64 | ||
| 65 | error: invalid argument for `sanitize` | |
| 66 | --> $DIR/invalid-sanitize.rs:3:1 | |
| 67 | | | |
| 68 | LL | #[sanitize(brontosaurus = "off")] | |
| 69 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 70 | | | |
| 71 | = note: expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread` | |
| 72 | ||
| 73 | error: invalid argument for `sanitize` | |
| 74 | --> $DIR/invalid-sanitize.rs:15:1 | |
| 75 | | | |
| 76 | LL | #[sanitize(address = "bogus")] | |
| 77 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 78 | | | |
| 79 | = note: expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread` | |
| 80 | ||
| 81 | error: aborting due to 6 previous errors | |
| 82 |
tests/ui/sanitize-attr/valid-sanitize.rs created+115| ... | ... | @@ -0,0 +1,115 @@ |
| 1 | //! Tests where the `#[sanitize(..)]` attribute can and cannot be used. | |
| 2 | ||
| 3 | #![feature(sanitize)] | |
| 4 | #![feature(extern_types)] | |
| 5 | #![feature(impl_trait_in_assoc_type)] | |
| 6 | #![warn(unused_attributes)] | |
| 7 | #![sanitize(address = "off", thread = "on")] | |
| 8 | ||
| 9 | #[sanitize(address = "off", thread = "on")] | |
| 10 | mod submod {} | |
| 11 | ||
| 12 | #[sanitize(address = "off")] | |
| 13 | static FOO: u32 = 0; | |
| 14 | ||
| 15 | #[sanitize(thread = "off")] //~ ERROR sanitize attribute not allowed here | |
| 16 | static BAR: u32 = 0; | |
| 17 | ||
| 18 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 19 | type MyTypeAlias = (); | |
| 20 | ||
| 21 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 22 | trait MyTrait { | |
| 23 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 24 | const TRAIT_ASSOC_CONST: u32; | |
| 25 | ||
| 26 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 27 | type TraitAssocType; | |
| 28 | ||
| 29 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 30 | fn trait_method(&self); | |
| 31 | ||
| 32 | #[sanitize(address = "off", thread = "on")] | |
| 33 | fn trait_method_with_default(&self) {} | |
| 34 | ||
| 35 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 36 | fn trait_assoc_fn(); | |
| 37 | } | |
| 38 | ||
| 39 | #[sanitize(address = "off")] | |
| 40 | impl MyTrait for () { | |
| 41 | const TRAIT_ASSOC_CONST: u32 = 0; | |
| 42 | ||
| 43 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 44 | type TraitAssocType = Self; | |
| 45 | ||
| 46 | #[sanitize(address = "off", thread = "on")] | |
| 47 | fn trait_method(&self) {} | |
| 48 | #[sanitize(address = "off", thread = "on")] | |
| 49 | fn trait_method_with_default(&self) {} | |
| 50 | #[sanitize(address = "off", thread = "on")] | |
| 51 | fn trait_assoc_fn() {} | |
| 52 | } | |
| 53 | ||
| 54 | trait HasAssocType { | |
| 55 | type T; | |
| 56 | fn constrain_assoc_type() -> Self::T; | |
| 57 | } | |
| 58 | ||
| 59 | impl HasAssocType for () { | |
| 60 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 61 | type T = impl Copy; | |
| 62 | fn constrain_assoc_type() -> Self::T {} | |
| 63 | } | |
| 64 | ||
| 65 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 66 | struct MyStruct { | |
| 67 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 68 | field: u32, | |
| 69 | } | |
| 70 | ||
| 71 | #[sanitize(address = "off", thread = "on")] | |
| 72 | impl MyStruct { | |
| 73 | #[sanitize(address = "off", thread = "on")] | |
| 74 | fn method(&self) {} | |
| 75 | #[sanitize(address = "off", thread = "on")] | |
| 76 | fn assoc_fn() {} | |
| 77 | } | |
| 78 | ||
| 79 | extern "C" { | |
| 80 | #[sanitize(address = "off", thread = "on")] //~ ERROR sanitize attribute not allowed here | |
| 81 | static X: u32; | |
| 82 | ||
| 83 | #[sanitize(address = "off", thread = "on")] //~ ERROR sanitize attribute not allowed here | |
| 84 | type T; | |
| 85 | ||
| 86 | #[sanitize(address = "off", thread = "on")] //~ ERROR sanitize attribute not allowed here | |
| 87 | fn foreign_fn(); | |
| 88 | } | |
| 89 | ||
| 90 | #[sanitize(address = "off", thread = "on")] | |
| 91 | fn main() { | |
| 92 | #[sanitize(address = "off", thread = "on")] //~ ERROR sanitize attribute not allowed here | |
| 93 | let _ = (); | |
| 94 | ||
| 95 | // Currently not allowed on let statements, even if they bind to a closure. | |
| 96 | // It might be nice to support this as a special case someday, but trying | |
| 97 | // to define the precise boundaries of that special case might be tricky. | |
| 98 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 99 | let _let_closure = || (); | |
| 100 | ||
| 101 | // In situations where attributes can already be applied to expressions, | |
| 102 | // the sanitize attribute is allowed on closure expressions. | |
| 103 | let _closure_tail_expr = { | |
| 104 | #[sanitize(address = "off", thread = "on")] | |
| 105 | || () | |
| 106 | }; | |
| 107 | ||
| 108 | match () { | |
| 109 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 110 | () => (), | |
| 111 | } | |
| 112 | ||
| 113 | #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here | |
| 114 | return (); | |
| 115 | } |
tests/ui/sanitize-attr/valid-sanitize.stderr created+190| ... | ... | @@ -0,0 +1,190 @@ |
| 1 | error: sanitize attribute not allowed here | |
| 2 | --> $DIR/valid-sanitize.rs:15:1 | |
| 3 | | | |
| 4 | LL | #[sanitize(thread = "off")] | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | LL | static BAR: u32 = 0; | |
| 7 | | -------------------- not a function, impl block, or module | |
| 8 | | | |
| 9 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 10 | ||
| 11 | error: sanitize attribute not allowed here | |
| 12 | --> $DIR/valid-sanitize.rs:18:1 | |
| 13 | | | |
| 14 | LL | #[sanitize(address = "off")] | |
| 15 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 16 | LL | type MyTypeAlias = (); | |
| 17 | | ---------------------- not a function, impl block, or module | |
| 18 | | | |
| 19 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 20 | ||
| 21 | error: sanitize attribute not allowed here | |
| 22 | --> $DIR/valid-sanitize.rs:21:1 | |
| 23 | | | |
| 24 | LL | #[sanitize(address = "off")] | |
| 25 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 26 | LL | / trait MyTrait { | |
| 27 | LL | | #[sanitize(address = "off")] | |
| 28 | LL | | const TRAIT_ASSOC_CONST: u32; | |
| 29 | ... | | |
| 30 | LL | | fn trait_assoc_fn(); | |
| 31 | LL | | } | |
| 32 | | |_- not a function, impl block, or module | |
| 33 | | | |
| 34 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 35 | ||
| 36 | error: sanitize attribute not allowed here | |
| 37 | --> $DIR/valid-sanitize.rs:65:1 | |
| 38 | | | |
| 39 | LL | #[sanitize(address = "off")] | |
| 40 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 41 | LL | / struct MyStruct { | |
| 42 | LL | | #[sanitize(address = "off")] | |
| 43 | LL | | field: u32, | |
| 44 | LL | | } | |
| 45 | | |_- not a function, impl block, or module | |
| 46 | | | |
| 47 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 48 | ||
| 49 | error: sanitize attribute not allowed here | |
| 50 | --> $DIR/valid-sanitize.rs:67:5 | |
| 51 | | | |
| 52 | LL | #[sanitize(address = "off")] | |
| 53 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 54 | LL | field: u32, | |
| 55 | | ---------- not a function, impl block, or module | |
| 56 | | | |
| 57 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 58 | ||
| 59 | error: sanitize attribute not allowed here | |
| 60 | --> $DIR/valid-sanitize.rs:92:5 | |
| 61 | | | |
| 62 | LL | #[sanitize(address = "off", thread = "on")] | |
| 63 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 64 | LL | let _ = (); | |
| 65 | | ----------- not a function, impl block, or module | |
| 66 | | | |
| 67 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 68 | ||
| 69 | error: sanitize attribute not allowed here | |
| 70 | --> $DIR/valid-sanitize.rs:98:5 | |
| 71 | | | |
| 72 | LL | #[sanitize(address = "off")] | |
| 73 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 74 | LL | let _let_closure = || (); | |
| 75 | | ------------------------- not a function, impl block, or module | |
| 76 | | | |
| 77 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 78 | ||
| 79 | error: sanitize attribute not allowed here | |
| 80 | --> $DIR/valid-sanitize.rs:109:9 | |
| 81 | | | |
| 82 | LL | #[sanitize(address = "off")] | |
| 83 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 84 | LL | () => (), | |
| 85 | | -------- not a function, impl block, or module | |
| 86 | | | |
| 87 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 88 | ||
| 89 | error: sanitize attribute not allowed here | |
| 90 | --> $DIR/valid-sanitize.rs:113:5 | |
| 91 | | | |
| 92 | LL | #[sanitize(address = "off")] | |
| 93 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 94 | LL | return (); | |
| 95 | | --------- not a function, impl block, or module | |
| 96 | | | |
| 97 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 98 | ||
| 99 | error: sanitize attribute not allowed here | |
| 100 | --> $DIR/valid-sanitize.rs:23:5 | |
| 101 | | | |
| 102 | LL | #[sanitize(address = "off")] | |
| 103 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 104 | LL | const TRAIT_ASSOC_CONST: u32; | |
| 105 | | ----------------------------- not a function, impl block, or module | |
| 106 | | | |
| 107 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 108 | ||
| 109 | error: sanitize attribute not allowed here | |
| 110 | --> $DIR/valid-sanitize.rs:26:5 | |
| 111 | | | |
| 112 | LL | #[sanitize(address = "off")] | |
| 113 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 114 | LL | type TraitAssocType; | |
| 115 | | -------------------- not a function, impl block, or module | |
| 116 | | | |
| 117 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 118 | ||
| 119 | error: sanitize attribute not allowed here | |
| 120 | --> $DIR/valid-sanitize.rs:29:5 | |
| 121 | | | |
| 122 | LL | #[sanitize(address = "off")] | |
| 123 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 124 | LL | fn trait_method(&self); | |
| 125 | | ----------------------- function has no body | |
| 126 | | | |
| 127 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 128 | ||
| 129 | error: sanitize attribute not allowed here | |
| 130 | --> $DIR/valid-sanitize.rs:35:5 | |
| 131 | | | |
| 132 | LL | #[sanitize(address = "off")] | |
| 133 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 134 | LL | fn trait_assoc_fn(); | |
| 135 | | -------------------- function has no body | |
| 136 | | | |
| 137 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 138 | ||
| 139 | error: sanitize attribute not allowed here | |
| 140 | --> $DIR/valid-sanitize.rs:43:5 | |
| 141 | | | |
| 142 | LL | #[sanitize(address = "off")] | |
| 143 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 144 | LL | type TraitAssocType = Self; | |
| 145 | | --------------------------- not a function, impl block, or module | |
| 146 | | | |
| 147 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 148 | ||
| 149 | error: sanitize attribute not allowed here | |
| 150 | --> $DIR/valid-sanitize.rs:60:5 | |
| 151 | | | |
| 152 | LL | #[sanitize(address = "off")] | |
| 153 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 154 | LL | type T = impl Copy; | |
| 155 | | ------------------- not a function, impl block, or module | |
| 156 | | | |
| 157 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 158 | ||
| 159 | error: sanitize attribute not allowed here | |
| 160 | --> $DIR/valid-sanitize.rs:80:5 | |
| 161 | | | |
| 162 | LL | #[sanitize(address = "off", thread = "on")] | |
| 163 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 164 | LL | static X: u32; | |
| 165 | | -------------- not a function, impl block, or module | |
| 166 | | | |
| 167 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 168 | ||
| 169 | error: sanitize attribute not allowed here | |
| 170 | --> $DIR/valid-sanitize.rs:83:5 | |
| 171 | | | |
| 172 | LL | #[sanitize(address = "off", thread = "on")] | |
| 173 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 174 | LL | type T; | |
| 175 | | ------- not a function, impl block, or module | |
| 176 | | | |
| 177 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 178 | ||
| 179 | error: sanitize attribute not allowed here | |
| 180 | --> $DIR/valid-sanitize.rs:86:5 | |
| 181 | | | |
| 182 | LL | #[sanitize(address = "off", thread = "on")] | |
| 183 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 184 | LL | fn foreign_fn(); | |
| 185 | | ---------------- function has no body | |
| 186 | | | |
| 187 | = help: sanitize attribute can be applied to a function (with body), impl block, or module | |
| 188 | ||
| 189 | error: aborting due to 18 previous errors | |
| 190 |
tests/ui/sanitizer/inline-always-sanitize.rs created+15| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | //@ check-pass | |
| 2 | ||
| 3 | #![feature(sanitize)] | |
| 4 | ||
| 5 | #[inline(always)] | |
| 6 | //~^ NOTE inlining requested here | |
| 7 | #[sanitize(address = "off")] | |
| 8 | //~^ WARN setting `sanitize` off will have no effect after inlining | |
| 9 | //~| NOTE on by default | |
| 10 | fn x() { | |
| 11 | } | |
| 12 | ||
| 13 | fn main() { | |
| 14 | x() | |
| 15 | } |
tests/ui/sanitizer/inline-always-sanitize.stderr created+15| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | warning: setting `sanitize` off will have no effect after inlining | |
| 2 | --> $DIR/inline-always-sanitize.rs:7:1 | |
| 3 | | | |
| 4 | LL | #[sanitize(address = "off")] | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | | | |
| 7 | note: inlining requested here | |
| 8 | --> $DIR/inline-always-sanitize.rs:5:1 | |
| 9 | | | |
| 10 | LL | #[inline(always)] | |
| 11 | | ^^^^^^^^^^^^^^^^^ | |
| 12 | = note: `#[warn(inline_no_sanitize)]` on by default | |
| 13 | ||
| 14 | warning: 1 warning emitted | |
| 15 |
tests/ui/sanitizer/inline-always.rs deleted-15| ... | ... | @@ -1,15 +0,0 @@ |
| 1 | //@ check-pass | |
| 2 | ||
| 3 | #![feature(no_sanitize)] | |
| 4 | ||
| 5 | #[inline(always)] | |
| 6 | //~^ NOTE inlining requested here | |
| 7 | #[no_sanitize(address)] | |
| 8 | //~^ WARN will have no effect after inlining | |
| 9 | //~| NOTE on by default | |
| 10 | fn x() { | |
| 11 | } | |
| 12 | ||
| 13 | fn main() { | |
| 14 | x() | |
| 15 | } |
tests/ui/sanitizer/inline-always.stderr deleted-15| ... | ... | @@ -1,15 +0,0 @@ |
| 1 | warning: `no_sanitize` will have no effect after inlining | |
| 2 | --> $DIR/inline-always.rs:7:1 | |
| 3 | | | |
| 4 | LL | #[no_sanitize(address)] | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | | | |
| 7 | note: inlining requested here | |
| 8 | --> $DIR/inline-always.rs:5:1 | |
| 9 | | | |
| 10 | LL | #[inline(always)] | |
| 11 | | ^^^^^^^^^^^^^^^^^ | |
| 12 | = note: `#[warn(inline_no_sanitize)]` on by default | |
| 13 | ||
| 14 | warning: 1 warning emitted | |
| 15 |
tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr+2| ... | ... | @@ -10,6 +10,8 @@ LL | String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); |
| 10 | 10 | `String` implements `PartialEq<ByteStr>` |
| 11 | 11 | `String` implements `PartialEq<ByteString>` |
| 12 | 12 | `String` implements `PartialEq<Cow<'_, str>>` |
| 13 | `String` implements `PartialEq<Path>` | |
| 14 | `String` implements `PartialEq<PathBuf>` | |
| 13 | 15 | `String` implements `PartialEq<str>` |
| 14 | 16 | `String` implements `PartialEq` |
| 15 | 17 | = note: `T` implements `PartialEq<String>` |
tests/ui/transmutability/references/reject_lifetime_extension.stderr+3-3| ... | ... | @@ -67,11 +67,11 @@ LL | unsafe { extend_hrtb(src) } |
| 67 | 67 | | `src` escapes the function body here |
| 68 | 68 | | argument requires that `'a` must outlive `'static` |
| 69 | 69 | | |
| 70 | note: due to current limitations in the borrow checker, this implies a `'static` lifetime | |
| 71 | --> $DIR/reject_lifetime_extension.rs:85:25 | |
| 70 | note: due to a current limitation of the type system, this implies a `'static` lifetime | |
| 71 | --> $DIR/reject_lifetime_extension.rs:85:9 | |
| 72 | 72 | | |
| 73 | 73 | LL | for<'b> &'b u8: TransmuteFrom<&'a u8>, |
| 74 | | ^^^^^^^^^^^^^^^^^^^^^ | |
| 74 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 75 | 75 | |
| 76 | 76 | error: aborting due to 8 previous errors |
| 77 | 77 |
triagebot.toml+2-2| ... | ... | @@ -579,7 +579,7 @@ trigger_files = [ |
| 579 | 579 | "src/doc/unstable-book/src/compiler-flags/sanitizer.md", |
| 580 | 580 | "src/doc/unstable-book/src/language-features/cfg-sanitize.md", |
| 581 | 581 | "src/doc/unstable-book/src/language-features/cfi-encoding.md", |
| 582 | "src/doc/unstable-book/src/language-features/no-sanitize.md", | |
| 582 | "src/doc/unstable-book/src/language-features/sanitize.md", | |
| 583 | 583 | "tests/codegen-llvm/sanitizer", |
| 584 | 584 | "tests/codegen-llvm/split-lto-unit.rs", |
| 585 | 585 | "tests/codegen-llvm/stack-probes-inline.rs", |
| ... | ... | @@ -1209,7 +1209,7 @@ cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"] |
| 1209 | 1209 | [mentions."src/doc/unstable-book/src/language-features/cfi-encoding.md"] |
| 1210 | 1210 | cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"] |
| 1211 | 1211 | |
| 1212 | [mentions."src/doc/unstable-book/src/language-features/no-sanitize.md"] | |
| 1212 | [mentions."src/doc/unstable-book/src/language-features/sanitize.md"] | |
| 1213 | 1213 | cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"] |
| 1214 | 1214 | |
| 1215 | 1215 | [mentions."src/doc/rustc/src/check-cfg.md"] |