diff options
| author | bors <bors@rust-lang.org> | 2024-01-21 13:13:03 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-01-21 13:13:03 +0000 |
| commit | 70e720bc685e21bbae276445fedf7d89613ecb0d (patch) | |
| tree | bea46b5cbfb4f8baa9e4b0e34be77eb9600be0da /src | |
| parent | 0c8e1e6f444d28742aff75f8789df69007ca589a (diff) | |
| parent | 7092c660077d185df375eaf07d649be2b49018d0 (diff) | |
| download | rust-70e720bc685e21bbae276445fedf7d89613ecb0d.tar.gz rust-70e720bc685e21bbae276445fedf7d89613ecb0d.zip | |
Auto merge of #3270 - rust-lang:rustup-2024-01-21, r=RalfJung
Automatic Rustup
Diffstat (limited to 'src')
174 files changed, 2145 insertions, 1289 deletions
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index fea194a80ef..28d96cb1cfe 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -917,8 +917,24 @@ class RustBuild(object): if toml_val is not None: env["{}_{}".format(var_name, host_triple_sanitized)] = toml_val - # preserve existing RUSTFLAGS - env.setdefault("RUSTFLAGS", "") + # In src/etc/rust_analyzer_settings.json, we configure rust-analyzer to + # pass RUSTC_BOOTSTRAP=1 to all cargo invocations because the standard + # library uses unstable Cargo features. Without RUSTC_BOOTSTRAP, + # rust-analyzer would fail to fetch workspace layout when the system's + # default toolchain is not nightly. + # + # But that setting has the collateral effect of rust-analyzer also + # passing RUSTC_BOOTSTRAP=1 to all x.py invocations too (the various overrideCommand). + # For compiling bootstrap that can cause spurious rebuilding of bootstrap when + # rust-analyzer x.py invocations are interleaved with handwritten ones on the + # command line. + # + # Set RUSTC_BOOTSTRAP=1 consistently. + env["RUSTC_BOOTSTRAP"] = "1" + + default_rustflags = "" if env.get("RUSTFLAGS_BOOTSTRAP", "") else "-Zallow-features=" + + env.setdefault("RUSTFLAGS", default_rustflags) target_features = [] if self.get_toml("crt-static", build_section) == "true": diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 9c897ae1bb7..dd9c68aba7f 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -37,6 +37,7 @@ static SETTINGS_HASHES: &[&str] = &[ "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541", "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923", "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a", + "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000", ]; static RUST_ANALYZER_SETTINGS: &str = include_str!("../../../../etc/rust_analyzer_settings.json"); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 4e20babc55a..3770d0687b2 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1799,15 +1799,20 @@ impl<'a> Builder<'a> { } if self.config.rust_remap_debuginfo { - // FIXME: handle vendored sources - let registry_src = t!(home::cargo_home()).join("registry").join("src"); let mut env_var = OsString::new(); - for entry in t!(std::fs::read_dir(registry_src)) { - if !env_var.is_empty() { - env_var.push("\t"); - } - env_var.push(t!(entry).path()); + if self.config.vendor { + let vendor = self.build.src.join("vendor"); + env_var.push(vendor); env_var.push("=/rust/deps"); + } else { + let registry_src = t!(home::cargo_home()).join("registry").join("src"); + for entry in t!(std::fs::read_dir(registry_src)) { + if !env_var.is_empty() { + env_var.push("\t"); + } + env_var.push(t!(entry).path()); + env_var.push("=/rust/deps"); + } } cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var); } diff --git a/src/doc/nomicon b/src/doc/nomicon -Subproject f6bd083c4ccfc4ce6699b8b4154e3c45c5a27a8 +Subproject 6bc2415218d4dd0cb01433d8320f5ccf79c343a diff --git a/src/doc/reference b/src/doc/reference -Subproject 3565c7978cfc9662f5963b135690ff9cbbfa031 +Subproject 8c77e8be9da1a9c70545556218d563c8d061f1f diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example -Subproject c0be6299e52e4164c30ba6f41bd0ad0aaee6497 +Subproject ddf5cb0e6ee54ba2dd84c8ca3e1314120014e20 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide -Subproject d13e85152a977cd0bcaf583cf5f49e86225697d +Subproject 4af29d1a7f64f88a36539662c6a84fe1fbe6cde diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md index 95932db14e1..48b58f6f06a 100644 --- a/src/doc/rustc/src/target-tier-policy.md +++ b/src/doc/rustc/src/target-tier-policy.md @@ -246,6 +246,8 @@ approved by the appropriate team for that shared code before acceptance. introducing unconditional uses of features that another variation of the target may not have; use conditional compilation or runtime detection, as appropriate, to let each target run code supported by that target. +- Tier 3 targets must be able to produce assembly using at least one of + rustc's supported backends from any host target. If a tier 3 target stops meeting these requirements, or the target maintainers no longer have interest or time, or the target shows no signs of activity and diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/unstable-book/src/compiler-flags/check-cfg.md index 78bc8dceb78..bf83f6ad7c5 100644 --- a/src/doc/unstable-book/src/compiler-flags/check-cfg.md +++ b/src/doc/unstable-book/src/compiler-flags/check-cfg.md @@ -44,15 +44,20 @@ To enable checking of values, but to provide an *none*/empty set of expected val ```bash rustc --check-cfg 'cfg(name)' -rustc --check-cfg 'cfg(name, values())' rustc --check-cfg 'cfg(name, values(none()))' ``` -To enable checking of name but not values (i.e. unknown expected values), use this form: +To enable checking of name but not values, use one of these forms: -```bash -rustc --check-cfg 'cfg(name, values(any()))' -``` + - No expected values (_will lint on every value_): + ```bash + rustc --check-cfg 'cfg(name, values())' + ``` + + - Unknown expected values (_will never lint_): + ```bash + rustc --check-cfg 'cfg(name, values(any()))' + ``` To avoid repeating the same set of values, use this form: @@ -114,18 +119,14 @@ as argument to `--check-cfg`. This table describe the equivalence of a `--cfg` argument to a `--check-cfg` argument. | `--cfg` | `--check-cfg` | -|-----------------------------|----------------------------------------------------------| +|-------------------------------|------------------------------------------------------------| | *nothing* | *nothing* or `--check-cfg=cfg()` (to enable the checking) | -| `--cfg foo` | `--check-cfg=cfg(foo), --check-cfg=cfg(foo, values())` or `--check-cfg=cfg(foo, values(none()))` | +| `--cfg foo` | `--check-cfg=cfg(foo)` or `--check-cfg=cfg(foo, values(none()))` | | `--cfg foo=""` | `--check-cfg=cfg(foo, values(""))` | | `--cfg foo="bar"` | `--check-cfg=cfg(foo, values("bar"))` | | `--cfg foo="1" --cfg foo="2"` | `--check-cfg=cfg(foo, values("1", "2"))` | | `--cfg foo="1" --cfg bar="2"` | `--check-cfg=cfg(foo, values("1")) --check-cfg=cfg(bar, values("2"))` | -| `--cfg foo --cfg foo="bar"` | `--check-cfg=cfg(foo) --check-cfg=cfg(foo, values("bar"))` | - -NOTE: There is (currently) no way to express that a condition name is expected but no (!= none) -values are expected. Passing an empty `values()` means *(none)* in the sense of `#[cfg(foo)]` -with no value. Users are expected to NOT pass a `--check-cfg` with that condition name. +| `--cfg foo --cfg foo="bar"` | `--check-cfg=cfg(foo, values(none(), "bar"))` | ### Example: Cargo-like `feature` example diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index 32a04cfd5d1..d329fe997cd 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -1,4 +1,5 @@ { + "git.detectSubmodulesLimit": 20, "rust-analyzer.check.invocationLocation": "root", "rust-analyzer.check.invocationStrategy": "once", "rust-analyzer.check.overrideCommand": [ diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 014bcb1a881..aab974ad79e 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -312,7 +312,7 @@ pub(crate) fn build_impls( let tcx = cx.tcx; // for each implementation of an item represented by `did`, build the clean::Item for that impl - for &did in tcx.inherent_impls(did).iter() { + for &did in tcx.inherent_impls(did).into_iter().flatten() { build_impl(cx, did, attrs, ret); } @@ -325,7 +325,7 @@ pub(crate) fn build_impls( if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { let type_ = if tcx.is_trait(did) { SimplifiedType::Trait(did) } else { SimplifiedType::Adt(did) }; - for &did in tcx.incoherent_impls(type_) { + for &did in tcx.incoherent_impls(type_).into_iter().flatten() { build_impl(cx, did, attrs, ret); } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 90eb783c7ca..6710193f961 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1862,7 +1862,7 @@ impl PrimitiveType { .get(self) .into_iter() .flatten() - .flat_map(move |&simp| tcx.incoherent_impls(simp)) + .flat_map(move |&simp| tcx.incoherent_impls(simp).into_iter().flatten()) .copied() } @@ -1870,7 +1870,7 @@ impl PrimitiveType { Self::simplified_types() .values() .flatten() - .flat_map(move |&simp| tcx.incoherent_impls(simp)) + .flat_map(move |&simp| tcx.incoherent_impls(simp).into_iter().flatten()) .copied() } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 437517598ac..b2b20c95a7e 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -312,7 +312,9 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { Symbol::intern(&match p.kind { // FIXME(never_patterns): does this make sense? - PatKind::Wild | PatKind::Never | PatKind::Struct(..) => return kw::Underscore, + PatKind::Wild | PatKind::Err(_) | PatKind::Never | PatKind::Struct(..) => { + return kw::Underscore; + } PatKind::Binding(_, _, ident, _) => return ident.name, PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), PatKind::Or(pats) => { diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 642265f5f6b..bad1511dfd2 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -530,7 +530,6 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator for event in &mut self.inner { match &event.0 { Event::End(Tag::Heading(..)) => break, - Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {} Event::Text(text) | Event::Code(text) => { id.extend(text.chars().filter_map(slugify)); self.buf.push_back(event); @@ -549,12 +548,10 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator let level = std::cmp::min(level as u32 + (self.heading_offset as u32), MAX_HEADER_LEVEL); - self.buf.push_back((Event::Html(format!("</a></h{level}>").into()), 0..0)); + self.buf.push_back((Event::Html(format!("</h{level}>").into()), 0..0)); - let start_tags = format!( - "<h{level} id=\"{id}\">\ - <a href=\"#{id}\">", - ); + let start_tags = + format!("<h{level} id=\"{id}\"><a class=\"doc-anchor\" href=\"#{id}\">§</a>"); return Some((Event::Html(start_tags.into()), 0..0)); } event diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 5eba1d0609f..4dd176b3a69 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -311,26 +311,38 @@ fn test_header() { assert_eq!(output, expect, "original: {}", input); } - t("# Foo bar", "<h2 id=\"foo-bar\"><a href=\"#foo-bar\">Foo bar</a></h2>"); + t( + "# Foo bar", + "<h2 id=\"foo-bar\"><a class=\"doc-anchor\" href=\"#foo-bar\">§</a>Foo bar</h2>", + ); t( "## Foo-bar_baz qux", "<h3 id=\"foo-bar_baz-qux\">\ - <a href=\"#foo-bar_baz-qux\">Foo-bar_baz qux</a></h3>", + <a class=\"doc-anchor\" href=\"#foo-bar_baz-qux\">§</a>\ + Foo-bar_baz qux\ + </h3>", ); t( "### **Foo** *bar* baz!?!& -_qux_-%", "<h4 id=\"foo-bar-baz--qux-\">\ - <a href=\"#foo-bar-baz--qux-\"><strong>Foo</strong> \ - <em>bar</em> baz!?!& -<em>qux</em>-%</a>\ + <a class=\"doc-anchor\" href=\"#foo-bar-baz--qux-\">§</a>\ + <strong>Foo</strong> <em>bar</em> baz!?!& -<em>qux</em>-%\ </h4>", ); t( "#### **Foo?** & \\*bar?!* _`baz`_ ❤ #qux", "<h5 id=\"foo--bar--baz--qux\">\ - <a href=\"#foo--bar--baz--qux\"><strong>Foo?</strong> & *bar?!* \ - <em><code>baz</code></em> ❤ #qux</a>\ + <a class=\"doc-anchor\" href=\"#foo--bar--baz--qux\">§</a>\ + <strong>Foo?</strong> & *bar?!* <em><code>baz</code></em> ❤ #qux\ </h5>", ); + t( + "# Foo [bar](https://hello.yo)", + "<h2 id=\"foo-bar\">\ + <a class=\"doc-anchor\" href=\"#foo-bar\">§</a>\ + Foo <a href=\"https://hello.yo\">bar</a>\ + </h2>", + ); } #[test] @@ -351,12 +363,36 @@ fn test_header_ids_multiple_blocks() { assert_eq!(output, expect, "original: {}", input); } - t(&mut map, "# Example", "<h2 id=\"example\"><a href=\"#example\">Example</a></h2>"); - t(&mut map, "# Panics", "<h2 id=\"panics\"><a href=\"#panics\">Panics</a></h2>"); - t(&mut map, "# Example", "<h2 id=\"example-1\"><a href=\"#example-1\">Example</a></h2>"); - t(&mut map, "# Search", "<h2 id=\"search-1\"><a href=\"#search-1\">Search</a></h2>"); - t(&mut map, "# Example", "<h2 id=\"example-2\"><a href=\"#example-2\">Example</a></h2>"); - t(&mut map, "# Panics", "<h2 id=\"panics-1\"><a href=\"#panics-1\">Panics</a></h2>"); + t( + &mut map, + "# Example", + "<h2 id=\"example\"><a class=\"doc-anchor\" href=\"#example\">§</a>Example</h2>", + ); + t( + &mut map, + "# Panics", + "<h2 id=\"panics\"><a class=\"doc-anchor\" href=\"#panics\">§</a>Panics</h2>", + ); + t( + &mut map, + "# Example", + "<h2 id=\"example-1\"><a class=\"doc-anchor\" href=\"#example-1\">§</a>Example</h2>", + ); + t( + &mut map, + "# Search", + "<h2 id=\"search-1\"><a class=\"doc-anchor\" href=\"#search-1\">§</a>Search</h2>", + ); + t( + &mut map, + "# Example", + "<h2 id=\"example-2\"><a class=\"doc-anchor\" href=\"#example-2\">§</a>Example</h2>", + ); + t( + &mut map, + "# Panics", + "<h2 id=\"panics-1\"><a class=\"doc-anchor\" href=\"#panics-1\">§</a>Panics</h2>", + ); } #[test] diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index bea5ccd7c86..ac7ae291d29 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1207,17 +1207,31 @@ impl<'a> AssocItemLink<'a> { } } -fn write_impl_section_heading(mut w: impl fmt::Write, title: &str, id: &str) { +pub fn write_section_heading( + w: &mut impl fmt::Write, + title: &str, + id: &str, + extra_class: Option<&str>, + extra: impl fmt::Display, +) { + let (extra_class, whitespace) = match extra_class { + Some(extra) => (extra, " "), + None => ("", ""), + }; write!( w, - "<h2 id=\"{id}\" class=\"section-header\">\ + "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\ {title}\ <a href=\"#{id}\" class=\"anchor\">§</a>\ - </h2>" + </h2>{extra}", ) .unwrap(); } +fn write_impl_section_heading(w: &mut impl fmt::Write, title: &str, id: &str) { + write_section_heading(w, title, id, None, "") +} + pub(crate) fn render_all_impls( mut w: impl Write, cx: &mut Context<'_>, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 3b91fbdcb29..71186319e07 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -19,8 +19,8 @@ use super::{ item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl, render_rightside, render_stability_since_raw, - render_stability_since_raw_with_extra, AssocItemLink, AssocItemRender, Context, - ImplRenderingParameters, RenderMode, + render_stability_since_raw_with_extra, write_section_heading, AssocItemLink, AssocItemRender, + Context, ImplRenderingParameters, RenderMode, }; use crate::clean; use crate::config::ModuleSorting; @@ -425,13 +425,12 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: w.write_str(ITEM_TABLE_CLOSE); } last_section = Some(my_section); - write!( + write_section_heading( w, - "<h2 id=\"{id}\" class=\"section-header\">\ - <a href=\"#{id}\">{name}</a>\ - </h2>{ITEM_TABLE_OPEN}", - id = cx.derive_id(my_section.id()), - name = my_section.name(), + my_section.name(), + &cx.derive_id(my_section.id()), + None, + ITEM_TABLE_OPEN, ); } @@ -814,16 +813,6 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // Trait documentation write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); - fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { - write!( - w, - "<h2 id=\"{0}\" class=\"section-header\">\ - {1}<a href=\"#{0}\" class=\"anchor\">§</a>\ - </h2>{2}", - id, title, extra_content - ) - } - fn trait_item(w: &mut Buffer, cx: &mut Context<'_>, m: &clean::Item, t: &clean::Item) { let name = m.name.unwrap(); info!("Documenting {name} on {ty_name:?}", ty_name = t.name); @@ -857,10 +846,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } if !required_types.is_empty() { - write_small_section_header( + write_section_heading( w, - "required-associated-types", "Required Associated Types", + "required-associated-types", + None, "<div class=\"methods\">", ); for t in required_types { @@ -869,10 +859,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: w.write_str("</div>"); } if !provided_types.is_empty() { - write_small_section_header( + write_section_heading( w, - "provided-associated-types", "Provided Associated Types", + "provided-associated-types", + None, "<div class=\"methods\">", ); for t in provided_types { @@ -882,10 +873,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } if !required_consts.is_empty() { - write_small_section_header( + write_section_heading( w, - "required-associated-consts", "Required Associated Constants", + "required-associated-consts", + None, "<div class=\"methods\">", ); for t in required_consts { @@ -894,10 +886,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: w.write_str("</div>"); } if !provided_consts.is_empty() { - write_small_section_header( + write_section_heading( w, - "provided-associated-consts", "Provided Associated Constants", + "provided-associated-consts", + None, "<div class=\"methods\">", ); for t in provided_consts { @@ -908,10 +901,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // Output the documentation for each function individually if !required_methods.is_empty() || must_implement_one_of_functions.is_some() { - write_small_section_header( + write_section_heading( w, - "required-methods", "Required Methods", + "required-methods", + None, "<div class=\"methods\">", ); @@ -929,10 +923,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: w.write_str("</div>"); } if !provided_methods.is_empty() { - write_small_section_header( + write_section_heading( w, - "provided-methods", "Provided Methods", + "provided-methods", + None, "<div class=\"methods\">", ); for m in provided_methods { @@ -949,10 +944,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: let mut extern_crates = FxHashSet::default(); if !t.is_object_safe(cx.tcx()) { - write_small_section_header( + write_section_heading( w, - "object-safety", "Object Safety", + "object-safety", + None, &format!( "<div class=\"object-safety-info\">This trait is <b>not</b> \ <a href=\"{base}/reference/items/traits.html#object-safety\">\ @@ -996,7 +992,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: foreign.sort_by_cached_key(|i| ImplString::new(i, cx)); if !foreign.is_empty() { - write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", ""); + write_section_heading(w, "Implementations on Foreign Types", "foreign-impls", None, ""); for implementor in foreign { let provided_methods = implementor.inner_impl().provided_trait_methods(tcx); @@ -1021,10 +1017,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } } - write_small_section_header( + write_section_heading( w, - "implementors", "Implementors", + "implementors", + None, "<div id=\"implementors-list\">", ); for implementor in concrete { @@ -1033,10 +1030,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: w.write_str("</div>"); if t.is_auto(tcx) { - write_small_section_header( + write_section_heading( w, - "synthetic-implementors", "Auto implementors", + "synthetic-implementors", + None, "<div id=\"synthetic-implementors-list\">", ); for implementor in synthetic { @@ -1054,18 +1052,20 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } else { // even without any implementations to write in, we still want the heading and list, so the // implementors javascript file pulled in below has somewhere to write the impls into - write_small_section_header( + write_section_heading( w, - "implementors", "Implementors", + "implementors", + None, "<div id=\"implementors-list\"></div>", ); if t.is_auto(tcx) { - write_small_section_header( + write_section_heading( w, - "synthetic-implementors", "Auto implementors", + "synthetic-implementors", + None, "<div id=\"synthetic-implementors-list\"></div>", ); } @@ -1248,11 +1248,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); if let Some(inner_type) = &t.inner_type { - write!( - w, - "<h2 id=\"aliased-type\" class=\"section-header\">\ - Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>" - ); + write_section_heading(w, "Aliased Type", "aliased-type", None, ""); match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { @@ -1673,16 +1669,14 @@ fn item_variants( enum_def_id: DefId, ) { let tcx = cx.tcx(); - write!( + write_section_heading( w, - "<h2 id=\"variants\" class=\"variants section-header\">\ - Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\ - </h2>\ - {}\ - <div class=\"variants\">", - document_non_exhaustive_header(it), - document_non_exhaustive(it) + &format!("Variants{}", document_non_exhaustive_header(it)), + "variants", + Some("variants"), + format!("{}<div class=\"variants\">", document_non_exhaustive(it)), ); + let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants); for (index, variant) in variants.iter_enumerated() { if variant.is_stripped() { @@ -1930,16 +1924,12 @@ fn item_fields( .peekable(); if let None | Some(CtorKind::Fn) = ctor_kind { if fields.peek().is_some() { - write!( - w, - "<h2 id=\"fields\" class=\"fields section-header\">\ - {}{}<a href=\"#fields\" class=\"anchor\">§</a>\ - </h2>\ - {}", + let title = format!( + "{}{}", if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" }, document_non_exhaustive_header(it), - document_non_exhaustive(it) ); + write_section_heading(w, &title, "fields", Some("fields"), document_non_exhaustive(it)); for (index, (field, ty)) in fields.enumerate() { let field_name = field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string()); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index cd53fcb8b7c..9c593aa85d9 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -849,11 +849,30 @@ nav.sub { h2.section-header > .anchor { padding-right: 6px; } +a.doc-anchor { + color: var(--main-color); + display: none; + position: absolute; + left: -17px; + /* We add this padding so that when the cursor moves from the heading's text to the anchor, + the anchor doesn't disappear. */ + padding-right: 5px; + /* And this padding is used to make the anchor larger and easier to click on. */ + padding-left: 3px; +} +*:hover > .doc-anchor { + display: block; +} +/* If the first element of the top doc block is a heading, we don't want to ever display its anchor +because of the `[-]` element which would overlap with it. */ +.top-doc > .docblock > *:first-child > .doc-anchor { + display: none !important; +} .main-heading a:hover, .example-wrap .rust a:hover, .all-items a:hover, -.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover, +.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover:not(.doc-anchor), .docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover, .item-info a { text-decoration: underline; diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index dbff33dc1ec..30b3e5c784d 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -624,7 +624,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // Checks if item_name belongs to `impl SomeItem` let mut assoc_items: Vec<_> = tcx .inherent_impls(did) - .iter() + .into_iter() + .flatten() .flat_map(|&imp| { filter_assoc_items_by_name_and_namespace( tcx, diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index df955421ba4..1bea93c7842 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -56,13 +56,10 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { | clean::TraitItem(..) | clean::FunctionItem(..) | clean::VariantItem(..) - | clean::MethodItem(..) | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) | clean::ConstantItem(..) | clean::UnionItem(..) - | clean::AssocConstItem(..) - | clean::AssocTypeItem(..) | clean::TraitAliasItem(..) | clean::MacroItem(..) | clean::ForeignTypeItem => { @@ -80,6 +77,16 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { } } + clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => { + let item_id = i.item_id; + if item_id.is_local() + && !self.effective_visibilities.is_reachable(self.tcx, item_id.expect_def_id()) + { + debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name); + return None; + } + } + clean::StructFieldItem(..) => { if i.visibility(self.tcx) != Some(Visibility::Public) { return Some(strip_item(i)); @@ -192,16 +199,16 @@ impl<'a> DocFolder for ImplStripper<'a, '_> { && imp.items.iter().all(|i| { let item_id = i.item_id; item_id.is_local() - && !is_item_reachable( - self.tcx, - self.is_json_output, - &self.cache.effective_visibilities, - item_id, - ) + && !self + .cache + .effective_visibilities + .is_reachable(self.tcx, item_id.expect_def_id()) }) { + debug!("ImplStripper: no public item; removing {imp:?}"); return None; } else if imp.items.is_empty() && i.doc_value().is_empty() { + debug!("ImplStripper: no item and no doc; removing {imp:?}"); return None; } } @@ -212,13 +219,13 @@ impl<'a> DocFolder for ImplStripper<'a, '_> { && !imp.for_.is_assoc_ty() && !self.should_keep_impl(&i, did) { - debug!("ImplStripper: impl item for stripped type; removing"); + debug!("ImplStripper: impl item for stripped type; removing {imp:?}"); return None; } if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) && !self.should_keep_impl(&i, did) { - debug!("ImplStripper: impl item for stripped trait; removing"); + debug!("ImplStripper: impl item for stripped trait; removing {imp:?}"); return None; } if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) { @@ -226,7 +233,7 @@ impl<'a> DocFolder for ImplStripper<'a, '_> { if let Some(did) = typaram.def_id(self.cache) && !self.should_keep_impl(&i, did) { - debug!("ImplStripper: stripped item in trait's generics; removing impl"); + debug!("ImplStripper: stripped item in trait's generics; removing {imp:?}"); return None; } } diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 84976cd699f4aea56cb3a90ce3eedeed9e20d5a +Subproject 1ae631085f01c1a72d05df1ec81f3759a836004 diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs index bb08ac7508b..eeaa3de3725 100644 --- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs +++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs @@ -45,50 +45,72 @@ declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]); impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - // For functions, with explicitly defined types, don't warn. - // XXXkhuey maybe we should? - if let ExprKind::Closure(Closure { - kind: - ClosureKind::Coroutine(CoroutineKind::Desugared( - CoroutineDesugaring::Async, - CoroutineSource::Block | CoroutineSource::Closure, - )), + let ExprKind::Closure(Closure { + kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, kind)), body: body_id, .. }) = expr.kind - { - if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() { - let typeck_results = cx.tcx.typeck_body(*body_id); - let body = cx.tcx.hir().body(*body_id); - let expr_ty = typeck_results.expr_ty(body.value); + else { + return; + }; - if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { - let return_expr_span = match &body.value.kind { - // XXXkhuey there has to be a better way. - ExprKind::Block(block, _) => block.expr.map(|e| e.span), - ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), - _ => None, - }; - if let Some(return_expr_span) = return_expr_span { - span_lint_hir_and_then( - cx, - ASYNC_YIELDS_ASYNC, - body.value.hir_id, + let body_expr = match kind { + CoroutineSource::Fn => { + // For functions, with explicitly defined types, don't warn. + // XXXkhuey maybe we should? + return; + }, + CoroutineSource::Block => cx.tcx.hir().body(*body_id).value, + CoroutineSource::Closure => { + // Like `async fn`, async closures are wrapped in an additional block + // to move all of the closure's arguments into the future. + + let async_closure_body = cx.tcx.hir().body(*body_id).value; + let ExprKind::Block(block, _) = async_closure_body.kind else { + return; + }; + let Some(block_expr) = block.expr else { + return; + }; + let ExprKind::DropTemps(body_expr) = block_expr.kind else { + return; + }; + body_expr + }, + }; + + let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() else { + return; + }; + + let typeck_results = cx.tcx.typeck_body(*body_id); + let expr_ty = typeck_results.expr_ty(body_expr); + + if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { + let return_expr_span = match &body_expr.kind { + // XXXkhuey there has to be a better way. + ExprKind::Block(block, _) => block.expr.map(|e| e.span), + ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), + _ => None, + }; + if let Some(return_expr_span) = return_expr_span { + span_lint_hir_and_then( + cx, + ASYNC_YIELDS_ASYNC, + body_expr.hir_id, + return_expr_span, + "an async construct yields a type which is itself awaitable", + |db| { + db.span_label(body_expr.span, "outer async construct"); + db.span_label(return_expr_span, "awaitable value not awaited"); + db.span_suggestion( return_expr_span, - "an async construct yields a type which is itself awaitable", - |db| { - db.span_label(body.value.span, "outer async construct"); - db.span_label(return_expr_span, "awaitable value not awaited"); - db.span_suggestion( - return_expr_span, - "consider awaiting this value", - format!("{}.await", snippet(cx, return_expr_span, "..")), - Applicability::MaybeIncorrect, - ); - }, + "consider awaiting this value", + format!("{}.await", snippet(cx, return_expr_span, "..")), + Applicability::MaybeIncorrect, ); - } - } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index db01ff2cd22..bfd89bfd2c7 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { /// (ZST fields having an arbitrary offset is completely inconsequential, and /// if there is only one field left after ignoring ZST fields then the offset /// of that field does not matter either.) -fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool { +fn is_union_with_two_non_zst_fields<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool { if let ItemKind::Union(..) = &item.kind && let ty::Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() { diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index d8abe411030..df596338b95 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -386,7 +386,8 @@ fn check_unsafe_derive_deserialize<'tcx>( && cx .tcx .inherent_impls(def.did()) - .iter() + .into_iter() + .flatten() .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local())) .any(|imp| has_unsafe(cx, imp)) { @@ -450,12 +451,12 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r && let Some(def_id) = trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, adt.did(),&[]) + && !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[]) // If all of our fields implement `Eq`, we can implement `Eq` too && adt .all_fields() .map(|f| f.ty(cx.tcx, args)) - .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, adt.did(), &[])) + .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[])) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 3c435294252..4e728d61b85 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -51,7 +51,8 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { | PatKind::Binding(..) | PatKind::Wild | PatKind::Never - | PatKind::Or(_) => false, + | PatKind::Or(_) + | PatKind::Err(_) => false, PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x), diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 788fe828727..87f6f5e7959 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -210,7 +210,7 @@ enum ImplicitHasherType<'tcx> { impl<'tcx> ImplicitHasherType<'tcx> { /// Checks that `ty` is a target type without a `BuildHasher`. - fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option<Self> { + fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Option<Self> { if let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind { let params: Vec<_> = path .segments diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index e4781752e75..fb7b82ec304 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -53,9 +53,10 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // List of spans to lint. (lint_span, first_span) let mut lint_spans = Vec::new(); + let Ok(impls) = cx.tcx.crate_inherent_impls(()) else { return }; let inherent_impls = cx .tcx - .with_stable_hashing_context(|hcx| cx.tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx, true)); + .with_stable_hashing_context(|hcx| impls.inherent_impls.to_sorted(&hcx, true)); for (_, impl_ids) in inherent_impls.into_iter().filter(|(&id, impls)| { impls.len() > 1 diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index 903d3a2ab89..82a37bb4f27 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -139,7 +139,7 @@ fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iter fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) { - cx.tcx.inherent_impls(ty_did).iter().any(|&did| { + cx.tcx.inherent_impls(ty_did).into_iter().flatten().any(|&did| { cx.tcx .associated_items(did) .filter_by_name_unhygienic(method_name) diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index c09d3d1862b..c1ab020117c 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -441,7 +441,8 @@ fn check_for_is_empty( let is_empty = cx .tcx .inherent_impls(impl_ty) - .iter() + .into_iter() + .flatten() .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty)) .find(|item| item.kind == AssocKind::Fn); @@ -605,7 +606,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks the inherent impl's items for an `is_empty(self)` method. fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { let is_empty = sym!(is_empty); - cx.tcx.inherent_impls(id).iter().any(|imp| { + cx.tcx.inherent_impls(id).into_iter().flatten().any(|imp| { cx.tcx .associated_items(*imp) .filter_by_name_unhygienic(is_empty) diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index c7980060807..814ccaa36f5 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -118,7 +118,7 @@ fn is_ref_iterable<'tcx>( .liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder()) && let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output && let param_env = cx.tcx.param_env(fn_id) - && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, fn_id, &[]) + && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, Some(fn_id), &[]) && let Some(into_iter_ty) = make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty]) && let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty) diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index c823d07e2bd..5ca161e9309 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -11,7 +11,7 @@ use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, P use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Symbol; +use rustc_span::{ErrorGuaranteed, Symbol}; use super::MATCH_SAME_ARMS; @@ -167,6 +167,8 @@ enum NormalizedPat<'a> { /// contains everything afterwards. Note that either side, or both sides, may contain zero /// patterns. Slice(&'a [Self], Option<&'a [Self]>), + /// A placeholder for a pattern that wasn't well formed in some way. + Err(ErrorGuaranteed), } #[derive(Clone, Copy)] @@ -329,6 +331,7 @@ impl<'a> NormalizedPat<'a> { arena.alloc_from_iter(front.iter().map(|pat| Self::from_pat(cx, arena, pat))), wild_pat.map(|_| &*arena.alloc_from_iter(back.iter().map(|pat| Self::from_pat(cx, arena, pat)))), ), + PatKind::Err(guar) => Self::Err(guar), } } diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index e38c66f6741..0602eeaa704 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -73,7 +73,8 @@ pub(super) fn check<'tcx>( let has_suggested_method = receiver_ty.ty_adt_def().is_some_and(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) - .iter() + .into_iter() + .flatten() .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) .any(|assoc| { assoc.fn_has_self_parameter diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index c62c351e716..3416a93e3c4 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -257,9 +257,9 @@ fn is_call_with_ref_arg<'tcx>( .. } = kind && args.len() == 1 - && let mir::Operand::Move(mir::Place { local, .. }) = &args[0] + && let mir::Operand::Move(mir::Place { local, .. }) = &args[0].node && let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind() - && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx)) + && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].node.ty(mir, cx.tcx)) && !is_copy(cx, inner_ty) { Some((def_id, *local, inner_ty, destination.as_local()?)) diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index cde08dfcc74..334e6770ae4 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -5,7 +5,9 @@ use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; -use rustc_hir::{intravisit as hir_visit, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Node}; +use rustc_hir::{ + intravisit as hir_visit, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; @@ -166,10 +168,22 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if coroutine_kind.is_async() && let hir::ExprKind::Closure(closure) = body.kind { - let async_closure_body = cx.tcx.hir().body(closure.body); + // Like `async fn`, async closures are wrapped in an additional block + // to move all of the closure's arguments into the future. + + let async_closure_body = cx.tcx.hir().body(closure.body).value; + let ExprKind::Block(block, _) = async_closure_body.kind else { + return; + }; + let Some(block_expr) = block.expr else { + return; + }; + let ExprKind::DropTemps(body_expr) = block_expr.kind else { + return; + }; // `async x` is a syntax error, so it becomes `async { x }` - if !matches!(async_closure_body.value.kind, hir::ExprKind::Block(_, _)) { + if !matches!(body_expr.kind, hir::ExprKind::Block(_, _)) { hint = hint.blockify(); } diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 81efec65343..7882bfdd09f 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -314,9 +314,9 @@ impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BO impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn( &mut self, - cx: &LateContext<'_>, + cx: &LateContext<'tcx>, fn_kind: FnKind<'_>, - decl: &FnDecl<'_>, + decl: &FnDecl<'tcx>, _: &Body<'_>, _: Span, def_id: LocalDefId, @@ -346,7 +346,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { ); } - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); match item.kind { @@ -363,7 +363,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } } - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { match item.kind { ImplItemKind::Const(ty, _) => { let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx @@ -391,7 +391,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } } - fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { + fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &hir::FieldDef<'tcx>) { let is_exported = cx.effective_visibilities.is_exported(field.def_id); self.check_ty( @@ -404,7 +404,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { ); } - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'tcx>) { let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); let context = CheckTyContext { @@ -421,7 +421,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } } - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &Local<'tcx>) { if let Some(ty) = local.ty { self.check_ty( cx, @@ -444,7 +444,7 @@ impl Types { } } - fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) { + fn check_fn_decl<'tcx>(&mut self, cx: &LateContext<'tcx>, decl: &FnDecl<'tcx>, context: CheckTyContext) { // Ignore functions in trait implementations as they are usually forced by the trait definition. // // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard @@ -466,7 +466,7 @@ impl Types { /// lint found. /// /// The parameter `is_local` distinguishes the context of the type. - fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, mut context: CheckTyContext) { + fn check_ty<'tcx>(&mut self, cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, mut context: CheckTyContext) { if hir_ty.span.from_expansion() { return; } diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs index 5a986254fad..a0d609501a0 100644 --- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs +++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs @@ -11,7 +11,7 @@ use rustc_span::symbol::sym; use super::{utils, REDUNDANT_ALLOCATION}; -pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: &QPath<'tcx>, def_id: DefId) -> bool { let mut applicability = Applicability::MaybeIncorrect; let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { "Box" diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs index 9d5066199bd..a285f771f1b 100644 --- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs @@ -13,10 +13,10 @@ use rustc_span::symbol::sym; use super::VEC_BOX; -pub(super) fn check( - cx: &LateContext<'_>, +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>, - qpath: &QPath<'_>, + qpath: &QPath<'tcx>, def_id: DefId, box_size_threshold: u64, ) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index e90306ded61..b418db53ea4 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -77,7 +77,7 @@ fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> { } } -fn get_hir_ty_def_id(tcx: TyCtxt<'_>, hir_ty: rustc_hir::Ty<'_>) -> Option<DefId> { +fn get_hir_ty_def_id<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: rustc_hir::Ty<'tcx>) -> Option<DefId> { let TyKind::Path(qpath) = hir_ty.kind else { return None }; match qpath { QPath::Resolved(_, path) => path.res.opt_def_id(), @@ -229,7 +229,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local } } -fn is_default_method_on_current_ty(tcx: TyCtxt<'_>, qpath: QPath<'_>, implemented_ty_id: DefId) -> bool { +fn is_default_method_on_current_ty<'tcx>(tcx: TyCtxt<'tcx>, qpath: QPath<'tcx>, implemented_ty_id: DefId) -> bool { match qpath { QPath::Resolved(_, path) => match path.segments { [first, .., last] => last.ident.name == kw::Default && first.res.opt_def_id() == Some(implemented_ty_id), diff --git a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs index 903593ecfd7..6732a43a19e 100644 --- a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs +++ b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs @@ -57,11 +57,11 @@ impl LateLintPass<'_> for UninhabitedReferences { } } - fn check_fn( + fn check_fn<'tcx>( &mut self, - cx: &LateContext<'_>, + cx: &LateContext<'tcx>, kind: FnKind<'_>, - fndecl: &'_ FnDecl<'_>, + fndecl: &'_ FnDecl<'tcx>, _: &'_ Body<'_>, span: Span, _: LocalDefId, diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 77adcdd0e6b..7246214f9bf 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -226,7 +226,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us // Therefore they are not some form of constructor `C`, // with which a pattern `C(p_0)` may be formed, // which we would want to join with other `C(p_j)`s. - Ident(.., None) | Lit(_) | Wild | Never | Path(..) | Range(..) | Rest | MacCall(_) + Ident(.., None) | Lit(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) // Skip immutable refs, as grouping them saves few characters, // and almost always requires adding parens (increasing noisiness). // In the case of only two patterns, replacement adds net characters. diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index fa033838ef3..a1b08d105b9 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } - fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) { if !hir_ty.span.from_expansion() && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) && let Some(&StackItem::Check { diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 8d38b87e1d7..b26ebe5cee3 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -710,6 +710,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.slice(start, |pat| self.pat(pat)); self.slice(end, |pat| self.pat(pat)); }, + PatKind::Err(_) => kind!("Err"), } } diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs index b36c4ef91dc..81d4a26e9da 100644 --- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs +++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs @@ -44,7 +44,7 @@ declare_clippy_lint! { declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]); impl LateLintPass<'_> for ZeroSizedMapValues { - fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { + fn check_ty<'tcx>(&mut self, cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) { if !hir_ty.span.from_expansion() && !in_trait_impl(cx, hir_ty.hir_id) && let ty = ty_from_hir_ty(cx, hir_ty) @@ -82,7 +82,7 @@ fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { false } -fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { +fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { cx.maybe_typeck_results() .and_then(|results| { if results.hir_owner == hir_ty.hir_id.owner { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 482eaed77d1..979b117db25 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1007,7 +1007,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } e.hash(&mut self.s); }, - PatKind::Never | PatKind::Wild => {}, + PatKind::Never | PatKind::Wild | PatKind::Err(_) => {}, } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index cdf8528f48a..d264e46f133 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -534,10 +534,11 @@ fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<It "u128" => SimplifiedType::Uint(UintTy::U128), "f32" => SimplifiedType::Float(FloatTy::F32), "f64" => SimplifiedType::Float(FloatTy::F64), - _ => return [].iter().copied(), + #[allow(trivial_casts)] + _ => return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_]).into_iter().flatten().copied(), }; - tcx.incoherent_impls(ty).iter().copied() + tcx.incoherent_impls(ty).into_iter().flatten().copied() } fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> { @@ -663,7 +664,8 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> { // `impl S { ... }` let inherent_impl_children = tcx .inherent_impls(def_id) - .iter() + .into_iter() + .flatten() .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); let direct_children = item_children_by_name(tcx, def_id, segment); @@ -1733,6 +1735,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, + PatKind::Err(_) => true, } } diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 703985b9d4b..f9cc5f19125 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -104,7 +104,7 @@ impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, let mut mutable_borrowers = vec![]; for op in args { - match op { + match &op.node { mir::Operand::Copy(p) | mir::Operand::Move(p) => { if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { mutable_borrowers.push(p.local); diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 668ea9fcf3b..81f4fcc2133 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -345,7 +345,7 @@ fn check_terminator<'tcx>( check_operand(tcx, func, span, body)?; for arg in args { - check_operand(tcx, arg, span, body)?; + check_operand(tcx, &arg.node, span, body)?; } Ok(()) } else { diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 61d0663aa83..59ebe685c11 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -214,36 +214,21 @@ pub fn implements_trait<'tcx>( trait_id: DefId, args: &[GenericArg<'tcx>], ) -> bool { - let callee_id = cx - .enclosing_body - .map(|body| cx.tcx.hir().body_owner(body).owner.to_def_id()); - implements_trait_with_env_from_iter( - cx.tcx, - cx.param_env, - ty, - trait_id, - callee_id, - args.iter().map(|&x| Some(x)), - ) + implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, None, args.iter().map(|&x| Some(x))) } /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. +/// +/// The `callee_id` argument is used to determine whether this is a function call in a `const fn` environment, used for checking const traits. pub fn implements_trait_with_env<'tcx>( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, trait_id: DefId, - callee_id: DefId, + callee_id: Option<DefId>, args: &[GenericArg<'tcx>], ) -> bool { - implements_trait_with_env_from_iter( - tcx, - param_env, - ty, - trait_id, - Some(callee_id), - args.iter().map(|&x| Some(x)), - ) + implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, callee_id, args.iter().map(|&x| Some(x))) } /// Same as `implements_trait_from_env` but takes the arguments as an iterator. @@ -258,6 +243,13 @@ pub fn implements_trait_with_env_from_iter<'tcx>( // Clippy shouldn't have infer types assert!(!ty.has_infer()); + // If a `callee_id` is passed, then we assert that it is a body owner + // through calling `body_owner_kind`, which would panic if the callee + // does not have a body. + if let Some(callee_id) = callee_id { + let _ = tcx.hir().body_owner_kind(callee_id); + } + let ty = tcx.erase_regions(ty); if ty.has_escaping_bound_vars() { return false; diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout index 62de661f8ff..8c4d71e68f8 100644 --- a/src/tools/clippy/tests/ui/author/blocks.stdout +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -48,7 +48,11 @@ if let ExprKind::Closure { capture_clause: CaptureBy::Value { .. }, fn_decl: fn_ && expr2 = &cx.tcx.hir().body(body_id1).value && let ExprKind::Block(block, None) = expr2.kind && block.stmts.is_empty() - && block.expr.is_none() + && let Some(trailing_expr) = block.expr + && let ExprKind::DropTemps(expr3) = trailing_expr.kind + && let ExprKind::Block(block1, None) = expr3.kind + && block1.stmts.is_empty() + && block1.expr.is_none() { // report your lint here } diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 34d48559c37..42c751bb6be 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -15,6 +15,15 @@ fn main() { eprintln!("warning: `tidy` is not installed; diffs will not be generated"); } + if !config.profiler_support && config.mode == Mode::CoverageRun { + let actioned = if config.bless { "blessed" } else { "checked" }; + eprintln!( + r#" +WARNING: profiler runtime is not available, so `.coverage` files won't be {actioned} +help: try setting `profiler = true` in the `[build]` section of `config.toml`"# + ); + } + log_config(&config); run_tests(config); } diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 6cd22a91518..8cd996d8564 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -486,9 +486,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "measureme" -version = "10.1.2" +version = "11.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e381dcdad44c3c435f8052b08c5c4a1449c48ab56f312345eae12d7a693dbe" +checksum = "dfa4a40f09af7aa6faef38285402a78847d0d72bf8827006cd2a332e1e6e4a8d" dependencies = [ "log", "memmap2", diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 3da751c69a9..a65010b055b 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -24,7 +24,7 @@ log = "0.4" rand = "0.8" smallvec = "1.7" aes = { version = "0.8.3", features = ["hazmat"] } -measureme = "10.0.0" +measureme = "11" ctrlc = "3.2.5" # Copied from `compiler/rustc/Cargo.toml`. diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ad3a534b7ed..aa05d224c21 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3deb9bbf84f6431ebcbb7cbdbe3d89bc2636bc1b +867d39cdf625e4db4b381faff993346582e598b4 diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index cacd02bbfae..80a47c85269 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -8,7 +8,6 @@ #![feature(variant_count)] #![feature(yeet_expr)] #![feature(nonzero_ops)] -#![feature(round_ties_even)] #![feature(let_chains)] #![feature(lint_reasons)] #![feature(int_roundings)] diff --git a/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs b/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs index 1d6e4796d0a..d7aef47235f 100644 --- a/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs +++ b/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs @@ -1,5 +1,5 @@ //! The man pages for mmap/munmap suggest that it is possible to partly unmap a previously-mapped -//! region of addres space, but to LLVM that would be partial deallocation, which LLVM does not +//! region of address space, but to LLVM that would be partial deallocation, which LLVM does not //! support. So even though the man pages say this sort of use is possible, we must report UB. //@ignore-target-windows: No libc on Windows //@normalize-stderr-test: "size [0-9]+ and alignment" -> "size SIZE and alignment" diff --git a/src/tools/miri/tests/fail/issue-miri-1112.rs b/src/tools/miri/tests/fail/issue-miri-1112.rs index 9542673b0d9..387253a3f98 100644 --- a/src/tools/miri/tests/fail/issue-miri-1112.rs +++ b/src/tools/miri/tests/fail/issue-miri-1112.rs @@ -1,7 +1,7 @@ trait Empty {} #[repr(transparent)] -pub struct FunnyPointer(#[allow(dead_code)] dyn Empty); +pub struct FunnyPointer(dyn Empty); #[repr(C)] pub struct Meta { diff --git a/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.rs b/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.rs index 2283231eb01..d71d5954a40 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/drop_in_place.rs @@ -1,7 +1,7 @@ //@compile-flags: -Cdebug-assertions=no #[repr(transparent)] -struct HasDrop(#[allow(dead_code)] u8); +struct HasDrop(u8); impl Drop for HasDrop { fn drop(&mut self) {} diff --git a/src/tools/miri/tests/pass/async-fn.rs b/src/tools/miri/tests/pass/async-fn.rs index 6c92735df0a..13400c88c71 100644 --- a/src/tools/miri/tests/pass/async-fn.rs +++ b/src/tools/miri/tests/pass/async-fn.rs @@ -76,8 +76,7 @@ async fn uninhabited_variant() { fn run_fut<T>(fut: impl Future<Output = T>) -> T { use std::task::{Context, Poll, Waker}; - let waker = Waker::noop(); - let mut context = Context::from_waker(&waker); + let mut context = Context::from_waker(Waker::noop()); let mut pinned = Box::pin(fut); loop { diff --git a/src/tools/miri/tests/pass/dyn-star.rs b/src/tools/miri/tests/pass/dyn-star.rs index 8e26c4850fa..dab589b4651 100644 --- a/src/tools/miri/tests/pass/dyn-star.rs +++ b/src/tools/miri/tests/pass/dyn-star.rs @@ -93,8 +93,7 @@ fn dispatch_on_pin_mut() { let mut fut = async_main(); // Poll loop, just to test the future... - let waker = Waker::noop(); - let ctx = &mut Context::from_waker(&waker); + let ctx = &mut Context::from_waker(Waker::noop()); loop { match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } { diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 5f2d4489f4a..1bb44d56bf6 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1,5 +1,4 @@ #![feature(stmt_expr_attributes)] -#![feature(round_ties_even)] #![feature(float_gamma)] #![allow(arithmetic_overflow)] diff --git a/src/tools/miri/tests/pass/future-self-referential.rs b/src/tools/miri/tests/pass/future-self-referential.rs index 38cb700fd58..8aeb26a7a95 100644 --- a/src/tools/miri/tests/pass/future-self-referential.rs +++ b/src/tools/miri/tests/pass/future-self-referential.rs @@ -77,8 +77,7 @@ impl Future for DoStuff { } fn run_fut<T>(fut: impl Future<Output = T>) -> T { - let waker = Waker::noop(); - let mut context = Context::from_waker(&waker); + let mut context = Context::from_waker(Waker::noop()); let mut pinned = pin!(fut); loop { @@ -90,8 +89,7 @@ fn run_fut<T>(fut: impl Future<Output = T>) -> T { } fn self_referential_box() { - let waker = Waker::noop(); - let cx = &mut Context::from_waker(&waker); + let cx = &mut Context::from_waker(Waker::noop()); async fn my_fut() -> i32 { let val = 10; diff --git a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs index f18c4a3a065..ccee2221e29 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs @@ -6,8 +6,7 @@ use std::task::{Context, Poll, Waker}; pub fn fuzzing_block_on<O, F: Future<Output = O>>(fut: F) -> O { let mut fut = std::pin::pin!(fut); - let waker = Waker::noop(); - let mut context = Context::from_waker(&waker); + let mut context = Context::from_waker(Waker::noop()); loop { match fut.as_mut().poll(&mut context) { Poll::Ready(v) => return v, diff --git a/src/tools/miri/tests/pass/move-data-across-await-point.rs b/src/tools/miri/tests/pass/move-data-across-await-point.rs index 9bea6ea5742..1a93a6bf664 100644 --- a/src/tools/miri/tests/pass/move-data-across-await-point.rs +++ b/src/tools/miri/tests/pass/move-data-across-await-point.rs @@ -56,8 +56,7 @@ fn data_moved() { fn run_fut<T>(fut: impl Future<Output = T>) -> T { use std::task::{Context, Poll, Waker}; - let waker = Waker::noop(); - let mut context = Context::from_waker(&waker); + let mut context = Context::from_waker(Waker::noop()); let mut pinned = Box::pin(fut); loop { diff --git a/src/tools/miropt-test-tools/src/lib.rs b/src/tools/miropt-test-tools/src/lib.rs index cae96f59319..7d60033c3e8 100644 --- a/src/tools/miropt-test-tools/src/lib.rs +++ b/src/tools/miropt-test-tools/src/lib.rs @@ -98,11 +98,12 @@ pub fn files_for_miropt_test( from_file = format!("{}.{}.mir", test_name, first_pass); to_file = Some(second_file); } else { - let ext_re = regex::Regex::new(r#"(\.(mir|dot|html))$"#).unwrap(); - let cap = ext_re - .captures_iter(test_name) - .next() - .expect("test_name has an invalid extension"); + // Allow-list for file extensions that can be produced by MIR dumps. + // Other extensions can be added here, as needed by new dump flags. + let ext_re = regex::Regex::new(r#"(\.(mir|dot))$"#).unwrap(); + let cap = ext_re.captures_iter(test_name).next().unwrap_or_else(|| { + panic!("in {testfile:?}:\nEMIT_MIR has an unrecognized extension: {test_name}") + }); let extension = cap.get(1).unwrap().as_str(); expected_file = diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 6670e92f51b..15d06222eb4 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -513,7 +513,8 @@ dependencies = [ "mbe", "once_cell", "profile", - "rustc-dependencies", + "ra-ap-rustc_abi", + "ra-ap-rustc_parse_format", "rustc-hash", "smallvec", "span", @@ -579,7 +580,8 @@ dependencies = [ "oorandom", "profile", "project-model", - "rustc-dependencies", + "ra-ap-rustc_abi", + "ra-ap-rustc_index", "rustc-hash", "scoped-tls", "smallvec", @@ -1196,7 +1198,7 @@ dependencies = [ "drop_bomb", "expect-test", "limit", - "rustc-dependencies", + "ra-ap-rustc_lexer", "sourcegen", "stdx", ] @@ -1540,7 +1542,6 @@ dependencies = [ "profile", "project-model", "rayon", - "rustc-dependencies", "rustc-hash", "scip", "serde", @@ -1567,9 +1568,9 @@ dependencies = [ [[package]] name = "rust-analyzer-salsa" -version = "0.17.0-pre.4" +version = "0.17.0-pre.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c42b8737c320578b441a82daf7cdf8d897468de64e8a774fa54b53a50b6cc0" +checksum = "ca9d387a9801f4fb9b366789ad1bfc08448cafc49cf148d907cfcd88ab665d7f" dependencies = [ "indexmap", "lock_api", @@ -1579,13 +1580,14 @@ dependencies = [ "rust-analyzer-salsa-macros", "rustc-hash", "smallvec", + "triomphe", ] [[package]] name = "rust-analyzer-salsa-macros" -version = "0.17.0-pre.4" +version = "0.17.0-pre.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db72b0883f3592ade2be15a10583c75e0b269ec26e1190800fda2e2ce5ae6634" +checksum = "a2035f385d7fae31e9b086f40b272ee1d79c484472f31c9a10348a406e841eaf" dependencies = [ "heck", "proc-macro2", @@ -1600,16 +1602,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "rustc-dependencies" -version = "0.0.0" -dependencies = [ - "ra-ap-rustc_abi", - "ra-ap-rustc_index", - "ra-ap-rustc_lexer", - "ra-ap-rustc_parse_format", -] - -[[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1752,6 +1744,12 @@ dependencies = [ ] [[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1808,9 +1806,9 @@ dependencies = [ "proc-macro2", "profile", "quote", + "ra-ap-rustc_lexer", "rayon", "rowan", - "rustc-dependencies", "rustc-hash", "smol_str", "sourcegen", @@ -2028,9 +2026,13 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c5a71827ac326072b6405552093e2ad2accd25a32fd78d4edc82d98c7f2409" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" +dependencies = [ + "serde", + "stable_deref_trait", +] [[package]] name = "tt" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 4ee8064b5e3..35bef151196 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -78,7 +78,11 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -rustc-dependencies = { path = "./crates/rustc-dependencies", version = "0.0.0" } + +ra-ap-rustc_lexer = { version = "0.21.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.21.0", default-features = false } +ra-ap-rustc_index = { version = "0.21.0", default-features = false } +ra-ap-rustc_abi = { version = "0.21.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. sourcegen = { path = "./crates/sourcegen" } @@ -108,7 +112,7 @@ itertools = "0.12.0" libc = "0.2.150" nohash-hasher = "0.2.0" rayon = "1.8.0" -rust-analyzer-salsa = "0.17.0-pre.4" +rust-analyzer-salsa = "0.17.0-pre.5" rustc-hash = "1.1.0" semver = "1.0.14" serde = { version = "1.0.192", features = ["derive"] } diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index a0a55df5f99..92d2b9c3f57 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -7,7 +7,6 @@ mod change; use std::panic; -use rustc_hash::FxHashSet; use syntax::{ast, Parse, SourceFile}; use triomphe::Arc; @@ -44,12 +43,13 @@ pub trait Upcast<T: ?Sized> { } pub const DEFAULT_PARSE_LRU_CAP: usize = 128; +pub const DEFAULT_BORROWCK_LRU_CAP: usize = 256; pub trait FileLoader { /// Text of the file. fn file_text(&self, file_id: FileId) -> Arc<str>; fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>; - fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>; + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>; } /// Database which stores all significant input facts: source code and project @@ -84,19 +84,21 @@ pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; - fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>; + fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>; } -fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<FxHashSet<CrateId>> { +fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[CrateId]> { let graph = db.crate_graph(); - let res = graph + let mut crates = graph .iter() .filter(|&krate| { let root_file = graph[krate].root_file_id; db.file_source_root(root_file) == id }) - .collect(); - Arc::new(res) + .collect::<Vec<_>>(); + crates.sort(); + crates.dedup(); + crates.into_iter().collect() } /// Silly workaround for cyclic deps between the traits @@ -113,7 +115,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> { source_root.resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> { let _p = profile::span("relevant_crates"); let source_root = self.0.file_source_root(file_id); self.0.source_root_crates(source_root) diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 5933d30040f..523ff6fc404 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -29,7 +29,8 @@ smallvec.workspace = true hashbrown.workspace = true triomphe.workspace = true -rustc-dependencies.workspace = true +ra-ap-rustc_parse_format.workspace = true +ra-ap-rustc_abi.workspace = true # local deps stdx.workspace = true @@ -53,7 +54,7 @@ test-utils.workspace = true test-fixture.workspace = true [features] -in-rust-tree = ["rustc-dependencies/in-rust-tree"] +in-rust-tree = [] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 26f76afb1f0..30452e34aac 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -207,6 +207,13 @@ impl Attrs { }) } + pub fn has_doc_notable_trait(&self) -> bool { + self.by_key("doc").tt_values().any(|tt| { + tt.delimiter.kind == DelimiterKind::Parenthesis && + matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "notable_trait") + }) + } + pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ { self.by_key("doc").tt_values().map(DocExpr::parse) } @@ -355,7 +362,7 @@ fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> { } impl AttrsWithOwner { - pub(crate) fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self { + pub fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self { Self { attrs: db.attrs(owner), owner } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index c728570d986..fc0a4eb43dc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -965,11 +965,10 @@ impl ExprCollector<'_> { let res = match self.def_map.modules[module] .scope - .macro_invocations - .get(&InFile::new(outer_file, self.ast_id_map.ast_id_for_ptr(syntax_ptr))) + .macro_invoc(InFile::new(outer_file, self.ast_id_map.ast_id_for_ptr(syntax_ptr))) { // fast path, macro call is in a block module - Some(&call) => Ok(self.expander.enter_expand_id(self.db, call)), + Some(call) => Ok(self.expander.enter_expand_id(self.db, call)), None => self.expander.enter_expand(self.db, mcall, |path| { self.def_map .resolve_path( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs index c82d2347de5..32c53cb9503 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs @@ -92,7 +92,7 @@ impl ChildBySource for ItemScope { self.impls().for_each(|imp| add_impl(db, res, file_id, imp)); self.extern_crate_decls().for_each(|ext| add_extern_crate(db, res, file_id, ext)); self.use_decls().for_each(|ext| add_use(db, res, file_id, ext)); - self.unnamed_consts().for_each(|konst| { + self.unnamed_consts(db).for_each(|konst| { let loc = konst.lookup(db); if loc.id.file_id() == file_id { res[keys::CONST].insert(loc.source(db).value, konst); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index a95b78614e8..8772c34f02f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -11,7 +11,7 @@ use hir_expand::{ }; use intern::Interned; use la_arena::{Arena, ArenaMap}; -use rustc_dependencies::abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; +use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use syntax::ast::{self, HasName, HasVisibility}; use triomphe::Arc; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index d5831022f28..70c0d5193d4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -210,13 +210,10 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba #[salsa::invoke(AttrsWithOwner::attrs_query)] fn attrs(&self, def: AttrDefId) -> Attrs; + #[salsa::transparent] #[salsa::invoke(lang_item::lang_attr_query)] fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>; - #[salsa::transparent] - #[salsa::invoke(AttrsWithOwner::attrs_with_owner)] - fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner; - // endregion:attrs #[salsa::invoke(LangItems::lang_item_query)] @@ -240,7 +237,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba // endregion:visibilities #[salsa::invoke(LangItems::crate_lang_items_query)] - fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>; + fn crate_lang_items(&self, krate: CrateId) -> Option<Arc<LangItems>>; fn crate_supports_no_std(&self, crate_id: CrateId) -> bool; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 4737b48703d..67e43f15cd3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -10,7 +10,7 @@ use crate::{ item_scope::ItemInNs, nameres::DefMap, path::{ModPath, PathKind}, - visibility::Visibility, + visibility::{Visibility, VisibilityExplicity}, CrateRootModuleId, ModuleDefId, ModuleId, }; @@ -24,7 +24,7 @@ pub fn find_path( prefer_prelude: bool, ) -> Option<ModPath> { let _p = profile::span("find_path"); - find_path_inner(db, item, from, None, prefer_no_std, prefer_prelude) + find_path_inner(FindPathCtx { db, prefixed: None, prefer_no_std, prefer_prelude }, item, from) } pub fn find_path_prefixed( @@ -36,7 +36,11 @@ pub fn find_path_prefixed( prefer_prelude: bool, ) -> Option<ModPath> { let _p = profile::span("find_path_prefixed"); - find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std, prefer_prelude) + find_path_inner( + FindPathCtx { db, prefixed: Some(prefix_kind), prefer_no_std, prefer_prelude }, + item, + from, + ) } #[derive(Copy, Clone, Debug)] @@ -83,64 +87,60 @@ impl PrefixKind { } } -/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId -fn find_path_inner( - db: &dyn DefDatabase, - item: ItemInNs, - from: ModuleId, +#[derive(Copy, Clone)] +struct FindPathCtx<'db> { + db: &'db dyn DefDatabase, prefixed: Option<PrefixKind>, prefer_no_std: bool, prefer_prelude: bool, -) -> Option<ModPath> { +} + +/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId +fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Option<ModPath> { // - if the item is a builtin, it's in scope if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item { return Some(ModPath::from_segments(PathKind::Plain, Some(builtin.as_name()))); } - let def_map = from.def_map(db); + let def_map = from.def_map(ctx.db); let crate_root = def_map.crate_root(); // - if the item is a module, jump straight to module search if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { let mut visited_modules = FxHashSet::default(); return find_path_for_module( - db, + FindPathCtx { + prefer_no_std: ctx.prefer_no_std || ctx.db.crate_supports_no_std(crate_root.krate), + ..ctx + }, &def_map, &mut visited_modules, crate_root, from, module_id, MAX_PATH_LEN, - prefixed, - prefer_no_std || db.crate_supports_no_std(crate_root.krate), - prefer_prelude, ) .map(|(item, _)| item); } // - if the item is already in scope, return the name under which it is - let scope_name = find_in_scope(db, &def_map, from, item); - if prefixed.is_none() { + let scope_name = find_in_scope(ctx.db, &def_map, from, item); + if ctx.prefixed.is_none() { if let Some(scope_name) = scope_name { return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name))); } } // - if the item is in the prelude, return the name from there - if let value @ Some(_) = find_in_prelude(db, &crate_root.def_map(db), &def_map, item, from) { + if let value @ Some(_) = + find_in_prelude(ctx.db, &crate_root.def_map(ctx.db), &def_map, item, from) + { return value; } if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { // - if the item is an enum variant, refer to it via the enum - if let Some(mut path) = find_path_inner( - db, - ItemInNs::Types(variant.parent.into()), - from, - prefixed, - prefer_no_std, - prefer_prelude, - ) { - let data = db.enum_data(variant.parent); + if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(variant.parent.into()), from) { + let data = ctx.db.enum_data(variant.parent); path.push_segment(data.variants[variant.local_id].name.clone()); return Some(path); } @@ -152,32 +152,29 @@ fn find_path_inner( let mut visited_modules = FxHashSet::default(); calculate_best_path( - db, + FindPathCtx { + prefer_no_std: ctx.prefer_no_std || ctx.db.crate_supports_no_std(crate_root.krate), + ..ctx + }, &def_map, &mut visited_modules, crate_root, MAX_PATH_LEN, item, from, - prefixed, - prefer_no_std || db.crate_supports_no_std(crate_root.krate), - prefer_prelude, scope_name, ) .map(|(item, _)| item) } fn find_path_for_module( - db: &dyn DefDatabase, + ctx: FindPathCtx<'_>, def_map: &DefMap, visited_modules: &mut FxHashSet<ModuleId>, crate_root: CrateRootModuleId, from: ModuleId, module_id: ModuleId, max_len: usize, - prefixed: Option<PrefixKind>, - prefer_no_std: bool, - prefer_prelude: bool, ) -> Option<(ModPath, Stability)> { if max_len == 0 { return None; @@ -185,8 +182,8 @@ fn find_path_for_module( // Base cases: // - if the item is already in scope, return the name under which it is - let scope_name = find_in_scope(db, def_map, from, ItemInNs::Types(module_id.into())); - if prefixed.is_none() { + let scope_name = find_in_scope(ctx.db, def_map, from, ItemInNs::Types(module_id.into())); + if ctx.prefixed.is_none() { if let Some(scope_name) = scope_name { return Some((ModPath::from_segments(PathKind::Plain, Some(scope_name)), Stable)); } @@ -198,20 +195,20 @@ fn find_path_for_module( } // - if relative paths are fine, check if we are searching for a parent - if prefixed.filter(PrefixKind::is_absolute).is_none() { + if ctx.prefixed.filter(PrefixKind::is_absolute).is_none() { if let modpath @ Some(_) = find_self_super(def_map, module_id, from) { return modpath.zip(Some(Stable)); } } // - if the item is the crate root of a dependency crate, return the name from the extern prelude - let root_def_map = crate_root.def_map(db); + let root_def_map = crate_root.def_map(ctx.db); for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude() { if module_id == def_id { let name = scope_name.unwrap_or_else(|| name.clone()); let name_already_occupied_in_type_ns = def_map - .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| { + .with_ancestor_maps(ctx.db, from.local_id, &mut |def_map, local_id| { def_map[local_id] .scope .type_(&name) @@ -229,21 +226,18 @@ fn find_path_for_module( } if let value @ Some(_) = - find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from) + find_in_prelude(ctx.db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from) { return value.zip(Some(Stable)); } calculate_best_path( - db, + ctx, def_map, visited_modules, crate_root, max_len, ItemInNs::Types(module_id.into()), from, - prefixed, - prefer_no_std, - prefer_prelude, scope_name, ) } @@ -256,7 +250,7 @@ fn find_in_scope( item: ItemInNs, ) -> Option<Name> { def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| { - def_map[local_id].scope.name_of(item).map(|(name, _)| name.clone()) + def_map[local_id].scope.name_of(item).map(|(name, _, _)| name.clone()) }) } @@ -273,7 +267,7 @@ fn find_in_prelude( // Preludes in block DefMaps are ignored, only the crate DefMap is searched let prelude_def_map = prelude_module.def_map(db); let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; - let (name, vis) = prelude_scope.name_of(item)?; + let (name, vis, _declared) = prelude_scope.name_of(item)?; if !vis.is_visible_from(db, from) { return None; } @@ -315,16 +309,13 @@ fn find_self_super(def_map: &DefMap, item: ModuleId, from: ModuleId) -> Option<M } fn calculate_best_path( - db: &dyn DefDatabase, + ctx: FindPathCtx<'_>, def_map: &DefMap, visited_modules: &mut FxHashSet<ModuleId>, crate_root: CrateRootModuleId, max_len: usize, item: ItemInNs, from: ModuleId, - mut prefixed: Option<PrefixKind>, - prefer_no_std: bool, - prefer_prelude: bool, scope_name: Option<Name>, ) -> Option<(ModPath, Stability)> { if max_len <= 1 { @@ -341,32 +332,29 @@ fn calculate_best_path( }; // Recursive case: // - otherwise, look for modules containing (reexporting) it and import it from one of those - if item.krate(db) == Some(from.krate) { + if item.krate(ctx.db) == Some(from.krate) { let mut best_path_len = max_len; // Item was defined in the same crate that wants to import it. It cannot be found in any // dependency in this case. - for (module_id, name) in find_local_import_locations(db, item, from) { + for (module_id, name) in find_local_import_locations(ctx.db, item, from) { if !visited_modules.insert(module_id) { cov_mark::hit!(recursive_imports); continue; } if let Some(mut path) = find_path_for_module( - db, + ctx, def_map, visited_modules, crate_root, from, module_id, best_path_len - 1, - prefixed, - prefer_no_std, - prefer_prelude, ) { path.0.push_segment(name); let new_path = match best_path.take() { Some(best_path) => { - select_best_path(best_path, path, prefer_no_std, prefer_prelude) + select_best_path(best_path, path, ctx.prefer_no_std, ctx.prefer_prelude) } None => path, }; @@ -379,8 +367,8 @@ fn calculate_best_path( // too (unless we can't name it at all). It could *also* be (re)exported by the same crate // that wants to import it here, but we always prefer to use the external path here. - for dep in &db.crate_graph()[from.krate].dependencies { - let import_map = db.import_map(dep.crate_id); + for dep in &ctx.db.crate_graph()[from.krate].dependencies { + let import_map = ctx.db.import_map(dep.crate_id); let Some(import_info_for) = import_map.import_info_for(item) else { continue }; for info in import_info_for { if info.is_doc_hidden { @@ -391,16 +379,13 @@ fn calculate_best_path( // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? let Some((mut path, path_stability)) = find_path_for_module( - db, + ctx, def_map, visited_modules, crate_root, from, info.container, max_len - 1, - prefixed, - prefer_no_std, - prefer_prelude, ) else { continue; }; @@ -413,17 +398,21 @@ fn calculate_best_path( ); let new_path_with_stab = match best_path.take() { - Some(best_path) => { - select_best_path(best_path, path_with_stab, prefer_no_std, prefer_prelude) - } + Some(best_path) => select_best_path( + best_path, + path_with_stab, + ctx.prefer_no_std, + ctx.prefer_prelude, + ), None => path_with_stab, }; update_best_path(&mut best_path, new_path_with_stab); } } } - if let Some(module) = item.module(db) { - if module.containing_block().is_some() && prefixed.is_some() { + let mut prefixed = ctx.prefixed; + if let Some(module) = item.module(ctx.db) { + if module.containing_block().is_some() && ctx.prefixed.is_some() { cov_mark::hit!(prefixed_in_block_expression); prefixed = Some(PrefixKind::Plain); } @@ -548,34 +537,35 @@ fn find_local_import_locations( &ext_def_map[module.local_id] }; - if let Some((name, vis)) = data.scope.name_of(item) { + if let Some((name, vis, declared)) = data.scope.name_of(item) { if vis.is_visible_from(db, from) { - let is_private = match vis { - Visibility::Module(private_to) => private_to.local_id == module.local_id, - Visibility::Public => false, - }; - let is_original_def = match item.as_module_def_id() { - Some(module_def_id) => data.scope.declarations().any(|it| it == module_def_id), - None => false, + let is_pub_or_explicit = match vis { + Visibility::Module(_, VisibilityExplicity::Explicit) => { + cov_mark::hit!(explicit_private_imports); + true + } + Visibility::Module(_, VisibilityExplicity::Implicit) => { + cov_mark::hit!(discount_private_imports); + false + } + Visibility::Public => true, }; - // Ignore private imports. these could be used if we are + // Ignore private imports unless they are explicit. these could be used if we are // in a submodule of this module, but that's usually not // what the user wants; and if this module can import // the item and we're a submodule of it, so can we. // Also this keeps the cached data smaller. - if !is_private || is_original_def { + if is_pub_or_explicit || declared { locations.push((module, name.clone())); } } } // Descend into all modules visible from `from`. - for (ty, vis) in data.scope.types() { - if let ModuleDefId::ModuleId(module) = ty { - if vis.is_visible_from(db, from) { - worklist.push(module); - } + for (module, vis) in data.scope.modules_in_scope() { + if vis.is_visible_from(db, from) { + worklist.push(module); } } } @@ -625,16 +615,14 @@ mod tests { .expect("path does not resolve to a type"); let found_path = find_path_inner( - &db, + FindPathCtx { prefer_no_std: false, db: &db, prefixed: prefix_kind, prefer_prelude }, ItemInNs::Types(resolved), module, - prefix_kind, - false, - prefer_prelude, ); assert_eq!(found_path, Some(mod_path), "on kind: {prefix_kind:?}"); } + #[track_caller] fn check_found_path( ra_fixture: &str, unprefixed: &str, @@ -1004,6 +992,7 @@ pub use crate::foo::bar::S; #[test] fn discount_private_imports() { + cov_mark::check!(discount_private_imports); check_found_path( r#" //- /main.rs @@ -1022,6 +1011,47 @@ $0 } #[test] + fn explicit_private_imports_crate() { + cov_mark::check!(explicit_private_imports); + check_found_path( + r#" +//- /main.rs +mod foo; +pub mod bar { pub struct S; } +pub(crate) use bar::S; +//- /foo.rs +$0 + "#, + "crate::S", + "crate::S", + "crate::S", + "crate::S", + ); + } + + #[test] + fn explicit_private_imports() { + cov_mark::check!(explicit_private_imports); + check_found_path( + r#" +//- /main.rs +pub mod bar { + mod foo; + pub mod baz { pub struct S; } + pub(self) use baz::S; +} + +//- /bar/foo.rs +$0 + "#, + "super::S", + "super::S", + "crate::bar::S", + "super::S", + ); + } + + #[test] fn import_cycle() { check_found_path( r#" diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index f5324f052e5..6cb9b8448d1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -107,11 +107,11 @@ impl TypeOrConstParamData { impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData); /// Data about the generic parameters of a function, struct, impl, etc. -#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct GenericParams { pub type_or_consts: Arena<TypeOrConstParamData>, pub lifetimes: Arena<LifetimeParamData>, - pub where_predicates: Vec<WherePredicate>, + pub where_predicates: Box<[WherePredicate]>, } /// A single predicate from a where clause, i.e. `where Type: Trait`. Combined @@ -142,109 +142,14 @@ pub enum WherePredicateTypeTarget { TypeOrConstParam(LocalTypeOrConstParamId), } -impl GenericParams { - /// Iterator of type_or_consts field - pub fn iter( - &self, - ) -> impl DoubleEndedIterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)> { - self.type_or_consts.iter() - } - - pub(crate) fn generic_params_query( - db: &dyn DefDatabase, - def: GenericDefId, - ) -> Interned<GenericParams> { - let _p = profile::span("generic_params_query"); - - let krate = def.module(db).krate; - let cfg_options = db.crate_graph(); - let cfg_options = &cfg_options[krate].cfg_options; - - // Returns the generic parameters that are enabled under the current `#[cfg]` options - let enabled_params = |params: &Interned<GenericParams>, item_tree: &ItemTree| { - let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); - - // In the common case, no parameters will by disabled by `#[cfg]` attributes. - // Therefore, make a first pass to check if all parameters are enabled and, if so, - // clone the `Interned<GenericParams>` instead of recreating an identical copy. - let all_type_or_consts_enabled = - params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into())); - let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into())); - - if all_type_or_consts_enabled && all_lifetimes_enabled { - params.clone() - } else { - Interned::new(GenericParams { - type_or_consts: all_type_or_consts_enabled - .then(|| params.type_or_consts.clone()) - .unwrap_or_else(|| { - params - .type_or_consts - .iter() - .filter_map(|(idx, param)| { - enabled(idx.into()).then(|| param.clone()) - }) - .collect() - }), - lifetimes: all_lifetimes_enabled - .then(|| params.lifetimes.clone()) - .unwrap_or_else(|| { - params - .lifetimes - .iter() - .filter_map(|(idx, param)| { - enabled(idx.into()).then(|| param.clone()) - }) - .collect() - }), - where_predicates: params.where_predicates.clone(), - }) - } - }; - macro_rules! id_to_generics { - ($id:ident) => {{ - let id = $id.lookup(db).id; - let tree = id.item_tree(db); - let item = &tree[id.value]; - enabled_params(&item.generic_params, &tree) - }}; - } - - match def { - GenericDefId::FunctionId(id) => { - let loc = id.lookup(db); - let tree = loc.id.item_tree(db); - let item = &tree[loc.id.value]; - - let enabled_params = enabled_params(&item.explicit_generic_params, &tree); - let mut generic_params = GenericParams::clone(&enabled_params); - - let module = loc.container.module(db); - let func_data = db.function_data(id); - - // Don't create an `Expander` if not needed since this - // could cause a reparse after the `ItemTree` has been created due to the spanmap. - let mut expander = - Lazy::new(|| (module.def_map(db), Expander::new(db, loc.id.file_id(), module))); - for param in func_data.params.iter() { - generic_params.fill_implicit_impl_trait_args(db, &mut expander, param); - } - - Interned::new(generic_params) - } - GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics!(id), - GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics!(id), - GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics!(id), - GenericDefId::TraitId(id) => id_to_generics!(id), - GenericDefId::TraitAliasId(id) => id_to_generics!(id), - GenericDefId::TypeAliasId(id) => id_to_generics!(id), - GenericDefId::ImplId(id) => id_to_generics!(id), - GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => { - Interned::new(GenericParams::default()) - } - } - } +#[derive(Clone, Default)] +pub(crate) struct GenericParamsCollector { + pub(crate) type_or_consts: Arena<TypeOrConstParamData>, + lifetimes: Arena<LifetimeParamData>, + where_predicates: Vec<WherePredicate>, +} +impl GenericParamsCollector { pub(crate) fn fill( &mut self, lower_ctx: &LowerCtx<'_>, @@ -444,11 +349,131 @@ impl GenericParams { }); } - pub(crate) fn shrink_to_fit(&mut self) { - let Self { lifetimes, type_or_consts: types, where_predicates } = self; + pub(crate) fn finish(self) -> GenericParams { + let Self { mut lifetimes, mut type_or_consts, where_predicates } = self; lifetimes.shrink_to_fit(); - types.shrink_to_fit(); - where_predicates.shrink_to_fit(); + type_or_consts.shrink_to_fit(); + GenericParams { + type_or_consts, + lifetimes, + where_predicates: where_predicates.into_boxed_slice(), + } + } +} + +impl GenericParams { + /// Iterator of type_or_consts field + pub fn iter( + &self, + ) -> impl DoubleEndedIterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)> { + self.type_or_consts.iter() + } + + pub(crate) fn generic_params_query( + db: &dyn DefDatabase, + def: GenericDefId, + ) -> Interned<GenericParams> { + let _p = profile::span("generic_params_query"); + + let krate = def.module(db).krate; + let cfg_options = db.crate_graph(); + let cfg_options = &cfg_options[krate].cfg_options; + + // Returns the generic parameters that are enabled under the current `#[cfg]` options + let enabled_params = |params: &Interned<GenericParams>, item_tree: &ItemTree| { + let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); + + // In the common case, no parameters will by disabled by `#[cfg]` attributes. + // Therefore, make a first pass to check if all parameters are enabled and, if so, + // clone the `Interned<GenericParams>` instead of recreating an identical copy. + let all_type_or_consts_enabled = + params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into())); + let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into())); + + if all_type_or_consts_enabled && all_lifetimes_enabled { + params.clone() + } else { + Interned::new(GenericParams { + type_or_consts: all_type_or_consts_enabled + .then(|| params.type_or_consts.clone()) + .unwrap_or_else(|| { + params + .type_or_consts + .iter() + .filter_map(|(idx, param)| { + enabled(idx.into()).then(|| param.clone()) + }) + .collect() + }), + lifetimes: all_lifetimes_enabled + .then(|| params.lifetimes.clone()) + .unwrap_or_else(|| { + params + .lifetimes + .iter() + .filter_map(|(idx, param)| { + enabled(idx.into()).then(|| param.clone()) + }) + .collect() + }), + where_predicates: params.where_predicates.clone(), + }) + } + }; + macro_rules! id_to_generics { + ($id:ident) => {{ + let id = $id.lookup(db).id; + let tree = id.item_tree(db); + let item = &tree[id.value]; + enabled_params(&item.generic_params, &tree) + }}; + } + + match def { + GenericDefId::FunctionId(id) => { + let loc = id.lookup(db); + let tree = loc.id.item_tree(db); + let item = &tree[loc.id.value]; + + let enabled_params = enabled_params(&item.explicit_generic_params, &tree); + + let module = loc.container.module(db); + let func_data = db.function_data(id); + if func_data.params.is_empty() { + enabled_params + } else { + let mut generic_params = GenericParamsCollector { + type_or_consts: enabled_params.type_or_consts.clone(), + lifetimes: enabled_params.lifetimes.clone(), + where_predicates: enabled_params.where_predicates.clone().into(), + }; + + // Don't create an `Expander` if not needed since this + // could cause a reparse after the `ItemTree` has been created due to the spanmap. + let mut expander = Lazy::new(|| { + (module.def_map(db), Expander::new(db, loc.id.file_id(), module)) + }); + for param in func_data.params.iter() { + generic_params.fill_implicit_impl_trait_args(db, &mut expander, param); + } + Interned::new(generic_params.finish()) + } + } + GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics!(id), + GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics!(id), + GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics!(id), + GenericDefId::TraitId(id) => id_to_generics!(id), + GenericDefId::TraitAliasId(id) => id_to_generics!(id), + GenericDefId::TypeAliasId(id) => id_to_generics!(id), + GenericDefId::ImplId(id) => id_to_generics!(id), + GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => { + Interned::new(GenericParams { + type_or_consts: Default::default(), + lifetimes: Default::default(), + where_predicates: Default::default(), + }) + } + } } pub fn find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option<TypeParamId> { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index 7fc33abc7c9..c0d1738b504 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -2,7 +2,7 @@ use std::mem; use hir_expand::name::Name; -use rustc_dependencies::parse_format as parse; +use rustc_parse_format as parse; use stdx::TupleExt; use syntax::{ ast::{self, IsString}, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index 75adf21abdc..935a8ebad16 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs @@ -116,8 +116,7 @@ pub enum TypeRef { Path(Path), RawPtr(Box<TypeRef>, Mutability), Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability), - // FIXME: for full const generics, the latter element (length) here is going to have to be an - // expression that is further lowered later in hir_ty. + // FIXME: This should be Array(Box<TypeRef>, Ast<ConstArg>), Array(Box<TypeRef>, ConstRef), Slice(Box<TypeRef>), /// A fn pointer. Last element of the vector is the return type. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 4902f24e2e3..168ee4acffb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -15,9 +15,11 @@ use stdx::format_to; use syntax::ast; use crate::{ - db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, - ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, - TraitId, UseId, + db::DefDatabase, + per_ns::PerNs, + visibility::{Visibility, VisibilityExplicity}, + AdtId, BuiltinType, ConstId, ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, + ModuleDefId, ModuleId, TraitId, UseId, }; #[derive(Debug, Default)] @@ -105,7 +107,7 @@ pub struct ItemScope { /// The attribute macro invocations in this scope. attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>, /// The macro invocations in this scope. - pub macro_invocations: FxHashMap<AstId<ast::MacroCall>, MacroCallId>, + macro_invocations: FxHashMap<AstId<ast::MacroCall>, MacroCallId>, /// The derive macro invocations in this scope, keyed by the owner item over the actual derive attributes /// paired with the derive macro invocations for the specific attribute. derive_macros: FxHashMap<AstId<ast::Adt>, SmallVec<[DeriveMacroInvocation; 1]>>, @@ -145,8 +147,8 @@ impl ItemScope { .chain(self.values.keys()) .chain(self.macros.keys()) .chain(self.unresolved.iter()) - .unique() .sorted() + .dedup() .map(move |name| (name, self.get(name))) } @@ -157,8 +159,8 @@ impl ItemScope { .filter_map(ImportOrExternCrate::into_import) .chain(self.use_imports_values.keys().copied()) .chain(self.use_imports_macros.keys().copied()) - .unique() .sorted() + .dedup() } pub fn fully_resolve_import(&self, db: &dyn DefDatabase, mut import: ImportId) -> PerNs { @@ -234,20 +236,37 @@ impl ItemScope { self.impls.iter().copied() } - pub fn values( - &self, - ) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ { - self.values.values().copied().map(|(a, b, _)| (a, b)) + pub(crate) fn modules_in_scope(&self) -> impl Iterator<Item = (ModuleId, Visibility)> + '_ { + self.types.values().copied().filter_map(|(def, vis, _)| match def { + ModuleDefId::ModuleId(module) => Some((module, vis)), + _ => None, + }) } - pub(crate) fn types( - &self, - ) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ { - self.types.values().copied().map(|(def, vis, _)| (def, vis)) - } + pub fn unnamed_consts<'a>( + &'a self, + db: &'a dyn DefDatabase, + ) -> impl Iterator<Item = ConstId> + 'a { + // FIXME: Also treat consts named `_DERIVE_*` as unnamed, since synstructure generates those. + // Should be removed once synstructure stops doing that. + let synstructure_hack_consts = self.values.values().filter_map(|(item, _, _)| match item { + &ModuleDefId::ConstId(id) => { + let loc = id.lookup(db); + let item_tree = loc.id.item_tree(db); + if item_tree[loc.id.value] + .name + .as_ref() + .map_or(false, |n| n.to_smol_str().starts_with("_DERIVE_")) + { + Some(id) + } else { + None + } + } + _ => None, + }); - pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ { - self.unnamed_consts.iter().copied() + self.unnamed_consts.iter().copied().chain(synstructure_hack_consts) } /// Iterate over all module scoped macros @@ -274,21 +293,18 @@ impl ItemScope { } /// XXX: this is O(N) rather than O(1), try to not introduce new usages. - pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { + pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility, /*declared*/ bool)> { match item { - ItemInNs::Macros(def) => self - .macros - .iter() - .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), - ItemInNs::Types(def) => self - .types - .iter() - .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), - - ItemInNs::Values(def) => self - .values - .iter() - .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), + ItemInNs::Macros(def) => self.macros.iter().find_map(|(name, &(other_def, vis, i))| { + (other_def == def).then_some((name, vis, i.is_none())) + }), + ItemInNs::Types(def) => self.types.iter().find_map(|(name, &(other_def, vis, i))| { + (other_def == def).then_some((name, vis, i.is_none())) + }), + + ItemInNs::Values(def) => self.values.iter().find_map(|(name, &(other_def, vis, i))| { + (other_def == def).then_some((name, vis, i.is_none())) + }), } } @@ -316,6 +332,10 @@ impl ItemScope { }), ) } + + pub(crate) fn macro_invoc(&self, call: AstId<ast::MacroCall>) -> Option<MacroCallId> { + self.macro_invocations.get(&call).copied() + } } impl ItemScope { @@ -624,18 +644,17 @@ impl ItemScope { pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) { self.types .values_mut() - .map(|(def, vis, _)| (def, vis)) - .chain(self.values.values_mut().map(|(def, vis, _)| (def, vis))) - .map(|(_, v)| v) + .map(|(_, vis, _)| vis) + .chain(self.values.values_mut().map(|(_, vis, _)| vis)) .chain(self.unnamed_trait_imports.values_mut().map(|(vis, _)| vis)) - .for_each(|vis| *vis = Visibility::Module(this_module)); + .for_each(|vis| *vis = Visibility::Module(this_module, VisibilityExplicity::Implicit)); for (mac, vis, import) in self.macros.values_mut() { if matches!(mac, MacroId::ProcMacroId(_) if import.is_none()) { continue; } - *vis = Visibility::Module(this_module); + *vis = Visibility::Module(this_module, VisibilityExplicity::Implicit); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 20e4e44339e..82ea5ffeba1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -69,7 +69,7 @@ use crate::{ generics::{GenericParams, LifetimeParamData, TypeOrConstParamData}, path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind}, type_ref::{Mutability, TraitRef, TypeBound, TypeRef}, - visibility::RawVisibility, + visibility::{RawVisibility, VisibilityExplicity}, BlockId, Lookup, }; @@ -78,8 +78,9 @@ pub struct RawVisibilityId(u32); impl RawVisibilityId { pub const PUB: Self = RawVisibilityId(u32::max_value()); - pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1); - pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2); + pub const PRIV_IMPLICIT: Self = RawVisibilityId(u32::max_value() - 1); + pub const PRIV_EXPLICIT: Self = RawVisibilityId(u32::max_value() - 2); + pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 3); } impl fmt::Debug for RawVisibilityId { @@ -87,7 +88,7 @@ impl fmt::Debug for RawVisibilityId { let mut f = f.debug_tuple("RawVisibilityId"); match *self { Self::PUB => f.field(&"pub"), - Self::PRIV => f.field(&"pub(self)"), + Self::PRIV_IMPLICIT | Self::PRIV_EXPLICIT => f.field(&"pub(self)"), Self::PUB_CRATE => f.field(&"pub(crate)"), _ => f.field(&self.0), }; @@ -249,19 +250,30 @@ impl ItemVisibilities { fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId { match &vis { RawVisibility::Public => RawVisibilityId::PUB, - RawVisibility::Module(path) if path.segments().is_empty() => match &path.kind { - PathKind::Super(0) => RawVisibilityId::PRIV, - PathKind::Crate => RawVisibilityId::PUB_CRATE, - _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), - }, + RawVisibility::Module(path, explicitiy) if path.segments().is_empty() => { + match (&path.kind, explicitiy) { + (PathKind::Super(0), VisibilityExplicity::Explicit) => { + RawVisibilityId::PRIV_EXPLICIT + } + (PathKind::Super(0), VisibilityExplicity::Implicit) => { + RawVisibilityId::PRIV_IMPLICIT + } + (PathKind::Crate, _) => RawVisibilityId::PUB_CRATE, + _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), + } + } _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), } } } static VIS_PUB: RawVisibility = RawVisibility::Public; -static VIS_PRIV: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Super(0))); -static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Crate)); +static VIS_PRIV_IMPLICIT: RawVisibility = + RawVisibility::Module(ModPath::from_kind(PathKind::Super(0)), VisibilityExplicity::Implicit); +static VIS_PRIV_EXPLICIT: RawVisibility = + RawVisibility::Module(ModPath::from_kind(PathKind::Super(0)), VisibilityExplicity::Explicit); +static VIS_PUB_CRATE: RawVisibility = + RawVisibility::Module(ModPath::from_kind(PathKind::Crate), VisibilityExplicity::Explicit); #[derive(Default, Debug, Eq, PartialEq)] struct ItemTreeData { @@ -540,7 +552,8 @@ impl Index<RawVisibilityId> for ItemTree { type Output = RawVisibility; fn index(&self, index: RawVisibilityId) -> &Self::Output { match index { - RawVisibilityId::PRIV => &VIS_PRIV, + RawVisibilityId::PRIV_IMPLICIT => &VIS_PRIV_IMPLICIT, + RawVisibilityId::PRIV_EXPLICIT => &VIS_PRIV_EXPLICIT, RawVisibilityId::PUB => &VIS_PUB, RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE, _ => &self.data().vis.arena[Idx::from_raw(index.0.into())], diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 8e2fafe81b5..6343b43a016 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -6,7 +6,7 @@ use hir_expand::{ast_id_map::AstIdMap, span_map::SpanMapRef, HirFileId}; use syntax::ast::{self, HasModuleItem, HasTypeBounds}; use crate::{ - generics::{GenericParams, TypeParamData, TypeParamProvenance}, + generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance}, type_ref::{LifetimeRef, TraitBoundModifier, TraitRef}, LocalLifetimeParamId, LocalTypeOrConstParamId, }; @@ -386,17 +386,16 @@ impl<'a> Ctx<'a> { flags |= FnFlags::HAS_UNSAFE_KW; } - let mut res = Function { + let res = Function { name, visibility, - explicit_generic_params: Interned::new(GenericParams::default()), + explicit_generic_params: self.lower_generic_params(HasImplicitSelf::No, func), abi, params, ret_type: Interned::new(ret_type), ast_id, flags, }; - res.explicit_generic_params = self.lower_generic_params(HasImplicitSelf::No, func); Some(id(self.data().functions.alloc(res))) } @@ -604,7 +603,7 @@ impl<'a> Ctx<'a> { has_implicit_self: HasImplicitSelf, node: &dyn ast::HasGenericParams, ) -> Interned<GenericParams> { - let mut generics = GenericParams::default(); + let mut generics = GenericParamsCollector::default(); if let HasImplicitSelf::Yes(bounds) = has_implicit_self { // Traits and trait aliases get the Self type as an implicit first type parameter. @@ -642,8 +641,7 @@ impl<'a> Ctx<'a> { }; generics.fill(&self.body_ctx, node, add_param_attrs); - generics.shrink_to_fit(); - Interned::new(generics) + Interned::new(generics.finish()) } fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Box<[Interned<TypeBound>]> { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 6d92fce0727..8693b9a98c9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -104,7 +104,9 @@ impl Printer<'_> { fn print_visibility(&mut self, vis: RawVisibilityId) { match &self.tree[vis] { - RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db.upcast())), + RawVisibility::Module(path, _expl) => { + w!(self, "pub({}) ", path.display(self.db.upcast())) + } RawVisibility::Public => w!(self, "pub "), }; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 1ae6bd4c919..66e0d2cc346 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -87,7 +87,10 @@ impl LangItems { } /// Salsa query. This will look for lang items in a specific crate. - pub(crate) fn crate_lang_items_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<LangItems> { + pub(crate) fn crate_lang_items_query( + db: &dyn DefDatabase, + krate: CrateId, + ) -> Option<Arc<LangItems>> { let _p = profile::span("crate_lang_items_query"); let mut lang_items = LangItems::default(); @@ -150,7 +153,11 @@ impl LangItems { } } - Arc::new(lang_items) + if lang_items.items.is_empty() { + None + } else { + Some(Arc::new(lang_items)) + } } /// Salsa query. Look for a lang item, starting from the specified crate and recursively @@ -161,9 +168,9 @@ impl LangItems { item: LangItem, ) -> Option<LangItemTarget> { let _p = profile::span("lang_item_query"); - let lang_items = db.crate_lang_items(start_crate); - let start_crate_target = lang_items.items.get(&item); - if let Some(&target) = start_crate_target { + if let Some(target) = + db.crate_lang_items(start_crate).and_then(|it| it.items.get(&item).copied()) + { return Some(target); } db.crate_graph()[start_crate] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 250d7b677b5..aa84ccaee6e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -10,10 +10,17 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#[allow(unused)] -macro_rules! eprintln { - ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; -} +#[cfg(feature = "in-rust-tree")] +extern crate rustc_parse_format; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_parse_format as rustc_parse_format; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_abi; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_abi as rustc_abi; pub mod db; @@ -49,7 +56,7 @@ pub mod visibility; pub mod find_path; pub mod import_map; -pub use rustc_dependencies::abi as layout; +pub use rustc_abi as layout; use triomphe::Arc; #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index abd84c6a46d..553c0b79533 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -16,13 +16,12 @@ struct Foo; #[derive(Copy)] struct Foo; -impl < > core::marker::Copy for Foo< > where {}"#]], +impl < > $crate::marker::Copy for Foo< > where {}"#]], ); } #[test] fn test_copy_expand_in_core() { - cov_mark::check!(test_copy_expand_in_core); check( r#" //- /lib.rs crate:core @@ -41,7 +40,7 @@ macro Copy {} #[derive(Copy)] struct Foo; -impl < > crate ::marker::Copy for Foo< > where {}"#]], +impl < > $crate::marker::Copy for Foo< > where {}"#]], ); } @@ -57,7 +56,7 @@ struct Foo<A, B>; #[derive(Copy)] struct Foo<A, B>; -impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]], +impl <A: $crate::marker::Copy, B: $crate::marker::Copy, > $crate::marker::Copy for Foo<A, B, > where {}"#]], ); } @@ -74,7 +73,7 @@ struct Foo<A, B, 'a, 'b>; #[derive(Copy)] struct Foo<A, B, 'a, 'b>; -impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]], +impl <A: $crate::marker::Copy, B: $crate::marker::Copy, > $crate::marker::Copy for Foo<A, B, > where {}"#]], ); } @@ -98,7 +97,7 @@ enum Command<A, B> { Jump, } -impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Command<A, B, > where { +impl <A: $crate::clone::Clone, B: $crate::clone::Clone, > $crate::clone::Clone for Command<A, B, > where { fn clone(&self ) -> Self { match self { Command::Move { @@ -158,7 +157,7 @@ where generic: Vec<T::InGenericArg>, } -impl <T: core::clone::Clone, > core::clone::Clone for Foo<T, > where T: Trait, T::InFieldShorthand: core::clone::Clone, T::InGenericArg: core::clone::Clone, { +impl <T: $crate::clone::Clone, > $crate::clone::Clone for Foo<T, > where T: Trait, T::InFieldShorthand: $crate::clone::Clone, T::InGenericArg: $crate::clone::Clone, { fn clone(&self ) -> Self { match self { Foo { @@ -186,7 +185,7 @@ struct Foo<const X: usize, T>(u32); #[derive(Clone)] struct Foo<const X: usize, T>(u32); -impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where { +impl <const X: usize, T: $crate::clone::Clone, > $crate::clone::Clone for Foo<X, T, > where { fn clone(&self ) -> Self { match self { Foo(f0, )=>Foo(f0.clone(), ), @@ -226,14 +225,14 @@ enum Bar { Bar, } -impl < > core::default::Default for Foo< > where { +impl < > $crate::default::Default for Foo< > where { fn default() -> Self { Foo { - field1: core::default::Default::default(), field2: core::default::Default::default(), + field1: $crate::default::Default::default(), field2: $crate::default::Default::default(), } } } -impl < > core::default::Default for Bar< > where { +impl < > $crate::default::Default for Bar< > where { fn default() -> Self { Bar::Bar } @@ -261,7 +260,7 @@ enum Command { Jump, } -impl < > core::cmp::PartialEq for Command< > where { +impl < > $crate::cmp::PartialEq for Command< > where { fn eq(&self , other: &Self ) -> bool { match (self , other) { (Command::Move { @@ -274,7 +273,7 @@ impl < > core::cmp::PartialEq for Command< > where { } } } -impl < > core::cmp::Eq for Command< > where {}"#]], +impl < > $crate::cmp::Eq for Command< > where {}"#]], ); } @@ -299,7 +298,7 @@ enum Command { Jump, } -impl < > core::cmp::PartialEq for Command< > where { +impl < > $crate::cmp::PartialEq for Command< > where { fn eq(&self , other: &Self ) -> bool { match (self , other) { (Command::Move { @@ -312,7 +311,7 @@ impl < > core::cmp::PartialEq for Command< > where { } } } -impl < > core::cmp::Eq for Command< > where {}"#]], +impl < > $crate::cmp::Eq for Command< > where {}"#]], ); } @@ -336,10 +335,10 @@ enum Command { Jump, } -impl < > core::cmp::PartialOrd for Command< > where { - fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option<core::cmp::Ordering> { - match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) { - core::option::Option::Some(core::cmp::Ordering::Equal)=> { +impl < > $crate::cmp::PartialOrd for Command< > where { + fn partial_cmp(&self , other: &Self ) -> $crate::option::Option::Option<$crate::cmp::Ordering> { + match $crate::intrinsics::discriminant_value(self ).partial_cmp(&$crate::intrinsics::discriminant_value(other)) { + $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { match (self , other) { (Command::Move { x: x_self, y: y_self, @@ -348,10 +347,10 @@ impl < > core::cmp::PartialOrd for Command< > where { x: x_other, y: y_other, } )=>match x_self.partial_cmp(&x_other) { - core::option::Option::Some(core::cmp::Ordering::Equal)=> { + $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { match y_self.partial_cmp(&y_other) { - core::option::Option::Some(core::cmp::Ordering::Equal)=> { - core::option::Option::Some(core::cmp::Ordering::Equal) + $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { + $crate::option::Option::Some($crate::cmp::Ordering::Equal) } c=>return c, } @@ -359,22 +358,22 @@ impl < > core::cmp::PartialOrd for Command< > where { c=>return c, } , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) { - core::option::Option::Some(core::cmp::Ordering::Equal)=> { - core::option::Option::Some(core::cmp::Ordering::Equal) + $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { + $crate::option::Option::Some($crate::cmp::Ordering::Equal) } c=>return c, } - , (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal) + , (Command::Jump, Command::Jump)=>$crate::option::Option::Some($crate::cmp::Ordering::Equal), _unused=>$crate::option::Option::Some($crate::cmp::Ordering::Equal) } } c=>return c, } } } -impl < > core::cmp::Ord for Command< > where { - fn cmp(&self , other: &Self ) -> core::cmp::Ordering { - match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) { - core::cmp::Ordering::Equal=> { +impl < > $crate::cmp::Ord for Command< > where { + fn cmp(&self , other: &Self ) -> $crate::cmp::Ordering { + match $crate::intrinsics::discriminant_value(self ).cmp(&$crate::intrinsics::discriminant_value(other)) { + $crate::cmp::Ordering::Equal=> { match (self , other) { (Command::Move { x: x_self, y: y_self, @@ -383,10 +382,10 @@ impl < > core::cmp::Ord for Command< > where { x: x_other, y: y_other, } )=>match x_self.cmp(&x_other) { - core::cmp::Ordering::Equal=> { + $crate::cmp::Ordering::Equal=> { match y_self.cmp(&y_other) { - core::cmp::Ordering::Equal=> { - core::cmp::Ordering::Equal + $crate::cmp::Ordering::Equal=> { + $crate::cmp::Ordering::Equal } c=>return c, } @@ -394,12 +393,12 @@ impl < > core::cmp::Ord for Command< > where { c=>return c, } , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) { - core::cmp::Ordering::Equal=> { - core::cmp::Ordering::Equal + $crate::cmp::Ordering::Equal=> { + $crate::cmp::Ordering::Equal } c=>return c, } - , (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal + , (Command::Jump, Command::Jump)=>$crate::cmp::Ordering::Equal, _unused=>$crate::cmp::Ordering::Equal } } c=>return c, @@ -433,8 +432,8 @@ struct Foo { z: (i32, u64), } -impl < > core::hash::Hash for Foo< > where { - fn hash<H: core::hash::Hasher>(&self , ra_expand_state: &mut H) { +impl < > $crate::hash::Hash for Foo< > where { + fn hash<H: $crate::hash::Hasher>(&self , ra_expand_state: &mut H) { match self { Foo { x: x, y: y, z: z, @@ -471,9 +470,9 @@ enum Command { Jump, } -impl < > core::hash::Hash for Command< > where { - fn hash<H: core::hash::Hasher>(&self , ra_expand_state: &mut H) { - core::mem::discriminant(self ).hash(ra_expand_state); +impl < > $crate::hash::Hash for Command< > where { + fn hash<H: $crate::hash::Hasher>(&self , ra_expand_state: &mut H) { + $crate::mem::discriminant(self ).hash(ra_expand_state); match self { Command::Move { x: x, y: y, @@ -517,8 +516,8 @@ enum Command { Jump, } -impl < > core::fmt::Debug for Command< > where { - fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result { +impl < > $crate::fmt::Debug for Command< > where { + fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { match self { Command::Move { x: x, y: y, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index d4798f4507d..4690ca5d363 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -136,7 +136,7 @@ fn main() { option_env!("TEST_ENV_VAR"); } #[rustc_builtin_macro] macro_rules! option_env {() => {}} -fn main() { ::core::option::Option::None:: < &str>; } +fn main() { $crate::option::Option::None:: < &str>; } "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 52a981fd19e..53644f58efc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -79,7 +79,7 @@ use crate::{ nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, path::ModPath, per_ns::PerNs, - visibility::Visibility, + visibility::{Visibility, VisibilityExplicity}, AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, }; @@ -332,7 +332,10 @@ impl DefMap { // NB: we use `None` as block here, which would be wrong for implicit // modules declared by blocks with items. At the moment, we don't use // this visibility for anything outside IDE, so that's probably OK. - let visibility = Visibility::Module(ModuleId { krate, local_id, block: None }); + let visibility = Visibility::Module( + ModuleId { krate, local_id, block: None }, + VisibilityExplicity::Implicit, + ); let module_data = ModuleData::new( ModuleOrigin::BlockExpr { block: block.ast_id, id: block_id }, visibility, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index be3438e427d..389dabdbc86 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -87,7 +87,7 @@ impl DefMap { within_impl: bool, ) -> Option<Visibility> { let mut vis = match visibility { - RawVisibility::Module(path) => { + RawVisibility::Module(path, explicity) => { let (result, remaining) = self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None); if remaining.is_some() { @@ -95,7 +95,7 @@ impl DefMap { } let types = result.take_types()?; match types { - ModuleDefId::ModuleId(m) => Visibility::Module(m), + ModuleDefId::ModuleId(m) => Visibility::Module(m, *explicity), // error: visibility needs to refer to module _ => { return None; @@ -108,11 +108,11 @@ impl DefMap { // In block expressions, `self` normally refers to the containing non-block module, and // `super` to its parent (etc.). However, visibilities must only refer to a module in the // DefMap they're written in, so we restrict them when that happens. - if let Visibility::Module(m) = vis { + if let Visibility::Module(m, mv) = vis { // ...unless we're resolving visibility for an associated item in an impl. if self.block_id() != m.block && !within_impl { cov_mark::hit!(adjust_vis_in_block_def_map); - vis = Visibility::Module(self.module_id(Self::ROOT)); + vis = Visibility::Module(self.module_id(Self::ROOT), mv); tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index 48fe43450a7..0f6e64016f1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -1265,6 +1265,54 @@ struct A; } #[test] +fn nested_include() { + check( + r#" +//- minicore: include +//- /lib.rs +include!("out_dir/includes.rs"); + +//- /out_dir/includes.rs +pub mod company_name { + pub mod network { + pub mod v1 { + include!("company_name.network.v1.rs"); + } + } +} +//- /out_dir/company_name.network.v1.rs +pub struct IpAddress { + pub ip_type: &'static str, +} +/// Nested message and enum types in `IpAddress`. +pub mod ip_address { + pub enum IpType { + IpV4(u32), + } +} + +"#, + expect![[r#" + crate + company_name: t + + crate::company_name + network: t + + crate::company_name::network + v1: t + + crate::company_name::network::v1 + IpAddress: t + ip_address: t + + crate::company_name::network::v1::ip_address + IpType: t + "#]], + ); +} + +#[test] fn macro_use_imports_all_macro_types() { let db = TestDB::with_files( r#" diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 301391516d6..1d850f721c1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -242,7 +242,7 @@ impl Resolver { let within_impl = self.scopes().find(|scope| matches!(scope, Scope::ImplDefScope(_))).is_some(); match visibility { - RawVisibility::Module(_) => { + RawVisibility::Module(_, _) => { let (item_map, module) = self.item_scope(); item_map.resolve_visibility(db, module, visibility, within_impl) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index f4a6b61f7af..c992c3c9204 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -8,7 +8,6 @@ use base_db::{ Upcast, }; use hir_expand::{db::ExpandDatabase, InFile}; -use rustc_hash::FxHashSet; use syntax::{algo, ast, AstNode}; use triomphe::Arc; @@ -76,7 +75,7 @@ impl FileLoader for TestDB { fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> { FileLoaderDelegate(self).relevant_crates(file_id) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs index 49688c5ee9c..cd8023f5d7d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs @@ -20,14 +20,14 @@ use crate::{ pub enum RawVisibility { /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is /// equivalent to `pub(self)`. - Module(ModPath), + Module(ModPath, VisibilityExplicity), /// `pub`. Public, } impl RawVisibility { pub(crate) const fn private() -> RawVisibility { - RawVisibility::Module(ModPath::from_kind(PathKind::Super(0))) + RawVisibility::Module(ModPath::from_kind(PathKind::Super(0)), VisibilityExplicity::Implicit) } pub(crate) fn from_ast( @@ -42,17 +42,8 @@ impl RawVisibility { node: Option<ast::Visibility>, span_map: SpanMapRef<'_>, ) -> RawVisibility { - Self::from_ast_with_span_map_and_default(db, node, RawVisibility::private(), span_map) - } - - pub(crate) fn from_ast_with_span_map_and_default( - db: &dyn DefDatabase, - node: Option<ast::Visibility>, - default: RawVisibility, - span_map: SpanMapRef<'_>, - ) -> RawVisibility { let node = match node { - None => return default, + None => return RawVisibility::private(), Some(node) => node, }; match node.kind() { @@ -62,19 +53,19 @@ impl RawVisibility { None => return RawVisibility::private(), Some(path) => path, }; - RawVisibility::Module(path) + RawVisibility::Module(path, VisibilityExplicity::Explicit) } ast::VisibilityKind::PubCrate => { let path = ModPath::from_kind(PathKind::Crate); - RawVisibility::Module(path) + RawVisibility::Module(path, VisibilityExplicity::Explicit) } ast::VisibilityKind::PubSuper => { let path = ModPath::from_kind(PathKind::Super(1)); - RawVisibility::Module(path) + RawVisibility::Module(path, VisibilityExplicity::Explicit) } ast::VisibilityKind::PubSelf => { let path = ModPath::from_kind(PathKind::Super(0)); - RawVisibility::Module(path) + RawVisibility::Module(path, VisibilityExplicity::Explicit) } ast::VisibilityKind::Pub => RawVisibility::Public, } @@ -94,7 +85,7 @@ impl RawVisibility { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Visibility { /// Visibility is restricted to a certain module. - Module(ModuleId), + Module(ModuleId, VisibilityExplicity), /// Visibility is unrestricted. Public, } @@ -102,7 +93,7 @@ pub enum Visibility { impl Visibility { pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool { let to_module = match self { - Visibility::Module(m) => m, + Visibility::Module(m, _) => m, Visibility::Public => return true, }; // if they're not in the same crate, it can't be visible @@ -124,7 +115,7 @@ impl Visibility { mut from_module: LocalModuleId, ) -> bool { let mut to_module = match self { - Visibility::Module(m) => m, + Visibility::Module(m, _) => m, Visibility::Public => return true, }; @@ -181,9 +172,9 @@ impl Visibility { /// visible in unrelated modules). pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> { match (self, other) { - (Visibility::Module(_) | Visibility::Public, Visibility::Public) - | (Visibility::Public, Visibility::Module(_)) => Some(Visibility::Public), - (Visibility::Module(mod_a), Visibility::Module(mod_b)) => { + (Visibility::Module(_, _) | Visibility::Public, Visibility::Public) + | (Visibility::Public, Visibility::Module(_, _)) => Some(Visibility::Public), + (Visibility::Module(mod_a, vis_a), Visibility::Module(mod_b, vis_b)) => { if mod_a.krate != mod_b.krate { return None; } @@ -199,12 +190,12 @@ impl Visibility { if a_ancestors.any(|m| m == mod_b.local_id) { // B is above A - return Some(Visibility::Module(mod_b)); + return Some(Visibility::Module(mod_b, vis_b)); } if b_ancestors.any(|m| m == mod_a.local_id) { // A is above B - return Some(Visibility::Module(mod_a)); + return Some(Visibility::Module(mod_a, vis_a)); } None @@ -213,6 +204,19 @@ impl Visibility { } } +/// Whether the item was imported through `pub(crate) use` or just `use`. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum VisibilityExplicity { + Explicit, + Implicit, +} + +impl VisibilityExplicity { + pub fn is_explicit(&self) -> bool { + matches!(self, Self::Explicit) + } +} + /// Resolve visibility of all specific fields of a struct or union variant. pub(crate) fn field_visibilities_query( db: &dyn DefDatabase, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs index 8f240ef0732..46bbb7f92c0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs @@ -1,6 +1,5 @@ //! Builtin derives. -use base_db::{CrateOrigin, LangCrateOrigin}; use itertools::izip; use rustc_hash::FxHashSet; use span::{MacroCallId, Span}; @@ -10,6 +9,7 @@ use tracing::debug; use crate::{ hygiene::span_with_def_site_ctxt, name::{AsName, Name}, + quote::dollar_crate, span_map::SpanMapRef, tt, }; @@ -38,7 +38,7 @@ macro_rules! register_builtin { let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); - expander(db, id, span, tt, token_map) + expander(span, tt, token_map) } fn find_by_name(name: &name::Name) -> Option<Self> { @@ -398,41 +398,13 @@ fn expand_simple_derive( ExpandResult::ok(expanded) } -fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: Span) -> tt::TokenTree { - // FIXME: make hygiene works for builtin derive macro - // such that $crate can be used here. - let cg = db.crate_graph(); - let krate = db.lookup_intern_macro_call(id).krate; - - let tt = if matches!(cg[krate].origin, CrateOrigin::Lang(LangCrateOrigin::Core)) { - cov_mark::hit!(test_copy_expand_in_core); - quote! {span => crate } - } else { - quote! {span => core } - }; - - tt.token_trees[0].clone() -} - -fn copy_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult<tt::Subtree> { - let krate = find_builtin_crate(db, id, span); +fn copy_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> { + let krate = dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::marker::Copy }, |_| quote! {span =>}) } -fn clone_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult<tt::Subtree> { - let krate = find_builtin_crate(db, id, span); +fn clone_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> { + let krate = dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::clone::Clone }, |adt| { if matches!(adt.shape, AdtShape::Union) { let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span }; @@ -482,14 +454,8 @@ fn and_and(span: Span) -> tt::Subtree { quote! {span => #and& } } -fn default_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult<tt::Subtree> { - let krate = &find_builtin_crate(db, id, span); +fn default_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::default::Default }, |adt| { let body = match &adt.shape { AdtShape::Struct(fields) => { @@ -527,14 +493,8 @@ fn default_expand( }) } -fn debug_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult<tt::Subtree> { - let krate = &find_builtin_crate(db, id, span); +fn debug_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::fmt::Debug }, |adt| { let for_variant = |name: String, v: &VariantShape| match v { VariantShape::Struct(fields) => { @@ -605,14 +565,8 @@ fn debug_expand( }) } -fn hash_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult<tt::Subtree> { - let krate = &find_builtin_crate(db, id, span); +fn hash_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::hash::Hash }, |adt| { if matches!(adt.shape, AdtShape::Union) { // FIXME: Return expand error here @@ -658,25 +612,13 @@ fn hash_expand( }) } -fn eq_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult<tt::Subtree> { - let krate = find_builtin_crate(db, id, span); +fn eq_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> { + let krate = dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>}) } -fn partial_eq_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult<tt::Subtree> { - let krate = find_builtin_crate(db, id, span); +fn partial_eq_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> { + let krate = dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialEq }, |adt| { if matches!(adt.shape, AdtShape::Union) { // FIXME: Return expand error here @@ -747,17 +689,11 @@ fn self_and_other_patterns( (self_patterns, other_patterns) } -fn ord_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult<tt::Subtree> { - let krate = &find_builtin_crate(db, id, span); +fn ord_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Ord }, |adt| { fn compare( - krate: &tt::TokenTree, + krate: &tt::Ident, left: tt::Subtree, right: tt::Subtree, rest: tt::Subtree, @@ -811,17 +747,11 @@ fn ord_expand( }) } -fn partial_ord_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult<tt::Subtree> { - let krate = &find_builtin_crate(db, id, span); +fn partial_ord_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult<tt::Subtree> { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialOrd }, |adt| { fn compare( - krate: &tt::TokenTree, + krate: &tt::Ident, left: tt::Subtree, right: tt::Subtree, rest: tt::Subtree, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index f99a8917623..c892f462d2c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -6,18 +6,16 @@ use either::Either; use itertools::Itertools; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; -use syntax::{ - ast::{self, AstToken}, - SmolStr, -}; +use syntax::ast::{self, AstToken}; use crate::{ db::ExpandDatabase, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, name::{self, known}, quote, + quote::dollar_crate, tt::{self, DelimSpan}, - ExpandError, ExpandResult, HirFileIdExt, MacroCallId, + ExpandError, ExpandResult, HirFileIdExt, MacroCallId, MacroFileIdExt, }; macro_rules! register_builtin { @@ -205,7 +203,7 @@ fn assert_expand( ) -> ExpandResult<tt::Subtree> { let call_site_span = span_with_call_site_ctxt(db, span, id); let args = parse_exprs_with_sep(tt, ',', call_site_span); - let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; + let dollar_crate = dollar_crate(span); let expanded = match &*args { [cond, panic_args @ ..] => { let comma = tt::Subtree { @@ -300,7 +298,7 @@ fn asm_expand( [tt::TokenTree::Leaf(tt::Leaf::Literal(lit))] | [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] => { - let dollar_krate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; + let dollar_krate = dollar_crate(span); literals.push(quote!(span=>#dollar_krate::format_args!(#lit);)); } _ => break, @@ -345,7 +343,7 @@ fn panic_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult<tt::Subtree> { - let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; + let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); let mac = @@ -371,7 +369,7 @@ fn unreachable_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult<tt::Subtree> { - let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; + let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); let mac = if use_panic_2021(db, call_site_span) { @@ -611,7 +609,7 @@ fn relative_file( path_str: &str, allow_recursion: bool, ) -> Result<FileId, ExpandError> { - let call_site = call_id.as_file().original_file(db); + let call_site = call_id.as_macro_file().parent(db).original_file_respecting_includes(db); let path = AnchoredPath { anchor: call_site, path: path_str }; let res = db .resolve_path(path) @@ -763,10 +761,10 @@ fn option_env_expand( return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) } }; - // FIXME: Use `DOLLAR_CRATE` when that works in eager macros. + let dollar_crate = dollar_crate(span); let expanded = match get_env_inner(db, arg_id, &key) { - None => quote! {span => ::core::option::Option::None::<&str> }, - Some(s) => quote! {span => ::core::option::Option::Some(#s) }, + None => quote! {span => #dollar_crate::option::Option::None::<&str> }, + Some(s) => quote! {span => #dollar_crate::option::Option::Some(#s) }, }; ExpandResult::ok(expanded) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 6a122e0859c..ae7d17e49a9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -318,6 +318,7 @@ pub trait MacroFileIdExt { fn expansion_level(self, db: &dyn ExpandDatabase) -> u32; /// If this is a macro call, returns the syntax node of the call. fn call_node(self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode>; + fn parent(self, db: &dyn ExpandDatabase) -> HirFileId; fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo; @@ -353,6 +354,9 @@ impl MacroFileIdExt for MacroFileId { }; } } + fn parent(self, db: &dyn ExpandDatabase) -> HirFileId { + self.macro_call_id.lookup(db).kind.file_id() + } /// Return expansion information if it is a macro-expansion file fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs index 9bdd75f9d22..a3b84afd2ae 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs @@ -4,6 +4,10 @@ use span::Span; use crate::name::Name; +pub(crate) fn dollar_crate(span: Span) -> tt::Ident<Span> { + tt::Ident { text: syntax::SmolStr::new_inline("$crate"), span } +} + // A helper macro quote macro // FIXME: // 1. Not all puncts are handled diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index c7807bcf9aa..803c18677da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -34,7 +34,9 @@ nohash-hasher.workspace = true typed-arena = "2.0.1" indexmap.workspace = true -rustc-dependencies.workspace = true +ra-ap-rustc_abi.workspace = true +ra-ap-rustc_index.workspace = true + # local deps stdx.workspace = true @@ -58,7 +60,7 @@ test-utils.workspace = true test-fixture.workspace = true [features] -in-rust-tree = ["rustc-dependencies/in-rust-tree"] +in-rust-tree = [] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index f4fbace19e3..e81d4ced554 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -167,7 +167,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> { } }); }) - .map(|block_id| self.db.trait_impls_in_block(block_id)); + .filter_map(|block_id| self.db.trait_impls_in_block(block_id)); let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); let mut result = vec![]; @@ -183,7 +183,8 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> { def_blocks .into_iter() .flatten() - .for_each(|it| f(&self.db.trait_impls_in_block(it))); + .filter_map(|it| self.db.trait_impls_in_block(it)) + .for_each(|it| f(&it)); } fps => { let mut f = @@ -198,7 +199,8 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> { def_blocks .into_iter() .flatten() - .for_each(|it| f(&self.db.trait_impls_in_block(it))); + .filter_map(|it| self.db.trait_impls_in_block(it)) + .for_each(|it| f(&it)); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 410bcbf0356..ad790fa094d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -34,6 +34,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[salsa::invoke(crate::infer::infer_query)] fn infer_query(&self, def: DefWithBodyId) -> Arc<InferenceResult>; + // region:mir + #[salsa::invoke(crate::mir::mir_body_query)] #[salsa::cycle(crate::mir::mir_body_recover)] fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>; @@ -61,20 +63,6 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[salsa::invoke(crate::mir::borrowck_query)] fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>; - #[salsa::invoke(crate::lower::ty_query)] - #[salsa::cycle(crate::lower::ty_recover)] - fn ty(&self, def: TyDefId) -> Binders<Ty>; - - #[salsa::invoke(crate::lower::value_ty_query)] - fn value_ty(&self, def: ValueTyDefId) -> Binders<Ty>; - - #[salsa::invoke(crate::lower::impl_self_ty_query)] - #[salsa::cycle(crate::lower::impl_self_ty_recover)] - fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>; - - #[salsa::invoke(crate::lower::const_param_ty_query)] - fn const_param_ty(&self, def: ConstParamId) -> Ty; - #[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::cycle(crate::consteval::const_eval_recover)] fn const_eval( @@ -92,6 +80,22 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] fn const_eval_discriminant(&self, def: EnumVariantId) -> Result<i128, ConstEvalError>; + // endregion:mir + + #[salsa::invoke(crate::lower::ty_query)] + #[salsa::cycle(crate::lower::ty_recover)] + fn ty(&self, def: TyDefId) -> Binders<Ty>; + + #[salsa::invoke(crate::lower::value_ty_query)] + fn value_ty(&self, def: ValueTyDefId) -> Binders<Ty>; + + #[salsa::invoke(crate::lower::impl_self_ty_query)] + #[salsa::cycle(crate::lower::impl_self_ty_recover)] + fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>; + + #[salsa::invoke(crate::lower::const_param_ty_query)] + fn const_param_ty(&self, def: ConstParamId) -> Ty; + #[salsa::invoke(crate::lower::impl_trait_query)] fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>; @@ -158,7 +162,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>; #[salsa::invoke(InherentImpls::inherent_impls_in_block_query)] - fn inherent_impls_in_block(&self, block: BlockId) -> Arc<InherentImpls>; + fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>; /// Collects all crates in the dependency graph that have impls for the /// given fingerprint. This is only used for primitive types and types @@ -175,7 +179,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; #[salsa::invoke(TraitImpls::trait_impls_in_block_query)] - fn trait_impls_in_block(&self, block: BlockId) -> Arc<TraitImpls>; + fn trait_impls_in_block(&self, block: BlockId) -> Option<Arc<TraitImpls>>; #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<[Arc<TraitImpls>]>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 23d95154221..d63a64a70de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1629,7 +1629,7 @@ pub fn write_visibility( ) -> Result<(), HirDisplayError> { match vis { Visibility::Public => write!(f, "pub "), - Visibility::Module(vis_id) => { + Visibility::Module(vis_id, _) => { let def_map = module_id.def_map(f.db.upcast()); let root_module_id = def_map.module_id(DefMap::ROOT); if vis_id == module_id { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 68619bb8b18..b7bfaf2931b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -12,10 +12,9 @@ use hir_def::{ LocalEnumVariantId, LocalFieldId, StructId, }; use la_arena::{Idx, RawIdx}; -use rustc_dependencies::{ - abi::AddressSpace, - index::{IndexSlice, IndexVec}, -}; +use rustc_abi::AddressSpace; +use rustc_index::{IndexSlice, IndexVec}; + use stdx::never; use triomphe::Arc; @@ -35,7 +34,7 @@ mod target; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub LocalEnumVariantId); -impl rustc_dependencies::index::Idx for RustcEnumVariantIdx { +impl rustc_index::Idx for RustcEnumVariantIdx { fn new(idx: usize) -> Self { RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32))) } @@ -54,7 +53,7 @@ impl RustcFieldIdx { } } -impl rustc_dependencies::index::Idx for RustcFieldIdx { +impl rustc_index::Idx for RustcFieldIdx { fn new(idx: usize) -> Self { RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32))) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 39788a95029..8a7715ce872 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -9,7 +9,7 @@ use hir_def::{ AdtId, EnumVariantId, LocalEnumVariantId, VariantId, }; use la_arena::RawIdx; -use rustc_dependencies::index::IndexVec; +use rustc_index::IndexVec; use smallvec::SmallVec; use triomphe::Arc; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index ef0be7ab2da..57214193cfb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -118,7 +118,7 @@ fn check_fail(ra_fixture: &str, e: LayoutError) { macro_rules! size_and_align { (minicore: $($x:tt),*;$($t:tt)*) => { { - #[allow(dead_code)] + #![allow(dead_code)] $($t)* check_size_and_align( stringify!($($t)*), @@ -130,7 +130,7 @@ macro_rules! size_and_align { }; ($($t:tt)*) => { { - #[allow(dead_code)] + #![allow(dead_code)] $($t)* check_size_and_align( stringify!($($t)*), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 793b52b49fa..e72864a12ee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -3,10 +3,17 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#[allow(unused)] -macro_rules! eprintln { - ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; -} +#[cfg(feature = "in-rust-tree")] +extern crate rustc_index; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_index as rustc_index; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_abi; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_abi as rustc_abi; mod builder; mod chalk_db; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 97c4a741ff2..e371e427615 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1601,7 +1601,7 @@ fn implicitly_sized_clauses<'a>( pub(crate) fn generic_defaults_query( db: &dyn HirDatabase, def: GenericDefId, -) -> Arc<[Binders<chalk_ir::GenericArg<Interner>>]> { +) -> Arc<[Binders<crate::GenericArg>]> { let resolver = def.resolver(db.upcast()); let ctx = TyLoweringContext::new(db, &resolver, def.into()) .with_type_param_mode(ParamLoweringMode::Variable); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 33619edfee9..03ed8d36a1d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -8,10 +8,9 @@ use base_db::{CrateId, Edition}; use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use hir_def::{ data::{adt::StructFlags, ImplData}, - item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, - ModuleDefId, ModuleId, TraitId, + ModuleId, TraitId, }; use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; @@ -132,34 +131,40 @@ pub(crate) const ALL_FLOAT_FPS: [TyFingerprint; 2] = [ TyFingerprint::Scalar(Scalar::Float(FloatTy::F64)), ]; +type TraitFpMap = FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Box<[ImplId]>>>; +type TraitFpMapCollector = FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>; + /// Trait impls defined or available in some crate. #[derive(Debug, Eq, PartialEq)] pub struct TraitImpls { // If the `Option<TyFingerprint>` is `None`, the impl may apply to any self type. - map: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>, + map: TraitFpMap, } impl TraitImpls { pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { let _p = profile::span("trait_impls_in_crate_query").detail(|| format!("{krate:?}")); - let mut impls = Self { map: FxHashMap::default() }; + let mut impls = FxHashMap::default(); - let crate_def_map = db.crate_def_map(krate); - impls.collect_def_map(db, &crate_def_map); - impls.shrink_to_fit(); + Self::collect_def_map(db, &mut impls, &db.crate_def_map(krate)); - Arc::new(impls) + Arc::new(Self::finish(impls)) } - pub(crate) fn trait_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc<Self> { + pub(crate) fn trait_impls_in_block_query( + db: &dyn HirDatabase, + block: BlockId, + ) -> Option<Arc<Self>> { let _p = profile::span("trait_impls_in_block_query"); - let mut impls = Self { map: FxHashMap::default() }; + let mut impls = FxHashMap::default(); - let block_def_map = db.block_def_map(block); - impls.collect_def_map(db, &block_def_map); - impls.shrink_to_fit(); + Self::collect_def_map(db, &mut impls, &db.block_def_map(block)); - Arc::new(impls) + if impls.is_empty() { + None + } else { + Some(Arc::new(Self::finish(impls))) + } } pub(crate) fn trait_impls_in_deps_query( @@ -174,15 +179,16 @@ impl TraitImpls { ) } - fn shrink_to_fit(&mut self) { - self.map.shrink_to_fit(); - self.map.values_mut().for_each(|map| { - map.shrink_to_fit(); - map.values_mut().for_each(Vec::shrink_to_fit); - }); + fn finish(map: TraitFpMapCollector) -> TraitImpls { + TraitImpls { + map: map + .into_iter() + .map(|(k, v)| (k, v.into_iter().map(|(k, v)| (k, v.into_boxed_slice())).collect())) + .collect(), + } } - fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) { + fn collect_def_map(db: &dyn HirDatabase, map: &mut TraitFpMapCollector, def_map: &DefMap) { for (_module_id, module_data) in def_map.modules() { for impl_id in module_data.scope.impls() { // Reservation impls should be ignored during trait resolution, so we never need @@ -200,20 +206,15 @@ impl TraitImpls { }; let self_ty = db.impl_self_ty(impl_id); let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders()); - self.map - .entry(target_trait) - .or_default() - .entry(self_ty_fp) - .or_default() - .push(impl_id); + map.entry(target_trait).or_default().entry(self_ty_fp).or_default().push(impl_id); } // To better support custom derives, collect impls in all unnamed const items. // const _: () = { ... }; - for konst in collect_unnamed_consts(db, &module_data.scope) { + for konst in module_data.scope.unnamed_consts(db.upcast()) { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db.upcast()) { - self.collect_def_map(db, &block_def_map); + Self::collect_def_map(db, map, &block_def_map); } } } @@ -281,7 +282,10 @@ impl InherentImpls { Arc::new(impls) } - pub(crate) fn inherent_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc<Self> { + pub(crate) fn inherent_impls_in_block_query( + db: &dyn HirDatabase, + block: BlockId, + ) -> Option<Arc<Self>> { let _p = profile::span("inherent_impls_in_block_query"); let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; @@ -289,7 +293,11 @@ impl InherentImpls { impls.collect_def_map(db, &block_def_map); impls.shrink_to_fit(); - Arc::new(impls) + if impls.map.is_empty() && impls.invalid_impls.is_empty() { + None + } else { + Some(Arc::new(impls)) + } } fn shrink_to_fit(&mut self) { @@ -321,7 +329,7 @@ impl InherentImpls { // To better support custom derives, collect impls in all unnamed const items. // const _: () = { ... }; - for konst in collect_unnamed_consts(db, &module_data.scope) { + for konst in module_data.scope.unnamed_consts(db.upcast()) { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db.upcast()) { self.collect_def_map(db, &block_def_map); @@ -367,34 +375,6 @@ pub(crate) fn incoherent_inherent_impl_crates( res } -fn collect_unnamed_consts<'a>( - db: &'a dyn HirDatabase, - scope: &'a ItemScope, -) -> impl Iterator<Item = ConstId> + 'a { - let unnamed_consts = scope.unnamed_consts(); - - // FIXME: Also treat consts named `_DERIVE_*` as unnamed, since synstructure generates those. - // Should be removed once synstructure stops doing that. - let synstructure_hack_consts = scope.values().filter_map(|(item, _)| match item { - ModuleDefId::ConstId(id) => { - let loc = id.lookup(db.upcast()); - let item_tree = loc.id.item_tree(db.upcast()); - if item_tree[loc.id.value] - .name - .as_ref() - .map_or(false, |n| n.to_smol_str().starts_with("_DERIVE_")) - { - Some(id) - } else { - None - } - } - _ => None, - }); - - unnamed_consts.chain(synstructure_hack_consts) -} - pub fn def_crates( db: &dyn HirDatabase, ty: &Ty, @@ -737,7 +717,7 @@ fn lookup_impl_assoc_item_for_trait_ref( let impls = db.trait_impls_in_deps(env.krate); let self_impls = match self_ty.kind(Interner) { TyKind::Adt(id, _) => { - id.0.module(db.upcast()).containing_block().map(|it| db.trait_impls_in_block(it)) + id.0.module(db.upcast()).containing_block().and_then(|it| db.trait_impls_in_block(it)) } _ => None, }; @@ -1254,17 +1234,18 @@ fn iterate_inherent_methods( }; while let Some(block_id) = block { - let impls = db.inherent_impls_in_block(block_id); - impls_for_self_ty( - &impls, - self_ty, - table, - name, - receiver_ty, - receiver_adjustments.clone(), - module, - callback, - )?; + if let Some(impls) = db.inherent_impls_in_block(block_id) { + impls_for_self_ty( + &impls, + self_ty, + table, + name, + receiver_ty, + receiver_adjustments.clone(), + module, + callback, + )?; + } block = db.block_def_map(block_id).parent().and_then(|module| module.containing_block()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index 6f4aef22d2f..d0a1fb1d576 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -9,7 +9,6 @@ use base_db::{ use hir_def::{db::DefDatabase, ModuleId}; use hir_expand::db::ExpandDatabase; use nohash_hasher::IntMap; -use rustc_hash::FxHashSet; use syntax::TextRange; use test_utils::extract_annotations; use triomphe::Arc; @@ -81,7 +80,7 @@ impl FileLoader for TestDB { fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> { FileLoaderDelegate(self).relevant_crates(file_id) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index d16e0eb0137..622b4f56d4f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -987,15 +987,12 @@ fn infer_builtin_macros_env() { fn infer_builtin_macros_option_env() { check_types( r#" - //- minicore: option - //- /main.rs env:foo=bar - #[rustc_builtin_macro] - macro_rules! option_env {() => {}} - - fn main() { - let x = option_env!("foo"); - //^ Option<&str> - } +//- minicore: env +//- /main.rs env:foo=bar +fn main() { + let x = option_env!("foo"); + //^ Option<&str> +} "#, ); } @@ -1015,6 +1012,21 @@ fn test() { } #[test] +fn infer_builtin_derive_resolves_with_core_module() { + check_types( + r#" +//- minicore: derive, clone +mod core {} +#[derive(Clone)] +struct S; +fn test() { + S.clone(); +} //^^^^^^^^^ S +"#, + ); +} + +#[test] fn infer_derive_clone_with_params() { check_types( r#" diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 60ddc4aa86f..5a21f41dca8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -35,7 +35,7 @@ macro_rules! impl_has_attrs { impl HasAttrs for $def { fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { let def = AttrDefId::$def_id(self.into()); - db.attrs_with_owner(def) + AttrsWithOwner::attrs_with_owner(db.upcast(), def) } fn attr_id(self) -> AttrDefId { AttrDefId::$def_id(self.into()) diff --git a/src/tools/rust-analyzer/crates/hir/src/db.rs b/src/tools/rust-analyzer/crates/hir/src/db.rs index 7204868464b..403a6c88ab0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir/src/db.rs @@ -15,8 +15,8 @@ pub use hir_def::db::{ InternExternBlockQuery, InternExternCrateQuery, InternFunctionQuery, InternImplQuery, InternInTypeConstQuery, InternMacro2Query, InternMacroRulesQuery, InternProcMacroQuery, InternStaticQuery, InternStructQuery, InternTraitAliasQuery, InternTraitQuery, - InternTypeAliasQuery, InternUnionQuery, InternUseQuery, LangAttrQuery, LangItemQuery, - Macro2DataQuery, MacroRulesDataQuery, ProcMacroDataQuery, StaticDataQuery, StructDataQuery, + InternTypeAliasQuery, InternUnionQuery, InternUseQuery, LangItemQuery, Macro2DataQuery, + MacroRulesDataQuery, ProcMacroDataQuery, StaticDataQuery, StructDataQuery, StructDataWithDiagnosticsQuery, TraitAliasDataQuery, TraitDataQuery, TraitDataWithDiagnosticsQuery, TypeAliasDataQuery, UnionDataQuery, UnionDataWithDiagnosticsQuery, VariantsAttrsQuery, VariantsAttrsSourceMapQuery, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 0266915c39b..3180a2b713a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -754,7 +754,7 @@ impl Module { scope .declarations() .map(ModuleDef::from) - .chain(scope.unnamed_consts().map(|id| ModuleDef::Const(Const::from(id)))) + .chain(scope.unnamed_consts(db.upcast()).map(|id| ModuleDef::Const(Const::from(id)))) .collect() } diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 841ddfb9c43..e1101dd8236 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -165,6 +165,7 @@ impl<'a> SymbolCollector<'a> { // Record renamed imports. // FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily // for now. + // FIXME: This parses! for id in scope.imports() { let source = id.import.child_source(self.db.upcast()); let Some(use_tree_src) = source.value.get(id.idx) else { continue }; @@ -195,7 +196,7 @@ impl<'a> SymbolCollector<'a> { }); } - for const_id in scope.unnamed_consts() { + for const_id in scope.unnamed_consts(self.db.upcast()) { self.collect_from_body(const_id); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index 7b71d9b8696..a64591c9ca0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -1551,4 +1551,39 @@ use foo::Foo$0; ", ); } + + #[test] + fn considers_pub_crate() { + check_assist( + auto_import, + r#" +mod foo { + pub struct Foo; +} + +pub(crate) use self::foo::*; + +mod bar { + fn main() { + Foo$0; + } +} +"#, + r#" +mod foo { + pub struct Foo; +} + +pub(crate) use self::foo::*; + +mod bar { + use crate::Foo; + + fn main() { + Foo; + } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 347a3e9ba07..1eb28626f75 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -25,7 +25,7 @@ use syntax::{ edit::{AstNodeEdit, IndentLevel}, AstNode, HasGenericParams, }, - match_ast, ted, SyntaxElement, + match_ast, ted, AstToken, SyntaxElement, SyntaxKind::{self, COMMENT}, SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, }; @@ -1733,8 +1733,23 @@ fn make_body( ast::Expr::BlockExpr(block) => { // If the extracted expression is itself a block, there is no need to wrap it inside another block. let block = block.dedent(old_indent); - // Recreate the block for formatting consistency with other extracted functions. - make::block_expr(block.statements(), block.tail_expr()) + let elements = block.stmt_list().map_or_else( + || Either::Left(iter::empty()), + |stmt_list| { + let elements = stmt_list.syntax().children_with_tokens().filter_map( + |node_or_token| match &node_or_token { + syntax::NodeOrToken::Node(node) => { + ast::Stmt::cast(node.clone()).map(|_| node_or_token) + } + syntax::NodeOrToken::Token(token) => { + ast::Comment::cast(token.clone()).map(|_| node_or_token) + } + }, + ); + Either::Right(elements) + }, + ); + make::hacky_block_expr(elements, block.tail_expr()) } _ => { let expr = expr.dedent(old_indent).indent(IndentLevel(1)); @@ -5962,6 +5977,37 @@ fn $0fun_name() -> ControlFlow<()> { } #[test] + fn comments_in_block_expr() { + check_assist( + extract_function, + r#" +fn f() { + let c = $0{ + // comment 1 + let a = 2 + 3; + // comment 2 + let b = 5; + a + b + }$0; +} +"#, + r#" +fn f() { + let c = fun_name(); +} + +fn $0fun_name() -> i32 { + // comment 1 + let a = 2 + 3; + // comment 2 + let b = 5; + a + b +} +"#, + ); + } + + #[test] fn in_left_curly_is_not_applicable() { cov_mark::check!(extract_function_in_braces_is_not_applicable); check_assist_not_applicable(extract_function, r"fn foo() { $0}$0"); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs index a4e8e7388f6..8b8c6ceee99 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs @@ -50,6 +50,10 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O ty.original().display_source_code(ctx.db(), constant_module.into(), false).ok()?; let target = statement.syntax().parent()?.text_range(); let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?; + if path.parent_path().is_some() { + cov_mark::hit!(not_last_path_segment); + return None; + } let name_refs = path.segments().map(|s| s.name_ref()); let mut outer_exists = false; @@ -253,4 +257,16 @@ fn bar() -> i32 { }"#, ); } + + #[test] + fn test_wont_apply_when_not_last_path_segment() { + cov_mark::check!(not_last_path_segment); + check_assist_not_applicable( + generate_constant, + r#"mod foo {} +fn bar() -> i32 { + foo::A_CON$0STANT::invalid_segment +}"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index dc02aaf9af6..1f5c24f8ea4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -107,31 +107,48 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' |edit| { // Create the function let method_source = match ctx.sema.source(method) { - Some(source) => source.value, + Some(source) => { + let v = source.value.clone_for_update(); + let source_scope = ctx.sema.scope(v.syntax()); + let target_scope = ctx.sema.scope(strukt.syntax()); + if let (Some(s), Some(t)) = (source_scope, target_scope) { + PathTransform::generic_transformation(&t, &s).apply(v.syntax()); + } + v + } None => return, }; + let vis = method_source.visibility(); + let is_async = method_source.async_token().is_some(); + let is_const = method_source.const_token().is_some(); + let is_unsafe = method_source.unsafe_token().is_some(); + let fn_name = make::name(&name); + + let type_params = method_source.generic_param_list(); + let where_clause = method_source.where_clause(); let params = method_source.param_list().unwrap_or_else(|| make::param_list(None, [])); - let type_params = method_source.generic_param_list(); - let arg_list = match method_source.param_list() { - Some(list) => convert_param_list_to_arg_list(list), - None => make::arg_list([]), - }; + + // compute the `body` + let arg_list = method_source + .param_list() + .map(|list| convert_param_list_to_arg_list(list)) + .unwrap_or_else(|| make::arg_list([])); + let tail_expr = make::expr_method_call(field, make::name_ref(&name), arg_list); - let ret_type = method_source.ret_type(); - let is_async = method_source.async_token().is_some(); - let is_const = method_source.const_token().is_some(); - let is_unsafe = method_source.unsafe_token().is_some(); let tail_expr_finished = if is_async { make::expr_await(tail_expr) } else { tail_expr }; let body = make::block_expr([], Some(tail_expr_finished)); + + let ret_type = method_source.ret_type(); + let f = make::fn_( vis, fn_name, type_params, - method_source.where_clause(), + where_clause, params, body, ret_type, @@ -184,12 +201,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let assoc_items = impl_def.get_or_create_assoc_item_list(); assoc_items.add_item(f.clone().into()); - if let Some((target, source)) = - ctx.sema.scope(strukt.syntax()).zip(ctx.sema.scope(method_source.syntax())) - { - PathTransform::generic_transformation(&target, &source).apply(f.syntax()); - } - if let Some(cap) = ctx.config.snippet_cap { edit.add_tabstop_before(cap, f) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs new file mode 100644 index 00000000000..2f3136f027b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs @@ -0,0 +1,246 @@ +use ide_db::syntax_helpers::node_ext::is_pattern_cond; +use syntax::{ + ast::{self, AstNode, BinaryOp}, + T, +}; + +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, AssistKind, +}; +// Assist: merge_nested_if +// +// This transforms if expressions of the form `if x { if y {A} }` into `if x && y {A}` +// This assist can only be applied with the cursor on `if`. +// +// ``` +// fn main() { +// i$0f x == 3 { if y == 4 { 1 } } +// } +// ``` +// -> +// ``` +// fn main() { +// if x == 3 && y == 4 { 1 } +// } +// ``` +pub(crate) fn merge_nested_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let if_keyword = ctx.find_token_syntax_at_offset(T![if])?; + let expr = ast::IfExpr::cast(if_keyword.parent()?)?; + let if_range = if_keyword.text_range(); + let cursor_in_range = if_range.contains_range(ctx.selection_trimmed()); + if !cursor_in_range { + return None; + } + + //should not apply to if with else branch. + if expr.else_branch().is_some() { + return None; + } + + let cond = expr.condition()?; + //should not apply for if-let + if is_pattern_cond(cond.clone()) { + return None; + } + + let cond_range = cond.syntax().text_range(); + + //check if the then branch is a nested if + let then_branch = expr.then_branch()?; + let stmt = then_branch.stmt_list()?; + if stmt.statements().count() != 0 { + return None; + } + + let nested_if_to_merge = then_branch.tail_expr().and_then(|e| match e { + ast::Expr::IfExpr(e) => Some(e), + _ => None, + })?; + // should not apply to nested if with else branch. + if nested_if_to_merge.else_branch().is_some() { + return None; + } + let nested_if_cond = nested_if_to_merge.condition()?; + if is_pattern_cond(nested_if_cond.clone()) { + return None; + } + + let nested_if_then_branch = nested_if_to_merge.then_branch()?; + let then_branch_range = then_branch.syntax().text_range(); + + acc.add( + AssistId("merge_nested_if", AssistKind::RefactorRewrite), + "Merge nested if", + if_range, + |edit| { + let cond_text = if has_logic_op_or(&cond) { + format!("({})", cond.syntax().text()) + } else { + cond.syntax().text().to_string() + }; + + let nested_if_cond_text = if has_logic_op_or(&nested_if_cond) { + format!("({})", nested_if_cond.syntax().text()) + } else { + nested_if_cond.syntax().text().to_string() + }; + + let replace_cond = format!("{} && {}", cond_text, nested_if_cond_text); + + edit.replace(cond_range, replace_cond); + edit.replace(then_branch_range, nested_if_then_branch.syntax().text()); + }, + ) +} + +/// Returns whether the given if condition has logical operators. +fn has_logic_op_or(expr: &ast::Expr) -> bool { + match expr { + ast::Expr::BinExpr(bin_expr) => { + if let Some(kind) = bin_expr.op_kind() { + matches!(kind, BinaryOp::LogicOp(ast::LogicOp::Or)) + } else { + false + } + } + _ => false, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn merge_nested_if_test1() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 { if y == 4 { 1 } } }", + "fn f() { if x == 3 && y == 4 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test2() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 || y == 1 { if z == 4 { 1 } } }", + "fn f() { if (x == 3 || y == 1) && z == 4 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test3() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 && y == 1 { if z == 4 { 1 } } }", + "fn f() { if x == 3 && y == 1 && z == 4 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test4() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 && y == 1 { if z == 4 && q == 3 { 1 } } }", + "fn f() { if x == 3 && y == 1 && z == 4 && q == 3 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test5() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 && y == 1 { if z == 4 || q == 3 { 1 } } }", + "fn f() { if x == 3 && y == 1 && (z == 4 || q == 3) { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test6() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 || y == 1 { if z == 4 || q == 3 { 1 } } }", + "fn f() { if (x == 3 || y == 1) && (z == 4 || q == 3) { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test7() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 || y == 1 { if z == 4 && q == 3 { 1 } } }", + "fn f() { if (x == 3 || y == 1) && z == 4 && q == 3 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_if_with_else_branch() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 3 { if y == 4 { 1 } } else { 2 } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_nested_if_with_else_branch() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 3 { if y == 4 { 1 } else { 2 } } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_if_let() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f let Some(x) = y { if x == 4 { 1 } } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_nested_if_let() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f y == 0 { if let Some(x) = y { 1 } } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_if_with_else_branch_and_nested_if() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 3 { if y == 4 { 1 } } else { if z == 5 { 2 } } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_with_cursor_not_on_if() { + check_assist_not_applicable(merge_nested_if, "fn f() { if $0x==0 { if y == 3 { 1 } } }") + } + + #[test] + fn merge_nested_if_do_not_apply_with_mulpiple_if() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 0 { if y == 3 { 1 } else if y == 4 { 2 } } }", + ) + } + #[test] + fn merge_nested_if_do_not_apply_with_not_only_has_nested_if() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 0 { if y == 3 { foo(); } foo(); } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_with_multiply_nested_if() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 0 { if y == 3 { foo(); } if z == 3 { 2 } } }", + ) + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 1e4d1c94f5b..1eb4903ab20 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -217,6 +217,7 @@ mod handlers { mod unqualify_method_call; mod wrap_return_type_in_result; mod into_to_qualified_from; + mod merge_nested_if; pub(crate) fn all() -> &'static [Handler] { &[ @@ -291,6 +292,7 @@ mod handlers { invert_if::invert_if, merge_imports::merge_imports, merge_match_arms::merge_match_arms, + merge_nested_if::merge_nested_if, move_bounds::move_bounds_to_where_clause, move_const_to_impl::move_const_to_impl, move_guard::move_arm_cond_to_match_guard, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 0c2331796f9..0ce89ae0a9a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -2052,6 +2052,23 @@ fn handle(action: Action) { } #[test] +fn doctest_merge_nested_if() { + check_doc_test( + "merge_nested_if", + r#####" +fn main() { + i$0f x == 3 { if y == 4 { 1 } } +} +"#####, + r#####" +fn main() { + if x == 3 && y == 4 { 1 } +} +"#####, + ) +} + +#[test] fn doctest_move_arm_cond_to_match_guard() { check_doc_test( "move_arm_cond_to_match_guard", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 108b040de6b..92aa1da89c4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -529,6 +529,11 @@ impl CompletionContext<'_> { } } + /// Whether the given trait has `#[doc(notable_trait)]` + pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool { + trait_.attrs(self.db).has_doc_notable_trait() + } + /// Returns the traits in scope, with the [`Drop`] trait removed. pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits { let mut traits_in_scope = self.scope.visible_traits(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index de41a5bd70c..affd9b72964 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -152,6 +152,8 @@ pub struct CompletionRelevance { pub is_local: bool, /// This is set when trait items are completed in an impl of that trait. pub is_item_from_trait: bool, + /// This is set for when trait items are from traits with `#[doc(notable_trait)]` + pub is_item_from_notable_trait: bool, /// This is set when an import is suggested whose name is already imported. pub is_name_already_imported: bool, /// This is set for completions that will insert a `use` item. @@ -228,6 +230,7 @@ impl CompletionRelevance { is_private_editable, postfix_match, is_definite, + is_item_from_notable_trait, } = self; // lower rank private things @@ -266,6 +269,9 @@ impl CompletionRelevance { if is_item_from_trait { score += 1; } + if is_item_from_notable_trait { + score += 1; + } if is_definite { score += 10; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 581d557e831..8c0e6694761 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -1170,6 +1170,7 @@ fn main() { let _: m::Spam = S$0 } ), is_local: false, is_item_from_trait: false, + is_item_from_notable_trait: false, is_name_already_imported: false, requires_import: false, is_op_method: false, @@ -1196,6 +1197,7 @@ fn main() { let _: m::Spam = S$0 } ), is_local: false, is_item_from_trait: false, + is_item_from_notable_trait: false, is_name_already_imported: false, requires_import: false, is_op_method: false, @@ -1274,6 +1276,7 @@ fn foo() { A { the$0 } } ), is_local: false, is_item_from_trait: false, + is_item_from_notable_trait: false, is_name_already_imported: false, requires_import: false, is_op_method: false, @@ -2089,6 +2092,7 @@ fn foo() { ), is_local: false, is_item_from_trait: false, + is_item_from_notable_trait: false, is_name_already_imported: false, requires_import: false, is_op_method: false, @@ -2439,4 +2443,81 @@ impl S { "#, ) } + + #[test] + fn notable_traits_method_relevance() { + check_kinds( + r#" +#[doc(notable_trait)] +trait Write { + fn write(&self); + fn flush(&self); +} + +struct Writer; + +impl Write for Writer { + fn write(&self) {} + fn flush(&self) {} +} + +fn main() { + Writer.$0 +} +"#, + &[ + CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Field), + CompletionItemKind::SymbolKind(SymbolKind::Function), + ], + expect![[r#" + [ + CompletionItem { + label: "flush()", + source_range: 193..193, + delete: 193..193, + insert: "flush()$0", + kind: Method, + lookup: "flush", + detail: "fn(&self)", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + is_item_from_trait: false, + is_item_from_notable_trait: true, + is_name_already_imported: false, + requires_import: false, + is_op_method: false, + is_private_editable: false, + postfix_match: None, + is_definite: false, + }, + }, + CompletionItem { + label: "write()", + source_range: 193..193, + delete: 193..193, + insert: "write()$0", + kind: Method, + lookup: "write", + detail: "fn(&self)", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + is_item_from_trait: false, + is_item_from_notable_trait: true, + is_name_already_imported: false, + requires_import: false, + is_op_method: false, + is_private_editable: false, + postfix_match: None, + is_definite: false, + }, + }, + ] + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index b306bede653..6ad84eba33b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -74,10 +74,13 @@ fn render( ); let ret_type = func.ret_type(db); - let is_op_method = func - .as_assoc_item(ctx.db()) - .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db())) - .map_or(false, |trait_| completion.is_ops_trait(trait_)); + let assoc_item = func.as_assoc_item(db); + + let trait_ = assoc_item.and_then(|trait_| trait_.containing_trait_or_trait_impl(db)); + let is_op_method = trait_.map_or(false, |trait_| completion.is_ops_trait(trait_)); + + let is_item_from_notable_trait = + trait_.map_or(false, |trait_| completion.is_doc_notable_trait(trait_)); let (has_dot_receiver, has_call_parens, cap) = match func_kind { FuncKind::Function(&PathCompletionCtx { @@ -105,6 +108,7 @@ fn render( }, exact_name_match: compute_exact_name_match(completion, &call), is_op_method, + is_item_from_notable_trait, ..ctx.completion_relevance() }); @@ -141,7 +145,7 @@ fn render( item.add_import(import_to_add); } None => { - if let Some(actm) = func.as_assoc_item(db) { + if let Some(actm) = assoc_item { if let Some(trt) = actm.containing_trait_or_trait_impl(db) { item.trait_name(trt.name(db).to_smol_str()); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index db6cd128e83..259d141404d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -84,26 +84,53 @@ impl RootDatabase { )*} } purge_each_query![ - // SourceDatabase - base_db::ParseQuery - base_db::CrateGraphQuery - - // SourceDatabaseExt - base_db::FileTextQuery - base_db::FileSourceRootQuery - base_db::SourceRootQuery - base_db::SourceRootCratesQuery - - // ExpandDatabase - hir::db::AstIdMapQuery - hir::db::DeclMacroExpanderQuery - hir::db::ExpandProcMacroQuery - hir::db::InternMacroCallQuery - hir::db::InternSyntaxContextQuery - hir::db::MacroArgQuery - hir::db::ParseMacroExpansionQuery - hir::db::RealSpanMapQuery - hir::db::ProcMacrosQuery + // SymbolsDatabase + crate::symbol_index::ModuleSymbolsQuery + crate::symbol_index::LibrarySymbolsQuery + crate::symbol_index::LocalRootsQuery + crate::symbol_index::LibraryRootsQuery + // HirDatabase + hir::db::InferQueryQuery + hir::db::MirBodyQuery + hir::db::BorrowckQuery + hir::db::TyQuery + hir::db::ValueTyQuery + hir::db::ImplSelfTyQuery + hir::db::ConstParamTyQuery + hir::db::ConstEvalQuery + hir::db::ConstEvalDiscriminantQuery + hir::db::ImplTraitQuery + hir::db::FieldTypesQuery + hir::db::LayoutOfAdtQuery + hir::db::TargetDataLayoutQuery + hir::db::CallableItemSignatureQuery + hir::db::ReturnTypeImplTraitsQuery + hir::db::GenericPredicatesForParamQuery + hir::db::GenericPredicatesQuery + hir::db::TraitEnvironmentQuery + hir::db::GenericDefaultsQuery + hir::db::InherentImplsInCrateQuery + hir::db::InherentImplsInBlockQuery + hir::db::IncoherentInherentImplCratesQuery + hir::db::TraitImplsInCrateQuery + hir::db::TraitImplsInBlockQuery + hir::db::TraitImplsInDepsQuery + hir::db::InternCallableDefQuery + hir::db::InternLifetimeParamIdQuery + hir::db::InternImplTraitIdQuery + hir::db::InternTypeOrConstParamIdQuery + hir::db::InternClosureQuery + hir::db::InternGeneratorQuery + hir::db::AssociatedTyDataQuery + hir::db::TraitDatumQuery + hir::db::StructDatumQuery + hir::db::ImplDatumQuery + hir::db::FnDefDatumQuery + hir::db::FnDefVarianceQuery + hir::db::AdtVarianceQuery + hir::db::AssociatedTyValueQuery + hir::db::TraitSolveQueryQuery + hir::db::ProgramClausesForChalkEnvQuery // DefDatabase hir::db::FileItemTreeQuery @@ -145,64 +172,11 @@ impl RootDatabase { hir::db::CrateSupportsNoStdQuery hir::db::BlockItemTreeQueryQuery hir::db::ExternCrateDeclDataQuery - hir::db::LangAttrQuery hir::db::InternAnonymousConstQuery hir::db::InternExternCrateQuery hir::db::InternInTypeConstQuery hir::db::InternUseQuery - // HirDatabase - hir::db::InferQueryQuery - hir::db::MirBodyQuery - hir::db::BorrowckQuery - hir::db::TyQuery - hir::db::ValueTyQuery - hir::db::ImplSelfTyQuery - hir::db::ConstParamTyQuery - hir::db::ConstEvalQuery - hir::db::ConstEvalDiscriminantQuery - hir::db::ImplTraitQuery - hir::db::FieldTypesQuery - hir::db::LayoutOfAdtQuery - hir::db::TargetDataLayoutQuery - hir::db::CallableItemSignatureQuery - hir::db::ReturnTypeImplTraitsQuery - hir::db::GenericPredicatesForParamQuery - hir::db::GenericPredicatesQuery - hir::db::TraitEnvironmentQuery - hir::db::GenericDefaultsQuery - hir::db::InherentImplsInCrateQuery - hir::db::InherentImplsInBlockQuery - hir::db::IncoherentInherentImplCratesQuery - hir::db::TraitImplsInCrateQuery - hir::db::TraitImplsInBlockQuery - hir::db::TraitImplsInDepsQuery - hir::db::InternCallableDefQuery - hir::db::InternLifetimeParamIdQuery - hir::db::InternImplTraitIdQuery - hir::db::InternTypeOrConstParamIdQuery - hir::db::InternClosureQuery - hir::db::InternGeneratorQuery - hir::db::AssociatedTyDataQuery - hir::db::TraitDatumQuery - hir::db::StructDatumQuery - hir::db::ImplDatumQuery - hir::db::FnDefDatumQuery - hir::db::FnDefVarianceQuery - hir::db::AdtVarianceQuery - hir::db::AssociatedTyValueQuery - hir::db::TraitSolveQueryQuery - hir::db::ProgramClausesForChalkEnvQuery - - // SymbolsDatabase - crate::symbol_index::ModuleSymbolsQuery - crate::symbol_index::LibrarySymbolsQuery - crate::symbol_index::LocalRootsQuery - crate::symbol_index::LibraryRootsQuery - - // LineIndexDatabase - crate::LineIndexQuery - // InternDatabase hir::db::InternFunctionQuery hir::db::InternStructQuery @@ -219,6 +193,30 @@ impl RootDatabase { hir::db::InternMacro2Query hir::db::InternProcMacroQuery hir::db::InternMacroRulesQuery + + // ExpandDatabase + hir::db::AstIdMapQuery + hir::db::DeclMacroExpanderQuery + hir::db::ExpandProcMacroQuery + hir::db::InternMacroCallQuery + hir::db::InternSyntaxContextQuery + hir::db::MacroArgQuery + hir::db::ParseMacroExpansionQuery + hir::db::RealSpanMapQuery + hir::db::ProcMacrosQuery + + // LineIndexDatabase + crate::LineIndexQuery + + // SourceDatabase + base_db::ParseQuery + base_db::CrateGraphQuery + + // SourceDatabaseExt + base_db::FileTextQuery + base_db::FileSourceRootQuery + base_db::SourceRootQuery + base_db::SourceRootCratesQuery ]; acc.sort_by_key(|it| std::cmp::Reverse(it.1)); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 652968d808f..b834f517d49 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -681,11 +681,10 @@ fn path_import_candidate( Some(qualifier) => match sema.resolve_path(&qualifier) { None => { if qualifier.first_qualifier().map_or(true, |it| sema.resolve_path(&it).is_none()) { - let mut qualifier = qualifier - .segments_of_this_path_only_rev() + let qualifier = qualifier + .segments() .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text()))) .collect::<Option<Vec<_>>>()?; - qualifier.reverse(); ImportCandidate::Path(PathImportCandidate { qualifier: Some(qualifier), name }) } else { return None; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 128971994f6..eae23e95482 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -124,7 +124,7 @@ impl FileLoader for RootDatabase { fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> { FileLoaderDelegate(self).relevant_crates(file_id) } } @@ -145,7 +145,7 @@ impl RootDatabase { db.set_local_roots_with_durability(Default::default(), Durability::HIGH); db.set_library_roots_with_durability(Default::default(), Durability::HIGH); db.set_expand_proc_attr_macros_with_durability(false, Durability::HIGH); - db.update_parse_query_lru_capacity(lru_capacity); + db.update_base_query_lru_capacities(lru_capacity); db.setup_syntax_context_root(); db } @@ -154,11 +154,12 @@ impl RootDatabase { self.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); } - pub fn update_parse_query_lru_capacity(&mut self, lru_capacity: Option<usize>) { + pub fn update_base_query_lru_capacities(&mut self, lru_capacity: Option<usize>) { let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP); base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); // macro expansions are usually rather small, so we can afford to keep more of them alive hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity); + hir::db::BorrowckQuery.in_db_mut(self).set_lru_capacity(base_db::DEFAULT_BORROWCK_LRU_CAP); } pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) { @@ -176,6 +177,12 @@ impl RootDatabase { .copied() .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP), ); + hir_db::BorrowckQuery.in_db_mut(self).set_lru_capacity( + lru_capacities + .get(stringify!(BorrowckQuery)) + .copied() + .unwrap_or(base_db::DEFAULT_BORROWCK_LRU_CAP), + ); macro_rules! update_lru_capacity_per_query { ($( $module:ident :: $query:ident )*) => {$( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index edfeddc1bc9..47bcaae259b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -3,6 +3,7 @@ use crate::helpers::mod_path_to_ast; use either::Either; use hir::{AsAssocItem, HirDisplay, ModuleDef, SemanticsScope}; +use itertools::Itertools; use rustc_hash::FxHashMap; use syntax::{ ast::{self, make, AstNode}, @@ -227,11 +228,15 @@ struct Ctx<'a> { same_self_type: bool, } -fn postorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> { - item.preorder().filter_map(|event| match event { - syntax::WalkEvent::Enter(_) => None, - syntax::WalkEvent::Leave(node) => Some(node), - }) +fn preorder_rev(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> { + let x = item + .preorder() + .filter_map(|event| match event { + syntax::WalkEvent::Enter(node) => Some(node), + syntax::WalkEvent::Leave(_) => None, + }) + .collect_vec(); + x.into_iter().rev() } impl Ctx<'_> { @@ -239,12 +244,12 @@ impl Ctx<'_> { // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = postorder(item).filter_map(ast::Path::cast).collect::<Vec<_>>(); + let paths = preorder_rev(item).filter_map(ast::Path::cast).collect::<Vec<_>>(); for path in paths { self.transform_path(path); } - postorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { + preorder_rev(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) { ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax()); } @@ -263,7 +268,7 @@ impl Ctx<'_> { // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = postorder(value).filter_map(ast::Path::cast).collect::<Vec<_>>(); + let paths = preorder_rev(value).filter_map(ast::Path::cast).collect::<Vec<_>>(); for path in paths { self.transform_path(path); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index a40dd2692cf..e2b20ef92fc 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -356,7 +356,7 @@ impl Definition { if let Some(Visibility::Public) = vis { return SearchScope::reverse_dependencies(db, module.krate()); } - if let Some(Visibility::Module(module)) = vis { + if let Some(Visibility::Module(module, _)) = vis { return SearchScope::module_and_children(db, module.into()); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs index f1c95993c84..551021c55a9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs @@ -13,6 +13,7 @@ pub(crate) fn unresolved_assoc_item( "no such associated item", d.expr_or_pat.clone().map(Into::into), ) + .experimental() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 024053effe4..17c701ad035 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -481,7 +481,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where { + impl < >$crate::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -507,7 +507,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{}"#]], + impl < >$crate::marker::Copy for Foo< >where{}"#]], ); } @@ -522,7 +522,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{}"#]], + impl < >$crate::marker::Copy for Foo< >where{}"#]], ); check( r#" @@ -533,7 +533,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where { + impl < >$crate::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index c98e9fba120..60a9367adce 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -171,7 +171,7 @@ impl AnalysisHost { } pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { - self.db.update_parse_query_lru_capacity(lru_capacity); + self.db.update_base_query_lru_capacities(lru_capacity); } pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) { diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 556ed73a04c..e6ddfd580c3 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -322,7 +322,7 @@ fn load_crate_graph( break; } } - vfs::loader::Message::Loaded { files } => { + vfs::loader::Message::Loaded { files } | vfs::loader::Message::Changed { files } => { for (path, contents) in files { vfs.set_file_contents(path.into(), contents); } @@ -331,9 +331,8 @@ fn load_crate_graph( } let changes = vfs.take_changes(); for file in changes { - if file.exists() { - let contents = vfs.file_contents(file.file_id); - if let Ok(text) = std::str::from_utf8(contents) { + if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change { + if let Ok(text) = std::str::from_utf8(&v) { analysis_change.change_file(file.file_id, Some(text.into())) } } diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml index 0c63484634b..e74b340126c 100644 --- a/src/tools/rust-analyzer/crates/parser/Cargo.toml +++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml @@ -13,8 +13,7 @@ doctest = false [dependencies] drop_bomb = "0.1.5" -rustc-dependencies.workspace = true - +ra-ap-rustc_lexer.workspace = true limit.workspace = true [dev-dependencies] @@ -24,7 +23,7 @@ stdx.workspace = true sourcegen.workspace = true [features] -in-rust-tree = ["rustc-dependencies/in-rust-tree"] +in-rust-tree = [] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index e346ece2f94..c8626111145 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -371,7 +371,15 @@ fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLik if p.at(op) { m = p.start(); p.bump(op); - if p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])) { + + // test closure_range_method_call + // fn foo() { + // || .. .method(); + // || .. .field; + // } + let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT); + let struct_forbidden = r.forbid_structs && p.at(T!['{']); + if p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden { expr_bp(p, None, r, 2); } let cm = m.complete(p, RANGE_EXPR); diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index b9e7566fdf9..aa25f82ae1d 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -8,8 +8,6 @@ //! Note that these tokens, unlike the tokens we feed into the parser, do //! include info about comments and whitespace. -use rustc_dependencies::lexer as rustc_lexer; - use std::ops; use rustc_lexer::unescape::{EscapeError, Mode}; @@ -371,6 +369,7 @@ fn error_to_diagnostic_message(error: EscapeError, mode: Mode) -> &'static str { "non-ASCII character in byte string literal" } EscapeError::NonAsciiCharInByte => "non-ASCII character in raw byte string literal", + EscapeError::NulInCStr => "null character in C string literal", EscapeError::UnskippedWhitespaceWarning => "", EscapeError::MultipleSkippedLinesWarning => "", } diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs index d9b3f46f200..ed0aec3cab3 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lib.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs @@ -21,6 +21,11 @@ #![allow(rustdoc::private_intra_doc_links)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; + mod lexed_str; mod token_set; mod syntax_kind; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rast new file mode 100644 index 00000000000..542711339d1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rast @@ -0,0 +1,49 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + METHOD_CALL_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "method" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + FIELD_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "field" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rs new file mode 100644 index 00000000000..a81d3c37133 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rs @@ -0,0 +1,4 @@ +fn foo() { + || .. .method(); + || .. .field; +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index a87becd63e2..208051113a7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -14,8 +14,10 @@ mod version; use indexmap::IndexSet; use paths::AbsPathBuf; use span::Span; -use std::{fmt, io, sync::Mutex}; -use triomphe::Arc; +use std::{ + fmt, io, + sync::{Arc, Mutex}, +}; use serde::{Deserialize, Serialize}; @@ -81,9 +83,11 @@ impl PartialEq for ProcMacro { } } +#[derive(Clone, Debug)] pub struct ServerError { pub message: String, - pub io: Option<io::Error>, + // io::Error isn't Clone for some reason + pub io: Option<Arc<io::Error>>, } impl fmt::Display for ServerError { diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 3494164c067..5ce601bce69 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -1,8 +1,9 @@ //! Handle process life-time and message passing for proc-macro client use std::{ - io::{self, BufRead, BufReader, Write}, + io::{self, BufRead, BufReader, Read, Write}, process::{Child, ChildStdin, ChildStdout, Command, Stdio}, + sync::Arc, }; use paths::{AbsPath, AbsPathBuf}; @@ -15,9 +16,11 @@ use crate::{ #[derive(Debug)] pub(crate) struct ProcMacroProcessSrv { - _process: Process, + process: Process, stdin: ChildStdin, stdout: BufReader<ChildStdout>, + /// Populated when the server exits. + server_exited: Option<ServerError>, version: u32, mode: SpanMode, } @@ -29,9 +32,10 @@ impl ProcMacroProcessSrv { let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); io::Result::Ok(ProcMacroProcessSrv { - _process: process, + process, stdin, stdout, + server_exited: None, version: 0, mode: SpanMode::Id, }) @@ -105,8 +109,35 @@ impl ProcMacroProcessSrv { } pub(crate) fn send_task(&mut self, req: Request) -> Result<Response, ServerError> { + if let Some(server_error) = &self.server_exited { + return Err(server_error.clone()); + } + let mut buf = String::new(); - send_request(&mut self.stdin, &mut self.stdout, req, &mut buf) + send_request(&mut self.stdin, &mut self.stdout, req, &mut buf).map_err(|e| { + if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { + match self.process.child.try_wait() { + Ok(None) => e, + Ok(Some(status)) => { + let mut msg = String::new(); + if !status.success() { + if let Some(stderr) = self.process.child.stderr.as_mut() { + _ = stderr.read_to_string(&mut msg); + } + } + let server_error = ServerError { + message: format!("server exited with {status}: {msg}"), + io: None, + }; + self.server_exited = Some(server_error.clone()); + server_error + } + Err(_) => e, + } + } else { + e + } + }) } } @@ -131,12 +162,19 @@ impl Process { } fn mk_child(path: &AbsPath, null_stderr: bool) -> io::Result<Child> { - Command::new(path.as_os_str()) - .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") + let mut cmd = Command::new(path.as_os_str()); + cmd.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .stdin(Stdio::piped()) .stdout(Stdio::piped()) - .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() }) - .spawn() + .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() }); + if cfg!(windows) { + let mut path_var = std::ffi::OsString::new(); + path_var.push(path.parent().unwrap().parent().unwrap().as_os_str()); + path_var.push("\\bin;"); + path_var.push(std::env::var_os("PATH").unwrap_or_default()); + cmd.env("PATH", path_var); + } + cmd.spawn() } fn send_request( @@ -145,9 +183,13 @@ fn send_request( req: Request, buf: &mut String, ) -> Result<Response, ServerError> { - req.write(&mut writer) - .map_err(|err| ServerError { message: "failed to write request".into(), io: Some(err) })?; - let res = Response::read(&mut reader, buf) - .map_err(|err| ServerError { message: "failed to read response".into(), io: Some(err) })?; + req.write(&mut writer).map_err(|err| ServerError { + message: "failed to write request".into(), + io: Some(Arc::new(err)), + })?; + let res = Response::read(&mut reader, buf).map_err(|err| ServerError { + message: "failed to read response".into(), + io: Some(Arc::new(err)), + })?; res.ok_or_else(|| ServerError { message: "server exited".into(), io: None }) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index 87f7555b02c..af9a03826ff 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -24,7 +24,8 @@ fn main() -> std::io::Result<()> { #[cfg(not(any(feature = "sysroot-abi", rust_analyzer)))] fn run() -> io::Result<()> { - panic!("proc-macro-srv-cli requires the `sysroot-abi` feature to be enabled"); + eprintln!("proc-macro-srv-cli requires the `sysroot-abi` feature to be enabled"); + std::process::exit(70); } #[cfg(any(feature = "sysroot-abi", rust_analyzer))] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 9c4375559c1..ba17ea6f7b4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -37,7 +37,7 @@ expect-test = "1.4.0" proc-macro-test.path = "./proc-macro-test" [features] -sysroot-abi = ["proc-macro-test/sysroot-abi"] +sysroot-abi = [] in-rust-tree = ["mbe/in-rust-tree", "sysroot-abi"] [lints] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml index 90545bb5130..7977afb1cbd 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml @@ -14,6 +14,3 @@ cargo_metadata = "0.18.1" # local deps toolchain.workspace = true - -[features] -sysroot-abi = [] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs index 7299147686d..e6903fb8d4a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs @@ -17,11 +17,24 @@ use cargo_metadata::Message; fn main() { println!("cargo:rerun-if-changed=imp"); - println!("cargo:rerun-if-env-changed=PROC_MACRO_TEST_TOOLCHAIN"); + + let has_features = env::var_os("RUSTC_BOOTSTRAP").is_some() + || String::from_utf8( + Command::new(toolchain::cargo()).arg("--version").output().unwrap().stdout, + ) + .unwrap() + .contains("nightly"); let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = Path::new(&out_dir); + if !has_features { + println!("proc-macro-test testing only works on nightly toolchains"); + let info_path = out_dir.join("proc_macro_test_location.txt"); + fs::File::create(info_path).unwrap(); + return; + } + let name = "proc-macro-test-impl"; let version = "0.0.0"; @@ -53,15 +66,7 @@ fn main() { let target_dir = out_dir.join("target"); - let mut cmd = if let Ok(toolchain) = std::env::var("PROC_MACRO_TEST_TOOLCHAIN") { - // leverage rustup to find user-specific toolchain - let mut cmd = Command::new("cargo"); - cmd.arg(format!("+{toolchain}")); - cmd - } else { - Command::new(toolchain::cargo()) - }; - + let mut cmd = Command::new(toolchain::cargo()); cmd.current_dir(&staging_dir) .args(["build", "-p", "proc-macro-test-impl", "--message-format", "json"]) // Explicit override the target directory to avoid using the same one which the parent @@ -70,9 +75,6 @@ fn main() { // instance to use the same target directory. .arg("--target-dir") .arg(&target_dir); - if cfg!(feature = "sysroot-abi") { - cmd.args(["--features", "sysroot-abi"]); - } if let Ok(target) = std::env::var("TARGET") { cmd.args(["--target", &target]); @@ -90,12 +92,24 @@ fn main() { panic!("proc-macro-test-impl failed to build"); } + // Old Package ID Spec + let repr = format!("{name} {version}"); + // New Package Id Spec since rust-lang/cargo#13311 + let pkgid = String::from_utf8( + Command::new(toolchain::cargo()) + .current_dir(&staging_dir) + .args(["pkgid", name]) + .output() + .unwrap().stdout, + ) + .unwrap(); + let pkgid = pkgid.trim(); + let mut artifact_path = None; for message in Message::parse_stream(output.stdout.as_slice()) { if let Message::CompilerArtifact(artifact) = message.unwrap() { if artifact.target.kind.contains(&"proc-macro".to_string()) { - let repr = format!("{name} {version}"); - if artifact.package_id.repr.starts_with(&repr) { + if artifact.package_id.repr.starts_with(&repr) || artifact.package_id.repr == pkgid { artifact_path = Some(PathBuf::from(&artifact.filenames[0])); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml index dc94fcd61a4..fa189752b76 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml @@ -13,7 +13,4 @@ proc-macro = true # this crate should not have any dependencies, since it uses its own workspace, # and its own `Cargo.lock` -[features] -sysroot-abi = [] - [workspace] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs index b8aad4acefc..d9018b1b87d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs @@ -1,8 +1,5 @@ //! Exports a few trivial procedural macros for testing. -#![allow(unexpected_cfgs)] -#![cfg(feature = "sysroot-abi")] -#![cfg(any(feature = "sysroot-abi", rust_analyzer))] #![warn(rust_2018_idioms, unused_lifetimes)] #![feature(proc_macro_span, proc_macro_def_site)] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index f1575a5b0bd..67b9f57a163 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -11,11 +11,13 @@ //! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… #![cfg(any(feature = "sysroot-abi", rust_analyzer))] -#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unreachable_pub, internal_features)] extern crate proc_macro; +#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod dylib; diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 00cc7c30ee3..c04eddc56fb 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -1398,7 +1398,7 @@ fn sysroot_to_crate_graph( let public_deps = SysrootPublicDeps { deps: sysroot .public_deps() - .map(|(name, idx, prelude)| (name, sysroot_crates[&idx], prelude)) + .filter_map(|(name, idx, prelude)| Some((name, *sysroot_crates.get(&idx)?, prelude))) .collect::<Vec<_>>(), }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index ad24d6d28cd..76414160716 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -49,7 +49,6 @@ flycheck.workspace = true hir-def.workspace = true hir-ty.workspace = true hir.workspace = true -rustc-dependencies.workspace = true ide-db.workspace = true # This should only be used in CLI ide-ssr.workspace = true @@ -89,11 +88,10 @@ in-rust-tree = [ "ide/in-rust-tree", "syntax/in-rust-tree", "parser/in-rust-tree", - "rustc-dependencies/in-rust-tree", "hir/in-rust-tree", "hir-def/in-rust-tree", "hir-ty/in-rust-tree", ] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index f57a27305f0..c4a29e0cbb0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -3,7 +3,7 @@ //! //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. -use std::time::Instant; +use std::{collections::hash_map::Entry, time::Instant}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; @@ -21,7 +21,7 @@ use proc_macro_api::ProcMacroServer; use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts}; use rustc_hash::{FxHashMap, FxHashSet}; use triomphe::Arc; -use vfs::{AnchoredPathBuf, Vfs}; +use vfs::{AnchoredPathBuf, ChangedFile, Vfs}; use crate::{ config::{Config, ConfigError}, @@ -217,8 +217,8 @@ impl GlobalState { pub(crate) fn process_changes(&mut self) -> bool { let _p = profile::span("GlobalState::process_changes"); - let mut file_changes = FxHashMap::default(); - let (change, changed_files, workspace_structure_change) = { + let mut file_changes = FxHashMap::<_, (bool, ChangedFile)>::default(); + let (change, modified_rust_files, workspace_structure_change) = { let mut change = Change::new(); let mut guard = self.vfs.write(); let changed_files = guard.0.take_changes(); @@ -233,64 +233,63 @@ impl GlobalState { // id that is followed by a delete we actually skip observing the file text from the // earlier event, to avoid problems later on. for changed_file in changed_files { - use vfs::ChangeKind::*; - - file_changes - .entry(changed_file.file_id) - .and_modify(|(change, just_created)| { - // None -> Delete => keep - // Create -> Delete => collapse - // - match (change, just_created, changed_file.change_kind) { + use vfs::Change::*; + match file_changes.entry(changed_file.file_id) { + Entry::Occupied(mut o) => { + let (just_created, change) = o.get_mut(); + match (&mut change.change, just_created, changed_file.change) { // latter `Delete` wins (change, _, Delete) => *change = Delete, // merge `Create` with `Create` or `Modify` - (Create, _, Create | Modify) => {} + (Create(prev), _, Create(new) | Modify(new)) => *prev = new, // collapse identical `Modify`es - (Modify, _, Modify) => {} + (Modify(prev), _, Modify(new)) => *prev = new, // equivalent to `Modify` - (change @ Delete, just_created, Create) => { - *change = Modify; + (change @ Delete, just_created, Create(new)) => { + *change = Modify(new); *just_created = true; } // shouldn't occur, but collapse into `Create` - (change @ Delete, just_created, Modify) => { - *change = Create; + (change @ Delete, just_created, Modify(new)) => { + *change = Create(new); *just_created = true; } - // shouldn't occur, but collapse into `Modify` - (Modify, _, Create) => {} + // shouldn't occur, but keep the Create + (prev @ Modify(_), _, new @ Create(_)) => *prev = new, } - }) - .or_insert(( - changed_file.change_kind, - matches!(changed_file.change_kind, Create), - )); + } + Entry::Vacant(v) => { + _ = v.insert((matches!(&changed_file.change, Create(_)), changed_file)) + } + } } let changed_files: Vec<_> = file_changes .into_iter() - .filter(|(_, (change_kind, just_created))| { - !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true)) + .filter(|(_, (just_created, change))| { + !(*just_created && matches!(change.change, vfs::Change::Delete)) }) - .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }) + .map(|(file_id, (_, change))| vfs::ChangedFile { file_id, ..change }) .collect(); let mut workspace_structure_change = None; // A file was added or deleted let mut has_structure_changes = false; let mut bytes = vec![]; - for file in &changed_files { + let mut modified_rust_files = vec![]; + for file in changed_files { let vfs_path = &vfs.file_path(file.file_id); if let Some(path) = vfs_path.as_path() { let path = path.to_path_buf(); - if reload::should_refresh_for_change(&path, file.change_kind) { + if reload::should_refresh_for_change(&path, file.kind()) { workspace_structure_change = Some((path.clone(), false)); } if file.is_created_or_deleted() { has_structure_changes = true; workspace_structure_change = Some((path, self.crate_graph_file_dependencies.contains(vfs_path))); + } else if path.extension() == Some("rs".as_ref()) { + modified_rust_files.push(file.file_id); } } @@ -299,10 +298,8 @@ impl GlobalState { self.diagnostics.clear_native_for(file.file_id); } - let text = if file.exists() { - let bytes = vfs.file_contents(file.file_id).to_vec(); - - String::from_utf8(bytes).ok().and_then(|text| { + let text = if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change { + String::from_utf8(v).ok().and_then(|text| { // FIXME: Consider doing normalization in the `vfs` instead? That allows // getting rid of some locking let (text, line_endings) = LineEndings::normalize(text); @@ -327,11 +324,10 @@ impl GlobalState { let roots = self.source_root_config.partition(vfs); change.set_roots(roots); } - (change, changed_files, workspace_structure_change) + (change, modified_rust_files, workspace_structure_change) }; self.analysis_host.apply_change(change); - { let raw_database = self.analysis_host.raw_database(); // FIXME: ideally we should only trigger a workspace fetch for non-library changes @@ -343,13 +339,12 @@ impl GlobalState { force_crate_graph_reload, ); } - self.proc_macro_changed = - changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| { - let crates = raw_database.relevant_crates(file.file_id); - let crate_graph = raw_database.crate_graph(); + self.proc_macro_changed = modified_rust_files.into_iter().any(|file_id| { + let crates = raw_database.relevant_crates(file_id); + let crate_graph = raw_database.crate_graph(); - crates.iter().any(|&krate| crate_graph[krate].is_proc_macro) - }); + crates.iter().any(|&krate| crate_graph[krate].is_proc_macro) + }); } true @@ -494,10 +489,6 @@ impl GlobalStateSnapshot { }) } - pub(crate) fn vfs_memory_usage(&self) -> usize { - self.vfs_read().memory_usage() - } - pub(crate) fn file_exists(&self, file_id: FileId) -> bool { self.vfs.read().0.exists(file_id) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 7e6219991b6..ce69d612255 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -59,7 +59,13 @@ pub(crate) fn handle_did_open_text_document( if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { let already_exists = state .mem_docs - .insert(path.clone(), DocumentData::new(params.text_document.version)) + .insert( + path.clone(), + DocumentData::new( + params.text_document.version, + params.text_document.text.clone().into_bytes(), + ), + ) .is_err(); if already_exists { tracing::error!("duplicate DidOpenTextDocument: {}", path); @@ -76,11 +82,12 @@ pub(crate) fn handle_did_change_text_document( let _p = profile::span("handle_did_change_text_document"); if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { - match state.mem_docs.get_mut(&path) { + let data = match state.mem_docs.get_mut(&path) { Some(doc) => { // The version passed in DidChangeTextDocument is the version after all edits are applied // so we should apply it before the vfs is notified. doc.version = params.text_document.version; + &mut doc.data } None => { tracing::error!("unexpected DidChangeTextDocument: {}", path); @@ -88,16 +95,16 @@ pub(crate) fn handle_did_change_text_document( } }; - let text = apply_document_changes( + let new_contents = apply_document_changes( state.config.position_encoding(), - || { - let vfs = &state.vfs.read().0; - let file_id = vfs.file_id(&path).unwrap(); - std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into() - }, + std::str::from_utf8(data).unwrap(), params.content_changes, - ); - state.vfs.write().0.set_file_contents(path, Some(text.into_bytes())); + ) + .into_bytes(); + if *data != new_contents { + *data = new_contents.clone(); + state.vfs.write().0.set_file_contents(path, Some(new_contents)); + } } Ok(()) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 13544558c50..22c7e9b0503 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -103,7 +103,6 @@ pub(crate) fn handle_analyzer_status( .collect::<Vec<&AbsPath>>() ); } - format_to!(buf, "\nVfs memory usage: {}\n", profile::Bytes::new(snap.vfs_memory_usage() as _)); buf.push_str("\nAnalysis:\n"); buf.push_str( &snap diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs index a4417e4d4a1..fa5ea5b57db 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs @@ -168,7 +168,7 @@ impl GlobalState { pub(crate) fn apply_document_changes( encoding: PositionEncoding, - file_contents: impl FnOnce() -> String, + file_contents: &str, mut content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>, ) -> String { // If at least one of the changes is a full document change, use the last @@ -179,7 +179,7 @@ pub(crate) fn apply_document_changes( let text = mem::take(&mut content_changes[idx].text); (text, &content_changes[idx + 1..]) } - None => (file_contents(), &content_changes[..]), + None => (file_contents.to_owned(), &content_changes[..]), }; if content_changes.is_empty() { return text; @@ -276,11 +276,11 @@ mod tests { } let encoding = PositionEncoding::Wide(WideEncoding::Utf16); - let text = apply_document_changes(encoding, || String::new(), vec![]); + let text = apply_document_changes(encoding, "", vec![]); assert_eq!(text, ""); let text = apply_document_changes( encoding, - || text, + &text, vec![TextDocumentContentChangeEvent { range: None, range_length: None, @@ -288,49 +288,46 @@ mod tests { }], ); assert_eq!(text, "the"); - let text = apply_document_changes(encoding, || text, c![0, 3; 0, 3 => " quick"]); + let text = apply_document_changes(encoding, &text, c![0, 3; 0, 3 => " quick"]); assert_eq!(text, "the quick"); let text = - apply_document_changes(encoding, || text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); + apply_document_changes(encoding, &text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); assert_eq!(text, "quick foxes"); - let text = apply_document_changes(encoding, || text, c![0, 11; 0, 11 => "\ndream"]); + let text = apply_document_changes(encoding, &text, c![0, 11; 0, 11 => "\ndream"]); assert_eq!(text, "quick foxes\ndream"); - let text = apply_document_changes(encoding, || text, c![1, 0; 1, 0 => "have "]); + let text = apply_document_changes(encoding, &text, c![1, 0; 1, 0 => "have "]); assert_eq!(text, "quick foxes\nhave dream"); let text = apply_document_changes( encoding, - || text, + &text, c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"], ); assert_eq!(text, "the quick foxes\nhave quiet dreams\n"); - let text = apply_document_changes( - encoding, - || text, - c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"], - ); + let text = + apply_document_changes(encoding, &text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]); assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n"); let text = apply_document_changes( encoding, - || text, + &text, c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"], ); assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); let text = - apply_document_changes(encoding, || text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); + apply_document_changes(encoding, &text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); assert_eq!(text, "the quick \nthey have quiet dreams\n"); let text = String::from("❤️"); - let text = apply_document_changes(encoding, || text, c![0, 0; 0, 0 => "a"]); + let text = apply_document_changes(encoding, &text, c![0, 0; 0, 0 => "a"]); assert_eq!(text, "a❤️"); let text = String::from("a\nb"); let text = - apply_document_changes(encoding, || text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); + apply_document_changes(encoding, &text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); assert_eq!(text, "adcb"); let text = String::from("a\nb"); let text = - apply_document_changes(encoding, || text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); + apply_document_changes(encoding, &text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); assert_eq!(text, "ațc\ncb"); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index cdf41c955d2..ca7893faf5d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -571,13 +571,18 @@ impl GlobalState { } fn handle_vfs_msg(&mut self, message: vfs::loader::Message) { + let is_changed = matches!(message, vfs::loader::Message::Changed { .. }); match message { - vfs::loader::Message::Loaded { files } => { + vfs::loader::Message::Changed { files } | vfs::loader::Message::Loaded { files } => { let vfs = &mut self.vfs.write().0; for (path, contents) in files { let path = VfsPath::from(path); + // if the file is in mem docs, it's managed by the client via notifications + // so only set it if its not in there if !self.mem_docs.contains(&path) { - vfs.set_file_contents(path, contents); + if is_changed || vfs.file_id(&path).is_none() { + vfs.set_file_contents(path, contents); + } } } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs index 45a1dab9772..6a93a0ebb4c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs @@ -62,10 +62,11 @@ impl MemDocs { #[derive(Debug, Clone)] pub(crate) struct DocumentData { pub(crate) version: i32, + pub(crate) data: Vec<u8>, } impl DocumentData { - pub(crate) fn new(version: i32) -> Self { - DocumentData { version } + pub(crate) fn new(version: i32, data: Vec<u8>) -> Self { + DocumentData { version, data } } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 91dc6c2e4b4..8e3fa7fa4da 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -503,10 +503,9 @@ impl GlobalState { match vfs.file_id(&vfs_path) { Some(file_id) => Some(file_id), None => { - if !self.mem_docs.contains(&vfs_path) { - let contents = loader.handle.load_sync(path); - vfs.set_file_contents(vfs_path.clone(), contents); - } + // FIXME: Consider not loading this here? + let contents = loader.handle.load_sync(path); + vfs.set_file_contents(vfs_path.clone(), contents); vfs.file_id(&vfs_path) } } diff --git a/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml b/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml deleted file mode 100644 index 0bf04301df1..00000000000 --- a/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "rustc-dependencies" -version = "0.0.0" -description = "TBD" - -rust-version.workspace = true -edition.workspace = true -license.workspace = true -authors.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -ra-ap-rustc_lexer = { version = "0.21.0" } -ra-ap-rustc_parse_format = { version = "0.21.0", default-features = false } -ra-ap-rustc_index = { version = "0.21.0", default-features = false } -ra-ap-rustc_abi = { version = "0.21.0", default-features = false } - -[features] -in-rust-tree = [] - -[lints] -workspace = true \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/rustc-dependencies/src/lib.rs b/src/tools/rust-analyzer/crates/rustc-dependencies/src/lib.rs deleted file mode 100644 index 13fcbc49193..00000000000 --- a/src/tools/rust-analyzer/crates/rustc-dependencies/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! A wrapper around rustc internal crates, which enables switching between compiler provided -//! ones and stable ones published in crates.io - -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_lexer; - -pub mod lexer { - #[cfg(not(feature = "in-rust-tree"))] - pub use ::ra_ap_rustc_lexer::*; - - #[cfg(feature = "in-rust-tree")] - pub use ::rustc_lexer::*; -} - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_parse_format; - -pub mod parse_format { - #[cfg(not(feature = "in-rust-tree"))] - pub use ::ra_ap_rustc_parse_format::*; - - #[cfg(feature = "in-rust-tree")] - pub use ::rustc_parse_format::*; -} - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_abi; - -pub mod abi { - #[cfg(not(feature = "in-rust-tree"))] - pub use ::ra_ap_rustc_abi::*; - - #[cfg(feature = "in-rust-tree")] - pub use ::rustc_abi::*; -} - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_index; - -pub mod index { - #[cfg(not(feature = "in-rust-tree"))] - pub use ::ra_ap_rustc_index::*; - - #[cfg(feature = "in-rust-tree")] - pub use ::rustc_index::*; -} diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 40a93fec2ce..9f78614bba6 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -23,7 +23,7 @@ indexmap.workspace = true smol_str.workspace = true triomphe.workspace = true -rustc-dependencies.workspace = true +ra-ap-rustc_lexer.workspace = true parser.workspace = true profile.workspace = true @@ -41,7 +41,7 @@ test-utils.workspace = true sourcegen.workspace = true [features] -in-rust-tree = ["rustc-dependencies/in-rust-tree"] +in-rust-tree = [] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 8618018c0b6..1c6157de559 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -285,14 +285,16 @@ impl ast::Path { self.first_qualifier_or_self().segment() } - // FIXME: Check usages of Self::segments, they might be wrong because of the logic of the bloew function - pub fn segments_of_this_path_only_rev(&self) -> impl Iterator<Item = ast::PathSegment> + Clone { - self.qualifiers_and_self().filter_map(|it| it.segment()) - } - pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone { - successors(self.first_segment(), |p| { - p.parent_path().parent_path().and_then(|p| p.segment()) + let path_range = self.syntax().text_range(); + successors(self.first_segment(), move |p| { + p.parent_path().parent_path().and_then(|p| { + if path_range.contains_range(p.syntax().text_range()) { + p.segment() + } else { + None + } + }) }) } @@ -300,10 +302,6 @@ impl ast::Path { successors(self.qualifier(), |p| p.qualifier()) } - pub fn qualifiers_and_self(&self) -> impl Iterator<Item = ast::Path> + Clone { - successors(Some(self.clone()), |p| p.qualifier()) - } - pub fn top_path(&self) -> ast::Path { let mut this = self.clone(); while let Some(path) = this.parent_path() { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index d5d565a015a..ede392fc62a 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -2,8 +2,6 @@ use std::borrow::Cow; -use rustc_dependencies::lexer as rustc_lexer; - use rustc_lexer::unescape::{ unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit, Mode, }; diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index d600698040d..1b41596a5f2 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -22,6 +22,11 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![warn(rust_2018_idioms, unused_lifetimes)] +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; + #[allow(unused)] macro_rules! eprintln { ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs index eabbda2c398..69dffbf79f1 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs @@ -5,7 +5,7 @@ mod block; use rowan::Direction; -use rustc_dependencies::lexer::unescape::{self, unescape_literal, Mode}; +use rustc_lexer::unescape::{self, unescape_literal, Mode}; use crate::{ algo, @@ -106,6 +106,9 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> (&'static str, EE::NonAsciiCharInByte => { "Byte literals must not contain non-ASCII characters" } + EE::NulInCStr => { + "C strings literals must not contain null characters" + } EE::UnskippedWhitespaceWarning => "Whitespace after this escape is not skipped", EE::MultipleSkippedLinesWarning => "Multiple lines are skipped by this escape", diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 1f3136404c6..140bb080427 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -25,6 +25,7 @@ //! derive: //! discriminant: //! drop: +//! env: option //! eq: sized //! error: fmt //! fmt: result, transmute, coerce_unsized @@ -1450,6 +1451,15 @@ mod macros { #[macro_export] macro_rules! concat {} // endregion:concat + + // region:env + #[rustc_builtin_macro] + #[macro_export] + macro_rules! env {} + #[rustc_builtin_macro] + #[macro_export] + macro_rules! option_env {} + // endregion:env } // region:non_zero diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 03065043714..19b34ffe6b9 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -160,7 +160,7 @@ impl NotifyActor { Some((path, contents)) }) .collect(); - self.send(loader::Message::Loaded { files }); + self.send(loader::Message::Changed { files }); } } } diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs index ef5b10ee9db..34a85818eb8 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs @@ -1,8 +1,8 @@ //! # Virtual File System //! -//! VFS stores all files read by rust-analyzer. Reading file contents from VFS -//! always returns the same contents, unless VFS was explicitly modified with -//! [`set_file_contents`]. All changes to VFS are logged, and can be retrieved via +//! VFS records all file changes pushed to it via [`set_file_contents`]. +//! As such it only ever stores changes, not the actual content of a file at any given moment. +//! All file changes are logged, and can be retrieved via //! [`take_changes`] method. The pack of changes is then pushed to `salsa` and //! triggers incremental recomputation. //! @@ -84,40 +84,65 @@ impl FileId { /// safe because `FileId` is a newtype of `u32` impl nohash_hasher::IsEnabled for FileId {} -/// Storage for all files read by rust-analyzer. +/// Storage for all file changes and the file id to path mapping. /// /// For more information see the [crate-level](crate) documentation. #[derive(Default)] pub struct Vfs { interner: PathInterner, - data: Vec<Option<Vec<u8>>>, + data: Vec<FileState>, changes: Vec<ChangedFile>, } +#[derive(Copy, PartialEq, PartialOrd, Clone)] +pub enum FileState { + Exists, + Deleted, +} + /// Changed file in the [`Vfs`]. #[derive(Debug)] pub struct ChangedFile { /// Id of the changed file pub file_id: FileId, /// Kind of change - pub change_kind: ChangeKind, + pub change: Change, } impl ChangedFile { /// Returns `true` if the change is not [`Delete`](ChangeKind::Delete). pub fn exists(&self) -> bool { - self.change_kind != ChangeKind::Delete + !matches!(self.change, Change::Delete) } /// Returns `true` if the change is [`Create`](ChangeKind::Create) or - /// [`Delete`](ChangeKind::Delete). + /// [`Delete`](Change::Delete). pub fn is_created_or_deleted(&self) -> bool { - matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete) + matches!(self.change, Change::Create(_) | Change::Delete) } + + pub fn kind(&self) -> ChangeKind { + match self.change { + Change::Create(_) => ChangeKind::Create, + Change::Modify(_) => ChangeKind::Modify, + Change::Delete => ChangeKind::Delete, + } + } +} + +/// Kind of [file change](ChangedFile). +#[derive(Eq, PartialEq, Debug)] +pub enum Change { + /// The file was (re-)created + Create(Vec<u8>), + /// The file was modified + Modify(Vec<u8>), + /// The file was deleted + Delete, } /// Kind of [file change](ChangedFile). -#[derive(Eq, PartialEq, Copy, Clone, Debug)] +#[derive(Eq, PartialEq, Debug)] pub enum ChangeKind { /// The file was (re-)created Create, @@ -130,7 +155,7 @@ pub enum ChangeKind { impl Vfs { /// Id of the given path if it exists in the `Vfs` and is not deleted. pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { - self.interner.get(path).filter(|&it| self.get(it).is_some()) + self.interner.get(path).filter(|&it| matches!(self.get(it), FileState::Exists)) } /// File path corresponding to the given `file_id`. @@ -142,28 +167,13 @@ impl Vfs { self.interner.lookup(file_id).clone() } - /// File content corresponding to the given `file_id`. - /// - /// # Panics - /// - /// Panics if the id is not present in the `Vfs`, or if the corresponding file is - /// deleted. - pub fn file_contents(&self, file_id: FileId) -> &[u8] { - self.get(file_id).as_deref().unwrap() - } - - /// Returns the overall memory usage for the stored files. - pub fn memory_usage(&self) -> usize { - self.data.iter().flatten().map(|d| d.capacity()).sum() - } - /// Returns an iterator over the stored ids and their corresponding paths. /// /// This will skip deleted files. pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ { (0..self.data.len()) .map(|it| FileId(it as u32)) - .filter(move |&file_id| self.get(file_id).is_some()) + .filter(move |&file_id| matches!(self.get(file_id), FileState::Exists)) .map(move |file_id| { let path = self.interner.lookup(file_id); (file_id, path) @@ -176,28 +186,21 @@ impl Vfs { /// /// If the path does not currently exists in the `Vfs`, allocates a new /// [`FileId`] for it. - pub fn set_file_contents(&mut self, path: VfsPath, mut contents: Option<Vec<u8>>) -> bool { + pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool { let file_id = self.alloc_file_id(path); - let change_kind = match (self.get(file_id), &contents) { - (None, None) => return false, - (Some(old), Some(new)) if old == new => return false, - (None, Some(_)) => ChangeKind::Create, - (Some(_), None) => ChangeKind::Delete, - (Some(_), Some(_)) => ChangeKind::Modify, + let change_kind = match (self.get(file_id), contents) { + (FileState::Deleted, None) => return false, + (FileState::Deleted, Some(v)) => Change::Create(v), + (FileState::Exists, None) => Change::Delete, + (FileState::Exists, Some(v)) => Change::Modify(v), }; - if let Some(contents) = &mut contents { - contents.shrink_to_fit(); - } - *self.get_mut(file_id) = contents; - self.changes.push(ChangedFile { file_id, change_kind }); + let changed_file = ChangedFile { file_id, change: change_kind }; + self.data[file_id.0 as usize] = + if changed_file.exists() { FileState::Exists } else { FileState::Deleted }; + self.changes.push(changed_file); true } - /// Returns `true` if the `Vfs` contains [changes](ChangedFile). - pub fn has_changes(&self) -> bool { - !self.changes.is_empty() - } - /// Drain and returns all the changes in the `Vfs`. pub fn take_changes(&mut self) -> Vec<ChangedFile> { mem::take(&mut self.changes) @@ -205,7 +208,7 @@ impl Vfs { /// Provides a panic-less way to verify file_id validity. pub fn exists(&self, file_id: FileId) -> bool { - self.get(file_id).is_some() + matches!(self.get(file_id), FileState::Exists) } /// Returns the id associated with `path` @@ -219,26 +222,17 @@ impl Vfs { let file_id = self.interner.intern(path); let idx = file_id.0 as usize; let len = self.data.len().max(idx + 1); - self.data.resize_with(len, || None); + self.data.resize(len, FileState::Deleted); file_id } - /// Returns the content associated with the given `file_id`. - /// - /// # Panics - /// - /// Panics if no file is associated to that id. - fn get(&self, file_id: FileId) -> &Option<Vec<u8>> { - &self.data[file_id.0 as usize] - } - - /// Mutably returns the content associated with the given `file_id`. + /// Returns the status of the file associated with the given `file_id`. /// /// # Panics /// /// Panics if no file is associated to that id. - fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> { - &mut self.data[file_id.0 as usize] + fn get(&self, file_id: FileId) -> FileState { + self.data[file_id.0 as usize] } } diff --git a/src/tools/rust-analyzer/crates/vfs/src/loader.rs b/src/tools/rust-analyzer/crates/vfs/src/loader.rs index e2d74782ae5..89a544c81d8 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/loader.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/loader.rs @@ -51,6 +51,8 @@ pub enum Message { Progress { n_total: usize, n_done: usize, config_version: u32 }, /// The handle loaded the following files' content. Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, + /// The handle loaded the following files' content. + Changed { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, } /// Type that will receive [`Messages`](Message) from a [`Handle`]. @@ -199,6 +201,9 @@ impl fmt::Debug for Message { Message::Loaded { files } => { f.debug_struct("Loaded").field("n_files", &files.len()).finish() } + Message::Changed { files } => { + f.debug_struct("Changed").field("n_files", &files.len()).finish() + } Message::Progress { n_total, n_done, config_version } => f .debug_struct("Progress") .field("n_total", n_total) diff --git a/src/tools/rust-analyzer/docs/dev/syntax.md b/src/tools/rust-analyzer/docs/dev/syntax.md index 97e376787c8..fd6f220f4fa 100644 --- a/src/tools/rust-analyzer/docs/dev/syntax.md +++ b/src/tools/rust-analyzer/docs/dev/syntax.md @@ -41,7 +41,6 @@ Syntax trees are a semi-transient data structure. In general, frontend does not keep syntax trees for all files in memory. Instead, it *lowers* syntax trees to more compact and rigid representation, which is not full-fidelity, but which can be mapped back to a syntax tree if so desired. - ### GreenNode GreenNode is a purely-functional tree with arbitrary arity. Conceptually, it is equivalent to the following run of the mill struct: @@ -500,7 +499,7 @@ Specifically, `TreeSink` constructs the tree in lockstep with draining the origi In the process, it records which tokens of the tree correspond to which tokens of the input, by using text ranges to identify syntax tokens. The end result is that parsing an expanded code yields a syntax tree and a mapping of text-ranges of the tree to original tokens. -To deal with precedence in cases like `$expr * 1`, we use special invisible parenthesis, which are explicitly handled by the parser +To deal with precedence in cases like `$expr * 1`, we use special invisible parenthesis, which are explicitly handled by the parser. ### Whitespace & Comments diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 8b9d5d4a746..291cef926f8 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -2290,9 +2290,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "dev": true, "funding": [ { diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 0fa6edaa5d7..7f576279432 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -40,9 +40,11 @@ pub(crate) fn is_short_pattern(pat: &ast::Pat, pat_str: &str) -> bool { fn is_short_pattern_inner(pat: &ast::Pat) -> bool { match pat.kind { - ast::PatKind::Rest | ast::PatKind::Never | ast::PatKind::Wild | ast::PatKind::Lit(_) => { - true - } + ast::PatKind::Rest + | ast::PatKind::Never + | ast::PatKind::Wild + | ast::PatKind::Err(_) + | ast::PatKind::Lit(_) => true, ast::PatKind::Ident(_, _, ref pat) => pat.is_none(), ast::PatKind::Struct(..) | ast::PatKind::MacCall(..) @@ -274,6 +276,7 @@ impl Rewrite for Pat { PatKind::Paren(ref pat) => pat .rewrite(context, shape.offset_left(1)?.sub_width(1)?) .map(|inner_pat| format!("({})", inner_pat)), + PatKind::Err(_) => None, } } } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index c198c116282..b03811e5efd 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -359,7 +359,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "tracing-tree", "twox-hash", "type-map", - "typed-arena", "typenum", "unic-langid", "unic-langid-impl", diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 8e791a7dc69..d673ce7a736 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -15,7 +15,7 @@ use std::ffi::OsStr; use std::fmt; use std::fs; use std::num::NonZeroU32; -use std::path::Path; +use std::path::{Path, PathBuf}; use regex::Regex; @@ -52,6 +52,8 @@ pub struct Feature { pub since: Option<Version>, pub has_gate_test: bool, pub tracking_issue: Option<NonZeroU32>, + pub file: PathBuf, + pub line: usize, } impl Feature { fn tracking_issue_display(&self) -> impl fmt::Display { @@ -184,23 +186,25 @@ pub fn check( .chain(lib_features.iter().map(|feat| (feat, "lib"))); for ((feature_name, feature), kind) in all_features_iter { let since = if let Some(since) = feature.since { since } else { continue }; + let file = feature.file.display(); + let line = feature.line; if since > version && since != Version::CurrentPlaceholder { tidy_error!( bad, - "The stabilization version {since} of {kind} feature `{feature_name}` is newer than the current {version}" + "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is newer than the current {version}" ); } if channel == "nightly" && since == version { tidy_error!( bad, - "The stabilization version {since} of {kind} feature `{feature_name}` is written out but should be {}", + "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is written out but should be {}", version::VERSION_PLACEHOLDER ); } if channel != "nightly" && since == Version::CurrentPlaceholder { tidy_error!( bad, - "The placeholder use of {kind} feature `{feature_name}` is not allowed on the {channel} channel", + "{file}:{line}: The placeholder use of {kind} feature `{feature_name}` is not allowed on the {channel} channel", ); } } @@ -433,7 +437,14 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba ); } Entry::Vacant(e) => { - e.insert(Feature { level, since, has_gate_test: false, tracking_issue }); + e.insert(Feature { + level, + since, + has_gate_test: false, + tracking_issue, + file: path.to_path_buf(), + line: line_number, + }); } } } @@ -559,6 +570,8 @@ fn map_lib_features( since: None, has_gate_test: false, tracking_issue: find_attr_val(line, "issue").and_then(handle_issue_none), + file: file.to_path_buf(), + line: i + 1, }; mf(Ok((feature_name, feature)), file, i + 1); continue; @@ -588,7 +601,14 @@ fn map_lib_features( }; let tracking_issue = find_attr_val(line, "issue").and_then(handle_issue_none); - let feature = Feature { level, since, has_gate_test: false, tracking_issue }; + let feature = Feature { + level, + since, + has_gate_test: false, + tracking_issue, + file: file.to_path_buf(), + line: i + 1, + }; if line.contains(']') { mf(Ok((feature_name, feature)), file, i + 1); } else { diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index eb0a2fda290..95149987033 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -70,6 +70,7 @@ pub mod pal; pub mod rustdoc_css_themes; pub mod rustdoc_gui_tests; pub mod style; +pub mod target_policy; pub mod target_specific_tests; pub mod tests_placement; pub mod ui_tests; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 9f92b8995b7..a9340c40f44 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -109,6 +109,7 @@ fn main() { // Checks that only make sense for the compiler. check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose); check!(fluent_alphabetical, &compiler_path, bless); + check!(target_policy, &root_path); // Checks that only make sense for the std libs. check!(pal, &library_path); diff --git a/src/tools/tidy/src/target_policy.rs b/src/tools/tidy/src/target_policy.rs new file mode 100644 index 00000000000..ca6590d1502 --- /dev/null +++ b/src/tools/tidy/src/target_policy.rs @@ -0,0 +1,52 @@ +//! Tests for target tier policy compliance. +//! +//! As of writing, only checks that sanity-check assembly test for targets doesn't miss any targets. + +use crate::walk::{filter_not_rust, walk}; +use std::{collections::HashSet, path::Path}; + +const TARGET_DEFINITIONS_PATH: &str = "compiler/rustc_target/src/spec/targets/"; +const ASSEMBLY_TEST_PATH: &str = "tests/assembly/targets/"; +const REVISION_LINE_START: &str = "// revisions: "; +const EXCEPTIONS: &[&str] = &[ + // FIXME: disabled since it fails on CI saying the csky component is missing + "csky_unknown_linux_gnuabiv2", + "csky_unknown_linux_gnuabiv2hf", +]; + +pub fn check(root_path: &Path, bad: &mut bool) { + let mut targets_to_find = HashSet::new(); + + let definitions_path = root_path.join(TARGET_DEFINITIONS_PATH); + for defn in ignore::WalkBuilder::new(&definitions_path) + .max_depth(Some(1)) + .filter_entry(|e| !filter_not_rust(e.path())) + .build() + { + let defn = defn.unwrap(); + // Skip directory itself. + if defn.path() == definitions_path { + continue; + } + + let path = defn.path(); + let target_name = path.file_stem().unwrap().to_string_lossy().into_owned(); + let _ = targets_to_find.insert(target_name); + } + + walk(&root_path.join(ASSEMBLY_TEST_PATH), |_, _| false, &mut |_, contents| { + for line in contents.lines() { + let Some(_) = line.find(REVISION_LINE_START) else { + continue; + }; + let (_, target_name) = line.split_at(REVISION_LINE_START.len()); + targets_to_find.remove(target_name); + } + }); + + for target in targets_to_find { + if !EXCEPTIONS.contains(&target.as_str()) { + tidy_error!(bad, "{ASSEMBLY_TEST_PATH}: missing assembly test for {target}") + } + } +} |
