Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #4969 from rust-lang/rustup-2026-04-19
Automatic Rustup
294 files changed, 14250 insertions, 5507 deletions
diff --git a/.github/workflows/gen-lints.yml b/.github/workflows/gen-lints.yml new file mode 100644 index 0000000000..c978e3571c --- /dev/null +++ b/.github/workflows/gen-lints.yml @@ -0,0 +1,44 @@ +name: Generate lints and feature flags + +on: + workflow_dispatch: + schedule: + - cron: '50 23 * * 6' + +defaults: + run: + shell: bash + +jobs: + lints-gen: + name: Generate lints + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Install nightly + run: rustup default nightly + + - name: Generate lints/feature flags + run: cargo codegen lint-definitions + + - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + id: app-token + with: + app-id: ${{ vars.APP_CLIENT_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Submit PR + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 + with: + token: ${{ steps.app-token.outputs.token }} + commit-message: "internal: update generated lints" + branch: "ci/gen-lints" + delete-branch: true + sign-commits: true + title: "Update generated lints" + body: "Weekly lint updates for `crates/ide-db/src/generated/lints.rs`." + labels: "A-infra" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b35614f91b..ef61e397fc 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -264,8 +264,6 @@ jobs: name: ${{ env.TAG }} token: ${{ secrets.GITHUB_TOKEN }} - - run: rm dist/rust-analyzer-no-server.vsix - - run: npm ci working-directory: ./editors/code diff --git a/.github/workflows/rustc-pull.yml b/.github/workflows/rustc-pull.yml index 37cf5f3726..be3362b79b 100644 --- a/.github/workflows/rustc-pull.yml +++ b/.github/workflows/rustc-pull.yml @@ -12,6 +12,7 @@ jobs: uses: rust-lang/josh-sync/.github/workflows/rustc-pull.yml@main with: github-app-id: ${{ vars.APP_CLIENT_ID }} + pr-author: "workflows-rust-analyzer[bot]" zulip-stream-id: 185405 zulip-bot-email: "[email protected]" pr-base-branch: master diff --git a/Cargo.lock b/Cargo.lock index 5370127ddc..da530b3a93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -234,7 +234,6 @@ dependencies = [ "intern", "oorandom", "rustc-hash 2.1.1", - "span", "syntax", "syntax-bridge", "tracing", @@ -846,7 +845,6 @@ dependencies = [ name = "hir-expand" version = "0.0.0" dependencies = [ - "arrayvec", "base-db", "cfg", "cov-mark", @@ -1731,9 +1729,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf-event" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5396562cd2eaa828445d6d34258ae21ee1eb9d40fe626ca7f51c8dccb4af9d66" +checksum = "b4d6393d9238342159080d79b78cb59c67399a8e7ecfa5d410bd614169e4e823" dependencies = [ "libc", "perf-event-open-sys", @@ -1741,9 +1739,9 @@ dependencies = [ [[package]] name = "perf-event-open-sys" -version = "1.0.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce9bedf5da2c234fdf2391ede2b90fabf585355f33100689bc364a3ea558561a" +checksum = "7c44fb1c7651a45a3652c4afc6e754e40b3d6e6556f1487e2b230bfc4f33c2a8" dependencies = [ "libc", ] @@ -2283,9 +2281,9 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rowan" -version = "0.15.17" +version = "0.15.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4f1e4a001f863f41ea8d0e6a0c34b356d5b733db50dadab3efef640bafb779b" +checksum = "62f509095fc8cc0c8c8564016771d458079c11a8d857e65861f045145c0d3208" dependencies = [ "countme", "hashbrown 0.14.5", @@ -2733,6 +2731,7 @@ dependencies = [ "rustc-hash 2.1.1", "rustc-literal-escaper 0.0.4", "rustc_apfloat", + "smallvec", "smol_str 0.3.2", "stdx", "test-utils", diff --git a/Cargo.toml b/Cargo.toml index 9f31e1903a..3b3929df0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,7 +132,7 @@ process-wrap = { version = "8.2.1", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } rayon = "1.10.0" -rowan = "=0.15.17" +rowan = "=0.15.18" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it salsa = { version = "0.25.2", default-features = false, features = [ diff --git a/crates/base-db/src/change.rs b/crates/base-db/src/change.rs index c728f3e5ca..4d4bf78cbc 100644 --- a/crates/base-db/src/change.rs +++ b/crates/base-db/src/change.rs @@ -9,7 +9,8 @@ use triomphe::Arc; use vfs::FileId; use crate::{ - CrateGraphBuilder, CratesIdMap, LibraryRoots, LocalRoots, RootQueryDb, SourceRoot, SourceRootId, + CrateGraphBuilder, CratesIdMap, LibraryRoots, LocalRoots, SourceDatabase, SourceRoot, + SourceRootId, }; /// Encapsulate a bunch of raw `.set` calls on the database. @@ -49,7 +50,7 @@ impl FileChange { self.crate_graph = Some(graph); } - pub fn apply(self, db: &mut dyn RootQueryDb) -> Option<CratesIdMap> { + pub fn apply(self, db: &mut dyn SourceDatabase) -> Option<CratesIdMap> { let _p = tracing::info_span!("FileChange::apply").entered(); if let Some(roots) = self.roots { let mut local_roots = FxHashSet::default(); diff --git a/crates/base-db/src/editioned_file_id.rs b/crates/base-db/src/editioned_file_id.rs index 8721f3a0ff..a77b45f8ae 100644 --- a/crates/base-db/src/editioned_file_id.rs +++ b/crates/base-db/src/editioned_file_id.rs @@ -5,14 +5,71 @@ use std::hash::Hash; use salsa::Database; use span::Edition; +use syntax::{SyntaxError, ast}; use vfs::FileId; +use crate::SourceDatabase; + #[salsa::interned(debug, constructor = from_span_file_id, no_lifetime)] #[derive(PartialOrd, Ord)] pub struct EditionedFileId { field: span::EditionedFileId, } +// Currently does not work due to a salsa bug +// #[salsa::tracked] +// impl EditionedFileId { +// #[salsa::tracked(lru = 128)] +// pub fn parse(self, db: &dyn SourceDatabase) -> syntax::Parse<ast::SourceFile> { +// let _p = tracing::info_span!("parse", ?self).entered(); +// let (file_id, edition) = self.unpack(db); +// let text = db.file_text(file_id).text(db); +// ast::SourceFile::parse(text, edition) +// } + +// // firewall query +// #[salsa::tracked(returns(as_deref))] +// pub fn parse_errors(self, db: &dyn SourceDatabase) -> Option<Box<[SyntaxError]>> { +// let errors = self.parse(db).errors(); +// match &*errors { +// [] => None, +// [..] => Some(errors.into()), +// } +// } +// } + +impl EditionedFileId { + pub fn parse(self, db: &dyn SourceDatabase) -> syntax::Parse<ast::SourceFile> { + #[salsa::tracked(lru = 128)] + pub fn parse( + db: &dyn SourceDatabase, + file_id: EditionedFileId, + ) -> syntax::Parse<ast::SourceFile> { + let _p = tracing::info_span!("parse", ?file_id).entered(); + let (file_id, edition) = file_id.unpack(db); + let text = db.file_text(file_id).text(db); + ast::SourceFile::parse(text, edition) + } + parse(db, self) + } + + // firewall query + pub fn parse_errors(self, db: &dyn SourceDatabase) -> Option<&[SyntaxError]> { + #[salsa::tracked(returns(as_deref))] + pub fn parse_errors( + db: &dyn SourceDatabase, + file_id: EditionedFileId, + ) -> Option<Box<[SyntaxError]>> { + let errors = file_id.parse(db).errors(); + match &*errors { + [] => None, + [..] => Some(errors.into()), + } + } + parse_errors(db, self) + } +} + impl EditionedFileId { #[inline] pub fn new(db: &dyn Database, file_id: FileId, edition: Edition) -> Self { diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 246c57edc2..38f9c5a5a1 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -21,7 +21,10 @@ use span::Edition; use triomphe::Arc; use vfs::{AbsPathBuf, AnchoredPath, FileId, VfsPath, file_set::FileSet}; -use crate::{CrateWorkspaceData, EditionedFileId, FxIndexSet, RootQueryDb}; +use crate::{ + CrateWorkspaceData, EditionedFileId, FxIndexSet, SourceDatabase, all_crates, + set_all_crates_with_durability, +}; pub type ProcMacroPaths = FxHashMap<CrateBuilderId, Result<(String, AbsPathBuf), ProcMacroLoadingError>>; @@ -490,13 +493,13 @@ impl Crate { /// including the crate itself. /// /// **Warning**: do not use this query in `hir-*` crates! It kills incrementality across crate metadata modifications. - pub fn transitive_rev_deps(self, db: &dyn RootQueryDb) -> Box<[Crate]> { + pub fn transitive_rev_deps(self, db: &dyn SourceDatabase) -> Box<[Crate]> { let mut worklist = vec![self]; let mut rev_deps = FxHashSet::default(); rev_deps.insert(self); let mut inverted_graph = FxHashMap::<_, Vec<_>>::default(); - db.all_crates().iter().for_each(|&krate| { + all_crates(db).iter().for_each(|&krate| { krate .data(db) .dependencies @@ -586,15 +589,15 @@ impl CrateGraphBuilder { Ok(()) } - pub fn set_in_db(self, db: &mut dyn RootQueryDb) -> CratesIdMap { + pub fn set_in_db(self, db: &mut dyn SourceDatabase) -> CratesIdMap { + let old_all_crates = all_crates(db); + // For some reason in some repositories we have duplicate crates, so we use a set and not `Vec`. // We use an `IndexSet` because the list needs to be topologically sorted. let mut all_crates = FxIndexSet::with_capacity_and_hasher(self.arena.len(), FxBuildHasher); let mut visited = FxHashMap::default(); let mut visited_root_files = FxHashSet::default(); - let old_all_crates = db.all_crates(); - let crates_map = db.crates_map(); // salsa doesn't compare new input to old input to see if they are the same, so here we are doing all the work ourselves. for krate in self.iter() { @@ -612,17 +615,14 @@ impl CrateGraphBuilder { if old_all_crates.len() != all_crates.len() || old_all_crates.iter().any(|&krate| !all_crates.contains(&krate)) { - db.set_all_crates_with_durability( - Arc::new(Vec::from_iter(all_crates).into_boxed_slice()), - Durability::MEDIUM, - ); + set_all_crates_with_durability(db, all_crates, Durability::MEDIUM); } return visited; fn go( graph: &CrateGraphBuilder, - db: &mut dyn RootQueryDb, + db: &mut dyn SourceDatabase, crates_map: &CratesMap, visited: &mut FxHashMap<CrateBuilderId, Crate>, visited_root_files: &mut FxHashSet<FileId>, @@ -929,6 +929,27 @@ impl<'a> IntoIterator for &'a Env { } } +/// The crate graph had a cycle. This is typically a bug, and +/// rust-analyzer logs a warning when it encounters a cycle. Generally +/// rust-analyzer will continue working OK in the presence of cycle, +/// but it's better to have an accurate crate graph. +/// +/// ## dev-dependencies +/// +/// Note that it's actually legal for a cargo package (i.e. a thing +/// with a Cargo.toml) to depend on itself in dev-dependencies. This +/// can enable additional features, and is typically used when a +/// project wants features to be enabled in tests. Dev-dependencies +/// are not propagated, so they aren't visible to package that depend +/// on this one. +/// +/// <https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies> +/// +/// However, rust-analyzer constructs its crate graph from Cargo +/// metadata, so it can end up producing a cyclic crate graph from a +/// well-formed package graph. +/// +/// <https://github.com/rust-lang/rust-analyzer/issues/14167> #[derive(Debug)] pub struct CyclicDependenciesError { path: Vec<(CrateBuilderId, Option<CrateDisplayName>)>, diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 5baf4ce6f9..e438505c07 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -36,7 +36,6 @@ pub use query_group; use rustc_hash::{FxHashSet, FxHasher}; use salsa::{Durability, Setter}; pub use semver::{BuildMetadata, Prerelease, Version, VersionReq}; -use syntax::{Parse, SyntaxError, ast}; use triomphe::Arc; pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; @@ -236,36 +235,6 @@ pub struct SourceRootInput { pub source_root: Arc<SourceRoot>, } -/// Database which stores all significant input facts: source code and project -/// model. Everything else in rust-analyzer is derived from these queries. -#[query_group::query_group] -pub trait RootQueryDb: SourceDatabase + salsa::Database { - /// Parses the file into the syntax tree. - #[salsa::invoke(parse)] - #[salsa::lru(128)] - fn parse(&self, file_id: EditionedFileId) -> Parse<ast::SourceFile>; - - /// Returns the set of errors obtained from parsing the file including validation errors. - #[salsa::transparent] - fn parse_errors(&self, file_id: EditionedFileId) -> Option<&[SyntaxError]>; - - #[salsa::transparent] - fn toolchain_channel(&self, krate: Crate) -> Option<ReleaseChannel>; - - /// Crates whose root file is in `id`. - #[salsa::invoke_interned(source_root_crates)] - fn source_root_crates(&self, id: SourceRootId) -> Arc<[Crate]>; - - #[salsa::transparent] - fn relevant_crates(&self, file_id: FileId) -> Arc<[Crate]>; - - /// Returns the crates in topological order. - /// - /// **Warning**: do not use this query in `hir-*` crates! It kills incrementality across crate metadata modifications. - #[salsa::input] - fn all_crates(&self) -> Arc<Box<[Crate]>>; -} - #[salsa_macros::db] pub trait SourceDatabase: salsa::Database { /// Text of the file. @@ -353,46 +322,67 @@ impl CrateWorkspaceData { } } -fn toolchain_channel(db: &dyn RootQueryDb, krate: Crate) -> Option<ReleaseChannel> { +pub fn toolchain_channel(db: &dyn salsa::Database, krate: Crate) -> Option<ReleaseChannel> { krate.workspace_data(db).toolchain.as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre)) } -fn parse(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Parse<ast::SourceFile> { - let _p = tracing::info_span!("parse", ?file_id).entered(); - let (file_id, edition) = file_id.unpack(db.as_dyn_database()); - let text = db.file_text(file_id).text(db); - ast::SourceFile::parse(text, edition) +#[salsa::input(singleton, debug)] +struct AllCrates { + crates: std::sync::Arc<[Crate]>, } -fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<&[SyntaxError]> { - #[salsa_macros::tracked(returns(ref))] - fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<Box<[SyntaxError]>> { - let errors = db.parse(file_id).errors(); - match &*errors { - [] => None, - [..] => Some(errors.into()), - } - } - parse_errors(db, file_id).as_ref().map(|it| &**it) +pub fn set_all_crates_with_durability( + db: &mut dyn salsa::Database, + crates: impl IntoIterator<Item = Crate>, + durability: Durability, +) { + AllCrates::try_get(db) + .unwrap_or_else(|| AllCrates::new(db, std::sync::Arc::default())) + .set_crates(db) + .with_durability(durability) + .to(crates.into_iter().collect()); } -fn source_root_crates(db: &dyn RootQueryDb, id: SourceRootId) -> Arc<[Crate]> { - let crates = db.all_crates(); - crates - .iter() - .copied() - .filter(|&krate| { - let root_file = krate.data(db).root_file_id; - db.file_source_root(root_file).source_root_id(db) == id - }) - .collect() +/// Returns the crates in topological order. +/// +/// **Warning**: do not use this query in `hir-*` crates! It kills incrementality across crate metadata modifications. +pub fn all_crates(db: &dyn salsa::Database) -> std::sync::Arc<[Crate]> { + AllCrates::try_get(db).map_or(std::sync::Arc::default(), |all_crates| all_crates.crates(db)) +} + +// FIXME: VFS rewrite should allow us to get rid of this wrapper +#[doc(hidden)] +#[salsa::interned] +pub struct InternedSourceRootId { + pub id: SourceRootId, +} + +/// Crates whose root file is in `id`. +pub fn source_root_crates(db: &dyn SourceDatabase, id: SourceRootId) -> &[Crate] { + #[salsa::tracked(returns(deref))] + pub fn source_root_crates<'db>( + db: &'db dyn SourceDatabase, + id: InternedSourceRootId<'db>, + ) -> Box<[Crate]> { + let crates = AllCrates::get(db).crates(db); + let id = id.id(db); + crates + .iter() + .copied() + .filter(|&krate| { + let root_file = krate.data(db).root_file_id; + db.file_source_root(root_file).source_root_id(db) == id + }) + .collect() + } + source_root_crates(db, InternedSourceRootId::new(db, id)) } -fn relevant_crates(db: &dyn RootQueryDb, file_id: FileId) -> Arc<[Crate]> { +pub fn relevant_crates(db: &dyn SourceDatabase, file_id: FileId) -> &[Crate] { let _p = tracing::info_span!("relevant_crates").entered(); let source_root = db.file_source_root(file_id); - db.source_root_crates(source_root.source_root_id(db)) + source_root_crates(db, source_root.source_root_id(db)) } #[must_use] diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml index cf2a7607b0..15de1f3293 100644 --- a/crates/cfg/Cargo.toml +++ b/crates/cfg/Cargo.toml @@ -19,7 +19,6 @@ tracing.workspace = true # locals deps tt = { workspace = true, optional = true } syntax = { workspace = true, optional = true } -span = { path = "../span", version = "0.0", optional = true } intern.workspace = true [dev-dependencies] @@ -36,7 +35,7 @@ cfg = { path = ".", default-features = false, features = ["tt"] } [features] default = [] -syntax = ["dep:syntax", "dep:span"] +syntax = ["dep:syntax"] tt = ["dep:tt"] in-rust-tree = [] diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index d253f6f492..7af3ed5dc9 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs @@ -106,10 +106,54 @@ impl CfgExpr { } #[cfg(feature = "syntax")] - pub fn parse_from_ast( - ast: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>, - ) -> CfgExpr { - next_cfg_expr_from_ast(ast).unwrap_or(CfgExpr::Invalid) + pub fn parse_from_ast(ast: syntax::ast::CfgPredicate) -> CfgExpr { + use intern::sym; + use syntax::ast::{self, AstToken}; + + match ast { + ast::CfgPredicate::CfgAtom(atom) => { + let atom = match atom.key() { + Some(ast::CfgAtomKey::True) => CfgAtom::Flag(sym::true_), + Some(ast::CfgAtomKey::False) => CfgAtom::Flag(sym::false_), + Some(ast::CfgAtomKey::Ident(key)) => { + let key = Symbol::intern(key.text()); + match atom.string_token().and_then(ast::String::cast) { + Some(value) => { + if let Ok(value) = value.value() { + CfgAtom::KeyValue { key, value: Symbol::intern(&value) } + } else { + return CfgExpr::Invalid; + } + } + None => CfgAtom::Flag(key), + } + } + None => return CfgExpr::Invalid, + }; + CfgExpr::Atom(atom) + } + ast::CfgPredicate::CfgComposite(composite) => { + let Some(keyword) = composite.keyword() else { + return CfgExpr::Invalid; + }; + match keyword.text() { + "all" => CfgExpr::All( + composite.cfg_predicates().map(CfgExpr::parse_from_ast).collect(), + ), + "any" => CfgExpr::Any( + composite.cfg_predicates().map(CfgExpr::parse_from_ast).collect(), + ), + "not" => { + let mut inner = composite.cfg_predicates(); + let (Some(inner), None) = (inner.next(), inner.next()) else { + return CfgExpr::Invalid; + }; + CfgExpr::Not(Box::new(CfgExpr::parse_from_ast(inner))) + } + _ => CfgExpr::Invalid, + } + } + } } /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. @@ -128,65 +172,6 @@ impl CfgExpr { } } -#[cfg(feature = "syntax")] -fn next_cfg_expr_from_ast( - it: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>, -) -> Option<CfgExpr> { - use intern::sym; - use syntax::{NodeOrToken, SyntaxKind, T, ast}; - - let name = match it.next() { - None => return None, - Some(NodeOrToken::Token(ident)) if ident.kind().is_any_identifier() => { - Symbol::intern(ident.text()) - } - Some(_) => return Some(CfgExpr::Invalid), - }; - - let ret = match it.peek() { - Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => { - it.next(); - if let Some(NodeOrToken::Token(literal)) = it.peek() - && matches!(literal.kind(), SyntaxKind::STRING) - { - let dummy_span = span::Span { - range: span::TextRange::empty(span::TextSize::new(0)), - anchor: span::SpanAnchor { - file_id: span::EditionedFileId::from_raw(0), - ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER, - }, - ctx: span::SyntaxContext::root(span::Edition::Edition2015), - }; - let literal = - Symbol::intern(tt::token_to_literal(literal.text(), dummy_span).text()); - it.next(); - CfgAtom::KeyValue { key: name, value: literal.clone() }.into() - } else { - return Some(CfgExpr::Invalid); - } - } - Some(NodeOrToken::Node(subtree)) => { - let mut subtree_iter = ast::TokenTreeChildren::new(subtree).peekable(); - it.next(); - let mut subs = std::iter::from_fn(|| next_cfg_expr_from_ast(&mut subtree_iter)); - match name { - s if s == sym::all => CfgExpr::All(subs.collect()), - s if s == sym::any => CfgExpr::Any(subs.collect()), - s if s == sym::not => { - CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid))) - } - _ => CfgExpr::Invalid, - } - } - _ => CfgAtom::Flag(name).into(), - }; - - // Eat comma separator - while it.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {} - - Some(ret) -} - #[cfg(feature = "tt")] fn next_cfg_expr(it: &mut tt::iter::TtIter<'_>) -> Option<CfgExpr> { use intern::sym; diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs index 52c581dbbd..bfc9220a05 100644 --- a/crates/cfg/src/tests.rs +++ b/crates/cfg/src/tests.rs @@ -1,10 +1,7 @@ use arbitrary::{Arbitrary, Unstructured}; use expect_test::{Expect, expect}; use intern::Symbol; -use syntax::{ - AstNode, Edition, - ast::{self, TokenTreeChildren}, -}; +use syntax::{AstNode, Edition, ast}; use syntax_bridge::{ DocCommentDesugarMode, dummy_test_span_utils::{DUMMY, DummyTestSpanMap}, @@ -14,32 +11,32 @@ use syntax_bridge::{ use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; #[track_caller] -fn parse_ast_cfg(tt: &ast::TokenTree) -> CfgExpr { - CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable()) +fn parse_ast_cfg(pred: ast::CfgPredicate) -> CfgExpr { + CfgExpr::parse_from_ast(pred) } #[track_caller] fn assert_parse_result(input: &str, expected: CfgExpr) { - let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); - let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let source_file = ast::SourceFile::parse(input, Edition::CURRENT).syntax_node(); + let pred_ast = source_file.descendants().find_map(ast::CfgPredicate::cast).unwrap(); let tt = syntax_node_to_token_tree( - tt_ast.syntax(), + pred_ast.syntax(), DummyTestSpanMap, DUMMY, DocCommentDesugarMode::ProcMacro, ); let cfg = CfgExpr::parse(&tt); assert_eq!(cfg, expected); - let cfg = parse_ast_cfg(&tt_ast); + let cfg = parse_ast_cfg(pred_ast); assert_eq!(cfg, expected); } #[track_caller] fn check_dnf(input: &str, expect: Expect) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); - let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap(); let tt = syntax_node_to_token_tree( - tt_ast.syntax(), + pred_ast.syntax(), DummyTestSpanMap, DUMMY, DocCommentDesugarMode::ProcMacro, @@ -47,7 +44,7 @@ fn check_dnf(input: &str, expect: Expect) { let cfg = CfgExpr::parse(&tt); let actual = format!("#![cfg({})]", DnfExpr::new(&cfg)); expect.assert_eq(&actual); - let cfg = parse_ast_cfg(&tt_ast); + let cfg = parse_ast_cfg(pred_ast); let actual = format!("#![cfg({})]", DnfExpr::new(&cfg)); expect.assert_eq(&actual); } @@ -55,9 +52,9 @@ fn check_dnf(input: &str, expect: Expect) { #[track_caller] fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); - let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap(); let tt = syntax_node_to_token_tree( - tt_ast.syntax(), + pred_ast.syntax(), DummyTestSpanMap, DUMMY, DocCommentDesugarMode::ProcMacro, @@ -66,7 +63,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { let dnf = DnfExpr::new(&cfg); let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); expect.assert_eq(&why_inactive); - let cfg = parse_ast_cfg(&tt_ast); + let cfg = parse_ast_cfg(pred_ast); let dnf = DnfExpr::new(&cfg); let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); expect.assert_eq(&why_inactive); @@ -75,9 +72,9 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { #[track_caller] fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); - let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap(); let tt = syntax_node_to_token_tree( - tt_ast.syntax(), + pred_ast.syntax(), DummyTestSpanMap, DUMMY, DocCommentDesugarMode::ProcMacro, @@ -86,7 +83,7 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { let dnf = DnfExpr::new(&cfg); let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>(); assert_eq!(hints, expected_hints); - let cfg = parse_ast_cfg(&tt_ast); + let cfg = parse_ast_cfg(pred_ast); let dnf = DnfExpr::new(&cfg); let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>(); assert_eq!(hints, expected_hints); @@ -119,20 +116,6 @@ fn test_cfg_expr_parser() { .into_boxed_slice(), ), ); - - assert_parse_result( - r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, - CfgExpr::Any( - vec![ - CfgExpr::Not(Box::new(CfgExpr::Invalid)), - CfgExpr::All(Box::new([])), - CfgExpr::Invalid, - CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") } - .into(), - ] - .into_boxed_slice(), - ), - ); } #[test] diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs index e3e1aac709..b560d08492 100644 --- a/crates/hir-def/src/attrs.rs +++ b/crates/hir-def/src/attrs.rs @@ -12,25 +12,17 @@ //! its value. This way, queries are only called on items that have the attribute, which is //! usually only a few. //! -//! An exception to this model that is also defined in this module is documentation (doc -//! comments and `#[doc = "..."]` attributes). But it also has a more compact form than -//! the attribute: a concatenated string of the full docs as well as a source map -//! to map it back to AST (which is needed for things like resolving links in doc comments -//! and highlight injection). The lowering and upmapping of doc comments is a bit complicated, -//! but it is encapsulated in the [`Docs`] struct. - -use std::{ - convert::Infallible, - iter::Peekable, - ops::{ControlFlow, Range}, -}; +//! Documentation (doc comments and `#[doc = "..."]` attributes) is handled by the [`docs`] +//! submodule. + +use std::{convert::Infallible, iter::Peekable, ops::ControlFlow}; use base_db::Crate; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ - HirFileId, InFile, Lookup, - attrs::{Meta, expand_cfg_attr, expand_cfg_attr_with_doc_comments}, + InFile, Lookup, + attrs::{AstKeyValueMetaExt, AstPathExt, expand_cfg_attr}, }; use intern::Symbol; use itertools::Itertools; @@ -39,10 +31,10 @@ use rustc_abi::ReprOptions; use rustc_hash::FxHashSet; use smallvec::SmallVec; use syntax::{ - AstNode, AstToken, NodeOrToken, SmolStr, SourceFile, SyntaxNode, SyntaxToken, T, - ast::{self, AttrDocCommentIter, HasAttrs, IsString, TokenTreeChildren}, + AstNode, AstToken, NodeOrToken, SmolStr, SourceFile, T, + ast::{self, HasAttrs, TokenTreeChildren}, }; -use tt::{TextRange, TextSize}; +use tt::TextSize; use crate::{ AdtId, AstIdLoc, AttrDefId, FieldId, FunctionId, GenericDefId, HasModule, LifetimeParamId, @@ -50,9 +42,14 @@ use crate::{ db::DefDatabase, hir::generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId}, nameres::ModuleOrigin, + resolver::{HasResolver, Resolver}, src::{HasChildSource, HasSource}, }; +pub mod docs; + +pub use self::docs::{Docs, IsInnerDoc}; + #[inline] fn attrs_from_ast_id_loc<N: AstNode + Into<ast::AnyHasAttrs>>( db: &dyn DefDatabase, @@ -131,63 +128,89 @@ fn extract_rustc_skip_during_method_dispatch(attr_flags: &mut AttrFlags, tt: ast } #[inline] -fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infallible> { +fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<Infallible> { match attr { - Meta::NamedKeyValue { name: Some(name), value, .. } => match name.text() { - "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), - "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE), - "lang" => attr_flags.insert(AttrFlags::LANG_ITEM), - "path" => attr_flags.insert(AttrFlags::HAS_PATH), - "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), - "export_name" => { - if let Some(value) = value - && let Some(value) = ast::String::cast(value) - && let Ok(value) = value.value() - && *value == *"main" - { - attr_flags.insert(AttrFlags::IS_EXPORT_NAME_MAIN); - } - } - _ => {} - }, - Meta::TokenTree { path, tt } => match path.segments.len() { - 1 => match path.segments[0].text() { + ast::Meta::CfgMeta(_) => attr_flags.insert(AttrFlags::HAS_CFG), + ast::Meta::KeyValueMeta(attr) => { + let Some(key) = attr.path().as_one_segment() else { return ControlFlow::Continue(()) }; + match &*key { "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), - "cfg" => attr_flags.insert(AttrFlags::HAS_CFG), - "doc" => extract_doc_tt_attr(attr_flags, tt), - "repr" => attr_flags.insert(AttrFlags::HAS_REPR), - "target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE), - "proc_macro_derive" | "rustc_builtin_macro" => { - attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO) - } + "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE), + "lang" => attr_flags.insert(AttrFlags::LANG_ITEM), + "path" => attr_flags.insert(AttrFlags::HAS_PATH), "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), - "rustc_layout_scalar_valid_range_start" | "rustc_layout_scalar_valid_range_end" => { - attr_flags.insert(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE) - } - "rustc_legacy_const_generics" => { - attr_flags.insert(AttrFlags::HAS_LEGACY_CONST_GENERICS) - } - "rustc_skip_during_method_dispatch" => { - extract_rustc_skip_during_method_dispatch(attr_flags, tt) - } - "rustc_deprecated_safe_2024" => { - attr_flags.insert(AttrFlags::RUSTC_DEPRECATED_SAFE_2024) + "export_name" => { + if let Some(value) = attr.value_string() + && *value == *"main" + { + attr_flags.insert(AttrFlags::IS_EXPORT_NAME_MAIN); + } } _ => {} - }, - 2 => match path.segments[0].text() { - "rust_analyzer" => match path.segments[1].text() { - "completions" => extract_ra_completions(attr_flags, tt), - "macro_style" => extract_ra_macro_style(attr_flags, tt), + } + } + ast::Meta::TokenTreeMeta(attr) => { + let (Some((first_segment, second_segment)), Some(tt)) = + (attr.path().as_up_to_two_segment(), attr.token_tree()) + else { + return ControlFlow::Continue(()); + }; + match second_segment { + None => match &*first_segment { + "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), + "doc" => extract_doc_tt_attr(attr_flags, tt), + "repr" => attr_flags.insert(AttrFlags::HAS_REPR), + "target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE), + "proc_macro_derive" | "rustc_builtin_macro" => { + attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO) + } + "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), + "rustc_layout_scalar_valid_range_start" + | "rustc_layout_scalar_valid_range_end" => { + attr_flags.insert(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE) + } + "rustc_legacy_const_generics" => { + attr_flags.insert(AttrFlags::HAS_LEGACY_CONST_GENERICS) + } + "rustc_skip_during_method_dispatch" => { + extract_rustc_skip_during_method_dispatch(attr_flags, tt) + } + "rustc_deprecated_safe_2024" => { + attr_flags.insert(AttrFlags::RUSTC_DEPRECATED_SAFE_2024) + } _ => {} }, - _ => {} - }, - _ => {} - }, - Meta::Path { path } => { - match path.segments.len() { - 1 => match path.segments[0].text() { + Some(second_segment) => match &*first_segment { + "rust_analyzer" => match &*second_segment { + "completions" => extract_ra_completions(attr_flags, tt), + "macro_style" => extract_ra_macro_style(attr_flags, tt), + _ => {} + }, + _ => {} + }, + } + } + ast::Meta::PathMeta(attr) => { + let is_test = attr.path().is_some_and(|path| { + let Some(segment1) = (|| path.segment()?.name_ref())() else { return false }; + let segment2 = path.qualifier(); + let segment3 = segment2.as_ref().and_then(|it| it.qualifier()); + let segment4 = segment3.as_ref().and_then(|it| it.qualifier()); + let segment3 = segment3.and_then(|it| it.segment()?.name_ref()); + let segment4 = segment4.and_then(|it| it.segment()?.name_ref()); + segment1.text() == "test" + && segment3.is_none_or(|it| it.text() == "prelude") + && segment4.is_none_or(|it| it.text() == "core") + }); + if is_test { + attr_flags.insert(AttrFlags::IS_TEST); + } + + let Some((first_segment, second_segment)) = attr.path().as_up_to_two_segment() else { + return ControlFlow::Continue(()); + }; + match second_segment { + None => match &*first_segment { "rustc_has_incoherent_inherent_impls" => { attr_flags.insert(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) } @@ -231,18 +254,13 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal } _ => {} }, - 2 => match path.segments[0].text() { - "rust_analyzer" => match path.segments[1].text() { + Some(second_segment) => match &*first_segment { + "rust_analyzer" => match &*second_segment { "skip" => attr_flags.insert(AttrFlags::RUST_ANALYZER_SKIP), _ => {} }, _ => {} }, - _ => {} - } - - if path.is_test { - attr_flags.insert(AttrFlags::IS_TEST); } } _ => {} @@ -354,13 +372,13 @@ fn attrs_source( let krate = def_map.krate(); let (definition, declaration, extra_crate_attrs) = match def_map[id].origin { ModuleOrigin::CrateRoot { definition } => { - let definition_source = db.parse(definition).tree(); + let definition_source = definition.parse(db).tree(); let definition = InFile::new(definition.into(), definition_source.into()); let extra_crate_attrs = parse_extra_crate_attrs(db, krate); (definition, None, extra_crate_attrs) } ModuleOrigin::File { declaration, declaration_tree_id, definition, .. } => { - let definition_source = db.parse(definition).tree(); + let definition_source = definition.parse(db).tree(); let definition = InFile::new(definition.into(), definition_source.into()); let declaration = InFile::new(declaration_tree_id.file_id(), declaration); let declaration = declaration.with_value(declaration.to_node(db)); @@ -398,10 +416,32 @@ fn attrs_source( (owner, None, None, krate) } +fn resolver_for_attr_def_id(db: &dyn DefDatabase, owner: AttrDefId) -> Resolver<'_> { + match owner { + AttrDefId::ModuleId(id) => id.resolver(db), + AttrDefId::AdtId(AdtId::StructId(id)) => id.resolver(db), + AttrDefId::AdtId(AdtId::UnionId(id)) => id.resolver(db), + AttrDefId::AdtId(AdtId::EnumId(id)) => id.resolver(db), + AttrDefId::FunctionId(id) => id.resolver(db), + AttrDefId::EnumVariantId(id) => id.resolver(db), + AttrDefId::StaticId(id) => id.resolver(db), + AttrDefId::ConstId(id) => id.resolver(db), + AttrDefId::TraitId(id) => id.resolver(db), + AttrDefId::TypeAliasId(id) => id.resolver(db), + AttrDefId::MacroId(MacroId::Macro2Id(id)) => id.resolver(db), + AttrDefId::MacroId(MacroId::MacroRulesId(id)) => id.resolver(db), + AttrDefId::MacroId(MacroId::ProcMacroId(id)) => id.resolver(db), + AttrDefId::ImplId(id) => id.resolver(db), + AttrDefId::ExternBlockId(id) => id.resolver(db), + AttrDefId::ExternCrateId(id) => id.resolver(db), + AttrDefId::UseId(id) => id.resolver(db), + } +} + fn collect_attrs<BreakValue>( db: &dyn DefDatabase, owner: AttrDefId, - mut callback: impl FnMut(Meta) -> ControlFlow<BreakValue>, + mut callback: impl FnMut(ast::Meta) -> ControlFlow<BreakValue>, ) -> Option<BreakValue> { let (source, outer_mod_decl, extra_crate_attrs, krate) = attrs_source(db, owner); let extra_attrs = extra_crate_attrs @@ -413,7 +453,7 @@ fn collect_attrs<BreakValue>( expand_cfg_attr( extra_attrs.chain(ast::attrs_including_inner(&source.value)), || cfg_options.get_or_insert_with(|| krate.cfg_options(db)), - move |meta, _, _, _| callback(meta), + move |meta, _| callback(meta), ) } @@ -475,291 +515,16 @@ pub struct RustcLayoutScalarValidRange { pub end: Option<u128>, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -struct DocsSourceMapLine { - /// The offset in [`Docs::docs`]. - string_offset: TextSize, - /// The offset in the AST of the text. - ast_offset: TextSize, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Docs { - /// The concatenated string of all `#[doc = "..."]` attributes and documentation comments. - docs: String, - /// A sorted map from an offset in `docs` to an offset in the source code. - docs_source_map: Vec<DocsSourceMapLine>, - /// If the item is an outlined module (`mod foo;`), `docs_source_map` store the concatenated - /// list of the outline and inline docs (outline first). Then, this field contains the [`HirFileId`] - /// of the outline declaration, and the index in `docs` from which the inline docs - /// begin. - outline_mod: Option<(HirFileId, usize)>, - inline_file: HirFileId, - /// The size the prepended prefix, which does not map to real doc comments. - prefix_len: TextSize, - /// The offset in `docs` from which the docs are inner attributes/comments. - inline_inner_docs_start: Option<TextSize>, - /// Like `inline_inner_docs_start`, but for `outline_mod`. This can happen only when merging `Docs` - /// (as outline modules don't have inner attributes). - outline_inner_docs_start: Option<TextSize>, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum IsInnerDoc { - No, - Yes, -} - -impl IsInnerDoc { - #[inline] - pub fn yes(self) -> bool { - self == IsInnerDoc::Yes - } -} - -impl Docs { - #[inline] - pub fn docs(&self) -> &str { - &self.docs - } - - #[inline] - pub fn into_docs(self) -> String { - self.docs - } - - pub fn find_ast_range( - &self, - mut string_range: TextRange, - ) -> Option<(InFile<TextRange>, IsInnerDoc)> { - if string_range.start() < self.prefix_len { - return None; - } - string_range -= self.prefix_len; - - let mut file = self.inline_file; - let mut inner_docs_start = self.inline_inner_docs_start; - // Check whether the range is from the outline, the inline, or both. - let source_map = if let Some((outline_mod_file, outline_mod_end)) = self.outline_mod { - if let Some(first_inline) = self.docs_source_map.get(outline_mod_end) { - if string_range.end() <= first_inline.string_offset { - // The range is completely in the outline. - file = outline_mod_file; - inner_docs_start = self.outline_inner_docs_start; - &self.docs_source_map[..outline_mod_end] - } else if string_range.start() >= first_inline.string_offset { - // The range is completely in the inline. - &self.docs_source_map[outline_mod_end..] - } else { - // The range is combined from the outline and the inline - cannot map it back. - return None; - } - } else { - // There is no inline. - file = outline_mod_file; - inner_docs_start = self.outline_inner_docs_start; - &self.docs_source_map - } - } else { - // There is no outline. - &self.docs_source_map - }; - - let after_range = - source_map.partition_point(|line| line.string_offset <= string_range.start()) - 1; - let after_range = &source_map[after_range..]; - let line = after_range.first()?; - if after_range.get(1).is_some_and(|next_line| next_line.string_offset < string_range.end()) - { - // The range is combined from two lines - cannot map it back. - return None; - } - let ast_range = string_range - line.string_offset + line.ast_offset; - let is_inner = if inner_docs_start - .is_some_and(|inner_docs_start| string_range.start() >= inner_docs_start) - { - IsInnerDoc::Yes - } else { - IsInnerDoc::No - }; - Some((InFile::new(file, ast_range), is_inner)) - } - - #[inline] - pub fn shift_by(&mut self, offset: TextSize) { - self.prefix_len += offset; - } - - pub fn prepend_str(&mut self, s: &str) { - self.prefix_len += TextSize::of(s); - self.docs.insert_str(0, s); - } - - pub fn append_str(&mut self, s: &str) { - self.docs.push_str(s); - } - - pub fn append(&mut self, other: &Docs) { - let other_offset = TextSize::of(&self.docs); - - assert!( - self.outline_mod.is_none() && other.outline_mod.is_none(), - "cannot merge `Docs` that have `outline_mod` set" - ); - self.outline_mod = Some((self.inline_file, self.docs_source_map.len())); - self.inline_file = other.inline_file; - self.outline_inner_docs_start = self.inline_inner_docs_start; - self.inline_inner_docs_start = other.inline_inner_docs_start.map(|it| it + other_offset); - - self.docs.push_str(&other.docs); - self.docs_source_map.extend(other.docs_source_map.iter().map( - |&DocsSourceMapLine { string_offset, ast_offset }| DocsSourceMapLine { - ast_offset, - string_offset: string_offset + other_offset, - }, - )); - } - - fn extend_with_doc_comment(&mut self, comment: ast::Comment, indent: &mut usize) { - let Some((doc, offset)) = comment.doc_comment() else { return }; - self.extend_with_doc_str(doc, comment.syntax().text_range().start() + offset, indent); - } - - fn extend_with_doc_attr(&mut self, value: SyntaxToken, indent: &mut usize) { - let Some(value) = ast::String::cast(value) else { return }; - let Some(value_offset) = value.text_range_between_quotes() else { return }; - let value_offset = value_offset.start(); - let Ok(value) = value.value() else { return }; - // FIXME: Handle source maps for escaped text. - self.extend_with_doc_str(&value, value_offset, indent); - } - - fn extend_with_doc_str(&mut self, doc: &str, mut offset_in_ast: TextSize, indent: &mut usize) { - for line in doc.split('\n') { - self.docs_source_map.push(DocsSourceMapLine { - string_offset: TextSize::of(&self.docs), - ast_offset: offset_in_ast, - }); - offset_in_ast += TextSize::of(line) + TextSize::of("\n"); - - let line = line.trim_end(); - if let Some(line_indent) = line.chars().position(|ch| !ch.is_whitespace()) { - // Empty lines are handled because `position()` returns `None` for them. - *indent = std::cmp::min(*indent, line_indent); - } - self.docs.push_str(line); - self.docs.push('\n'); - } - } - - fn remove_indent(&mut self, indent: usize, start_source_map_index: usize) { - /// In case of panics, we want to avoid corrupted UTF-8 in `self.docs`, so we clear it. - struct Guard<'a>(&'a mut Docs); - impl Drop for Guard<'_> { - fn drop(&mut self) { - let Docs { - docs, - docs_source_map, - outline_mod, - inline_file: _, - prefix_len: _, - inline_inner_docs_start: _, - outline_inner_docs_start: _, - } = self.0; - // Don't use `String::clear()` here because it's not guaranteed to not do UTF-8-dependent things, - // and we may have temporarily broken the string's encoding. - unsafe { docs.as_mut_vec() }.clear(); - // This is just to avoid panics down the road. - docs_source_map.clear(); - *outline_mod = None; - } - } - - if self.docs.is_empty() { - return; - } - - let guard = Guard(self); - let source_map = &mut guard.0.docs_source_map[start_source_map_index..]; - let Some(&DocsSourceMapLine { string_offset: mut copy_into, .. }) = source_map.first() - else { - return; - }; - // We basically want to remove multiple ranges from a string. Doing this efficiently (without O(N^2) - // or allocations) requires unsafe. Basically, for each line, we copy the line minus the indent into - // consecutive to the previous line (which may have moved). Then at the end we truncate. - let mut accumulated_offset = TextSize::new(0); - for idx in 0..source_map.len() { - let string_end_offset = source_map - .get(idx + 1) - .map_or_else(|| TextSize::of(&guard.0.docs), |next_attr| next_attr.string_offset); - let line_source = &mut source_map[idx]; - let line_docs = - &guard.0.docs[TextRange::new(line_source.string_offset, string_end_offset)]; - let line_docs_len = TextSize::of(line_docs); - let indent_size = line_docs.char_indices().nth(indent).map_or_else( - || TextSize::of(line_docs) - TextSize::of("\n"), - |(offset, _)| TextSize::new(offset as u32), - ); - unsafe { guard.0.docs.as_bytes_mut() }.copy_within( - Range::<usize>::from(TextRange::new( - line_source.string_offset + indent_size, - string_end_offset, - )), - copy_into.into(), - ); - copy_into += line_docs_len - indent_size; - - if let Some(inner_attrs_start) = &mut guard.0.inline_inner_docs_start - && *inner_attrs_start == line_source.string_offset - { - *inner_attrs_start -= accumulated_offset; - } - // The removals in the string accumulate, but in the AST not, because it already points - // to the beginning of each attribute. - // Also, we need to shift the AST offset of every line, but the string offset of the first - // line should not get shifted (in general, the shift for the string offset is by the - // number of lines until the current one, excluding the current one). - line_source.string_offset -= accumulated_offset; - line_source.ast_offset += indent_size; - - accumulated_offset += indent_size; - } - // Don't use `String::truncate()` here because it's not guaranteed to not do UTF-8-dependent things, - // and we may have temporarily broken the string's encoding. - unsafe { guard.0.docs.as_mut_vec() }.truncate(copy_into.into()); - - std::mem::forget(guard); - } - - fn remove_last_newline(&mut self) { - self.docs.truncate(self.docs.len().saturating_sub(1)); - } - - fn shrink_to_fit(&mut self) { - let Docs { - docs, - docs_source_map, - outline_mod: _, - inline_file: _, - prefix_len: _, - inline_inner_docs_start: _, - outline_inner_docs_start: _, - } = self; - docs.shrink_to_fit(); - docs_source_map.shrink_to_fit(); - } -} - #[derive(Debug, PartialEq, Eq, Hash)] pub struct DeriveInfo { pub trait_name: Symbol, pub helpers: Box<[Symbol]>, } -fn extract_doc_aliases(result: &mut Vec<Symbol>, attr: Meta) -> ControlFlow<Infallible> { - if let Meta::TokenTree { path, tt } = attr - && path.is1("doc") +fn extract_doc_aliases(result: &mut Vec<Symbol>, attr: ast::Meta) -> ControlFlow<Infallible> { + if let ast::Meta::TokenTreeMeta(attr) = attr + && attr.path().is1("doc") + && let Some(tt) = attr.token_tree() { for atom in DocAtom::parse(tt) { match atom { @@ -776,85 +541,15 @@ fn extract_doc_aliases(result: &mut Vec<Symbol>, attr: Meta) -> ControlFlow<Infa ControlFlow::Continue(()) } -fn extract_cfgs(result: &mut Vec<CfgExpr>, attr: Meta) -> ControlFlow<Infallible> { - if let Meta::TokenTree { path, tt } = attr - && path.is1("cfg") +fn extract_cfgs(result: &mut Vec<CfgExpr>, attr: ast::Meta) -> ControlFlow<Infallible> { + if let ast::Meta::CfgMeta(attr) = attr + && let Some(cfg_predicate) = attr.cfg_predicate() { - result.push(CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable())); + result.push(CfgExpr::parse_from_ast(cfg_predicate)); } ControlFlow::Continue(()) } -fn extract_docs<'a>( - get_cfg_options: &dyn Fn() -> &'a CfgOptions, - source: InFile<ast::AnyHasAttrs>, - outer_mod_decl: Option<InFile<ast::Module>>, - inner_attrs_node: Option<SyntaxNode>, -) -> Option<Box<Docs>> { - let mut result = Docs { - docs: String::new(), - docs_source_map: Vec::new(), - outline_mod: None, - inline_file: source.file_id, - prefix_len: TextSize::new(0), - inline_inner_docs_start: None, - outline_inner_docs_start: None, - }; - - let mut cfg_options = None; - let mut extend_with_attrs = - |result: &mut Docs, node: &SyntaxNode, expect_inner_attrs, indent: &mut usize| { - expand_cfg_attr_with_doc_comments::<_, Infallible>( - AttrDocCommentIter::from_syntax_node(node).filter(|attr| match attr { - Either::Left(attr) => attr.kind().is_inner() == expect_inner_attrs, - Either::Right(comment) => comment.kind().doc.is_some_and(|kind| { - (kind == ast::CommentPlacement::Inner) == expect_inner_attrs - }), - }), - || cfg_options.get_or_insert_with(get_cfg_options), - |attr| { - match attr { - Either::Right(doc_comment) => { - result.extend_with_doc_comment(doc_comment, indent) - } - Either::Left((attr, _, _, _)) => match attr { - // FIXME: Handle macros: `#[doc = concat!("foo", "bar")]`. - Meta::NamedKeyValue { - name: Some(name), value: Some(value), .. - } if name.text() == "doc" => { - result.extend_with_doc_attr(value, indent); - } - _ => {} - }, - } - ControlFlow::Continue(()) - }, - ); - }; - - if let Some(outer_mod_decl) = outer_mod_decl { - let mut indent = usize::MAX; - extend_with_attrs(&mut result, outer_mod_decl.value.syntax(), false, &mut indent); - result.remove_indent(indent, 0); - result.outline_mod = Some((outer_mod_decl.file_id, result.docs_source_map.len())); - } - - let inline_source_map_start = result.docs_source_map.len(); - let mut indent = usize::MAX; - extend_with_attrs(&mut result, source.value.syntax(), false, &mut indent); - if let Some(inner_attrs_node) = &inner_attrs_node { - result.inline_inner_docs_start = Some(TextSize::of(&result.docs)); - extend_with_attrs(&mut result, inner_attrs_node, true, &mut indent); - } - result.remove_indent(indent, inline_source_map_start); - - result.remove_last_newline(); - - result.shrink_to_fit(); - - if result.docs.is_empty() { None } else { Some(Box::new(result)) } -} - #[salsa::tracked] impl AttrFlags { #[salsa::tracked] @@ -881,7 +576,7 @@ impl AttrFlags { expand_cfg_attr( field.value.attrs(), || cfg_options, - |attr, _, _, _| match_attr_flags(&mut attr_flags, attr), + |attr, _| match_attr_flags(&mut attr_flags, attr), ); attr_flags }) @@ -918,7 +613,7 @@ impl AttrFlags { let lifetimes_source = HasChildSource::<LocalLifetimeParamId>::child_source(&def, db); for (lifetime_id, lifetime) in lifetimes_source.value.iter() { let mut attr_flags = AttrFlags::empty(); - expand_cfg_attr(lifetime.attrs(), &mut cfg_options, |attr, _, _, _| { + expand_cfg_attr(lifetime.attrs(), &mut cfg_options, |attr, _| { match_attr_flags(&mut attr_flags, attr) }); if !attr_flags.is_empty() { @@ -930,7 +625,7 @@ impl AttrFlags { HasChildSource::<LocalTypeOrConstParamId>::child_source(&def, db); for (type_or_const_id, type_or_const) in type_and_consts_source.value.iter() { let mut attr_flags = AttrFlags::empty(); - expand_cfg_attr(type_or_const.attrs(), &mut cfg_options, |attr, _, _, _| { + expand_cfg_attr(type_or_const.attrs(), &mut cfg_options, |attr, _| { match_attr_flags(&mut attr_flags, attr) }); if !attr_flags.is_empty() { @@ -969,11 +664,10 @@ impl AttrFlags { let result = expand_cfg_attr( attrs, || cfg_options, - |attr, _, _, _| { - if let Meta::TokenTree { path, tt } = attr - && path.is1("cfg") - && let cfg = - CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable()) + |attr, _| { + if let ast::Meta::CfgMeta(attr) = attr + && let Some(cfg_predicate) = attr.cfg_predicate() + && let cfg = CfgExpr::parse_from_ast(cfg_predicate) && cfg_options.check(&cfg) == Some(false) { ControlFlow::Break(cfg) @@ -1005,10 +699,9 @@ impl AttrFlags { #[salsa::tracked] fn lang_item(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> { collect_attrs(db, owner, |attr| { - if let Meta::NamedKeyValue { name: Some(name), value: Some(value), .. } = attr - && name.text() == "lang" - && let Some(value) = ast::String::cast(value) - && let Ok(value) = value.value() + if let ast::Meta::KeyValueMeta(attr) = attr + && attr.path().is1("lang") + && let Some(value) = attr.value_string() { ControlFlow::Break(Symbol::intern(&value)) } else { @@ -1031,8 +724,9 @@ impl AttrFlags { fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option<ReprOptions> { let mut result = None; collect_attrs::<Infallible>(db, owner.into(), |attr| { - if let Meta::TokenTree { path, tt } = attr - && path.is1("repr") + if let ast::Meta::TokenTreeMeta(attr) = attr + && attr.path().is1("repr") + && let Some(tt) = attr.token_tree() && let Some(repr) = parse_repr_tt(&tt) { match &mut result { @@ -1053,8 +747,9 @@ impl AttrFlags { owner: FunctionId, ) -> Option<Box<[u32]>> { let result = collect_attrs(db, owner.into(), |attr| { - if let Meta::TokenTree { path, tt } = attr - && path.is1("rustc_legacy_const_generics") + if let ast::Meta::TokenTreeMeta(attr) = attr + && attr.path().is1("rustc_legacy_const_generics") + && let Some(tt) = attr.token_tree() { let result = parse_rustc_legacy_const_generics(tt); ControlFlow::Break(result) @@ -1069,7 +764,7 @@ impl AttrFlags { #[salsa::tracked(returns(ref))] pub fn doc_html_root_url(db: &dyn DefDatabase, krate: Crate) -> Option<SmolStr> { let root_file_id = krate.root_file_id(db); - let syntax = db.parse(root_file_id).tree(); + let syntax = root_file_id.parse(db).tree(); let extra_crate_attrs = parse_extra_crate_attrs(db, krate).into_iter().flat_map(|src| src.attrs()); @@ -1077,9 +772,10 @@ impl AttrFlags { expand_cfg_attr( extra_crate_attrs.chain(syntax.attrs()), || cfg_options.get_or_insert(krate.cfg_options(db)), - |attr, _, _, _| { - if let Meta::TokenTree { path, tt } = attr - && path.is1("doc") + |attr, _| { + if let ast::Meta::TokenTreeMeta(attr) = attr + && attr.path().is1("doc") + && let Some(tt) = attr.token_tree() && let Some(result) = DocAtom::parse(tt).into_iter().find_map(|atom| { if let DocAtom::KeyValue { key, value } = atom && key == "html_root_url" @@ -1110,8 +806,9 @@ impl AttrFlags { fn target_features(db: &dyn DefDatabase, owner: FunctionId) -> FxHashSet<Symbol> { let mut result = FxHashSet::default(); collect_attrs::<Infallible>(db, owner.into(), |attr| { - if let Meta::TokenTree { path, tt } = attr - && path.is1("target_feature") + if let ast::Meta::TokenTreeMeta(attr) = attr + && attr.path().is1("target_feature") + && let Some(tt) = attr.token_tree() { let mut tt = TokenTreeChildren::new(&tt); while let Some(NodeOrToken::Token(enable_ident)) = tt.next() @@ -1158,9 +855,11 @@ impl AttrFlags { ) -> RustcLayoutScalarValidRange { let mut result = RustcLayoutScalarValidRange::default(); collect_attrs::<Infallible>(db, owner.into(), |attr| { - if let Meta::TokenTree { path, tt } = attr + if let ast::Meta::TokenTreeMeta(attr) = attr + && let path = attr.path() && (path.is1("rustc_layout_scalar_valid_range_start") || path.is1("rustc_layout_scalar_valid_range_end")) + && let Some(tt) = attr.token_tree() && let tt = TokenTreeChildren::new(&tt) && let Ok(NodeOrToken::Token(value)) = Itertools::exactly_one(tt) && let Some(value) = ast::IntNumber::cast(value) @@ -1208,7 +907,7 @@ impl AttrFlags { expand_cfg_attr( field.value.attrs(), || cfg_options, - |attr, _, _, _| extract_doc_aliases(&mut result, attr), + |attr, _| extract_doc_aliases(&mut result, attr), ); result.into_boxed_slice() }) @@ -1250,7 +949,7 @@ impl AttrFlags { expand_cfg_attr( field.value.attrs(), || cfg_options, - |attr, _, _, _| extract_cfgs(&mut result, attr), + |attr, _| extract_cfgs(&mut result, attr), ); match result.len() { 0 => None, @@ -1271,8 +970,9 @@ impl AttrFlags { #[salsa::tracked] fn doc_keyword(db: &dyn DefDatabase, owner: ModuleId) -> Option<Symbol> { collect_attrs(db, AttrDefId::ModuleId(owner), |attr| { - if let Meta::TokenTree { path, tt } = attr - && path.is1("doc") + if let ast::Meta::TokenTreeMeta(attr) = attr + && attr.path().is1("doc") + && let Some(tt) = attr.token_tree() { for atom in DocAtom::parse(tt) { if let DocAtom::KeyValue { key, value } = atom @@ -1295,7 +995,15 @@ impl AttrFlags { // Note: we don't have to pass down `_extra_crate_attrs` here, since `extract_docs` // does not handle crate-level attributes related to docs. // See: https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#at-the-crate-level - extract_docs(&|| krate.cfg_options(db), source, outer_mod_decl, inner_attrs_node) + self::docs::extract_docs( + db, + krate, + &|| resolver_for_attr_def_id(db, owner), + &|| krate.cfg_options(db), + source, + outer_mod_decl, + inner_attrs_node, + ) } #[inline] @@ -1308,8 +1016,17 @@ impl AttrFlags { db: &dyn DefDatabase, variant: VariantId, ) -> ArenaMap<LocalFieldId, Option<Box<Docs>>> { + let krate = variant.module(db).krate(db); collect_field_attrs(db, variant, |cfg_options, field| { - extract_docs(&|| cfg_options, field, None, None) + self::docs::extract_docs( + db, + krate, + &|| variant.resolver(db), + &|| cfg_options, + field, + None, + None, + ) }) } } @@ -1325,12 +1042,10 @@ impl AttrFlags { #[salsa::tracked(returns(ref))] fn derive_info(db: &dyn DefDatabase, owner: MacroId) -> Option<DeriveInfo> { collect_attrs(db, owner.into(), |attr| { - if let Meta::TokenTree { path, tt } = attr - && path.segments.len() == 1 - && matches!( - path.segments[0].text(), - "proc_macro_derive" | "rustc_builtin_macro" - ) + if let ast::Meta::TokenTreeMeta(attr) = attr + && (attr.path().is1("proc_macro_derive") + || attr.path().is1("rustc_builtin_macro")) + && let Some(tt) = attr.token_tree() && let mut tt = TokenTreeChildren::new(&tt) && let Some(NodeOrToken::Token(trait_name)) = tt.next() && trait_name.kind().is_any_identifier() @@ -1537,151 +1252,13 @@ fn next_doc_expr(it: &mut Peekable<TokenTreeChildren>) -> Option<DocAtom> { #[cfg(test)] mod tests { - use expect_test::expect; - use hir_expand::InFile; use test_fixture::WithFixture; - use tt::{TextRange, TextSize}; use crate::AttrDefId; - use crate::attrs::{AttrFlags, Docs, IsInnerDoc}; + use crate::attrs::AttrFlags; use crate::test_db::TestDB; #[test] - fn docs() { - let (_db, file_id) = TestDB::with_single_file(""); - let mut docs = Docs { - docs: String::new(), - docs_source_map: Vec::new(), - outline_mod: None, - inline_file: file_id.into(), - prefix_len: TextSize::new(0), - inline_inner_docs_start: None, - outline_inner_docs_start: None, - }; - let mut indent = usize::MAX; - - let outer = " foo\n\tbar baz"; - let mut ast_offset = TextSize::new(123); - for line in outer.split('\n') { - docs.extend_with_doc_str(line, ast_offset, &mut indent); - ast_offset += TextSize::of(line) + TextSize::of("\n"); - } - - docs.inline_inner_docs_start = Some(TextSize::of(&docs.docs)); - ast_offset += TextSize::new(123); - let inner = " bar \n baz"; - for line in inner.split('\n') { - docs.extend_with_doc_str(line, ast_offset, &mut indent); - ast_offset += TextSize::of(line) + TextSize::of("\n"); - } - - assert_eq!(indent, 1); - expect![[r#" - [ - DocsSourceMapLine { - string_offset: 0, - ast_offset: 123, - }, - DocsSourceMapLine { - string_offset: 5, - ast_offset: 128, - }, - DocsSourceMapLine { - string_offset: 15, - ast_offset: 261, - }, - DocsSourceMapLine { - string_offset: 20, - ast_offset: 267, - }, - ] - "#]] - .assert_debug_eq(&docs.docs_source_map); - - docs.remove_indent(indent, 0); - - assert_eq!(docs.inline_inner_docs_start, Some(TextSize::new(13))); - - assert_eq!(docs.docs, "foo\nbar baz\nbar\nbaz\n"); - expect![[r#" - [ - DocsSourceMapLine { - string_offset: 0, - ast_offset: 124, - }, - DocsSourceMapLine { - string_offset: 4, - ast_offset: 129, - }, - DocsSourceMapLine { - string_offset: 13, - ast_offset: 262, - }, - DocsSourceMapLine { - string_offset: 17, - ast_offset: 268, - }, - ] - "#]] - .assert_debug_eq(&docs.docs_source_map); - - docs.append(&docs.clone()); - docs.prepend_str("prefix---"); - assert_eq!(docs.docs, "prefix---foo\nbar baz\nbar\nbaz\nfoo\nbar baz\nbar\nbaz\n"); - expect![[r#" - [ - DocsSourceMapLine { - string_offset: 0, - ast_offset: 124, - }, - DocsSourceMapLine { - string_offset: 4, - ast_offset: 129, - }, - DocsSourceMapLine { - string_offset: 13, - ast_offset: 262, - }, - DocsSourceMapLine { - string_offset: 17, - ast_offset: 268, - }, - DocsSourceMapLine { - string_offset: 21, - ast_offset: 124, - }, - DocsSourceMapLine { - string_offset: 25, - ast_offset: 129, - }, - DocsSourceMapLine { - string_offset: 34, - ast_offset: 262, - }, - DocsSourceMapLine { - string_offset: 38, - ast_offset: 268, - }, - ] - "#]] - .assert_debug_eq(&docs.docs_source_map); - - let range = |start, end| TextRange::new(TextSize::new(start), TextSize::new(end)); - let in_file = |range| InFile::new(file_id.into(), range); - assert_eq!(docs.find_ast_range(range(0, 2)), None); - assert_eq!(docs.find_ast_range(range(8, 10)), None); - assert_eq!( - docs.find_ast_range(range(9, 10)), - Some((in_file(range(124, 125)), IsInnerDoc::No)) - ); - assert_eq!(docs.find_ast_range(range(20, 23)), None); - assert_eq!( - docs.find_ast_range(range(23, 25)), - Some((in_file(range(263, 265)), IsInnerDoc::Yes)) - ); - } - - #[test] fn crate_attrs() { let fixture = r#" //- /lib.rs crate:foo crate-attr:no_std crate-attr:cfg(target_arch="x86") diff --git a/crates/hir-def/src/attrs/docs.rs b/crates/hir-def/src/attrs/docs.rs new file mode 100644 index 0000000000..9a715b1968 --- /dev/null +++ b/crates/hir-def/src/attrs/docs.rs @@ -0,0 +1,736 @@ +//! Documentation extraction and source mapping. +//! +//! This module handles the extraction and processing of doc comments and `#[doc = "..."]` +//! attributes, including macro expansion for `#[doc = macro!()]` patterns. +//! It builds a concatenated string of the full docs as well as a source map +//! to map it back to AST (which is needed for things like resolving links in doc comments +//! and highlight injection). + +use std::{ + convert::Infallible, + ops::{ControlFlow, Range}, +}; + +use base_db::Crate; +use cfg::CfgOptions; +use either::Either; +use hir_expand::{ + AstId, ExpandTo, HirFileId, InFile, + attrs::{AstPathExt, expand_cfg_attr_with_doc_comments}, + mod_path::ModPath, + span_map::SpanMap, +}; +use span::AstIdMap; +use syntax::{ + AstNode, AstToken, SyntaxNode, + ast::{self, AttrDocCommentIter, IsString}, +}; +use tt::{TextRange, TextSize}; + +use crate::{db::DefDatabase, macro_call_as_call_id, nameres::MacroSubNs, resolver::Resolver}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct DocsSourceMapLine { + /// The offset in [`Docs::docs`]. + string_offset: TextSize, + /// The offset in the AST of the text. `None` for macro-expanded doc strings + /// where we cannot provide a faithful source mapping. + ast_offset: Option<TextSize>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Docs { + /// The concatenated string of all `#[doc = "..."]` attributes and documentation comments. + docs: String, + /// A sorted map from an offset in `docs` to an offset in the source code. + docs_source_map: Vec<DocsSourceMapLine>, + /// If the item is an outlined module (`mod foo;`), `docs_source_map` store the concatenated + /// list of the outline and inline docs (outline first). Then, this field contains the [`HirFileId`] + /// of the outline declaration, and the index in `docs` from which the inline docs + /// begin. + outline_mod: Option<(HirFileId, usize)>, + inline_file: HirFileId, + /// The size the prepended prefix, which does not map to real doc comments. + prefix_len: TextSize, + /// The offset in `docs` from which the docs are inner attributes/comments. + inline_inner_docs_start: Option<TextSize>, + /// Like `inline_inner_docs_start`, but for `outline_mod`. This can happen only when merging `Docs` + /// (as outline modules don't have inner attributes). + outline_inner_docs_start: Option<TextSize>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum IsInnerDoc { + No, + Yes, +} + +impl IsInnerDoc { + #[inline] + pub fn yes(self) -> bool { + self == IsInnerDoc::Yes + } +} + +impl Docs { + #[inline] + pub fn docs(&self) -> &str { + &self.docs + } + + #[inline] + pub fn into_docs(self) -> String { + self.docs + } + + pub fn find_ast_range( + &self, + mut string_range: TextRange, + ) -> Option<(InFile<TextRange>, IsInnerDoc)> { + if string_range.start() < self.prefix_len { + return None; + } + string_range -= self.prefix_len; + + let mut file = self.inline_file; + let mut inner_docs_start = self.inline_inner_docs_start; + // Check whether the range is from the outline, the inline, or both. + let source_map = if let Some((outline_mod_file, outline_mod_end)) = self.outline_mod { + if let Some(first_inline) = self.docs_source_map.get(outline_mod_end) { + if string_range.end() <= first_inline.string_offset { + // The range is completely in the outline. + file = outline_mod_file; + inner_docs_start = self.outline_inner_docs_start; + &self.docs_source_map[..outline_mod_end] + } else if string_range.start() >= first_inline.string_offset { + // The range is completely in the inline. + &self.docs_source_map[outline_mod_end..] + } else { + // The range is combined from the outline and the inline - cannot map it back. + return None; + } + } else { + // There is no inline. + file = outline_mod_file; + inner_docs_start = self.outline_inner_docs_start; + &self.docs_source_map + } + } else { + // There is no outline. + &self.docs_source_map + }; + + let after_range = + source_map.partition_point(|line| line.string_offset <= string_range.start()) - 1; + let after_range = &source_map[after_range..]; + let line = after_range.first()?; + // Unmapped lines (from macro-expanded docs) cannot be mapped back to AST. + let ast_offset = line.ast_offset?; + if after_range.get(1).is_some_and(|next_line| next_line.string_offset < string_range.end()) + { + // The range is combined from two lines - cannot map it back. + return None; + } + let ast_range = string_range - line.string_offset + ast_offset; + let is_inner = if inner_docs_start + .is_some_and(|inner_docs_start| string_range.start() >= inner_docs_start) + { + IsInnerDoc::Yes + } else { + IsInnerDoc::No + }; + Some((InFile::new(file, ast_range), is_inner)) + } + + #[inline] + pub fn shift_by(&mut self, offset: TextSize) { + self.prefix_len += offset; + } + + pub fn prepend_str(&mut self, s: &str) { + self.prefix_len += TextSize::of(s); + self.docs.insert_str(0, s); + } + + pub fn append_str(&mut self, s: &str) { + self.docs.push_str(s); + } + + pub fn append(&mut self, other: &Docs) { + let other_offset = TextSize::of(&self.docs); + + assert!( + self.outline_mod.is_none() && other.outline_mod.is_none(), + "cannot merge `Docs` that have `outline_mod` set" + ); + self.outline_mod = Some((self.inline_file, self.docs_source_map.len())); + self.inline_file = other.inline_file; + self.outline_inner_docs_start = self.inline_inner_docs_start; + self.inline_inner_docs_start = other.inline_inner_docs_start.map(|it| it + other_offset); + + self.docs.push_str(&other.docs); + self.docs_source_map.extend(other.docs_source_map.iter().map( + |&DocsSourceMapLine { string_offset, ast_offset }| DocsSourceMapLine { + ast_offset, + string_offset: string_offset + other_offset, + }, + )); + } + + fn extend_with_doc_comment(&mut self, comment: ast::Comment, indent: &mut usize) { + let Some((doc, offset)) = comment.doc_comment() else { return }; + self.extend_with_doc_str(doc, comment.syntax().text_range().start() + offset, indent); + } + + fn extend_with_doc_attr(&mut self, value: ast::String, indent: &mut usize) { + let Some(value_offset) = value.text_range_between_quotes() else { return }; + let value_offset = value_offset.start(); + let Ok(value) = value.value() else { return }; + // FIXME: Handle source maps for escaped text. + self.extend_with_doc_str(&value, value_offset, indent); + } + + pub(crate) fn extend_with_doc_str( + &mut self, + doc: &str, + offset_in_ast: TextSize, + indent: &mut usize, + ) { + self.push_doc_lines(doc, Some(offset_in_ast), indent); + } + + fn extend_with_unmapped_doc_str(&mut self, doc: &str, indent: &mut usize) { + self.push_doc_lines(doc, None, indent); + } + + fn push_doc_lines(&mut self, doc: &str, mut ast_offset: Option<TextSize>, indent: &mut usize) { + for line in doc.split('\n') { + self.docs_source_map + .push(DocsSourceMapLine { string_offset: TextSize::of(&self.docs), ast_offset }); + if let Some(ref mut offset) = ast_offset { + *offset += TextSize::of(line) + TextSize::of("\n"); + } + + let line = line.trim_end(); + if let Some(line_indent) = line.chars().position(|ch| !ch.is_whitespace()) { + // Empty lines are handled because `position()` returns `None` for them. + *indent = std::cmp::min(*indent, line_indent); + } + self.docs.push_str(line); + self.docs.push('\n'); + } + } + + fn remove_indent(&mut self, indent: usize, start_source_map_index: usize) { + /// In case of panics, we want to avoid corrupted UTF-8 in `self.docs`, so we clear it. + struct Guard<'a>(&'a mut Docs); + impl Drop for Guard<'_> { + fn drop(&mut self) { + let Docs { + docs, + docs_source_map, + outline_mod, + inline_file: _, + prefix_len: _, + inline_inner_docs_start: _, + outline_inner_docs_start: _, + } = self.0; + // Don't use `String::clear()` here because it's not guaranteed to not do UTF-8-dependent things, + // and we may have temporarily broken the string's encoding. + unsafe { docs.as_mut_vec() }.clear(); + // This is just to avoid panics down the road. + docs_source_map.clear(); + *outline_mod = None; + } + } + + if self.docs.is_empty() { + return; + } + + let guard = Guard(self); + let source_map = &mut guard.0.docs_source_map[start_source_map_index..]; + let Some(&DocsSourceMapLine { string_offset: mut copy_into, .. }) = source_map.first() + else { + return; + }; + // We basically want to remove multiple ranges from a string. Doing this efficiently (without O(N^2) + // or allocations) requires unsafe. Basically, for each line, we copy the line minus the indent into + // consecutive to the previous line (which may have moved). Then at the end we truncate. + let mut accumulated_offset = TextSize::new(0); + for idx in 0..source_map.len() { + let string_end_offset = source_map + .get(idx + 1) + .map_or_else(|| TextSize::of(&guard.0.docs), |next_attr| next_attr.string_offset); + let line_source = &mut source_map[idx]; + let line_docs = + &guard.0.docs[TextRange::new(line_source.string_offset, string_end_offset)]; + let line_docs_len = TextSize::of(line_docs); + let indent_size = line_docs.char_indices().nth(indent).map_or_else( + || TextSize::of(line_docs) - TextSize::of("\n"), + |(offset, _)| TextSize::new(offset as u32), + ); + unsafe { guard.0.docs.as_bytes_mut() }.copy_within( + Range::<usize>::from(TextRange::new( + line_source.string_offset + indent_size, + string_end_offset, + )), + copy_into.into(), + ); + copy_into += line_docs_len - indent_size; + + if let Some(inner_attrs_start) = &mut guard.0.inline_inner_docs_start + && *inner_attrs_start == line_source.string_offset + { + *inner_attrs_start -= accumulated_offset; + } + // The removals in the string accumulate, but in the AST not, because it already points + // to the beginning of each attribute. + // Also, we need to shift the AST offset of every line, but the string offset of the first + // line should not get shifted (in general, the shift for the string offset is by the + // number of lines until the current one, excluding the current one). + line_source.string_offset -= accumulated_offset; + if let Some(ref mut ast_offset) = line_source.ast_offset { + *ast_offset += indent_size; + } + + accumulated_offset += indent_size; + } + // Don't use `String::truncate()` here because it's not guaranteed to not do UTF-8-dependent things, + // and we may have temporarily broken the string's encoding. + unsafe { guard.0.docs.as_mut_vec() }.truncate(copy_into.into()); + + std::mem::forget(guard); + } + + fn remove_last_newline(&mut self) { + self.docs.truncate(self.docs.len().saturating_sub(1)); + } + + fn shrink_to_fit(&mut self) { + let Docs { + docs, + docs_source_map, + outline_mod: _, + inline_file: _, + prefix_len: _, + inline_inner_docs_start: _, + outline_inner_docs_start: _, + } = self; + docs.shrink_to_fit(); + docs_source_map.shrink_to_fit(); + } +} + +struct DocMacroExpander<'db> { + db: &'db dyn DefDatabase, + krate: Crate, + recursion_depth: usize, + recursion_limit: usize, +} + +struct DocExprSourceCtx<'db> { + resolver: Resolver<'db>, + file_id: HirFileId, + ast_id_map: &'db AstIdMap, + span_map: SpanMap, +} + +fn expand_doc_expr_via_macro_pipeline<'db>( + expander: &mut DocMacroExpander<'db>, + source_ctx: &DocExprSourceCtx<'db>, + expr: ast::Expr, +) -> Option<String> { + match expr { + ast::Expr::ParenExpr(paren_expr) => { + expand_doc_expr_via_macro_pipeline(expander, source_ctx, paren_expr.expr()?) + } + ast::Expr::Literal(literal) => match literal.kind() { + ast::LiteralKind::String(string) => string.value().ok().map(Into::into), + _ => None, + }, + ast::Expr::MacroExpr(macro_expr) => { + let macro_call = macro_expr.macro_call()?; + let (expr, new_source_ctx) = expand_doc_macro_call(expander, source_ctx, macro_call)?; + // After expansion, the expr lives in the expansion file; use its source context. + expand_doc_expr_via_macro_pipeline(expander, &new_source_ctx, expr) + } + _ => None, + } +} + +fn expand_doc_macro_call<'db>( + expander: &mut DocMacroExpander<'db>, + source_ctx: &DocExprSourceCtx<'db>, + macro_call: ast::MacroCall, +) -> Option<(ast::Expr, DocExprSourceCtx<'db>)> { + if expander.recursion_depth >= expander.recursion_limit { + return None; + } + + let path = macro_call.path()?; + let mod_path = ModPath::from_src(expander.db, path, &mut |range| { + source_ctx.span_map.span_for_range(range).ctx + })?; + let call_site = source_ctx.span_map.span_for_range(macro_call.syntax().text_range()); + let ast_id = AstId::new(source_ctx.file_id, source_ctx.ast_id_map.ast_id(¯o_call)); + let call_id = macro_call_as_call_id( + expander.db, + ast_id, + &mod_path, + call_site.ctx, + ExpandTo::Expr, + expander.krate, + |path| { + source_ctx.resolver.resolve_path_as_macro_def(expander.db, path, Some(MacroSubNs::Bang)) + }, + &mut |_, _| (), + ) + .ok()? + .value?; + + expander.recursion_depth += 1; + let parse = expander.db.parse_macro_expansion(call_id).value.0; + let expr = parse.cast::<ast::Expr>().map(|parse| parse.tree())?; + expander.recursion_depth -= 1; + + // Build a new source context for the expansion file so that any further + // recursive expansion (e.g. a user macro expanding to `concat!(...)`) + // correctly resolves AstIds and spans in the expansion. + let expansion_file_id: HirFileId = call_id.into(); + let new_source_ctx = DocExprSourceCtx { + resolver: source_ctx.resolver.clone(), + file_id: expansion_file_id, + ast_id_map: expander.db.ast_id_map(expansion_file_id), + span_map: expander.db.span_map(expansion_file_id), + }; + Some((expr, new_source_ctx)) +} + +fn extend_with_attrs<'a, 'db>( + result: &mut Docs, + db: &'db dyn DefDatabase, + krate: Crate, + node: &SyntaxNode, + file_id: HirFileId, + expect_inner_attrs: bool, + indent: &mut usize, + get_cfg_options: &dyn Fn() -> &'a CfgOptions, + cfg_options: &mut Option<&'a CfgOptions>, + make_resolver: &dyn Fn() -> Resolver<'db>, +) { + // Lazily initialised when we first encounter a `#[doc = macro!()]`. + let mut expander: Option<(DocMacroExpander<'db>, DocExprSourceCtx<'db>)> = None; + + expand_cfg_attr_with_doc_comments::<_, Infallible>( + AttrDocCommentIter::from_syntax_node(node).filter(|attr| match attr { + Either::Left(attr) => attr.kind().is_inner() == expect_inner_attrs, + Either::Right(comment) => comment + .kind() + .doc + .is_some_and(|kind| (kind == ast::CommentPlacement::Inner) == expect_inner_attrs), + }), + || *cfg_options.get_or_insert_with(get_cfg_options), + |attr| { + match attr { + Either::Right(doc_comment) => result.extend_with_doc_comment(doc_comment, indent), + Either::Left((attr, _)) => match attr { + ast::Meta::KeyValueMeta(attr) if attr.path().is1("doc") => { + if let Some(value) = attr.expr() { + if let ast::Expr::Literal(value) = &value + && let ast::LiteralKind::String(value) = value.kind() + { + result.extend_with_doc_attr(value, indent); + } else { + let (exp, ctx) = expander.get_or_insert_with(|| { + let resolver = make_resolver(); + let def_map = resolver.top_level_def_map(); + let recursion_limit = def_map.recursion_limit() as usize; + ( + DocMacroExpander { + db, + krate, + recursion_depth: 0, + recursion_limit, + }, + DocExprSourceCtx { + resolver, + file_id, + ast_id_map: db.ast_id_map(file_id), + span_map: db.span_map(file_id), + }, + ) + }); + if let Some(expanded) = + expand_doc_expr_via_macro_pipeline(exp, ctx, value) + { + result.extend_with_unmapped_doc_str(&expanded, indent); + } + } + } + } + _ => {} + }, + } + ControlFlow::Continue(()) + }, + ); +} + +pub(crate) fn extract_docs<'a, 'db>( + db: &'db dyn DefDatabase, + krate: Crate, + resolver: &dyn Fn() -> Resolver<'db>, + get_cfg_options: &dyn Fn() -> &'a CfgOptions, + source: InFile<ast::AnyHasAttrs>, + outer_mod_decl: Option<InFile<ast::Module>>, + inner_attrs_node: Option<SyntaxNode>, +) -> Option<Box<Docs>> { + let mut result = Docs { + docs: String::new(), + docs_source_map: Vec::new(), + outline_mod: None, + inline_file: source.file_id, + prefix_len: TextSize::new(0), + inline_inner_docs_start: None, + outline_inner_docs_start: None, + }; + + let mut cfg_options = None; + + if let Some(outer_mod_decl) = outer_mod_decl { + let mut indent = usize::MAX; + // For outer docs (the `mod foo;` declaration), use the module's own resolver. + extend_with_attrs( + &mut result, + db, + krate, + outer_mod_decl.value.syntax(), + outer_mod_decl.file_id, + false, + &mut indent, + get_cfg_options, + &mut cfg_options, + resolver, + ); + result.remove_indent(indent, 0); + result.outline_mod = Some((outer_mod_decl.file_id, result.docs_source_map.len())); + } + + let inline_source_map_start = result.docs_source_map.len(); + let mut indent = usize::MAX; + // For inline docs, use the item's own resolver. + extend_with_attrs( + &mut result, + db, + krate, + source.value.syntax(), + source.file_id, + false, + &mut indent, + get_cfg_options, + &mut cfg_options, + resolver, + ); + if let Some(inner_attrs_node) = &inner_attrs_node { + result.inline_inner_docs_start = Some(TextSize::of(&result.docs)); + extend_with_attrs( + &mut result, + db, + krate, + inner_attrs_node, + source.file_id, + true, + &mut indent, + get_cfg_options, + &mut cfg_options, + resolver, + ); + } + result.remove_indent(indent, inline_source_map_start); + + result.remove_last_newline(); + + result.shrink_to_fit(); + + if result.docs.is_empty() { None } else { Some(Box::new(result)) } +} + +#[cfg(test)] +mod tests { + use expect_test::expect; + use hir_expand::InFile; + use test_fixture::WithFixture; + use tt::{TextRange, TextSize}; + + use crate::test_db::TestDB; + + use super::{Docs, IsInnerDoc}; + + #[test] + fn docs() { + let (_db, file_id) = TestDB::with_single_file(""); + let mut docs = Docs { + docs: String::new(), + docs_source_map: Vec::new(), + outline_mod: None, + inline_file: file_id.into(), + prefix_len: TextSize::new(0), + inline_inner_docs_start: None, + outline_inner_docs_start: None, + }; + let mut indent = usize::MAX; + + let outer = " foo\n\tbar baz"; + let mut ast_offset = TextSize::new(123); + for line in outer.split('\n') { + docs.extend_with_doc_str(line, ast_offset, &mut indent); + ast_offset += TextSize::of(line) + TextSize::of("\n"); + } + + docs.inline_inner_docs_start = Some(TextSize::of(&docs.docs)); + ast_offset += TextSize::new(123); + let inner = " bar \n baz"; + for line in inner.split('\n') { + docs.extend_with_doc_str(line, ast_offset, &mut indent); + ast_offset += TextSize::of(line) + TextSize::of("\n"); + } + + assert_eq!(indent, 1); + expect![[r#" + [ + DocsSourceMapLine { + string_offset: 0, + ast_offset: Some( + 123, + ), + }, + DocsSourceMapLine { + string_offset: 5, + ast_offset: Some( + 128, + ), + }, + DocsSourceMapLine { + string_offset: 15, + ast_offset: Some( + 261, + ), + }, + DocsSourceMapLine { + string_offset: 20, + ast_offset: Some( + 267, + ), + }, + ] + "#]] + .assert_debug_eq(&docs.docs_source_map); + + docs.remove_indent(indent, 0); + + assert_eq!(docs.inline_inner_docs_start, Some(TextSize::new(13))); + + assert_eq!(docs.docs, "foo\nbar baz\nbar\nbaz\n"); + expect![[r#" + [ + DocsSourceMapLine { + string_offset: 0, + ast_offset: Some( + 124, + ), + }, + DocsSourceMapLine { + string_offset: 4, + ast_offset: Some( + 129, + ), + }, + DocsSourceMapLine { + string_offset: 13, + ast_offset: Some( + 262, + ), + }, + DocsSourceMapLine { + string_offset: 17, + ast_offset: Some( + 268, + ), + }, + ] + "#]] + .assert_debug_eq(&docs.docs_source_map); + + docs.append(&docs.clone()); + docs.prepend_str("prefix---"); + assert_eq!(docs.docs, "prefix---foo\nbar baz\nbar\nbaz\nfoo\nbar baz\nbar\nbaz\n"); + expect![[r#" + [ + DocsSourceMapLine { + string_offset: 0, + ast_offset: Some( + 124, + ), + }, + DocsSourceMapLine { + string_offset: 4, + ast_offset: Some( + 129, + ), + }, + DocsSourceMapLine { + string_offset: 13, + ast_offset: Some( + 262, + ), + }, + DocsSourceMapLine { + string_offset: 17, + ast_offset: Some( + 268, + ), + }, + DocsSourceMapLine { + string_offset: 21, + ast_offset: Some( + 124, + ), + }, + DocsSourceMapLine { + string_offset: 25, + ast_offset: Some( + 129, + ), + }, + DocsSourceMapLine { + string_offset: 34, + ast_offset: Some( + 262, + ), + }, + DocsSourceMapLine { + string_offset: 38, + ast_offset: Some( + 268, + ), + }, + ] + "#]] + .assert_debug_eq(&docs.docs_source_map); + + let range = |start, end| TextRange::new(TextSize::new(start), TextSize::new(end)); + let in_file = |range| InFile::new(file_id.into(), range); + assert_eq!(docs.find_ast_range(range(0, 2)), None); + assert_eq!(docs.find_ast_range(range(8, 10)), None); + assert_eq!( + docs.find_ast_range(range(9, 10)), + Some((in_file(range(124, 125)), IsInnerDoc::No)) + ); + assert_eq!(docs.find_ast_range(range(20, 23)), None); + assert_eq!( + docs.find_ast_range(range(23, 25)), + Some((in_file(range(263, 265)), IsInnerDoc::Yes)) + ); + } +} diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 5d5d435398..9dd7768ead 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -1,5 +1,5 @@ //! Defines database & queries for name resolution. -use base_db::{Crate, RootQueryDb, SourceDatabase}; +use base_db::{Crate, SourceDatabase}; use hir_expand::{ EditionedFileId, HirFileId, InFile, Lookup, MacroCallId, MacroDefId, MacroDefKind, db::ExpandDatabase, @@ -22,7 +22,7 @@ use crate::{ use salsa::plumbing::AsId; #[query_group::query_group(InternDatabaseStorage)] -pub trait InternDatabase: RootQueryDb { +pub trait InternDatabase: SourceDatabase { // region: items #[salsa::interned] fn intern_use(&self, loc: UseLoc) -> UseId; diff --git a/crates/hir-def/src/dyn_map.rs b/crates/hir-def/src/dyn_map.rs index 4308d0ef1c..c38ceccd1f 100644 --- a/crates/hir-def/src/dyn_map.rs +++ b/crates/hir-def/src/dyn_map.rs @@ -68,7 +68,7 @@ pub mod keys { pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = Key::new(); pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new(); pub const DERIVE_MACRO_CALL: Key< - ast::Attr, + ast::Meta, ( AttrId, /* derive() */ MacroCallId, diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index ca523622ec..62a17168b1 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs @@ -520,7 +520,7 @@ impl ExpressionStore { self.const_expr_origins().iter().map(|&(id, _)| id) } - /// Like [`Self::signature_const_expr_roots`], but also returns the origin + /// Like [`Self::expr_roots`], but also returns the origin /// of each expression. pub fn expr_roots_with_origins(&self) -> impl Iterator<Item = (ExprId, RootExprOrigin)> { self.const_expr_origins().iter().map(|&(id, origin)| (id, origin)) diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 74006c6037..7fe91a3d02 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -1465,7 +1465,15 @@ impl<'db> ExprCollector<'db> { match kind { ArrayExprKind::ElementList(e) => { - let elements = e.map(|expr| self.collect_expr(expr)).collect(); + let elements = e + .filter_map(|expr| { + if self.check_cfg(&expr) { + Some(self.collect_expr(expr)) + } else { + None + } + }) + .collect(); self.alloc_expr(Expr::Array(Array::ElementList { elements }), syntax_ptr) } ArrayExprKind::Repeat { initializer, repeat } => { diff --git a/crates/hir-def/src/expr_store/scope.rs b/crates/hir-def/src/expr_store/scope.rs index 40ae0b7de4..9738ac5c44 100644 --- a/crates/hir-def/src/expr_store/scope.rs +++ b/crates/hir-def/src/expr_store/scope.rs @@ -371,7 +371,6 @@ fn compute_expr_scopes( #[cfg(test)] mod tests { - use base_db::RootQueryDb; use hir_expand::{InFile, name::AsName}; use span::FileId; use syntax::{AstNode, algo::find_node_at_offset, ast}; @@ -414,7 +413,7 @@ mod tests { let (file_id, _) = editioned_file_id.unpack(&db); - let file_syntax = db.parse(editioned_file_id).syntax_node(); + let file_syntax = editioned_file_id.parse(&db).syntax_node(); let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap(); let function = find_function(&db, file_id); @@ -570,7 +569,7 @@ fn foo() { let (file_id, _) = editioned_file_id.unpack(&db); - let file = db.parse(editioned_file_id).ok().unwrap(); + let file = editioned_file_id.parse(&db).ok().unwrap(); let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()) .expect("failed to find a name at the target offset"); let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap(); diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs index 985cd96662..4e5f2ca893 100644 --- a/crates/hir-def/src/expr_store/tests/body.rs +++ b/crates/hir-def/src/expr_store/tests/body.rs @@ -660,3 +660,24 @@ async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, }"#]], ) } + +#[test] +fn array_element_cfg() { + pretty_print( + r#" +fn foo() { + [ + (), + #[cfg(false)] + () + ]; +} + "#, + expect![[r#" + fn foo() { + [ + (), + ]; + }"#]], + ); +} diff --git a/crates/hir-def/src/expr_store/tests/body/block.rs b/crates/hir-def/src/expr_store/tests/body/block.rs index 83594ee021..71fcced2d8 100644 --- a/crates/hir-def/src/expr_store/tests/body/block.rs +++ b/crates/hir-def/src/expr_store/tests/body/block.rs @@ -190,13 +190,13 @@ fn f() { "#, expect![[r#" ModuleIdLt { - [salsa id]: Id(3803), + [salsa id]: Id(3403), krate: Crate( - Id(2400), + Id(2000), ), block: Some( BlockId( - 4801, + 4401, ), ), }"#]], diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 0014e1af5c..ba077b1b2e 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -499,7 +499,7 @@ fn search_maps( #[cfg(test)] mod tests { - use base_db::RootQueryDb; + use base_db::all_crates; use expect_test::{Expect, expect}; use test_fixture::WithFixture; @@ -536,7 +536,7 @@ mod tests { expect: Expect, ) { let db = TestDB::with_files(ra_fixture); - let all_crates = db.all_crates(); + let all_crates = all_crates(&db); let krate = all_crates .iter() .copied() @@ -616,7 +616,7 @@ mod tests { fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let db = TestDB::with_files(ra_fixture); - let all_crates = db.all_crates(); + let all_crates = all_crates(&db); let actual = all_crates .iter() diff --git a/crates/hir-def/src/item_tree/attrs.rs b/crates/hir-def/src/item_tree/attrs.rs index 7907611284..867d813e3f 100644 --- a/crates/hir-def/src/item_tree/attrs.rs +++ b/crates/hir-def/src/item_tree/attrs.rs @@ -13,12 +13,12 @@ use std::{ use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ - attrs::{Attr, AttrId, AttrInput, Meta, collect_item_tree_attrs}, + attrs::{Attr, AttrId, AttrInput, collect_item_tree_attrs}, mod_path::ModPath, name::Name, }; use intern::{Interned, Symbol, sym}; -use syntax::{AstNode, T, ast}; +use syntax::{AstNode, ast}; use syntax_bridge::DocCommentDesugarMode; use tt::token_to_literal; @@ -51,58 +51,62 @@ impl AttrsOrCfg { S: syntax_bridge::SpanMapper + Copy, { let mut attrs = Vec::new(); - let result = - collect_item_tree_attrs::<Infallible>(owner, cfg_options, |meta, container, _, _| { - // NOTE: We cannot early return from this function, *every* attribute must be pushed, otherwise we'll mess the `AttrId` - // tracking. - let (span, path_range, input) = match meta { - Meta::NamedKeyValue { path_range, name: _, value } => { - let span = span_map.span_for(path_range); - let input = value.map(|value| { - Box::new(AttrInput::Literal(token_to_literal( - value.text(), - span_map.span_for(value.text_range()), - ))) - }); - (span, path_range, input) - } - Meta::TokenTree { path, tt } => { - let span = span_map.span_for(path.range); - let tt = syntax_bridge::syntax_node_to_token_tree( - tt.syntax(), - span_map, - span, - DocCommentDesugarMode::ProcMacro, - ); - let input = Some(Box::new(AttrInput::TokenTree(tt))); - (span, path.range, input) - } - Meta::Path { path } => { - let span = span_map.span_for(path.range); - (span, path.range, None) - } - }; + let result = collect_item_tree_attrs::<Infallible>(owner, cfg_options, |meta, _| { + // NOTE: We cannot early return from this function, *every* attribute must be pushed, otherwise we'll mess the `AttrId` + // tracking. + let path = meta.path(); + let path_range = path + .as_ref() + .map(|path| path.syntax().text_range()) + .unwrap_or_else(|| meta.syntax().text_range()); + let (span, input) = match &meta { + ast::Meta::KeyValueMeta(meta) => { + let span = span_map.span_for(path_range); + let input = meta.expr().and_then(|value| { + if let ast::Expr::Literal(value) = value { + Some(Box::new(AttrInput::Literal(token_to_literal( + value.token().text(), + span_map.span_for(value.syntax().text_range()), + )))) + } else { + None + } + }); + (span, input) + } + ast::Meta::TokenTreeMeta(meta) => { + let span = span_map.span_for(path_range); + let tt = syntax_bridge::syntax_node_to_token_tree( + &meta + .token_tree() + .map(|it| it.syntax().clone()) + .unwrap_or_else(|| meta.syntax().clone()), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); + let input = Some(Box::new(AttrInput::TokenTree(tt))); + (span, input) + } + ast::Meta::PathMeta(_) => { + let span = span_map.span_for(path_range); + (span, None) + } + ast::Meta::CfgMeta(_) | ast::Meta::CfgAttrMeta(_) | ast::Meta::UnsafeMeta(_) => { + unreachable!( + "`cfg`, `cfg_attr` and `unsafe(...)` are handled in `collect_item_tree_attrs()`" + ) + } + }; - let path = container.token_at_offset(path_range.start()).right_biased().and_then( - |first_path_token| { - let is_abs = matches!(first_path_token.kind(), T![:] | T![::]); - let segments = - std::iter::successors(Some(first_path_token), |it| it.next_token()) - .take_while(|it| it.text_range().end() <= path_range.end()) - .filter(|it| it.kind().is_any_identifier()); - ModPath::from_tokens( - db, - &mut |range| span_map.span_for(range).ctx, - is_abs, - segments, - ) - }, - ); - let path = path.unwrap_or_else(|| Name::missing().into()); - - attrs.push(Attr { path: Interned::new(path), input, ctxt: span.ctx }); - ControlFlow::Continue(()) + let path = path.and_then(|path| { + ModPath::from_src(db, path, &mut |range| span_map.span_for(range).ctx) }); + let path = path.unwrap_or_else(|| Name::missing().into()); + + attrs.push(Attr { path: Interned::new(path), input, ctxt: span.ctx }); + ControlFlow::Continue(()) + }); let attrs = AttrsOwned(attrs.into_boxed_slice()); match result { Some(Either::Right(cfg)) => AttrsOrCfg::CfgDisabled(Box::new((cfg, attrs))), diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index d93df7af6a..e75c96b630 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -35,9 +35,9 @@ macro_rules! f { }; } -struct#0:MacroRules[BE8F, 0]@58..64#18432# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#18432# - map#0:MacroRules[BE8F, 0]@86..89#18432#:#0:MacroRules[BE8F, 0]@89..90#18432# #0:MacroRules[BE8F, 0]@89..90#18432#::#0:MacroRules[BE8F, 0]@91..93#18432#std#0:MacroRules[BE8F, 0]@93..96#18432#::#0:MacroRules[BE8F, 0]@96..98#18432#collections#0:MacroRules[BE8F, 0]@98..109#18432#::#0:MacroRules[BE8F, 0]@109..111#18432#HashSet#0:MacroRules[BE8F, 0]@111..118#18432#<#0:MacroRules[BE8F, 0]@118..119#18432#(#0:MacroRules[BE8F, 0]@119..120#18432#)#0:MacroRules[BE8F, 0]@120..121#18432#>#0:MacroRules[BE8F, 0]@121..122#18432#,#0:MacroRules[BE8F, 0]@122..123#18432# -}#0:MacroRules[BE8F, 0]@132..133#18432# +struct#0:MacroRules[BE8F, 0]@58..64#17408# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#17408# + map#0:MacroRules[BE8F, 0]@86..89#17408#:#0:MacroRules[BE8F, 0]@89..90#17408# #0:MacroRules[BE8F, 0]@89..90#17408#::#0:MacroRules[BE8F, 0]@91..93#17408#std#0:MacroRules[BE8F, 0]@93..96#17408#::#0:MacroRules[BE8F, 0]@96..98#17408#collections#0:MacroRules[BE8F, 0]@98..109#17408#::#0:MacroRules[BE8F, 0]@109..111#17408#HashSet#0:MacroRules[BE8F, 0]@111..118#17408#<#0:MacroRules[BE8F, 0]@118..119#17408#(#0:MacroRules[BE8F, 0]@119..120#17408#)#0:MacroRules[BE8F, 0]@120..121#17408#>#0:MacroRules[BE8F, 0]@121..122#17408#,#0:MacroRules[BE8F, 0]@122..123#17408# +}#0:MacroRules[BE8F, 0]@132..133#17408# "#]], ); } @@ -197,7 +197,7 @@ macro_rules! mk_struct { #[macro_use] mod foo; -struct#1:MacroRules[DB0C, 0]@59..65#18432# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#18432#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#18432#;#1:MacroRules[DB0C, 0]@75..76#18432# +struct#1:MacroRules[DB0C, 0]@59..65#17408# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#17408#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#17408#;#1:MacroRules[DB0C, 0]@75..76#17408# "#]], ); } @@ -423,10 +423,10 @@ m! { foo, bar } macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } -impl#\18432# Bar#\18432# {#\18432# - fn#\18432# foo#\ROOT2024#(#\18432#)#\18432# {#\18432#}#\18432# - fn#\18432# bar#\ROOT2024#(#\18432#)#\18432# {#\18432#}#\18432# -}#\18432# +impl#\17408# Bar#\17408# {#\17408# + fn#\17408# foo#\ROOT2024#(#\17408#)#\17408# {#\17408#}#\17408# + fn#\17408# bar#\ROOT2024#(#\17408#)#\17408# {#\17408#}#\17408# +}#\17408# "#]], ); } @@ -1198,7 +1198,7 @@ m! { hello::world } macro_rules! m { ($m:meta) => ( #[$m] fn bar() {} ) } -#[cfg(target_os = "windows")] fn bar() {} +#[cfg (target_os = "windows")] fn bar() {} #[hello::world] fn bar() {} "#]], ); diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index ddabb50251..cac248f47f 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -205,7 +205,7 @@ impl Clone for D3DVSHADERCAPS2_0 { *self } } -#[cfg(feature = "impl-default")] impl Default for D3DVSHADERCAPS2_0 { +#[cfg (feature = "impl-default")] impl Default for D3DVSHADERCAPS2_0 { #[inline] fn default() -> D3DVSHADERCAPS2_0 { unsafe { $crate::_core::mem::zeroed() @@ -215,7 +215,7 @@ impl Clone for D3DVSHADERCAPS2_0 { #[repr(C)] #[derive(Copy)] -#[cfg_attr(target_arch = "x86", repr(packed))] pub struct D3DCONTENTPROTECTIONCAPS { +#[cfg_attr (target_arch = "x86", repr(packed))] pub struct D3DCONTENTPROTECTIONCAPS { pub Caps: u8, } impl Clone for D3DCONTENTPROTECTIONCAPS { @@ -223,7 +223,7 @@ impl Clone for D3DCONTENTPROTECTIONCAPS { *self } } -#[cfg(feature = "impl-default")] impl Default for D3DCONTENTPROTECTIONCAPS { +#[cfg (feature = "impl-default")] impl Default for D3DCONTENTPROTECTIONCAPS { #[inline] fn default() -> D3DCONTENTPROTECTIONCAPS { unsafe { $crate::_core::mem::zeroed() @@ -1001,8 +1001,8 @@ macro_rules! with_std { ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*) } -#[cfg(feature = "std")] mod m; -#[cfg(feature = "std")] mod f; +#[cfg (feature = "std")] mod m; +#[cfg (feature = "std")] mod f; "#]], ) } diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index 8317c56caf..eabdada67c 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -16,7 +16,6 @@ mod proc_macros; use std::{any::TypeId, iter, ops::Range, sync}; -use base_db::RootQueryDb; use expect_test::Expect; use hir_expand::{ AstId, ExpansionInfo, InFile, MacroCallId, MacroCallKind, MacroKind, @@ -75,7 +74,7 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) let editioned_file_id = ast_id.file_id.file_id().expect("macros inside macros are not supported"); - let ast = db.parse(editioned_file_id).syntax_node(); + let ast = editioned_file_id.parse(&db).syntax_node(); let ast_id_map = db.ast_id_map(ast_id.file_id); let node = ast_id_map.get_erased(ast_id.value).to_node(&ast); Some((node.text_range(), errors)) diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index bf04a500a5..8c91cf6793 100644 --- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -55,8 +55,8 @@ mod foo { # ![doc = "123..."] # ![attr2] # ![attr3] - #[cfg_attr(true , cfg(false ))] fn foo() {} - #[cfg(true )] fn bar() {} + #[cfg_attr (true , cfg (false ))] fn foo() {} + #[cfg (true )] fn bar() {} }"##]], ); } diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 5fda1beab4..56b3f03f7b 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -346,7 +346,7 @@ impl ModuleOrigin { match self { &ModuleOrigin::File { definition: editioned_file_id, .. } | &ModuleOrigin::CrateRoot { definition: editioned_file_id } => { - let sf = db.parse(editioned_file_id).tree(); + let sf = editioned_file_id.parse(db).tree(); InFile::new(editioned_file_id.into(), ModuleSource::SourceFile(sf)) } &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new( diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 9c101c127b..703f070dba 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -22,7 +22,7 @@ use itertools::izip; use la_arena::Idx; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; -use span::{Edition, FileAstId, SyntaxContext}; +use span::{Edition, FileAstId, ROOT_ERASED_FILE_AST_ID, SyntaxContext}; use stdx::always; use syntax::ast; use triomphe::Arc; @@ -369,7 +369,14 @@ impl<'db> DefCollector<'db> { self.inject_prelude(); - if matches!(item_tree.top_level_attrs(), AttrsOrCfg::CfgDisabled(_)) { + if let AttrsOrCfg::CfgDisabled(attrs) = item_tree.top_level_attrs() { + let (cfg_expr, _) = &**attrs; + self.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( + self.def_map.root, + InFile::new(file_id.into(), ROOT_ERASED_FILE_AST_ID), + cfg_expr.clone(), + self.cfg_options.clone(), + )); return; } diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs index fe55252e25..08d98dff33 100644 --- a/crates/hir-def/src/nameres/tests.rs +++ b/crates/hir-def/src/nameres/tests.rs @@ -4,7 +4,6 @@ mod incremental; mod macros; mod mod_resolution; -use base_db::RootQueryDb; use expect_test::{Expect, expect}; use test_fixture::WithFixture; diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs index 5b75c078ec..0f1828abce 100644 --- a/crates/hir-def/src/nameres/tests/incremental.rs +++ b/crates/hir-def/src/nameres/tests/incremental.rs @@ -1,6 +1,6 @@ use base_db::{ CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, - DependencyBuilder, Env, RootQueryDb, SourceDatabase, + DependencyBuilder, Env, SourceDatabase, all_crates, }; use expect_test::{Expect, expect}; use intern::Symbol; @@ -56,11 +56,11 @@ pub const BAZ: u32 = 0; "#, ); - for &krate in db.all_crates().iter() { + for &krate in all_crates(&db).iter() { crate_def_map(&db, krate); } - let all_crates_before = db.all_crates(); + let all_crates_before = all_crates(&db); { // Add dependencies: c -> b, b -> a. @@ -100,15 +100,15 @@ pub const BAZ: u32 = 0; new_crate_graph.set_in_db(&mut db); } - let all_crates_after = db.all_crates(); + let all_crates_after = all_crates(&db); assert!( - Arc::ptr_eq(&all_crates_before, &all_crates_after), + std::sync::Arc::ptr_eq(&all_crates_before, &all_crates_after), "the all_crates list should not have been invalidated" ); execute_assert_events( &db, || { - for &krate in db.all_crates().iter() { + for &krate in all_crates(&db).iter() { crate_def_map(&db, krate); } }, @@ -167,22 +167,22 @@ fn no() {} "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "EnumVariants::of_", ] "#]], expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -225,16 +225,16 @@ pub struct S {} "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "decl_macro_expander_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "macro_def_shim", "file_item_tree_query", @@ -245,7 +245,7 @@ pub struct S {} "#]], expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -283,21 +283,21 @@ fn f() { foo } "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "crate_local_def_map", "proc_macros_for_crate_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "macro_def_shim", "file_item_tree_query", @@ -310,7 +310,7 @@ fn f() { foo } "#]], expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -407,22 +407,22 @@ pub struct S {} "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "crate_local_def_map", "proc_macros_for_crate_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "decl_macro_expander_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "macro_def_shim", "file_item_tree_query", @@ -446,7 +446,7 @@ pub struct S {} "#]], expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -524,16 +524,16 @@ m!(Z); "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "decl_macro_expander_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "macro_def_shim", "file_item_tree_query", @@ -571,7 +571,7 @@ m!(Z); &[("file_item_tree_query", 1), ("parse_macro_expansion_shim", 0)], expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -611,7 +611,7 @@ pub type Ty = (); [ "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", ] "#]], @@ -629,7 +629,7 @@ pub type Ty = (); &[("file_item_tree_query", 1), ("parse", 1)], expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index a013f8b2bc..f073cf777d 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -1,3 +1,4 @@ +use base_db::all_crates; use expect_test::expect; use itertools::Itertools; @@ -1129,7 +1130,7 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream { } "#, ); - let krate = *db.all_crates().last().expect("no crate graph present"); + let krate = *all_crates(&db).last().expect("no crate graph present"); let def_map = crate_def_map(&db, krate); assert_eq!(def_map.data.exported_derives.len(), 1); @@ -1497,7 +1498,7 @@ struct TokenStream; fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } "#, ); - let krate = *db.all_crates().last().expect("no crate graph present"); + let krate = *all_crates(&db).last().expect("no crate graph present"); let def_map = crate_def_map(&db, krate); let root_module = &def_map[def_map.root].scope; diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs index 0d260279f9..b854d2aa21 100644 --- a/crates/hir-def/src/test_db.rs +++ b/crates/hir-def/src/test_db.rs @@ -3,8 +3,9 @@ use std::{fmt, panic, sync::Mutex}; use base_db::{ - Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, RootQueryDb, - SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, + Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, SourceDatabase, + SourceRoot, SourceRootId, SourceRootInput, all_crates, relevant_crates, + set_all_crates_with_durability, }; use hir_expand::{InFile, files::FilePosition}; use salsa::Durability; @@ -49,7 +50,7 @@ impl Default for TestDB { }; this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); // This needs to be here otherwise `CrateGraphBuilder` panics. - this.set_all_crates(Arc::new(Box::new([]))); + set_all_crates_with_durability(&mut this, std::iter::empty(), Durability::HIGH); _ = base_db::LibraryRoots::builder(Default::default()) .durability(Durability::MEDIUM) .new(&this); @@ -145,7 +146,7 @@ impl SourceDatabase for TestDB { impl TestDB { pub(crate) fn fetch_test_crate(&self) -> Crate { - let all_crates = self.all_crates(); + let all_crates = all_crates(self); all_crates .iter() .copied() @@ -157,7 +158,7 @@ impl TestDB { } pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId { - for &krate in self.relevant_crates(file_id).iter() { + for &krate in relevant_crates(self, file_id).iter() { let crate_def_map = crate_def_map(self, krate); for (local_id, data) in crate_def_map.modules() { if data.origin.file_id().map(|file_id| file_id.file_id(self)) == Some(file_id) { @@ -288,7 +289,7 @@ impl TestDB { let source_map = &Body::with_source_map(self, def_with_body).1; let scopes = ExprScopes::body_expr_scopes(self, def_with_body); - let root_syntax_node = self.parse(file_id).syntax_node(); + let root_syntax_node = file_id.parse(self).syntax_node(); let scope_iter = algo::ancestors_at_offset(&root_syntax_node, position.offset).filter_map(|node| { let block = ast::BlockExpr::cast(node)?; diff --git a/crates/hir-expand/Cargo.toml b/crates/hir-expand/Cargo.toml index 4fa476afb6..43b0bea891 100644 --- a/crates/hir-expand/Cargo.toml +++ b/crates/hir-expand/Cargo.toml @@ -23,7 +23,6 @@ triomphe.workspace = true query-group.workspace = true salsa.workspace = true salsa-macros.workspace = true -arrayvec.workspace = true thin-vec.workspace = true # local deps diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index e3f10b2129..49baecb90c 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -4,20 +4,8 @@ //! [`expand_cfg_attr_with_doc_comments()`]. It is used to implement all attribute lowering //! in r-a. Its basic job is to list attributes; however, attributes do not necessarily map //! into [`ast::Attr`], because `cfg_attr` can map to zero, one, or more attributes -//! (`#[cfg_attr(predicate, attr1, attr2, ...)]`). To bridge this gap, this module defines -//! [`Meta`], which represents a desugared attribute. Various bits of r-a need different -//! things from [`Meta`], therefore it contains many parts. The basic idea is: -//! -//! - There are three kinds of attributes, `path = value`, `path`, and `path(token_tree)`. -//! - Most bits of rust-analyzer only need to deal with some paths. Therefore, we keep -//! the path only if it has up to 2 segments, or one segment for `path = value`. -//! We also only keep the value in `path = value` if it is a literal. However, we always -//! save the all relevant ranges of attributes (the path range, and the full attribute range) -//! for parts of r-a (e.g. name resolution) that need a faithful representation of the -//! attribute. -//! -//! [`expand_cfg_attr()`] expands `cfg_attr`s as it goes (as its name implies), to list -//! all attributes. +//! (`#[cfg_attr(predicate, attr1, attr2, ...)]`). [`expand_cfg_attr()`] expands `cfg_attr`s +//! as it goes (as its name implies), to list all attributes. //! //! Another thing to note is that we need to be able to map an attribute back to a range //! (for diagnostic purposes etc.). This is only ever needed for attributes that participate @@ -26,26 +14,18 @@ //! place (here) and one function ([`is_item_tree_filtered_attr()`]) that decides whether //! an attribute participate in name resolution. -use std::{ - borrow::Cow, cell::OnceCell, convert::Infallible, fmt, iter::Peekable, ops::ControlFlow, -}; +use std::{borrow::Cow, cell::OnceCell, convert::Infallible, fmt, ops::ControlFlow}; -use ::tt::{TextRange, TextSize}; -use arrayvec::ArrayVec; +use ::tt::TextRange; use base_db::Crate; use cfg::{CfgExpr, CfgOptions}; use either::Either; use intern::Interned; use itertools::Itertools; use mbe::{DelimiterKind, Punct}; -use parser::T; use smallvec::SmallVec; use span::{RealSpanMap, Span, SyntaxContext}; -use syntax::{ - AstNode, NodeOrToken, SyntaxNode, SyntaxToken, - ast::{self, TokenTreeChildren}, - unescape, -}; +use syntax::{AstNode, SmolStr, ast, unescape}; use syntax_bridge::DocCommentDesugarMode; use crate::{ @@ -56,207 +36,75 @@ use crate::{ tt::{self, TopSubtree}, }; -#[derive(Debug)] -pub struct AttrPath { - /// This can be empty if the path is not of 1 or 2 segments exactly. - pub segments: ArrayVec<SyntaxToken, 2>, - pub range: TextRange, - // FIXME: This shouldn't be textual, `#[test]` needs name resolution. - // And if textual, it shouldn't be here, it should be in hir-def/src/attrs.rs. But some macros - // fully qualify `test` as `core::prelude::vX::test`, and this is more than 2 segments, so hir-def - // attrs can't find it. But this will mean we have to push every up-to-4-segments path, which - // may impact perf. So it was easier to just hack it here. - pub is_test: bool, +pub trait AstPathExt { + fn is1(&self, segment: &str) -> bool; + + fn as_one_segment(&self) -> Option<SmolStr>; + + fn as_up_to_two_segment(&self) -> Option<(SmolStr, Option<SmolStr>)>; } -impl AttrPath { - #[inline] - fn extract(path: &ast::Path) -> Self { - let mut is_test = false; - let segments = (|| { - let mut segments = ArrayVec::new(); - let segment2 = path.segment()?.name_ref()?.syntax().first_token()?; - if segment2.text() == "test" { - // `#[test]` or `#[core::prelude::vX::test]`. - is_test = true; - } - let segment1 = path.qualifier(); - if let Some(segment1) = segment1 { - if segment1.qualifier().is_some() { - None - } else { - let segment1 = segment1.segment()?.name_ref()?.syntax().first_token()?; - segments.push(segment1); - segments.push(segment2); - Some(segments) - } - } else { - segments.push(segment2); - Some(segments) - } - })(); - AttrPath { - segments: segments.unwrap_or(ArrayVec::new()), - range: path.syntax().text_range(), - is_test, - } +impl AstPathExt for ast::Path { + fn is1(&self, segment: &str) -> bool { + self.as_one_segment().is_some_and(|it| it == segment) } - #[inline] - pub fn is1(&self, segment: &str) -> bool { - self.segments.len() == 1 && self.segments[0].text() == segment + fn as_one_segment(&self) -> Option<SmolStr> { + Some(self.as_single_name_ref()?.text().into()) } -} -#[derive(Debug)] -pub enum Meta { - /// `name` is `None` if not a single token. `value` is a literal or `None`. - NamedKeyValue { - path_range: TextRange, - name: Option<SyntaxToken>, - value: Option<SyntaxToken>, - }, - TokenTree { - path: AttrPath, - tt: ast::TokenTree, - }, - Path { - path: AttrPath, - }, + fn as_up_to_two_segment(&self) -> Option<(SmolStr, Option<SmolStr>)> { + let parent = self.qualifier().as_one_segment(); + let this = self.segment()?.name_ref()?.text().into(); + if let Some(parent) = parent { Some((parent, Some(this))) } else { Some((this, None)) } + } } -impl Meta { - #[inline] - pub fn path_range(&self) -> TextRange { - match self { - Meta::NamedKeyValue { path_range, .. } => *path_range, - Meta::TokenTree { path, .. } | Meta::Path { path } => path.range, - } +impl AstPathExt for Option<ast::Path> { + fn is1(&self, segment: &str) -> bool { + self.as_ref().is_some_and(|it| it.is1(segment)) } - fn extract(iter: &mut Peekable<TokenTreeChildren>) -> Option<(Self, TextSize)> { - let mut start_offset = None; - if let Some(NodeOrToken::Token(colon1)) = iter.peek() - && colon1.kind() == T![:] - { - start_offset = Some(colon1.text_range().start()); - iter.next(); - iter.next_if(|it| it.as_token().is_some_and(|it| it.kind() == T![:])); - } - let first_segment = iter - .next_if(|it| it.as_token().is_some_and(|it| it.kind().is_any_identifier()))? - .into_token()?; - let mut is_test = first_segment.text() == "test"; - let start_offset = start_offset.unwrap_or_else(|| first_segment.text_range().start()); - - let mut segments_len = 1; - let mut second_segment = None; - let mut path_range = first_segment.text_range(); - while iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:]) - && let _ = iter.next() - && iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:]) - && let _ = iter.next() - && let Some(NodeOrToken::Token(segment)) = iter.peek() - && segment.kind().is_any_identifier() - { - segments_len += 1; - is_test = segment.text() == "test"; - second_segment = Some(segment.clone()); - path_range = TextRange::new(path_range.start(), segment.text_range().end()); - iter.next(); - } + fn as_one_segment(&self) -> Option<SmolStr> { + self.as_ref().and_then(|it| it.as_one_segment()) + } - let segments = |first, second| { - let mut segments = ArrayVec::new(); - if segments_len <= 2 { - segments.push(first); - if let Some(second) = second { - segments.push(second); - } - } - segments - }; - let meta = match iter.peek() { - Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => { - iter.next(); - let value = match iter.peek() { - Some(NodeOrToken::Token(token)) if token.kind().is_literal() => { - // No need to consume it, it will be consumed by `extract_and_eat_comma()`. - Some(token.clone()) - } - _ => None, - }; - let name = if second_segment.is_none() { Some(first_segment) } else { None }; - Meta::NamedKeyValue { path_range, name, value } - } - Some(NodeOrToken::Node(tt)) => Meta::TokenTree { - path: AttrPath { - segments: segments(first_segment, second_segment), - range: path_range, - is_test, - }, - tt: tt.clone(), - }, - _ => Meta::Path { - path: AttrPath { - segments: segments(first_segment, second_segment), - range: path_range, - is_test, - }, - }, - }; - Some((meta, start_offset)) + fn as_up_to_two_segment(&self) -> Option<(SmolStr, Option<SmolStr>)> { + self.as_ref().and_then(|it| it.as_up_to_two_segment()) } +} + +pub trait AstKeyValueMetaExt { + fn value_string(&self) -> Option<SmolStr>; +} - fn extract_possibly_unsafe( - iter: &mut Peekable<TokenTreeChildren>, - container: &ast::TokenTree, - ) -> Option<(Self, TextRange)> { - if iter.peek().is_some_and(|it| it.as_token().is_some_and(|it| it.kind() == T![unsafe])) { - iter.next(); - let tt = iter.next()?.into_node()?; - let result = Self::extract(&mut TokenTreeChildren::new(&tt).peekable()).map( - |(meta, start_offset)| (meta, TextRange::new(start_offset, tt_end_offset(&tt))), - ); - while iter.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {} - result +impl AstKeyValueMetaExt for ast::KeyValueMeta { + fn value_string(&self) -> Option<SmolStr> { + if let Some(ast::Expr::Literal(value)) = self.expr() + && let ast::LiteralKind::String(value) = value.kind() + && let Ok(value) = value.value() + { + Some((*value).into()) } else { - Self::extract(iter).map(|(meta, start_offset)| { - let end_offset = 'find_end_offset: { - for it in iter { - if let NodeOrToken::Token(it) = it - && it.kind() == T![,] - { - break 'find_end_offset it.text_range().start(); - } - } - tt_end_offset(container) - }; - (meta, TextRange::new(start_offset, end_offset)) - }) + None } } } -fn tt_end_offset(tt: &ast::TokenTree) -> TextSize { - tt.syntax().last_token().unwrap().text_range().start() -} - -/// The callback is passed a desugared form of the attribute ([`Meta`]), a [`SyntaxNode`] fully containing it -/// (note: it may not be the direct parent), the range within the [`SyntaxNode`] bounding the attribute, -/// and the outermost `ast::Attr`. Note that one node may map to multiple [`Meta`]s due to `cfg_attr`. +/// The callback is passed the attribute and the outermost `ast::Attr`. +/// Note that one node may map to multiple [`Meta`]s due to `cfg_attr`. +/// +/// `unsafe(attr)` are passed the inner attribute for now. #[inline] pub fn expand_cfg_attr<'a, BreakValue>( attrs: impl Iterator<Item = ast::Attr>, cfg_options: impl FnMut() -> &'a CfgOptions, - mut callback: impl FnMut(Meta, &SyntaxNode, TextRange, &ast::Attr) -> ControlFlow<BreakValue>, + mut callback: impl FnMut(ast::Meta, ast::Attr) -> ControlFlow<BreakValue>, ) -> Option<BreakValue> { expand_cfg_attr_with_doc_comments::<Infallible, _>( attrs.map(Either::Left), cfg_options, - move |Either::Left((meta, container, range, top_attr))| { - callback(meta, container, range, top_attr) - }, + move |Either::Left((meta, top_attr))| callback(meta, top_attr), ) } @@ -264,66 +112,47 @@ pub fn expand_cfg_attr<'a, BreakValue>( pub fn expand_cfg_attr_with_doc_comments<'a, DocComment, BreakValue>( mut attrs: impl Iterator<Item = Either<ast::Attr, DocComment>>, mut cfg_options: impl FnMut() -> &'a CfgOptions, - mut callback: impl FnMut( - Either<(Meta, &SyntaxNode, TextRange, &ast::Attr), DocComment>, - ) -> ControlFlow<BreakValue>, + mut callback: impl FnMut(Either<(ast::Meta, ast::Attr), DocComment>) -> ControlFlow<BreakValue>, ) -> Option<BreakValue> { let mut stack = SmallVec::<[_; 1]>::new(); - let result = attrs.try_for_each(|top_attr| { - let top_attr = match top_attr { - Either::Left(it) => it, - Either::Right(comment) => return callback(Either::Right(comment)), - }; - if let Some((attr_name, tt)) = top_attr.as_simple_call() - && attr_name == "cfg_attr" - { - let mut tt_iter = TokenTreeChildren::new(&tt).peekable(); - let cfg = cfg::CfgExpr::parse_from_ast(&mut tt_iter); - if cfg_options().check(&cfg) != Some(false) { - stack.push((tt_iter, tt)); - while let Some((tt_iter, tt)) = stack.last_mut() { - let Some((attr, range)) = Meta::extract_possibly_unsafe(tt_iter, tt) else { - stack.pop(); - continue; - }; - if let Meta::TokenTree { path, tt: nested_tt } = &attr - && path.is1("cfg_attr") - { - let mut nested_tt_iter = TokenTreeChildren::new(nested_tt).peekable(); - let cfg = cfg::CfgExpr::parse_from_ast(&mut nested_tt_iter); - if cfg_options().check(&cfg) != Some(false) { - stack.push((nested_tt_iter, nested_tt.clone())); - } - } else { - callback(Either::Left((attr, tt.syntax(), range, &top_attr)))?; + loop { + let (mut meta, top_attr) = if let Some(it) = stack.pop() { + it + } else { + let attr = attrs.next()?; + match attr { + Either::Left(attr) => { + let Some(meta) = attr.meta() else { continue }; + stack.push((meta, attr)); + } + Either::Right(doc_comment) => { + if let ControlFlow::Break(break_value) = callback(Either::Right(doc_comment)) { + return Some(break_value); } } } - } else if let Some(ast_meta) = top_attr.meta() - && let Some(path) = ast_meta.path() - { - let path = AttrPath::extract(&path); - let meta = if let Some(tt) = ast_meta.token_tree() { - Meta::TokenTree { path, tt } - } else if let Some(value) = ast_meta.expr() { - let value = - if let ast::Expr::Literal(value) = value { Some(value.token()) } else { None }; - let name = - if path.segments.len() == 1 { Some(path.segments[0].clone()) } else { None }; - Meta::NamedKeyValue { name, value, path_range: path.range } - } else { - Meta::Path { path } - }; - callback(Either::Left(( - meta, - ast_meta.syntax(), - ast_meta.syntax().text_range(), - &top_attr, - )))?; + continue; + }; + + while let ast::Meta::UnsafeMeta(unsafe_meta) = &meta { + let Some(inner) = unsafe_meta.meta() else { continue }; + meta = inner; } - ControlFlow::Continue(()) - }); - result.break_value() + + if let ast::Meta::CfgAttrMeta(meta) = meta { + let Some(cfg_predicate) = meta.cfg_predicate() else { continue }; + let cfg_predicate = CfgExpr::parse_from_ast(cfg_predicate); + if cfg_options().check(&cfg_predicate) != Some(false) { + let prev_stack_len = stack.len(); + stack.extend(meta.metas().map(|meta| (meta, top_attr.clone()))); + stack[prev_stack_len..].reverse(); + } + } else { + if let ControlFlow::Break(break_value) = callback(Either::Left((meta, top_attr))) { + return Some(break_value); + } + } + } } #[inline] @@ -351,39 +180,33 @@ pub(crate) fn is_item_tree_filtered_attr(name: &str) -> bool { pub fn collect_item_tree_attrs<'a, BreakValue>( owner: &dyn ast::HasAttrs, cfg_options: impl Fn() -> &'a CfgOptions, - mut on_attr: impl FnMut(Meta, &SyntaxNode, &ast::Attr, TextRange) -> ControlFlow<BreakValue>, + mut on_attr: impl FnMut(ast::Meta, ast::Attr) -> ControlFlow<BreakValue>, ) -> Option<Either<BreakValue, CfgExpr>> { let attrs = ast::attrs_including_inner(owner); expand_cfg_attr( attrs, || cfg_options(), - |attr, container, range, top_attr| { + |attr, top_attr| { // We filter builtin attributes that we don't need for nameres, because this saves memory. // I only put the most common attributes, but if some attribute becomes common feel free to add it. // Notice, however: for an attribute to be filtered out, it *must* not be shadowable with a macro! let filter = match &attr { - Meta::NamedKeyValue { name: Some(name), .. } => { - is_item_tree_filtered_attr(name.text()) - } - Meta::TokenTree { path, tt } if path.segments.len() == 1 => { - let name = path.segments[0].text(); - if name == "cfg" { - let cfg = - CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable()); - if cfg_options().check(&cfg) == Some(false) { - return ControlFlow::Break(Either::Right(cfg)); - } - true - } else { - is_item_tree_filtered_attr(name) + ast::Meta::CfgMeta(attr) => { + let Some(cfg_predicate) = attr.cfg_predicate() else { + return ControlFlow::Continue(()); + }; + let cfg = CfgExpr::parse_from_ast(cfg_predicate); + if cfg_options().check(&cfg) == Some(false) { + return ControlFlow::Break(Either::Right(cfg)); } + true } - Meta::Path { path } => { - path.segments.len() == 1 && is_item_tree_filtered_attr(path.segments[0].text()) - } - _ => false, + _ => attr + .path() + .and_then(|path| path.as_one_segment()) + .is_some_and(|segment| is_item_tree_filtered_attr(&segment)), }; - if !filter && let ControlFlow::Break(v) = on_attr(attr, container, top_attr, range) { + if !filter && let ControlFlow::Break(v) = on_attr(attr, top_attr) { return ControlFlow::Break(Either::Left(v)); } ControlFlow::Continue(()) @@ -540,34 +363,32 @@ impl AttrId { } /// Returns the containing `ast::Attr` (note that it may contain other attributes as well due - /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the - /// attribute, and its desugared [`Meta`]. + /// to `cfg_attr`) and its [`ast::Meta`]. pub fn find_attr_range<N: ast::HasAttrs>( self, db: &dyn ExpandDatabase, krate: Crate, owner: AstId<N>, - ) -> (ast::Attr, SyntaxNode, TextRange, Meta) { + ) -> (ast::Attr, ast::Meta) { self.find_attr_range_with_source(db, krate, &owner.to_node(db)) } /// Returns the containing `ast::Attr` (note that it may contain other attributes as well due - /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the - /// attribute, and its desugared [`Meta`]. + /// to `cfg_attr`) and its [`ast::Meta`]. pub fn find_attr_range_with_source( self, db: &dyn ExpandDatabase, krate: Crate, owner: &dyn ast::HasAttrs, - ) -> (ast::Attr, SyntaxNode, TextRange, Meta) { + ) -> (ast::Attr, ast::Meta) { let cfg_options = OnceCell::new(); let mut index = 0; let result = collect_item_tree_attrs( owner, || cfg_options.get_or_init(|| krate.cfg_options(db)), - |meta, container, top_attr, range| { + |meta, top_attr| { if index == self.id { - return ControlFlow::Break((top_attr.clone(), container.clone(), range, meta)); + return ControlFlow::Break((top_attr, meta)); } index += 1; ControlFlow::Continue(()) @@ -588,9 +409,12 @@ impl AttrId { owner: AstId<ast::Adt>, derive_index: u32, ) -> TextRange { - let (_, _, derive_attr_range, derive_attr) = self.find_attr_range(db, krate, owner); - let Meta::TokenTree { tt, .. } = derive_attr else { - return derive_attr_range; + let (_, derive_attr) = self.find_attr_range(db, krate, owner); + let ast::Meta::TokenTreeMeta(derive_attr) = derive_attr else { + return derive_attr.syntax().text_range(); + }; + let Some(tt) = derive_attr.token_tree() else { + return derive_attr.syntax().text_range(); }; // Fake the span map, as we don't really need spans here, just the offsets of the node in the file. let span_map = RealSpanMap::absolute(span::EditionedFileId::current_edition( @@ -605,11 +429,11 @@ impl AttrId { let Some((_, _, derive_tts)) = parse_path_comma_token_tree(db, &tt).nth(derive_index as usize) else { - return derive_attr_range; + return derive_attr.syntax().text_range(); }; let (Some(first_span), Some(last_span)) = (derive_tts.first_span(), derive_tts.last_span()) else { - return derive_attr_range; + return derive_attr.syntax().text_range(); }; let start = first_span.range.start(); let end = last_span.range.end(); diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs index b3572a1cef..9962677a9d 100644 --- a/crates/hir-expand/src/builtin/fn_macro.rs +++ b/crates/hir-expand/src/builtin/fn_macro.rs @@ -828,7 +828,7 @@ fn include_expand( let span_map = db.real_span_map(editioned_file_id); // FIXME: Parse errors ExpandResult::ok(syntax_node_to_token_tree( - &db.parse(editioned_file_id).syntax_node(), + &editioned_file_id.parse(db).syntax_node(), SpanMap::RealSpanMap(span_map), span, syntax_bridge::DocCommentDesugarMode::ProcMacro, diff --git a/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs index ccef9168ac..6258fac0e9 100644 --- a/crates/hir-expand/src/cfg_process.rs +++ b/crates/hir-expand/src/cfg_process.rs @@ -8,12 +8,12 @@ use parser::T; use smallvec::SmallVec; use syntax::{ AstNode, PreorderWithTokens, SyntaxElement, SyntaxNode, SyntaxToken, WalkEvent, - ast::{self, HasAttrs, TokenTreeChildren}, + ast::{self, HasAttrs}, }; use syntax_bridge::DocCommentDesugarMode; use crate::{ - attrs::{AttrId, Meta, expand_cfg_attr, is_item_tree_filtered_attr}, + attrs::{AstPathExt, AttrId, expand_cfg_attr, is_item_tree_filtered_attr}, db::ExpandDatabase, fixup::{self, SyntaxFixupUndoInfo}, span_map::SpanMapRef, @@ -24,7 +24,7 @@ struct ItemIsCfgedOut; #[derive(Debug)] struct ExpandedAttrToProcess { - range: TextRange, + attr: ast::Meta, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -143,42 +143,29 @@ fn macro_input_callback( }); attrs_idx = 0; - let strip_current_item = expand_cfg_attr( - node_attrs, - &cfg_options, - |attr, _container, range, top_attr| { + let strip_current_item = + expand_cfg_attr(node_attrs, &cfg_options, |attr, top_attr| { // Find the attr. while attrs[attrs_idx].range != top_attr.syntax().text_range() { attrs_idx += 1; } let mut strip_current_attr = false; - match attr { - Meta::NamedKeyValue { name, .. } => { - if name - .is_none_or(|name| !is_item_tree_filtered_attr(name.text())) - { - strip_current_attr = should_strip_attr(); - } - } - Meta::TokenTree { path, tt } => { - if path.is1("cfg") { - let cfg_expr = CfgExpr::parse_from_ast( - &mut TokenTreeChildren::new(&tt).peekable(), - ); + match &attr { + ast::Meta::CfgMeta(attr) => { + if let Some(cfg_predicate) = attr.cfg_predicate() { + let cfg_expr = CfgExpr::parse_from_ast(cfg_predicate); if cfg_options().check(&cfg_expr) == Some(false) { return ControlFlow::Break(ItemIsCfgedOut); } strip_current_attr = true; - } else if path.segments.len() != 1 - || !is_item_tree_filtered_attr(path.segments[0].text()) - { - strip_current_attr = should_strip_attr(); } } - Meta::Path { path } => { - if path.segments.len() != 1 - || !is_item_tree_filtered_attr(path.segments[0].text()) + _ => { + if attr + .path() + .as_one_segment() + .is_none_or(|name| !is_item_tree_filtered_attr(&name)) { strip_current_attr = should_strip_attr(); } @@ -188,12 +175,11 @@ fn macro_input_callback( if !strip_current_attr { attrs[attrs_idx] .expanded_attrs - .push(ExpandedAttrToProcess { range }); + .push(ExpandedAttrToProcess { attr }); } ControlFlow::Continue(()) - }, - ); + }); attrs_idx = 0; if strip_current_item.is_some() { @@ -248,7 +234,7 @@ fn macro_input_callback( }; match ast_attr.next_expanded_attr { NextExpandedAttrState::NotStarted => { - if token_range.start() >= expanded_attr.range.start() { + if token_range.start() >= expanded_attr.attr.syntax().text_range().start() { // We started the next attribute. let mut insert_tokens = Vec::with_capacity(3); insert_tokens.push(tt::Leaf::Punct(tt::Punct { @@ -278,7 +264,7 @@ fn macro_input_callback( } } NextExpandedAttrState::InTheMiddle => { - if token_range.start() >= expanded_attr.range.end() { + if token_range.start() >= expanded_attr.attr.syntax().text_range().end() { // Finished the current attribute. let insert_tokens = vec![tt::Leaf::Punct(tt::Punct { char: ']', @@ -329,12 +315,3 @@ pub(crate) fn attr_macro_input_to_token_tree( fixups.undo_info, ) } - -pub fn check_cfg_attr_value( - db: &dyn ExpandDatabase, - attr: &ast::TokenTree, - krate: Crate, -) -> Option<bool> { - let cfg_expr = CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(attr).peekable()); - krate.cfg_options(db).check(&cfg_expr) -} diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index 020731cf9a..8dddddfabb 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -1,6 +1,6 @@ //! Defines database & queries for macro expansion. -use base_db::{Crate, RootQueryDb}; +use base_db::{Crate, SourceDatabase}; use mbe::MatchedArmIndex; use span::{AstIdMap, Edition, Span, SyntaxContext}; use syntax::{AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, ast}; @@ -11,7 +11,6 @@ use crate::{ AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, FileRange, HirFileId, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, - attrs::Meta, builtin::pseudo_derive_attr_expansion, cfg_process::attr_macro_input_to_token_tree, declarative::DeclarativeMacroExpander, @@ -48,7 +47,7 @@ pub enum TokenExpander { } #[query_group::query_group] -pub trait ExpandDatabase: RootQueryDb { +pub trait ExpandDatabase: SourceDatabase { /// The proc macros. Do not use this! Use `proc_macros_for_crate()` instead. #[salsa::input] fn proc_macros(&self) -> Arc<ProcMacros>; @@ -239,8 +238,15 @@ pub fn expand_speculative( MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => { if loc.def.is_attribute_derive() { // for pseudo-derive expansion we actually pass the attribute itself only - ast::Attr::cast(speculative_args.clone()).and_then(|attr| attr.token_tree()).map( - |token_tree| { + ast::Attr::cast(speculative_args.clone()) + .and_then(|attr| { + if let ast::Meta::TokenTreeMeta(meta) = attr.meta()? { + meta.token_tree() + } else { + None + } + }) + .map(|token_tree| { let mut tree = syntax_node_to_token_tree( token_tree.syntax(), span_map, @@ -250,26 +256,26 @@ pub fn expand_speculative( tree.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); tree.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span)); tree - }, - ) + }) } else { // Attributes may have an input token tree, build the subtree and map for this as well // then try finding a token id for our token if it is inside this input subtree. let item = ast::Item::cast(speculative_args.clone())?; - let (_, _, _, meta) = + let (_, meta) = attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &item); - match meta { - Meta::TokenTree { tt, .. } => { - let mut attr_arg = syntax_bridge::syntax_node_to_token_tree( - tt.syntax(), - span_map, - span, - DocCommentDesugarMode::ProcMacro, - ); - attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); - Some(attr_arg) - } - _ => None, + if let ast::Meta::TokenTreeMeta(meta) = meta + && let Some(tt) = meta.token_tree() + { + let mut attr_arg = syntax_bridge::syntax_node_to_token_tree( + tt.syntax(), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); + attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + Some(attr_arg) + } else { + None } } } @@ -343,7 +349,7 @@ fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> AstIdMap { /// file or a macro expansion. fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode { match file_id { - HirFileId::FileId(file_id) => db.parse(file_id).syntax_node(), + HirFileId::FileId(file_id) => file_id.parse(db).syntax_node(), HirFileId::MacroFile(macro_file) => { db.parse_macro_expansion(macro_file).value.0.syntax_node() } @@ -389,7 +395,7 @@ pub(crate) fn parse_with_map( ) -> (Parse<SyntaxNode>, SpanMap) { match file_id { HirFileId::FileId(file_id) => { - (db.parse(file_id).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id))) + (file_id.parse(db).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id))) } HirFileId::MacroFile(macro_file) => { let (parse, map) = db.parse_macro_expansion(macro_file).value; @@ -501,11 +507,11 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { } MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { let node = ast_id.to_ptr(db).to_node(&root); - let range = attr_ids - .invoc_attr() - .find_attr_range_with_source(db, loc.krate, &node) - .3 - .path_range(); + let (_, attr) = attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &node); + let range = attr + .path() + .map(|path| path.syntax().text_range()) + .unwrap_or_else(|| attr.syntax().text_range()); let span = map.span_for_range(range); let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive()); diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs index 1726412275..4b2c6e7351 100644 --- a/crates/hir-expand/src/declarative.rs +++ b/crates/hir-expand/src/declarative.rs @@ -6,7 +6,7 @@ use base_db::Crate; use span::{Edition, Span, SyntaxContext}; use stdx::TupleExt; use syntax::{ - AstNode, AstToken, + AstNode, ast::{self, HasAttrs}, }; use syntax_bridge::DocCommentDesugarMode; @@ -15,7 +15,7 @@ use triomphe::Arc; use crate::{ AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId, MacroCallStyle, - attrs::{Meta, expand_cfg_attr}, + attrs::{AstKeyValueMetaExt, AstPathExt, expand_cfg_attr}, db::ExpandDatabase, hygiene::{Transparency, apply_mark}, tt, @@ -92,11 +92,10 @@ impl DeclarativeMacroExpander { expand_cfg_attr( node.attrs(), || cfg_options.get_or_init(|| def_crate.cfg_options(db)), - |attr, _, _, _| { - if let Meta::NamedKeyValue { name: Some(name), value, .. } = attr - && name.text() == "rustc_macro_transparency" - && let Some(value) = value.and_then(ast::String::cast) - && let Ok(value) = value.value() + |attr, _| { + if let ast::Meta::KeyValueMeta(attr) = attr + && attr.path().is1("rustc_macro_transparency") + && let Some(value) = attr.value_string() { match &*value { "transparent" => ControlFlow::Break(Transparency::Transparent), diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs index fce92c8a3e..71da560b15 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -198,7 +198,7 @@ trait FileIdToSyntax: Copy { impl FileIdToSyntax for EditionedFileId { fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode { - db.parse(self).syntax_node() + self.parse(db).syntax_node() } } impl FileIdToSyntax for MacroCallId { @@ -333,8 +333,8 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> { )?; let kind = self.kind(); - let value = db - .parse(editioned_file_id) + let value = editioned_file_id + .parse(db) .syntax_node() .covering_element(range) .ancestors() @@ -521,7 +521,7 @@ impl<N: AstNode> InFile<N> { )?; // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes? - let anc = db.parse(editioned_file_id).syntax_node().covering_element(range); + let anc = editioned_file_id.parse(db).syntax_node().covering_element(range); let value = anc.ancestors().find_map(N::cast)?; Some(InRealFile::new(editioned_file_id, value)) } diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 4b2c75ed38..8d42a24e2f 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -58,7 +58,6 @@ use crate::{ }; pub use crate::{ - cfg_process::check_cfg_attr_value, files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile}, prettify_macro_expansion_::prettify_macro_expansion, }; @@ -635,14 +634,12 @@ impl MacroCallLoc { ast_id.with_value(ast_id.to_node(db).syntax().clone()) } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { - // FIXME: handle `cfg_attr` - let (attr, _, _, _) = derive_attr_index.find_attr_range(db, self.krate, *ast_id); + let (_, attr) = derive_attr_index.find_attr_range(db, self.krate, *ast_id); ast_id.with_value(attr.syntax().clone()) } MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { if self.def.is_attribute_derive() { - let (attr, _, _, _) = - attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id); + let (_, attr) = attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id); ast_id.with_value(attr.syntax().clone()) } else { ast_id.with_value(ast_id.to_node(db).syntax().clone()) @@ -770,11 +767,11 @@ impl MacroCallKind { } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive - derive_attr_index.find_attr_range(db, krate, ast_id).2 + derive_attr_index.find_attr_range(db, krate, ast_id).1.syntax().text_range() } // FIXME: handle `cfg_attr` MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { - attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).2 + attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).1.syntax().text_range() } }; diff --git a/crates/hir-expand/src/span_map.rs b/crates/hir-expand/src/span_map.rs index 71d0b880ca..aa8603341b 100644 --- a/crates/hir-expand/src/span_map.rs +++ b/crates/hir-expand/src/span_map.rs @@ -86,7 +86,7 @@ pub(crate) fn real_span_map( let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)]; let ast_id_map = db.ast_id_map(editioned_file_id.into()); - let tree = db.parse(editioned_file_id).tree(); + let tree = editioned_file_id.parse(db).tree(); // This is an incrementality layer. Basically we can't use absolute ranges for our spans as that // would mean we'd invalidate everything whenever we type. So instead we make the text ranges // relative to some AstIds reducing the risk of invalidation as typing somewhere no longer diff --git a/crates/hir-ty/src/builtin_derive.rs b/crates/hir-ty/src/builtin_derive.rs index 92629b7a05..eb3922f4b6 100644 --- a/crates/hir-ty/src/builtin_derive.rs +++ b/crates/hir-ty/src/builtin_derive.rs @@ -174,8 +174,11 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> if matches!(loc.adt, AdtId::EnumId(_)) { // Enums don't have extra bounds. GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( - Clauses::new_from_slice(adt_predicates.explicit_predicates().skip_binder()) - .store(), + Clauses::new_from_iter( + interner, + adt_predicates.own_explicit_predicates().skip_binder(), + ) + .store(), )) } else { simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id) @@ -191,7 +194,7 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> )); }; let duplicated_bounds = - adt_predicates.explicit_predicates().iter_identity_copied().filter_map(|pred| { + adt_predicates.explicit_predicates().iter_identity().filter_map(|pred| { let mentions_pointee = pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break(); if !mentions_pointee { @@ -212,7 +215,7 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> interner, adt_predicates .explicit_predicates() - .iter_identity_copied() + .iter_identity() .chain(duplicated_bounds) .chain(unsize_bound), ) @@ -313,7 +316,7 @@ fn simple_trait_predicates<'db>( interner, adt_predicates .explicit_predicates() - .iter_identity_copied() + .iter_identity() .chain(extra_predicates) .chain(assoc_type_bounds), ) @@ -440,7 +443,7 @@ mod tests { format_to!( predicates, "{}\n\n", - preds.iter().format_with("\n", |pred, formatter| formatter(&format_args!( + preds.format_with("\n", |pred, formatter| formatter(&format_args!( "{pred:?}" ))), ); diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 31cf86476f..aee27dcfde 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -1,4 +1,4 @@ -use base_db::RootQueryDb; +use base_db::all_crates; use hir_def::signatures::ConstSignature; use hir_expand::EditionedFileId; use rustc_apfloat::{ @@ -108,7 +108,7 @@ fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String { let mut err = String::new(); let span_formatter = |file, range| format!("{file:?} {range:?}"); let display_target = - DisplayTarget::from_crate(db, *db.all_crates().last().expect("no crate graph present")); + DisplayTarget::from_crate(db, *all_crates(db).last().expect("no crate graph present")); match e { ConstEvalError::MirLowerError(e) => { e.pretty_print(&mut err, db, span_formatter, display_target) diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index d680588645..0c4e34db7d 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -640,7 +640,7 @@ fn write_projection<'db>( // FIXME: We shouldn't use `param.id`, it should be removed. We should know the // `GenericDefId` from the formatted type (store it inside the `HirFormatter`). let bounds = GenericPredicates::query_all(f.db, param.id.parent()) - .iter_identity_copied() + .iter_identity() .filter(|wc| { let ty = match wc.kind().skip_binder() { ClauseKind::Trait(tr) => tr.self_ty(), @@ -1466,7 +1466,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TypeParamProvenance::ArgumentImplTrait => { let bounds = GenericPredicates::query_all(f.db, param.id.parent()) - .iter_identity_copied() + .iter_identity() .filter(|wc| match wc.kind().skip_binder() { ClauseKind::Trait(tr) => tr.self_ty() == *self, ClauseKind::Projection(proj) => proj.self_ty() == *self, diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs index 4c300affd8..e70918f8e1 100644 --- a/crates/hir-ty/src/dyn_compatibility.rs +++ b/crates/hir-ty/src/dyn_compatibility.rs @@ -141,7 +141,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b // FIXME: We should use `explicit_predicates_of` here, which hasn't been implemented to // rust-analyzer yet // https://github.com/rust-lang/rust/blob/ddaf12390d3ffb7d5ba74491a48f3cd528e5d777/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L490 - elaborate::elaborate(interner, predicates.iter_identity_copied()).any(|pred| { + elaborate::elaborate(interner, predicates.iter_identity()).any(|pred| { match pred.kind().skip_binder() { ClauseKind::Trait(trait_pred) => { if sized == trait_pred.def_id().0 @@ -164,7 +164,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b // So, just return single boolean value for existence of such `Self` reference fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { GenericPredicates::query_explicit(db, trait_.into()) - .iter_identity_copied() + .iter_identity() .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) } @@ -360,8 +360,8 @@ where cb(MethodViolationCode::UndispatchableReceiver)?; } - let predicates = GenericPredicates::query_own(db, func.into()); - for pred in predicates.iter_identity_copied() { + let predicates = GenericPredicates::query_own_explicit(db, func.into()); + for pred in predicates.iter_identity() { let pred = pred.kind().skip_binder(); if matches!(pred, ClauseKind::TypeOutlives(_)) { @@ -459,7 +459,7 @@ fn receiver_is_dispatchable<'db>( clauses: Clauses::new_from_iter( interner, generic_predicates - .iter_identity_copied() + .iter_identity() .chain([unsize_predicate.upcast(interner), trait_predicate.upcast(interner)]) .chain(meta_sized_predicate), ), diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index d14e9d6526..bd897113bf 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -1706,6 +1706,61 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.generic_def, LifetimeElisionKind::Infer, ); + + if let Some(type_anchor) = path.type_anchor() { + let mut segments = path.segments(); + if segments.is_empty() { + return (self.err_ty(), None); + } + let (mut ty, type_ns) = ctx.lower_ty_ext(type_anchor); + ty = self.table.process_user_written_ty(ty); + + if let Some(TypeNs::SelfType(impl_)) = type_ns + && let Some(trait_ref) = self.db.impl_trait(impl_) + && let trait_ref = trait_ref.instantiate_identity() + && let Some(assoc_type) = trait_ref + .def_id + .0 + .trait_items(self.db) + .associated_type_by_name(segments.first().unwrap().name) + { + // `<Self>::AssocType` + let args = self.infcx().fill_rest_fresh_args(assoc_type.into(), trait_ref.args); + let alias = Ty::new_alias( + self.interner(), + AliasTyKind::Projection, + AliasTy::new_from_args(self.interner(), assoc_type.into(), args), + ); + ty = self.table.try_structurally_resolve_type(alias); + segments = segments.skip(1); + } + + let variant = match ty.as_adt() { + Some((AdtId::StructId(id), _)) => id.into(), + Some((AdtId::UnionId(id), _)) => id.into(), + Some((AdtId::EnumId(id), _)) => { + if let Some(segment) = segments.first() + && let enum_data = id.enum_variants(self.db) + && let Some(variant) = enum_data.variant(segment.name) + { + // FIXME: Report error if there are generics on the variant. + segments = segments.skip(1); + variant.into() + } else { + return (self.err_ty(), None); + } + } + None => return (self.err_ty(), None), + }; + + if !segments.is_empty() { + // FIXME: Report an error. + return (self.err_ty(), None); + } else { + return (ty, Some(variant)); + } + } + let mut path_ctx = ctx.at_path(path, node); let interner = DbInterner::conjure(); let (resolution, unresolved) = if value_ns { @@ -1838,6 +1893,46 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }); (ty, variant) } + TypeNs::TraitId(_) => { + let Some(remaining_idx) = unresolved else { + return (self.err_ty(), None); + }; + + let remaining_segments = path.segments().skip(remaining_idx); + + if remaining_segments.len() >= 2 { + path_ctx.ignore_last_segment(); + } + + let (mut ty, _) = path_ctx.lower_partly_resolved_path(resolution, true); + ty = self.table.process_user_written_ty(ty); + + if let Some(segment) = remaining_segments.get(1) + && let Some((AdtId::EnumId(id), _)) = ty.as_adt() + { + let enum_data = id.enum_variants(self.db); + if let Some(variant) = enum_data.variant(segment.name) { + return if remaining_segments.len() == 2 { + (ty, Some(variant.into())) + } else { + // We still have unresolved paths, but enum variants never have + // associated types! + // FIXME: Report an error. + (self.err_ty(), None) + }; + } + } + + let variant = ty.as_adt().and_then(|(id, _)| match id { + AdtId::StructId(s) => Some(VariantId::StructId(s)), + AdtId::UnionId(u) => Some(VariantId::UnionId(u)), + AdtId::EnumId(_) => { + // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` + None + } + }); + (ty, variant) + } TypeNs::TypeAliasId(it) => { let Some(mod_path) = path.mod_path() else { never!("resolver should always resolve lang item paths"); @@ -1859,10 +1954,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // FIXME potentially resolve assoc type (self.err_ty(), None) } - TypeNs::AdtId(AdtId::EnumId(_)) - | TypeNs::BuiltinType(_) - | TypeNs::TraitId(_) - | TypeNs::ModuleId(_) => { + TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::ModuleId(_) => { // FIXME diagnostic (self.err_ty(), None) } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index ce99016470..b868f02342 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -164,13 +164,7 @@ impl<'db> InferenceContext<'_, 'db> { let coroutine_captures_by_ref_ty = Ty::new_fn_ptr( interner, Binder::bind_with_vars( - interner.mk_fn_sig( - [], - self.types.types.unit, - false, - Safety::Safe, - FnAbi::Rust, - ), + interner.mk_fn_sig_safe_rust_abi([], self.types.types.unit), self.types.coroutine_captures_by_ref_bound_var_kinds, ), ); @@ -484,13 +478,8 @@ impl<'db> InferenceContext<'_, 'db> { let ret_param_ty = projection.skip_binder().term.expect_type(); debug!(?ret_param_ty); - let sig = projection.rebind(self.interner().mk_fn_sig( - input_tys, - ret_param_ty, - false, - Safety::Safe, - FnAbi::Rust, - )); + let sig = + projection.rebind(self.interner().mk_fn_sig_safe_rust_abi(input_tys, ret_param_ty)); Some(sig) } @@ -572,13 +561,8 @@ impl<'db> InferenceContext<'_, 'db> { // that does not misuse a `FnSig` type, but that can be done separately. let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var()); - let sig = projection.rebind(self.interner().mk_fn_sig( - input_tys, - return_ty, - false, - Safety::Safe, - FnAbi::Rust, - )); + let sig = + projection.rebind(self.interner().mk_fn_sig_safe_rust_abi(input_tys, return_ty)); Some(sig) } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index dc57b1d1c2..ee34a30eba 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -2158,7 +2158,7 @@ impl<'db> InferenceContext<'_, 'db> { ); let param_env = self.table.param_env; self.table.register_predicates(clauses_as_obligations( - generic_predicates.iter_instantiated_copied(self.interner(), parameters.as_slice()), + generic_predicates.iter_instantiated(self.interner(), parameters.as_slice()), ObligationCause::new(), param_env, )); diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 71d68ccd47..3cadc8e933 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -228,7 +228,7 @@ impl<'db> InferenceContext<'_, 'db> { let predicates = GenericPredicates::query_all(self.db, def); let param_env = self.table.param_env; self.table.register_predicates(clauses_as_obligations( - predicates.iter_instantiated_copied(interner, subst.as_slice()), + predicates.iter_instantiated(interner, subst.as_slice()), ObligationCause::new(), param_env, )); diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 7259099107..71a7db6559 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -2016,17 +2016,21 @@ fn type_alias_bounds_with_diagnostics<'db>( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericPredicates { - // The order is the following: first, if `parent_is_trait == true`, comes the implicit trait - // predicate for the parent. Then come the bounds of the associated types of the parents, - // then the explicit, self-only predicates for the parent, then the explicit, self-only trait - // predicate for the child, then the bounds of the associated types of the child, - // then the implicit trait predicate for the child, if `is_trait` is `true`. + // The order is the following: + // + // 1. If `has_trait_implied_predicate == true`, the implicit trait predicate. + // 2. The bounds of the associated types of the parents, coming from `Trait<Assoc: Trait>`. + // Note: associated type bounds from `Self::Assoc: Trait` on traits *won't* be included + // here, they are in 3. + // 3. The explicit, self-only predicates for the parent. + // 4. The explicit, self-only trait predicate for the child, + // 5. The bounds of the associated types of the child. predicates: StoredEarlyBinder<StoredClauses>, + // Keep this ordered according to the above. + has_trait_implied_predicate: bool, parent_explicit_self_predicates_start: u32, own_predicates_start: u32, own_assoc_ty_bounds_start: u32, - is_trait: bool, - parent_is_trait: bool, } #[salsa::tracked] @@ -2065,11 +2069,10 @@ impl GenericPredicates { let len = predicates.get().skip_binder().len() as u32; Self { predicates, + has_trait_implied_predicate: false, parent_explicit_self_predicates_start: 0, own_predicates_start: 0, own_assoc_ty_bounds_start: len, - is_trait: false, - parent_is_trait: false, } } @@ -2082,58 +2085,68 @@ impl GenericPredicates { pub fn query_all<'db>( db: &'db dyn HirDatabase, def: GenericDefId, - ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + ) -> EarlyBinder<'db, impl Iterator<Item = Clause<'db>>> { Self::query(db, def).all_predicates() } #[inline] - pub fn query_own<'db>( + pub fn query_own_explicit<'db>( db: &'db dyn HirDatabase, def: GenericDefId, - ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - Self::query(db, def).own_predicates() + ) -> EarlyBinder<'db, impl Iterator<Item = Clause<'db>>> { + Self::query(db, def).own_explicit_predicates() } #[inline] pub fn query_explicit<'db>( db: &'db dyn HirDatabase, def: GenericDefId, - ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + ) -> EarlyBinder<'db, impl Iterator<Item = Clause<'db>>> { Self::query(db, def).explicit_predicates() } #[inline] - pub fn query_explicit_implied<'db>( - db: &'db dyn HirDatabase, - def: GenericDefId, - ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - Self::query(db, def).explicit_implied_predicates() + pub fn all_predicates(&self) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> { + self.predicates.get().map_bound(|it| it.as_slice().iter().copied()) } #[inline] - pub fn all_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { - self.predicates.get().map_bound(|it| it.as_slice()) + pub fn own_explicit_predicates(&self) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> { + self.predicates + .get() + .map_bound(|it| it.as_slice()[self.own_predicates_start as usize..].iter().copied()) } #[inline] - pub fn own_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { - self.predicates.get().map_bound(|it| &it.as_slice()[self.own_predicates_start as usize..]) + pub fn explicit_predicates(&self) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> { + self.predicates.get().map_bound(|it| { + it.as_slice()[usize::from(self.has_trait_implied_predicate)..].iter().copied() + }) } - /// Returns the predicates, minus the implicit `Self: Trait` predicate and bounds of the - /// associated types for a trait. #[inline] - pub fn explicit_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { + pub fn explicit_non_assoc_types_predicates( + &self, + ) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> { self.predicates.get().map_bound(|it| { - &it.as_slice()[self.parent_explicit_self_predicates_start as usize + it.as_slice()[self.parent_explicit_self_predicates_start as usize ..self.own_assoc_ty_bounds_start as usize] + .iter() + .copied() }) } #[inline] - pub fn explicit_implied_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { - self.predicates.get().map_bound(|it| { - &it.as_slice()[usize::from(self.parent_is_trait)..it.len() - usize::from(self.is_trait)] + pub fn explicit_assoc_types_predicates( + &self, + ) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> { + self.predicates.get().map_bound(|predicates| { + let predicates = predicates.as_slice(); + predicates[usize::from(self.has_trait_implied_predicate) + ..self.parent_explicit_self_predicates_start as usize] + .iter() + .copied() + .chain(predicates[self.own_assoc_ty_bounds_start as usize..].iter().copied()) }) } } @@ -2142,10 +2155,8 @@ pub(crate) fn param_env_from_predicates<'db>( interner: DbInterner<'db>, predicates: &'db GenericPredicates, ) -> ParamEnv<'db> { - let clauses = rustc_type_ir::elaborate::elaborate( - interner, - predicates.all_predicates().iter_identity_copied(), - ); + let clauses = + rustc_type_ir::elaborate::elaborate(interner, predicates.all_predicates().iter_identity()); let clauses = Clauses::new_from_iter(interner, clauses); // FIXME: We should normalize projections here, like rustc does. @@ -2290,42 +2301,28 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic let diagnostics = create_diagnostics(ctx.diagnostics); - // The order is: - // - // 1. parent implicit trait pred - // 2. parent assoc bounds - // 3. parent self only preds - // 4. own self only preds - // 5. own assoc ty bounds - // 6. own implicit trait pred - // - // The purpose of this is to index the slice of the followings, without making extra `Vec`s or - // iterators: - // - explicit self only predicates, of own or own + self - // - explicit predicates, of own or own + self let predicates = parent_implicit_trait_predicate .iter() + .chain(own_implicit_trait_predicate.iter()) .chain(parent_assoc_ty_bounds.iter()) .chain(parent_predicates.iter()) .chain(own_predicates.iter()) .chain(own_assoc_ty_bounds.iter()) - .chain(own_implicit_trait_predicate.iter()) .copied() .collect::<Vec<_>>(); - let parent_is_trait = parent_implicit_trait_predicate.is_some(); - let is_trait = own_implicit_trait_predicate.is_some(); + let has_trait_implied_predicate = + parent_implicit_trait_predicate.is_some() || own_implicit_trait_predicate.is_some(); let parent_explicit_self_predicates_start = - parent_is_trait as u32 + parent_assoc_ty_bounds.len() as u32; + has_trait_implied_predicate as u32 + parent_assoc_ty_bounds.len() as u32; let own_predicates_start = parent_explicit_self_predicates_start + parent_predicates.len() as u32; let own_assoc_ty_bounds_start = own_predicates_start + own_predicates.len() as u32; let predicates = GenericPredicates { + has_trait_implied_predicate, parent_explicit_self_predicates_start, own_predicates_start, own_assoc_ty_bounds_start, - is_trait, - parent_is_trait, predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), }; return (predicates, diagnostics); diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 05b9ea5d74..b18e48c1fe 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -324,7 +324,7 @@ impl<'db> InferenceTable<'db> { // any late-bound regions appearing in its bounds. let bounds = GenericPredicates::query_all(self.db, method_item.into()); let bounds = clauses_as_obligations( - bounds.iter_instantiated_copied(interner, args.as_slice()), + bounds.iter_instantiated(interner, args.as_slice()), ObligationCause::new(), self.param_env, ); diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs index ec589085a8..94c70c29f7 100644 --- a/crates/hir-ty/src/method_resolution/confirm.rs +++ b/crates/hir-ty/src/method_resolution/confirm.rs @@ -136,7 +136,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { ); let illegal_sized_bound = self.predicates_require_illegal_sized_bound( GenericPredicates::query_all(self.db(), self.candidate.into()) - .iter_instantiated_copied(self.interner(), filler_args.as_slice()), + .iter_instantiated(self.interner(), filler_args.as_slice()), ); // Unify the (adjusted) self type with what the method expects. @@ -509,7 +509,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { let def_id = self.candidate; let method_predicates = clauses_as_obligations( GenericPredicates::query_all(self.db(), def_id.into()) - .iter_instantiated_copied(self.interner(), all_args), + .iter_instantiated(self.interner(), all_args), ObligationCause::new(), self.ctx.table.param_env, ); diff --git a/crates/hir-ty/src/method_resolution/probe.rs b/crates/hir-ty/src/method_resolution/probe.rs index 8c76bfbc07..3604076ccd 100644 --- a/crates/hir-ty/src/method_resolution/probe.rs +++ b/crates/hir-ty/src/method_resolution/probe.rs @@ -1595,7 +1595,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // Check whether the impl imposes obligations we have to worry about. let impl_bounds = GenericPredicates::query_all(self.db(), impl_def_id.into()); let impl_bounds = clauses_as_obligations( - impl_bounds.iter_instantiated_copied(self.interner(), impl_args.as_slice()), + impl_bounds.iter_instantiated(self.interner(), impl_args.as_slice()), ObligationCause::new(), self.param_env(), ); diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index ff6c99ca53..2aed76ec90 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -518,7 +518,7 @@ impl<'db> Evaluator<'db> { "sched_getaffinity" => { let [_pid, _set_size, set] = args else { return Err(MirEvalError::InternalError( - "libc::write args are not provided".into(), + "sched_getaffinity args are not provided".into(), )); }; let set = Address::from_bytes(set.get(self)?)?; @@ -530,9 +530,7 @@ impl<'db> Evaluator<'db> { } "getenv" => { let [name] = args else { - return Err(MirEvalError::InternalError( - "libc::write args are not provided".into(), - )); + return Err(MirEvalError::InternalError("getenv args are not provided".into())); }; let mut name_buf = vec![]; let name = { diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs index 5b81c7675d..5d7ad84e1f 100644 --- a/crates/hir-ty/src/next_solver/interner.rs +++ b/crates/hir-ty/src/next_solver/interner.rs @@ -1439,81 +1439,55 @@ impl<'db> Interner for DbInterner<'db> { } } - #[tracing::instrument(level = "debug", skip(self), ret)] fn predicates_of( self, def_id: Self::DefId, ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> { - predicates_of(self.db, def_id).all_predicates().map_bound(|it| it.iter().copied()) + predicates_of(self.db, def_id).all_predicates() } - #[tracing::instrument(level = "debug", skip(self), ret)] fn own_predicates_of( self, def_id: Self::DefId, ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> { - predicates_of(self.db, def_id).own_predicates().map_bound(|it| it.iter().copied()) + predicates_of(self.db, def_id).own_explicit_predicates() } - #[tracing::instrument(skip(self), ret)] fn explicit_super_predicates_of( self, def_id: Self::TraitId, ) -> EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>> { - let is_self = |ty: Ty<'db>| match ty.kind() { - rustc_type_ir::TyKind::Param(param) => param.index == 0, - _ => false, - }; - - GenericPredicates::query_explicit(self.db, def_id.0.into()).map_bound(move |predicates| { - predicates - .iter() - .copied() - .filter(move |p| match p.kind().skip_binder() { - // rustc has the following assertion: - // https://github.com/rust-lang/rust/blob/52618eb338609df44978b0ca4451ab7941fd1c7a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs#L525-L608 - ClauseKind::Trait(it) => is_self(it.self_ty()), - ClauseKind::TypeOutlives(it) => is_self(it.0), - ClauseKind::Projection(it) => is_self(it.self_ty()), - ClauseKind::HostEffect(it) => is_self(it.self_ty()), - _ => false, - }) - .map(|p| (p, Span::dummy())) - }) + GenericPredicates::query(self.db, def_id.0.into()) + .explicit_non_assoc_types_predicates() + .map_bound(move |predicates| { + predicates.filter(|p| is_clause_at_ty(p, is_ty_self)).map(|p| (p, Span::dummy())) + }) } - #[tracing::instrument(skip(self), ret)] fn explicit_implied_predicates_of( self, def_id: Self::DefId, ) -> EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>> { - fn is_self_or_assoc(ty: Ty<'_>) -> bool { - match ty.kind() { - rustc_type_ir::TyKind::Param(param) => param.index == 0, - rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias) => { - is_self_or_assoc(alias.self_ty()) - } - _ => false, + fn is_ty_assoc_of_self(ty: Ty<'_>) -> bool { + // FIXME: Is this correct wrt. combined kind of assoc type bounds, i.e. `where Self::Assoc: Trait<Assoc2: Trait>` + // wrt. `Assoc2`, which we should exclude? + if let TyKind::Alias(AliasTyKind::Projection, alias) = ty.kind() { + is_ty_assoc_of_self(alias.self_ty()) + } else { + is_ty_self(ty) } } - predicates_of(self.db, def_id).explicit_implied_predicates().map_bound(|predicates| { - predicates - .iter() - .copied() - .filter(|p| match p.kind().skip_binder() { - ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()), - ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0), - ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()), - ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()), - // FIXME: Not sure is this correct to allow other clauses but we might replace - // `generic_predicates_ns` query here with something closer to rustc's - // `implied_bounds_with_filter`, which is more granular lowering than this - // "lower at once and then filter" implementation. - _ => true, - }) - .map(|p| (p, Span::dummy())) - }) + let predicates = predicates_of(self.db, def_id); + let non_assoc_types = predicates + .explicit_non_assoc_types_predicates() + .skip_binder() + .filter(|p| is_clause_at_ty(p, is_ty_self)); + let assoc_types = predicates + .explicit_assoc_types_predicates() + .skip_binder() + .filter(|p| is_clause_at_ty(p, is_ty_assoc_of_self)); + EarlyBinder::bind(non_assoc_types.chain(assoc_types).map(|it| (it, Span::dummy()))) } fn impl_super_outlives( @@ -2294,6 +2268,24 @@ impl<'db> Interner for DbInterner<'db> { } } +fn is_ty_self(ty: Ty<'_>) -> bool { + match ty.kind() { + TyKind::Param(param) => param.index == 0, + _ => false, + } +} +fn is_clause_at_ty(p: &Clause<'_>, filter: impl FnOnce(Ty<'_>) -> bool) -> bool { + match p.kind().skip_binder() { + // rustc has the following assertion: + // https://github.com/rust-lang/rust/blob/52618eb338609df44978b0ca4451ab7941fd1c7a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs#L525-L608 + ClauseKind::Trait(it) => filter(it.self_ty()), + ClauseKind::TypeOutlives(it) => filter(it.0), + ClauseKind::Projection(it) => filter(it.self_ty()), + ClauseKind::HostEffect(it) => filter(it.self_ty()), + _ => false, + } +} + impl<'db> DbInterner<'db> { pub fn shift_bound_var_indices<T>(self, bound_vars: usize, value: T) -> T where @@ -2366,6 +2358,22 @@ impl<'db> DbInterner<'db> { abi, } } + + /// `mk_fn_sig`, but with a safe Rust ABI, and no C-variadic argument. + pub fn mk_fn_sig_safe_rust_abi<I>(self, inputs: I, output: Ty<'db>) -> FnSig<'db> + where + I: IntoIterator<Item = Ty<'db>>, + { + FnSig { + inputs_and_output: Tys::new_from_iter( + self, + inputs.into_iter().chain(std::iter::once(output)), + ), + c_variadic: false, + safety: Safety::Safe, + abi: FnAbi::Rust, + } + } } fn predicates_of(db: &dyn HirDatabase, def_id: SolverDefId) -> &GenericPredicates { diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs index 192cdb70ae..8e892b65ea 100644 --- a/crates/hir-ty/src/next_solver/ty.rs +++ b/crates/hir-ty/src/next_solver/ty.rs @@ -696,7 +696,7 @@ impl<'db> Ty<'db> { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::ArgumentImplTrait => { let predicates = GenericPredicates::query_all(db, param.id.parent()) - .iter_identity_copied() + .iter_identity() .filter(|wc| match wc.kind().skip_binder() { ClauseKind::Trait(tr) => tr.self_ty() == self, ClauseKind::Projection(pred) => pred.self_ty() == self, diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs index 90cbcfea6a..8bc6c51fae 100644 --- a/crates/hir-ty/src/specialization.rs +++ b/crates/hir-ty/src/specialization.rs @@ -109,7 +109,7 @@ fn specializes_query( // only be referenced via projection predicates. ocx.register_obligations(clauses_as_obligations( GenericPredicates::query_all(db, parent_impl_def_id.into()) - .iter_instantiated_copied(interner, parent_args.as_slice()), + .iter_instantiated(interner, parent_args.as_slice()), cause.clone(), param_env, )); diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs index 243456c85f..e19e26ebc4 100644 --- a/crates/hir-ty/src/test_db.rs +++ b/crates/hir-ty/src/test_db.rs @@ -3,8 +3,8 @@ use std::{fmt, panic, sync::Mutex}; use base_db::{ - CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, RootQueryDb, - SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, + CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, SourceDatabase, SourceRoot, + SourceRootId, SourceRootInput, all_crates, relevant_crates, set_all_crates_with_durability, }; use hir_def::{ModuleId, db::DefDatabase, nameres::crate_def_map}; @@ -45,7 +45,7 @@ impl Default for TestDB { }; this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); // This needs to be here otherwise `CrateGraphBuilder` panics. - this.set_all_crates(Arc::new(Box::new([]))); + set_all_crates_with_durability(&mut this, std::iter::empty(), Durability::HIGH); _ = base_db::LibraryRoots::builder(Default::default()) .durability(Durability::MEDIUM) .new(&this); @@ -142,7 +142,7 @@ impl panic::RefUnwindSafe for TestDB {} impl TestDB { pub(crate) fn module_for_file_opt(&self, file_id: impl Into<FileId>) -> Option<ModuleId> { let file_id = file_id.into(); - for &krate in self.relevant_crates(file_id).iter() { + for &krate in relevant_crates(self, file_id).iter() { let crate_def_map = crate_def_map(self, krate); for (module_id, data) in crate_def_map.modules() { if data.origin.file_id().map(|file_id| file_id.file_id(self)) == Some(file_id) { @@ -161,7 +161,7 @@ impl TestDB { &self, ) -> FxHashMap<EditionedFileId, Vec<(TextRange, String)>> { let mut files = Vec::new(); - for &krate in self.all_crates().iter() { + for &krate in all_crates(self).iter() { let crate_def_map = crate_def_map(self, krate); for (module_id, _) in crate_def_map.modules() { let file_id = crate_def_map[module_id].origin.file_id(); diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs index e806999cb4..7cda259664 100644 --- a/crates/hir-ty/src/tests/incremental.rs +++ b/crates/hir-ty/src/tests/incremental.rs @@ -31,11 +31,11 @@ fn foo() -> i32 { &[("InferenceResult::for_body_", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", + "source_root_crates", "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "InferenceResult::for_body_", "FunctionSignature::of_", @@ -76,7 +76,7 @@ fn foo() -> i32 { &[("InferenceResult::for_body_", 0)], expect_test::expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -119,11 +119,11 @@ fn baz() -> i32 { &[("InferenceResult::for_body_", 3)], expect_test::expect![[r#" [ - "source_root_crates_shim", + "source_root_crates", "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "InferenceResult::for_body_", "FunctionSignature::of_", @@ -189,7 +189,7 @@ fn baz() -> i32 { &[("InferenceResult::for_body_", 1)], expect_test::expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -239,11 +239,11 @@ $0", &[("TraitImpls::for_crate_", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", + "source_root_crates", "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "TraitImpls::for_crate_", "lang_items", @@ -278,7 +278,7 @@ pub struct NewStruct { &[("TraitImpls::for_crate_", 1)], expect_test::expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -314,11 +314,11 @@ $0", &[("TraitImpls::for_crate_", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", + "source_root_crates", "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "TraitImpls::for_crate_", "lang_items", @@ -354,7 +354,7 @@ pub enum SomeEnum { &[("TraitImpls::for_crate_", 1)], expect_test::expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -390,11 +390,11 @@ $0", &[("TraitImpls::for_crate_", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", + "source_root_crates", "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "TraitImpls::for_crate_", "lang_items", @@ -427,7 +427,7 @@ fn bar() -> f32 { &[("TraitImpls::for_crate_", 1)], expect_test::expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -467,11 +467,11 @@ $0", &[("TraitImpls::for_crate_", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", + "source_root_crates", "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "TraitImpls::for_crate_", "lang_items", @@ -512,7 +512,7 @@ impl SomeStruct { &[("TraitImpls::for_crate_", 1)], expect_test::expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", @@ -568,11 +568,11 @@ fn main() { &[("trait_solve_shim", 0)], expect_test::expect![[r#" [ - "source_root_crates_shim", + "source_root_crates", "crate_local_def_map", "file_item_tree_query", "ast_id_map", - "parse_shim", + "parse", "real_span_map_shim", "TraitItems::query_with_diagnostics_", "Body::of_", @@ -664,7 +664,7 @@ fn main() { &[("trait_solve_shim", 0)], expect_test::expect![[r#" [ - "parse_shim", + "parse", "ast_id_map", "file_item_tree_query", "real_span_map_shim", diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index e4fc7e56c6..d3dfc44c22 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -2841,3 +2841,18 @@ fn wrapped_abs<T: SelfAbs<Output = T>>(v: T) -> T { "#, ); } + +#[test] +fn regression_21899() { + check_no_mismatches( + r#" +trait B where + Self::T: B, +{ + type T; +} + +fn foo<T: B>(v: T::T) {} + "#, + ); +} diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 22359d8f1f..1d27d52a36 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4449,14 +4449,14 @@ impl Trait for () { let a = Self::Assoc { x }; // ^ S let a = <Self>::Assoc { x }; // unstable - // ^ {unknown} + // ^ S // should be `Copy` but we don't track ownership anyway. let value = S { x }; if let Self::Assoc { x } = value {} // ^ u32 if let <Self>::Assoc { x } = value {} // unstable - // ^ {unknown} + // ^ u32 } } "#, @@ -4508,22 +4508,22 @@ impl Trait for () { let a = Self::Assoc::Struct { x }; // ^ E let a = <Self>::Assoc::Struct { x }; // unstable - // ^ {unknown} + // ^ E let a = <Self::Assoc>::Struct { x }; // unstable - // ^ {unknown} + // ^ E let a = <<Self>::Assoc>::Struct { x }; // unstable - // ^ {unknown} + // ^ E // should be `Copy` but we don't track ownership anyway. let value = E::Struct { x: 42 }; if let Self::Assoc::Struct { x } = value {} // ^ u32 if let <Self>::Assoc::Struct { x } = value {} // unstable - // ^ {unknown} + // ^ u32 if let <Self::Assoc>::Struct { x } = value {} // unstable - // ^ {unknown} + // ^ u32 if let <<Self>::Assoc>::Struct { x } = value {} // unstable - // ^ {unknown} + // ^ u32 } } "#, @@ -5148,3 +5148,98 @@ fn foo(v: Struct<f32>) { "#, ); } + +#[test] +fn more_qualified_paths() { + check_infer( + r#" +struct T; +struct S { + a: u32, +} + +trait Trait { + type Assoc; + + fn foo(); +} + +impl Trait for T { + type Assoc = S; + + fn foo() { + let <Self>::Assoc { a } = <Self>::Assoc { a: 0 }; + } +} + +enum E { + ES { a: u32 }, + ET(u32), +} + +impl Trait for E { + type Assoc = Self; + + fn foo() { + let <Self>::Assoc::ES { a } = <Self>::Assoc::ES { a: 0 }; + } +} + +fn foo() { + let <T as Trait>::Assoc { a } = <T as Trait>::Assoc { a: 0 }; + + let <E>::ES { a } = (<E>::ES { a: 0 }) else { loop {} }; + let <E>::ET(a) = <E>::ET(0) else { loop {} }; + let <E as Trait>::Assoc::ES { a } = (<E as Trait>::Assoc::ES { a: 0 }) else { loop {} }; + let <E as Trait>::Assoc::ET(a) = <E as Trait>::Assoc::ET(0) else { loop {} }; +} + "#, + expect![[r#" + 137..202 '{ ... }': () + 151..170 '<Self>... { a }': S + 167..168 'a': u32 + 173..195 '<Self>...a: 0 }': S + 192..193 '0': u32 + 306..379 '{ ... }': () + 320..343 '<Self>... { a }': E + 340..341 'a': u32 + 346..372 '<Self>...a: 0 }': E + 369..370 '0': u32 + 392..748 '{ ...} }; }': () + 402..427 '<T as ... { a }': S + 424..425 'a': u32 + 430..458 '<T as ...a: 0 }': S + 455..456 '0': u32 + 469..482 '<E>::ES { a }': E + 479..480 'a': u32 + 486..502 '<E>::E...a: 0 }': E + 499..500 '0': u32 + 509..520 '{ loop {} }': ! + 511..518 'loop {}': ! + 516..518 '{}': () + 530..540 '<E>::ET(a)': E + 538..539 'a': u32 + 543..550 '<E>::ET': fn ET(u32) -> E + 543..553 '<E>::ET(0)': E + 551..552 '0': u32 + 559..570 '{ loop {} }': ! + 561..568 'loop {}': ! + 566..568 '{}': () + 580..609 '<E as ... { a }': E + 606..607 'a': u32 + 613..645 '<E as ...a: 0 }': E + 642..643 '0': u32 + 652..663 '{ loop {} }': ! + 654..661 'loop {}': ! + 659..661 '{}': () + 673..699 '<E as ...:ET(a)': E + 697..698 'a': u32 + 702..725 '<E as ...oc::ET': fn ET(u32) -> E + 702..728 '<E as ...:ET(0)': E + 726..727 '0': u32 + 734..745 '{ loop {} }': ! + 736..743 'loop {}': ! + 741..743 '{}': () + "#]], + ); +} diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 7f672a697c..555270bad8 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -282,7 +282,7 @@ pub struct MissingFields { pub file: HirFileId, pub field_list_parent: AstPtr<Either<ast::RecordExpr, ast::RecordPat>>, pub field_list_parent_path: Option<AstPtr<ast::Path>>, - pub missed_fields: Vec<Name>, + pub missed_fields: Vec<(Name, Field)>, } #[derive(Debug)] @@ -476,7 +476,12 @@ impl<'db> AnyDiagnostic<'db> { let variant_data = variant.fields(db); let missed_fields = missed_fields .into_iter() - .map(|idx| variant_data.fields()[idx].name.clone()) + .map(|idx| { + ( + variant_data.fields()[idx].name.clone(), + Field { parent: variant.into(), id: idx }, + ) + }) .collect(); let record = match record { diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 4bfdd239f9..53f24713cd 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -76,7 +76,7 @@ fn write_builtin_derive_impl_method<'db>( let predicates = hir_ty::builtin_derive::predicates(db, impl_).explicit_predicates().skip_binder(); - write_params_bounds(f, predicates)?; + write_params_bounds(f, &Vec::from_iter(predicates))?; } Ok(()) @@ -578,7 +578,7 @@ impl<'db> HirDisplay<'db> for TypeParam { let ty = self.ty(f.db).ty; let predicates = GenericPredicates::query_all(f.db, self.id.parent()); let predicates = predicates - .iter_identity_copied() + .iter_identity() .filter(|wc| match wc.kind().skip_binder() { ClauseKind::Trait(tr) => tr.self_ty() == ty, ClauseKind::Projection(proj) => proj.self_ty() == ty, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bc5e164830..2829902035 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -45,7 +45,7 @@ use std::{ }; use arrayvec::ArrayVec; -use base_db::{CrateDisplayName, CrateOrigin, LangCrateOrigin}; +use base_db::{CrateDisplayName, CrateOrigin, LangCrateOrigin, all_crates}; use either::Either; use hir_def::{ AdtId, AssocItemId, AssocItemLoc, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, @@ -243,7 +243,7 @@ impl Crate { } pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> { - let all_crates = db.all_crates(); + let all_crates = all_crates(db); all_crates .iter() .copied() @@ -310,7 +310,7 @@ impl Crate { } pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { - db.all_crates().iter().map(|&id| Crate { id }).collect() + all_crates(db).iter().map(|&id| Crate { id }).collect() } /// Try to get the root URL of the documentation of a crate. @@ -334,7 +334,7 @@ impl Crate { } fn core(db: &dyn HirDatabase) -> Option<Crate> { - db.all_crates() + all_crates(db) .iter() .copied() .find(|&krate| { @@ -547,7 +547,7 @@ impl HasCrate for ModuleDef { fn krate(&self, db: &dyn HirDatabase) -> Crate { match self.module(db) { Some(module) => module.krate(db), - None => Crate::core(db).unwrap_or_else(|| db.all_crates()[0].into()), + None => Crate::core(db).unwrap_or_else(|| all_crates(db)[0].into()), } } } @@ -1239,11 +1239,15 @@ fn emit_def_diagnostic_<'db>( ); } DefDiagnosticKind::InvalidDeriveTarget { ast, id } => { - let derive = id.find_attr_range(db, krate, *ast).3.path_range(); + let (_, attr) = id.find_attr_range(db, krate, *ast); + let derive = attr + .path() + .map(|path| path.syntax().text_range()) + .unwrap_or_else(|| attr.syntax().text_range()); acc.push(InvalidDeriveTarget { range: ast.with_value(derive) }.into()); } DefDiagnosticKind::MalformedDerive { ast, id } => { - let derive = id.find_attr_range(db, krate, *ast).2; + let derive = id.find_attr_range(db, krate, *ast).1.syntax().text_range(); acc.push(MalformedDerive { range: ast.with_value(derive) }.into()); } DefDiagnosticKind::MacroDefError { ast, message } => { @@ -1283,7 +1287,8 @@ fn precise_macro_call_location( ast_id.with_value(range) } MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { - let attr_range = attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).2; + let attr_range = + attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range(); ast_id.with_value(attr_range) } } @@ -3394,7 +3399,7 @@ impl BuiltinType { } pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| db.all_crates()[0]); + let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| all_crates(db)[0]); let interner = DbInterner::new_no_crate(db); Type::new_for_crate(core, Ty::from_builtin_type(interner, self.inner)) } @@ -4680,7 +4685,7 @@ impl TypeParam { pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> { let self_ty = self.ty(db).ty; GenericPredicates::query_explicit(db, self.id.parent()) - .iter_identity_copied() + .iter_identity() .filter_map(|pred| match &pred.kind().skip_binder() { ClauseKind::Trait(trait_ref) if trait_ref.self_ty() == self_ty => { Some(Trait::from(trait_ref.def_id().0)) @@ -4898,12 +4903,12 @@ impl Impl { std::iter::successors(module.block(db), |block| block.loc(db).module.block(db)) .filter_map(|block| TraitImpls::for_block(db, block).as_deref()) .for_each(|impls| impls.for_self_ty(&simplified_ty, &mut extend_with_impls)); - for &krate in &**db.all_crates() { + for &krate in &*all_crates(db) { TraitImpls::for_crate(db, krate) .for_self_ty(&simplified_ty, &mut extend_with_impls); } } else { - for &krate in &**db.all_crates() { + for &krate in &*all_crates(db) { TraitImpls::for_crate(db, krate) .for_self_ty(&simplified_ty, &mut extend_with_impls); } @@ -7175,7 +7180,7 @@ pub fn resolve_absolute_path<'a, I: Iterator<Item = Symbol> + Clone + 'a>( .next() .into_iter() .flat_map(move |crate_name| { - db.all_crates() + all_crates(db) .iter() .filter(|&krate| { krate diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 4e9e3c44be..9996162485 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -10,7 +10,7 @@ use std::{ ops::{self, ControlFlow, Not}, }; -use base_db::FxIndexSet; +use base_db::{FxIndexSet, all_crates, toolchain_channel}; use either::Either; use hir_def::{ BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, HasModule, MacroId, StructId, @@ -24,6 +24,7 @@ use hir_def::{ }; use hir_expand::{ EditionedFileId, ExpandResult, FileRange, HirFileId, InMacroFile, MacroCallId, + attrs::AstPathExt, builtin::{BuiltinFnLikeExpander, EagerExpander}, db::ExpandDatabase, files::{FileRangeWrapper, HirFileRange, InRealFile}, @@ -298,14 +299,15 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> { hir_expand::attrs::expand_cfg_attr::<Infallible>( extra_crate_attrs.chain(ast::attrs_including_inner(&item)), cfg_options, - |attr, _, _, _| { - let hir_expand::attrs::Meta::TokenTree { path, tt } = attr else { + |attr, _| { + let ast::Meta::TokenTreeMeta(attr) = attr else { return ControlFlow::Continue(()); }; - if path.segments.len() != 1 { + let (Some(segment), Some(tt)) = (attr.path().as_one_segment(), attr.token_tree()) + else { return ControlFlow::Continue(()); - } - let lint_attr = match path.segments[0].text() { + }; + let lint_attr = match &*segment { "allow" => LintAttr::Allow, "expect" => LintAttr::Expect, "warn" => LintAttr::Warn, @@ -392,7 +394,7 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> { } pub fn is_nightly(&self, krate: Crate) -> bool { - let toolchain = self.db.toolchain_channel(krate.into()); + let toolchain = toolchain_channel(self.db.as_dyn_database(), krate.into()); // `toolchain == None` means we're in some detached files. Since we have no information on // the toolchain being used, let's just allow unstable items to be listed. matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None) @@ -458,7 +460,7 @@ impl<'db> SemanticsImpl<'db> { pub fn parse(&self, file_id: EditionedFileId) -> ast::SourceFile { let hir_file_id = file_id.into(); - let tree = self.db.parse(file_id).tree(); + let tree = file_id.parse(self.db).tree(); self.cache(tree.syntax().clone(), hir_file_id); tree } @@ -467,7 +469,7 @@ impl<'db> SemanticsImpl<'db> { pub fn first_crate(&self, file: FileId) -> Option<Crate> { match self.file_to_module_defs(file).next() { Some(module) => Some(module.krate(self.db)), - None => self.db.all_crates().last().copied().map(Into::into), + None => all_crates(self.db).last().copied().map(Into::into), } } @@ -484,7 +486,7 @@ impl<'db> SemanticsImpl<'db> { pub fn parse_guess_edition(&self, file_id: FileId) -> ast::SourceFile { let file_id = self.attach_first_edition(file_id); - let tree = self.db.parse(file_id).tree(); + let tree = file_id.parse(self.db).tree(); self.cache(tree.syntax().clone(), file_id.into()); tree } @@ -554,17 +556,6 @@ impl<'db> SemanticsImpl<'db> { Some(InFile::new(file_id.into(), node)) } - pub fn check_cfg_attr(&self, attr: &ast::TokenTree) -> Option<bool> { - let file_id = self.find_file(attr.syntax()).file_id; - let krate = match file_id { - HirFileId::FileId(file_id) => { - self.file_to_module_defs(file_id.file_id(self.db)).next()?.krate(self.db).id - } - HirFileId::MacroFile(macro_file) => self.db.lookup_intern_macro_call(macro_file).krate, - }; - hir_expand::check_cfg_attr_value(self.db, attr, krate) - } - /// Expands the macro if it isn't one of the built-in ones that expand to custom syntax or dummy /// expansions. pub fn expand_allowed_builtins( @@ -608,8 +599,8 @@ impl<'db> SemanticsImpl<'db> { Some(self.expand(macro_call_id).map(|it| InFile::new(macro_call_id.into(), it))) } - pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> { - let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; + pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Meta) -> Option<SyntaxNode> { + let adt = attr.parent_attr()?.syntax().parent().and_then(ast::Adt::cast)?; let src = self.wrap_node_infile(attr.clone()); let call_id = self.with_ctx(|ctx| { ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it) @@ -617,7 +608,7 @@ impl<'db> SemanticsImpl<'db> { Some(self.parse_or_expand(call_id.into())) } - pub fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> { + pub fn resolve_derive_macro(&self, attr: &ast::Meta) -> Option<Vec<Option<Macro>>> { let calls = self.derive_macro_calls(attr)?; self.with_ctx(|ctx| { Some( @@ -644,7 +635,7 @@ impl<'db> SemanticsImpl<'db> { pub fn expand_derive_macro( &self, - attr: &ast::Attr, + attr: &ast::Meta, ) -> Option<Vec<Option<ExpandResult<SyntaxNode>>>> { let res: Vec<_> = self .derive_macro_calls(attr)? @@ -662,9 +653,9 @@ impl<'db> SemanticsImpl<'db> { fn derive_macro_calls( &self, - attr: &ast::Attr, + attr: &ast::Meta, ) -> Option<Vec<Option<Either<MacroCallId, BuiltinDeriveImplId>>>> { - let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; + let adt = attr.parent_attr()?.syntax().parent().and_then(ast::Adt::cast)?; let file_id = self.find_file(adt.syntax()).file_id; let adt = InFile::new(file_id, &adt); let src = InFile::new(file_id, attr.clone()); @@ -773,7 +764,11 @@ impl<'db> SemanticsImpl<'db> { let attr = self.wrap_node_infile(actual_macro_call.clone()); let adt = actual_macro_call.syntax().parent().and_then(ast::Adt::cast)?; let macro_call_id = self.with_ctx(|ctx| { - ctx.attr_to_derive_macro_call(attr.with_value(&adt), attr).map(|(_, it, _)| it) + ctx.attr_to_derive_macro_call( + attr.with_value(&adt), + attr.with_value(attr.value.meta()?), + ) + .map(|(_, it, _)| it) })?; hir_expand::db::expand_speculative( self.db, @@ -1328,7 +1323,7 @@ impl<'db> SemanticsImpl<'db> { // text ranges of the outer ones, and then all of the inner ones up // to the invoking attribute so that the inbetween is ignored. // FIXME: Should cfg_attr be handled differently? - let (attr, _, _, _) = attr_ids + let (attr, _) = attr_ids .invoc_attr() .find_attr_range_with_source(db, loc.krate, &item); let start = attr.syntax().text_range().start(); @@ -1435,7 +1430,7 @@ impl<'db> SemanticsImpl<'db> { let derive_call = ctx .attr_to_derive_macro_call( InFile::new(expansion, &adt), - InFile::new(expansion, attr.clone()), + InFile::new(expansion, meta.clone()), )? .1; @@ -2461,7 +2456,7 @@ fn macro_call_to_macro_id( Either::Left(it) => { let node = match it.file_id { HirFileId::FileId(file_id) => { - it.to_ptr(db).to_node(&db.parse(file_id).syntax_node()) + it.to_ptr(db).to_node(&file_id.parse(db).syntax_node()) } HirFileId::MacroFile(macro_file) => { let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file); @@ -2473,7 +2468,7 @@ fn macro_call_to_macro_id( Either::Right(it) => { let node = match it.file_id { HirFileId::FileId(file_id) => { - it.to_ptr(db).to_node(&db.parse(file_id).syntax_node()) + it.to_ptr(db).to_node(&file_id.parse(db).syntax_node()) } HirFileId::MacroFile(macro_file) => { let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file); diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs index f6d1bec575..babeb35913 100644 --- a/crates/hir/src/semantics/child_by_source.rs +++ b/crates/hir/src/semantics/child_by_source.rs @@ -126,8 +126,7 @@ impl ChildBySource for ItemScope { calls.for_each(|(attr_id, call_id, calls)| { // FIXME: Is this the right crate? let krate = call_id.lookup(db).krate; - // FIXME: Fix cfg_attr handling. - let (attr, _, _, _) = attr_id.find_attr_range_with_source(db, krate, &adt); + let (_, attr) = attr_id.find_attr_range_with_source(db, krate, &adt); res[keys::DERIVE_MACRO_CALL] .insert(AstPtr::new(&attr), (attr_id, call_id, calls.into())); }); diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index a9a779a287..d932198b43 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -85,6 +85,7 @@ //! active crate for a given position, and then provide an API to resolve all //! syntax nodes against this specific crate. +use base_db::relevant_crates; use either::Either; use hir_def::{ AdtId, BlockId, BuiltinDeriveImplId, ConstId, ConstParamId, DefWithBodyId, EnumId, @@ -145,7 +146,7 @@ impl SourceToDefCache { return m; } self.included_file_cache.insert(file, None); - for &crate_id in db.relevant_crates(file.file_id(db)).iter() { + for &crate_id in relevant_crates(db, file.file_id(db)).iter() { db.include_macro_invoc(crate_id).iter().for_each(|&(macro_call_id, file_id)| { self.included_file_cache.insert(file_id, Some(macro_call_id)); }); @@ -180,7 +181,7 @@ impl SourceToDefCtx<'_, '_> { self.cache.file_to_def_cache.entry(file).or_insert_with(|| { let mut mods = SmallVec::new(); - for &crate_id in self.db.relevant_crates(file).iter() { + for &crate_id in relevant_crates(self.db, file).iter() { // Note: `mod` declarations in block modules cannot be supported here let crate_def_map = crate_def_map(self.db, crate_id); let n_mods = mods.len(); @@ -397,7 +398,7 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn attr_to_derive_macro_call( &mut self, item: InFile<&ast::Adt>, - src: InFile<ast::Attr>, + src: InFile<ast::Meta>, ) -> Option<(AttrId, MacroCallId, &[Option<Either<MacroCallId, BuiltinDeriveImplId>>])> { let map = self.dyn_map(item)?; map[keys::DERIVE_MACRO_CALL] @@ -422,6 +423,7 @@ impl SourceToDefCtx<'_, '_> { let dyn_map = &map[keys::DERIVE_MACRO_CALL]; adt.value .attrs() + .flat_map(|attr| attr.skip_cfg_attrs()) .filter_map(move |attr| dyn_map.get(&AstPtr::new(&attr))) .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids)) }) diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index afdced4215..44b367059e 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -1,7 +1,7 @@ use hir::HasSource; use syntax::{ Edition, - ast::{self, AstNode, make}, + ast::{self, AstNode, syntax_factory::SyntaxFactory}, syntax_editor::{Position, SyntaxEditor}, }; @@ -148,7 +148,9 @@ fn add_missing_impl_members_inner( let target = impl_def.syntax().text_range(); acc.add(AssistId::quick_fix(assist_id), label, target, |edit| { + let make = SyntaxFactory::with_mappings(); let new_item = add_trait_assoc_items_to_impl( + &make, &ctx.sema, ctx.config, &missing_items, @@ -164,6 +166,7 @@ fn add_missing_impl_members_inner( let mut first_new_item = if let DefaultMethods::No = mode && let ast::AssocItem::Fn(func) = &first_new_item && let Some(body) = try_gen_trait_body( + &make, ctx, func, trait_ref, @@ -172,7 +175,7 @@ fn add_missing_impl_members_inner( ) && let Some(func_body) = func.body() { - let mut func_editor = SyntaxEditor::new(first_new_item.syntax().clone_subtree()); + let (mut func_editor, _) = SyntaxEditor::new(first_new_item.syntax().clone()); func_editor.replace(func_body.syntax(), body.syntax()); ast::AssocItem::cast(func_editor.finish().new_root().clone()) } else { @@ -189,10 +192,10 @@ fn add_missing_impl_members_inner( if let Some(assoc_item_list) = impl_def.assoc_item_list() { assoc_item_list.add_items(&mut editor, new_assoc_items); } else { - let assoc_item_list = make::assoc_item_list(Some(new_assoc_items)).clone_for_update(); + let assoc_item_list = make.assoc_item_list(new_assoc_items); editor.insert_all( Position::after(impl_def.syntax()), - vec![make::tokens::whitespace(" ").into(), assoc_item_list.syntax().clone().into()], + vec![make.whitespace(" ").into(), assoc_item_list.syntax().clone().into()], ); first_new_item = assoc_item_list.assoc_items().next(); } @@ -215,23 +218,24 @@ fn add_missing_impl_members_inner( editor.add_annotation(first_new_item.syntax(), tabstop); }; }; + editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }) } fn try_gen_trait_body( + make: &SyntaxFactory, ctx: &AssistContext<'_>, func: &ast::Fn, trait_ref: hir::TraitRef<'_>, impl_def: &ast::Impl, edition: Edition, ) -> Option<ast::BlockExpr> { - let trait_path = make::ext::ident_path( - &trait_ref.trait_().name(ctx.db()).display(ctx.db(), edition).to_string(), - ); + let trait_path = + make.ident_path(&trait_ref.trait_().name(ctx.db()).display(ctx.db(), edition).to_string()); let hir_ty = ctx.sema.resolve_type(&impl_def.self_ty()?)?; let adt = hir_ty.as_adt()?.source(ctx.db())?; - gen_trait_fn_body(func, &trait_path, &adt.value, Some(trait_ref)) + gen_trait_fn_body(make, func, &trait_path, &adt.value, Some(trait_ref)) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index b063e5ffce..b7510bb826 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -1,4 +1,4 @@ -use std::iter::{self, Peekable}; +use std::iter; use either::Either; use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics}; @@ -93,8 +93,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) } else { None }; - let (mut missing_pats, is_non_exhaustive, has_hidden_variants): ( - Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>, + let (missing_pats, is_non_exhaustive, has_hidden_variants): ( + Vec<(ast::Pat, bool)>, bool, bool, ) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr, self_ty.as_ref()) { @@ -117,15 +117,15 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); let option_enum = FamousDefs(&ctx.sema, module.krate(ctx.db())).core_option_Option(); - let missing_pats: Box<dyn Iterator<Item = _>> = if matches!(enum_def, ExtendedEnum::Enum { enum_: e, .. } if Some(e) == option_enum) + let missing_pats: Vec<_> = if matches!(enum_def, ExtendedEnum::Enum { enum_: e, .. } if Some(e) == option_enum) { // Match `Some` variant first. cov_mark::hit!(option_order); - Box::new(missing_pats.rev()) + missing_pats.rev().collect() } else { - Box::new(missing_pats) + missing_pats.collect() }; - (missing_pats.peekable(), is_non_exhaustive, has_hidden_variants) + (missing_pats, is_non_exhaustive, has_hidden_variants) } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr, self_ty.as_ref()) { let is_non_exhaustive = enum_defs .iter() @@ -169,12 +169,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) (ast::Pat::from(make.tuple_pat(patterns)), is_hidden) }) - .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); - ( - (Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(), - is_non_exhaustive, - has_hidden_variants, - ) + .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)) + .collect(); + (missing_pats, is_non_exhaustive, has_hidden_variants) } else if let Some((enum_def, len)) = resolve_array_of_enum_def(&ctx.sema, &expr, self_ty.as_ref()) { @@ -205,12 +202,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) (ast::Pat::from(make.slice_pat(patterns)), is_hidden) }) - .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); - ( - (Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(), - is_non_exhaustive, - has_hidden_variants, - ) + .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)) + .collect(); + (missing_pats, is_non_exhaustive, has_hidden_variants) } else { return None; }; @@ -218,20 +212,31 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let mut needs_catch_all_arm = is_non_exhaustive && !has_catch_all_arm; if !needs_catch_all_arm - && ((has_hidden_variants && has_catch_all_arm) || missing_pats.peek().is_none()) + && ((has_hidden_variants && has_catch_all_arm) || missing_pats.is_empty()) { return None; } + let visible_count = missing_pats.iter().filter(|(_, hidden)| !hidden).count(); + let label = if visible_count == 0 { + "Add missing catch-all match arm `_`".to_owned() + } else if visible_count == 1 { + let pat = &missing_pats.iter().find(|(_, hidden)| !hidden).unwrap().0; + format!("Add missing match arm `{pat}`") + } else { + format!("Add {visible_count} missing match arms") + }; + acc.add( AssistId::quick_fix("add_missing_match_arms"), - "Fill match arms", + label, ctx.sema.original_range(match_expr.syntax()).range, |builder| { // having any hidden variants means that we need a catch-all arm needs_catch_all_arm |= has_hidden_variants; let mut missing_arms = missing_pats + .into_iter() .filter(|(_, hidden)| { // filter out hidden patterns because they're handled by the catch-all arm !hidden @@ -635,7 +640,7 @@ mod tests { use crate::AssistConfig; use crate::tests::{ TEST_CONFIG, check_assist, check_assist_not_applicable, check_assist_target, - check_assist_unresolved, check_assist_with_config, + check_assist_unresolved, check_assist_with_config, check_assist_with_label, }; use super::add_missing_match_arms; @@ -1828,8 +1833,10 @@ fn foo(t: Test) { #[test] fn lazy_computation() { - // Computing a single missing arm is enough to determine applicability of the assist. - cov_mark::check_count!(add_missing_match_arms_lazy_computation, 1); + // We now collect all missing arms eagerly, so we can show the count + // of missing arms. + cov_mark::check_count!(add_missing_match_arms_lazy_computation, 4); + check_assist_unresolved( add_missing_match_arms, r#" @@ -1842,6 +1849,54 @@ fn foo(tuple: (A, A)) { } #[test] + fn label_single_missing_arm() { + check_assist_with_label( + add_missing_match_arms, + r#" +enum A { One, Two } +fn foo(a: A) { + match $0a { + A::One => {} + } +} +"#, + "Add missing match arm `A::Two`", + ); + } + + #[test] + fn label_multiple_missing_arms() { + check_assist_with_label( + add_missing_match_arms, + r#" +enum A { One, Two, Three } +fn foo(a: A) { + match $0a {} +} +"#, + "Add 3 missing match arms", + ); + } + + #[test] + fn label_catch_all_only() { + check_assist_with_label( + add_missing_match_arms, + r#" +//- /main.rs crate:main deps:e +fn foo(t: ::e::E) { + match $0t { + e::E::A => {} + } +} +//- /e.rs crate:e +pub enum E { A, #[doc(hidden)] B, } +"#, + "Add missing catch-all match arm `_`", + ); + } + + #[test] fn adds_comma_before_new_arms() { check_assist( add_missing_match_arms, diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index 4ee4970248..2ea0d76b01 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -82,8 +82,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let make = SyntaxFactory::with_mappings(); - let demorganed = bin_expr.clone_subtree(); - let mut editor = SyntaxEditor::new(demorganed.syntax().clone()); + let (mut editor, demorganed) = SyntaxEditor::with_ast_node(&bin_expr); editor.replace(demorganed.op_token()?, make.token(inv_token)); let mut exprs = VecDeque::from([ diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index de5dfdf4d9..adeb191719 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -1928,4 +1928,33 @@ fn f() { "#; check_auto_import_order(before, &["Import `foo::wanted`", "Import `quux::wanted`"]); } + + #[test] + fn consider_definition_kind() { + check_assist( + auto_import, + r#" +//- /eyre.rs crate:eyre +#[macro_export] +macro_rules! eyre { + () => {}; +} + +//- /color-eyre.rs crate:color-eyre deps:eyre +pub use eyre; + +//- /main.rs crate:main deps:color-eyre +fn main() { + ey$0re!(); +} + "#, + r#" +use color_eyre::eyre::eyre; + +fn main() { + eyre!(); +} + "#, + ); + } } diff --git a/crates/ide-assists/src/handlers/convert_bool_then.rs b/crates/ide-assists/src/handlers/convert_bool_then.rs index b3bfe5b8c4..c36c79ee99 100644 --- a/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -77,8 +77,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> "Convert `if` expression to `bool::then` call", target, |builder| { - let closure_body = closure_body.clone_subtree(); - let mut editor = SyntaxEditor::new(closure_body.syntax().clone()); + let (mut editor, closure_body) = SyntaxEditor::with_ast_node(&closure_body); // Rewrite all `Some(e)` in tail position to `e` for_each_tail_expr(&closure_body, &mut |e| { let e = match e { @@ -188,8 +187,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> e => mapless_make.block_expr(None, Some(e)), }; - let closure_body = closure_body.clone_subtree(); - let mut editor = SyntaxEditor::new(closure_body.syntax().clone()); + let (mut editor, closure_body) = SyntaxEditor::with_ast_node(&closure_body); // Wrap all tails in `Some(...)` let none_path = mapless_make.expr_path(mapless_make.ident_path("None")); let some_path = mapless_make.expr_path(mapless_make.ident_path("Some")); diff --git a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs index 15f324eff3..a5c29a45a5 100644 --- a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs +++ b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs @@ -81,14 +81,14 @@ pub(crate) fn convert_for_loop_to_while_let( let indent = IndentLevel::from_node(for_loop.syntax()); if let Some(label) = for_loop.label() { - let label = label.syntax().clone_for_update(); + let label = label.syntax(); editor.insert(Position::before(for_loop.syntax()), make.whitespace(" ")); editor.insert(Position::before(for_loop.syntax()), label); } crate::utils::insert_attributes( for_loop.syntax(), &mut editor, - for_loop.attrs().map(|it| it.clone_for_update()), + for_loop.attrs(), &make, ); diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index d2336a4a5d..9a9808e270 100644 --- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -32,10 +32,12 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' .or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?; let let_stmt = LetStmt::cast(let_stmt)?; let else_block = let_stmt.let_else()?.block_expr()?; - let else_expr = if else_block.statements().next().is_none() { - else_block.tail_expr()? + let else_expr = if else_block.statements().next().is_none() + && let Some(tail_expr) = else_block.tail_expr() + { + tail_expr.reset_indent() } else { - else_block.into() + else_block.reset_indent().into() }; let init = let_stmt.initializer()?; // Ignore let stmt with type annotation @@ -91,8 +93,8 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' }, ); let else_arm = make.match_arm(make.wildcard_pat().into(), None, else_expr); - let match_ = make.expr_match(init, make.match_arm_list([binding_arm, else_arm])); - let match_ = match_.reset_indent(); + let arms = [binding_arm, else_arm].map(|arm| arm.indent(1.into())); + let match_ = make.expr_match(init, make.match_arm_list(arms)); let match_ = match_.indent(let_stmt.indent_level()); if bindings.is_empty() { @@ -190,7 +192,7 @@ fn remove_mut_and_collect_idents( let inner = p.pat()?; if let ast::Pat::IdentPat(ident) = inner { acc.push(ident); - p.clone_for_update().into() + p.clone().into() } else { make.ref_pat(remove_mut_and_collect_idents(make, &inner, acc)?).into() } @@ -299,6 +301,81 @@ fn main() { } #[test] + fn convert_let_else_to_match_with_empty_else_block() { + check_assist( + convert_let_else_to_match, + r" +fn main() { + let Ok(x) = f() else$0 {}; +}", + r" +fn main() { + let x = match f() { + Ok(x) => x, + _ => {} + }; +}", + ); + } + + #[test] + fn convert_let_else_to_match_with_some_indent() { + check_assist( + convert_let_else_to_match, + r#" +mod indent { + fn main() { + let Ok(x) = f() else$0 { + log(); + unreachable!( + "..." + ); + }; + } +}"#, + r#" +mod indent { + fn main() { + let x = match f() { + Ok(x) => x, + _ => { + log(); + unreachable!( + "..." + ); + } + }; + } +}"#, + ); + + check_assist( + convert_let_else_to_match, + r#" +mod indent { + fn main() { + let Ok(x) = f() else$0 { + unreachable!( + "..." + ) + }; + } +}"#, + r#" +mod indent { + fn main() { + let x = match f() { + Ok(x) => x, + _ => unreachable!( + "..." + ), + }; + } +}"#, + ); + } + + #[test] fn convert_let_else_to_match_const_ref() { check_assist( convert_let_else_to_match, diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index 1a6d176c90..4b132d68ee 100644 --- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -121,8 +121,7 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti // Rename `extracted` with `binding` in `pat`. fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode { - let syntax = pat.syntax().clone_subtree(); - let mut editor = SyntaxEditor::new(syntax.clone()); + let (mut editor, syntax) = SyntaxEditor::new(pat.syntax().clone()); let make = SyntaxFactory::with_mappings(); let extracted = extracted .iter() @@ -138,15 +137,12 @@ fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> Syn if let Some(name_ref) = record_pat_field.field_name() { editor.replace( record_pat_field.syntax(), - make.record_pat_field( - make.name_ref(&name_ref.text()), - binding.clone_for_update(), - ) - .syntax(), + make.record_pat_field(make.name_ref(&name_ref.text()), binding.clone()) + .syntax(), ); } } else { - editor.replace(extracted_syntax, binding.syntax().clone_for_update()); + editor.replace(extracted_syntax, binding.syntax()); } } editor.add_mappings(make.finish_with_mappings()); diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index aaf727058c..4ea56e3e61 100644 --- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -102,11 +102,11 @@ fn edit_struct_def( // Note that we don't need to consider macro files in this function because this is // currently not triggered for struct definitions inside macro calls. let tuple_fields = record_fields.fields().filter_map(|f| { - let field = ast::make::tuple_field(f.visibility(), f.ty()?); - let mut editor = SyntaxEditor::new(field.syntax().clone()); + let (mut editor, field) = + SyntaxEditor::with_ast_node(&ast::make::tuple_field(f.visibility(), f.ty()?)); editor.insert_all( Position::first_child_of(field.syntax()), - f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(), + f.attrs().map(|attr| attr.syntax().clone().into()).collect(), ); let field_syntax = editor.finish().new_root().clone(); let field = ast::TupleField::cast(field_syntax)?; @@ -328,8 +328,7 @@ fn delete_whitespace(edit: &mut SyntaxEditor, whitespace: Option<impl Element>) } fn remove_trailing_comma(w: ast::WhereClause) -> SyntaxNode { - let w = w.syntax().clone_subtree(); - let mut editor = SyntaxEditor::new(w.clone()); + let (mut editor, w) = SyntaxEditor::new(w.syntax().clone()); if let Some(last) = w.last_child_or_token() && last.kind() == T![,] { diff --git a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs index 2e649f14be..6139395076 100644 --- a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs +++ b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs @@ -133,7 +133,7 @@ fn process_loop_body( ) -> Option<()> { let last = previous_non_trivia_token(body.r_curly_token()?)?.syntax_element(); - let new_body = body.indent(1.into()).clone_subtree(); + let new_body = body.indent(1.into()); let mut continues = vec![]; collect_continue_to( &mut continues, @@ -155,15 +155,15 @@ fn process_loop_body( let block_content = first.clone()..=children.last().unwrap_or(first); let continue_label = make::lifetime("'cont"); - let break_expr = make::expr_break(Some(continue_label.clone()), None).clone_for_update(); - let mut new_edit = SyntaxEditor::new(new_body.syntax().clone()); + let break_expr = make::expr_break(Some(continue_label.clone()), None); + let (mut new_edit, _) = SyntaxEditor::new(new_body.syntax().clone()); for continue_expr in &continues { new_edit.replace(continue_expr.syntax(), break_expr.syntax()); } let new_body = new_edit.finish().new_root().clone(); let elements = itertools::chain( [ - continue_label.syntax().clone_for_update().syntax_element(), + continue_label.syntax().syntax_element(), make::token(T![:]).syntax_element(), make::tokens::single_space().syntax_element(), new_body.syntax_element(), diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index db45916792..004d09acac 100644 --- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -1,17 +1,19 @@ use std::iter::once; use either::Either; -use hir::{Semantics, TypeInfo}; +use hir::Semantics; use ide_db::{RootDatabase, ty_filter::TryEnum}; use syntax::{ AstNode, - SyntaxKind::{CLOSURE_EXPR, FN, FOR_EXPR, LOOP_EXPR, WHILE_EXPR, WHITESPACE}, + SyntaxKind::WHITESPACE, SyntaxNode, T, ast::{ self, edit::{AstNodeEdit, IndentLevel}, syntax_factory::SyntaxFactory, }, + match_ast, + syntax_editor::SyntaxEditor, }; use crate::{ @@ -71,9 +73,7 @@ fn if_expr_to_guarded_return( ) -> Option<()> { let make = SyntaxFactory::without_mappings(); let else_block = match if_expr.else_branch() { - Some(ast::ElseBranch::Block(block_expr)) if is_never_block(&ctx.sema, &block_expr) => { - Some(block_expr) - } + Some(ast::ElseBranch::Block(block_expr)) => Some(block_expr), Some(_) => return None, _ => None, }; @@ -96,25 +96,20 @@ fn if_expr_to_guarded_return( let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; - if parent_block.tail_expr() != Some(if_expr.clone().into()) - && !(else_block.is_some() && ast::ExprStmt::can_cast(if_expr.syntax().parent()?.kind())) - { - return None; - } - // check for early return and continue if is_early_block(&then_block) || is_never_block(&ctx.sema, &then_branch) { return None; } - let parent_container = parent_block.syntax().parent()?; + let container = container_of(&parent_block)?; + let else_block = ElseBlock::new(&ctx.sema, else_block, &container)?; - let early_expression = else_block - .or_else(|| { - early_expression(parent_container, &ctx.sema, &make) - .map(ast::make::tail_only_block_expr) - })? - .reset_indent(); + if parent_block.tail_expr() != Some(if_expr.clone().into()) + && !(else_block.is_never_block + && ast::ExprStmt::can_cast(if_expr.syntax().parent()?.kind())) + { + return None; + } then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{'])?; @@ -137,6 +132,7 @@ fn if_expr_to_guarded_return( |edit| { let make = SyntaxFactory::without_mappings(); let if_indent_level = IndentLevel::from_node(if_expr.syntax()); + let early_expression = else_block.make_early_block(&ctx.sema, &make); let replacement = let_chains.into_iter().map(|expr| { if let ast::Expr::LetExpr(let_expr) = &expr && let (Some(pat), Some(expr)) = (let_expr.pat(), let_expr.expr()) @@ -204,14 +200,9 @@ fn let_stmt_to_guarded_return( let happy_pattern = try_enum.happy_pattern(pat); let target = let_stmt.syntax().text_range(); - let make = SyntaxFactory::without_mappings(); - let early_expression: ast::Expr = { - let parent_block = - let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; - let parent_container = parent_block.syntax().parent()?; - - early_expression(parent_container, &ctx.sema, &make)? - }; + let parent_block = let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; + let container = container_of(&parent_block)?; + let else_block = ElseBlock::new(&ctx.sema, None, &container)?; acc.add( AssistId::refactor_rewrite("convert_to_guarded_return"), @@ -226,7 +217,7 @@ fn let_stmt_to_guarded_return( happy_pattern, let_stmt.ty(), expr.reset_indent(), - ast::make::tail_only_block_expr(early_expression), + else_block.make_early_block(&ctx.sema, &make), ); let let_else_stmt = let_else_stmt.indent(let_indent_level); let_else_stmt.syntax().clone() @@ -239,33 +230,130 @@ fn let_stmt_to_guarded_return( ) } -fn early_expression( - parent_container: SyntaxNode, - sema: &Semantics<'_, RootDatabase>, - make: &SyntaxFactory, -) -> Option<ast::Expr> { - let return_none_expr = || { - let none_expr = make.expr_path(make.ident_path("None")); - make.expr_return(Some(none_expr)) - }; - if let Some(fn_) = ast::Fn::cast(parent_container.clone()) - && let Some(fn_def) = sema.to_def(&fn_) - && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &fn_def.ret_type(sema.db)) - { - return Some(return_none_expr().into()); +fn container_of(block: &ast::BlockExpr) -> Option<SyntaxNode> { + if block.label().is_some() { + return Some(block.syntax().clone()); } - if let Some(body) = ast::ClosureExpr::cast(parent_container.clone()).and_then(|it| it.body()) - && let Some(ret_ty) = sema.type_of_expr(&body).map(TypeInfo::original) - && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &ret_ty) - { - return Some(return_none_expr().into()); + block.syntax().parent() +} + +struct ElseBlock<'db> { + exist_else_block: Option<ast::BlockExpr>, + is_never_block: bool, + kind: EarlyKind<'db>, +} + +impl<'db> ElseBlock<'db> { + fn new( + sema: &Semantics<'db, RootDatabase>, + exist_else_block: Option<ast::BlockExpr>, + parent_container: &SyntaxNode, + ) -> Option<Self> { + let is_never_block = exist_else_block.as_ref().is_some_and(|it| is_never_block(sema, it)); + let kind = EarlyKind::from_node(parent_container, sema)?; + + Some(Self { exist_else_block, is_never_block, kind }) + } + + fn make_early_block( + self, + sema: &Semantics<'_, RootDatabase>, + make: &SyntaxFactory, + ) -> ast::BlockExpr { + let Some(block_expr) = self.exist_else_block else { + return make.tail_only_block_expr(self.kind.make_early_expr(sema, make, None)); + }; + + if self.is_never_block { + return block_expr.reset_indent(); + } + + let (mut edit, block_expr) = SyntaxEditor::with_ast_node(&block_expr.reset_indent()); + + let last_stmt = block_expr.statements().last().map(|it| it.syntax().clone()); + let tail_expr = block_expr.tail_expr().map(|it| it.syntax().clone()); + let Some(last_element) = tail_expr.clone().or(last_stmt.clone()) else { + return make.tail_only_block_expr(self.kind.make_early_expr(sema, make, None)); + }; + let whitespace = last_element.prev_sibling_or_token().filter(|it| it.kind() == WHITESPACE); + + let make = SyntaxFactory::without_mappings(); + + if let Some(tail_expr) = block_expr.tail_expr() + && !self.kind.is_unit() + { + let early_expr = self.kind.make_early_expr(sema, &make, Some(tail_expr.clone())); + edit.replace(tail_expr.syntax(), early_expr.syntax()); + } else { + let last_stmt = match block_expr.tail_expr() { + Some(expr) => make.expr_stmt(expr).syntax().clone(), + None => last_element.clone(), + }; + let whitespace = + make.whitespace(&whitespace.map_or(String::new(), |it| it.to_string())); + let early_expr = self.kind.make_early_expr(sema, &make, None).syntax().clone().into(); + edit.replace_with_many( + last_element, + vec![last_stmt.into(), whitespace.into(), early_expr], + ); + } + + ast::BlockExpr::cast(edit.finish().new_root().clone()).unwrap() + } +} + +enum EarlyKind<'db> { + Continue, + Break(ast::Lifetime, hir::Type<'db>), + Return(hir::Type<'db>), +} + +impl<'db> EarlyKind<'db> { + fn from_node( + parent_container: &SyntaxNode, + sema: &Semantics<'db, RootDatabase>, + ) -> Option<Self> { + match_ast! { + match parent_container { + ast::Fn(it) => Some(Self::Return(sema.to_def(&it)?.ret_type(sema.db))), + ast::ClosureExpr(it) => Some(Self::Return(sema.type_of_expr(&it.body()?)?.original)), + ast::BlockExpr(it) => Some(Self::Break(it.label()?.lifetime()?, sema.type_of_expr(&it.into())?.original)), + ast::WhileExpr(_) => Some(Self::Continue), + ast::LoopExpr(_) => Some(Self::Continue), + ast::ForExpr(_) => Some(Self::Continue), + _ => None + } + } } - Some(match parent_container.kind() { - WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make.expr_continue(None).into(), - FN | CLOSURE_EXPR => make.expr_return(None).into(), - _ => return None, - }) + fn make_early_expr( + &self, + sema: &Semantics<'_, RootDatabase>, + make: &SyntaxFactory, + ret: Option<ast::Expr>, + ) -> ast::Expr { + match self { + EarlyKind::Continue => make.expr_continue(None).into(), + EarlyKind::Break(label, _) => make.expr_break(Some(label.clone()), ret).into(), + EarlyKind::Return(ty) => { + let expr = match TryEnum::from_ty(sema, ty) { + Some(TryEnum::Option) => { + ret.or_else(|| Some(make.expr_path(make.ident_path("None")))) + } + _ => ret, + }; + make.expr_return(expr).into() + } + } + } + + fn is_unit(&self) -> bool { + match self { + EarlyKind::Continue => true, + EarlyKind::Break(_, ty) => ty.is_unit(), + EarlyKind::Return(ty) => ty.is_unit(), + } + } } fn flat_let_chain(mut expr: ast::Expr, make: &SyntaxFactory) -> Vec<ast::Expr> { @@ -465,6 +553,74 @@ fn main() { } #[test] + fn convert_if_let_has_else_block() { + check_assist( + convert_to_guarded_return, + r#" +fn main() -> i32 { + if$0 true { + foo(); + } else { + bar() + } +} +"#, + r#" +fn main() -> i32 { + if false { + return bar(); + } + foo(); +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +fn main() { + if$0 true { + foo(); + } else { + bar() + } +} +"#, + r#" +fn main() { + if false { + bar(); + return + } + foo(); +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +fn main() { + if$0 true { + foo(); + } else { + bar(); + } +} +"#, + r#" +fn main() { + if false { + bar(); + return + } + foo(); +} +"#, + ); + } + + #[test] fn convert_if_let_has_never_type_else_block() { check_assist( convert_to_guarded_return, @@ -512,7 +668,7 @@ fn main() { } #[test] - fn convert_if_let_has_else_block_in_statement() { + fn convert_if_let_has_never_type_else_block_in_statement() { check_assist( convert_to_guarded_return, r#" @@ -923,6 +1079,63 @@ fn main() { } #[test] + fn convert_let_inside_labeled_block() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + 'l: { + if$0 let Some(n) = n { + foo(n); + bar(); + } + } +} +"#, + r#" +fn main() { + 'l: { + let Some(n) = n else { break 'l }; + foo(n); + bar(); + } +} +"#, + ); + } + + #[test] + fn convert_let_inside_for_with_else() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + for n in ns { + if$0 let Some(n) = n { + foo(n); + bar(); + } else { + baz() + } + } +} +"#, + r#" +fn main() { + for n in ns { + let Some(n) = n else { + baz(); + continue + }; + foo(n); + bar(); + } +} +"#, + ); + } + + #[test] fn convert_let_stmt_inside_fn() { check_assist( convert_to_guarded_return, @@ -1186,16 +1399,18 @@ fn main() { } #[test] - fn ignore_else_branch() { + fn ignore_else_branch_has_non_never_types_in_statement() { check_assist_not_applicable( convert_to_guarded_return, r#" fn main() { + some_statements(); if$0 true { foo(); } else { bar() } + some_statements(); } "#, ); diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index ae41e6c015..4ce7a9d866 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -103,11 +103,11 @@ fn edit_struct_def( names: Vec<ast::Name>, ) { let record_fields = tuple_fields.fields().zip(names).filter_map(|(f, name)| { - let field = ast::make::record_field(f.visibility(), name, f.ty()?); - let mut field_editor = SyntaxEditor::new(field.syntax().clone()); + let (mut field_editor, field) = + SyntaxEditor::with_ast_node(&ast::make::record_field(f.visibility(), name, f.ty()?)); field_editor.insert_all( Position::first_child_of(field.syntax()), - f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(), + f.attrs().map(|attr| attr.syntax().clone().into()).collect(), ); ast::RecordField::cast(field_editor.finish().new_root().clone()) }); @@ -120,7 +120,7 @@ fn edit_struct_def( editor.delete(w.syntax()); let mut insert_element = Vec::new(); insert_element.push(ast::make::tokens::single_newline().syntax_element()); - insert_element.push(w.syntax().clone_for_update().syntax_element()); + insert_element.push(w.syntax().syntax_element()); if w.syntax().last_token().is_none_or(|t| t.kind() != SyntaxKind::COMMA) { insert_element.push(ast::make::token(T![,]).into()); } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 124ef509fb..fa5bb39c54 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -92,11 +92,13 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let anchor = if self_param.is_some() { Anchor::Method } else { Anchor::Freestanding }; let insert_after = node_to_insert_after(&body, anchor)?; + let trait_name = ast::Trait::cast(insert_after.clone()).and_then(|trait_| trait_.name()); let semantics_scope = ctx.sema.scope(&insert_after)?; let module = semantics_scope.module(); let edition = semantics_scope.krate().edition(ctx.db()); - let (container_info, contains_tail_expr) = body.analyze_container(&ctx.sema, edition)?; + let (container_info, contains_tail_expr) = + body.analyze_container(&ctx.sema, edition, trait_name)?; let ret_ty = body.return_ty(ctx)?; let control_flow = body.external_control_flow(ctx, &container_info)?; @@ -181,6 +183,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op builder.add_tabstop_before(cap, name); } + // FIXME: wrap non-adt types let fn_def = match fun.self_param_adt(ctx) { Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { fn_def.indent(1.into()); @@ -377,6 +380,7 @@ struct ControlFlow<'db> { struct ContainerInfo<'db> { is_const: bool, parent_loop: Option<SyntaxNode>, + trait_name: Option<ast::Type>, /// The function's return type, const's type etc. ret_type: Option<hir::Type<'db>>, generic_param_lists: Vec<ast::GenericParamList>, @@ -838,6 +842,7 @@ impl FunctionBody { &self, sema: &Semantics<'db, RootDatabase>, edition: Edition, + trait_name: Option<ast::Name>, ) -> Option<(ContainerInfo<'db>, bool)> { let mut ancestors = self.parent()?.ancestors(); let infer_expr_opt = |expr| sema.type_of_expr(&expr?).map(TypeInfo::adjusted); @@ -924,6 +929,9 @@ impl FunctionBody { false }; + // FIXME: make trait arguments + let trait_name = trait_name.map(|name| make::ty_path(make::ext::ident_path(&name.text()))); + let parent = self.parent()?; let parents = generic_parents(&parent); let generic_param_lists = parents.iter().filter_map(|it| it.generic_param_list()).collect(); @@ -934,6 +942,7 @@ impl FunctionBody { ContainerInfo { is_const, parent_loop, + trait_name, ret_type: ty, generic_param_lists, where_clauses, @@ -1419,14 +1428,18 @@ fn fixup_call_site(builder: &mut SourceChangeBuilder, body: &FunctionBody) { fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) -> SyntaxNode { let ret_ty = fun.return_type(ctx); - let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx, fun.mods.edition))); let name = fun.name.clone(); - let mut call_expr = if fun.self_param.is_some() { + let args = fun.params.iter().map(|param| param.to_arg(ctx, fun.mods.edition)); + let mut call_expr = if fun.make_this_param().is_some() { + let self_arg = make::expr_path(make::ext::ident_path("self")); + let func = make::expr_path(make::path_unqualified(make::path_segment(name))); + make::expr_call(func, make::arg_list(Some(self_arg).into_iter().chain(args))).into() + } else if fun.self_param.is_some() { let self_arg = make::expr_path(make::ext::ident_path("self")); - make::expr_method_call(self_arg, name, args).into() + make::expr_method_call(self_arg, name, make::arg_list(args)).into() } else { let func = make::expr_path(make::path_unqualified(make::path_segment(name))); - make::expr_call(func, args).into() + make::expr_call(func, make::arg_list(args)).into() }; let handler = FlowHandler::from_ret_ty(fun, &ret_ty); @@ -1729,9 +1742,28 @@ impl<'db> Function<'db> { module: hir::Module, edition: Edition, ) -> ast::ParamList { - let self_param = self.self_param.clone(); + let this_param = self.make_this_param().map(|f| f()); + let self_param = self.self_param.clone().filter(|_| this_param.is_none()); let params = self.params.iter().map(|param| param.to_param(ctx, module, edition)); - make::param_list(self_param, params) + make::param_list(self_param, this_param.into_iter().chain(params)) + } + + fn make_this_param(&self) -> Option<impl FnOnce() -> ast::Param> { + if let Some(name) = self.mods.trait_name.clone() + && let Some(self_param) = &self.self_param + { + Some(|| { + let bounds = make::type_bound_list([make::type_bound(name)]); + let pat = make::path_pat(make::ext::ident_path("this")); + let mut ty = make::impl_trait_type(bounds.unwrap()).into(); + if self_param.amp_token().is_some() { + ty = make::ty_ref(ty, self_param.mut_token().is_some()); + } + make::param(pat, ty) + }) + } else { + None + } } fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> { @@ -1806,10 +1838,12 @@ fn make_body( ) -> ast::BlockExpr { let ret_ty = fun.return_type(ctx); let handler = FlowHandler::from_ret_ty(fun, &ret_ty); + let to_this_param = fun.self_param.clone().filter(|_| fun.make_this_param().is_some()); let block = match &fun.body { FunctionBody::Expr(expr) => { - let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); + let expr = + rewrite_body_segment(ctx, to_this_param, &fun.params, &handler, expr.syntax()); let expr = ast::Expr::cast(expr).expect("Body segment should be an expr"); match expr { ast::Expr::BlockExpr(block) => { @@ -1847,7 +1881,7 @@ fn make_body( .filter(|it| text_range.contains_range(it.text_range())) .map(|it| match &it { syntax::NodeOrToken::Node(n) => syntax::NodeOrToken::Node( - rewrite_body_segment(ctx, &fun.params, &handler, n), + rewrite_body_segment(ctx, to_this_param.clone(), &fun.params, &handler, n), ), _ => it, }) @@ -1997,11 +2031,13 @@ fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> fn rewrite_body_segment( ctx: &AssistContext<'_>, + to_this_param: Option<ast::SelfParam>, params: &[Param<'_>], handler: &FlowHandler<'_>, syntax: &SyntaxNode, ) -> SyntaxNode { - let syntax = fix_param_usages(ctx, params, syntax); + let to_this_param = to_this_param.and_then(|it| ctx.sema.to_def(&it)); + let syntax = fix_param_usages(ctx, to_this_param, params, syntax); update_external_control_flow(handler, &syntax); syntax } @@ -2009,30 +2045,46 @@ fn rewrite_body_segment( /// change all usages to account for added `&`/`&mut` for some params fn fix_param_usages( ctx: &AssistContext<'_>, + to_this_param: Option<Local>, params: &[Param<'_>], syntax: &SyntaxNode, ) -> SyntaxNode { let mut usages_for_param: Vec<(&Param<'_>, Vec<ast::Expr>)> = Vec::new(); + let mut usages_for_self_param: Vec<ast::Expr> = Vec::new(); let tm = TreeMutator::new(syntax); + let reference_filter = |reference: &FileReference| { + syntax + .text_range() + .contains_range(reference.range) + .then_some(()) + .and_then(|_| path_element_of_reference(syntax, reference)) + .map(|expr| tm.make_mut(&expr)) + }; + if let Some(self_param) = to_this_param { + usages_for_self_param = LocalUsages::find_local_usages(ctx, self_param) + .iter() + .filter_map(reference_filter) + .collect(); + } for param in params { if !param.kind().is_ref() { continue; } let usages = LocalUsages::find_local_usages(ctx, param.var); - let usages = usages - .iter() - .filter(|reference| syntax.text_range().contains_range(reference.range)) - .filter_map(|reference| path_element_of_reference(syntax, reference)) - .map(|expr| tm.make_mut(&expr)); + let usages = usages.iter().filter_map(reference_filter); usages_for_param.push((param, usages.unique().collect())); } let res = tm.make_syntax_mut(syntax); + for self_usage in usages_for_self_param { + let this_expr = make::expr_path(make::ext::ident_path("this")).clone_for_update(); + ted::replace(self_usage.syntax(), this_expr.syntax()); + } for (param, usages) in usages_for_param { for usage in usages { match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { @@ -2940,6 +2992,35 @@ impl S { } #[test] + fn method_in_trait() { + check_assist( + extract_function, + r#" +trait Foo { + fn f(&self) -> i32; + + fn foo(&self) -> i32 { + $0self.f()+self.f()$0 + } +} +"#, + r#" +trait Foo { + fn f(&self) -> i32; + + fn foo(&self) -> i32 { + fun_name(self) + } +} + +fn $0fun_name(this: &impl Foo) -> i32 { + this.f()+this.f() +} +"#, + ); + } + + #[test] fn variable_defined_inside_and_used_after_no_ret() { check_assist( extract_function, diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 4c46a51bef..3bbf9a0ad3 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -6,7 +6,7 @@ use ide_db::{ FxHashSet, RootDatabase, defs::Definition, helpers::mod_path_to_ast, - imports::insert_use::{ImportScope, InsertUseConfig, insert_use}, + imports::insert_use::{ImportScope, InsertUseConfig, insert_use_with_editor}, path_transform::PathTransform, search::FileReference, }; @@ -16,12 +16,14 @@ use syntax::{ SyntaxKind::*, SyntaxNode, T, ast::{ - self, AstNode, HasAttrs, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, make, + self, AstNode, HasAttrs, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, + syntax_factory::SyntaxFactory, }, - match_ast, ted, + match_ast, + syntax_editor::{Position, SyntaxEditor}, }; -use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder}; +use crate::{AssistContext, AssistId, Assists}; // Assist: extract_struct_from_enum_variant // @@ -58,6 +60,8 @@ pub(crate) fn extract_struct_from_enum_variant( "Extract struct from enum variant", target, |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(variant.syntax()); let edition = enum_hir.krate(ctx.db()).edition(ctx.db()); let variant_hir_name = variant_hir.name(ctx.db()); let enum_module_def = ModuleDef::from(enum_hir); @@ -73,40 +77,56 @@ pub(crate) fn extract_struct_from_enum_variant( def_file_references = Some(references); continue; } - builder.edit_file(file_id.file_id(ctx.db())); let processed = process_references( ctx, - builder, &mut visited_modules_set, &enum_module_def, &variant_hir_name, references, ); + if processed.is_empty() { + continue; + } + let mut file_editor = builder.make_editor(processed[0].0.syntax()); processed.into_iter().for_each(|(path, node, import)| { - apply_references(ctx.config.insert_use, path, node, import, edition) + apply_references( + ctx.config.insert_use, + path, + node, + import, + edition, + &mut file_editor, + &make, + ) }); + file_editor.add_mappings(make.take()); + builder.add_file_edits(file_id.file_id(ctx.db()), file_editor); } - builder.edit_file(ctx.vfs_file_id()); - let variant = builder.make_mut(variant.clone()); if let Some(references) = def_file_references { let processed = process_references( ctx, - builder, &mut visited_modules_set, &enum_module_def, &variant_hir_name, references, ); processed.into_iter().for_each(|(path, node, import)| { - apply_references(ctx.config.insert_use, path, node, import, edition) + apply_references( + ctx.config.insert_use, + path, + node, + import, + edition, + &mut editor, + &make, + ) }); } - let generic_params = enum_ast - .generic_param_list() - .and_then(|known_generics| extract_generic_params(&known_generics, &field_list)); - let generics = generic_params.as_ref().map(|generics| generics.clone_for_update()); + let generic_params = enum_ast.generic_param_list().and_then(|known_generics| { + extract_generic_params(&make, &known_generics, &field_list) + }); // resolve GenericArg in field_list to actual type let field_list = if let Some((target_scope, source_scope)) = @@ -124,25 +144,45 @@ pub(crate) fn extract_struct_from_enum_variant( } } } else { - field_list.clone_for_update() + field_list.clone() }; - let def = - create_struct_def(variant_name.clone(), &variant, &field_list, generics, &enum_ast); + let (comments_for_struct, comments_to_delete) = + collect_variant_comments(&make, variant.syntax()); + for element in &comments_to_delete { + editor.delete(element.clone()); + } + + let def = create_struct_def( + &make, + variant_name.clone(), + &field_list, + generic_params.clone(), + &enum_ast, + ); let enum_ast = variant.parent_enum(); let indent = enum_ast.indent_level(); let def = def.indent(indent); - ted::insert_all( - ted::Position::before(enum_ast.syntax()), - vec![ - def.syntax().clone().into(), - make::tokens::whitespace(&format!("\n\n{indent}")).into(), - ], + let mut insert_items: Vec<SyntaxElement> = Vec::new(); + for attr in enum_ast.attrs() { + insert_items.push(attr.syntax().clone().into()); + insert_items.push(make.whitespace("\n").into()); + } + insert_items.extend(comments_for_struct); + insert_items.push(def.syntax().clone().into()); + insert_items.push(make.whitespace(&format!("\n\n{indent}")).into()); + editor.insert_all_with_whitespace( + Position::before(enum_ast.syntax()), + insert_items, + &make, ); - update_variant(&variant, generic_params.map(|g| g.clone_for_update())); + update_variant(&make, &mut editor, &variant, generic_params); + + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -184,6 +224,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En } fn extract_generic_params( + make: &SyntaxFactory, known_generics: &ast::GenericParamList, field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, ) -> Option<ast::GenericParamList> { @@ -201,7 +242,7 @@ fn extract_generic_params( }; let generics = generics.into_iter().filter_map(|(param, tag)| tag.then_some(param)); - tagged_one.then(|| make::generic_param_list(generics)) + tagged_one.then(|| make.generic_param_list(generics)) } fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, bool)]) -> bool { @@ -250,82 +291,74 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b } fn create_struct_def( + make: &SyntaxFactory, name: ast::Name, - variant: &ast::Variant, field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, generics: Option<ast::GenericParamList>, enum_: &ast::Enum, ) -> ast::Struct { let enum_vis = enum_.visibility(); - let insert_vis = |node: &'_ SyntaxNode, vis: &'_ SyntaxNode| { - let vis = vis.clone_for_update(); - ted::insert(ted::Position::before(node), vis); - }; - // for fields without any existing visibility, use visibility of enum let field_list: ast::FieldList = match field_list { Either::Left(field_list) => { if let Some(vis) = &enum_vis { - field_list - .fields() - .filter(|field| field.visibility().is_none()) - .filter_map(|field| field.name()) - .for_each(|it| insert_vis(it.syntax(), vis.syntax())); + let new_fields = field_list.fields().map(|field| { + if field.visibility().is_none() + && let Some(name) = field.name() + && let Some(ty) = field.ty() + { + make.record_field(Some(vis.clone()), name, ty) + } else { + field + } + }); + make.record_field_list(new_fields).into() + } else { + field_list.clone().into() } - - field_list.clone().into() } Either::Right(field_list) => { if let Some(vis) = &enum_vis { - field_list - .fields() - .filter(|field| field.visibility().is_none()) - .filter_map(|field| field.ty()) - .for_each(|it| insert_vis(it.syntax(), vis.syntax())); + let new_fields = field_list.fields().map(|field| { + if field.visibility().is_none() + && let Some(ty) = field.ty() + { + make.tuple_field(Some(vis.clone()), ty) + } else { + field + } + }); + make.tuple_field_list(new_fields).into() + } else { + field_list.clone().into() } - - field_list.clone().into() } }; - let strukt = make::struct_(enum_vis, name, generics, field_list).clone_for_update(); - - // take comments from variant - ted::insert_all( - ted::Position::first_child_of(strukt.syntax()), - take_all_comments(variant.syntax()), - ); - - // copy attributes from enum - ted::insert_all( - ted::Position::first_child_of(strukt.syntax()), - enum_ - .attrs() - .flat_map(|it| { - vec![it.syntax().clone_for_update().into(), make::tokens::single_newline().into()] - }) - .collect(), - ); - - strukt + make.struct_(enum_vis, name, generics, field_list) } -fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList>) -> Option<()> { +fn update_variant( + make: &SyntaxFactory, + editor: &mut SyntaxEditor, + variant: &ast::Variant, + generics: Option<ast::GenericParamList>, +) -> Option<()> { let name = variant.name()?; let generic_args = generics .filter(|generics| generics.generic_params().count() > 0) .map(|generics| generics.to_generic_args()); // FIXME: replace with a `ast::make` constructor let ty = match generic_args { - Some(generic_args) => make::ty(&format!("{name}{generic_args}")), - None => make::ty(&name.text()), + Some(generic_args) => make.ty(&format!("{name}{generic_args}")), + None => make.ty(&name.text()), }; // change from a record to a tuple field list - let tuple_field = make::tuple_field(None, ty); - let field_list = make::tuple_field_list(iter::once(tuple_field)).clone_for_update(); - ted::replace(variant.field_list()?.syntax(), field_list.syntax()); + let tuple_field = make.tuple_field(None, ty); + let field_list = make.tuple_field_list(iter::once(tuple_field)); + editor.replace(variant.field_list()?.syntax(), field_list.syntax()); // remove any ws after the name if let Some(ws) = name @@ -333,35 +366,39 @@ fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList .siblings_with_tokens(syntax::Direction::Next) .find_map(|tok| tok.into_token().filter(|tok| tok.kind() == WHITESPACE)) { - ted::remove(SyntaxElement::Token(ws)); + editor.delete(ws); } Some(()) } -// Note: this also detaches whitespace after comments, -// since `SyntaxNode::splice_children` (and by extension `ted::insert_all_raw`) -// detaches nodes. If we only took the comments, we'd leave behind the old whitespace. -fn take_all_comments(node: &SyntaxNode) -> Vec<SyntaxElement> { - let mut remove_next_ws = false; - node.children_with_tokens() - .filter_map(move |child| match child.kind() { +fn collect_variant_comments( + make: &SyntaxFactory, + node: &SyntaxNode, +) -> (Vec<SyntaxElement>, Vec<SyntaxElement>) { + let mut to_insert: Vec<SyntaxElement> = Vec::new(); + let mut to_delete: Vec<SyntaxElement> = Vec::new(); + let mut after_comment = false; + + for child in node.children_with_tokens() { + match child.kind() { COMMENT => { - remove_next_ws = true; - child.detach(); - Some(child) + after_comment = true; + to_insert.push(child.clone()); + to_delete.push(child); } - WHITESPACE if remove_next_ws => { - remove_next_ws = false; - child.detach(); - Some(make::tokens::single_newline().into()) + WHITESPACE if after_comment => { + after_comment = false; + to_insert.push(make.whitespace("\n").into()); + to_delete.push(child); } _ => { - remove_next_ws = false; - None + after_comment = false; } - }) - .collect() + } + } + + (to_insert, to_delete) } fn apply_references( @@ -370,20 +407,27 @@ fn apply_references( node: SyntaxNode, import: Option<(ImportScope, hir::ModPath)>, edition: Edition, + editor: &mut SyntaxEditor, + make: &SyntaxFactory, ) { if let Some((scope, path)) = import { - insert_use(&scope, mod_path_to_ast(&path, edition), &insert_use_cfg); + insert_use_with_editor( + &scope, + mod_path_to_ast(&path, edition), + &insert_use_cfg, + editor, + make, + ); } // deep clone to prevent cycle - let path = make::path_from_segments(iter::once(segment.clone_subtree()), false); - ted::insert_raw(ted::Position::before(segment.syntax()), path.clone_for_update().syntax()); - ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['('])); - ted::insert_raw(ted::Position::after(&node), make::token(T![')'])); + let path = make.path_from_segments(iter::once(segment.clone()), false); + editor.insert(Position::before(segment.syntax()), make.token(T!['('])); + editor.insert(Position::before(segment.syntax()), path.syntax()); + editor.insert(Position::after(&node), make.token(T![')'])); } fn process_references( ctx: &AssistContext<'_>, - builder: &mut SourceChangeBuilder, visited_modules: &mut FxHashSet<Module>, enum_module_def: &ModuleDef, variant_hir_name: &Name, @@ -394,8 +438,6 @@ fn process_references( refs.into_iter() .flat_map(|reference| { let (segment, scope_node, module) = reference_to_node(&ctx.sema, reference)?; - let segment = builder.make_mut(segment); - let scope_node = builder.make_syntax_mut(scope_node); if !visited_modules.contains(&module) { let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db))); diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index e5ce02cf53..1556339d8d 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs @@ -283,13 +283,17 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr { /// Check whether the node is a valid expression which can be extracted to a variable. /// In general that's true for any expression, but in some cases that would produce invalid code. fn valid_target_expr(ctx: &AssistContext<'_>) -> impl Fn(SyntaxNode) -> Option<ast::Expr> { - |node| match node.kind() { + let selection = ctx.selection_trimmed(); + move |node| match node.kind() { SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None, SyntaxKind::BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()), SyntaxKind::RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()), SyntaxKind::BLOCK_EXPR => { ast::BlockExpr::cast(node).filter(|it| it.is_standalone()).map(ast::Expr::from) } + SyntaxKind::ARG_LIST => ast::ArgList::cast(node)? + .args() + .find(|expr| crate::utils::is_selected(expr, selection, false)), SyntaxKind::PATH_EXPR => { let path_expr = ast::PathExpr::cast(node)?; let path_resolution = ctx.sema.resolve_path(&path_expr.path()?)?; @@ -1286,6 +1290,33 @@ fn main() { } #[test] + fn extract_var_in_arglist_with_comma() { + check_assist_by_label( + extract_variable, + r#" +fn main() { + let x = 2; + foo( + x + x, + $0x - x,$0 + ) +} +"#, + r#" +fn main() { + let x = 2; + let $0var_name = x - x; + foo( + x + x, + var_name, + ) +} +"#, + "Extract into variable", + ); + } + + #[test] fn extract_var_path_simple() { check_assist_by_label( extract_variable, diff --git a/crates/ide-assists/src/handlers/flip_binexpr.rs b/crates/ide-assists/src/handlers/flip_binexpr.rs index 8f2306e903..922a61bf3a 100644 --- a/crates/ide-assists/src/handlers/flip_binexpr.rs +++ b/crates/ide-assists/src/handlers/flip_binexpr.rs @@ -142,11 +142,11 @@ pub(crate) fn flip_range_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt } (Some(start), None) => { edit.delete(start.syntax()); - edit.insert(Position::after(&op), start.syntax().clone_for_update()); + edit.insert(Position::after(&op), start.syntax()); } (None, Some(end)) => { edit.delete(end.syntax()); - edit.insert(Position::before(&op), end.syntax().clone_for_update()); + edit.insert(Position::before(&op), end.syntax()); } (None, None) => (), } diff --git a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index e022a27e51..fccc04770e 100644 --- a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -279,7 +279,7 @@ fn todo_fn(f: &ast::Fn, config: &AssistConfig) -> ast::Fn { } fn cfg_attrs(node: &impl HasAttrs) -> impl Iterator<Item = ast::Attr> { - node.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")) + node.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index f703e4dc4a..abe447d9d9 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -363,9 +363,9 @@ fn generate_impl( ast_strukt, &old_impl, &transform_args, - trait_args.clone_subtree(), + trait_args.clone(), ) { - *trait_args = new_args.clone_subtree(); + *trait_args = new_args.clone(); Some(new_args) } else { None @@ -563,7 +563,7 @@ fn finalize_delegate( return Some(delegate.clone()); } - let mut editor = SyntaxEditor::new(delegate.syntax().clone_subtree()); + let (mut editor, delegate) = SyntaxEditor::with_ast_node(delegate); // 1. Replace assoc_item_list if we have new items if let Some(items) = assoc_items @@ -577,7 +577,7 @@ fn finalize_delegate( // 2. Remove useless where clauses if remove_where_clauses { - remove_useless_where_clauses(&mut editor, delegate); + remove_useless_where_clauses(&mut editor, &delegate); } ast::Impl::cast(editor.finish().new_root().clone()) @@ -703,7 +703,7 @@ fn resolve_name_conflicts( } } p @ ast::GenericParam::LifetimeParam(_) => { - new_params.push(p.clone_for_update()); + new_params.push(p); } ast::GenericParam::TypeParam(t) => { let type_bounds = t.type_bound_list(); diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs index 3ef68f06e4..7aeb5e3396 100644 --- a/crates/ide-assists/src/handlers/generate_derive.rs +++ b/crates/ide-assists/src/handlers/generate_derive.rs @@ -68,9 +68,11 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt ], ); - let delimiter = derive - .meta() - .expect("make::attr_outer was expected to have Meta") + let meta = derive.meta().expect("make::attr_outer was expected to have Meta"); + let ast::Meta::TokenTreeMeta(meta) = meta else { + unreachable!("make::attr_outer was passed a token tree meta"); + }; + let delimiter = meta .token_tree() .expect("failed to get token tree out of Meta") .r_paren_token() diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index 62ffd3d965..4cd018d02d 100644 --- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -429,7 +429,7 @@ fn build_source_change( generate_getter_from_info(ctx, &assist_info, record_field_info, &syntax_factory) } }; - let new_fn = method.clone_for_update(); + let new_fn = method; let new_fn = new_fn.indent(1.into()); new_fn.into() }) diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs index 2d1235792d..af123eeaa0 100644 --- a/crates/ide-assists/src/handlers/generate_impl.rs +++ b/crates/ide-assists/src/handlers/generate_impl.rs @@ -8,8 +8,8 @@ use syntax::{ use crate::{ AssistContext, AssistId, Assists, utils::{ - self, DefaultMethods, IgnoreAssocItems, generate_impl_with_factory, - generate_trait_impl_intransitive, + self, DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl, + generate_impl_with_factory, generate_trait_impl_intransitive, }, }; @@ -212,7 +212,8 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> make_impl_(None) } else { let impl_ = make_impl_(None); - let assoc_items = utils::add_trait_assoc_items_to_impl( + let assoc_items = add_trait_assoc_items_to_impl( + &make, &ctx.sema, ctx.config, &missing_items, diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index 3a62a8853e..31e49c8ce4 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -67,8 +67,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> format!("Generate `{trait_new}` impl from this `{trait_name}` trait"), target, |edit| { - let impl_clone = impl_def.reset_indent().clone_subtree(); - let mut editor = SyntaxEditor::new(impl_clone.syntax().clone()); + let (mut editor, impl_clone) = SyntaxEditor::with_ast_node(&impl_def.reset_indent()); let factory = SyntaxFactory::without_mappings(); apply_generate_mut_impl(&mut editor, &factory, &impl_clone, trait_new); diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index d3022ceda3..7746cdc068 100644 --- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -1,14 +1,16 @@ -use ast::make; use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; use ide_db::{ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast, imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor, }; -use syntax::syntax_editor::{Element, Position}; +use syntax::syntax_editor::{Position, SyntaxEditor}; use syntax::{ TokenText, - ast::{self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit}, + ast::{ + self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit, + syntax_factory::SyntaxFactory, + }, }; use crate::{ @@ -78,47 +80,51 @@ pub(crate) fn generate_single_field_struct_from( "Generate single field `From`", strukt.syntax().text_range(), |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(strukt.syntax()); + let indent = strukt.indent_level(); let ty_where_clause = strukt.where_clause(); let type_gen_params = strukt.generic_param_list(); let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args()); - let trait_gen_args = Some(make::generic_arg_list([ast::GenericArg::TypeArg( - make::type_arg(main_field_ty.clone()), - )])); + let trait_gen_args = Some(make.generic_arg_list( + [ast::GenericArg::TypeArg(make.type_arg(main_field_ty.clone()))], + false, + )); - let ty = make::ty(&strukt_name.text()); + let ty = make.ty(&strukt_name.text()); let constructor = - make_adt_constructor(names.as_deref(), constructors, &main_field_name); - let body = make::block_expr([], Some(constructor)); + make_adt_constructor(names.as_deref(), constructors, &main_field_name, &make); + let body = make.block_expr([], Some(constructor)); - let fn_ = make::fn_( - None, - None, - make::name("from"), - None, - None, - make::param_list( + let fn_ = make + .fn_( + [], None, - [make::param( - make::path_pat(make::path_from_text(&main_field_name)), - main_field_ty, - )], - ), - body, - Some(make::ret_type(make::ty("Self"))), - false, - false, - false, - false, - ) - .indent(1.into()); - - let cfg_attrs = strukt - .attrs() - .filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); - - let impl_ = make::impl_trait( + make.name("from"), + None, + None, + make.param_list( + None, + [make.param( + make.path_pat(make.path_from_text(&main_field_name)), + main_field_ty, + )], + ), + body, + Some(make.ret_type(make.ty("Self"))), + false, + false, + false, + false, + ) + .indent_with_mapping(1.into(), &make); + + let cfg_attrs = + strukt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); + + let impl_ = make.impl_trait( cfg_attrs, false, None, @@ -126,28 +132,31 @@ pub(crate) fn generate_single_field_struct_from( type_gen_params, type_gen_args, false, - make::ty("From"), + make.ty("From"), ty.clone(), None, ty_where_clause.map(|wc| wc.reset_indent()), None, - ) - .clone_for_update(); - - impl_.get_or_create_assoc_item_list().add_item(fn_.into()); - let impl_ = impl_.indent(indent); + ); - let mut edit = builder.make_editor(strukt.syntax()); + let (mut impl_editor, impl_root) = SyntaxEditor::with_ast_node(&impl_); + let assoc_list = + impl_root.get_or_create_assoc_item_list_with_editor(&mut impl_editor, &make); + assoc_list.add_items(&mut impl_editor, vec![fn_.into()]); + let impl_ = ast::Impl::cast(impl_editor.finish().new_root().clone()) + .unwrap() + .indent_with_mapping(indent, &make); - edit.insert_all( + editor.insert_all( Position::after(strukt.syntax()), vec![ - make::tokens::whitespace(&format!("\n\n{indent}")).syntax_element(), - impl_.syntax().syntax_element(), + make.whitespace(&format!("\n\n{indent}")).into(), + impl_.syntax().clone().into(), ], ); - builder.add_file_edits(ctx.vfs_file_id(), edit); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -156,19 +165,18 @@ fn make_adt_constructor( names: Option<&[ast::Name]>, constructors: Vec<Option<ast::Expr>>, main_field_name: &TokenText<'_>, + make: &SyntaxFactory, ) -> ast::Expr { if let Some(names) = names { - let fields = make::record_expr_field_list(names.iter().zip(constructors).map( - |(name, initializer)| { - make::record_expr_field(make::name_ref(&name.text()), initializer) - }, + let fields = make.record_expr_field_list(names.iter().zip(constructors).map( + |(name, initializer)| make.record_expr_field(make.name_ref(&name.text()), initializer), )); - make::record_expr(make::path_from_text("Self"), fields).into() + make.record_expr(make.path_from_text("Self"), fields).into() } else { - let arg_list = make::arg_list(constructors.into_iter().map(|expr| { - expr.unwrap_or_else(|| make::expr_path(make::path_from_text(main_field_name))) + let arg_list = make.arg_list(constructors.into_iter().map(|expr| { + expr.unwrap_or_else(|| make.expr_path(make.path_from_text(main_field_name))) })); - make::expr_call(make::expr_path(make::path_from_text("Self")), arg_list).into() + make.expr_call(make.expr_path(make.path_from_text("Self")), arg_list).into() } } @@ -177,6 +185,7 @@ fn make_constructors( module: hir::Module, types: &[ast::Type], ) -> Vec<Option<ast::Expr>> { + let make = SyntaxFactory::without_mappings(); let (db, sema) = (ctx.db(), &ctx.sema); let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db))); types @@ -184,7 +193,7 @@ fn make_constructors( .map(|ty| { let ty = sema.resolve_type(ty)?; if ty.is_unit() { - return Some(make::expr_tuple([]).into()); + return Some(make.expr_tuple([]).into()); } let item_in_ns = ModuleDef::Adt(ty.as_adt()?).into(); let edition = module.krate(db).edition(db); diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 1286abe356..2d3d05849b 100644 --- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -4,6 +4,7 @@ use syntax::{ AstNode, AstToken, SyntaxKind, T, ast::{ self, HasDocComments, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, make, + syntax_factory::SyntaxFactory, }, syntax_editor::{Position, SyntaxEditor}, }; @@ -98,8 +99,8 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ impl_ast.syntax().text_range(), |builder| { let trait_items: ast::AssocItemList = { - let trait_items = impl_assoc_items.clone_subtree(); - let mut trait_items_editor = SyntaxEditor::new(trait_items.syntax().clone()); + let (mut trait_items_editor, trait_items) = + SyntaxEditor::with_ast_node(&impl_assoc_items); trait_items.assoc_items().for_each(|item| { strip_body(&mut trait_items_editor, &item); @@ -107,17 +108,18 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ }); ast::AssocItemList::cast(trait_items_editor.finish().new_root().clone()).unwrap() }; - let trait_ast = make::trait_( + + let factory = SyntaxFactory::with_mappings(); + let trait_ast = factory.trait_( false, &trait_name(&impl_assoc_items).text(), impl_ast.generic_param_list(), impl_ast.where_clause(), trait_items, - ) - .clone_for_update(); + ); let trait_name = trait_ast.name().expect("new trait should have a name"); - let trait_name_ref = make::name_ref(&trait_name.to_string()).clone_for_update(); + let trait_name_ref = factory.name_ref(&trait_name.to_string()); // Change `impl Foo` to `impl NewTrait for Foo` let mut elements = vec![ @@ -128,7 +130,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ ]; if let Some(params) = impl_ast.generic_param_list() { - let gen_args = ¶ms.to_generic_args().clone_for_update(); + let gen_args = ¶ms.to_generic_args(); elements.insert(1, gen_args.syntax().clone().into()); } @@ -156,6 +158,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ editor.add_annotation(trait_name_ref.syntax(), placeholder); } + editor.add_mappings(factory.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs index f3ebe61078..4b60f0ac1e 100644 --- a/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -170,7 +170,7 @@ impl Replacement { Replacement::Generic { lifetime_map, const_and_type_map } => { create_replacement(lifetime_map, const_and_type_map, concrete_type) } - Replacement::Plain => concrete_type.syntax().clone_subtree().clone_for_update(), + Replacement::Plain => concrete_type.syntax().clone(), } } } @@ -312,8 +312,7 @@ fn create_replacement( const_and_type_map: &ConstAndTypeMap, concrete_type: &ast::Type, ) -> SyntaxNode { - let updated_concrete_type = concrete_type.syntax().clone_subtree(); - let mut editor = SyntaxEditor::new(updated_concrete_type.clone()); + let (mut editor, updated_concrete_type) = SyntaxEditor::new(concrete_type.syntax().clone()); let mut replacements: Vec<(SyntaxNode, SyntaxNode)> = Vec::new(); let mut removals: Vec<NodeOrToken<SyntaxNode, _>> = Vec::new(); @@ -361,7 +360,7 @@ fn create_replacement( continue; } - replacements.push((syntax.clone(), new_lifetime.syntax().clone_for_update())); + replacements.push((syntax.clone(), new_lifetime.syntax().clone())); } } else if let Some(name_ref) = ast::NameRef::cast(syntax.clone()) { let Some(replacement_syntax) = const_and_type_map.0.get(&name_ref.to_string()) else { @@ -449,15 +448,12 @@ impl ConstOrTypeGeneric { } fn replacement_value(&self) -> Option<SyntaxNode> { - Some( - match self { - ConstOrTypeGeneric::ConstArg(ca) => ca.expr()?.syntax().clone(), - ConstOrTypeGeneric::TypeArg(ta) => ta.syntax().clone(), - ConstOrTypeGeneric::ConstParam(cp) => cp.default_val()?.syntax().clone(), - ConstOrTypeGeneric::TypeParam(tp) => tp.default_type()?.syntax().clone(), - } - .clone_for_update(), - ) + Some(match self { + ConstOrTypeGeneric::ConstArg(ca) => ca.expr()?.syntax().clone(), + ConstOrTypeGeneric::TypeArg(ta) => ta.syntax().clone(), + ConstOrTypeGeneric::ConstParam(cp) => cp.default_val()?.syntax().clone(), + ConstOrTypeGeneric::TypeParam(tp) => tp.default_type()?.syntax().clone(), + }) } } diff --git a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 854e9561d2..5e8ea7daff 100644 --- a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -97,8 +97,7 @@ fn generate_fn_def_assist( }; acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { - let root = fn_def.syntax().ancestors().last().unwrap().clone(); - let mut editor = SyntaxEditor::new(root); + let mut editor = edit.make_editor(fn_def.syntax()); let factory = SyntaxFactory::with_mappings(); if let Some(generic_list) = fn_def.generic_param_list() { @@ -167,8 +166,7 @@ fn generate_impl_def_assist( let new_lifetime_name = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { - let root = impl_def.syntax().ancestors().last().unwrap().clone(); - let mut editor = SyntaxEditor::new(root); + let mut editor = edit.make_editor(impl_def.syntax()); let factory = SyntaxFactory::without_mappings(); if let Some(generic_list) = impl_def.generic_param_list() { diff --git a/crates/ide-assists/src/handlers/pull_assignment_up.rs b/crates/ide-assists/src/handlers/pull_assignment_up.rs index 812ebf6c6e..74ed2e14fa 100644 --- a/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -75,7 +75,8 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> } let target = tgt.syntax().text_range(); - let edit_tgt = tgt.syntax().clone_subtree(); + let (mut editor, edit_tgt) = SyntaxEditor::new(tgt.syntax().clone()); + let assignments: Vec<_> = collector .assignments .into_iter() @@ -93,7 +94,6 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> }) .collect(); - let mut editor = SyntaxEditor::new(edit_tgt); for (stmt, rhs) in assignments { let mut stmt = stmt.syntax().clone(); if let Some(parent) = stmt.parent() diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index 08779a3ed1..f4c354b8a2 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -50,7 +50,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let mut editor = builder.make_editor(ctx.source_file().syntax()); for (range, expr) in replacements { if let Some(expr) = expr { - editor.insert(Position::before(range[0].clone()), expr.syntax().clone_for_update()); + editor.insert(Position::before(range[0].clone()), expr.syntax()); } for node_or_token in range { editor.delete(node_or_token); @@ -163,7 +163,7 @@ fn compute_dbg_replacement( None => false, }; let expr = replace_nested_dbgs(expr.clone()); - let expr = if wrap { make::expr_paren(expr).into() } else { expr.clone_subtree() }; + let expr = if wrap { make::expr_paren(expr).into() } else { expr }; (vec![macro_call.syntax().clone().into()], Some(expr)) } // dbg!(expr0, expr1, ...) @@ -209,8 +209,7 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr { return replaced; } - let expanded = expanded.clone_subtree(); - let mut editor = SyntaxEditor::new(expanded.syntax().clone()); + let (mut editor, expanded) = SyntaxEditor::with_ast_node(&expanded); // We need to collect to avoid mutation during traversal. let macro_exprs: Vec<_> = expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect(); @@ -222,7 +221,7 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr { }; if let Some(expr) = expr_opt { - editor.replace(mac.syntax(), expr.syntax().clone_for_update()); + editor.replace(mac.syntax(), expr.syntax()); } else { editor.delete(mac.syntax()); } diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index f54f7a02d2..5e595218f6 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -12,8 +12,8 @@ use crate::{ AssistConfig, AssistId, assist_context::{AssistContext, Assists}, utils::{ - DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl_with_factory, - filter_assoc_items, gen_trait_fn_body, generate_trait_impl, + DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl, filter_assoc_items, + gen_trait_fn_body, generate_trait_impl, generate_trait_impl_with_item, }, }; @@ -64,9 +64,10 @@ pub(crate) fn replace_derive_with_manual_impl( .filter_map(|attr| attr.path()) .collect::<Vec<_>>(); - let adt = value.parent().and_then(ast::Adt::cast)?; - let attr = ast::Attr::cast(value)?; - let args = attr.token_tree()?; + let attr = ast::Meta::cast(value)?.parent_attr()?; + let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; + let ast::Meta::TokenTreeMeta(meta) = attr.meta()? else { return None }; + let args = meta.token_tree()?; let current_module = ctx.sema.scope(adt.syntax())?.module(); let current_crate = current_module.krate(ctx.db()); @@ -127,7 +128,7 @@ fn add_assist( let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`"); acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| { - let make = SyntaxFactory::without_mappings(); + let make = SyntaxFactory::with_mappings(); let insert_after = Position::after(adt.syntax()); let impl_is_unsafe = trait_.map(|s| s.is_unsafe(ctx.db())).unwrap_or(false); let impl_def = impl_def_from_trait( @@ -141,7 +142,7 @@ fn add_assist( ); let mut editor = builder.make_editor(attr.syntax()); - update_attribute(&mut editor, old_derives, old_tree, old_trait_path, attr); + update_attribute(&make, &mut editor, old_derives, old_tree, old_trait_path, attr); let trait_path = make.ty_path(replace_trait_path.clone()).into(); @@ -177,6 +178,7 @@ fn add_assist( insert_after, vec![make.whitespace("\n\n").into(), impl_def.syntax().clone().into()], ); + editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }) } @@ -207,10 +209,10 @@ fn impl_def_from_trait( return None; } let make = SyntaxFactory::without_mappings(); - let trait_ty = make.ty_path(trait_path.clone()).into(); - let impl_def = generate_trait_impl(&make, impl_is_unsafe, adt, trait_ty); + let trait_ty: ast::Type = make.ty_path(trait_path.clone()).into(); + let impl_def = generate_trait_impl(&make, impl_is_unsafe, adt, trait_ty.clone()); - let assoc_items = add_trait_assoc_items_to_impl_with_factory( + let assoc_items = add_trait_assoc_items_to_impl( &make, sema, config, @@ -219,14 +221,12 @@ fn impl_def_from_trait( &impl_def, &target_scope, ); - let assoc_item_list = if let Some((first, other)) = - assoc_items.split_first().map(|(first, other)| (first.clone_subtree(), other)) - { - let first_item = if let ast::AssocItem::Fn(ref func) = first - && let Some(body) = gen_trait_fn_body(func, trait_path, adt, None) + let assoc_item_list = if let Some((first, other)) = assoc_items.split_first() { + let first_item = if let ast::AssocItem::Fn(func) = first + && let Some(body) = gen_trait_fn_body(&make, func, trait_path, adt, None) && let Some(func_body) = func.body() { - let mut editor = SyntaxEditor::new(first.syntax().clone()); + let (mut editor, _) = SyntaxEditor::new(first.syntax().clone()); editor.replace(func_body.syntax(), body.syntax()); ast::AssocItem::cast(editor.finish().new_root().clone()) } else { @@ -239,21 +239,17 @@ fn impl_def_from_trait( make.assoc_item_list_empty() }; - let impl_def = impl_def.clone_subtree(); - let mut editor = SyntaxEditor::new(impl_def.syntax().clone()); - editor.replace(impl_def.assoc_item_list()?.syntax(), assoc_item_list.syntax()); - let impl_def = ast::Impl::cast(editor.finish().new_root().clone())?; - Some(impl_def) + Some(generate_trait_impl_with_item(&make, impl_is_unsafe, adt, trait_ty, assoc_item_list)) } fn update_attribute( + make: &SyntaxFactory, editor: &mut SyntaxEditor, old_derives: &[ast::Path], old_tree: &ast::TokenTree, old_trait_path: &ast::Path, attr: &ast::Attr, ) { - let make = SyntaxFactory::without_mappings(); let new_derives = old_derives .iter() .filter(|t| t.to_string() != old_trait_path.to_string()) diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 8ff30fce5b..ada2fd9b21 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -13,7 +13,10 @@ use syntax::{ use crate::{ AssistContext, AssistId, Assists, - utils::{does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block}, + utils::{ + does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block, + wrap_paren, + }, }; // Assist: replace_if_let_with_match @@ -289,6 +292,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' _ => make.expr_let(if_let_pat, scrutinee).into(), }; let condition = if let Some(guard) = guard { + let guard = wrap_paren(guard, &make, ast::prec::ExprPrecedence::LAnd); make.expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard).into() } else { condition @@ -398,8 +402,7 @@ fn let_and_guard(cond: &ast::Expr) -> (Option<ast::LetExpr>, Option<ast::Expr>) } else if let ast::Expr::BinExpr(bin_expr) = cond && let Some(ast::Expr::LetExpr(let_expr)) = and_bin_expr_left(bin_expr).lhs() { - let new_expr = bin_expr.clone_subtree(); - let mut edit = SyntaxEditor::new(new_expr.syntax().clone()); + let (mut edit, new_expr) = SyntaxEditor::with_ast_node(bin_expr); let left_bin = and_bin_expr_left(&new_expr); if let Some(rhs) = left_bin.rhs() { @@ -2268,14 +2271,35 @@ fn main() { "#, r#" fn main() { - if let Some(n) = Some(0) && n % 2 == 0 && n != 6 { + if let Some(n) = Some(0) && (n % 2 == 0 && n != 6) { () } else { code() } } "#, - ) + ); + + check_assist( + replace_match_with_if_let, + r#" +fn main() { + match$0 Some(0) { + Some(n) if n % 2 == 0 || n == 7 => (), + _ => code(), + } +} +"#, + r#" +fn main() { + if let Some(n) = Some(0) && (n % 2 == 0 || n == 7) { + () + } else { + code() + } +} +"#, + ); } #[test] diff --git a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index cdf20586ef..fd090cc081 100644 --- a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -111,8 +111,7 @@ fn target_path(ctx: &AssistContext<'_>, mut original_path: ast::Path) -> Option< } fn drop_generic_args(path: &ast::Path) -> ast::Path { - let path = path.clone_subtree(); - let mut editor = SyntaxEditor::new(path.syntax().clone()); + let (mut editor, path) = SyntaxEditor::with_ast_node(path); if let Some(segment) = path.segment() && let Some(generic_args) = segment.generic_arg_list() { diff --git a/crates/ide-assists/src/handlers/unwrap_block.rs b/crates/ide-assists/src/handlers/unwrap_block.rs index e029d7884f..5593ca3eb8 100644 --- a/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/crates/ide-assists/src/handlers/unwrap_block.rs @@ -103,8 +103,7 @@ fn delete_else_before(container: SyntaxNode, edit: &mut SyntaxEditor) { fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExpr { let try_wrap_assign = || { let initializer = assign.initializer()?.syntax().syntax_element(); - let replacement = replacement.clone_subtree(); - let assign = assign.clone_for_update(); + let (mut edit, replacement) = SyntaxEditor::with_ast_node(&replacement); let tail_expr = replacement.tail_expr()?; let before = assign.syntax().children_with_tokens().take_while(|it| *it != initializer).collect(); @@ -115,7 +114,6 @@ fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExp .skip(1) .collect(); - let mut edit = SyntaxEditor::new(replacement.syntax().clone()); edit.insert_all(Position::before(tail_expr.syntax()), before); edit.insert_all(Position::after(tail_expr.syntax()), after); ast::BlockExpr::cast(edit.finish().new_root().clone()) diff --git a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 36df4af31d..3b8988db7a 100644 --- a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -19,7 +19,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // -> // ``` -// #[cfg_attr($0, derive(Debug))] +// #[cfg_attr(${0:cfg}, derive(Debug))] // struct S { // field: i32 // } @@ -147,12 +147,15 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) - } }?; match option { - WrapUnwrapOption::WrapAttr(attrs) => match &attrs[..] { - [attr] if attr.simple_name().as_deref() == Some("cfg_attr") => { - unwrap_cfg_attr(acc, attrs.into_iter().next().unwrap()) + WrapUnwrapOption::WrapAttr(attrs) => { + if let [attr] = &attrs[..] + && let Some(ast::Meta::CfgAttrMeta(meta)) = attr.meta() + { + unwrap_cfg_attr(acc, meta) + } else { + wrap_cfg_attrs(acc, ctx, attrs) } - _ => wrap_cfg_attrs(acc, ctx, attrs), - }, + } WrapUnwrapOption::WrapDerive { derive, attr } => wrap_derive(acc, ctx, attr, derive), } } @@ -164,7 +167,8 @@ fn wrap_derive( derive_element: TextRange, ) -> Option<()> { let range = attr.syntax().text_range(); - let token_tree = attr.token_tree()?; + let ast::Meta::TokenTreeMeta(meta) = attr.meta()? else { return None }; + let token_tree = meta.token_tree()?; let mut path_text = String::new(); let mut cfg_derive_tokens = Vec::new(); @@ -193,20 +197,15 @@ fn wrap_derive( let new_derive = make.attr_outer( make.meta_token_tree(make.ident_path("derive"), make.token_tree(T!['('], new_derive)), ); - let meta = make.meta_token_tree( - make.ident_path("cfg_attr"), - make.token_tree( - T!['('], - vec![ - NodeOrToken::Token(make.token(T![,])), - NodeOrToken::Token(make.whitespace(" ")), - NodeOrToken::Token(make.ident("derive")), - NodeOrToken::Node(make.token_tree(T!['('], cfg_derive_tokens)), - ], - ), + let meta = make.cfg_attr_meta( + make.cfg_flag("cfg"), + [make.meta_token_tree( + make.ident_path("derive"), + make.token_tree(T!['('], cfg_derive_tokens), + )], ); - let cfg_attr = make.attr_outer(meta); + let cfg_attr = make.attr_outer(meta.clone().into()); editor.replace_with_many( attr.syntax(), vec![ @@ -217,11 +216,10 @@ fn wrap_derive( ); if let Some(snippet_cap) = ctx.config.snippet_cap - && let Some(first_meta) = - cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) + && let Some(cfg_predicate) = meta.cfg_predicate() { - let tabstop = edit.make_tabstop_after(snippet_cap); - editor.add_annotation(first_meta, tabstop); + let tabstop = edit.make_placeholder_snippet(snippet_cap); + editor.add_annotation(cfg_predicate.syntax(), tabstop); } editor.add_mappings(make.finish_with_mappings()); @@ -236,58 +234,29 @@ fn wrap_derive( ); Some(()) } + fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec<ast::Attr>) -> Option<()> { let (first_attr, last_attr) = (attrs.first()?, attrs.last()?); let range = first_attr.syntax().text_range().cover(last_attr.syntax().text_range()); - let path_attrs = - attrs.iter().map(|attr| Some((attr.path()?, attr.clone()))).collect::<Option<Vec<_>>>()?; let handle_source_change = |edit: &mut SourceChangeBuilder| { let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(first_attr.syntax()); - let mut raw_tokens = vec![]; - for (path, attr) in path_attrs { - raw_tokens.extend([ - NodeOrToken::Token(make.token(T![,])), - NodeOrToken::Token(make.whitespace(" ")), - ]); - path.syntax().descendants_with_tokens().for_each(|it| { - if let NodeOrToken::Token(token) = it { - raw_tokens.push(NodeOrToken::Token(token)); - } - }); - if let Some(meta) = attr.meta() { - if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) { - raw_tokens.push(NodeOrToken::Token(make.whitespace(" "))); - raw_tokens.push(NodeOrToken::Token(eq)); - raw_tokens.push(NodeOrToken::Token(make.whitespace(" "))); - - expr.syntax().descendants_with_tokens().for_each(|it| { - if let NodeOrToken::Token(token) = it { - raw_tokens.push(NodeOrToken::Token(token)); - } - }); - } else if let Some(tt) = meta.token_tree() { - raw_tokens.extend(tt.token_trees_and_tokens()); - } - } - } let meta = - make.meta_token_tree(make.ident_path("cfg_attr"), make.token_tree(T!['('], raw_tokens)); + make.cfg_attr_meta(make.cfg_flag("cfg"), attrs.iter().filter_map(|attr| attr.meta())); let cfg_attr = if first_attr.excl_token().is_some() { - make.attr_inner(meta) + make.attr_inner(meta.clone().into()) } else { - make.attr_outer(meta) + make.attr_outer(meta.clone().into()) }; let syntax_range = first_attr.syntax().clone().into()..=last_attr.syntax().clone().into(); editor.replace_all(syntax_range, vec![cfg_attr.syntax().clone().into()]); if let Some(snippet_cap) = ctx.config.snippet_cap - && let Some(first_meta) = - cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) + && let Some(cfg_flag) = meta.cfg_predicate() { - let tabstop = edit.make_tabstop_after(snippet_cap); - editor.add_annotation(first_meta, tabstop); + let tabstop = edit.make_placeholder_snippet(snippet_cap); + editor.add_annotation(cfg_flag.syntax(), tabstop); } editor.add_mappings(make.finish_with_mappings()); @@ -301,66 +270,28 @@ fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec<ast::At ); Some(()) } -fn unwrap_cfg_attr(acc: &mut Assists, attr: ast::Attr) -> Option<()> { - let range = attr.syntax().text_range(); - let meta = attr.meta()?; - let meta_tt = meta.token_tree()?; - let mut inner_attrs = Vec::with_capacity(1); - let mut found_comma = false; - let mut iter = meta_tt.token_trees_and_tokens().skip(1).peekable(); - while let Some(tt) = iter.next() { - if let NodeOrToken::Token(token) = &tt { - if token.kind() == T![')'] { - break; - } - if token.kind() == T![,] { - found_comma = true; - continue; - } - } - if !found_comma { - continue; - } - let Some(attr_name) = tt.into_token().and_then(|token| { - if token.kind() == T![ident] { Some(make::ext::ident_path(token.text())) } else { None } - }) else { - continue; - }; - let next_tt = iter.next()?; - let meta = match next_tt { - NodeOrToken::Node(tt) => make::meta_token_tree(attr_name, tt), - NodeOrToken::Token(token) if token.kind() == T![,] || token.kind() == T![')'] => { - make::meta_path(attr_name) - } - NodeOrToken::Token(token) => { - let equals = algo::skip_trivia_token(token, syntax::Direction::Next)?; - if equals.kind() != T![=] { - return None; - } - let expr_token = - algo::skip_trivia_token(equals.next_token()?, syntax::Direction::Next) - .and_then(|it| { - if it.kind().is_literal() { - Some(make::expr_literal(it.text())) - } else { - None - } - })?; - make::meta_expr(attr_name, ast::Expr::Literal(expr_token)) + +fn unwrap_cfg_attr(acc: &mut Assists, meta: ast::CfgAttrMeta) -> Option<()> { + let top_attr = ast::Meta::from(meta.clone()).parent_attr()?; + let range = top_attr.syntax().text_range(); + let inner_attrs = meta + .metas() + .map(|meta| { + if top_attr.excl_token().is_some() { + make::attr_inner(meta) + } else { + make::attr_outer(meta) } - }; - if attr.excl_token().is_some() { - inner_attrs.push(make::attr_inner(meta)); - } else { - inner_attrs.push(make::attr_outer(meta)); - } - } + }) + .collect::<Vec<_>>(); if inner_attrs.is_empty() { return None; } let handle_source_change = |f: &mut SourceChangeBuilder| { - let inner_attrs = - inner_attrs.iter().map(|it| it.to_string()).join(&format!("\n{}", attr.indent_level())); + let inner_attrs = inner_attrs + .iter() + .map(|it| it.to_string()) + .join(&format!("\n{}", top_attr.indent_level())); f.replace(range, inner_attrs); }; acc.add( @@ -388,7 +319,7 @@ mod tests { } "#, r#" - #[cfg_attr($0, derive(Debug))] + #[cfg_attr(${0:cfg}, derive(Debug))] pub struct Test { test: u32, } @@ -422,7 +353,7 @@ mod tests { "#, r#" pub struct Test { - #[cfg_attr($0, foo)] + #[cfg_attr(${0:cfg}, foo)] test: u32, } "#, @@ -456,7 +387,7 @@ mod tests { r#" pub struct Test { #[other_attr] - #[cfg_attr($0, foo, bar)] + #[cfg_attr(${0:cfg}, foo, bar)] #[other_attr] test: u32, } @@ -491,7 +422,7 @@ mod tests { "#, r#" pub struct Test { - #[cfg_attr($0, foo = "bar")] + #[cfg_attr(${0:cfg}, foo = "bar")] test: u32, } "#, @@ -520,7 +451,7 @@ mod tests { #![no_std$0] "#, r#" - #![cfg_attr($0, no_std)] + #![cfg_attr(${0:cfg}, no_std)] "#, ); check_assist( @@ -545,7 +476,7 @@ mod tests { "#, r#" #[derive( Clone, Copy)] - #[cfg_attr($0, derive(Debug))] + #[cfg_attr(${0:cfg}, derive(Debug))] pub struct Test { test: u32, } @@ -561,7 +492,7 @@ mod tests { "#, r#" #[derive(Clone, Copy)] - #[cfg_attr($0, derive(Debug))] + #[cfg_attr(${0:cfg}, derive(Debug))] pub struct Test { test: u32, } @@ -580,7 +511,7 @@ mod tests { "#, r#" #[derive( Clone, Copy)] - #[cfg_attr($0, derive(std::fmt::Debug))] + #[cfg_attr(${0:cfg}, derive(std::fmt::Debug))] pub struct Test { test: u32, } @@ -596,7 +527,7 @@ mod tests { "#, r#" #[derive(Clone, Copy)] - #[cfg_attr($0, derive(std::fmt::Debug))] + #[cfg_attr(${0:cfg}, derive(std::fmt::Debug))] pub struct Test { test: u32, } @@ -615,7 +546,7 @@ mod tests { "#, r#" #[derive(std::fmt::Debug, Clone)] - #[cfg_attr($0, derive(Copy))] + #[cfg_attr(${0:cfg}, derive(Copy))] pub struct Test { test: u32, } @@ -631,7 +562,7 @@ mod tests { "#, r#" #[derive(Clone, Copy)] - #[cfg_attr($0, derive(std::fmt::Debug))] + #[cfg_attr(${0:cfg}, derive(std::fmt::Debug))] pub struct Test { test: u32, } diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index a52bd74d14..135e750ca0 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -208,6 +208,15 @@ pub(crate) fn check_assist_target( } #[track_caller] +pub(crate) fn check_assist_with_label( + assist: Handler, + #[rust_analyzer::rust_fixture] ra_fixture: &str, + label: &str, +) { + check(assist, ra_fixture, ExpectedResult::Label(label), None); +} + +#[track_caller] pub(crate) fn check_assist_not_applicable( assist: Handler, #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -307,6 +316,7 @@ enum ExpectedResult<'a> { Unresolved, After(&'a str), Target(&'a str), + Label(&'a str), } #[track_caller] @@ -335,7 +345,7 @@ fn check_with_config( let ctx = AssistContext::new(sema, &config, frange); let resolve = match expected { - ExpectedResult::Unresolved => AssistResolveStrategy::None, + ExpectedResult::Unresolved | ExpectedResult::Label(_) => AssistResolveStrategy::None, _ => AssistResolveStrategy::All, }; let mut acc = Assists::new(&ctx, resolve); @@ -404,6 +414,9 @@ fn check_with_config( let range = assist.target; assert_eq_text!(&text_without_caret[range], target); } + (Some(assist), ExpectedResult::Label(label)) => { + assert_eq!(assist.label.to_string(), label); + } (Some(assist), ExpectedResult::Unresolved) => assert!( assist.source_change.is_none(), "unresolved assist should not contain source changes" @@ -411,7 +424,10 @@ fn check_with_config( (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), ( None, - ExpectedResult::After(_) | ExpectedResult::Target(_) | ExpectedResult::Unresolved, + ExpectedResult::After(_) + | ExpectedResult::Target(_) + | ExpectedResult::Label(_) + | ExpectedResult::Unresolved, ) => { panic!("code action is not applicable") } @@ -479,6 +495,7 @@ pub fn test_some_range(a: int) -> bool { expect![[r#" Extract into... Replace if let with match + Convert to guarded return "#]] .assert_eq(&expected); } @@ -511,6 +528,7 @@ pub fn test_some_range(a: int) -> bool { expect![[r#" Extract into... Replace if let with match + Convert to guarded return "#]] .assert_eq(&expected); } diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 66d5cf834f..a499607c1f 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -3852,7 +3852,7 @@ struct S { } "#####, r#####" -#[cfg_attr($0, derive(Debug))] +#[cfg_attr(${0:cfg}, derive(Debug))] struct S { field: i32 } diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 10057f8681..896743342c 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -203,11 +203,9 @@ pub fn filter_assoc_items( /// [`filter_assoc_items()`]), clones each item for update and applies path transformation to it, /// then inserts into `impl_`. Returns the modified `impl_` and the first associated item that got /// inserted. -/// -/// Legacy: prefer [`add_trait_assoc_items_to_impl_with_factory`] when a [`SyntaxFactory`] is -/// available. #[must_use] pub fn add_trait_assoc_items_to_impl( + make: &SyntaxFactory, sema: &Semantics<'_, RootDatabase>, config: &AssistConfig, original_items: &[InFile<ast::AssocItem>], @@ -250,95 +248,23 @@ pub fn add_trait_assoc_items_to_impl( }) .filter_map(|item| match item { ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { - let fn_ = fn_.clone_subtree(); - let new_body = make::block_expr(None, Some(expr_fill_default(config))); - let mut fn_editor = SyntaxEditor::new(fn_.syntax().clone()); - fn_.replace_or_insert_body(&mut fn_editor, new_body.clone_for_update()); - let new_fn_ = fn_editor.finish().new_root().clone(); - ast::AssocItem::cast(new_fn_) - } - ast::AssocItem::TypeAlias(type_alias) => { - let type_alias = type_alias.clone_subtree(); - if let Some(type_bound_list) = type_alias.type_bound_list() { - let mut type_alias_editor = SyntaxEditor::new(type_alias.syntax().clone()); - type_bound_list.remove(&mut type_alias_editor); - let type_alias = type_alias_editor.finish().new_root().clone(); - ast::AssocItem::cast(type_alias) - } else { - Some(ast::AssocItem::TypeAlias(type_alias)) - } - } - item => Some(item), - }) - .map(|item| AstNodeEdit::indent(&item, new_indent_level)) - .collect() -} - -/// [`SyntaxFactory`]-based variant of [`add_trait_assoc_items_to_impl`]. -#[must_use] -pub fn add_trait_assoc_items_to_impl_with_factory( - make: &SyntaxFactory, - sema: &Semantics<'_, RootDatabase>, - config: &AssistConfig, - original_items: &[InFile<ast::AssocItem>], - trait_: hir::Trait, - impl_: &ast::Impl, - target_scope: &hir::SemanticsScope<'_>, -) -> Vec<ast::AssocItem> { - let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1; - original_items - .iter() - .map(|InFile { file_id, value: original_item }| { - let mut cloned_item = { - if let Some(macro_file) = file_id.macro_file() { - let span_map = sema.db.expansion_span_map(macro_file); - let item_prettified = prettify_macro_expansion( - sema.db, - original_item.syntax().clone(), - &span_map, - target_scope.krate().into(), - ); - if let Some(formatted) = ast::AssocItem::cast(item_prettified) { - return formatted; - } else { - stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`"); - } - } - original_item - } - .reset_indent(); - - if let Some(source_scope) = sema.scope(original_item.syntax()) { - let transform = - PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone()); - cloned_item = ast::AssocItem::cast(transform.apply(cloned_item.syntax())).unwrap(); - } - cloned_item.remove_attrs_and_docs(); - cloned_item - }) - .filter_map(|item| match item { - ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { - let fn_ = fn_.clone_subtree(); + let (mut fn_editor, fn_) = SyntaxEditor::with_ast_node(&fn_); let fill_expr: ast::Expr = match config.expr_fill_default { ExprFillDefaultMode::Todo | ExprFillDefaultMode::Default => make.expr_todo(), ExprFillDefaultMode::Underscore => make.expr_underscore().into(), }; let new_body = make.block_expr(None::<ast::Stmt>, Some(fill_expr)); - let mut fn_editor = SyntaxEditor::new(fn_.syntax().clone()); fn_.replace_or_insert_body(&mut fn_editor, new_body); let new_fn_ = fn_editor.finish().new_root().clone(); ast::AssocItem::cast(new_fn_) } ast::AssocItem::TypeAlias(type_alias) => { - let type_alias = type_alias.clone_subtree(); + let (mut type_alias_editor, type_alias) = SyntaxEditor::with_ast_node(&type_alias); if let Some(type_bound_list) = type_alias.type_bound_list() { - let mut type_alias_editor = SyntaxEditor::new(type_alias.syntax().clone()); type_bound_list.remove(&mut type_alias_editor); - let type_alias = type_alias_editor.finish().new_root().clone(); - ast::AssocItem::cast(type_alias) - } else { - Some(ast::AssocItem::TypeAlias(type_alias)) - } + }; + let type_alias = type_alias_editor.finish().new_root().clone(); + ast::AssocItem::cast(type_alias) } item => Some(item), }) @@ -404,10 +330,8 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option<ast::Ex Some(make.expr_method_call(receiver, make.name_ref(method), arg_list).into()) } ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? { - ast::Expr::ParenExpr(parexpr) => { - parexpr.expr().map(|e| e.clone_subtree().clone_for_update()) - } - _ => pe.expr().map(|e| e.clone_subtree().clone_for_update()), + ast::Expr::ParenExpr(parexpr) => parexpr.expr(), + _ => pe.expr(), }, ast::Expr::Literal(lit) => match lit.kind() { ast::LiteralKind::Bool(b) => match b { @@ -674,9 +598,7 @@ fn generate_impl_text_inner( // Copy any cfg attrs from the original adt buf.push_str("\n\n"); - let cfg_attrs = adt - .attrs() - .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false)); + let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n"))); // `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}` @@ -758,6 +680,16 @@ pub(crate) fn generate_trait_impl_intransitive_with_item( generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, Some(body)) } +pub(crate) fn generate_trait_impl_with_item( + make: &SyntaxFactory, + is_unsafe: bool, + adt: &ast::Adt, + trait_: ast::Type, + body: ast::AssocItemList, +) -> ast::Impl { + generate_impl_inner_with_factory(make, is_unsafe, adt, Some(trait_), true, Some(body)) +} + fn generate_impl_inner( is_unsafe: bool, adt: &ast::Adt, @@ -806,8 +738,7 @@ fn generate_impl_inner( let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text())); - let cfg_attrs = - adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); + let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); match trait_ { Some(trait_) => make::impl_trait( cfg_attrs, @@ -877,8 +808,7 @@ fn generate_impl_inner_with_factory( let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into(); - let cfg_attrs = - adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); + let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); match trait_ { Some(trait_) => make.impl_trait( cfg_attrs, diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 87e90e8519..b0d88737fe 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -1,7 +1,10 @@ //! This module contains functions to generate default trait impl function bodies where possible. use hir::TraitRef; -use syntax::ast::{self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNodeEdit, make}; +use syntax::ast::{ + self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNodeEdit, + syntax_factory::SyntaxFactory, +}; /// Generate custom trait bodies without default implementation where possible. /// @@ -11,6 +14,7 @@ use syntax::ast::{self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNod /// `None` means that generating a custom trait body failed, and the body will remain /// as `todo!` instead. pub(crate) fn gen_trait_fn_body( + make: &SyntaxFactory, func: &ast::Fn, trait_path: &ast::Path, adt: &ast::Adt, @@ -20,32 +24,32 @@ pub(crate) fn gen_trait_fn_body( match trait_path.segment()?.name_ref()?.text().as_str() { "Clone" => { stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); - gen_clone_impl(adt) + gen_clone_impl(make, adt) } - "Debug" => gen_debug_impl(adt), - "Default" => gen_default_impl(adt), + "Debug" => gen_debug_impl(make, adt), + "Default" => gen_default_impl(make, adt), "Hash" => { stdx::always!(func.name().is_some_and(|name| name.text() == "hash")); - gen_hash_impl(adt) + gen_hash_impl(make, adt) } "PartialEq" => { stdx::always!(func.name().is_some_and(|name| name.text() == "eq")); - gen_partial_eq(adt, trait_ref) + gen_partial_eq(make, adt, trait_ref) } "PartialOrd" => { stdx::always!(func.name().is_some_and(|name| name.text() == "partial_cmp")); - gen_partial_ord(adt, trait_ref) + gen_partial_ord(make, adt, trait_ref) } _ => None, } } /// Generate a `Clone` impl based on the fields and members of the target type. -fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { - fn gen_clone_call(target: ast::Expr) -> ast::Expr { - let method = make::name_ref("clone"); - make::expr_method_call(target, method, make::arg_list(None)).into() - } +fn gen_clone_impl(make: &SyntaxFactory, adt: &ast::Adt) -> Option<ast::BlockExpr> { + let gen_clone_call = |target: ast::Expr| -> ast::Expr { + let method = make.name_ref("clone"); + make.expr_method_call(target, method, make.arg_list([])).into() + }; let expr = match adt { // `Clone` cannot be derived for unions, so no default impl can be provided. ast::Adt::Union(_) => return None, @@ -54,7 +58,7 @@ fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { let mut arms = vec![]; for variant in list.variants() { let name = variant.name()?; - let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?; + let variant_name = make.path_from_idents(["Self", &format!("{name}")])?; match variant.field_list() { // => match self { Self::Name { x } => Self::Name { x: x.clone() } } @@ -63,19 +67,20 @@ fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { let mut fields = vec![]; for field in list.fields() { let field_name = field.name()?; - let pat = make::ident_pat(false, false, field_name.clone()); - pats.push(pat.into()); + let pat = make.ident_pat(false, false, field_name.clone()); + pats.push(make.record_pat_field_shorthand(pat.into())); - let path = make::ext::ident_path(&field_name.to_string()); - let method_call = gen_clone_call(make::expr_path(path)); - let name_ref = make::name_ref(&field_name.to_string()); - let field = make::record_expr_field(name_ref, Some(method_call)); + let path = make.ident_path(&field_name.to_string()); + let method_call = gen_clone_call(make.expr_path(path)); + let name_ref = make.name_ref(&field_name.to_string()); + let field = make.record_expr_field(name_ref, Some(method_call)); fields.push(field); } - let pat = make::record_pat(variant_name.clone(), pats.into_iter()); - let fields = make::record_expr_field_list(fields); - let record_expr = make::record_expr(variant_name, fields).into(); - arms.push(make::match_arm(pat.into(), None, record_expr)); + let pat_field_list = make.record_pat_field_list(pats, None); + let pat = make.record_pat_with_fields(variant_name.clone(), pat_field_list); + let fields = make.record_expr_field_list(fields); + let record_expr = make.record_expr(variant_name, fields).into(); + arms.push(make.match_arm(pat.into(), None, record_expr)); } // => match self { Self::Name(arg1) => Self::Name(arg1.clone()) } @@ -84,31 +89,30 @@ fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { let mut fields = vec![]; for (i, _) in list.fields().enumerate() { let field_name = format!("arg{i}"); - let pat = make::ident_pat(false, false, make::name(&field_name)); + let pat = make.ident_pat(false, false, make.name(&field_name)); pats.push(pat.into()); - let f_path = make::expr_path(make::ext::ident_path(&field_name)); + let f_path = make.expr_path(make.ident_path(&field_name)); fields.push(gen_clone_call(f_path)); } - let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter()); - let struct_name = make::expr_path(variant_name); - let tuple_expr = - make::expr_call(struct_name, make::arg_list(fields)).into(); - arms.push(make::match_arm(pat.into(), None, tuple_expr)); + let pat = make.tuple_struct_pat(variant_name.clone(), pats.into_iter()); + let struct_name = make.expr_path(variant_name); + let tuple_expr = make.expr_call(struct_name, make.arg_list(fields)).into(); + arms.push(make.match_arm(pat.into(), None, tuple_expr)); } // => match self { Self::Name => Self::Name } None => { - let pattern = make::path_pat(variant_name.clone()); - let variant_expr = make::expr_path(variant_name); - arms.push(make::match_arm(pattern, None, variant_expr)); + let pattern = make.path_pat(variant_name.clone()); + let variant_expr = make.expr_path(variant_name); + arms.push(make.match_arm(pattern, None, variant_expr)); } } } - let match_target = make::expr_path(make::ext::ident_path("self")); - let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - make::expr_match(match_target, list).into() + let match_target = make.expr_path(make.ident_path("self")); + let list = make.match_arm_list(arms).indent(ast::edit::IndentLevel(1)); + make.expr_match(match_target, list).into() } ast::Adt::Struct(strukt) => { match strukt.field_list() { @@ -116,43 +120,43 @@ fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { Some(ast::FieldList::RecordFieldList(field_list)) => { let mut fields = vec![]; for field in field_list.fields() { - let base = make::expr_path(make::ext::ident_path("self")); - let target = make::expr_field(base, &field.name()?.to_string()); + let base = make.expr_path(make.ident_path("self")); + let target = make.expr_field(base, &field.name()?.to_string()).into(); let method_call = gen_clone_call(target); - let name_ref = make::name_ref(&field.name()?.to_string()); - let field = make::record_expr_field(name_ref, Some(method_call)); + let name_ref = make.name_ref(&field.name()?.to_string()); + let field = make.record_expr_field(name_ref, Some(method_call)); fields.push(field); } - let struct_name = make::ext::ident_path("Self"); - let fields = make::record_expr_field_list(fields); - make::record_expr(struct_name, fields).into() + let struct_name = make.ident_path("Self"); + let fields = make.record_expr_field_list(fields); + make.record_expr(struct_name, fields).into() } // => Self(self.0.clone(), self.1.clone()) Some(ast::FieldList::TupleFieldList(field_list)) => { let mut fields = vec![]; for (i, _) in field_list.fields().enumerate() { - let f_path = make::expr_path(make::ext::ident_path("self")); - let target = make::expr_field(f_path, &format!("{i}")); + let f_path = make.expr_path(make.ident_path("self")); + let target = make.expr_field(f_path, &format!("{i}")).into(); fields.push(gen_clone_call(target)); } - let struct_name = make::expr_path(make::ext::ident_path("Self")); - make::expr_call(struct_name, make::arg_list(fields)).into() + let struct_name = make.expr_path(make.ident_path("Self")); + make.expr_call(struct_name, make.arg_list(fields)).into() } // => Self { } None => { - let struct_name = make::ext::ident_path("Self"); - let fields = make::record_expr_field_list(None); - make::record_expr(struct_name, fields).into() + let struct_name = make.ident_path("Self"); + let fields = make.record_expr_field_list([]); + make.record_expr(struct_name, fields).into() } } } }; - let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); + let body = make.block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); Some(body) } /// Generate a `Debug` impl based on the fields and members of the target type. -fn gen_debug_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { +fn gen_debug_impl(make: &SyntaxFactory, adt: &ast::Adt) -> Option<ast::BlockExpr> { let annotated_name = adt.name()?; match adt { // `Debug` cannot be derived for unions, so no default impl can be provided. @@ -164,156 +168,154 @@ fn gen_debug_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { let mut arms = vec![]; for variant in list.variants() { let name = variant.name()?; - let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?; - let target = make::expr_path(make::ext::ident_path("f")); + let variant_name = make.path_from_idents(["Self", &format!("{name}")])?; + let target = make.expr_path(make.ident_path("f")); match variant.field_list() { Some(ast::FieldList::RecordFieldList(list)) => { // => f.debug_struct(name) - let target = make::expr_path(make::ext::ident_path("f")); - let method = make::name_ref("debug_struct"); + let target = make.expr_path(make.ident_path("f")); + let method = make.name_ref("debug_struct"); let struct_name = format!("\"{name}\""); - let args = make::arg_list(Some(make::expr_literal(&struct_name).into())); - let mut expr = make::expr_method_call(target, method, args).into(); + let args = make.arg_list([make.expr_literal(&struct_name).into()]); + let mut expr = make.expr_method_call(target, method, args).into(); let mut pats = vec![]; for field in list.fields() { let field_name = field.name()?; // create a field pattern for use in `MyStruct { fields.. }` - let pat = make::ident_pat(false, false, field_name.clone()); - pats.push(pat.into()); + let pat = make.ident_pat(false, false, field_name.clone()); + pats.push(make.record_pat_field_shorthand(pat.into())); // => <expr>.field("field_name", field) - let method_name = make::name_ref("field"); - let name = make::expr_literal(&(format!("\"{field_name}\""))).into(); + let method_name = make.name_ref("field"); + let name = make.expr_literal(&(format!("\"{field_name}\""))).into(); let path = &format!("{field_name}"); - let path = make::expr_path(make::ext::ident_path(path)); - let args = make::arg_list(vec![name, path]); - expr = make::expr_method_call(expr, method_name, args).into(); + let path = make.expr_path(make.ident_path(path)); + let args = make.arg_list([name, path]); + expr = make.expr_method_call(expr, method_name, args).into(); } // => <expr>.finish() - let method = make::name_ref("finish"); - let expr = - make::expr_method_call(expr, method, make::arg_list(None)).into(); + let method = make.name_ref("finish"); + let expr = make.expr_method_call(expr, method, make.arg_list([])).into(); // => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(), - let pat = make::record_pat(variant_name.clone(), pats.into_iter()); - arms.push(make::match_arm(pat.into(), None, expr)); + let pat_field_list = make.record_pat_field_list(pats, None); + let pat = make.record_pat_with_fields(variant_name.clone(), pat_field_list); + arms.push(make.match_arm(pat.into(), None, expr)); } Some(ast::FieldList::TupleFieldList(list)) => { // => f.debug_tuple(name) - let target = make::expr_path(make::ext::ident_path("f")); - let method = make::name_ref("debug_tuple"); + let target = make.expr_path(make.ident_path("f")); + let method = make.name_ref("debug_tuple"); let struct_name = format!("\"{name}\""); - let args = make::arg_list(Some(make::expr_literal(&struct_name).into())); - let mut expr = make::expr_method_call(target, method, args).into(); + let args = make.arg_list([make.expr_literal(&struct_name).into()]); + let mut expr = make.expr_method_call(target, method, args).into(); let mut pats = vec![]; for (i, _) in list.fields().enumerate() { let name = format!("arg{i}"); // create a field pattern for use in `MyStruct(fields..)` - let field_name = make::name(&name); - let pat = make::ident_pat(false, false, field_name.clone()); + let field_name = make.name(&name); + let pat = make.ident_pat(false, false, field_name.clone()); pats.push(pat.into()); // => <expr>.field(field) - let method_name = make::name_ref("field"); + let method_name = make.name_ref("field"); let field_path = &name.to_string(); - let field_path = make::expr_path(make::ext::ident_path(field_path)); - let args = make::arg_list(vec![field_path]); - expr = make::expr_method_call(expr, method_name, args).into(); + let field_path = make.expr_path(make.ident_path(field_path)); + let args = make.arg_list([field_path]); + expr = make.expr_method_call(expr, method_name, args).into(); } // => <expr>.finish() - let method = make::name_ref("finish"); - let expr = - make::expr_method_call(expr, method, make::arg_list(None)).into(); + let method = make.name_ref("finish"); + let expr = make.expr_method_call(expr, method, make.arg_list([])).into(); // => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(), - let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter()); - arms.push(make::match_arm(pat.into(), None, expr)); + let pat = make.tuple_struct_pat(variant_name.clone(), pats.into_iter()); + arms.push(make.match_arm(pat.into(), None, expr)); } None => { - let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into(); - let args = make::ext::token_tree_from_node( - make::arg_list([target, fmt_string]).syntax(), - ); - let macro_name = make::ext::ident_path("write"); - let macro_call = make::expr_macro(macro_name, args); - - let variant_name = make::path_pat(variant_name); - arms.push(make::match_arm(variant_name, None, macro_call.into())); + let fmt_string = make.expr_literal(&(format!("\"{name}\""))).into(); + let args = + make.token_tree_from_node(make.arg_list([target, fmt_string]).syntax()); + let macro_name = make.ident_path("write"); + let macro_call = make.expr_macro(macro_name, args); + + let variant_name = make.path_pat(variant_name); + arms.push(make.match_arm(variant_name, None, macro_call.into())); } } } - let match_target = make::expr_path(make::ext::ident_path("self")); - let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - let match_expr = make::expr_match(match_target, list); + let match_target = make.expr_path(make.ident_path("self")); + let list = make.match_arm_list(arms).indent(ast::edit::IndentLevel(1)); + let match_expr = make.expr_match(match_target, list); - let body = make::block_expr(None, Some(match_expr.into())); + let body = make.block_expr(None::<ast::Stmt>, Some(match_expr.into())); let body = body.indent(ast::edit::IndentLevel(1)); Some(body) } ast::Adt::Struct(strukt) => { let name = format!("\"{annotated_name}\""); - let args = make::arg_list(Some(make::expr_literal(&name).into())); - let target = make::expr_path(make::ext::ident_path("f")); + let args = make.arg_list([make.expr_literal(&name).into()]); + let target = make.expr_path(make.ident_path("f")); let expr = match strukt.field_list() { // => f.debug_struct("Name").finish() - None => make::expr_method_call(target, make::name_ref("debug_struct"), args).into(), + None => make.expr_method_call(target, make.name_ref("debug_struct"), args).into(), // => f.debug_struct("Name").field("foo", &self.foo).finish() Some(ast::FieldList::RecordFieldList(field_list)) => { - let method = make::name_ref("debug_struct"); - let mut expr = make::expr_method_call(target, method, args).into(); + let method = make.name_ref("debug_struct"); + let mut expr = make.expr_method_call(target, method, args).into(); for field in field_list.fields() { let name = field.name()?; - let f_name = make::expr_literal(&(format!("\"{name}\""))).into(); - let f_path = make::expr_path(make::ext::ident_path("self")); - let f_path = make::expr_ref(f_path, false); - let f_path = make::expr_field(f_path, &format!("{name}")); - let args = make::arg_list([f_name, f_path]); - expr = make::expr_method_call(expr, make::name_ref("field"), args).into(); + let f_name = make.expr_literal(&(format!("\"{name}\""))).into(); + let f_path = make.expr_path(make.ident_path("self")); + let f_path = make.expr_field(f_path, &format!("{name}")).into(); + let f_path = make.expr_ref(f_path, false); + let args = make.arg_list([f_name, f_path]); + expr = make.expr_method_call(expr, make.name_ref("field"), args).into(); } expr } - // => f.debug_tuple("Name").field(self.0).finish() + // => f.debug_tuple("Name").field(&self.0).finish() Some(ast::FieldList::TupleFieldList(field_list)) => { - let method = make::name_ref("debug_tuple"); - let mut expr = make::expr_method_call(target, method, args).into(); + let method = make.name_ref("debug_tuple"); + let mut expr = make.expr_method_call(target, method, args).into(); for (i, _) in field_list.fields().enumerate() { - let f_path = make::expr_path(make::ext::ident_path("self")); - let f_path = make::expr_ref(f_path, false); - let f_path = make::expr_field(f_path, &format!("{i}")); - let method = make::name_ref("field"); - expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path))) - .into(); + let f_path = make.expr_path(make.ident_path("self")); + let f_path = make.expr_field(f_path, &format!("{i}")).into(); + let f_path = make.expr_ref(f_path, false); + let method = make.name_ref("field"); + expr = make.expr_method_call(expr, method, make.arg_list([f_path])).into(); } expr } }; - let method = make::name_ref("finish"); - let expr = make::expr_method_call(expr, method, make::arg_list(None)).into(); - let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); + let method = make.name_ref("finish"); + let expr = make.expr_method_call(expr, method, make.arg_list([])).into(); + let body = + make.block_expr(None::<ast::Stmt>, Some(expr)).indent(ast::edit::IndentLevel(1)); Some(body) } } } -/// Generate a `Debug` impl based on the fields and members of the target type. -fn gen_default_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { - fn gen_default_call() -> Option<ast::Expr> { - let fn_name = make::ext::path_from_idents(["Default", "default"])?; - Some(make::expr_call(make::expr_path(fn_name), make::arg_list(None)).into()) - } +/// Generate a `Default` impl based on the fields and members of the target type. +fn gen_default_impl(make: &SyntaxFactory, adt: &ast::Adt) -> Option<ast::BlockExpr> { + let gen_default_call = || -> Option<ast::Expr> { + let fn_name = make.path_from_idents(["Default", "default"])?; + Some(make.expr_call(make.expr_path(fn_name), make.arg_list([])).into()) + }; match adt { // `Debug` cannot be derived for unions, so no default impl can be provided. ast::Adt::Union(_) => None, @@ -325,42 +327,43 @@ fn gen_default_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { let mut fields = vec![]; for field in field_list.fields() { let method_call = gen_default_call()?; - let name_ref = make::name_ref(&field.name()?.to_string()); - let field = make::record_expr_field(name_ref, Some(method_call)); + let name_ref = make.name_ref(&field.name()?.to_string()); + let field = make.record_expr_field(name_ref, Some(method_call)); fields.push(field); } - let struct_name = make::ext::ident_path("Self"); - let fields = make::record_expr_field_list(fields); - make::record_expr(struct_name, fields).into() + let struct_name = make.ident_path("Self"); + let fields = make.record_expr_field_list(fields); + make.record_expr(struct_name, fields).into() } Some(ast::FieldList::TupleFieldList(field_list)) => { - let struct_name = make::expr_path(make::ext::ident_path("Self")); + let struct_name = make.expr_path(make.ident_path("Self")); let fields = field_list .fields() .map(|_| gen_default_call()) .collect::<Option<Vec<ast::Expr>>>()?; - make::expr_call(struct_name, make::arg_list(fields)).into() + make.expr_call(struct_name, make.arg_list(fields)).into() } None => { - let struct_name = make::ext::ident_path("Self"); - let fields = make::record_expr_field_list(None); - make::record_expr(struct_name, fields).into() + let struct_name = make.ident_path("Self"); + let fields = make.record_expr_field_list([]); + make.record_expr(struct_name, fields).into() } }; - let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); + let body = + make.block_expr(None::<ast::Stmt>, Some(expr)).indent(ast::edit::IndentLevel(1)); Some(body) } } } /// Generate a `Hash` impl based on the fields and members of the target type. -fn gen_hash_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { - fn gen_hash_call(target: ast::Expr) -> ast::Stmt { - let method = make::name_ref("hash"); - let arg = make::expr_path(make::ext::ident_path("state")); - let expr = make::expr_method_call(target, method, make::arg_list(Some(arg))).into(); - make::expr_stmt(expr).into() - } +fn gen_hash_impl(make: &SyntaxFactory, adt: &ast::Adt) -> Option<ast::BlockExpr> { + let gen_hash_call = |target: ast::Expr| -> ast::Stmt { + let method = make.name_ref("hash"); + let arg = make.expr_path(make.ident_path("state")); + let expr = make.expr_method_call(target, method, make.arg_list([arg])).into(); + make.expr_stmt(expr).into() + }; let body = match adt { // `Hash` cannot be derived for unions, so no default impl can be provided. @@ -368,35 +371,35 @@ fn gen_hash_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { // => std::mem::discriminant(self).hash(state); ast::Adt::Enum(_) => { - let fn_name = make_discriminant()?; + let fn_name = make_discriminant(make)?; - let arg = make::expr_path(make::ext::ident_path("self")); - let fn_call = make::expr_call(fn_name, make::arg_list(Some(arg))).into(); + let arg = make.expr_path(make.ident_path("self")); + let fn_call: ast::Expr = make.expr_call(fn_name, make.arg_list([arg])).into(); let stmt = gen_hash_call(fn_call); - make::block_expr(Some(stmt), None).indent(ast::edit::IndentLevel(1)) + make.block_expr([stmt], None).indent(ast::edit::IndentLevel(1)) } ast::Adt::Struct(strukt) => match strukt.field_list() { // => self.<field>.hash(state); Some(ast::FieldList::RecordFieldList(field_list)) => { let mut stmts = vec![]; for field in field_list.fields() { - let base = make::expr_path(make::ext::ident_path("self")); - let target = make::expr_field(base, &field.name()?.to_string()); + let base = make.expr_path(make.ident_path("self")); + let target = make.expr_field(base, &field.name()?.to_string()).into(); stmts.push(gen_hash_call(target)); } - make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1)) + make.block_expr(stmts, None).indent(ast::edit::IndentLevel(1)) } // => self.<field_index>.hash(state); Some(ast::FieldList::TupleFieldList(field_list)) => { let mut stmts = vec![]; for (i, _) in field_list.fields().enumerate() { - let base = make::expr_path(make::ext::ident_path("self")); - let target = make::expr_field(base, &format!("{i}")); + let base = make.expr_path(make.ident_path("self")); + let target = make.expr_field(base, &format!("{i}")).into(); stmts.push(gen_hash_call(target)); } - make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1)) + make.block_expr(stmts, None).indent(ast::edit::IndentLevel(1)) } // No fields in the body means there's nothing to hash. @@ -408,32 +411,37 @@ fn gen_hash_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { } /// Generate a `PartialEq` impl based on the fields and members of the target type. -fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast::BlockExpr> { - fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> { +fn gen_partial_eq( + make: &SyntaxFactory, + adt: &ast::Adt, + trait_ref: Option<TraitRef<'_>>, +) -> Option<ast::BlockExpr> { + let gen_eq_chain = |expr: Option<ast::Expr>, cmp: ast::Expr| -> Option<ast::Expr> { match expr { - Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)), + Some(expr) => Some(make.expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)), None => Some(cmp), } - } + }; - fn gen_record_pat_field(field_name: &str, pat_name: &str) -> ast::RecordPatField { - let pat = make::ext::simple_ident_pat(make::name(pat_name)); - let name_ref = make::name_ref(field_name); - make::record_pat_field(name_ref, pat.into()) - } + let gen_record_pat_field = |field_name: &str, pat_name: &str| -> ast::RecordPatField { + let pat = make.ident_pat(false, false, make.name(pat_name)); + let name_ref = make.name_ref(field_name); + make.record_pat_field(name_ref, pat.into()) + }; - fn gen_record_pat(record_name: ast::Path, fields: Vec<ast::RecordPatField>) -> ast::RecordPat { - let list = make::record_pat_field_list(fields, None); - make::record_pat_with_fields(record_name, list) - } + let gen_record_pat = + |record_name: ast::Path, fields: Vec<ast::RecordPatField>| -> ast::RecordPat { + let list = make.record_pat_field_list(fields, None); + make.record_pat_with_fields(record_name, list) + }; - fn gen_variant_path(variant: &ast::Variant) -> Option<ast::Path> { - make::ext::path_from_idents(["Self", &variant.name()?.to_string()]) - } + let gen_variant_path = |variant: &ast::Variant| -> Option<ast::Path> { + make.path_from_idents(["Self", &variant.name()?.to_string()]) + }; - fn gen_tuple_field(field_name: &str) -> ast::Pat { - ast::Pat::IdentPat(make::ident_pat(false, false, make::name(field_name))) - } + let gen_tuple_field = |field_name: &str| -> ast::Pat { + ast::Pat::IdentPat(make.ident_pat(false, false, make.name(field_name))) + }; // Check that self type and rhs type match. We don't know how to implement the method // automatically otherwise. @@ -451,14 +459,14 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast ast::Adt::Enum(enum_) => { // => std::mem::discriminant(self) == std::mem::discriminant(other) - let lhs_name = make::expr_path(make::ext::ident_path("self")); - let lhs = make::expr_call(make_discriminant()?, make::arg_list(Some(lhs_name.clone()))) - .into(); - let rhs_name = make::expr_path(make::ext::ident_path("other")); - let rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone()))) - .into(); + let lhs_name = make.expr_path(make.ident_path("self")); + let lhs = + make.expr_call(make_discriminant(make)?, make.arg_list([lhs_name.clone()])).into(); + let rhs_name = make.expr_path(make.ident_path("other")); + let rhs = + make.expr_call(make_discriminant(make)?, make.arg_list([rhs_name.clone()])).into(); let eq_check = - make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs); + make.expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs); let mut n_cases = 0; let mut arms = vec![]; @@ -480,9 +488,9 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast let r_name = &format!("r_{field_name}"); r_fields.push(gen_record_pat_field(&field_name, r_name)); - let lhs = make::expr_path(make::ext::ident_path(l_name)); - let rhs = make::expr_path(make::ext::ident_path(r_name)); - let cmp = make::expr_bin_op( + let lhs = make.expr_path(make.ident_path(l_name)); + let rhs = make.expr_path(make.ident_path(r_name)); + let cmp = make.expr_bin_op( lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs, @@ -492,10 +500,10 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast let left = gen_record_pat(gen_variant_path(&variant)?, l_fields); let right = gen_record_pat(gen_variant_path(&variant)?, r_fields); - let tuple = make::tuple_pat(vec![left.into(), right.into()]); + let tuple = make.tuple_pat(vec![left.into(), right.into()]); if let Some(expr) = expr { - arms.push(make::match_arm(tuple.into(), None, expr)); + arms.push(make.match_arm(tuple.into(), None, expr)); } } @@ -513,9 +521,9 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast let r_name = format!("r{field_name}"); r_fields.push(gen_tuple_field(&r_name)); - let lhs = make::expr_path(make::ext::ident_path(&l_name)); - let rhs = make::expr_path(make::ext::ident_path(&r_name)); - let cmp = make::expr_bin_op( + let lhs = make.expr_path(make.ident_path(&l_name)); + let rhs = make.expr_path(make.ident_path(&r_name)); + let cmp = make.expr_bin_op( lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs, @@ -523,12 +531,12 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast expr = gen_eq_chain(expr, cmp); } - let left = make::tuple_struct_pat(gen_variant_path(&variant)?, l_fields); - let right = make::tuple_struct_pat(gen_variant_path(&variant)?, r_fields); - let tuple = make::tuple_pat(vec![left.into(), right.into()]); + let left = make.tuple_struct_pat(gen_variant_path(&variant)?, l_fields); + let right = make.tuple_struct_pat(gen_variant_path(&variant)?, r_fields); + let tuple = make.tuple_pat(vec![left.into(), right.into()]); if let Some(expr) = expr { - arms.push(make::match_arm(tuple.into(), None, expr)); + arms.push(make.match_arm(tuple.into(), None, expr)); } } None => continue, @@ -542,57 +550,57 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast // The fallback arm will be `_ => false,` if we've already gone through every case where the variants of self and other match, // and `_ => std::mem::discriminant(self) == std::mem::discriminant(other),` otherwise. if n_cases > 1 { - let lhs = make::wildcard_pat().into(); + let lhs = make.wildcard_pat().into(); let rhs = if arms_len == n_cases { - make::expr_literal("false").into() + make.expr_literal("false").into() } else { eq_check }; - arms.push(make::match_arm(lhs, None, rhs)); + arms.push(make.match_arm(lhs, None, rhs)); } - let match_target = make::expr_tuple([lhs_name, rhs_name]).into(); - let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - make::expr_match(match_target, list).into() + let match_target = make.expr_tuple([lhs_name, rhs_name]).into(); + let list = make.match_arm_list(arms).indent(ast::edit::IndentLevel(1)); + make.expr_match(match_target, list).into() } }; - make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)) + make.block_expr(None::<ast::Stmt>, Some(expr)).indent(ast::edit::IndentLevel(1)) } ast::Adt::Struct(strukt) => match strukt.field_list() { Some(ast::FieldList::RecordFieldList(field_list)) => { let mut expr = None; for field in field_list.fields() { - let lhs = make::expr_path(make::ext::ident_path("self")); - let lhs = make::expr_field(lhs, &field.name()?.to_string()); - let rhs = make::expr_path(make::ext::ident_path("other")); - let rhs = make::expr_field(rhs, &field.name()?.to_string()); + let lhs = make.expr_path(make.ident_path("self")); + let lhs = make.expr_field(lhs, &field.name()?.to_string()).into(); + let rhs = make.expr_path(make.ident_path("other")); + let rhs = make.expr_field(rhs, &field.name()?.to_string()).into(); let cmp = - make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs); + make.expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs); expr = gen_eq_chain(expr, cmp); } - make::block_expr(None, expr).indent(ast::edit::IndentLevel(1)) + make.block_expr(None, expr).indent(ast::edit::IndentLevel(1)) } Some(ast::FieldList::TupleFieldList(field_list)) => { let mut expr = None; for (i, _) in field_list.fields().enumerate() { let idx = format!("{i}"); - let lhs = make::expr_path(make::ext::ident_path("self")); - let lhs = make::expr_field(lhs, &idx); - let rhs = make::expr_path(make::ext::ident_path("other")); - let rhs = make::expr_field(rhs, &idx); + let lhs = make.expr_path(make.ident_path("self")); + let lhs = make.expr_field(lhs, &idx).into(); + let rhs = make.expr_path(make.ident_path("other")); + let rhs = make.expr_field(rhs, &idx).into(); let cmp = - make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs); + make.expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs); expr = gen_eq_chain(expr, cmp); } - make::block_expr(None, expr).indent(ast::edit::IndentLevel(1)) + make.block_expr(None::<ast::Stmt>, expr).indent(ast::edit::IndentLevel(1)) } // No fields in the body means there's nothing to compare. None => { - let expr = make::expr_literal("true").into(); - make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)) + let expr = make.expr_literal("true").into(); + make.block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)) } }, }; @@ -600,29 +608,33 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast Some(body) } -fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast::BlockExpr> { - fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> { +fn gen_partial_ord( + make: &SyntaxFactory, + adt: &ast::Adt, + trait_ref: Option<TraitRef<'_>>, +) -> Option<ast::BlockExpr> { + let gen_partial_eq_match = |match_target: ast::Expr| -> Option<ast::Stmt> { let mut arms = vec![]; let variant_name = - make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?); - let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]); - arms.push(make::match_arm(lhs.into(), None, make::expr_empty_block().into())); + make.path_pat(make.path_from_idents(["core", "cmp", "Ordering", "Equal"])?); + let lhs = make.tuple_struct_pat(make.path_from_idents(["Some"])?, [variant_name]); + arms.push(make.match_arm(lhs.into(), None, make.expr_empty_block().into())); - arms.push(make::match_arm( - make::ident_pat(false, false, make::name("ord")).into(), + arms.push(make.match_arm( + make.ident_pat(false, false, make.name("ord")).into(), None, - make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))), + make.expr_return(Some(make.expr_path(make.ident_path("ord")))).into(), )); - let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - Some(make::expr_stmt(make::expr_match(match_target, list).into()).into()) - } + let list = make.match_arm_list(arms).indent(ast::edit::IndentLevel(1)); + Some(make.expr_stmt(make.expr_match(match_target, list).into()).into()) + }; - fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { - let rhs = make::expr_ref(rhs, false); - let method = make::name_ref("partial_cmp"); - make::expr_method_call(lhs, method, make::arg_list(Some(rhs))).into() - } + let gen_partial_cmp_call = |lhs: ast::Expr, rhs: ast::Expr| -> ast::Expr { + let rhs = make.expr_ref(rhs, false); + let method = make.name_ref("partial_cmp"); + make.expr_method_call(lhs, method, make.arg_list([rhs])).into() + }; // Check that self type and rhs type match. We don't know how to implement the method // automatically otherwise. @@ -643,10 +655,10 @@ fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<as Some(ast::FieldList::RecordFieldList(field_list)) => { let mut exprs = vec![]; for field in field_list.fields() { - let lhs = make::expr_path(make::ext::ident_path("self")); - let lhs = make::expr_field(lhs, &field.name()?.to_string()); - let rhs = make::expr_path(make::ext::ident_path("other")); - let rhs = make::expr_field(rhs, &field.name()?.to_string()); + let lhs = make.expr_path(make.ident_path("self")); + let lhs = make.expr_field(lhs, &field.name()?.to_string()).into(); + let rhs = make.expr_path(make.ident_path("other")); + let rhs = make.expr_field(rhs, &field.name()?.to_string()).into(); let ord = gen_partial_cmp_call(lhs, rhs); exprs.push(ord); } @@ -656,17 +668,17 @@ fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<as .into_iter() .map(gen_partial_eq_match) .collect::<Option<Vec<ast::Stmt>>>()?; - make::block_expr(stmts, tail).indent(ast::edit::IndentLevel(1)) + make.block_expr(stmts, tail).indent(ast::edit::IndentLevel(1)) } Some(ast::FieldList::TupleFieldList(field_list)) => { let mut exprs = vec![]; for (i, _) in field_list.fields().enumerate() { let idx = format!("{i}"); - let lhs = make::expr_path(make::ext::ident_path("self")); - let lhs = make::expr_field(lhs, &idx); - let rhs = make::expr_path(make::ext::ident_path("other")); - let rhs = make::expr_field(rhs, &idx); + let lhs = make.expr_path(make.ident_path("self")); + let lhs = make.expr_field(lhs, &idx).into(); + let rhs = make.expr_path(make.ident_path("other")); + let rhs = make.expr_field(rhs, &idx).into(); let ord = gen_partial_cmp_call(lhs, rhs); exprs.push(ord); } @@ -675,13 +687,13 @@ fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<as .into_iter() .map(gen_partial_eq_match) .collect::<Option<Vec<ast::Stmt>>>()?; - make::block_expr(stmts, tail).indent(ast::edit::IndentLevel(1)) + make.block_expr(stmts, tail).indent(ast::edit::IndentLevel(1)) } // No fields in the body means there's nothing to compare. None => { - let expr = make::expr_literal("true").into(); - make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)) + let expr = make.expr_literal("true").into(); + make.block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)) } }, }; @@ -689,6 +701,6 @@ fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<as Some(body) } -fn make_discriminant() -> Option<ast::Expr> { - Some(make::expr_path(make::ext::path_from_idents(["core", "mem", "discriminant"])?)) +fn make_discriminant(make: &SyntaxFactory) -> Option<ast::Expr> { + Some(make.expr_path(make.path_from_idents(["core", "mem", "discriminant"])?)) } diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 1fb1fd4e57..9a09e9bd4a 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -34,7 +34,7 @@ use crate::{ CompletionContext, CompletionItem, CompletionItemKind, context::{ DotAccess, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, - PathCompletionCtx, PathKind, PatternContext, TypeLocation, Visible, + PathCompletionCtx, PathKind, PatternContext, TypeAscriptionTarget, TypeLocation, Visible, }, item::Builder, render::{ @@ -45,7 +45,7 @@ use crate::{ macro_::render_macro, pattern::{render_struct_pat, render_variant_pat}, render_expr, render_field, render_path_resolution, render_pattern_resolution, - render_tuple_field, + render_tuple_field, render_type_keyword_snippet, type_alias::{render_type_alias, render_type_alias_with_eq}, union_literal::render_union_literal, }, @@ -104,6 +104,21 @@ impl Completions { } } + pub(crate) fn add_nameref_keywords_with_type_like( + &mut self, + ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, + ) { + let mut add_keyword = |kw| { + render_type_keyword_snippet(ctx, path_ctx, kw, kw).add_to(self, ctx.db); + }; + ["self::", "crate::"].into_iter().for_each(&mut add_keyword); + + if ctx.depth_from_crate_root > 0 { + add_keyword("super::"); + } + } + pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext<'_>) { ["self", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); @@ -112,11 +127,19 @@ impl Completions { } } - pub(crate) fn add_type_keywords(&mut self, ctx: &CompletionContext<'_>) { - self.add_keyword_snippet(ctx, "fn", "fn($1)"); - self.add_keyword_snippet(ctx, "dyn", "dyn $0"); - self.add_keyword_snippet(ctx, "impl", "impl $0"); - self.add_keyword_snippet(ctx, "for", "for<$1>"); + pub(crate) fn add_type_keywords( + &mut self, + ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, + ) { + let mut add_keyword = |kw, snippet| { + render_type_keyword_snippet(ctx, path_ctx, kw, snippet).add_to(self, ctx.db); + }; + + add_keyword("fn", "fn($1)"); + add_keyword("dyn", "dyn $0"); + add_keyword("impl", "impl $0"); + add_keyword("for", "for<$1>"); } pub(crate) fn add_super_keyword( @@ -733,7 +756,7 @@ pub(super) fn complete_name_ref( match &path_ctx.kind { PathKind::Expr { expr_ctx } => { expr::complete_expr_path(acc, ctx, path_ctx, expr_ctx); - expr::complete_expr(acc, ctx); + expr::complete_expr(acc, ctx, path_ctx); dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx); item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx); @@ -747,6 +770,12 @@ pub(super) fn complete_name_ref( field::complete_field_list_tuple_variant(acc, ctx, path_ctx); } TypeLocation::TypeAscription(ascription) => { + if let TypeAscriptionTarget::RetType { item: Some(item), .. } = + ascription + && path_ctx.required_thin_arrow().is_some() + { + keyword::complete_for_and_where(acc, ctx, &item.clone().into()); + } r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription); } TypeLocation::GenericArg { .. } diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 20776f6c49..da1e664f96 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -30,6 +30,7 @@ mod lint; mod macro_use; mod repr; +pub(crate) use self::cfg::complete_cfg; pub(crate) use self::derive::complete_derive_path; /// Complete inputs to known builtin attributes as well as derive attributes @@ -37,7 +38,7 @@ pub(crate) fn complete_known_attribute_input( acc: &mut Completions, ctx: &CompletionContext<'_>, &colon_prefix: &bool, - fake_attribute_under_caret: &ast::Attr, + fake_attribute_under_caret: &ast::TokenTreeMeta, extern_crate: Option<&ast::ExternCrate>, ) -> Option<()> { let attribute = fake_attribute_under_caret; @@ -70,7 +71,6 @@ pub(crate) fn complete_known_attribute_input( lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints); } - ["cfg"] | ["cfg_attr"] => cfg::complete_cfg(acc, ctx), ["macro_use"] => macro_use::complete_macro_use( acc, ctx, diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 92cbf411c1..885d1a3075 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -51,11 +51,10 @@ pub(crate) fn complete_cargo_env_vars( original: &ast::String, expanded: &ast::String, ) -> Option<()> { - let is_in_env_expansion = ctx - .sema - .hir_file_for(&expanded.syntax().parent()?) - .macro_file() - .is_some_and(|it| it.is_env_or_option_env(ctx.sema.db)); + let descends = ctx.sema.descend_into_macros_exact_with_file(original.syntax().clone()); + let macro_file = descends.first()?.file_id.macro_file(); + + let is_in_env_expansion = macro_file.is_some_and(|it| it.is_env_or_option_env(ctx.sema.db)); if !is_in_env_expansion { let call = macro_call_for_string_token(expanded)?; let makro = ctx.sema.resolve_macro_call(&call)?; @@ -117,6 +116,47 @@ fn main() { } #[test] + fn complete_in_expanded_env_macro() { + check_edit( + "CARGO_BIN_NAME", + r#" +//- minicore: env +macro_rules! bar { + ($($arg:tt)*) => { $($arg)* } +} + +fn main() { + let foo = bar!(env!("CA$0")); +} + "#, + r#" +macro_rules! bar { + ($($arg:tt)*) => { $($arg)* } +} + +fn main() { + let foo = bar!(env!("CARGO_BIN_NAME")); +} + "#, + ); + + check_edit( + "CARGO_BIN_NAME", + r#" +//- minicore: env, fmt +fn main() { + let foo = format_args!("{}", env!("CA$0")); +} + "#, + r#" +fn main() { + let foo = format_args!("{}", env!("CARGO_BIN_NAME")); +} + "#, + ); + } + + #[test] fn doesnt_complete_in_random_strings() { let fixture = r#" fn main() { diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 8c532e0f4d..99ca55bdaf 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -451,7 +451,11 @@ pub(crate) fn complete_expr_path( } } -pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>) { +pub(crate) fn complete_expr( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, +) { let _p = tracing::info_span!("complete_expr").entered(); if !ctx.config.enable_term_search { @@ -462,6 +466,10 @@ pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>) return; } + if !matches!(qualified, Qualified::No) { + return; + } + if let Some(ty) = &ctx.expected_type { // Ignore unit types as they are not very interesting if ty.is_unit() || ty.is_unknown() { diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 20d01485a4..2cf87baf33 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -133,9 +133,15 @@ pub(crate) fn import_on_the_fly_path( let potential_import_name = import_name(ctx); let qualifier = match qualified { Qualified::With { path, .. } => Some(path.clone()), - _ => None, + Qualified::TypeAnchor { .. } => return None, + Qualified::No | Qualified::Absolute => None, }; - let import_assets = import_assets_for_path(ctx, &potential_import_name, qualifier.clone())?; + let import_assets = import_assets_for_path( + ctx, + Some(&path_ctx.path), + &potential_import_name, + qualifier.clone(), + )?; import_on_the_fly( acc, @@ -160,7 +166,7 @@ pub(crate) fn import_on_the_fly_pat( } let potential_import_name = import_name(ctx); - let import_assets = import_assets_for_path(ctx, &potential_import_name, None)?; + let import_assets = import_assets_for_path(ctx, None, &potential_import_name, None)?; import_on_the_fly_pat_( acc, @@ -402,6 +408,7 @@ fn import_name(ctx: &CompletionContext<'_>) -> String { fn import_assets_for_path<'db>( ctx: &CompletionContext<'db>, + path: Option<&ast::Path>, potential_import_name: &str, qualifier: Option<ast::Path>, ) -> Option<ImportAssets<'db>> { @@ -411,6 +418,7 @@ fn import_assets_for_path<'db>( let fuzzy_name_length = potential_import_name.len(); let mut assets_for_path = ImportAssets::for_fuzzy_path( ctx.module, + path, qualifier, potential_import_name.to_owned(), &ctx.sema, diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 5b91e7c456..82baf885dd 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -310,7 +310,7 @@ pub(crate) fn complete_postfix( if let ast::Expr::Literal(literal) = dot_receiver.clone() && let Some(literal_text) = ast::String::cast(literal.token()) { - add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text); + add_format_like_completions(acc, ctx, dot_receiver, cap, &literal_text, semi); } postfix_snippet("return", "return expr", &format!("return {receiver_text}{semi}")) @@ -402,7 +402,7 @@ fn receiver_accessor(receiver: &ast::Expr) -> ast::Expr { .unwrap_or_else(|| receiver.clone()) } -/// Given an `initial_element`, tries to expand it to include deref(s), and then references. +/// Given an `initial_element`, tries to expand it to include deref(s), not(s), and then references. /// Returns the expanded expressions, and the added prefix as a string /// /// For example, if called with the `42` in `&&mut *42`, would return `(&&mut *42, "&&mut *")`. @@ -410,22 +410,23 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { let mut resulting_element = initial_element.clone(); let mut prefix = String::new(); - let mut found_ref_or_deref = false; - - while let Some(parent_deref_element) = - resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast) - && parent_deref_element.op_kind() == Some(ast::UnaryOp::Deref) + while let Some(parent) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast) + && parent.op_kind() == Some(ast::UnaryOp::Deref) { - found_ref_or_deref = true; - resulting_element = ast::Expr::from(parent_deref_element); - + resulting_element = ast::Expr::from(parent); prefix.insert(0, '*'); } + while let Some(parent) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast) + && parent.op_kind() == Some(ast::UnaryOp::Not) + { + resulting_element = ast::Expr::from(parent); + prefix.insert(0, '!'); + } + while let Some(parent_ref_element) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) { - found_ref_or_deref = true; let last_child_or_token = parent_ref_element.syntax().last_child_or_token(); prefix.insert_str( 0, @@ -440,13 +441,6 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { resulting_element = ast::Expr::from(parent_ref_element); } - if !found_ref_or_deref { - // If we do not find any ref/deref expressions, restore - // all the progress of tree climbing - prefix.clear(); - resulting_element = initial_element.clone(); - } - (resulting_element, prefix) } @@ -1133,6 +1127,27 @@ fn main() { } #[test] + fn postfix_completion_for_nots() { + check_edit( + "if", + r#" +fn main() { + let is_foo = true; + !is_foo.$0 +} +"#, + r#" +fn main() { + let is_foo = true; + if !is_foo { + $0 +} +} +"#, + ) + } + + #[test] fn postfix_completion_for_unsafe() { postfix_completion_for_block("unsafe"); } @@ -1287,34 +1302,42 @@ fn main() { check_edit( "panic", r#"fn main() { "Panic with {a}".$0 }"#, - r#"fn main() { panic!("Panic with {a}") }"#, + r#"fn main() { panic!("Panic with {a}"); }"#, ); check_edit( "println", r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".$0 }"#, - r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#, + r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }); }"#, ); check_edit( "loge", r#"fn main() { "{2+2}".$0 }"#, - r#"fn main() { log::error!("{}", 2+2) }"#, + r#"fn main() { log::error!("{}", 2+2); }"#, ); check_edit( "logt", r#"fn main() { "{2+2}".$0 }"#, - r#"fn main() { log::trace!("{}", 2+2) }"#, + r#"fn main() { log::trace!("{}", 2+2); }"#, ); check_edit( "logd", r#"fn main() { "{2+2}".$0 }"#, - r#"fn main() { log::debug!("{}", 2+2) }"#, + r#"fn main() { log::debug!("{}", 2+2); }"#, + ); + check_edit( + "logi", + r#"fn main() { "{2+2}".$0 }"#, + r#"fn main() { log::info!("{}", 2+2); }"#, + ); + check_edit( + "logw", + r#"fn main() { "{2+2}".$0 }"#, + r#"fn main() { log::warn!("{}", 2+2); }"#, ); - check_edit("logi", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::info!("{}", 2+2) }"#); - check_edit("logw", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::warn!("{}", 2+2) }"#); check_edit( "loge", r#"fn main() { "{2+2}".$0 }"#, - r#"fn main() { log::error!("{}", 2+2) }"#, + r#"fn main() { log::error!("{}", 2+2); }"#, ); } diff --git a/crates/ide-completion/src/completions/postfix/format_like.rs b/crates/ide-completion/src/completions/postfix/format_like.rs index 7faa113959..85a8899fd1 100644 --- a/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/crates/ide-completion/src/completions/postfix/format_like.rs @@ -40,6 +40,7 @@ static KINDS: &[(&str, &str)] = &[ ("logw", "log::warn!"), ("loge", "log::error!"), ]; +static SNIPPET_RETURNS_NON_UNIT: &[&str] = &["format"]; pub(crate) fn add_format_like_completions( acc: &mut Completions, @@ -47,6 +48,7 @@ pub(crate) fn add_format_like_completions( dot_receiver: &ast::Expr, cap: SnippetCap, receiver_text: &ast::String, + semi: &str, ) { let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, dot_receiver) { Some(it) => it, @@ -64,10 +66,11 @@ pub(crate) fn add_format_like_completions( let exprs = with_placeholders(exprs); for (label, macro_name) in KINDS { + let semi = if SNIPPET_RETURNS_NON_UNIT.contains(label) { "" } else { semi }; let snippet = if exprs.is_empty() { - format!(r#"{macro_name}({out})"#) + format!(r#"{macro_name}({out}){semi}"#) } else { - format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", ")) + format!(r#"{}({}, {}){semi}"#, macro_name, out, exprs.join(", ")) }; postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db); diff --git a/crates/ide-completion/src/completions/ra_fixture.rs b/crates/ide-completion/src/completions/ra_fixture.rs index b44c90757f..5a8881edc7 100644 --- a/crates/ide-completion/src/completions/ra_fixture.rs +++ b/crates/ide-completion/src/completions/ra_fixture.rs @@ -22,7 +22,7 @@ pub(crate) fn complete_ra_fixture( &ctx.sema, original.clone(), expanded, - ctx.config.minicore, + &ctx.config.ra_fixture, &mut |_| {}, )?; let (virtual_file_id, virtual_offset) = analysis.map_offset_down(ctx.position.offset)?; diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 8ff9c3258e..e2125a9678 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -206,8 +206,8 @@ pub(crate) fn complete_type_path( _ => {} }; - acc.add_nameref_keywords_with_colon(ctx); - acc.add_type_keywords(ctx); + acc.add_nameref_keywords_with_type_like(ctx, path_ctx); + acc.add_type_keywords(ctx, path_ctx); ctx.process_all_names(&mut |name, def, doc_aliases| { if scope_def_applicable(def) { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases); @@ -230,14 +230,14 @@ pub(crate) fn complete_ascribed_type( TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => { ctx.sema.type_of_pat(pat.as_ref()?) } - TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType(exp) => { + TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType { body: exp, .. } => { ctx.sema.type_of_expr(exp.as_ref()?) } }? .adjusted(); if !ty.is_unknown() { let ty_string = ty.display_source_code(ctx.db, ctx.module.into(), true).ok()?; - acc.add(render_type_inference(ty_string, ctx)); + acc.add(render_type_inference(ty_string, ctx, path_ctx)); } None } diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs index 5623257a27..80c1572972 100644 --- a/crates/ide-completion/src/config.rs +++ b/crates/ide-completion/src/config.rs @@ -6,8 +6,9 @@ use hir::FindPathConfig; use ide_db::{ - MiniCore, SnippetCap, + SnippetCap, imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig}, + ra_fixture::RaFixtureConfig, }; use crate::{CompletionFieldsToResolve, snippet::Snippet}; @@ -35,7 +36,7 @@ pub struct CompletionConfig<'a> { pub fields_to_resolve: CompletionFieldsToResolve, pub exclude_flyimport: Vec<(String, AutoImportExclusionType)>, pub exclude_traits: &'a [String], - pub minicore: MiniCore<'a>, + pub ra_fixture: RaFixtureConfig<'a>, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 4fd0348156..485e5f0caf 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -6,7 +6,7 @@ mod tests; use std::iter; -use base_db::RootQueryDb as _; +use base_db::toolchain_channel; use hir::{ DisplayTarget, HasAttrs, InFile, Local, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, @@ -102,6 +102,28 @@ impl PathCompletionCtx<'_> { } ) } + + pub(crate) fn required_thin_arrow(&self) -> Option<(&'static str, TextSize)> { + let PathKind::Type { + location: + TypeLocation::TypeAscription(TypeAscriptionTarget::RetType { + item: Some(ref fn_item), + .. + }), + } = self.kind + else { + return None; + }; + if fn_item.ret_type().is_some_and(|it| it.thin_arrow_token().is_some()) { + return None; + } + let ret_type = fn_item.ret_type().and_then(|it| it.ty()); + match (ret_type, fn_item.param_list()) { + (Some(ty), _) => Some(("-> ", ty.syntax().text_range().start())), + (None, Some(param)) => Some((" ->", param.syntax().text_range().end())), + (None, None) => None, + } + } } /// The kind of path we are completing right now. @@ -231,7 +253,7 @@ impl TypeLocation { pub(crate) enum TypeAscriptionTarget { Let(Option<ast::Pat>), FnParam(Option<ast::Pat>), - RetType(Option<ast::Expr>), + RetType { body: Option<ast::Expr>, item: Option<ast::Fn> }, Const(Option<ast::Expr>), } @@ -386,9 +408,11 @@ pub(crate) enum CompletionAnalysis<'db> { /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)` UnexpandedAttrTT { colon_prefix: bool, - fake_attribute_under_caret: Option<ast::Attr>, + fake_attribute_under_caret: Option<ast::TokenTreeMeta>, extern_crate: Option<ast::ExternCrate>, }, + /// Set if we are inside the predicate of a #[cfg] or #[cfg_attr]. + CfgPredicate, MacroSegment, } @@ -715,7 +739,7 @@ impl<'db> CompletionContext<'db> { // actual completion. let file_with_fake_ident = { let (_, edition) = editioned_file_id.unpack(db); - let parse = db.parse(editioned_file_id); + let parse = editioned_file_id.parse(db); parse.reparse(TextRange::empty(offset), COMPLETION_MARKER, edition).tree() }; @@ -768,7 +792,7 @@ impl<'db> CompletionContext<'db> { let containing_function = scope.containing_function(); let edition = krate.edition(db); - let toolchain = db.toolchain_channel(krate.into()); + let toolchain = toolchain_channel(db, krate.into()); // `toolchain == None` means we're in some detached files. Since we have no information on // the toolchain being used, let's just allow unstable items to be listed. let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None); diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index a3494b964f..2a293313f2 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -284,9 +284,12 @@ fn expand( }; // Expand pseudo-derive expansion aka `derive(Debug$0)` - if let Some((orig_attr, spec_attr)) = attrs { + if let Some((orig_attr, spec_attr)) = attrs + && let Some(orig_meta) = orig_attr.meta() + { + // FIXME: Support speculative expansion with `cfg_attr`. if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_tokens))) = ( - sema.expand_derive_as_pseudo_attr_macro(&orig_attr), + sema.expand_derive_as_pseudo_attr_macro(&orig_meta), sema.speculative_expand_derive_as_pseudo_attr_macro( &orig_attr, &spec_attr, @@ -463,7 +466,9 @@ fn analyze<'db>( } // Overwrite the path kind for derives - if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx { + if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx + && let Some(origin_meta) = origin_attr.meta() + { if let Some(ast::NameLike::NameRef(name_ref)) = find_node_at_offset(&file_with_fake_ident, offset) { @@ -473,7 +478,7 @@ fn analyze<'db>( if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind { path_ctx.kind = PathKind::Derive { existing_derives: sema - .resolve_derive_macro(&origin_attr) + .resolve_derive_macro(&origin_meta) .into_iter() .flatten() .flatten() @@ -498,7 +503,7 @@ fn analyze<'db>( let token = syntax::algo::skip_trivia_token(self_token.clone(), Direction::Prev)?; let p = token.parent()?; if p.kind() == SyntaxKind::TOKEN_TREE - && p.ancestors().any(|it| it.kind() == SyntaxKind::META) + && p.ancestors().any(|it| it.kind() == SyntaxKind::TOKEN_TREE_META) { let colon_prefix = previous_non_trivia_token(self_token.clone()) .is_some_and(|it| T![:] == it.kind()); @@ -506,7 +511,7 @@ fn analyze<'db>( CompletionAnalysis::UnexpandedAttrTT { fake_attribute_under_caret: fake_ident_token .parent_ancestors() - .find_map(ast::Attr::cast), + .find_map(ast::TokenTreeMeta::cast), colon_prefix, extern_crate: p.ancestors().find_map(ast::ExternCrate::cast), } @@ -525,6 +530,13 @@ fn analyze<'db>( } else { return None; } + } else if find_node_at_offset::<ast::CfgPredicate>( + &speculative_file, + speculative_offset, + ) + .is_some() + { + CompletionAnalysis::CfgPredicate } else { return None; } @@ -827,6 +839,19 @@ fn expected_type_and_name<'db>( .map(|c| (Some(c.return_type()), None)) .unwrap_or((None, None)) }, + ast::Variant(it) => { + let is_simple_field = |field: ast::TupleField| { + let Some(ty) = field.ty() else { return true }; + matches!(ty, ast::Type::PathType(_)) && ty.generic_arg_list().is_none() + }; + let is_simple_variant = matches!( + it.field_list(), + Some(ast::FieldList::TupleFieldList(list)) + if list.syntax().children_with_tokens().all(|it| it.kind() != T![,]) + && list.fields().next().is_none_or(is_simple_field) + ); + (None, it.name().filter(|_| is_simple_variant).map(NameOrNameRef::Name)) + }, ast::Stmt(_) => (None, None), ast::Item(_) => (None, None), _ => { @@ -1265,15 +1290,14 @@ fn classify_name_ref<'db>( let original = ast::Static::cast(name.syntax().parent()?)?; TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body())) }, - ast::RetType(it) => { - it.thin_arrow_token()?; + ast::RetType(_) => { let parent = match ast::Fn::cast(parent.parent()?) { Some(it) => it.param_list(), None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(), }; let parent = find_opt_node_in_file(original_file, parent)?.syntax().parent()?; - TypeLocation::TypeAscription(TypeAscriptionTarget::RetType(match_ast! { + let body = match_ast! { match parent { ast::ClosureExpr(it) => { it.body() @@ -1283,7 +1307,9 @@ fn classify_name_ref<'db>( }, _ => return None, } - })) + }; + let item = ast::Fn::cast(parent); + TypeLocation::TypeAscription(TypeAscriptionTarget::RetType { body, item }) }, ast::Param(it) => { it.colon_token()?; diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 1a9139d855..e6dd1d37d9 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -450,6 +450,7 @@ impl CompletionItem { ref_match: None, imports_to_add: Default::default(), doc_aliases: vec![], + adds_text: None, edition, } } @@ -480,12 +481,13 @@ impl CompletionItem { /// A helper to make `CompletionItem`s. #[must_use] -#[derive(Clone)] +#[derive(Debug, Clone)] pub(crate) struct Builder { source_range: TextRange, imports_to_add: SmallVec<[LocatedImport; 1]>, trait_name: Option<SmolStr>, doc_aliases: Vec<SmolStr>, + adds_text: Option<SmolStr>, label: SmolStr, insert_text: Option<String>, is_snippet: bool, @@ -526,9 +528,16 @@ impl Builder { let insert_text = self.insert_text.unwrap_or_else(|| label.to_string()); let mut detail_left = None; + let mut to_detail_left = |args: fmt::Arguments<'_>| { + let detail_left = detail_left.get_or_insert_with(String::new); + if !detail_left.is_empty() { + detail_left.push(' '); + } + format_to!(detail_left, "{args}") + }; if !self.doc_aliases.is_empty() { let doc_aliases = self.doc_aliases.iter().join(", "); - detail_left = Some(format!("(alias {doc_aliases})")); + to_detail_left(format_args!("(alias {doc_aliases})")); let lookup_doc_aliases = self .doc_aliases .iter() @@ -548,22 +557,17 @@ impl Builder { lookup = format_smolstr!("{lookup}{lookup_doc_aliases}"); } } + if let Some(adds_text) = self.adds_text { + to_detail_left(format_args!("(adds {})", adds_text.trim())); + } if let [import_edit] = &*self.imports_to_add { // snippets can have multiple imports, but normal completions only have up to one - let detail_left = detail_left.get_or_insert_with(String::new); - format_to!( - detail_left, - "{}(use {})", - if detail_left.is_empty() { "" } else { " " }, + to_detail_left(format_args!( + "(use {})", import_edit.import_path.display(db, self.edition) - ); + )); } else if let Some(trait_name) = self.trait_name { - let detail_left = detail_left.get_or_insert_with(String::new); - format_to!( - detail_left, - "{}(as {trait_name})", - if detail_left.is_empty() { "" } else { " " }, - ); + to_detail_left(format_args!("(as {trait_name})")); } let text_edit = match self.text_edit { @@ -613,6 +617,10 @@ impl Builder { self.doc_aliases = doc_aliases; self } + pub(crate) fn adds_text(&mut self, adds_text: SmolStr) -> &mut Builder { + self.adds_text = Some(adds_text); + self + } pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder { self.insert_text = Some(insert_text.into()); self diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 69ca2af772..3867e65ae5 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -263,6 +263,7 @@ pub fn completions( extern_crate.as_ref(), ); } + CompletionAnalysis::CfgPredicate => completions::attribute::complete_cfg(acc, ctx), CompletionAnalysis::MacroSegment => { completions::macro_def::complete_macro_segment(acc, ctx); } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index d77e793295..b6da6fba63 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -220,13 +220,15 @@ pub(crate) fn render_tuple_field( pub(crate) fn render_type_inference( ty_string: String, ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, ) -> CompletionItem { let mut builder = CompletionItem::new( CompletionItemKind::InferredType, ctx.source_range(), - ty_string, + &ty_string, ctx.edition, ); + adds_ret_type_arrow(ctx, path_ctx, &mut builder, ty_string); builder.set_relevance(CompletionRelevance { type_match: Some(CompletionRelevanceTypeMatch::Exact), exact_name_match: true, @@ -425,11 +427,10 @@ fn render_resolution_path( let config = completion.config; let requires_import = import_to_add.is_some(); - let name = local_name.display_no_db(ctx.completion.edition).to_smolstr(); + let name = local_name.display(db, completion.edition).to_smolstr(); let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution); - if local_name.needs_escape(completion.edition) { - item.insert_text(local_name.display_no_db(completion.edition).to_smolstr()); - } + let mut insert_text = name.clone(); + // Add `<>` for generic types let type_path_no_ty_args = matches!( path_ctx, @@ -446,12 +447,14 @@ fn render_resolution_path( if has_non_default_type_params { cov_mark::hit!(inserts_angle_brackets_for_generics); + insert_text = format_smolstr!("{insert_text}<$0>"); item.lookup_by(name.clone()) .label(SmolStr::from_iter([&name, "<…>"])) .trigger_call_info() - .insert_snippet(cap, format!("{}<$0>", local_name.display(db, completion.edition))); + .insert_snippet(cap, ""); // set is snippet } } + adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into()); let mut set_item_relevance = |ty: Type<'_>| { if !ty.is_unknown() { @@ -577,6 +580,48 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo } } +pub(crate) fn render_type_keyword_snippet( + ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, + label: &str, + snippet: &str, +) -> Builder { + let source_range = ctx.source_range(); + let mut item = + CompletionItem::new(CompletionItemKind::Keyword, source_range, label, ctx.edition); + + let insert_text = if !snippet.contains('$') { + item.insert_text(snippet); + snippet + } else if let Some(cap) = ctx.config.snippet_cap { + item.insert_snippet(cap, snippet); + snippet + } else { + label + }; + + adds_ret_type_arrow(ctx, path_ctx, &mut item, insert_text.to_owned()); + item +} + +fn adds_ret_type_arrow( + ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, + item: &mut Builder, + insert_text: String, +) { + if let Some((arrow, at)) = path_ctx.required_thin_arrow() { + let mut edit = TextEdit::builder(); + + edit.insert(at, arrow.to_owned()); + edit.replace(ctx.source_range(), insert_text); + + item.text_edit(edit.finish()).adds_text(SmolStr::new_static(arrow)); + } else { + item.insert_text(insert_text); + } +} + // FIXME: This checks types without possible coercions which some completions might want to do fn match_types( ctx: &CompletionContext<'_>, @@ -2166,7 +2211,6 @@ fn bb()-> &'static aa { } "#, expect![[r#" - ex bb() [type] fn from_bytes(…) fn(&[u8]) -> &aa [type_could_unify] "#]], ); @@ -3045,6 +3089,57 @@ fn main() { } #[test] + fn enum_variant_name_exact_match_is_high_priority() { + check_relevance( + r#" +struct Other; +struct String; +enum Foo { + String($0) +} + "#, + expect![[r#" + st String String [name] + en Foo Foo [] + st Other Other [] + sp Self Foo [] + "#]], + ); + + check_relevance( + r#" +struct Other; +struct String; +enum Foo { + String(String, $0) +} + "#, + expect![[r#" + en Foo Foo [] + st Other Other [] + sp Self Foo [] + st String String [] + "#]], + ); + + check_relevance( + r#" +struct Other; +struct Vec<T>(T); +enum Foo { + Vec(Vec<$0>) +} + "#, + expect![[r#" + en Foo Foo [] + st Other Other [] + sp Self Foo [] + st Vec<…> Vec<{unknown}> [] + "#]], + ); + } + + #[test] fn postfix_inexact_match_is_low_priority() { cov_mark::check!(postfix_inexact_match_is_low_priority); check_relevance_for_kinds( diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index cb1adfcfb6..02e299b2a9 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -29,8 +29,9 @@ use expect_test::Expect; use hir::db::HirDatabase; use hir::{PrefixKind, setup_tracing}; use ide_db::{ - FilePosition, MiniCore, RootDatabase, SnippetCap, + FilePosition, RootDatabase, SnippetCap, imports::insert_use::{ImportGranularity, InsertUseConfig}, + ra_fixture::RaFixtureConfig, }; use itertools::Itertools; use stdx::{format_to, trim_indent}; @@ -90,7 +91,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { exclude_traits: &[], enable_auto_await: true, enable_auto_iter: true, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 8e50ef10ec..4a5983097a 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -1030,8 +1030,6 @@ fn main() { "#, expect![[r#" fn test() fn() -> Zulu - ex Zulu - ex Zulu::test() "#]], ); } diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index d7db896679..5391e6c9ce 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -1243,6 +1243,39 @@ impl Bar for Foo { } #[test] +fn no_flyimports_type_anchor() { + check( + r#" +mod m { + pub fn foo() {} +} +struct Bar; +trait Foo {} +impl Foo for Bar {} +fn main() { + <Bar as Foo>::foo$0 +} + "#, + expect![[r#""#]], + ); + + check( + r#" +mod m { + pub fn foo() {} +} +struct Bar; +trait Foo {} +impl Foo for Bar {} +fn main() { + <Bar>::foo$0 +} + "#, + expect![[r#""#]], + ); +} + +#[test] fn no_inherent_candidates_proposed() { check( r#" diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs index 61a9da8c27..2f032c3f4c 100644 --- a/crates/ide-completion/src/tests/item.rs +++ b/crates/ide-completion/src/tests/item.rs @@ -116,8 +116,23 @@ fn completes_where() { check_with_base_items( r"fn func() $0", expect![[r#" - kw where - "#]], + en Enum (adds ->) Enum + ma makro!(…) macro_rules! makro + md module (adds ->) + st Record (adds ->) Record + st Tuple (adds ->) Tuple + st Unit (adds ->) Unit + tt Trait (adds ->) + un Union (adds ->) Union + bt u32 (adds ->) u32 + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], ); check_with_base_items( r"enum Enum $0", @@ -244,6 +259,19 @@ impl Copy for S where $0 } #[test] +fn fn_item_where_kw() { + check_edit( + "where", + r#" +fn foo() $0 +"#, + r#" +fn foo() where $0 +"#, + ); +} + +#[test] fn test_is_not_considered_macro() { check_with_base_items( r#" diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index b82b23541c..55059a4035 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -896,9 +896,6 @@ fn bar() -> Bar { "#, expect![[r#" fn foo() (as Foo) fn() -> Self - ex Bar - ex Bar::foo() - ex bar() "#]], ); } @@ -926,9 +923,6 @@ fn bar() -> Bar { expect![[r#" fn bar() fn() fn foo() (as Foo) fn() -> Self - ex Bar - ex Bar::foo() - ex bar() "#]], ); } @@ -955,9 +949,6 @@ fn bar() -> Bar { "#, expect![[r#" fn foo() (as Foo) fn() -> Self - ex Bar - ex Bar::foo() - ex bar() "#]], ); } diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 7c6b7370aa..7d4a7fe6b8 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -1,7 +1,7 @@ //! Completion tests for type position. use expect_test::expect; -use crate::tests::{check, check_with_base_items}; +use crate::tests::{check, check_edit, check_with_base_items}; #[test] fn record_field_ty() { @@ -94,6 +94,230 @@ fn x<'lt, T, const C: usize>() -> $0 } #[test] +fn fn_return_type_missing_thin_arrow() { + check_with_base_items( + r#" +fn x() u$0 +"#, + expect![[r#" + en Enum (adds ->) Enum + ma makro!(…) macro_rules! makro + md module (adds ->) + st Record (adds ->) Record + st Tuple (adds ->) Tuple + st Unit (adds ->) Unit + tt Trait (adds ->) + un Union (adds ->) Union + bt u32 (adds ->) u32 + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], + ); + + check_with_base_items( + r#" +fn x() $0 +"#, + expect![[r#" + en Enum (adds ->) Enum + ma makro!(…) macro_rules! makro + md module (adds ->) + st Record (adds ->) Record + st Tuple (adds ->) Tuple + st Unit (adds ->) Unit + tt Trait (adds ->) + un Union (adds ->) Union + bt u32 (adds ->) u32 + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], + ); +} + +#[test] +fn fn_return_type_missing_thin_arrow_path_completion() { + check_edit( + "u32", + r#" +fn foo() u$0 +"#, + r#" +fn foo() -> u32 +"#, + ); + + check_edit( + "u32", + r#" +fn foo() $0 +"#, + r#" +fn foo() -> u32 +"#, + ); + + check_edit( + "Num", + r#" +type Num = u32; +fn foo() $0 +"#, + r#" +type Num = u32; +fn foo() -> Num +"#, + ); + + check_edit( + "impl", + r#" +fn foo() $0 +"#, + r#" +fn foo() -> impl $0 +"#, + ); + + check_edit( + "foo", + r#" +mod foo { pub type Num = u32; } +fn foo() $0 +"#, + r#" +mod foo { pub type Num = u32; } +fn foo() -> foo +"#, + ); + + check_edit( + "crate::", + r#" +mod foo { pub type Num = u32; } +fn foo() $0 +"#, + r#" +mod foo { pub type Num = u32; } +fn foo() -> crate:: +"#, + ); + + check_edit( + "Num", + r#" +mod foo { pub type Num = u32; } +fn foo() foo::$0 +"#, + r#" +mod foo { pub type Num = u32; } +fn foo() -> foo::Num +"#, + ); + + // no spaces, test edit order + check_edit( + "foo", + r#" +mod foo { pub type Num = u32; } +fn foo()$0 +"#, + r#" +mod foo { pub type Num = u32; } +fn foo() ->foo +"#, + ); +} + +#[test] +fn fn_return_type_missing_thin_arrow_path_completion_with_generic_args() { + check_edit( + "Foo", + r#" +struct Foo<T>(T); +fn foo() F$0 +"#, + r#" +struct Foo<T>(T); +fn foo() -> Foo<$0> +"#, + ); + + check_edit( + "Foo", + r#" +struct Foo<T>(T); +fn foo() $0 +"#, + r#" +struct Foo<T>(T); +fn foo() -> Foo<$0> +"#, + ); + + check_edit( + "Foo", + r#" +type Foo<T> = T; +fn foo() $0 +"#, + r#" +type Foo<T> = T; +fn foo() -> Foo<$0> +"#, + ); +} + +#[test] +fn fn_return_type_missing_thin_arrow_infer_ref_type() { + check_with_base_items( + r#" +fn x() u$0 {&2u32} +"#, + expect![[r#" + en Enum (adds ->) Enum + ma makro!(…) macro_rules! makro + md module (adds ->) + st Record (adds ->) Record + st Tuple (adds ->) Tuple + st Unit (adds ->) Unit + tt Trait (adds ->) + un Union (adds ->) Union + bt u32 (adds ->) u32 + it &u32 (adds ->) + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], + ); + + check_edit( + "&u32", + r#" +struct Foo<T>(T); +fn x() u$0 {&2u32} +"#, + r#" +struct Foo<T>(T); +fn x() -> &u32 {&2u32} +"#, + ); +} + +#[test] fn fn_return_type_after_reference() { check_with_base_items( r#" diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs index 9e6d586008..c25feceb41 100644 --- a/crates/ide-db/src/generated/lints.rs +++ b/crates/ide-db/src/generated/lints.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen lint-definitions`, do not edit by hand. +//! Generated by `cargo xtask codegen lint-definitions`, do not edit by hand. use span::Edition; @@ -20,8 +20,8 @@ pub struct LintGroup { pub const DEFAULT_LINTS: &[Lint] = &[ Lint { - label: "abi_unsupported_vector_types", - description: r##"this function call or definition uses a vector type which is not enabled"##, + label: "aarch64_softfloat_neon", + description: r##"detects code that could be affected by ABI issues on aarch64 softfloat targets"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, @@ -41,9 +41,23 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "ambiguous_derive_helpers", + description: r##"detects derive helper attributes that are ambiguous with built-in attributes"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { + label: "ambiguous_glob_imported_traits", + description: r##"detects uses of ambiguously glob imported traits"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "ambiguous_glob_imports", description: r##"detects certain glob imports that require reporting an ambiguity error"##, - default_severity: Severity::Warning, + default_severity: Severity::Error, warn_since: None, deny_since: None, }, @@ -55,6 +69,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "ambiguous_import_visibilities", + description: r##"detects certain glob imports that require reporting an ambiguity error"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "ambiguous_negative_literals", description: r##"ambiguous negative literals operations"##, default_severity: Severity::Allow, @@ -62,6 +83,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "ambiguous_panic_imports", + description: r##"detects ambiguous core and std panic imports"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "ambiguous_wide_pointer_comparisons", description: r##"detects ambiguous wide pointer comparisons"##, default_severity: Severity::Warning, @@ -146,13 +174,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "cenum_impl_drop_cast", - description: r##"a C-like enum implementing Drop is cast"##, - default_severity: Severity::Error, - warn_since: None, - deny_since: None, - }, - Lint { label: "clashing_extern_declarations", description: r##"detects when an extern fn has been declared with the same name but different types"##, default_severity: Severity::Warning, @@ -195,6 +216,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "const_item_interior_mutations", + description: r##"checks for calls which mutates a interior mutable const-item"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "const_item_mutation", description: r##"detects attempts to mutate a `const` item"##, default_severity: Severity::Warning, @@ -202,6 +230,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "dangerous_implicit_autorefs", + description: r##"implicit reference to a dereference of a raw pointer"##, + default_severity: Severity::Error, + warn_since: None, + deny_since: None, + }, + Lint { + label: "dangling_pointers_from_locals", + description: r##"detects returning a pointer from a local variable"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "dangling_pointers_from_temporaries", description: r##"detects getting a pointer from a temporary"##, default_severity: Severity::Warning, @@ -216,9 +258,16 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "default_overrides_default_fields", + description: r##"detect `Default` impl that should use the type's default field values"##, + default_severity: Severity::Error, + warn_since: None, + deny_since: None, + }, + Lint { label: "dependency_on_unit_never_type_fallback", description: r##"never type fallback affecting unsafe function calls"##, - default_severity: Severity::Warning, + default_severity: Severity::Error, warn_since: None, deny_since: None, }, @@ -252,14 +301,21 @@ pub const DEFAULT_LINTS: &[Lint] = &[ }, Lint { label: "deref_into_dyn_supertrait", - description: r##"`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future"##, - default_severity: Severity::Warning, + description: r##"`Deref` implementation with a supertrait trait object for output is shadowed by trait upcasting"##, + default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { label: "deref_nullptr", description: r##"detects when an null pointer is dereferenced"##, + default_severity: Severity::Error, + warn_since: None, + deny_since: None, + }, + Lint { + label: "double_negations", + description: r##"detects expressions of the form `--x`"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, @@ -286,6 +342,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "duplicate_features", + description: r##"duplicate features found in crate-level `#[feature]` directives"##, + default_severity: Severity::Error, + warn_since: None, + deny_since: None, + }, + Lint { label: "duplicate_macro_attributes", description: r##"duplicated attribute"##, default_severity: Severity::Warning, @@ -321,13 +384,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "elided_named_lifetimes", - description: r##"detects when an elided lifetime gets resolved to be `'static` or some named parameter"##, - default_severity: Severity::Warning, - warn_since: None, - deny_since: None, - }, - Lint { label: "ellipsis_inclusive_range_patterns", description: r##"`...` range patterns are deprecated"##, default_severity: Severity::Warning, @@ -398,6 +454,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "function_casts_as_integer", + description: r##"casting a function into an integer"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "function_item_references", description: r##"suggest casting to a function pointer when attempting to take references to function items"##, default_severity: Severity::Warning, @@ -442,7 +505,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "impl_trait_redundant_captures", description: r##"redundant precise-capturing `use<...>` syntax on an `impl Trait`"##, - default_severity: Severity::Warning, + default_severity: Severity::Allow, warn_since: None, deny_since: None, }, @@ -461,6 +524,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "improper_gpu_kernel_arg", + description: r##"GPU kernel entry points have a limited ABI"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "incomplete_features", description: r##"incomplete features that may function improperly in some or all cases"##, default_severity: Severity::Warning, @@ -482,8 +552,29 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "inline_always_mismatching_target_features", + description: r##"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "inline_no_sanitize", - description: r##"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"##, + description: r##"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { + label: "integer_to_ptr_transmutes", + description: r##"detects integer to pointer transmutes"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { + label: "internal_eq_trait_method_impls", + description: r##"manual implementation of the internal `Eq::assert_receiver_is_total_eq` method"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, @@ -505,7 +596,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "invalid_doc_attributes", description: r##"detects invalid `#[doc(...)]` attributes"##, - default_severity: Severity::Error, + default_severity: Severity::Warning, warn_since: None, deny_since: None, }, @@ -526,7 +617,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "invalid_macro_export_arguments", description: r##""invalid_parameter" isn't a valid argument for `#[macro_export]`"##, - default_severity: Severity::Warning, + default_severity: Severity::Error, warn_since: None, deny_since: None, }, @@ -538,6 +629,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "invalid_null_arguments", + description: r##"invalid null pointer in arguments"##, + default_severity: Severity::Error, + warn_since: None, + deny_since: None, + }, + Lint { label: "invalid_reference_casting", description: r##"casts of `&T` to `&mut T` without interior mutability"##, default_severity: Severity::Error, @@ -596,7 +694,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "legacy_derive_helpers", description: r##"detects derive helper attributes that are used before they are introduced"##, - default_severity: Severity::Warning, + default_severity: Severity::Error, warn_since: None, deny_since: None, }, @@ -615,6 +713,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "linker_info", + description: r##"linker warnings known to be informational-only and not indicative of a problem"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "linker_messages", + description: r##"warnings emitted at runtime by the target-specific linker program"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "long_running_const_eval", description: r##"detects long const eval operations"##, default_severity: Severity::Error, @@ -643,6 +755,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "malformed_diagnostic_attributes", + description: r##"detects malformed diagnostic attributes"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { + label: "malformed_diagnostic_format_literals", + description: r##"detects diagnostic attribute with malformed diagnostic format literals"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "map_unit_fn", description: r##"`Iterator::map` call that discard the iterator's values"##, default_severity: Severity::Warning, @@ -657,9 +783,23 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "mismatched_lifetime_syntaxes", + description: r##"detects when a lifetime uses different syntax between arguments and return values"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { + label: "misplaced_diagnostic_attributes", + description: r##"detects diagnostic attributes that are placed on the wrong item"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "missing_abi", description: r##"No declared ABI for extern declaration"##, - default_severity: Severity::Allow, + default_severity: Severity::Warning, warn_since: None, deny_since: None, }, @@ -685,9 +825,9 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "missing_fragment_specifier", - description: r##"detects missing fragment specifiers in unused `macro_rules!` patterns"##, - default_severity: Severity::Error, + label: "missing_gpu_kernel_export_name", + description: r##"mangled gpu-kernel function"##, + default_severity: Severity::Warning, warn_since: None, deny_since: None, }, @@ -743,9 +883,9 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "never_type_fallback_flowing_into_unsafe", description: r##"never type fallback affecting unsafe function calls"##, - default_severity: Severity::Warning, + default_severity: Severity::Error, warn_since: None, - deny_since: Some(Edition::Edition2024), + deny_since: None, }, Lint { label: "no_mangle_const_items", @@ -839,16 +979,9 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "order_dependent_trait_objects", - description: r##"trait-object types were treated as different depending on marker-trait order"##, - default_severity: Severity::Error, - warn_since: None, - deny_since: None, - }, - Lint { label: "out_of_scope_macro_calls", description: r##"detects out of scope calls to `macro_rules` in key-value attributes"##, - default_severity: Severity::Warning, + default_severity: Severity::Error, warn_since: None, deny_since: None, }, @@ -902,13 +1035,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "ptr_cast_add_auto_to_object", - description: r##"detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`"##, - default_severity: Severity::Warning, - warn_since: None, - deny_since: None, - }, - Lint { label: "ptr_to_integer_transmute_in_consts", description: r##"detects pointer to integer transmutes in const functions and associated constants"##, default_severity: Severity::Warning, @@ -965,8 +1091,29 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "repr_transparent_external_private_fields", + label: "repr_c_enums_larger_than_int", + description: r##"repr(C) enums with discriminant values that do not fit into a C int"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { + label: "repr_transparent_non_zst_fields", description: r##"transparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields"##, + default_severity: Severity::Error, + warn_since: None, + deny_since: None, + }, + Lint { + label: "resolving_to_items_shadowing_supertrait_items", + description: r##"detects when a supertrait item is shadowed by a subtrait item"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "rtsan_nonblocking_async", + description: r##"detects incompatible uses of `#[sanitize(realtime = "nonblocking")]` on async functions"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, @@ -1030,21 +1177,21 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "semicolon_in_expressions_from_macros", description: r##"trailing semicolon in macro body used as expression"##, - default_severity: Severity::Warning, + default_severity: Severity::Error, warn_since: None, deny_since: None, }, Lint { - label: "single_use_lifetimes", - description: r##"detects lifetime parameters that are only used once"##, + label: "shadowing_supertrait_items", + description: r##"detects when a supertrait item is shadowed by a subtrait item"##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "soft_unstable", - description: r##"a feature gate that doesn't break dependent crates"##, - default_severity: Severity::Error, + label: "single_use_lifetimes", + description: r##"detects lifetime parameters that are only used once"##, + default_severity: Severity::Allow, warn_since: None, deny_since: None, }, @@ -1064,7 +1211,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ }, Lint { label: "static_mut_refs", - description: r##"shared references or mutable references of mutable static is discouraged"##, + description: r##"creating a shared reference to mutable static"##, default_severity: Severity::Warning, warn_since: None, deny_since: Some(Edition::Edition2024), @@ -1168,13 +1315,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "undefined_naked_function_abi", - description: r##"undefined naked function ABI"##, - default_severity: Severity::Warning, - warn_since: None, - deny_since: None, - }, - Lint { label: "undropped_manually_drops", description: r##"calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of it's inner value"##, default_severity: Severity::Error, @@ -1224,15 +1364,15 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "unknown_lints", - description: r##"unrecognized lint attribute"##, + label: "unknown_diagnostic_attributes", + description: r##"detects unknown diagnostic attributes"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, }, Lint { - label: "unknown_or_malformed_diagnostic_attributes", - description: r##"unrecognized or malformed diagnostic attribute"##, + label: "unknown_lints", + description: r##"unrecognized lint attribute"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, @@ -1252,6 +1392,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "unnecessary_transmutes", + description: r##"detects transmutes that can also be achieved by other operations"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { + label: "unpredictable_function_pointer_comparisons", + description: r##"detects unpredictable function pointer comparisons"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "unqualified_local_imports", description: r##"`use` of a local item without leading `self::`, `super::`, or `crate::`"##, default_severity: Severity::Allow, @@ -1259,6 +1413,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "unreachable_cfg_select_predicates", + description: r##"detects unreachable configuration predicates in the cfg_select macro"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "unreachable_code", description: r##"detects unreachable code paths"##, default_severity: Severity::Warning, @@ -1322,8 +1483,8 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "unsupported_fn_ptr_calling_conventions", - description: r##"use of unsupported calling convention for function pointer"##, + label: "unsupported_calling_conventions", + description: r##"use of unsupported calling convention"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, @@ -1490,6 +1651,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "unused_visibilities", + description: r##"detect visibility qualifiers on `const _` items"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "useless_deprecated", description: r##"detects deprecation attributes with no effect"##, default_severity: Severity::Error, @@ -1504,6 +1672,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "uses_power_alignment", + description: r##"Structs do not follow the power alignment rule under repr(C)"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { + label: "varargs_without_pattern", + description: r##"detects usage of `...` arguments without a pattern in non-foreign items"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "variant_size_differences", description: r##"detects enums with widely varying variant sizes"##, default_severity: Severity::Allow, @@ -1518,13 +1700,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "wasm_c_abi", - description: r##"detects dependencies that are incompatible with the Wasm C ABI"##, - default_severity: Severity::Error, - warn_since: None, - deny_since: None, - }, - Lint { label: "while_true", description: r##"suggest using `loop { }` instead of `while true { }`"##, default_severity: Severity::Warning, @@ -1540,7 +1715,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ }, Lint { label: "future_incompatible", - description: r##"lint group for: deref-into-dyn-supertrait, abi-unsupported-vector-types, ambiguous-associated-items, ambiguous-glob-imports, cenum-impl-drop-cast, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, order-dependent-trait-objects, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, ptr-cast-add-auto-to-object, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, soft-unstable, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-fn-ptr-calling-conventions, wasm-c-abi"##, + description: r##"lint group for: internal-eq-trait-method-impls, aarch64-softfloat-neon, ambiguous-associated-items, ambiguous-derive-helpers, ambiguous-glob-imported-traits, ambiguous-glob-imports, ambiguous-import-visibilities, ambiguous-panic-imports, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, invalid-macro-export-arguments, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-c-enums-larger-than-int, repr-transparent-non-zst-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, varargs-without-pattern"##, default_severity: Severity::Allow, warn_since: None, deny_since: None, @@ -1602,8 +1777,15 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "unknown_or_malformed_diagnostic_attributes", + description: r##"lint group for: malformed-diagnostic-attributes, malformed-diagnostic-format-literals, misplaced-diagnostic-attributes, unknown-diagnostic-attributes"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "unused", - description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##, + description: r##"lint group for: unused-imports, unused-variables, unused-visibilities, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##, default_severity: Severity::Allow, warn_since: None, deny_since: None, @@ -1631,44 +1813,45 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "future_incompatible", - description: r##"lint group for: deref-into-dyn-supertrait, abi-unsupported-vector-types, ambiguous-associated-items, ambiguous-glob-imports, cenum-impl-drop-cast, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, order-dependent-trait-objects, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, ptr-cast-add-auto-to-object, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, soft-unstable, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-fn-ptr-calling-conventions, wasm-c-abi"##, + description: r##"lint group for: internal-eq-trait-method-impls, aarch64-softfloat-neon, ambiguous-associated-items, ambiguous-derive-helpers, ambiguous-glob-imported-traits, ambiguous-glob-imports, ambiguous-import-visibilities, ambiguous-panic-imports, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, invalid-macro-export-arguments, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-c-enums-larger-than-int, repr-transparent-non-zst-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, varargs-without-pattern"##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, children: &[ - "deref_into_dyn_supertrait", - "abi_unsupported_vector_types", + "internal_eq_trait_method_impls", + "aarch64_softfloat_neon", "ambiguous_associated_items", + "ambiguous_derive_helpers", + "ambiguous_glob_imported_traits", "ambiguous_glob_imports", - "cenum_impl_drop_cast", + "ambiguous_import_visibilities", + "ambiguous_panic_imports", "coherence_leak_check", "conflicting_repr_hints", "const_evaluatable_unchecked", "elided_lifetimes_in_associated_constant", "forbidden_lint_groups", "ill_formed_attribute_input", + "invalid_macro_export_arguments", "invalid_type_param_default", "late_bound_lifetime_arguments", "legacy_derive_helpers", "macro_expanded_macro_exports_accessed_by_absolute_paths", - "missing_fragment_specifier", - "order_dependent_trait_objects", "out_of_scope_macro_calls", "patterns_in_fns_without_body", "proc_macro_derive_resolution_fallback", - "ptr_cast_add_auto_to_object", "pub_use_of_private_extern_crate", - "repr_transparent_external_private_fields", + "repr_c_enums_larger_than_int", + "repr_transparent_non_zst_fields", "self_constructor_from_outer_item", "semicolon_in_expressions_from_macros", - "soft_unstable", "uncovered_param_in_projection", "uninhabited_static", "unstable_name_collisions", "unstable_syntax_pre_expansion", - "unsupported_fn_ptr_calling_conventions", - "wasm_c_abi", + "unsupported_calling_conventions", + "varargs_without_pattern", ], }, LintGroup { @@ -1790,8 +1973,23 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ }, LintGroup { lint: Lint { + label: "unknown_or_malformed_diagnostic_attributes", + description: r##"lint group for: malformed-diagnostic-attributes, malformed-diagnostic-format-literals, misplaced-diagnostic-attributes, unknown-diagnostic-attributes"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + children: &[ + "malformed_diagnostic_attributes", + "malformed_diagnostic_format_literals", + "misplaced_diagnostic_attributes", + "unknown_diagnostic_attributes", + ], + }, + LintGroup { + lint: Lint { label: "unused", - description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##, + description: r##"lint group for: unused-imports, unused-variables, unused-visibilities, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##, default_severity: Severity::Allow, warn_since: None, deny_since: None, @@ -1799,6 +1997,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ children: &[ "unused_imports", "unused_variables", + "unused_visibilities", "unused_assignments", "dead_code", "unused_mut", @@ -1902,15 +2101,8 @@ pub const RUSTDOC_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "rustdoc::unportable_markdown", - description: r##"detects markdown that is interpreted differently in different parser"##, - default_severity: Severity::Warning, - warn_since: None, - deny_since: None, - }, - Lint { label: "rustdoc::all", - description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links, rustdoc::unportable-markdown"##, + description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links"##, default_severity: Severity::Allow, warn_since: None, deny_since: None, @@ -1920,7 +2112,7 @@ pub const RUSTDOC_LINTS: &[Lint] = &[ pub const RUSTDOC_LINT_GROUPS: &[LintGroup] = &[LintGroup { lint: Lint { label: "rustdoc::all", - description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links, rustdoc::unportable-markdown"##, + description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links"##, default_severity: Severity::Allow, warn_since: None, deny_since: None, @@ -1936,7 +2128,6 @@ pub const RUSTDOC_LINT_GROUPS: &[LintGroup] = &[LintGroup { "rustdoc::missing_crate_level_docs", "rustdoc::unescaped_backticks", "rustdoc::redundant_explicit_links", - "rustdoc::unportable_markdown", ], }]; @@ -1945,9 +2136,11 @@ pub const FEATURES: &[Lint] = &[ label: "aarch64_unstable_target_feature", description: r##"# `aarch64_unstable_target_feature` -The tracking issue for this feature is: [#44839] +The remaining unstable target features on aarch64. -[#44839]: https://github.com/rust-lang/rust/issues/44839 +The tracking issue for this feature is: [#150244] + +[#150244]: https://github.com/rust-lang/rust/issues/150244 ------------------------ "##, @@ -1959,9 +2152,11 @@ The tracking issue for this feature is: [#44839] label: "aarch64_ver_target_feature", description: r##"# `aarch64_ver_target_feature` -The tracking issue for this feature is: [#44839] +Instruction set "version" target features on aarch64. + +The tracking issue for this feature is: [#150245] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +[#150245]: https://github.com/rust-lang/rust/issues/150245 ------------------------ "##, @@ -1973,6 +2168,8 @@ The tracking issue for this feature is: [#44839] label: "abi_avr_interrupt", description: r##"# `abi_avr_interrupt` +Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`. + The tracking issue for this feature is: [#69664] [#69664]: https://github.com/rust-lang/rust/issues/69664 @@ -1984,8 +2181,8 @@ The tracking issue for this feature is: [#69664] deny_since: None, }, Lint { - label: "abi_c_cmse_nonsecure_call", - description: r##"# `abi_c_cmse_nonsecure_call` + label: "abi_cmse_nonsecure_call", + description: r##"# `abi_cmse_nonsecure_call` The tracking issue for this feature is: [#81391] @@ -2001,10 +2198,9 @@ LLVM, the Rust compiler and the linker are providing [support](https://developer.arm.com/documentation/ecm0359818/latest/) for the TrustZone-M feature. -One of the things provided, with this unstable feature, is the -`C-cmse-nonsecure-call` function ABI. This ABI is used on function pointers to -non-secure code to mark a non-secure function call (see [section -5.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details). +One of the things provided with this unstable feature is the "cmse-nonsecure-call" function ABI. +This ABI is used on function pointers to non-secure code to mark a non-secure function call +(see [section 5.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details). With this ABI, the compiler will do the following to perform the call: * save registers needed after the call to Secure memory @@ -2015,19 +2211,16 @@ With this ABI, the compiler will do the following to perform the call: To avoid using the non-secure stack, the compiler will constrain the number and type of parameters/return value. -The `extern "C-cmse-nonsecure-call"` ABI is otherwise equivalent to the -`extern "C"` ABI. - <!-- NOTE(ignore) this example is specific to thumbv8m targets --> ``` rust,ignore #![no_std] -#![feature(abi_c_cmse_nonsecure_call)] +#![feature(abi_cmse_nonsecure_call)] #[no_mangle] pub fn call_nonsecure_function(addr: usize) -> u32 { let non_secure_function = - unsafe { core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn() -> u32>(addr) }; + unsafe { core::mem::transmute::<usize, extern "cmse-nonsecure-call" fn() -> u32>(addr) }; non_secure_function() } ``` @@ -2079,6 +2272,38 @@ call_nonsecure_function: deny_since: None, }, Lint { + label: "abi_custom", + description: r##"# `abi_custom` + +Allows `extern "custom" fn()`. + +The tracking issue for this feature is: [#140829] + +[#140829]: https://github.com/rust-lang/rust/issues/140829 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "abi_gpu_kernel", + description: r##"# `abi_gpu_kernel` + +Allows `extern "gpu-kernel" fn()`. + +The tracking issue for this feature is: [#135467] + +[#135467]: https://github.com/rust-lang/rust/issues/135467 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "abi_msp430_interrupt", description: r##"# `abi_msp430_interrupt` @@ -2198,6 +2423,8 @@ $ cat $(find -name '*.s') label: "abi_riscv_interrupt", description: r##"# `abi_riscv_interrupt` +Allows `extern "riscv-interrupt-m" fn()` and `extern "riscv-interrupt-s" fn()`. + The tracking issue for this feature is: [#111889] [#111889]: https://github.com/rust-lang/rust/issues/111889 @@ -2212,6 +2439,8 @@ The tracking issue for this feature is: [#111889] label: "abi_unadjusted", description: r##"# `abi_unadjusted` +Allows using the `unadjusted` ABI; perma-unstable. + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -2250,6 +2479,8 @@ fn main() { label: "abi_x86_interrupt", description: r##"# `abi_x86_interrupt` +Allows `extern "x86-interrupt" fn()`. + The tracking issue for this feature is: [#40180] [#40180]: https://github.com/rust-lang/rust/issues/40180 @@ -2264,6 +2495,8 @@ The tracking issue for this feature is: [#40180] label: "abort_unwind", description: r##"# `abort_unwind` + + The tracking issue for this feature is: [#130338] [#130338]: https://github.com/rust-lang/rust/issues/130338 @@ -2278,6 +2511,8 @@ The tracking issue for this feature is: [#130338] label: "acceptfilter", description: r##"# `acceptfilter` + + The tracking issue for this feature is: [#121891] [#121891]: https://github.com/rust-lang/rust/issues/121891 @@ -2292,6 +2527,8 @@ The tracking issue for this feature is: [#121891] label: "addr_parse_ascii", description: r##"# `addr_parse_ascii` + + The tracking issue for this feature is: [#101035] [#101035]: https://github.com/rust-lang/rust/issues/101035 @@ -2345,9 +2582,27 @@ fn is_foo_a_and_bar_true<const F: Foo, const B: Bar>() -> bool { deny_since: None, }, Lint { + label: "align_to_uninit_mut", + description: r##"# `align_to_uninit_mut` + + + +The tracking issue for this feature is: [#139062] + +[#139062]: https://github.com/rust-lang/rust/issues/139062 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "alloc_error_handler", description: r##"# `alloc_error_handler` +Allows defining an `#[alloc_error_handler]`. + The tracking issue for this feature is: [#51540] [#51540]: https://github.com/rust-lang/rust/issues/51540 @@ -2362,6 +2617,8 @@ The tracking issue for this feature is: [#51540] label: "alloc_error_hook", description: r##"# `alloc_error_hook` + + The tracking issue for this feature is: [#51245] [#51245]: https://github.com/rust-lang/rust/issues/51245 @@ -2376,6 +2633,8 @@ The tracking issue for this feature is: [#51245] label: "alloc_internals", description: r##"# `alloc_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -2385,12 +2644,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "alloc_layout_extra", - description: r##"# `alloc_layout_extra` + label: "alloc_slice_into_array", + description: r##"# `alloc_slice_into_array` + -The tracking issue for this feature is: [#55724] -[#55724]: https://github.com/rust-lang/rust/issues/55724 +The tracking issue for this feature is: [#148082] + +[#148082]: https://github.com/rust-lang/rust/issues/148082 ------------------------ "##, @@ -2435,9 +2696,25 @@ compiler. deny_since: None, }, Lint { + label: "alloctests", + description: r##"# `alloctests` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "allow_internal_unsafe", description: r##"# `allow_internal_unsafe` +Allows using `#[allow_internal_unsafe]`. This is an attribute on `macro_rules!` and can't use the attribute handling below (it has to be checked before expansion possibly makes macros disappear). + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -2450,6 +2727,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "allow_internal_unstable", description: r##"# `allow_internal_unstable` +Allows using `#[allow_internal_unstable]`. This is an attribute on `macro_rules!` and can't use the attribute handling below (it has to be checked before expansion possibly makes macros disappear). + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -2462,6 +2741,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "anonymous_lifetime_in_impl_trait", description: r##"# `anonymous_lifetime_in_impl_trait` +Allows using anonymous lifetimes in argument-position impl-trait. + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -2471,12 +2752,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "anonymous_pipe", - description: r##"# `anonymous_pipe` + label: "apx_target_feature", + description: r##"# `apx_target_feature` -The tracking issue for this feature is: [#127154] +The `apxf` target feature on x86 -[#127154]: https://github.com/rust-lang/rust/issues/127154 +The tracking issue for this feature is: [#139284] + +[#139284]: https://github.com/rust-lang/rust/issues/139284 ------------------------ "##, @@ -2493,6 +2776,153 @@ The tracking issue for this feature is: [#44874] [#44874]: https://github.com/rust-lang/rust/issues/44874 ------------------------ + +Allows any type implementing `core::ops::Receiver<Target=T>` to be used as the type +of `self` in a method belonging to `T`. + +For example, + +```rust +#![feature(arbitrary_self_types)] + +struct A; + +impl A { + fn f(self: SmartPtr<Self>) -> i32 { 1 } // note self type +} + +struct SmartPtr<T>(T); + +impl<T> core::ops::Receiver for SmartPtr<T> { + type Target = T; +} + +fn main() { + let smart_ptr = SmartPtr(A); + assert_eq!(smart_ptr.f(), 1); +} +``` + +The `Receiver` trait has a blanket implementation for all `T: Deref`, so in fact +things like this work too: + +```rust +#![feature(arbitrary_self_types)] + +use std::rc::Rc; + +struct A; + +impl A { + fn f(self: Rc<Self>) -> i32 { 1 } // Rc implements Deref +} + +fn main() { + let smart_ptr = Rc::new(A); + assert_eq!(smart_ptr.f(), 1); +} +``` + +Interestingly, that works even without the `arbitrary_self_types` feature +- but that's because certain types are _effectively_ hard coded, including +`Rc`. ("Hard coding" isn't quite true; they use a lang-item called +`LegacyReceiver` to denote their special-ness in this way). With the +`arbitrary_self_types` feature, their special-ness goes away, and custom +smart pointers can achieve the same. + +## Changes to method lookup + +Method lookup previously used to work by stepping through the `Deref` +chain then using the resulting list of steps in two different ways: + +* To identify types that might contribute methods via their `impl` + blocks (inherent methods) or via traits +* To identify the types that the method receiver (`a` in the above + examples) can be converted to. + +With this feature, these lists are created by instead stepping through +the `Receiver` chain. However, a note is kept about whether the type +can be reached also via the `Deref` chain. + +The full chain (via `Receiver` hops) is used for the first purpose +(identifying relevant `impl` blocks and traits); whereas the shorter +list (reachable via `Deref`) is used for the second purpose. That's +because, to convert the method target (`a` in `a.b()`) to the self +type, Rust may need to be able to use `Deref::deref`. Type conversions, +then, can only proceed as far as the end of the `Deref` chain whereas +the longer `Receiver` chain can be used to explore more places where +useful methods might reside. + +## Types suitable for use as smart pointers + +This feature allows the creation of customised smart pointers - for example +your own equivalent to `Rc` or `Box` with whatever capabilities you like. +Those smart pointers can either implement `Deref` (if it's safe to +create a reference to the referent) or `Receiver` (if it isn't). + +Either way, smart pointer types should mostly _avoid having methods_. +Calling methods on a smart pointer leads to ambiguity about whether you're +aiming for a method on the pointer, or on the referent. + +Best practice is therefore to put smart pointer functionality into +associated functions instead - that's what's done in all the smart pointer +types within Rust's standard library which implement `Receiver`. + +If you choose to add any methods to your smart pointer type, your users +may run into errors from deshadowing, as described in the next section. + +## Avoiding shadowing + +With or without this feature, Rust emits an error if it finds two method +candidates, like this: + +```rust,compile_fail +use std::pin::Pin; +use std::pin::pin; + +struct A; + +impl A { + fn get_ref(self: Pin<&A>) {} +} + +fn main() { + let pinned_a: Pin<&A> = pin!(A).as_ref(); + let pinned_a: Pin<&A> = pinned_a.as_ref(); + pinned_a.get_ref(); // error[E0034]: multiple applicable items in scope +} +``` + +(this is why Rust's smart pointers are mostly carefully designed to avoid +having methods at all, and shouldn't add new methods in future.) + +With `arbitrary_self_types`, we take care to spot some other kinds of +conflict: + +```rust,compile_fail +#![feature(arbitrary_self_types)] + +use std::pin::Pin; +use std::pin::pin; + +struct A; + +impl A { + fn get_ref(self: &Pin<&A>) {} // note &Pin +} + +fn main() { + let pinned_a: Pin<&mut A> = pin!(A); + let pinned_a: Pin<&A> = pinned_a.as_ref(); + pinned_a.get_ref(); +} +``` + +This is to guard against the case where an inner (referent) type has a +method of a given name, taking the smart pointer by reference, and then +the smart pointer implementer adds a similar method taking self by value. +As noted in the previous section, the safe option is simply +not to add methods to smart pointers, and then these errors can't occur. "##, default_severity: Severity::Allow, warn_since: None, @@ -2504,21 +2934,73 @@ The tracking issue for this feature is: [#44874] The tracking issue for this feature is: [#44874] -[#44874]: https://github.com/rust-lang/rust/issues/44874 +[#38788]: https://github.com/rust-lang/rust/issues/44874 ------------------------ + +This extends the [arbitrary self types] feature to allow methods to +receive `self` by pointer. For example: + +```rust +#![feature(arbitrary_self_types_pointers)] + +struct A; + +impl A { + fn m(self: *const Self) {} +} + +fn main() { + let a = A; + let a_ptr: *const A = &a as *const A; + a_ptr.m(); +} +``` + +In general this is not advised: it's thought to be better practice to wrap +raw pointers in a newtype wrapper which implements the `core::ops::Receiver` +trait, then you need "only" the `arbitrary_self_types` feature. For example: + +```rust +#![feature(arbitrary_self_types)] +#![allow(dead_code)] + +struct A; + +impl A { + fn m(self: Wrapper<Self>) {} // can extract the pointer and do + // what it needs +} + +struct Wrapper<T>(*const T); + +impl<T> core::ops::Receiver for Wrapper<T> { + type Target = T; +} + +fn main() { + let a = A; + let a_ptr: *const A = &a as *const A; + let a_wrapper = Wrapper(a_ptr); + a_wrapper.m(); +} +``` + +[arbitrary self types]: arbitrary-self-types.md "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "arm_target_feature", - description: r##"# `arm_target_feature` + label: "arc_is_unique", + description: r##"# `arc_is_unique` -The tracking issue for this feature is: [#44839] -[#44839]: https://github.com/rust-lang/rust/issues/44839 + +The tracking issue for this feature is: [#138938] + +[#138938]: https://github.com/rust-lang/rust/issues/138938 ------------------------ "##, @@ -2527,12 +3009,14 @@ The tracking issue for this feature is: [#44839] deny_since: None, }, Lint { - label: "array_chunks", - description: r##"# `array_chunks` + label: "arm_target_feature", + description: r##"# `arm_target_feature` -The tracking issue for this feature is: [#74985] +Target features on arm. -[#74985]: https://github.com/rust-lang/rust/issues/74985 +The tracking issue for this feature is: [#150246] + +[#150246]: https://github.com/rust-lang/rust/issues/150246 ------------------------ "##, @@ -2544,6 +3028,8 @@ The tracking issue for this feature is: [#74985] label: "array_into_iter_constructors", description: r##"# `array_into_iter_constructors` + + The tracking issue for this feature is: [#91583] [#91583]: https://github.com/rust-lang/rust/issues/91583 @@ -2558,23 +3044,11 @@ The tracking issue for this feature is: [#91583] label: "array_ptr_get", description: r##"# `array_ptr_get` -The tracking issue for this feature is: [#119834] - -[#119834]: https://github.com/rust-lang/rust/issues/119834 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "array_repeat", - description: r##"# `array_repeat` -The tracking issue for this feature is: [#126695] +The tracking issue for this feature is: [#119834] -[#126695]: https://github.com/rust-lang/rust/issues/126695 +[#119834]: https://github.com/rust-lang/rust/issues/119834 ------------------------ "##, @@ -2586,6 +3060,8 @@ The tracking issue for this feature is: [#126695] label: "array_try_from_fn", description: r##"# `array_try_from_fn` + + The tracking issue for this feature is: [#89379] [#89379]: https://github.com/rust-lang/rust/issues/89379 @@ -2600,37 +3076,11 @@ The tracking issue for this feature is: [#89379] label: "array_try_map", description: r##"# `array_try_map` -The tracking issue for this feature is: [#79711] - -[#79711]: https://github.com/rust-lang/rust/issues/79711 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "array_windows", - description: r##"# `array_windows` - -The tracking issue for this feature is: [#75027] - -[#75027]: https://github.com/rust-lang/rust/issues/75027 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "as_array_of_cells", - description: r##"# `as_array_of_cells` -The tracking issue for this feature is: [#88248] +The tracking issue for this feature is: [#79711] -[#88248]: https://github.com/rust-lang/rust/issues/88248 +[#79711]: https://github.com/rust-lang/rust/issues/79711 ------------------------ "##, @@ -2642,6 +3092,8 @@ The tracking issue for this feature is: [#88248] label: "ascii_char", description: r##"# `ascii_char` + + The tracking issue for this feature is: [#110998] [#110998]: https://github.com/rust-lang/rust/issues/110998 @@ -2656,6 +3108,8 @@ The tracking issue for this feature is: [#110998] label: "ascii_char_variants", description: r##"# `ascii_char_variants` + + The tracking issue for this feature is: [#110998] [#110998]: https://github.com/rust-lang/rust/issues/110998 @@ -2678,7 +3132,6 @@ The tracking issue for this feature is: [#93335] This feature tracks `asm!` and `global_asm!` support for the following architectures: - NVPTX -- PowerPC - Hexagon - MIPS32r2 and MIPS64r2 - wasm32 @@ -2701,12 +3154,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | NVPTX | `reg64` | None\* | `l` | | Hexagon | `reg` | `r[0-28]` | `r` | | Hexagon | `preg` | `p[0-3]` | Only clobbers | -| PowerPC | `reg` | `r0`, `r[3-12]`, `r[14-28]` | `r` | -| PowerPC | `reg_nonzero` | `r[3-12]`, `r[14-28]` | `b` | -| PowerPC | `freg` | `f[0-31]` | `f` | -| PowerPC | `vreg` | `v[0-31]` | `v` | -| PowerPC | `cr` | `cr[0-7]`, `cr` | Only clobbers | -| PowerPC | `xer` | `xer` | Only clobbers | | wasm32 | `local` | None\* | `r` | | BPF | `reg` | `r[0-10]` | `r` | | BPF | `wreg` | `w[0-10]` | `w` | @@ -2742,13 +3189,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | | Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | | Hexagon | `preg` | N/A | Only clobbers | -| PowerPC | `reg` | None | `i8`, `i16`, `i32`, `i64` (powerpc64 only) | -| PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32`, `i64` (powerpc64 only) | -| PowerPC | `freg` | None | `f32`, `f64` | -| PowerPC | `vreg` | `altivec` | `i8x16`, `i16x8`, `i32x4`, `f32x4` | -| PowerPC | `vreg` | `vsx` | `f32`, `f64`, `i64x2`, `f64x2` | -| PowerPC | `cr` | N/A | Only clobbers | -| PowerPC | `xer` | N/A | Only clobbers | | wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` | | BPF | `reg` | None | `i8` `i16` `i32` `i64` | | BPF | `wreg` | `alu32` | `i8` `i16` `i32` | @@ -2769,10 +3209,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | Hexagon | `r29` | `sp` | | Hexagon | `r30` | `fr` | | Hexagon | `r31` | `lr` | -| PowerPC | `r1` | `sp` | -| PowerPC | `r31` | `fp` | -| PowerPC | `r[0-31]` | `[0-31]` | -| PowerPC | `f[0-31]` | `fr[0-31]`| | BPF | `r[0-10]` | `w[0-10]` | | AVR | `XH` | `r27` | | AVR | `XL` | `r26` | @@ -2811,18 +3247,14 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | Architecture | Unsupported register | Reason | | ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | All | `sp`, `r14`/`o6` (SPARC) | The stack pointer must be restored to its original value at the end of an asm code block. | -| All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC) | The frame pointer cannot be used as an input or output. | -| All | `r19` (Hexagon), `r29` (PowerPC), `r30` (PowerPC) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. | +| All | `fr` (Hexagon) `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC) | The frame pointer cannot be used as an input or output. | +| All | `r19` (Hexagon) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. | | MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | | MIPS | `$1` or `$at` | Reserved for assembler. | | MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | | MIPS | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. | | MIPS | `$ra` | Return address cannot be used as inputs or outputs. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | -| PowerPC | `r2`, `r13` | These are system reserved registers. | -| PowerPC | `lr` | The link register cannot be used as an input or output. | -| PowerPC | `ctr` | The counter register cannot be used as an input or output. | -| PowerPC | `vrsave` | The vrsave register cannot be used as an input or output. | | AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. | |MSP430 | `r0`, `r2`, `r3` | These are the program counter, status register, and constant generator respectively. Neither the status register nor constant generator can be written to. | | M68k | `a4`, `a5` | Used internally by LLVM for the base pointer and global base pointer. | @@ -2849,10 +3281,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | NVPTX | `reg32` | None | `r0` | None | | NVPTX | `reg64` | None | `rd0` | None | | Hexagon | `reg` | None | `r0` | None | -| PowerPC | `reg` | None | `0` | None | -| PowerPC | `reg_nonzero` | None | `3` | None | -| PowerPC | `freg` | None | `0` | None | -| PowerPC | `vreg` | None | `0` | None | | SPARC | `reg` | None | `%o0` | None | | CSKY | `reg` | None | `r0` | None | | CSKY | `freg` | None | `f0` | None | @@ -2869,6 +3297,8 @@ These flags registers must be restored upon exiting the asm block if the `preser - SPARC - Integer condition codes (`icc` and `xcc`) - Floating-point condition codes (`fcc[0-3]`) +- CSKY + - Condition/carry bit (C) in `PSR`. "##, default_severity: Severity::Allow, warn_since: None, @@ -2890,16 +3320,14 @@ This tracks support for additional registers in architectures where inline assem | Architecture | Register class | Registers | LLVM constraint code | | ------------ | -------------- | --------- | -------------------- | -| s390x | `vreg` | `v[0-31]` | `v` | - -> **Notes**: -> - s390x `vreg` is clobber-only in stable. ## Register class supported types | Architecture | Register class | Target feature | Allowed types | | ------------ | -------------- | -------------- | ------------- | -| s390x | `vreg` | `vector` | `i32`, `f32`, `i64`, `f64`, `i128`, `f128`, `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +| x86 | `xmm_reg` | `sse` | `i128` | +| x86 | `ymm_reg` | `avx` | `i128` | +| x86 | `zmm_reg` | `avx512f` | `i128` | ## Register aliases @@ -2915,15 +3343,14 @@ This tracks support for additional registers in architectures where inline assem | Architecture | Register class | Modifier | Example output | LLVM modifier | | ------------ | -------------- | -------- | -------------- | ------------- | -| s390x | `vreg` | None | `%v0` | None | "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "asm_goto", - description: r##"# `asm_goto` + label: "asm_goto_with_outputs", + description: r##"# `asm_goto_with_outputs` The tracking issue for this feature is: [#119364] @@ -2931,44 +3358,25 @@ The tracking issue for this feature is: [#119364] ------------------------ -This feature adds a `label <block>` operand type to `asm!`. +This feature allows label operands to be used together with output operands. Example: ```rust,ignore (partial-example, x86-only) unsafe { + let a: usize; asm!( + "mov {}, 1" "jmp {}", + out(reg) a, label { - println!("Jumped from asm!"); + println!("Jumped from asm {}!", a); } ); } ``` -The block must have unit type or diverge. The block starts a new safety context, -so despite outer `unsafe`, you need extra unsafe to perform unsafe operations -within `label <block>`. - -When `label <block>` is used together with `noreturn` option, it means that the -assembly will not fallthrough. It's allowed to jump to a label within the -assembly. In this case, the entire `asm!` expression will have an unit type as -opposed to diverging, if not all label blocks diverge. The `asm!` expression -still diverges if `noreturn` option is used and all label blocks diverge. -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "asm_goto_with_outputs", - description: r##"# `asm_goto_with_outputs` - -The tracking issue for this feature is: [#119364] - -[#119364]: https://github.com/rust-lang/rust/issues/119364 - ------------------------- +The output operands are assigned before the label blocks are executed. "##, default_severity: Severity::Allow, warn_since: None, @@ -2991,37 +3399,11 @@ This feature adds a `may_unwind` option to `asm!` which allows an `asm` block to deny_since: None, }, Lint { - label: "assert_matches", - description: r##"# `assert_matches` - -The tracking issue for this feature is: [#82775] - -[#82775]: https://github.com/rust-lang/rust/issues/82775 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "associated_const_equality", - description: r##"# `associated_const_equality` - -The tracking issue for this feature is: [#92827] - -[#92827]: https://github.com/rust-lang/rust/issues/92827 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "associated_type_defaults", description: r##"# `associated_type_defaults` +Allows associated type defaults. + The tracking issue for this feature is: [#29661] [#29661]: https://github.com/rust-lang/rust/issues/29661 @@ -3033,12 +3415,14 @@ The tracking issue for this feature is: [#29661] deny_since: None, }, Lint { - label: "async_closure", - description: r##"# `async_closure` + label: "async_drop", + description: r##"# `async_drop` -The tracking issue for this feature is: [#62290] +Allows implementing `AsyncDrop`. -[#62290]: https://github.com/rust-lang/rust/issues/62290 +The tracking issue for this feature is: [#126482] + +[#126482]: https://github.com/rust-lang/rust/issues/126482 ------------------------ "##, @@ -3047,12 +3431,14 @@ The tracking issue for this feature is: [#62290] deny_since: None, }, Lint { - label: "async_drop", - description: r##"# `async_drop` + label: "async_fn_in_dyn_trait", + description: r##"# `async_fn_in_dyn_trait` -The tracking issue for this feature is: [#126482] +Allows async functions to be called from `dyn Trait`. -[#126482]: https://github.com/rust-lang/rust/issues/126482 +The tracking issue for this feature is: [#133119] + +[#133119]: https://github.com/rust-lang/rust/issues/133119 ------------------------ "##, @@ -3064,6 +3450,8 @@ The tracking issue for this feature is: [#126482] label: "async_fn_track_caller", description: r##"# `async_fn_track_caller` +Allows `#[track_caller]` on async functions. + The tracking issue for this feature is: [#110011] [#110011]: https://github.com/rust-lang/rust/issues/110011 @@ -3098,6 +3486,8 @@ that borrows from itself (`FnOnce::Output` has no lifetime parameters, while `As label: "async_for_loop", description: r##"# `async_for_loop` +Allows `for await` loops. + The tracking issue for this feature is: [#118898] [#118898]: https://github.com/rust-lang/rust/issues/118898 @@ -3112,6 +3502,8 @@ The tracking issue for this feature is: [#118898] label: "async_gen_internals", description: r##"# `async_gen_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -3124,6 +3516,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "async_iter_from_iter", description: r##"# `async_iter_from_iter` + + The tracking issue for this feature is: [#81798] [#81798]: https://github.com/rust-lang/rust/issues/81798 @@ -3138,6 +3532,8 @@ The tracking issue for this feature is: [#81798] label: "async_iterator", description: r##"# `async_iterator` + + The tracking issue for this feature is: [#79024] [#79024]: https://github.com/rust-lang/rust/issues/79024 @@ -3152,6 +3548,8 @@ The tracking issue for this feature is: [#79024] label: "async_trait_bounds", description: r##"# `async_trait_bounds` +Allows `async` trait bound modifier. + The tracking issue for this feature is: [#62290] [#62290]: https://github.com/rust-lang/rust/issues/62290 @@ -3166,6 +3564,8 @@ The tracking issue for this feature is: [#62290] label: "atomic_from_mut", description: r##"# `atomic_from_mut` + + The tracking issue for this feature is: [#76314] [#76314]: https://github.com/rust-lang/rust/issues/76314 @@ -3177,6 +3577,36 @@ The tracking issue for this feature is: [#76314] deny_since: None, }, Lint { + label: "atomic_internals", + description: r##"# `atomic_internals` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "atomic_ptr_null", + description: r##"# `atomic_ptr_null` + + + +The tracking issue for this feature is: [#150733] + +[#150733]: https://github.com/rust-lang/rust/issues/150733 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "auto_traits", description: r##"# `auto_traits` @@ -3293,6 +3723,8 @@ Auto traits cannot have supertraits. This is for soundness reasons, as the inter label: "autodiff", description: r##"# `autodiff` + + The tracking issue for this feature is: [#124509] [#124509]: https://github.com/rust-lang/rust/issues/124509 @@ -3304,12 +3736,30 @@ The tracking issue for this feature is: [#124509] deny_since: None, }, Lint { - label: "avx512_target_feature", - description: r##"# `avx512_target_feature` + label: "avr_target_feature", + description: r##"# `avr_target_feature` -The tracking issue for this feature is: [#44839] +Target features on avr. -[#44839]: https://github.com/rust-lang/rust/issues/44839 +The tracking issue for this feature is: [#146889] + +[#146889]: https://github.com/rust-lang/rust/issues/146889 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "avx10_target_feature", + description: r##"# `avx10_target_feature` + +Allows using Intel AVX10 target features and intrinsics + +The tracking issue for this feature is: [#138843] + +[#138843]: https://github.com/rust-lang/rust/issues/138843 ------------------------ "##, @@ -3321,6 +3771,8 @@ The tracking issue for this feature is: [#44839] label: "backtrace_frames", description: r##"# `backtrace_frames` + + The tracking issue for this feature is: [#79676] [#79676]: https://github.com/rust-lang/rust/issues/79676 @@ -3332,12 +3784,28 @@ The tracking issue for this feature is: [#79676] deny_since: None, }, Lint { - label: "bigint_helper_methods", - description: r##"# `bigint_helper_methods` + label: "bikeshed_guaranteed_no_drop", + description: r##"# `bikeshed_guaranteed_no_drop` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "binary_heap_as_mut_slice", + description: r##"# `binary_heap_as_mut_slice` -The tracking issue for this feature is: [#85532] -[#85532]: https://github.com/rust-lang/rust/issues/85532 + +The tracking issue for this feature is: [#154009] + +[#154009]: https://github.com/rust-lang/rust/issues/154009 ------------------------ "##, @@ -3349,6 +3817,8 @@ The tracking issue for this feature is: [#85532] label: "binary_heap_drain_sorted", description: r##"# `binary_heap_drain_sorted` + + The tracking issue for this feature is: [#59278] [#59278]: https://github.com/rust-lang/rust/issues/59278 @@ -3360,9 +3830,27 @@ The tracking issue for this feature is: [#59278] deny_since: None, }, Lint { + label: "binary_heap_from_raw_vec", + description: r##"# `binary_heap_from_raw_vec` + + + +The tracking issue for this feature is: [#152500] + +[#152500]: https://github.com/rust-lang/rust/issues/152500 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "binary_heap_into_iter_sorted", description: r##"# `binary_heap_into_iter_sorted` + + The tracking issue for this feature is: [#59278] [#59278]: https://github.com/rust-lang/rust/issues/59278 @@ -3374,9 +3862,75 @@ The tracking issue for this feature is: [#59278] deny_since: None, }, Lint { + label: "binary_heap_peek_mut_refresh", + description: r##"# `binary_heap_peek_mut_refresh` + + + +The tracking issue for this feature is: [#138355] + +[#138355]: https://github.com/rust-lang/rust/issues/138355 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "binary_heap_pop_if", + description: r##"# `binary_heap_pop_if` + + + +The tracking issue for this feature is: [#151828] + +[#151828]: https://github.com/rust-lang/rust/issues/151828 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "bool_to_result", + description: r##"# `bool_to_result` + + + +The tracking issue for this feature is: [#142748] + +[#142748]: https://github.com/rust-lang/rust/issues/142748 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "borrowed_buf_init", + description: r##"# `borrowed_buf_init` + + + +The tracking issue for this feature is: [#78485] + +[#78485]: https://github.com/rust-lang/rust/issues/78485 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "bound_as_ref", description: r##"# `bound_as_ref` + + The tracking issue for this feature is: [#80996] [#80996]: https://github.com/rust-lang/rust/issues/80996 @@ -3388,9 +3942,27 @@ The tracking issue for this feature is: [#80996] deny_since: None, }, Lint { + label: "bound_copied", + description: r##"# `bound_copied` + + + +The tracking issue for this feature is: [#145966] + +[#145966]: https://github.com/rust-lang/rust/issues/145966 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "box_as_ptr", description: r##"# `box_as_ptr` + + The tracking issue for this feature is: [#129090] [#129090]: https://github.com/rust-lang/rust/issues/129090 @@ -3405,6 +3977,8 @@ The tracking issue for this feature is: [#129090] label: "box_into_boxed_slice", description: r##"# `box_into_boxed_slice` + + The tracking issue for this feature is: [#71582] [#71582]: https://github.com/rust-lang/rust/issues/71582 @@ -3419,6 +3993,8 @@ The tracking issue for this feature is: [#71582] label: "box_into_inner", description: r##"# `box_into_inner` + + The tracking issue for this feature is: [#80437] [#80437]: https://github.com/rust-lang/rust/issues/80437 @@ -3439,6 +4015,8 @@ The tracking issue for this feature is: [#29641] ------------------------ +> **Note**: This feature will be superseded by [`deref_patterns`] in the future. + Box patterns let you match on `Box<T>`s: @@ -3461,18 +4039,22 @@ fn main() { } } ``` + +[`deref_patterns`]: ./deref-patterns.md "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "box_uninit_write", - description: r##"# `box_uninit_write` + label: "box_take", + description: r##"# `box_take` + + -The tracking issue for this feature is: [#129397] +The tracking issue for this feature is: [#147212] -[#129397]: https://github.com/rust-lang/rust/issues/129397 +[#147212]: https://github.com/rust-lang/rust/issues/147212 ------------------------ "##, @@ -3484,6 +4066,8 @@ The tracking issue for this feature is: [#129397] label: "box_vec_non_null", description: r##"# `box_vec_non_null` + + The tracking issue for this feature is: [#130364] [#130364]: https://github.com/rust-lang/rust/issues/130364 @@ -3498,9 +4082,11 @@ The tracking issue for this feature is: [#130364] label: "bpf_target_feature", description: r##"# `bpf_target_feature` -The tracking issue for this feature is: [#44839] +Target features on bpf. -[#44839]: https://github.com/rust-lang/rust/issues/44839 +The tracking issue for this feature is: [#150247] + +[#150247]: https://github.com/rust-lang/rust/issues/150247 ------------------------ "##, @@ -3512,6 +4098,8 @@ The tracking issue for this feature is: [#44839] label: "breakpoint", description: r##"# `breakpoint` + + The tracking issue for this feature is: [#133724] [#133724]: https://github.com/rust-lang/rust/issues/133724 @@ -3523,12 +4111,28 @@ The tracking issue for this feature is: [#133724] deny_since: None, }, Lint { - label: "btree_cursors", - description: r##"# `btree_cursors` + label: "bstr", + description: r##"# `bstr` -The tracking issue for this feature is: [#107540] -[#107540]: https://github.com/rust-lang/rust/issues/107540 + +The tracking issue for this feature is: [#134915] + +[#134915]: https://github.com/rust-lang/rust/issues/134915 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "bstr_internals", + description: r##"# `bstr_internals` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -3537,12 +4141,14 @@ The tracking issue for this feature is: [#107540] deny_since: None, }, Lint { - label: "btree_entry_insert", - description: r##"# `btree_entry_insert` + label: "btree_cursors", + description: r##"# `btree_cursors` + -The tracking issue for this feature is: [#65225] -[#65225]: https://github.com/rust-lang/rust/issues/65225 +The tracking issue for this feature is: [#107540] + +[#107540]: https://github.com/rust-lang/rust/issues/107540 ------------------------ "##, @@ -3551,12 +4157,14 @@ The tracking issue for this feature is: [#65225] deny_since: None, }, Lint { - label: "btree_extract_if", - description: r##"# `btree_extract_if` + label: "btree_merge", + description: r##"# `btree_merge` + -The tracking issue for this feature is: [#70530] -[#70530]: https://github.com/rust-lang/rust/issues/70530 +The tracking issue for this feature is: [#152152] + +[#152152]: https://github.com/rust-lang/rust/issues/152152 ------------------------ "##, @@ -3568,6 +4176,8 @@ The tracking issue for this feature is: [#70530] label: "btree_set_entry", description: r##"# `btree_set_entry` + + The tracking issue for this feature is: [#133549] [#133549]: https://github.com/rust-lang/rust/issues/133549 @@ -3582,6 +4192,8 @@ The tracking issue for this feature is: [#133549] label: "btreemap_alloc", description: r##"# `btreemap_alloc` + + The tracking issue for this feature is: [#32838] [#32838]: https://github.com/rust-lang/rust/issues/32838 @@ -3596,6 +4208,8 @@ The tracking issue for this feature is: [#32838] label: "buf_read_has_data_left", description: r##"# `buf_read_has_data_left` + + The tracking issue for this feature is: [#86423] [#86423]: https://github.com/rust-lang/rust/issues/86423 @@ -3610,6 +4224,8 @@ The tracking issue for this feature is: [#86423] label: "bufreader_peek", description: r##"# `bufreader_peek` + + The tracking issue for this feature is: [#128405] [#128405]: https://github.com/rust-lang/rust/issues/128405 @@ -3624,6 +4240,8 @@ The tracking issue for this feature is: [#128405] label: "builtin_syntax", description: r##"# `builtin_syntax` +Allows builtin # foo() syntax + The tracking issue for this feature is: [#110680] [#110680]: https://github.com/rust-lang/rust/issues/110680 @@ -3638,23 +4256,11 @@ The tracking issue for this feature is: [#110680] label: "c_size_t", description: r##"# `c_size_t` -The tracking issue for this feature is: [#88345] - -[#88345]: https://github.com/rust-lang/rust/issues/88345 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "c_str_module", - description: r##"# `c_str_module` -The tracking issue for this feature is: [#112134] +The tracking issue for this feature is: [#88345] -[#112134]: https://github.com/rust-lang/rust/issues/112134 +[#88345]: https://github.com/rust-lang/rust/issues/88345 ------------------------ "##, @@ -3694,33 +4300,16 @@ pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { deny_since: None, }, Lint { - label: "c_variadic", - description: r##"# `c_variadic` - -The tracking issue for this feature is: [#44930] + label: "c_variadic_naked_functions", + description: r##"# `c_variadic_naked_functions` -[#44930]: https://github.com/rust-lang/rust/issues/44930 - ------------------------- - -The `c_variadic` library feature exposes the `VaList` structure, -Rust's analogue of C's `va_list` type. +Allows defining c-variadic naked functions with any extern ABI that is allowed on c-variadic foreign functions. -## Examples - -```rust -#![feature(c_variadic)] +The tracking issue for this feature is: [#148767] -use std::ffi::VaList; +[#148767]: https://github.com/rust-lang/rust/issues/148767 -pub unsafe extern "C" fn vadd(n: usize, mut args: VaList) -> usize { - let mut sum = 0; - for _ in 0..n { - sum += args.arg::<usize>(); - } - sum -} -``` +------------------------ "##, default_severity: Severity::Allow, warn_since: None, @@ -3742,6 +4331,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "can_vector", description: r##"# `can_vector` + + The tracking issue for this feature is: [#69941] [#69941]: https://github.com/rust-lang/rust/issues/69941 @@ -3753,12 +4344,14 @@ The tracking issue for this feature is: [#69941] deny_since: None, }, Lint { - label: "cell_leak", - description: r##"# `cell_leak` + label: "case_ignorable", + description: r##"# `case_ignorable` -The tracking issue for this feature is: [#69099] -[#69099]: https://github.com/rust-lang/rust/issues/69099 + +The tracking issue for this feature is: [#154848] + +[#154848]: https://github.com/rust-lang/rust/issues/154848 ------------------------ "##, @@ -3767,12 +4360,46 @@ The tracking issue for this feature is: [#69099] deny_since: None, }, Lint { - label: "cell_update", - description: r##"# `cell_update` + label: "cast_maybe_uninit", + description: r##"# `cast_maybe_uninit` -The tracking issue for this feature is: [#50186] -[#50186]: https://github.com/rust-lang/rust/issues/50186 + +The tracking issue for this feature is: [#145036] + +[#145036]: https://github.com/rust-lang/rust/issues/145036 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "cell_get_cloned", + description: r##"# `cell_get_cloned` + + + +The tracking issue for this feature is: [#145329] + +[#145329]: https://github.com/rust-lang/rust/issues/145329 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "cell_leak", + description: r##"# `cell_leak` + + + +The tracking issue for this feature is: [#69099] + +[#69099]: https://github.com/rust-lang/rust/issues/69099 ------------------------ "##, @@ -3784,6 +4411,8 @@ The tracking issue for this feature is: [#50186] label: "cfg_accessible", description: r##"# `cfg_accessible` + + The tracking issue for this feature is: [#64797] [#64797]: https://github.com/rust-lang/rust/issues/64797 @@ -3795,12 +4424,14 @@ The tracking issue for this feature is: [#64797] deny_since: None, }, Lint { - label: "cfg_eval", - description: r##"# `cfg_eval` + label: "cfg_contract_checks", + description: r##"# `cfg_contract_checks` -The tracking issue for this feature is: [#82679] +Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. -[#82679]: https://github.com/rust-lang/rust/issues/82679 +The tracking issue for this feature is: [#128044] + +[#128044]: https://github.com/rust-lang/rust/issues/128044 ------------------------ "##, @@ -3809,12 +4440,28 @@ The tracking issue for this feature is: [#82679] deny_since: None, }, Lint { - label: "cfg_match", - description: r##"# `cfg_match` + label: "cfg_emscripten_wasm_eh", + description: r##"# `cfg_emscripten_wasm_eh` -The tracking issue for this feature is: [#115585] +Allows access to the emscripten_wasm_eh config, used by panic_unwind and unwind -[#115585]: https://github.com/rust-lang/rust/issues/115585 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "cfg_eval", + description: r##"# `cfg_eval` + + + +The tracking issue for this feature is: [#82679] + +[#82679]: https://github.com/rust-lang/rust/issues/82679 ------------------------ "##, @@ -3826,6 +4473,8 @@ The tracking issue for this feature is: [#115585] label: "cfg_overflow_checks", description: r##"# `cfg_overflow_checks` +Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. + The tracking issue for this feature is: [#111466] [#111466]: https://github.com/rust-lang/rust/issues/111466 @@ -3840,6 +4489,8 @@ The tracking issue for this feature is: [#111466] label: "cfg_relocation_model", description: r##"# `cfg_relocation_model` +Provides the relocation model information as cfg entry + The tracking issue for this feature is: [#114929] [#114929]: https://github.com/rust-lang/rust/issues/114929 @@ -3895,6 +4546,8 @@ fn b() { label: "cfg_sanitizer_cfi", description: r##"# `cfg_sanitizer_cfi` +Allows `cfg(sanitizer_cfi_generalize_pointers)` and `cfg(sanitizer_cfi_normalize_integers)`. + The tracking issue for this feature is: [#89653] [#89653]: https://github.com/rust-lang/rust/issues/89653 @@ -3909,6 +4562,8 @@ The tracking issue for this feature is: [#89653] label: "cfg_target_compact", description: r##"# `cfg_target_compact` +Allows `cfg(target(abi = "..."))`. + The tracking issue for this feature is: [#96901] [#96901]: https://github.com/rust-lang/rust/issues/96901 @@ -3923,6 +4578,8 @@ The tracking issue for this feature is: [#96901] label: "cfg_target_has_atomic", description: r##"# `cfg_target_has_atomic` +Allows `cfg(target_has_atomic_load_store = "...")`. + The tracking issue for this feature is: [#94039] [#94039]: https://github.com/rust-lang/rust/issues/94039 @@ -3937,6 +4594,8 @@ The tracking issue for this feature is: [#94039] label: "cfg_target_has_atomic_equal_alignment", description: r##"# `cfg_target_has_atomic_equal_alignment` +Allows `cfg(target_has_atomic_equal_alignment = "...")`. + The tracking issue for this feature is: [#93822] [#93822]: https://github.com/rust-lang/rust/issues/93822 @@ -3948,9 +4607,25 @@ The tracking issue for this feature is: [#93822] deny_since: None, }, Lint { + label: "cfg_target_has_reliable_f16_f128", + description: r##"# `cfg_target_has_reliable_f16_f128` + +Allows checking whether or not the backend correctly supports unstable float types. + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "cfg_target_thread_local", description: r##"# `cfg_target_thread_local` +Allows `cfg(target_thread_local)`. + The tracking issue for this feature is: [#29594] [#29594]: https://github.com/rust-lang/rust/issues/29594 @@ -3965,6 +4640,8 @@ The tracking issue for this feature is: [#29594] label: "cfg_ub_checks", description: r##"# `cfg_ub_checks` +Allows the use of `#[cfg(ub_checks)` to check if UB checks are enabled. + The tracking issue for this feature is: [#123499] [#123499]: https://github.com/rust-lang/rust/issues/123499 @@ -4053,6 +4730,8 @@ extern { label: "char_internals", description: r##"# `char_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -4062,9 +4741,59 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { + label: "char_max_len", + description: r##"# `char_max_len` + + + +The tracking issue for this feature is: [#121714] + +[#121714]: https://github.com/rust-lang/rust/issues/121714 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "clamp_magnitude", + description: r##"# `clamp_magnitude` + + + +The tracking issue for this feature is: [#148519] + +[#148519]: https://github.com/rust-lang/rust/issues/148519 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "clone_from_ref", + description: r##"# `clone_from_ref` + + + +The tracking issue for this feature is: [#149075] + +[#149075]: https://github.com/rust-lang/rust/issues/149075 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "clone_to_uninit", description: r##"# `clone_to_uninit` + + The tracking issue for this feature is: [#126799] [#126799]: https://github.com/rust-lang/rust/issues/126799 @@ -4079,6 +4808,8 @@ The tracking issue for this feature is: [#126799] label: "closure_lifetime_binder", description: r##"# `closure_lifetime_binder` +Allows `for<...>` on closures and coroutines. + The tracking issue for this feature is: [#97362] [#97362]: https://github.com/rust-lang/rust/issues/97362 @@ -4112,6 +4843,8 @@ available through `std::panic::Location::caller()`, just like using label: "cmp_minmax", description: r##"# `cmp_minmax` + + The tracking issue for this feature is: [#115939] [#115939]: https://github.com/rust-lang/rust/issues/115939 @@ -4140,10 +4873,9 @@ LLVM, the Rust compiler and the linker are providing [support](https://developer.arm.com/documentation/ecm0359818/latest/) for the TrustZone-M feature. -One of the things provided, with this unstable feature, is the -`C-cmse-nonsecure-entry` ABI. This ABI marks a Secure function as an -entry function (see [section -5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details). +One of the things provided with this unstable feature is the "cmse-nonsecure-entry" ABI. +This ABI marks a Secure function as an entry function (see +[section 5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details). With this ABI, the compiler will do the following: * add a special symbol on the function which is the `__acle_se_` prefix and the standard function name @@ -4154,9 +4886,7 @@ With this ABI, the compiler will do the following: Because the stack can not be used to pass parameters, there will be compilation errors if: -* the total size of all parameters is too big (for example more than four 32 - bits integers) -* the entry function is not using a C ABI +* the total size of all parameters is too big (for example, more than four 32-bit integers) The special symbol `__acle_se_` will be used by the linker to generate a secure gateway veneer. @@ -4168,7 +4898,7 @@ gateway veneer. #![feature(cmse_nonsecure_entry)] #[no_mangle] -pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 { +pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 { input + 6 } ``` @@ -4211,9 +4941,25 @@ $ arm-none-eabi-objdump -D function.o deny_since: None, }, Lint { + label: "coerce_pointee_validated", + description: r##"# `coerce_pointee_validated` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "coerce_unsized", description: r##"# `coerce_unsized` + + The tracking issue for this feature is: [#18598] [#18598]: https://github.com/rust-lang/rust/issues/18598 @@ -4225,6 +4971,22 @@ The tracking issue for this feature is: [#18598] deny_since: None, }, Lint { + label: "command_resolved_envs", + description: r##"# `command_resolved_envs` + + + +The tracking issue for this feature is: [#149070] + +[#149070]: https://github.com/rust-lang/rust/issues/149070 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "compiler_builtins", description: r##"# `compiler_builtins` @@ -4240,6 +5002,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "concat_bytes", description: r##"# `concat_bytes` + + The tracking issue for this feature is: [#87555] [#87555]: https://github.com/rust-lang/rust/issues/87555 @@ -4251,41 +5015,46 @@ The tracking issue for this feature is: [#87555] deny_since: None, }, Lint { - label: "concat_idents", - description: r##"# `concat_idents` + label: "const_alloc_error", + description: r##"# `const_alloc_error` + -The tracking issue for this feature is: [#29599] -[#29599]: https://github.com/rust-lang/rust/issues/29599 +The tracking issue for this feature is: [#92523] + +[#92523]: https://github.com/rust-lang/rust/issues/92523 ------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_array", + description: r##"# `const_array` -The `concat_idents` feature adds a macro for concatenating multiple identifiers -into one identifier. -## Examples -```rust -#![feature(concat_idents)] +The tracking issue for this feature is: [#147606] -fn main() { - fn foobar() -> u32 { 23 } - let f = concat_idents!(foo, bar); - assert_eq!(f(), 23); -} -``` +[#147606]: https://github.com/rust-lang/rust/issues/147606 + +------------------------ "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "const_alloc_error", - description: r##"# `const_alloc_error` + label: "const_async_blocks", + description: r##"# `const_async_blocks` -The tracking issue for this feature is: [#92523] +Allows `async {}` expressions in const contexts. -[#92523]: https://github.com/rust-lang/rust/issues/92523 +The tracking issue for this feature is: [#85368] + +[#85368]: https://github.com/rust-lang/rust/issues/85368 ------------------------ "##, @@ -4294,12 +5063,14 @@ The tracking issue for this feature is: [#92523] deny_since: None, }, Lint { - label: "const_alloc_layout", - description: r##"# `const_alloc_layout` + label: "const_block_items", + description: r##"# `const_block_items` + +Allows `const { ... }` as a shorthand for `const _: () = const { ... };` for module items. -The tracking issue for this feature is: [#67521] +The tracking issue for this feature is: [#149226] -[#67521]: https://github.com/rust-lang/rust/issues/67521 +[#149226]: https://github.com/rust-lang/rust/issues/149226 ------------------------ "##, @@ -4308,12 +5079,14 @@ The tracking issue for this feature is: [#67521] deny_since: None, }, Lint { - label: "const_array_as_mut_slice", - description: r##"# `const_array_as_mut_slice` + label: "const_bool", + description: r##"# `const_bool` -The tracking issue for this feature is: [#133333] -[#133333]: https://github.com/rust-lang/rust/issues/133333 + +The tracking issue for this feature is: [#151531] + +[#151531]: https://github.com/rust-lang/rust/issues/151531 ------------------------ "##, @@ -4322,12 +5095,12 @@ The tracking issue for this feature is: [#133333] deny_since: None, }, Lint { - label: "const_array_each_ref", - description: r##"# `const_array_each_ref` + label: "const_btree_len", + description: r##"# `const_btree_len` -The tracking issue for this feature is: [#133289] -[#133289]: https://github.com/rust-lang/rust/issues/133289 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -4336,12 +5109,14 @@ The tracking issue for this feature is: [#133289] deny_since: None, }, Lint { - label: "const_async_blocks", - description: r##"# `const_async_blocks` + label: "const_c_variadic", + description: r##"# `const_c_variadic` -The tracking issue for this feature is: [#85368] +Allows defining and calling c-variadic functions in const contexts. -[#85368]: https://github.com/rust-lang/rust/issues/85368 +The tracking issue for this feature is: [#151787] + +[#151787]: https://github.com/rust-lang/rust/issues/151787 ------------------------ "##, @@ -4350,10 +5125,14 @@ The tracking issue for this feature is: [#85368] deny_since: None, }, Lint { - label: "const_black_box", - description: r##"# `const_black_box` + label: "const_carrying_mul_add", + description: r##"# `const_carrying_mul_add` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + + +The tracking issue for this feature is: [#85532] + +[#85532]: https://github.com/rust-lang/rust/issues/85532 ------------------------ "##, @@ -4362,12 +5141,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "const_box", - description: r##"# `const_box` + label: "const_cell_traits", + description: r##"# `const_cell_traits` + -The tracking issue for this feature is: [#92521] -[#92521]: https://github.com/rust-lang/rust/issues/92521 +The tracking issue for this feature is: [#147787] + +[#147787]: https://github.com/rust-lang/rust/issues/147787 ------------------------ "##, @@ -4376,10 +5157,14 @@ The tracking issue for this feature is: [#92521] deny_since: None, }, Lint { - label: "const_btree_len", - description: r##"# `const_btree_len` + label: "const_clone", + description: r##"# `const_clone` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + + +The tracking issue for this feature is: [#142757] + +[#142757]: https://github.com/rust-lang/rust/issues/142757 ------------------------ "##, @@ -4388,12 +5173,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "const_cell", - description: r##"# `const_cell` + label: "const_closures", + description: r##"# `const_closures` + +Allows `const || {}` closures in const contexts. -The tracking issue for this feature is: [#131283] +The tracking issue for this feature is: [#106003] -[#131283]: https://github.com/rust-lang/rust/issues/131283 +[#106003]: https://github.com/rust-lang/rust/issues/106003 ------------------------ "##, @@ -4402,12 +5189,14 @@ The tracking issue for this feature is: [#131283] deny_since: None, }, Lint { - label: "const_char_classify", - description: r##"# `const_char_classify` + label: "const_cmp", + description: r##"# `const_cmp` + + -The tracking issue for this feature is: [#132241] +The tracking issue for this feature is: [#143800] -[#132241]: https://github.com/rust-lang/rust/issues/132241 +[#143800]: https://github.com/rust-lang/rust/issues/143800 ------------------------ "##, @@ -4416,12 +5205,30 @@ The tracking issue for this feature is: [#132241] deny_since: None, }, Lint { - label: "const_closures", - description: r##"# `const_closures` + label: "const_control_flow", + description: r##"# `const_control_flow` -The tracking issue for this feature is: [#106003] -[#106003]: https://github.com/rust-lang/rust/issues/106003 + +The tracking issue for this feature is: [#148739] + +[#148739]: https://github.com/rust-lang/rust/issues/148739 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_convert", + description: r##"# `const_convert` + + + +The tracking issue for this feature is: [#143773] + +[#143773]: https://github.com/rust-lang/rust/issues/143773 ------------------------ "##, @@ -4430,12 +5237,14 @@ The tracking issue for this feature is: [#106003] deny_since: None, }, Lint { - label: "const_copy_from_slice", - description: r##"# `const_copy_from_slice` + label: "const_default", + description: r##"# `const_default` + + -The tracking issue for this feature is: [#131415] +The tracking issue for this feature is: [#143894] -[#131415]: https://github.com/rust-lang/rust/issues/131415 +[#143894]: https://github.com/rust-lang/rust/issues/143894 ------------------------ "##, @@ -4447,6 +5256,8 @@ The tracking issue for this feature is: [#131415] label: "const_destruct", description: r##"# `const_destruct` +Allows using `[const] Destruct` bounds and calling drop impls in const contexts. + The tracking issue for this feature is: [#133214] [#133214]: https://github.com/rust-lang/rust/issues/133214 @@ -4458,9 +5269,41 @@ The tracking issue for this feature is: [#133214] deny_since: None, }, Lint { + label: "const_drop_guard", + description: r##"# `const_drop_guard` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_drop_in_place", + description: r##"# `const_drop_in_place` + + + +The tracking issue for this feature is: [#109342] + +[#109342]: https://github.com/rust-lang/rust/issues/109342 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "const_eval_select", description: r##"# `const_eval_select` + + The tracking issue for this feature is: [#124625] [#124625]: https://github.com/rust-lang/rust/issues/124625 @@ -4475,6 +5318,8 @@ The tracking issue for this feature is: [#124625] label: "const_for", description: r##"# `const_for` +Allows `for _ in _` loops in const contexts. + The tracking issue for this feature is: [#87575] [#87575]: https://github.com/rust-lang/rust/issues/87575 @@ -4489,6 +5334,8 @@ The tracking issue for this feature is: [#87575] label: "const_format_args", description: r##"# `const_format_args` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -4501,6 +5348,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "const_heap", description: r##"# `const_heap` + + The tracking issue for this feature is: [#79597] [#79597]: https://github.com/rust-lang/rust/issues/79597 @@ -4512,12 +5361,46 @@ The tracking issue for this feature is: [#79597] deny_since: None, }, Lint { - label: "const_is_char_boundary", - description: r##"# `const_is_char_boundary` + label: "const_index", + description: r##"# `const_index` -The tracking issue for this feature is: [#131516] -[#131516]: https://github.com/rust-lang/rust/issues/131516 + +The tracking issue for this feature is: [#143775] + +[#143775]: https://github.com/rust-lang/rust/issues/143775 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_iter", + description: r##"# `const_iter` + + + +The tracking issue for this feature is: [#92476] + +[#92476]: https://github.com/rust-lang/rust/issues/92476 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_manually_drop_take", + description: r##"# `const_manually_drop_take` + + + +The tracking issue for this feature is: [#148773] + +[#148773]: https://github.com/rust-lang/rust/issues/148773 ------------------------ "##, @@ -4526,12 +5409,92 @@ The tracking issue for this feature is: [#131516] deny_since: None, }, Lint { - label: "const_mut_cursor", - description: r##"# `const_mut_cursor` + label: "const_never_short_circuit", + description: r##"# `const_never_short_circuit` -The tracking issue for this feature is: [#130801] -[#130801]: https://github.com/rust-lang/rust/issues/130801 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_nonnull_with_exposed_provenance", + description: r##"# `const_nonnull_with_exposed_provenance` + + + +The tracking issue for this feature is: [#154215] + +[#154215]: https://github.com/rust-lang/rust/issues/154215 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_ops", + description: r##"# `const_ops` + + + +The tracking issue for this feature is: [#143802] + +[#143802]: https://github.com/rust-lang/rust/issues/143802 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_option_ops", + description: r##"# `const_option_ops` + + + +The tracking issue for this feature is: [#143956] + +[#143956]: https://github.com/rust-lang/rust/issues/143956 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_param_ty_trait", + description: r##"# `const_param_ty_trait` + + + +The tracking issue for this feature is: [#95174] + +[#95174]: https://github.com/rust-lang/rust/issues/95174 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_path_separators", + description: r##"# `const_path_separators` + + + +The tracking issue for this feature is: [#153106] + +[#153106]: https://github.com/rust-lang/rust/issues/153106 ------------------------ "##, @@ -4543,6 +5506,8 @@ The tracking issue for this feature is: [#130801] label: "const_precise_live_drops", description: r##"# `const_precise_live_drops` +Be more precise when looking for live drops in a const context. + The tracking issue for this feature is: [#73255] [#73255]: https://github.com/rust-lang/rust/issues/73255 @@ -4554,12 +5519,12 @@ The tracking issue for this feature is: [#73255] deny_since: None, }, Lint { - label: "const_ptr_sub_ptr", - description: r##"# `const_ptr_sub_ptr` + label: "const_range", + description: r##"# `const_range` -The tracking issue for this feature is: [#95892] -[#95892]: https://github.com/rust-lang/rust/issues/95892 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -4571,6 +5536,8 @@ The tracking issue for this feature is: [#95892] label: "const_range_bounds", description: r##"# `const_range_bounds` + + The tracking issue for this feature is: [#108082] [#108082]: https://github.com/rust-lang/rust/issues/108082 @@ -4585,6 +5552,8 @@ The tracking issue for this feature is: [#108082] label: "const_raw_ptr_comparison", description: r##"# `const_raw_ptr_comparison` + + The tracking issue for this feature is: [#53020] [#53020]: https://github.com/rust-lang/rust/issues/53020 @@ -4596,12 +5565,14 @@ The tracking issue for this feature is: [#53020] deny_since: None, }, Lint { - label: "const_slice_flatten", - description: r##"# `const_slice_flatten` + label: "const_ref_cell", + description: r##"# `const_ref_cell` -The tracking issue for this feature is: [#95629] -[#95629]: https://github.com/rust-lang/rust/issues/95629 + +The tracking issue for this feature is: [#137844] + +[#137844]: https://github.com/rust-lang/rust/issues/137844 ------------------------ "##, @@ -4610,12 +5581,14 @@ The tracking issue for this feature is: [#95629] deny_since: None, }, Lint { - label: "const_slice_from_mut_ptr_range", - description: r##"# `const_slice_from_mut_ptr_range` + label: "const_result_trait_fn", + description: r##"# `const_result_trait_fn` -The tracking issue for this feature is: [#89792] -[#89792]: https://github.com/rust-lang/rust/issues/89792 + +The tracking issue for this feature is: [#144211] + +[#144211]: https://github.com/rust-lang/rust/issues/144211 ------------------------ "##, @@ -4624,12 +5597,30 @@ The tracking issue for this feature is: [#89792] deny_since: None, }, Lint { - label: "const_slice_from_ptr_range", - description: r##"# `const_slice_from_ptr_range` + label: "const_result_unwrap_unchecked", + description: r##"# `const_result_unwrap_unchecked` + + + +The tracking issue for this feature is: [#148714] + +[#148714]: https://github.com/rust-lang/rust/issues/148714 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "const_select_unpredictable", + description: r##"# `const_select_unpredictable` -The tracking issue for this feature is: [#89792] -[#89792]: https://github.com/rust-lang/rust/issues/89792 + +The tracking issue for this feature is: [#145938] + +[#145938]: https://github.com/rust-lang/rust/issues/145938 ------------------------ "##, @@ -4638,12 +5629,14 @@ The tracking issue for this feature is: [#89792] deny_since: None, }, Lint { - label: "const_sockaddr_setters", - description: r##"# `const_sockaddr_setters` + label: "const_slice_from_mut_ptr_range", + description: r##"# `const_slice_from_mut_ptr_range` -The tracking issue for this feature is: [#131714] -[#131714]: https://github.com/rust-lang/rust/issues/131714 + +The tracking issue for this feature is: [#89792] + +[#89792]: https://github.com/rust-lang/rust/issues/89792 ------------------------ "##, @@ -4652,12 +5645,14 @@ The tracking issue for this feature is: [#131714] deny_since: None, }, Lint { - label: "const_str_from_utf8", - description: r##"# `const_str_from_utf8` + label: "const_slice_from_ptr_range", + description: r##"# `const_slice_from_ptr_range` + + -The tracking issue for this feature is: [#91006] +The tracking issue for this feature is: [#89792] -[#91006]: https://github.com/rust-lang/rust/issues/91006 +[#89792]: https://github.com/rust-lang/rust/issues/89792 ------------------------ "##, @@ -4666,12 +5661,14 @@ The tracking issue for this feature is: [#91006] deny_since: None, }, Lint { - label: "const_str_split_at", - description: r##"# `const_str_split_at` + label: "const_slice_make_iter", + description: r##"# `const_slice_make_iter` + + -The tracking issue for this feature is: [#131518] +The tracking issue for this feature is: [#137737] -[#131518]: https://github.com/rust-lang/rust/issues/131518 +[#137737]: https://github.com/rust-lang/rust/issues/137737 ------------------------ "##, @@ -4680,12 +5677,14 @@ The tracking issue for this feature is: [#131518] deny_since: None, }, Lint { - label: "const_swap", - description: r##"# `const_swap` + label: "const_split_off_first_last", + description: r##"# `const_split_off_first_last` -The tracking issue for this feature is: [#83163] -[#83163]: https://github.com/rust-lang/rust/issues/83163 + +The tracking issue for this feature is: [#138539] + +[#138539]: https://github.com/rust-lang/rust/issues/138539 ------------------------ "##, @@ -4694,12 +5693,14 @@ The tracking issue for this feature is: [#83163] deny_since: None, }, Lint { - label: "const_swap_nonoverlapping", - description: r##"# `const_swap_nonoverlapping` + label: "const_swap_with_slice", + description: r##"# `const_swap_with_slice` + + -The tracking issue for this feature is: [#133668] +The tracking issue for this feature is: [#142204] -[#133668]: https://github.com/rust-lang/rust/issues/133668 +[#142204]: https://github.com/rust-lang/rust/issues/142204 ------------------------ "##, @@ -4711,6 +5712,8 @@ The tracking issue for this feature is: [#133668] label: "const_trait_impl", description: r##"# `const_trait_impl` +Allows `impl const Trait for T` syntax. + The tracking issue for this feature is: [#143874] [#143874]: https://github.com/rust-lang/rust/issues/143874 @@ -4725,6 +5728,8 @@ The tracking issue for this feature is: [#143874] label: "const_try", description: r##"# `const_try` +Allows the `?` operator in const contexts. + The tracking issue for this feature is: [#74935] [#74935]: https://github.com/rust-lang/rust/issues/74935 @@ -4736,12 +5741,14 @@ The tracking issue for this feature is: [#74935] deny_since: None, }, Lint { - label: "const_type_id", - description: r##"# `const_type_id` + label: "const_try_residual", + description: r##"# `const_try_residual` + + -The tracking issue for this feature is: [#77125] +The tracking issue for this feature is: [#91285] -[#77125]: https://github.com/rust-lang/rust/issues/77125 +[#91285]: https://github.com/rust-lang/rust/issues/91285 ------------------------ "##, @@ -4753,6 +5760,8 @@ The tracking issue for this feature is: [#77125] label: "const_type_name", description: r##"# `const_type_name` + + The tracking issue for this feature is: [#63084] [#63084]: https://github.com/rust-lang/rust/issues/63084 @@ -4764,8 +5773,26 @@ The tracking issue for this feature is: [#63084] deny_since: None, }, Lint { - label: "const_typed_swap", - description: r##"# `const_typed_swap` + label: "const_unsigned_bigint_helpers", + description: r##"# `const_unsigned_bigint_helpers` + + + +The tracking issue for this feature is: [#152015] + +[#152015]: https://github.com/rust-lang/rust/issues/152015 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "container_error_extra", + description: r##"# `container_error_extra` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. @@ -4776,12 +5803,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "const_vec_string_slice", - description: r##"# `const_vec_string_slice` + label: "context_ext", + description: r##"# `context_ext` + + -The tracking issue for this feature is: [#129041] +The tracking issue for this feature is: [#123392] -[#129041]: https://github.com/rust-lang/rust/issues/129041 +[#123392]: https://github.com/rust-lang/rust/issues/123392 ------------------------ "##, @@ -4790,10 +5819,14 @@ The tracking issue for this feature is: [#129041] deny_since: None, }, Lint { - label: "container_error_extra", - description: r##"# `container_error_extra` + label: "contracts", + description: r##"# `contracts` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +Allows use of contracts attributes. + +The tracking issue for this feature is: [#128044] + +[#128044]: https://github.com/rust-lang/rust/issues/128044 ------------------------ "##, @@ -4802,12 +5835,30 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "context_ext", - description: r##"# `context_ext` + label: "contracts_internals", + description: r##"# `contracts_internals` -The tracking issue for this feature is: [#123392] +Allows access to internal machinery used to implement contracts. -[#123392]: https://github.com/rust-lang/rust/issues/123392 +The tracking issue for this feature is: [#128044] + +[#128044]: https://github.com/rust-lang/rust/issues/128044 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "control_flow_into_value", + description: r##"# `control_flow_into_value` + + + +The tracking issue for this feature is: [#137461] + +[#137461]: https://github.com/rust-lang/rust/issues/137461 ------------------------ "##, @@ -4819,6 +5870,8 @@ The tracking issue for this feature is: [#123392] label: "convert_float_to_int", description: r##"# `convert_float_to_int` + + The tracking issue for this feature is: [#67057] [#67057]: https://github.com/rust-lang/rust/issues/67057 @@ -4830,6 +5883,36 @@ The tracking issue for this feature is: [#67057] deny_since: None, }, Lint { + label: "copied_into_inner", + description: r##"# `copied_into_inner` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "core_float_math", + description: r##"# `core_float_math` + + + +The tracking issue for this feature is: [#137578] + +[#137578]: https://github.com/rust-lang/rust/issues/137578 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "core_intrinsics", description: r##"# `core_intrinsics` @@ -4842,9 +5925,25 @@ This feature is internal to the Rust compiler and is not intended for general us deny_since: None, }, Lint { + label: "core_intrinsics_fallbacks", + description: r##"# `core_intrinsics_fallbacks` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "core_io_borrowed_buf", description: r##"# `core_io_borrowed_buf` + + The tracking issue for this feature is: [#117693] [#117693]: https://github.com/rust-lang/rust/issues/117693 @@ -4883,6 +5982,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "coroutine_clone", description: r##"# `coroutine_clone` +Allows coroutines to be cloned. + The tracking issue for this feature is: [#95360] [#95360]: https://github.com/rust-lang/rust/issues/95360 @@ -4897,6 +5998,8 @@ The tracking issue for this feature is: [#95360] label: "coroutine_trait", description: r##"# `coroutine_trait` + + The tracking issue for this feature is: [#43122] [#43122]: https://github.com/rust-lang/rust/issues/43122 @@ -5202,6 +6305,8 @@ fn bar() { label: "cow_is_borrowed", description: r##"# `cow_is_borrowed` + + The tracking issue for this feature is: [#65143] [#65143]: https://github.com/rust-lang/rust/issues/65143 @@ -5216,9 +6321,11 @@ The tracking issue for this feature is: [#65143] label: "csky_target_feature", description: r##"# `csky_target_feature` -The tracking issue for this feature is: [#44839] +Target features on csky. + +The tracking issue for this feature is: [#150248] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +[#150248]: https://github.com/rust-lang/rust/issues/150248 ------------------------ "##, @@ -5230,6 +6337,8 @@ The tracking issue for this feature is: [#44839] label: "cstr_bytes", description: r##"# `cstr_bytes` + + The tracking issue for this feature is: [#112115] [#112115]: https://github.com/rust-lang/rust/issues/112115 @@ -5241,9 +6350,27 @@ The tracking issue for this feature is: [#112115] deny_since: None, }, Lint { + label: "cstr_display", + description: r##"# `cstr_display` + + + +The tracking issue for this feature is: [#139984] + +[#139984]: https://github.com/rust-lang/rust/issues/139984 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "cstr_internals", description: r##"# `cstr_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -5253,9 +6380,27 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { + label: "current_thread_id", + description: r##"# `current_thread_id` + + + +The tracking issue for this feature is: [#147194] + +[#147194]: https://github.com/rust-lang/rust/issues/147194 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "cursor_split", description: r##"# `cursor_split` + + The tracking issue for this feature is: [#86369] [#86369]: https://github.com/rust-lang/rust/issues/86369 @@ -5270,6 +6415,8 @@ The tracking issue for this feature is: [#86369] label: "custom_inner_attributes", description: r##"# `custom_inner_attributes` +Allows non-builtin attributes in inner attribute position. + The tracking issue for this feature is: [#54726] [#54726]: https://github.com/rust-lang/rust/issues/54726 @@ -5284,6 +6431,8 @@ The tracking issue for this feature is: [#54726] label: "custom_mir", description: r##"# `custom_mir` +Allows writing custom MIR + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -5332,9 +6481,27 @@ const WILL_FAIL: i32 = 4; deny_since: None, }, Lint { + label: "darwin_objc", + description: r##"# `darwin_objc` + + + +The tracking issue for this feature is: [#145496] + +[#145496]: https://github.com/rust-lang/rust/issues/145496 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "deadline_api", description: r##"# `deadline_api` + + The tracking issue for this feature is: [#46316] [#46316]: https://github.com/rust-lang/rust/issues/46316 @@ -5349,6 +6516,8 @@ The tracking issue for this feature is: [#46316] label: "debug_closure_helpers", description: r##"# `debug_closure_helpers` + + The tracking issue for this feature is: [#117729] [#117729]: https://github.com/rust-lang/rust/issues/117729 @@ -5375,6 +6544,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "decl_macro", description: r##"# `decl_macro` +Allows declarative macros 2.0 (`macro`). + The tracking issue for this feature is: [#39412] [#39412]: https://github.com/rust-lang/rust/issues/39412 @@ -5393,19 +6564,107 @@ The tracking issue for this feature is: [#132162] [#132162]: https://github.com/rust-lang/rust/issues/132162 +The RFC for this feature is: [#3681] + +[#3681]: https://github.com/rust-lang/rfcs/blob/master/text/3681-default-field-values.md + ------------------------ + +The `default_field_values` feature allows users to specify a const value for +individual fields in struct definitions, allowing those to be omitted from +initializers. + +## Examples + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +struct Pet { + name: Option<String>, // impl Default for Pet will use Default::default() for name + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +fn main() { + let a = Pet { name: Some(String::new()), .. }; // Pet { name: Some(""), age: 42 } + let b = Pet::default(); // Pet { name: None, age: 42 } + assert_eq!(a.age, b.age); + // The following would be a compilation error: `name` needs to be specified + // let _ = Pet { .. }; +} +``` + +## `#[derive(Default)]` + +When deriving Default, the provided values are then used. On enum variants, +the variant must still be marked with `#[default]` and have all its fields +with default values. + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +enum A { + #[default] + B { + x: i32 = 0, + y: i32 = 0, + }, + C, +} +``` + +## Enum variants + +This feature also supports enum variants for both specifying default values +and `#[derive(Default)]`. + +## Interaction with `#[non_exhaustive]` + +A struct or enum variant marked with `#[non_exhaustive]` is not allowed to +have default field values. + +## Lints + +When manually implementing the `Default` trait for a type that has default +field values, if any of these are overridden in the impl the +`default_overrides_default_fields` lint will trigger. This lint is in place +to avoid surprising diverging behavior between `S { .. }` and +`S::default()`, where using the same type in both ways could result in +different values. The appropriate way to write a manual `Default` +implementation is to use the functional update syntax: + +```rust +#![feature(default_field_values)] + +struct Pet { + name: String, + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +impl Default for Pet { + fn default() -> Pet { + Pet { + name: "no-name".to_string(), + .. + } + } +} +``` "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "deprecated_safe", - description: r##"# `deprecated_safe` + label: "deprecated_suggestion", + description: r##"# `deprecated_suggestion` -The tracking issue for this feature is: [#94978] +Allows having using `suggestion` in the `#[deprecated]` attribute. + +The tracking issue for this feature is: [#94785] -[#94978]: https://github.com/rust-lang/rust/issues/94978 +[#94785]: https://github.com/rust-lang/rust/issues/94785 ------------------------ "##, @@ -5414,12 +6673,14 @@ The tracking issue for this feature is: [#94978] deny_since: None, }, Lint { - label: "deprecated_suggestion", - description: r##"# `deprecated_suggestion` + label: "deque_extend_front", + description: r##"# `deque_extend_front` -The tracking issue for this feature is: [#94785] -[#94785]: https://github.com/rust-lang/rust/issues/94785 + +The tracking issue for this feature is: [#146975] + +[#146975]: https://github.com/rust-lang/rust/issues/146975 ------------------------ "##, @@ -5436,6 +6697,101 @@ The tracking issue for this feature is: [#87121] [#87121]: https://github.com/rust-lang/rust/issues/87121 ------------------------ + +> **Note**: This feature supersedes [`box_patterns`]. + +This feature permits pattern matching on [smart pointers in the standard library] through their +`Deref` target types, either implicitly or with explicit `deref!(_)` patterns (the syntax of which +is currently a placeholder). + +```rust +#![feature(deref_patterns)] + +let mut v = vec![Box::new(Some(0))]; + +// Implicit dereferences are inserted when a pattern can match against the +// result of repeatedly dereferencing but can't match against a smart +// pointer itself. This works alongside match ergonomics for references. +if let [Some(x)] = &mut v { + *x += 1; +} + +// Explicit `deref!(_)` patterns may instead be used when finer control is +// needed, e.g. to dereference only a single smart pointer, or to bind the +// the result of dereferencing to a variable. +if let deref!([deref!(opt_x @ Some(1))]) = &mut v { + opt_x.as_mut().map(|x| *x += 1); +} + +assert_eq!(v, [Box::new(Some(2))]); +``` + +Without this feature, it may be necessary to introduce temporaries to represent dereferenced places +when matching on nested structures: + +```rust +let mut v = vec![Box::new(Some(0))]; +if let [b] = &mut *v { + if let Some(x) = &mut **b { + *x += 1; + } +} +if let [b] = &mut *v { + if let opt_x @ Some(1) = &mut **b { + opt_x.as_mut().map(|x| *x += 1); + } +} +assert_eq!(v, [Box::new(Some(2))]); +``` + +Like [`box_patterns`], deref patterns may move out of boxes: + +```rust +# #![feature(deref_patterns)] +struct NoCopy; +let deref!(x) = Box::new(NoCopy); +drop::<NoCopy>(x); +``` + +Additionally, `deref_patterns` implements changes to string and byte string literal patterns, +allowing then to be used in deref patterns: + +```rust +# #![feature(deref_patterns)] +match ("test".to_string(), Box::from("test"), b"test".to_vec()) { + ("test", "test", b"test") => {} + _ => panic!(), +} + +// This works through multiple layers of reference and smart pointer: +match (&Box::new(&"test".to_string()), &&&"test") { + ("test", "test") => {} + _ => panic!(), +} + +// `deref!("...")` syntax may also be used: +match "test".to_string() { + deref!("test") => {} + _ => panic!(), +} + +// Matching on slices and arrays using literals is possible elsewhere as well: +match *"test" { + "test" => {} + _ => panic!(), +} +match *b"test" { + b"test" => {} + _ => panic!(), +} +match *(b"test" as &[u8]) { + b"test" => {} + _ => panic!(), +} +``` + +[`box_patterns`]: ./box-patterns.md +[smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors "##, default_severity: Severity::Allow, warn_since: None, @@ -5445,6 +6801,8 @@ The tracking issue for this feature is: [#87121] label: "deref_pure_trait", description: r##"# `deref_pure_trait` + + The tracking issue for this feature is: [#87121] [#87121]: https://github.com/rust-lang/rust/issues/87121 @@ -5456,8 +6814,8 @@ The tracking issue for this feature is: [#87121] deny_since: None, }, Lint { - label: "derive_clone_copy", - description: r##"# `derive_clone_copy` + label: "derive_clone_copy_internals", + description: r##"# `derive_clone_copy_internals` This feature is internal to the Rust compiler and is not intended for general use. @@ -5471,6 +6829,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "derive_coerce_pointee", description: r##"# `derive_coerce_pointee` + + The tracking issue for this feature is: [#123430] [#123430]: https://github.com/rust-lang/rust/issues/123430 @@ -5485,7 +6845,11 @@ The tracking issue for this feature is: [#123430] label: "derive_const", description: r##"# `derive_const` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + + +The tracking issue for this feature is: [#118304] + +[#118304]: https://github.com/rust-lang/rust/issues/118304 ------------------------ "##, @@ -5494,8 +6858,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "derive_eq", - description: r##"# `derive_eq` + label: "derive_eq_internals", + description: r##"# `derive_eq_internals` This feature is internal to the Rust compiler and is not intended for general use. @@ -5506,9 +6870,75 @@ This feature is internal to the Rust compiler and is not intended for general us deny_since: None, }, Lint { + label: "derive_from", + description: r##"# `derive_from` + +Allows deriving the From trait on single-field structs. + +The tracking issue for this feature is: [#144889] + +[#144889]: https://github.com/rust-lang/rust/issues/144889 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "derive_macro_global_path", + description: r##"# `derive_macro_global_path` + + + +The tracking issue for this feature is: [#154645] + +[#154645]: https://github.com/rust-lang/rust/issues/154645 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "diagnostic_on_const", + description: r##"# `diagnostic_on_const` + +Allows giving non-const impls custom diagnostic messages if attempted to be used as const + +The tracking issue for this feature is: [#143874] + +[#143874]: https://github.com/rust-lang/rust/issues/143874 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "diagnostic_on_move", + description: r##"# `diagnostic_on_move` + +Allows giving on-move borrowck custom diagnostic messages for a type + +The tracking issue for this feature is: [#154181] + +[#154181]: https://github.com/rust-lang/rust/issues/154181 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "dir_entry_ext2", description: r##"# `dir_entry_ext2` + + The tracking issue for this feature is: [#85573] [#85573]: https://github.com/rust-lang/rust/issues/85573 @@ -5520,10 +6950,14 @@ The tracking issue for this feature is: [#85573] deny_since: None, }, Lint { - label: "discriminant_kind", - description: r##"# `discriminant_kind` + label: "dirfd", + description: r##"# `dirfd` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + + +The tracking issue for this feature is: [#120426] + +[#120426]: https://github.com/rust-lang/rust/issues/120426 ------------------------ "##, @@ -5532,8 +6966,26 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "dispatch_from_dyn", - description: r##"# `dispatch_from_dyn` + label: "dirhandle", + description: r##"# `dirhandle` + + + +The tracking issue for this feature is: [#120426] + +[#120426]: https://github.com/rust-lang/rust/issues/120426 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "discriminant_kind", + description: r##"# `discriminant_kind` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. @@ -5544,12 +6996,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "do_not_recommend", - description: r##"# `do_not_recommend` + label: "disjoint_bitor", + description: r##"# `disjoint_bitor` + -The tracking issue for this feature is: [#51992] -[#51992]: https://github.com/rust-lang/rust/issues/51992 +The tracking issue for this feature is: [#135758] + +[#135758]: https://github.com/rust-lang/rust/issues/135758 ------------------------ "##, @@ -5558,12 +7012,12 @@ The tracking issue for this feature is: [#51992] deny_since: None, }, Lint { - label: "doc_auto_cfg", - description: r##"# `doc_auto_cfg` + label: "dispatch_from_dyn", + description: r##"# `dispatch_from_dyn` -The tracking issue for this feature is: [#43781] -[#43781]: https://github.com/rust-lang/rust/issues/43781 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -5625,20 +7079,6 @@ pub struct Icon { deny_since: None, }, Lint { - label: "doc_cfg_hide", - description: r##"# `doc_cfg_hide` - -The tracking issue for this feature is: [#43781] - -[#43781]: https://github.com/rust-lang/rust/issues/43781 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "doc_masked", description: r##"# `doc_masked` @@ -5713,6 +7153,8 @@ See also its documentation in [the rustdoc book][rustdoc-book-notable_trait]. label: "downcast_unchecked", description: r##"# `downcast_unchecked` + + The tracking issue for this feature is: [#90850] [#90850]: https://github.com/rust-lang/rust/issues/90850 @@ -5727,6 +7169,8 @@ The tracking issue for this feature is: [#90850] label: "drain_keep_rest", description: r##"# `drain_keep_rest` + + The tracking issue for this feature is: [#101122] [#101122]: https://github.com/rust-lang/rust/issues/101122 @@ -5738,9 +7182,27 @@ The tracking issue for this feature is: [#101122] deny_since: None, }, Lint { + label: "drop_guard", + description: r##"# `drop_guard` + + + +The tracking issue for this feature is: [#144426] + +[#144426]: https://github.com/rust-lang/rust/issues/144426 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "dropck_eyepatch", description: r##"# `dropck_eyepatch` +Allows using the `may_dangle` attribute (RFC 1327). + The tracking issue for this feature is: [#34761] [#34761]: https://github.com/rust-lang/rust/issues/34761 @@ -5755,6 +7217,8 @@ The tracking issue for this feature is: [#34761] label: "duration_constants", description: r##"# `duration_constants` + + The tracking issue for this feature is: [#57391] [#57391]: https://github.com/rust-lang/rust/issues/57391 @@ -5782,9 +7246,27 @@ Add the methods `from_days` and `from_weeks` to `Duration`. deny_since: None, }, Lint { + label: "duration_integer_division", + description: r##"# `duration_integer_division` + + + +The tracking issue for this feature is: [#149573] + +[#149573]: https://github.com/rust-lang/rust/issues/149573 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "duration_millis_float", description: r##"# `duration_millis_float` + + The tracking issue for this feature is: [#122451] [#122451]: https://github.com/rust-lang/rust/issues/122451 @@ -5799,6 +7281,8 @@ The tracking issue for this feature is: [#122451] label: "duration_units", description: r##"# `duration_units` + + The tracking issue for this feature is: [#120301] [#120301]: https://github.com/rust-lang/rust/issues/120301 @@ -5810,12 +7294,12 @@ The tracking issue for this feature is: [#120301] deny_since: None, }, Lint { - label: "dyn_compatible_for_dispatch", - description: r##"# `dyn_compatible_for_dispatch` + label: "edition_panic", + description: r##"# `edition_panic` -The tracking issue for this feature is: [#43561] -[#43561]: https://github.com/rust-lang/rust/issues/43561 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -5824,12 +7308,14 @@ The tracking issue for this feature is: [#43561] deny_since: None, }, Lint { - label: "dyn_star", - description: r##"# `dyn_star` + label: "effective_target_features", + description: r##"# `effective_target_features` -The tracking issue for this feature is: [#102425] +Allows features to allow target_feature to better interact with traits. -[#102425]: https://github.com/rust-lang/rust/issues/102425 +The tracking issue for this feature is: [#143352] + +[#143352]: https://github.com/rust-lang/rust/issues/143352 ------------------------ "##, @@ -5838,8 +7324,10 @@ The tracking issue for this feature is: [#102425] deny_since: None, }, Lint { - label: "edition_panic", - description: r##"# `edition_panic` + label: "eii_internals", + description: r##"# `eii_internals` + +Implementation details of externally implementable items This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. @@ -5850,12 +7338,30 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { + label: "ergonomic_clones", + description: r##"# `ergonomic_clones` + +Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }` + +The tracking issue for this feature is: [#132290] + +[#132290]: https://github.com/rust-lang/rust/issues/132290 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "ermsb_target_feature", description: r##"# `ermsb_target_feature` -The tracking issue for this feature is: [#44839] +ermsb target feature on x86. + +The tracking issue for this feature is: [#150249] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +[#150249]: https://github.com/rust-lang/rust/issues/150249 ------------------------ "##, @@ -5867,6 +7373,8 @@ The tracking issue for this feature is: [#44839] label: "error_generic_member_access", description: r##"# `error_generic_member_access` + + The tracking issue for this feature is: [#99301] [#99301]: https://github.com/rust-lang/rust/issues/99301 @@ -5881,6 +7389,8 @@ The tracking issue for this feature is: [#99301] label: "error_iter", description: r##"# `error_iter` + + The tracking issue for this feature is: [#58520] [#58520]: https://github.com/rust-lang/rust/issues/58520 @@ -5895,6 +7405,8 @@ The tracking issue for this feature is: [#58520] label: "error_reporter", description: r##"# `error_reporter` + + The tracking issue for this feature is: [#90172] [#90172]: https://github.com/rust-lang/rust/issues/90172 @@ -5909,6 +7421,8 @@ The tracking issue for this feature is: [#90172] label: "error_type_id", description: r##"# `error_type_id` + + The tracking issue for this feature is: [#60784] [#60784]: https://github.com/rust-lang/rust/issues/60784 @@ -5920,9 +7434,43 @@ The tracking issue for this feature is: [#60784] deny_since: None, }, Lint { + label: "exact_bitshifts", + description: r##"# `exact_bitshifts` + + + +The tracking issue for this feature is: [#144336] + +[#144336]: https://github.com/rust-lang/rust/issues/144336 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "exact_div", + description: r##"# `exact_div` + + + +The tracking issue for this feature is: [#139911] + +[#139911]: https://github.com/rust-lang/rust/issues/139911 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "exact_size_is_empty", description: r##"# `exact_size_is_empty` + + The tracking issue for this feature is: [#35428] [#35428]: https://github.com/rust-lang/rust/issues/35428 @@ -5937,6 +7485,8 @@ The tracking issue for this feature is: [#35428] label: "exclusive_wrapper", description: r##"# `exclusive_wrapper` + + The tracking issue for this feature is: [#98407] [#98407]: https://github.com/rust-lang/rust/issues/98407 @@ -5951,6 +7501,8 @@ The tracking issue for this feature is: [#98407] label: "exhaustive_patterns", description: r##"# `exhaustive_patterns` +Allows exhaustive pattern matching on types that contain uninhabited types. + The tracking issue for this feature is: [#51085] [#51085]: https://github.com/rust-lang/rust/issues/51085 @@ -5965,6 +7517,8 @@ The tracking issue for this feature is: [#51085] label: "exit_status_error", description: r##"# `exit_status_error` + + The tracking issue for this feature is: [#84908] [#84908]: https://github.com/rust-lang/rust/issues/84908 @@ -5979,6 +7533,8 @@ The tracking issue for this feature is: [#84908] label: "exitcode_exit_method", description: r##"# `exitcode_exit_method` + + The tracking issue for this feature is: [#97100] [#97100]: https://github.com/rust-lang/rust/issues/97100 @@ -5990,9 +7546,43 @@ The tracking issue for this feature is: [#97100] deny_since: None, }, Lint { + label: "explicit_extern_abis", + description: r##"# `explicit_extern_abis` + +The tracking issue for this feature is: [#134986] + +------ + +Disallow `extern` without an explicit ABI. We should write `extern "C"` +(or another ABI) instead of just `extern`. + +By making the ABI explicit, it becomes much clearer that "C" is just one of the +possible choices, rather than the "standard" way for external functions. +Removing the default makes it easier to add a new ABI on equal footing as "C". + +```rust,editionfuture,compile_fail +#![feature(explicit_extern_abis)] + +extern fn function1() {} // ERROR `extern` declarations without an explicit ABI + // are disallowed + +extern "C" fn function2() {} // compiles + +extern "aapcs" fn function3() {} // compiles +``` + +[#134986]: https://github.com/rust-lang/rust/issues/134986 +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "explicit_tail_calls", description: r##"# `explicit_tail_calls` +Allows explicit tail calls via `become` expression. + The tracking issue for this feature is: [#112788] [#112788]: https://github.com/rust-lang/rust/issues/112788 @@ -6004,9 +7594,27 @@ The tracking issue for this feature is: [#112788] deny_since: None, }, Lint { + label: "export_stable", + description: r##"# `export_stable` + +Allows using `#[export_stable]` which indicates that an item is exportable. + +The tracking issue for this feature is: [#139939] + +[#139939]: https://github.com/rust-lang/rust/issues/139939 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "extend_one", description: r##"# `extend_one` + + The tracking issue for this feature is: [#72631] [#72631]: https://github.com/rust-lang/rust/issues/72631 @@ -6021,6 +7629,8 @@ The tracking issue for this feature is: [#72631] label: "extend_one_unchecked", description: r##"# `extend_one_unchecked` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -6030,12 +7640,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "extern_types", - description: r##"# `extern_types` + label: "extern_item_impls", + description: r##"# `extern_item_impls` -The tracking issue for this feature is: [#43467] +Externally implementable items -[#43467]: https://github.com/rust-lang/rust/issues/43467 +The tracking issue for this feature is: [#125418] + +[#125418]: https://github.com/rust-lang/rust/issues/125418 ------------------------ "##, @@ -6044,12 +7656,14 @@ The tracking issue for this feature is: [#43467] deny_since: None, }, Lint { - label: "extract_if", - description: r##"# `extract_if` + label: "extern_types", + description: r##"# `extern_types` -The tracking issue for this feature is: [#43244] +Allows defining `extern type`s. -[#43244]: https://github.com/rust-lang/rust/issues/43244 +The tracking issue for this feature is: [#43467] + +[#43467]: https://github.com/rust-lang/rust/issues/43467 ------------------------ "##, @@ -6067,7 +7681,7 @@ The tracking issue for this feature is: [#116909] --- -Enable the `f128` type for IEEE 128-bit floating numbers (quad precision). +Enable the `f128` type for IEEE 128-bit floating numbers (quad precision). "##, default_severity: Severity::Allow, warn_since: None, @@ -6083,7 +7697,7 @@ The tracking issue for this feature is: [#116909] --- -Enable the `f16` type for IEEE 16-bit floating numbers (half precision). +Enable the `f16` type for IEEE 16-bit floating numbers (half precision). "##, default_severity: Severity::Allow, warn_since: None, @@ -6102,18 +7716,6 @@ This feature is internal to the Rust compiler and is not intended for general us deny_since: None, }, Lint { - label: "fd_read", - description: r##"# `fd_read` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "ffi_const", description: r##"# `ffi_const` @@ -6236,9 +7838,41 @@ against are compatible with those of the `#[ffi_pure]`. deny_since: None, }, Lint { + label: "field_projections", + description: r##"# `field_projections` + +Experimental field projections. + +The tracking issue for this feature is: [#145383] + +[#145383]: https://github.com/rust-lang/rust/issues/145383 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "field_representing_type_raw", + description: r##"# `field_representing_type_raw` + +Implementation details of field representing types. + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "file_buffered", description: r##"# `file_buffered` + + The tracking issue for this feature is: [#130804] [#130804]: https://github.com/rust-lang/rust/issues/130804 @@ -6250,12 +7884,78 @@ The tracking issue for this feature is: [#130804] deny_since: None, }, Lint { - label: "file_lock", - description: r##"# `file_lock` + label: "final_associated_functions", + description: r##"# `final_associated_functions` + +Allows marking trait functions as `final` to prevent overriding impls + +The tracking issue for this feature is: [#131179] + +[#131179]: https://github.com/rust-lang/rust/issues/131179 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "float_algebraic", + description: r##"# `float_algebraic` -The tracking issue for this feature is: [#130994] -[#130994]: https://github.com/rust-lang/rust/issues/130994 + +The tracking issue for this feature is: [#136469] + +[#136469]: https://github.com/rust-lang/rust/issues/136469 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "float_bits_const", + description: r##"# `float_bits_const` + + + +The tracking issue for this feature is: [#151073] + +[#151073]: https://github.com/rust-lang/rust/issues/151073 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "float_erf", + description: r##"# `float_erf` + + + +The tracking issue for this feature is: [#136321] + +[#136321]: https://github.com/rust-lang/rust/issues/136321 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "float_exact_integer_constants", + description: r##"# `float_exact_integer_constants` + + + +The tracking issue for this feature is: [#152466] + +[#152466]: https://github.com/rust-lang/rust/issues/152466 ------------------------ "##, @@ -6267,6 +7967,8 @@ The tracking issue for this feature is: [#130994] label: "float_gamma", description: r##"# `float_gamma` + + The tracking issue for this feature is: [#99842] [#99842]: https://github.com/rust-lang/rust/issues/99842 @@ -6281,6 +7983,8 @@ The tracking issue for this feature is: [#99842] label: "float_minimum_maximum", description: r##"# `float_minimum_maximum` + + The tracking issue for this feature is: [#91079] [#91079]: https://github.com/rust-lang/rust/issues/91079 @@ -6292,12 +7996,10 @@ The tracking issue for this feature is: [#91079] deny_since: None, }, Lint { - label: "float_next_up_down", - description: r##"# `float_next_up_down` - -The tracking issue for this feature is: [#91399] + label: "flt2dec", + description: r##"# `flt2dec` -[#91399]: https://github.com/rust-lang/rust/issues/91399 +This feature is internal to the Rust compiler and is not intended for general use. ------------------------ "##, @@ -6306,10 +8008,14 @@ The tracking issue for this feature is: [#91399] deny_since: None, }, Lint { - label: "flt2dec", - description: r##"# `flt2dec` + label: "fmt_arguments_from_str", + description: r##"# `fmt_arguments_from_str` -This feature is internal to the Rust compiler and is not intended for general use. + + +The tracking issue for this feature is: [#148905] + +[#148905]: https://github.com/rust-lang/rust/issues/148905 ------------------------ "##, @@ -6321,6 +8027,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "fmt_debug", description: r##"# `fmt_debug` +Controlling the behavior of fmt::Debug + The tracking issue for this feature is: [#129709] [#129709]: https://github.com/rust-lang/rust/issues/129709 @@ -6335,6 +8043,8 @@ The tracking issue for this feature is: [#129709] label: "fmt_helpers_for_derive", description: r##"# `fmt_helpers_for_derive` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -6359,6 +8069,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "fn_align", description: r##"# `fn_align` +Allows using `#[align(...)]` on function items + The tracking issue for this feature is: [#82232] [#82232]: https://github.com/rust-lang/rust/issues/82232 @@ -6373,6 +8085,8 @@ The tracking issue for this feature is: [#82232] label: "fn_delegation", description: r##"# `fn_delegation` +Support delegating implementation of functions to other already implemented functions. + The tracking issue for this feature is: [#118212] [#118212]: https://github.com/rust-lang/rust/issues/118212 @@ -6387,6 +8101,8 @@ The tracking issue for this feature is: [#118212] label: "fn_ptr_trait", description: r##"# `fn_ptr_trait` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -6441,6 +8157,8 @@ fn main() { label: "forget_unsized", description: r##"# `forget_unsized` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -6453,6 +8171,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "format_args_nl", description: r##"# `format_args_nl` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -6465,6 +8185,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "formatting_options", description: r##"# `formatting_options` + + The tracking issue for this feature is: [#118117] [#118117]: https://github.com/rust-lang/rust/issues/118117 @@ -6479,6 +8201,8 @@ The tracking issue for this feature is: [#118117] label: "freeze", description: r##"# `freeze` + + The tracking issue for this feature is: [#121675] [#121675]: https://github.com/rust-lang/rust/issues/121675 @@ -6493,6 +8217,8 @@ The tracking issue for this feature is: [#121675] label: "freeze_impls", description: r##"# `freeze_impls` +Allows impls for the Freeze trait. + The tracking issue for this feature is: [#121675] [#121675]: https://github.com/rust-lang/rust/issues/121675 @@ -6504,9 +8230,59 @@ The tracking issue for this feature is: [#121675] deny_since: None, }, Lint { + label: "frontmatter", + description: r##"# `frontmatter` + +The tracking issue for this feature is: [#136889] + +------ + +The `frontmatter` feature allows an extra metadata block at the top of files for consumption by +external tools. For example, it can be used by [`cargo-script`] files to specify dependencies. + +```rust +#!/usr/bin/env -S cargo -Zscript +--- +[dependencies] +libc = "0.2.172" +--- +#![feature(frontmatter)] +# mod libc { pub type c_int = i32; } + +fn main() { + let x: libc::c_int = 1i32; +} +``` + +[#136889]: https://github.com/rust-lang/rust/issues/136889 +[`cargo-script`]: https://rust-lang.github.io/rfcs/3502-cargo-script.html +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "fs_set_times", + description: r##"# `fs_set_times` + + + +The tracking issue for this feature is: [#147455] + +[#147455]: https://github.com/rust-lang/rust/issues/147455 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "fundamental", description: r##"# `fundamental` +Allows using the `#[fundamental]` attribute. + The tracking issue for this feature is: [#29635] [#29635]: https://github.com/rust-lang/rust/issues/29635 @@ -6518,9 +8294,27 @@ The tracking issue for this feature is: [#29635] deny_since: None, }, Lint { + label: "funnel_shifts", + description: r##"# `funnel_shifts` + + + +The tracking issue for this feature is: [#145686] + +[#145686]: https://github.com/rust-lang/rust/issues/145686 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "future_join", description: r##"# `future_join` + + The tracking issue for this feature is: [#91642] [#91642]: https://github.com/rust-lang/rust/issues/91642 @@ -6535,6 +8329,8 @@ The tracking issue for this feature is: [#91642] label: "gen_blocks", description: r##"# `gen_blocks` +Allows defining gen blocks and `gen fn`. + The tracking issue for this feature is: [#117078] [#117078]: https://github.com/rust-lang/rust/issues/117078 @@ -6549,9 +8345,9 @@ The tracking issue for this feature is: [#117078] label: "gen_future", description: r##"# `gen_future` -The tracking issue for this feature is: [#50547] -[#50547]: https://github.com/rust-lang/rust/issues/50547 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -6560,12 +8356,12 @@ The tracking issue for this feature is: [#50547] deny_since: None, }, Lint { - label: "generic_arg_infer", - description: r##"# `generic_arg_infer` + label: "generic_assert", + description: r##"# `generic_assert` -The tracking issue for this feature is: [#85077] +Outputs useful `assert!` messages -[#85077]: https://github.com/rust-lang/rust/issues/85077 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -6574,10 +8370,14 @@ The tracking issue for this feature is: [#85077] deny_since: None, }, Lint { - label: "generic_assert", - description: r##"# `generic_assert` + label: "generic_assert_internals", + description: r##"# `generic_assert_internals` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + + +The tracking issue for this feature is: [#44838] + +[#44838]: https://github.com/rust-lang/rust/issues/44838 ------------------------ "##, @@ -6586,12 +8386,30 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "generic_assert_internals", - description: r##"# `generic_assert_internals` + label: "generic_atomic", + description: r##"# `generic_atomic` -The tracking issue for this feature is: [#44838] -[#44838]: https://github.com/rust-lang/rust/issues/44838 + +The tracking issue for this feature is: [#130539] + +[#130539]: https://github.com/rust-lang/rust/issues/130539 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "generic_const_args", + description: r##"# `generic_const_args` + +Allows using generics in more complex const expressions, based on definitional equality. + +The tracking issue for this feature is: [#151972] + +[#151972]: https://github.com/rust-lang/rust/issues/151972 ------------------------ "##, @@ -6603,6 +8421,8 @@ The tracking issue for this feature is: [#44838] label: "generic_const_exprs", description: r##"# `generic_const_exprs` +Allows non-trivial generic constants which have to have wfness manually propagated to callers + The tracking issue for this feature is: [#76560] [#76560]: https://github.com/rust-lang/rust/issues/76560 @@ -6617,6 +8437,8 @@ The tracking issue for this feature is: [#76560] label: "generic_const_items", description: r##"# `generic_const_items` +Allows generic parameters and where-clauses on free & associated const items. + The tracking issue for this feature is: [#113521] [#113521]: https://github.com/rust-lang/rust/issues/113521 @@ -6628,12 +8450,30 @@ The tracking issue for this feature is: [#113521] deny_since: None, }, Lint { - label: "get_many_mut", - description: r##"# `get_many_mut` + label: "generic_const_parameter_types", + description: r##"# `generic_const_parameter_types` + +Allows the type of const generics to depend on generic parameters + +The tracking issue for this feature is: [#137626] + +[#137626]: https://github.com/rust-lang/rust/issues/137626 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "generic_pattern_types", + description: r##"# `generic_pattern_types` -The tracking issue for this feature is: [#104642] +Allows any generic constants being used as pattern type range ends -[#104642]: https://github.com/rust-lang/rust/issues/104642 +The tracking issue for this feature is: [#136574] + +[#136574]: https://github.com/rust-lang/rust/issues/136574 ------------------------ "##, @@ -6642,8 +8482,10 @@ The tracking issue for this feature is: [#104642] deny_since: None, }, Lint { - label: "get_many_mut_helpers", - description: r##"# `get_many_mut_helpers` + label: "get_disjoint_mut_helpers", + description: r##"# `get_disjoint_mut_helpers` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. @@ -6657,6 +8499,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "get_mut_unchecked", description: r##"# `get_mut_unchecked` + + The tracking issue for this feature is: [#63292] [#63292]: https://github.com/rust-lang/rust/issues/63292 @@ -6668,9 +8512,27 @@ The tracking issue for this feature is: [#63292] deny_since: None, }, Lint { + label: "gethostname", + description: r##"# `gethostname` + + + +The tracking issue for this feature is: [#135142] + +[#135142]: https://github.com/rust-lang/rust/issues/135142 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "global_registration", description: r##"# `global_registration` +Allows registering static items globally, possibly across crates, to iterate over at runtime. + The tracking issue for this feature is: [#125119] [#125119]: https://github.com/rust-lang/rust/issues/125119 @@ -6682,9 +8544,25 @@ The tracking issue for this feature is: [#125119] deny_since: None, }, Lint { + label: "gpu_intrinsics", + description: r##"# `gpu_intrinsics` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "guard_patterns", description: r##"# `guard_patterns` +Allows using guards in patterns. + The tracking issue for this feature is: [#129967] [#129967]: https://github.com/rust-lang/rust/issues/129967 @@ -6732,12 +8610,12 @@ fn main() { deny_since: None, }, Lint { - label: "hash_extract_if", - description: r##"# `hash_extract_if` + label: "hash_map_internals", + description: r##"# `hash_map_internals` + -The tracking issue for this feature is: [#59618] -[#59618]: https://github.com/rust-lang/rust/issues/59618 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -6746,12 +8624,14 @@ The tracking issue for this feature is: [#59618] deny_since: None, }, Lint { - label: "hash_raw_entry", - description: r##"# `hash_raw_entry` + label: "hash_map_macro", + description: r##"# `hash_map_macro` + -The tracking issue for this feature is: [#56167] -[#56167]: https://github.com/rust-lang/rust/issues/56167 +The tracking issue for this feature is: [#144032] + +[#144032]: https://github.com/rust-lang/rust/issues/144032 ------------------------ "##, @@ -6763,6 +8643,8 @@ The tracking issue for this feature is: [#56167] label: "hash_set_entry", description: r##"# `hash_set_entry` + + The tracking issue for this feature is: [#60896] [#60896]: https://github.com/rust-lang/rust/issues/60896 @@ -6777,6 +8659,8 @@ The tracking issue for this feature is: [#60896] label: "hasher_prefixfree_extras", description: r##"# `hasher_prefixfree_extras` + + The tracking issue for this feature is: [#96762] [#96762]: https://github.com/rust-lang/rust/issues/96762 @@ -6791,6 +8675,8 @@ The tracking issue for this feature is: [#96762] label: "hashmap_internals", description: r##"# `hashmap_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -6803,9 +8689,11 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "hexagon_target_feature", description: r##"# `hexagon_target_feature` -The tracking issue for this feature is: [#44839] +Target features on hexagon. + +The tracking issue for this feature is: [#150250] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +[#150250]: https://github.com/rust-lang/rust/issues/150250 ------------------------ "##, @@ -6817,6 +8705,8 @@ The tracking issue for this feature is: [#44839] label: "hint_must_use", description: r##"# `hint_must_use` + + The tracking issue for this feature is: [#94745] [#94745]: https://github.com/rust-lang/rust/issues/94745 @@ -6828,12 +8718,30 @@ The tracking issue for this feature is: [#94745] deny_since: None, }, Lint { - label: "if_let_guard", - description: r##"# `if_let_guard` + label: "hint_prefetch", + description: r##"# `hint_prefetch` + + + +The tracking issue for this feature is: [#146941] + +[#146941]: https://github.com/rust-lang/rust/issues/146941 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "impl_restriction", + description: r##"# `impl_restriction` -The tracking issue for this feature is: [#51114] +Allows `impl(crate) trait Foo` restrictions. -[#51114]: https://github.com/rust-lang/rust/issues/51114 +The tracking issue for this feature is: [#105077] + +[#105077]: https://github.com/rust-lang/rust/issues/105077 ------------------------ "##, @@ -6845,6 +8753,8 @@ The tracking issue for this feature is: [#51114] label: "impl_trait_in_assoc_type", description: r##"# `impl_trait_in_assoc_type` +Allows `impl Trait` to be used inside associated types (RFC 2515). + The tracking issue for this feature is: [#63063] [#63063]: https://github.com/rust-lang/rust/issues/63063 @@ -6856,9 +8766,27 @@ The tracking issue for this feature is: [#63063] deny_since: None, }, Lint { + label: "impl_trait_in_bindings", + description: r##"# `impl_trait_in_bindings` + +Allows `impl Trait` in bindings (`let`). + +The tracking issue for this feature is: [#63065] + +[#63065]: https://github.com/rust-lang/rust/issues/63065 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "impl_trait_in_fn_trait_return", description: r##"# `impl_trait_in_fn_trait_return` +Allows `impl Trait` as output type in `Fn` traits in return position of functions. + The tracking issue for this feature is: [#99697] [#99697]: https://github.com/rust-lang/rust/issues/99697 @@ -6870,9 +8798,40 @@ The tracking issue for this feature is: [#99697] deny_since: None, }, Lint { + label: "import_trait_associated_functions", + description: r##"# import_trait_associated_functions + +The tracking issue for this feature is: [#134691] + +[#134691]: https://github.com/rust-lang/rust/issues/134691 + +------------------------ + +This feature allows importing associated functions and constants from traits and then using them like regular items. + +```rust +#![feature(import_trait_associated_functions)] + +use std::ops::Add::add; + +fn main() { + let numbers = vec![1, 2, 3, 4, 5, 6]; + let sum = numbers.into_iter().reduce(add); // instead of `.reduce(Add:add)` + + assert_eq!(sum, Some(21)); +} +``` +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "inherent_associated_types", description: r##"# `inherent_associated_types` +Allows associated types in inherent impls. + The tracking issue for this feature is: [#8995] [#8995]: https://github.com/rust-lang/rust/issues/8995 @@ -6884,39 +8843,60 @@ The tracking issue for this feature is: [#8995] deny_since: None, }, Lint { - label: "inline_const_pat", - description: r##"# `inline_const_pat` + label: "inplace_iteration", + description: r##"# `inplace_iteration` -The tracking issue for this feature is: [#76001] ------- -This feature allows you to use inline constant expressions in pattern position: +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. -```rust -#![feature(inline_const_pat)] +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "int_format_into", + description: r##"# `int_format_into` -const fn one() -> i32 { 1 } -let some_int = 3; -match some_int { - const { 1 + 2 } => println!("Matched 1 + 2"), - const { one() } => println!("Matched const fn returning 1"), - _ => println!("Didn't match anything :("), -} -``` -[#76001]: https://github.com/rust-lang/rust/issues/76001 +The tracking issue for this feature is: [#138215] + +[#138215]: https://github.com/rust-lang/rust/issues/138215 + +------------------------ "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "inplace_iteration", - description: r##"# `inplace_iteration` + label: "int_from_ascii", + description: r##"# `int_from_ascii` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + + +The tracking issue for this feature is: [#134821] + +[#134821]: https://github.com/rust-lang/rust/issues/134821 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "int_lowest_highest_one", + description: r##"# `int_lowest_highest_one` + + + +The tracking issue for this feature is: [#145203] + +[#145203]: https://github.com/rust-lang/rust/issues/145203 ------------------------ "##, @@ -6928,6 +8908,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "int_roundings", description: r##"# `int_roundings` + + The tracking issue for this feature is: [#88581] [#88581]: https://github.com/rust-lang/rust/issues/88581 @@ -6942,6 +8924,8 @@ The tracking issue for this feature is: [#88581] label: "integer_atomics", description: r##"# `integer_atomics` + + The tracking issue for this feature is: [#99069] [#99069]: https://github.com/rust-lang/rust/issues/99069 @@ -6953,24 +8937,14 @@ The tracking issue for this feature is: [#99069] deny_since: None, }, Lint { - label: "integer_sign_cast", - description: r##"# `integer_sign_cast` + label: "integer_extend_truncate", + description: r##"# `integer_extend_truncate` -The tracking issue for this feature is: [#125882] -[#125882]: https://github.com/rust-lang/rust/issues/125882 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "internal_impls_macro", - description: r##"# `internal_impls_macro` +The tracking issue for this feature is: [#154330] -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +[#154330]: https://github.com/rust-lang/rust/issues/154330 ------------------------ "##, @@ -7068,9 +9042,9 @@ with any regular function. Various intrinsics have native MIR operations that they correspond to. Instead of requiring backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass will convert the calls to the MIR operation. Backends do not need to know about these intrinsics -at all. These intrinsics only make sense without a body, and can either be declared as a "rust-intrinsic" -or as a `#[rustc_intrinsic]`. The body is never used, as calls to the intrinsic do not exist -anymore after MIR analyses. +at all. These intrinsics only make sense without a body, and can be declared as a `#[rustc_intrinsic]`. +The body is never used as the lowering pass implements support for all backends, so we never have to +use the fallback logic. ## Intrinsics without fallback logic @@ -7078,33 +9052,14 @@ These must be implemented by all backends. ### `#[rustc_intrinsic]` declarations -These are written like intrinsics with fallback bodies, but the body is irrelevant. -Use `loop {}` for the body or call the intrinsic recursively and add -`#[rustc_intrinsic_must_be_overridden]` to the function to ensure that backends don't -invoke the body. - -### Legacy extern ABI based intrinsics - -These are imported as if they were FFI functions, with the special -`rust-intrinsic` ABI. For example, if one was in a freestanding -context, but wished to be able to `transmute` between types, and -perform efficient pointer arithmetic, one would import those functions -via a declaration like - +These are written without a body: ```rust #![feature(intrinsics)] #![allow(internal_features)] -# fn main() {} -extern "rust-intrinsic" { - fn transmute<T, U>(x: T) -> U; - - fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; -} +#[rustc_intrinsic] +pub fn abort() -> !; ``` - -As with any other FFI functions, these are by default always `unsafe` to call. -You can add `#[rustc_safe_intrinsic]` to the intrinsic to make it safe to call. "##, default_severity: Severity::Allow, warn_since: None, @@ -7114,6 +9069,8 @@ You can add `#[rustc_safe_intrinsic]` to the intrinsic to make it safe to call. label: "io_const_error", description: r##"# `io_const_error` + + The tracking issue for this feature is: [#133448] [#133448]: https://github.com/rust-lang/rust/issues/133448 @@ -7128,6 +9085,8 @@ The tracking issue for this feature is: [#133448] label: "io_const_error_internals", description: r##"# `io_const_error_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -7140,6 +9099,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "io_error_inprogress", description: r##"# `io_error_inprogress` + + The tracking issue for this feature is: [#130840] [#130840]: https://github.com/rust-lang/rust/issues/130840 @@ -7154,6 +9115,8 @@ The tracking issue for this feature is: [#130840] label: "io_error_more", description: r##"# `io_error_more` + + The tracking issue for this feature is: [#86442] [#86442]: https://github.com/rust-lang/rust/issues/86442 @@ -7168,6 +9131,8 @@ The tracking issue for this feature is: [#86442] label: "io_error_uncategorized", description: r##"# `io_error_uncategorized` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -7180,6 +9145,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "io_slice_as_bytes", description: r##"# `io_slice_as_bytes` + + The tracking issue for this feature is: [#132818] [#132818]: https://github.com/rust-lang/rust/issues/132818 @@ -7194,6 +9161,8 @@ The tracking issue for this feature is: [#132818] label: "ip", description: r##"# `ip` + + The tracking issue for this feature is: [#27709] [#27709]: https://github.com/rust-lang/rust/issues/27709 @@ -7205,12 +9174,28 @@ The tracking issue for this feature is: [#27709] deny_since: None, }, Lint { - label: "ip_from", - description: r##"# `ip_from` + label: "ip_as_octets", + description: r##"# `ip_as_octets` + + + +The tracking issue for this feature is: [#137259] + +[#137259]: https://github.com/rust-lang/rust/issues/137259 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "ip_multicast_reserved", + description: r##"# `ip_multicast_reserved` -The tracking issue for this feature is: [#131360] -[#131360]: https://github.com/rust-lang/rust/issues/131360 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -7222,6 +9207,8 @@ The tracking issue for this feature is: [#131360] label: "is_ascii_octdigit", description: r##"# `is_ascii_octdigit` + + The tracking issue for this feature is: [#101288] [#101288]: https://github.com/rust-lang/rust/issues/101288 @@ -7236,6 +9223,8 @@ The tracking issue for this feature is: [#101288] label: "is_loongarch_feature_detected", description: r##"# `is_loongarch_feature_detected` + + The tracking issue for this feature is: [#117425] [#117425]: https://github.com/rust-lang/rust/issues/117425 @@ -7250,6 +9239,8 @@ The tracking issue for this feature is: [#117425] label: "is_riscv_feature_detected", description: r##"# `is_riscv_feature_detected` + + The tracking issue for this feature is: [#111192] [#111192]: https://github.com/rust-lang/rust/issues/111192 @@ -7261,12 +9252,14 @@ The tracking issue for this feature is: [#111192] deny_since: None, }, Lint { - label: "iter_advance_by", - description: r##"# `iter_advance_by` + label: "isolate_most_least_significant_one", + description: r##"# `isolate_most_least_significant_one` -The tracking issue for this feature is: [#77404] -[#77404]: https://github.com/rust-lang/rust/issues/77404 + +The tracking issue for this feature is: [#136909] + +[#136909]: https://github.com/rust-lang/rust/issues/136909 ------------------------ "##, @@ -7275,12 +9268,14 @@ The tracking issue for this feature is: [#77404] deny_since: None, }, Lint { - label: "iter_array_chunks", - description: r##"# `iter_array_chunks` + label: "iter_advance_by", + description: r##"# `iter_advance_by` -The tracking issue for this feature is: [#100450] -[#100450]: https://github.com/rust-lang/rust/issues/100450 + +The tracking issue for this feature is: [#77404] + +[#77404]: https://github.com/rust-lang/rust/issues/77404 ------------------------ "##, @@ -7289,12 +9284,14 @@ The tracking issue for this feature is: [#100450] deny_since: None, }, Lint { - label: "iter_chain", - description: r##"# `iter_chain` + label: "iter_array_chunks", + description: r##"# `iter_array_chunks` -The tracking issue for this feature is: [#125964] -[#125964]: https://github.com/rust-lang/rust/issues/125964 + +The tracking issue for this feature is: [#100450] + +[#100450]: https://github.com/rust-lang/rust/issues/100450 ------------------------ "##, @@ -7306,6 +9303,8 @@ The tracking issue for this feature is: [#125964] label: "iter_collect_into", description: r##"# `iter_collect_into` + + The tracking issue for this feature is: [#94780] [#94780]: https://github.com/rust-lang/rust/issues/94780 @@ -7320,6 +9319,8 @@ The tracking issue for this feature is: [#94780] label: "iter_from_coroutine", description: r##"# `iter_from_coroutine` + + The tracking issue for this feature is: [#43122] [#43122]: https://github.com/rust-lang/rust/issues/43122 @@ -7334,6 +9335,8 @@ The tracking issue for this feature is: [#43122] label: "iter_intersperse", description: r##"# `iter_intersperse` + + The tracking issue for this feature is: [#79524] [#79524]: https://github.com/rust-lang/rust/issues/79524 @@ -7348,6 +9351,8 @@ The tracking issue for this feature is: [#79524] label: "iter_is_partitioned", description: r##"# `iter_is_partitioned` + + The tracking issue for this feature is: [#62544] [#62544]: https://github.com/rust-lang/rust/issues/62544 @@ -7359,9 +9364,27 @@ The tracking issue for this feature is: [#62544] deny_since: None, }, Lint { + label: "iter_macro", + description: r##"# `iter_macro` + + + +The tracking issue for this feature is: [#142269] + +[#142269]: https://github.com/rust-lang/rust/issues/142269 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "iter_map_windows", description: r##"# `iter_map_windows` + + The tracking issue for this feature is: [#87155] [#87155]: https://github.com/rust-lang/rust/issues/87155 @@ -7376,6 +9399,8 @@ The tracking issue for this feature is: [#87155] label: "iter_next_chunk", description: r##"# `iter_next_chunk` + + The tracking issue for this feature is: [#98326] [#98326]: https://github.com/rust-lang/rust/issues/98326 @@ -7390,6 +9415,8 @@ The tracking issue for this feature is: [#98326] label: "iter_order_by", description: r##"# `iter_order_by` + + The tracking issue for this feature is: [#64295] [#64295]: https://github.com/rust-lang/rust/issues/64295 @@ -7404,6 +9431,8 @@ The tracking issue for this feature is: [#64295] label: "iter_partition_in_place", description: r##"# `iter_partition_in_place` + + The tracking issue for this feature is: [#62543] [#62543]: https://github.com/rust-lang/rust/issues/62543 @@ -7418,6 +9447,8 @@ The tracking issue for this feature is: [#62543] label: "iterator_try_collect", description: r##"# `iterator_try_collect` + + The tracking issue for this feature is: [#94047] [#94047]: https://github.com/rust-lang/rust/issues/94047 @@ -7432,6 +9463,8 @@ The tracking issue for this feature is: [#94047] label: "iterator_try_reduce", description: r##"# `iterator_try_reduce` + + The tracking issue for this feature is: [#87053] [#87053]: https://github.com/rust-lang/rust/issues/87053 @@ -7446,6 +9479,8 @@ The tracking issue for this feature is: [#87053] label: "junction_point", description: r##"# `junction_point` + + The tracking issue for this feature is: [#121709] [#121709]: https://github.com/rust-lang/rust/issues/121709 @@ -7460,9 +9495,11 @@ The tracking issue for this feature is: [#121709] label: "lahfsahf_target_feature", description: r##"# `lahfsahf_target_feature` -The tracking issue for this feature is: [#44839] +lahfsahf target feature on x86. -[#44839]: https://github.com/rust-lang/rust/issues/44839 +The tracking issue for this feature is: [#150251] + +[#150251]: https://github.com/rust-lang/rust/issues/150251 ------------------------ "##, @@ -7520,14 +9557,15 @@ allocation. A freestanding program that uses the `Box` sugar for dynamic allocations via `malloc` and `free`: ```rust,ignore (libc-is-finicky) -#![feature(lang_items, start, core_intrinsics, rustc_private, panic_unwind, rustc_attrs)] +#![feature(lang_items, core_intrinsics, rustc_private, panic_unwind, rustc_attrs)] #![allow(internal_features)] #![no_std] +#![no_main] extern crate libc; extern crate unwind; -use core::ffi::c_void; +use core::ffi::{c_int, c_void}; use core::intrinsics; use core::panic::PanicInfo; use core::ptr::NonNull; @@ -7565,8 +9603,8 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { p } -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: c_int, _argv: *const *const u8) -> c_int { let _x = Box::new(1); 0 @@ -7596,6 +9634,8 @@ An up-to-date list of all language items can be found [here] in the compiler cod label: "large_assignments", description: r##"# `large_assignments` +Allows setting the threshold for the `large_assignments` lint. + The tracking issue for this feature is: [#83518] [#83518]: https://github.com/rust-lang/rust/issues/83518 @@ -7610,6 +9650,8 @@ The tracking issue for this feature is: [#83518] label: "layout_for_ptr", description: r##"# `layout_for_ptr` + + The tracking issue for this feature is: [#69835] [#69835]: https://github.com/rust-lang/rust/issues/69835 @@ -7624,23 +9666,11 @@ The tracking issue for this feature is: [#69835] label: "lazy_cell_into_inner", description: r##"# `lazy_cell_into_inner` -The tracking issue for this feature is: [#125623] - -[#125623]: https://github.com/rust-lang/rust/issues/125623 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "lazy_get", - description: r##"# `lazy_get` -The tracking issue for this feature is: [#129333] +The tracking issue for this feature is: [#125623] -[#129333]: https://github.com/rust-lang/rust/issues/129333 +[#125623]: https://github.com/rust-lang/rust/issues/125623 ------------------------ "##, @@ -7652,6 +9682,8 @@ The tracking issue for this feature is: [#129333] label: "lazy_type_alias", description: r##"# `lazy_type_alias` +Allow to have type alias types for inter-crate use. + The tracking issue for this feature is: [#112792] [#112792]: https://github.com/rust-lang/rust/issues/112792 @@ -7666,21 +9698,9 @@ The tracking issue for this feature is: [#112792] label: "legacy_receiver_trait", description: r##"# `legacy_receiver_trait` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "let_chains", - description: r##"# `let_chains` - -The tracking issue for this feature is: [#53667] -[#53667]: https://github.com/rust-lang/rust/issues/53667 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -7692,6 +9712,8 @@ The tracking issue for this feature is: [#53667] label: "liballoc_internals", description: r##"# `liballoc_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -7713,10 +9735,14 @@ This feature is internal to the Rust compiler and is not intended for general us deny_since: None, }, Lint { - label: "lifetime_capture_rules_2024", - description: r##"# `lifetime_capture_rules_2024` + label: "likely_unlikely", + description: r##"# `likely_unlikely` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + + +The tracking issue for this feature is: [#151619] + +[#151619]: https://github.com/rust-lang/rust/issues/151619 ------------------------ "##, @@ -7768,6 +9794,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "link_llvm_intrinsics", description: r##"# `link_llvm_intrinsics` +Allows using `#[link_name="llvm.*"]`. + The tracking issue for this feature is: [#29602] [#29602]: https://github.com/rust-lang/rust/issues/29602 @@ -7782,6 +9810,8 @@ The tracking issue for this feature is: [#29602] label: "linkage", description: r##"# `linkage` +Allows using the `#[linkage = ".."]` attribute. + The tracking issue for this feature is: [#29603] [#29603]: https://github.com/rust-lang/rust/issues/29603 @@ -7796,6 +9826,8 @@ The tracking issue for this feature is: [#29603] label: "linked_list_cursors", description: r##"# `linked_list_cursors` + + The tracking issue for this feature is: [#58533] [#58533]: https://github.com/rust-lang/rust/issues/58533 @@ -7810,6 +9842,8 @@ The tracking issue for this feature is: [#58533] label: "linked_list_remove", description: r##"# `linked_list_remove` + + The tracking issue for this feature is: [#69210] [#69210]: https://github.com/rust-lang/rust/issues/69210 @@ -7824,6 +9858,8 @@ The tracking issue for this feature is: [#69210] label: "linked_list_retain", description: r##"# `linked_list_retain` + + The tracking issue for this feature is: [#114135] [#114135]: https://github.com/rust-lang/rust/issues/114135 @@ -7838,6 +9874,8 @@ The tracking issue for this feature is: [#114135] label: "linux_pidfd", description: r##"# `linux_pidfd` + + The tracking issue for this feature is: [#82971] [#82971]: https://github.com/rust-lang/rust/issues/82971 @@ -7849,9 +9887,27 @@ The tracking issue for this feature is: [#82971] deny_since: None, }, Lint { + label: "local_key_cell_update", + description: r##"# `local_key_cell_update` + + + +The tracking issue for this feature is: [#143989] + +[#143989]: https://github.com/rust-lang/rust/issues/143989 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "local_waker", description: r##"# `local_waker` + + The tracking issue for this feature is: [#118959] [#118959]: https://github.com/rust-lang/rust/issues/118959 @@ -7863,9 +9919,27 @@ The tracking issue for this feature is: [#118959] deny_since: None, }, Lint { + label: "lock_value_accessors", + description: r##"# `lock_value_accessors` + + + +The tracking issue for this feature is: [#133407] + +[#133407]: https://github.com/rust-lang/rust/issues/133407 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "log_syntax", description: r##"# `log_syntax` + + The tracking issue for this feature is: [#29598] [#29598]: https://github.com/rust-lang/rust/issues/29598 @@ -7880,9 +9954,11 @@ The tracking issue for this feature is: [#29598] label: "loongarch_target_feature", description: r##"# `loongarch_target_feature` -The tracking issue for this feature is: [#44839] +Target features on loongarch. + +The tracking issue for this feature is: [#150252] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +[#150252]: https://github.com/rust-lang/rust/issues/150252 ------------------------ "##, @@ -7891,12 +9967,73 @@ The tracking issue for this feature is: [#44839] deny_since: None, }, Lint { - label: "macro_metavar_expr", - description: r##"# `macro_metavar_expr` + label: "loop_match", + description: r##"# `loop_match` -The tracking issue for this feature is: [#83527] +The tracking issue for this feature is: [#132306] -[#83527]: https://github.com/rust-lang/rust/issues/83527 +[#132306]: https://github.com/rust-lang/rust/issues/132306 + +------ + +The `#[loop_match]` and `#[const_continue]` attributes can be used to improve the code +generation of logic that fits this shape: + +```ignore (pseudo-rust) +loop { + state = 'blk: { + match state { + State::A => { + break 'blk State::B + } + State::B => { /* ... */ } + /* ... */ + } + } +} +``` + +Here the loop itself can be annotated with `#[loop_match]`, and any `break 'blk` with +`#[const_continue]` if the value is know at compile time: + +```ignore (pseudo-rust) +#[loop_match] +loop { + state = 'blk: { + match state { + State::A => { + #[const_continue] + break 'blk State::B + } + State::B => { /* ... */ } + /* ... */ + } + } +} +``` + +The observable behavior of this loop is exactly the same as without the extra attributes. +The difference is in the generated output: normally, when the state is `A`, control flow +moves from the `A` branch, back to the top of the loop, then to the `B` branch. With the +attributes, The `A` branch will immediately jump to the `B` branch. + +Removing the indirection can be beneficial for stack usage and branch prediction, and +enables other optimizations by clearly splitting out the control flow paths that your +program will actually use. +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "m68k_target_feature", + description: r##"# `m68k_target_feature` + +Target features on m68k. + +The tracking issue for this feature is: [#134328] + +[#134328]: https://github.com/rust-lang/rust/issues/134328 ------------------------ "##, @@ -7905,12 +10042,30 @@ The tracking issue for this feature is: [#83527] deny_since: None, }, Lint { - label: "macro_metavar_expr_concat", - description: r##"# `macro_metavar_expr_concat` + label: "macro_attr", + description: r##"# `macro_attr` -The tracking issue for this feature is: [#124225] +Allow `macro_rules!` attribute rules -[#124225]: https://github.com/rust-lang/rust/issues/124225 +The tracking issue for this feature is: [#143547] + +[#143547]: https://github.com/rust-lang/rust/issues/143547 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "macro_derive", + description: r##"# `macro_derive` + +Allow `macro_rules!` derive rules + +The tracking issue for this feature is: [#143549] + +[#143549]: https://github.com/rust-lang/rust/issues/143549 ------------------------ "##, @@ -7919,14 +10074,173 @@ The tracking issue for this feature is: [#124225] deny_since: None, }, Lint { - label: "map_many_mut", - description: r##"# `map_many_mut` + label: "macro_guard_matcher", + description: r##"# `macro_guard_matcher` -The tracking issue for this feature is: [#97601] +Allow `$x:guard` matcher in macros -[#97601]: https://github.com/rust-lang/rust/issues/97601 +The tracking issue for this feature is: [#153104] + +[#153104]: https://github.com/rust-lang/rust/issues/153104 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "macro_metavar_expr", + description: r##"# `macro_metavar_expr` + +The tracking issue for this feature is: [#83527] ------------------------ + +> This feature is not to be confused with [`macro_metavar_expr_concat`]. + +[`macro_metavar_expr_concat`]: ./macro-metavar-expr-concat.md +[#83527]: https://github.com/rust-lang/rust/issues/83527 +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "macro_metavar_expr_concat", + description: r##"# `macro_metavar_expr_concat` + +The tracking issue for this feature is: [#124225] + +------------------------ + +In stable Rust, there is no way to create new identifiers by joining identifiers to literals or other identifiers without using procedural macros such as [`paste`]. + `#![feature(macro_metavar_expr_concat)]` introduces a way to do this, using the concat metavariable expression. + +> This feature uses the syntax from [`macro_metavar_expr`] but is otherwise +> independent. It replaces the since-removed unstable feature +> [`concat_idents`]. + +> This is an experimental feature; it and its syntax will require a RFC before stabilization. + + +### Overview + +`#![feature(macro_metavar_expr_concat)]` provides the `concat` metavariable expression for creating new identifiers: + +```rust +#![feature(macro_metavar_expr_concat)] + +macro_rules! create_some_structs { + ($name:ident) => { + pub struct ${ concat(First, $name) }; + pub struct ${ concat(Second, $name) }; + pub struct ${ concat(Third, $name) }; + } +} + +create_some_structs!(Thing); +``` + +This macro invocation expands to: + +```rust +pub struct FirstThing; +pub struct SecondThing; +pub struct ThirdThing; +``` + +### Syntax + +This feature builds upon the metavariable expression syntax `${ .. }` as specified in [RFC 3086] ([`macro_metavar_expr`]). + `concat` is available like `${ concat(items) }`, where `items` is a comma separated sequence of idents and/or literals. + +### Examples + +#### Create a function or method with a concatenated name + +```rust +#![feature(macro_metavar_expr_concat)] + +macro_rules! make_getter { + ($name:ident, $field: ident, $ret:ty) => { + impl $name { + pub fn ${ concat(get_, $field) }(&self) -> &$ret { + &self.$field + } + } + } +} + +pub struct Thing { + description: String, +} + +make_getter!(Thing, description, String); +``` + +This expands to: + +```rust +pub struct Thing { + description: String, +} + +impl Thing { + pub fn get_description(&self) -> &String { + &self.description + } +} +``` + +#### Create names for macro generated tests + +```rust +#![feature(macro_metavar_expr_concat)] + +macro_rules! test_math { + ($integer:ident) => { + #[test] + fn ${ concat(test_, $integer, _, addition) } () { + let a: $integer = 73; + let b: $integer = 42; + assert_eq!(a + b, 115) + } + + #[test] + fn ${ concat(test_, $integer, _, subtraction) } () { + let a: $integer = 73; + let b: $integer = 42; + assert_eq!(a - b, 31) + } + } +} + +test_math!(i32); +test_math!(u64); +test_math!(u128); +``` + +Running this returns the following output: + +```text +running 6 tests +test test_i32_subtraction ... ok +test test_i32_addition ... ok +test test_u128_addition ... ok +test test_u128_subtraction ... ok +test test_u64_addition ... ok +test test_u64_subtraction ... ok + +test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s +``` + +[`paste`]: https://crates.io/crates/paste +[RFC 3086]: https://rust-lang.github.io/rfcs/3086-macro-metavar-expr.html +[`macro_metavar_expr`]: ../language-features/macro-metavar-expr.md +[`concat_idents`]: https://github.com/rust-lang/rust/issues/29599 +[#124225]: https://github.com/rust-lang/rust/issues/124225 +[declarative macros]: https://doc.rust-lang.org/stable/reference/macros-by-example.html "##, default_severity: Severity::Allow, warn_since: None, @@ -7936,6 +10250,8 @@ The tracking issue for this feature is: [#97601] label: "map_try_insert", description: r##"# `map_try_insert` + + The tracking issue for this feature is: [#82766] [#82766]: https://github.com/rust-lang/rust/issues/82766 @@ -7950,6 +10266,8 @@ The tracking issue for this feature is: [#82766] label: "mapped_lock_guards", description: r##"# `mapped_lock_guards` + + The tracking issue for this feature is: [#117108] [#117108]: https://github.com/rust-lang/rust/issues/117108 @@ -8003,9 +10321,27 @@ feature, which applied to all empty traits (without needing an opt-in). deny_since: None, }, Lint { + label: "maybe_dangling", + description: r##"# `maybe_dangling` + + + +The tracking issue for this feature is: [#118166] + +[#118166]: https://github.com/rust-lang/rust/issues/118166 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "maybe_uninit_array_assume_init", description: r##"# `maybe_uninit_array_assume_init` + + The tracking issue for this feature is: [#96097] [#96097]: https://github.com/rust-lang/rust/issues/96097 @@ -8020,6 +10356,8 @@ The tracking issue for this feature is: [#96097] label: "maybe_uninit_as_bytes", description: r##"# `maybe_uninit_as_bytes` + + The tracking issue for this feature is: [#93092] [#93092]: https://github.com/rust-lang/rust/issues/93092 @@ -8034,6 +10372,8 @@ The tracking issue for this feature is: [#93092] label: "maybe_uninit_fill", description: r##"# `maybe_uninit_fill` + + The tracking issue for this feature is: [#117428] [#117428]: https://github.com/rust-lang/rust/issues/117428 @@ -8045,12 +10385,14 @@ The tracking issue for this feature is: [#117428] deny_since: None, }, Lint { - label: "maybe_uninit_slice", - description: r##"# `maybe_uninit_slice` + label: "maybe_uninit_uninit_array_transpose", + description: r##"# `maybe_uninit_uninit_array_transpose` -The tracking issue for this feature is: [#63569] -[#63569]: https://github.com/rust-lang/rust/issues/63569 + +The tracking issue for this feature is: [#96097] + +[#96097]: https://github.com/rust-lang/rust/issues/96097 ------------------------ "##, @@ -8059,12 +10401,14 @@ The tracking issue for this feature is: [#63569] deny_since: None, }, Lint { - label: "maybe_uninit_uninit_array", - description: r##"# `maybe_uninit_uninit_array` + label: "mem_conjure_zst", + description: r##"# `mem_conjure_zst` -The tracking issue for this feature is: [#96097] -[#96097]: https://github.com/rust-lang/rust/issues/96097 + +The tracking issue for this feature is: [#95383] + +[#95383]: https://github.com/rust-lang/rust/issues/95383 ------------------------ "##, @@ -8073,12 +10417,14 @@ The tracking issue for this feature is: [#96097] deny_since: None, }, Lint { - label: "maybe_uninit_uninit_array_transpose", - description: r##"# `maybe_uninit_uninit_array_transpose` + label: "mem_copy_fn", + description: r##"# `mem_copy_fn` -The tracking issue for this feature is: [#96097] -[#96097]: https://github.com/rust-lang/rust/issues/96097 + +The tracking issue for this feature is: [#98262] + +[#98262]: https://github.com/rust-lang/rust/issues/98262 ------------------------ "##, @@ -8087,12 +10433,14 @@ The tracking issue for this feature is: [#96097] deny_since: None, }, Lint { - label: "maybe_uninit_write_slice", - description: r##"# `maybe_uninit_write_slice` + label: "mgca_type_const_syntax", + description: r##"# `mgca_type_const_syntax` -The tracking issue for this feature is: [#79995] +Enable mgca `type const` syntax before expansion. + +The tracking issue for this feature is: [#132980] -[#79995]: https://github.com/rust-lang/rust/issues/79995 +[#132980]: https://github.com/rust-lang/rust/issues/132980 ------------------------ "##, @@ -8101,12 +10449,14 @@ The tracking issue for this feature is: [#79995] deny_since: None, }, Lint { - label: "mem_copy_fn", - description: r##"# `mem_copy_fn` + label: "min_adt_const_params", + description: r##"# `min_adt_const_params` -The tracking issue for this feature is: [#98262] +Allows additional const parameter types, such as [u8; 10] or user defined types. User defined types must not have fields more private than the type itself. -[#98262]: https://github.com/rust-lang/rust/issues/98262 +The tracking issue for this feature is: [#154042] + +[#154042]: https://github.com/rust-lang/rust/issues/154042 ------------------------ "##, @@ -8118,6 +10468,8 @@ The tracking issue for this feature is: [#98262] label: "min_generic_const_args", description: r##"# `min_generic_const_args` +Enables the generic const args MVP (only bare paths, not arbitrary computation). + The tracking issue for this feature is: [#132980] [#132980]: https://github.com/rust-lang/rust/issues/132980 @@ -8132,6 +10484,8 @@ The tracking issue for this feature is: [#132980] label: "min_specialization", description: r##"# `min_specialization` +A minimal, sound subset of specialization intended to be used by the standard library until the soundness issues with specialization are fixed. + The tracking issue for this feature is: [#31844] [#31844]: https://github.com/rust-lang/rust/issues/31844 @@ -8146,23 +10500,11 @@ The tracking issue for this feature is: [#31844] label: "mips_target_feature", description: r##"# `mips_target_feature` -The tracking issue for this feature is: [#44839] - -[#44839]: https://github.com/rust-lang/rust/issues/44839 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "mixed_integer_ops_unsigned_sub", - description: r##"# `mixed_integer_ops_unsigned_sub` +Target features on mips. -The tracking issue for this feature is: [#126043] +The tracking issue for this feature is: [#150253] -[#126043]: https://github.com/rust-lang/rust/issues/126043 +[#150253]: https://github.com/rust-lang/rust/issues/150253 ------------------------ "##, @@ -8174,9 +10516,11 @@ The tracking issue for this feature is: [#126043] label: "more_float_constants", description: r##"# `more_float_constants` -The tracking issue for this feature is: [#103883] -[#103883]: https://github.com/rust-lang/rust/issues/103883 + +The tracking issue for this feature is: [#146939] + +[#146939]: https://github.com/rust-lang/rust/issues/146939 ------------------------ "##, @@ -8188,6 +10532,8 @@ The tracking issue for this feature is: [#103883] label: "more_maybe_bounds", description: r##"# `more_maybe_bounds` +Allows using `?Trait` trait bounds in more contexts. + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -8237,9 +10583,43 @@ impl A for Foo { deny_since: None, }, Lint { + label: "motor_ext", + description: r##"# `motor_ext` + + + +The tracking issue for this feature is: [#147456] + +[#147456]: https://github.com/rust-lang/rust/issues/147456 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "movrs_target_feature", + description: r##"# `movrs_target_feature` + +The `movrs` target feature on x86. + +The tracking issue for this feature is: [#137976] + +[#137976]: https://github.com/rust-lang/rust/issues/137976 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "mpmc_channel", description: r##"# `mpmc_channel` + + The tracking issue for this feature is: [#126840] [#126840]: https://github.com/rust-lang/rust/issues/126840 @@ -8251,10 +10631,30 @@ The tracking issue for this feature is: [#126840] deny_since: None, }, Lint { + label: "mpsc_is_disconnected", + description: r##"# `mpsc_is_disconnected` + + + +The tracking issue for this feature is: [#153668] + +[#153668]: https://github.com/rust-lang/rust/issues/153668 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "multiple_supertrait_upcastable", description: r##"# `multiple_supertrait_upcastable` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +Allows the `multiple_supertrait_upcastable` lint. + +The tracking issue for this feature is: [#150833] + +[#150833]: https://github.com/rust-lang/rust/issues/150833 ------------------------ "##, @@ -8266,6 +10666,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "must_not_suspend", description: r##"# `must_not_suspend` +Allows the `#[must_not_suspend]` attribute. + The tracking issue for this feature is: [#83310] [#83310]: https://github.com/rust-lang/rust/issues/83310 @@ -8280,6 +10682,8 @@ The tracking issue for this feature is: [#83310] label: "mut_ref", description: r##"# `mut_ref` +Allows `mut ref` and `mut ref mut` identifier patterns. + The tracking issue for this feature is: [#123076] [#123076]: https://github.com/rust-lang/rust/issues/123076 @@ -8291,12 +10695,46 @@ The tracking issue for this feature is: [#123076] deny_since: None, }, Lint { - label: "naked_functions", - description: r##"# `naked_functions` + label: "mutex_data_ptr", + description: r##"# `mutex_data_ptr` + + + +The tracking issue for this feature is: [#140368] + +[#140368]: https://github.com/rust-lang/rust/issues/140368 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "naked_functions_rustic_abi", + description: r##"# `naked_functions_rustic_abi` + +Allows using `#[naked]` on `extern "Rust"` functions. -The tracking issue for this feature is: [#90957] +The tracking issue for this feature is: [#138997] -[#90957]: https://github.com/rust-lang/rust/issues/90957 +[#138997]: https://github.com/rust-lang/rust/issues/138997 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "naked_functions_target_feature", + description: r##"# `naked_functions_target_feature` + +Allows using `#[target_feature(enable = "...")]` on `#[naked]` on functions. + +The tracking issue for this feature is: [#138568] + +[#138568]: https://github.com/rust-lang/rust/issues/138568 ------------------------ "##, @@ -8333,6 +10771,8 @@ The default for this modifier is unclear, some targets currently specify it as ` label: "needs_panic_runtime", description: r##"# `needs_panic_runtime` +Allows declaring with `#![needs_panic_runtime]` that a panic runtime is needed. + The tracking issue for this feature is: [#32837] [#32837]: https://github.com/rust-lang/rust/issues/32837 @@ -8347,6 +10787,8 @@ The tracking issue for this feature is: [#32837] label: "negative_bounds", description: r##"# `negative_bounds` +Allow negative trait bounds. This is an internal-only feature for testing the trait solver! + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -8423,6 +10865,8 @@ This serves two purposes: label: "never_patterns", description: r##"# `never_patterns` +Allows the `!` pattern. + The tracking issue for this feature is: [#118155] [#118155]: https://github.com/rust-lang/rust/issues/118155 @@ -8437,6 +10881,8 @@ The tracking issue for this feature is: [#118155] label: "never_type", description: r##"# `never_type` +Allows the `!` type. Does not imply 'exhaustive_patterns' (below) any more. + The tracking issue for this feature is: [#35121] [#35121]: https://github.com/rust-lang/rust/issues/35121 @@ -8448,22 +10894,26 @@ The tracking issue for this feature is: [#35121] deny_since: None, }, Lint { - label: "never_type_fallback", - description: r##"# `never_type_fallback` + label: "new_range", + description: r##"# `new_range` -The tracking issue for this feature is: [#65992] +The tracking issue for this feature is: [#123741] -[#65992]: https://github.com/rust-lang/rust/issues/65992 +[#123741]: https://github.com/rust-lang/rust/issues/123741 ------------------------- +--- + +Switch the syntaxes `a..`, `a..b`, and `a..=b` to resolve the new range types. "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "new_range_api", - description: r##"# `new_range_api` + label: "new_range_api_legacy", + description: r##"# `new_range_api_legacy` + + The tracking issue for this feature is: [#125687] @@ -8476,12 +10926,14 @@ The tracking issue for this feature is: [#125687] deny_since: None, }, Lint { - label: "new_zeroed_alloc", - description: r##"# `new_zeroed_alloc` + label: "new_range_remainder", + description: r##"# `new_range_remainder` + + -The tracking issue for this feature is: [#129396] +The tracking issue for this feature is: [#154458] -[#129396]: https://github.com/rust-lang/rust/issues/129396 +[#154458]: https://github.com/rust-lang/rust/issues/154458 ------------------------ "##, @@ -8490,12 +10942,14 @@ The tracking issue for this feature is: [#129396] deny_since: None, }, Lint { - label: "no_core", - description: r##"# `no_core` + label: "next_index", + description: r##"# `next_index` -The tracking issue for this feature is: [#29639] -[#29639]: https://github.com/rust-lang/rust/issues/29639 + +The tracking issue for this feature is: [#130711] + +[#130711]: https://github.com/rust-lang/rust/issues/130711 ------------------------ "##, @@ -8504,36 +10958,16 @@ The tracking issue for this feature is: [#29639] deny_since: None, }, Lint { - label: "no_sanitize", - description: r##"# `no_sanitize` - -The tracking issue for this feature is: [#39699] - -[#39699]: https://github.com/rust-lang/rust/issues/39699 - ------------------------- - -The `no_sanitize` attribute can be used to selectively disable sanitizer -instrumentation in an annotated function. This might be useful to: avoid -instrumentation overhead in a performance critical function, or avoid -instrumenting code that contains constructs unsupported by given sanitizer. + label: "no_core", + description: r##"# `no_core` -The precise effect of this annotation depends on particular sanitizer in use. -For example, with `no_sanitize(thread)`, the thread sanitizer will no longer -instrument non-atomic store / load operations, but it will instrument atomic -operations to avoid reporting false positives and provide meaning full stack -traces. +Allows `#![no_core]`. -## Examples +The tracking issue for this feature is: [#29639] -``` rust -#![feature(no_sanitize)] +[#29639]: https://github.com/rust-lang/rust/issues/29639 -#[no_sanitize(address)] -fn foo() { - // ... -} -``` +------------------------ "##, default_severity: Severity::Allow, warn_since: None, @@ -8543,6 +10977,8 @@ fn foo() { label: "non_exhaustive_omitted_patterns_lint", description: r##"# `non_exhaustive_omitted_patterns_lint` +Allows using the `non_exhaustive_omitted_patterns` lint. + The tracking issue for this feature is: [#89554] [#89554]: https://github.com/rust-lang/rust/issues/89554 @@ -8557,6 +10993,8 @@ The tracking issue for this feature is: [#89554] label: "non_lifetime_binders", description: r##"# `non_lifetime_binders` +Allows `for<T>` binders in where-clauses + The tracking issue for this feature is: [#108185] [#108185]: https://github.com/rust-lang/rust/issues/108185 @@ -8568,12 +11006,30 @@ The tracking issue for this feature is: [#108185] deny_since: None, }, Lint { - label: "non_null_from_ref", - description: r##"# `non_null_from_ref` + label: "nonpoison_condvar", + description: r##"# `nonpoison_condvar` + + + +The tracking issue for this feature is: [#134645] + +[#134645]: https://github.com/rust-lang/rust/issues/134645 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "nonpoison_mutex", + description: r##"# `nonpoison_mutex` + + -The tracking issue for this feature is: [#130823] +The tracking issue for this feature is: [#134645] -[#130823]: https://github.com/rust-lang/rust/issues/130823 +[#134645]: https://github.com/rust-lang/rust/issues/134645 ------------------------ "##, @@ -8582,12 +11038,14 @@ The tracking issue for this feature is: [#130823] deny_since: None, }, Lint { - label: "non_zero_count_ones", - description: r##"# `non_zero_count_ones` + label: "nonpoison_rwlock", + description: r##"# `nonpoison_rwlock` -The tracking issue for this feature is: [#120287] -[#120287]: https://github.com/rust-lang/rust/issues/120287 + +The tracking issue for this feature is: [#134645] + +[#134645]: https://github.com/rust-lang/rust/issues/134645 ------------------------ "##, @@ -8599,6 +11057,8 @@ The tracking issue for this feature is: [#120287] label: "nonzero_bitwise", description: r##"# `nonzero_bitwise` + + The tracking issue for this feature is: [#128281] [#128281]: https://github.com/rust-lang/rust/issues/128281 @@ -8613,6 +11073,8 @@ The tracking issue for this feature is: [#128281] label: "nonzero_from_mut", description: r##"# `nonzero_from_mut` + + The tracking issue for this feature is: [#106290] [#106290]: https://github.com/rust-lang/rust/issues/106290 @@ -8624,9 +11086,27 @@ The tracking issue for this feature is: [#106290] deny_since: None, }, Lint { + label: "nonzero_from_str_radix", + description: r##"# `nonzero_from_str_radix` + + + +The tracking issue for this feature is: [#152193] + +[#152193]: https://github.com/rust-lang/rust/issues/152193 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "nonzero_internals", description: r##"# `nonzero_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -8639,6 +11119,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "nonzero_ops", description: r##"# `nonzero_ops` + + The tracking issue for this feature is: [#84186] [#84186]: https://github.com/rust-lang/rust/issues/84186 @@ -8650,12 +11132,28 @@ The tracking issue for this feature is: [#84186] deny_since: None, }, Lint { - label: "num_midpoint_signed", - description: r##"# `num_midpoint_signed` + label: "normalize_lexically", + description: r##"# `normalize_lexically` + + + +The tracking issue for this feature is: [#134694] + +[#134694]: https://github.com/rust-lang/rust/issues/134694 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "num_internals", + description: r##"# `num_internals` -The tracking issue for this feature is: [#110840] -[#110840]: https://github.com/rust-lang/rust/issues/110840 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -8667,6 +11165,8 @@ The tracking issue for this feature is: [#110840] label: "numfmt", description: r##"# `numfmt` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -8676,12 +11176,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "offset_of_enum", - description: r##"# `offset_of_enum` + label: "nvptx_target_feature", + description: r##"# `nvptx_target_feature` -The tracking issue for this feature is: [#120141] +Target feaures on nvptx. -[#120141]: https://github.com/rust-lang/rust/issues/120141 +The tracking issue for this feature is: [#150254] + +[#150254]: https://github.com/rust-lang/rust/issues/150254 ------------------------ "##, @@ -8690,12 +11192,12 @@ The tracking issue for this feature is: [#120141] deny_since: None, }, Lint { - label: "offset_of_slice", - description: r##"# `offset_of_slice` + label: "objc_class_variant", + description: r##"# `objc_class_variant` -The tracking issue for this feature is: [#126151] -[#126151]: https://github.com/rust-lang/rust/issues/126151 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -8704,8 +11206,10 @@ The tracking issue for this feature is: [#126151] deny_since: None, }, Lint { - label: "omit_gdb_pretty_printer_section", - description: r##"# `omit_gdb_pretty_printer_section` + label: "objc_selector_variant", + description: r##"# `objc_selector_variant` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. @@ -8716,9 +11220,84 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { + label: "offset_of_enum", + description: r##"# `offset_of_enum` + +The tracking issue for this feature is: [#120141] + +[#120141]: https://github.com/rust-lang/rust/issues/120141 + +------------------------ + +When the `offset_of_enum` feature is enabled, the [`offset_of!`] macro may be used to obtain the +offsets of fields of `enum`s; to express this, `enum` variants may be traversed as if they were +fields. Variants themselves do not have an offset, so they cannot appear as the last path component. + +```rust +#![feature(offset_of_enum)] +use std::mem; + +#[repr(u8)] +enum Enum { + A(u8, u16), + B { one: u8, two: u16 }, +} + +assert_eq!(mem::offset_of!(Enum, A.0), 1); +assert_eq!(mem::offset_of!(Enum, B.two), 2); + +assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0); +``` + +[`offset_of!`]: ../../std/mem/macro.offset_of.html +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "offset_of_slice", + description: r##"# `offset_of_slice` + +The tracking issue for this feature is: [#126151] + +[#126151]: https://github.com/rust-lang/rust/issues/126151 + +------------------------ + +When the `offset_of_slice` feature is enabled, the [`offset_of!`] macro may be used to determine +the offset of fields whose type is `[T]`, that is, a slice of dynamic size. + +In general, fields whose type is dynamically sized do not have statically known offsets because +they do not have statically known alignments. However, `[T]` has the same alignment as `T`, so +it specifically may be allowed. + +```rust +#![feature(offset_of_slice)] + +#[repr(C)] +pub struct Struct { + head: u32, + tail: [u8], +} + +fn main() { + assert_eq!(std::mem::offset_of!(Struct, tail), 4); +} +``` + +[`offset_of!`]: ../../std/mem/macro.offset_of.html +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "once_cell_get_mut", description: r##"# `once_cell_get_mut` + + The tracking issue for this feature is: [#121641] [#121641]: https://github.com/rust-lang/rust/issues/121641 @@ -8733,6 +11312,8 @@ The tracking issue for this feature is: [#121641] label: "once_cell_try", description: r##"# `once_cell_try` + + The tracking issue for this feature is: [#109737] [#109737]: https://github.com/rust-lang/rust/issues/109737 @@ -8747,6 +11328,8 @@ The tracking issue for this feature is: [#109737] label: "once_cell_try_insert", description: r##"# `once_cell_try_insert` + + The tracking issue for this feature is: [#116693] [#116693]: https://github.com/rust-lang/rust/issues/116693 @@ -8758,12 +11341,14 @@ The tracking issue for this feature is: [#116693] deny_since: None, }, Lint { - label: "once_wait", - description: r##"# `once_wait` + label: "one_sided_range", + description: r##"# `one_sided_range` + -The tracking issue for this feature is: [#127527] -[#127527]: https://github.com/rust-lang/rust/issues/127527 +The tracking issue for this feature is: [#69780] + +[#69780]: https://github.com/rust-lang/rust/issues/69780 ------------------------ "##, @@ -8772,12 +11357,14 @@ The tracking issue for this feature is: [#127527] deny_since: None, }, Lint { - label: "one_sided_range", - description: r##"# `one_sided_range` + label: "oneshot_channel", + description: r##"# `oneshot_channel` -The tracking issue for this feature is: [#69780] -[#69780]: https://github.com/rust-lang/rust/issues/69780 + +The tracking issue for this feature is: [#143674] + +[#143674]: https://github.com/rust-lang/rust/issues/143674 ------------------------ "##, @@ -8789,6 +11376,8 @@ The tracking issue for this feature is: [#69780] label: "optimize_attribute", description: r##"# `optimize_attribute` +Allows using `#[optimize(X)]`. + The tracking issue for this feature is: [#54882] [#54882]: https://github.com/rust-lang/rust/issues/54882 @@ -8803,6 +11392,8 @@ The tracking issue for this feature is: [#54882] label: "option_array_transpose", description: r##"# `option_array_transpose` + + The tracking issue for this feature is: [#130828] [#130828]: https://github.com/rust-lang/rust/issues/130828 @@ -8814,12 +11405,14 @@ The tracking issue for this feature is: [#130828] deny_since: None, }, Lint { - label: "option_zip", - description: r##"# `option_zip` + label: "option_get_or_try_insert_with", + description: r##"# `option_get_or_try_insert_with` -The tracking issue for this feature is: [#70086] -[#70086]: https://github.com/rust-lang/rust/issues/70086 + +The tracking issue for this feature is: [#143648] + +[#143648]: https://github.com/rust-lang/rust/issues/143648 ------------------------ "##, @@ -8828,12 +11421,14 @@ The tracking issue for this feature is: [#70086] deny_since: None, }, Lint { - label: "os_str_display", - description: r##"# `os_str_display` + label: "option_into_flat_iter", + description: r##"# `option_into_flat_iter` + + -The tracking issue for this feature is: [#120048] +The tracking issue for this feature is: [#148441] -[#120048]: https://github.com/rust-lang/rust/issues/120048 +[#148441]: https://github.com/rust-lang/rust/issues/148441 ------------------------ "##, @@ -8842,12 +11437,14 @@ The tracking issue for this feature is: [#120048] deny_since: None, }, Lint { - label: "os_str_slice", - description: r##"# `os_str_slice` + label: "option_reduce", + description: r##"# `option_reduce` -The tracking issue for this feature is: [#118485] -[#118485]: https://github.com/rust-lang/rust/issues/118485 + +The tracking issue for this feature is: [#144273] + +[#144273]: https://github.com/rust-lang/rust/issues/144273 ------------------------ "##, @@ -8856,12 +11453,46 @@ The tracking issue for this feature is: [#118485] deny_since: None, }, Lint { - label: "os_string_pathbuf_leak", - description: r##"# `os_string_pathbuf_leak` + label: "option_reference_flattening", + description: r##"# `option_reference_flattening` + -The tracking issue for this feature is: [#125965] -[#125965]: https://github.com/rust-lang/rust/issues/125965 +The tracking issue for this feature is: [#149221] + +[#149221]: https://github.com/rust-lang/rust/issues/149221 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "option_zip", + description: r##"# `option_zip` + + + +The tracking issue for this feature is: [#70086] + +[#70086]: https://github.com/rust-lang/rust/issues/70086 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "os_str_slice", + description: r##"# `os_str_slice` + + + +The tracking issue for this feature is: [#118485] + +[#118485]: https://github.com/rust-lang/rust/issues/118485 ------------------------ "##, @@ -8873,6 +11504,8 @@ The tracking issue for this feature is: [#125965] label: "os_string_truncate", description: r##"# `os_string_truncate` + + The tracking issue for this feature is: [#133262] [#133262]: https://github.com/rust-lang/rust/issues/133262 @@ -8887,6 +11520,8 @@ The tracking issue for this feature is: [#133262] label: "panic_abort", description: r##"# `panic_abort` + + The tracking issue for this feature is: [#32837] [#32837]: https://github.com/rust-lang/rust/issues/32837 @@ -8901,6 +11536,8 @@ The tracking issue for this feature is: [#32837] label: "panic_always_abort", description: r##"# `panic_always_abort` + + The tracking issue for this feature is: [#84438] [#84438]: https://github.com/rust-lang/rust/issues/84438 @@ -8915,6 +11552,8 @@ The tracking issue for this feature is: [#84438] label: "panic_backtrace_config", description: r##"# `panic_backtrace_config` + + The tracking issue for this feature is: [#93346] [#93346]: https://github.com/rust-lang/rust/issues/93346 @@ -8929,6 +11568,8 @@ The tracking issue for this feature is: [#93346] label: "panic_can_unwind", description: r##"# `panic_can_unwind` + + The tracking issue for this feature is: [#92988] [#92988]: https://github.com/rust-lang/rust/issues/92988 @@ -8943,21 +11584,9 @@ The tracking issue for this feature is: [#92988] label: "panic_internals", description: r##"# `panic_internals` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "panic_payload_as_str", - description: r##"# `panic_payload_as_str` -The tracking issue for this feature is: [#125175] -[#125175]: https://github.com/rust-lang/rust/issues/125175 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -8969,6 +11598,8 @@ The tracking issue for this feature is: [#125175] label: "panic_runtime", description: r##"# `panic_runtime` +Allows using the `#![panic_runtime]` attribute. + The tracking issue for this feature is: [#32837] [#32837]: https://github.com/rust-lang/rust/issues/32837 @@ -8983,6 +11614,8 @@ The tracking issue for this feature is: [#32837] label: "panic_unwind", description: r##"# `panic_unwind` + + The tracking issue for this feature is: [#32837] [#32837]: https://github.com/rust-lang/rust/issues/32837 @@ -8997,6 +11630,8 @@ The tracking issue for this feature is: [#32837] label: "panic_update_hook", description: r##"# `panic_update_hook` + + The tracking issue for this feature is: [#92649] [#92649]: https://github.com/rust-lang/rust/issues/92649 @@ -9008,9 +11643,25 @@ The tracking issue for this feature is: [#92649] deny_since: None, }, Lint { + label: "partial_ord_chaining_methods", + description: r##"# `partial_ord_chaining_methods` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "patchable_function_entry", description: r##"# `patchable_function_entry` +Allows specifying nop padding on functions for dynamic patching. + The tracking issue for this feature is: [#123115] [#123115]: https://github.com/rust-lang/rust/issues/123115 @@ -9022,12 +11673,30 @@ The tracking issue for this feature is: [#123115] deny_since: None, }, Lint { - label: "path_add_extension", - description: r##"# `path_add_extension` + label: "path_absolute_method", + description: r##"# `path_absolute_method` + + + +The tracking issue for this feature is: [#153328] + +[#153328]: https://github.com/rust-lang/rust/issues/153328 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "path_is_empty", + description: r##"# `path_is_empty` + -The tracking issue for this feature is: [#127292] -[#127292]: https://github.com/rust-lang/rust/issues/127292 +The tracking issue for this feature is: [#148494] + +[#148494]: https://github.com/rust-lang/rust/issues/148494 ------------------------ "##, @@ -9036,12 +11705,14 @@ The tracking issue for this feature is: [#127292] deny_since: None, }, Lint { - label: "path_file_prefix", - description: r##"# `path_file_prefix` + label: "path_trailing_sep", + description: r##"# `path_trailing_sep` + -The tracking issue for this feature is: [#86319] -[#86319]: https://github.com/rust-lang/rust/issues/86319 +The tracking issue for this feature is: [#142503] + +[#142503]: https://github.com/rust-lang/rust/issues/142503 ------------------------ "##, @@ -9053,6 +11724,8 @@ The tracking issue for this feature is: [#86319] label: "pattern", description: r##"# `pattern` + + The tracking issue for this feature is: [#27721] [#27721]: https://github.com/rust-lang/rust/issues/27721 @@ -9067,6 +11740,8 @@ The tracking issue for this feature is: [#27721] label: "pattern_complexity_limit", description: r##"# `pattern_complexity_limit` +Set the maximum pattern complexity allowed (not limited by default). + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -9079,6 +11754,24 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "pattern_type_macro", description: r##"# `pattern_type_macro` + + +The tracking issue for this feature is: [#123646] + +[#123646]: https://github.com/rust-lang/rust/issues/123646 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "pattern_type_range_trait", + description: r##"# `pattern_type_range_trait` + + + The tracking issue for this feature is: [#123646] [#123646]: https://github.com/rust-lang/rust/issues/123646 @@ -9093,6 +11786,8 @@ The tracking issue for this feature is: [#123646] label: "pattern_types", description: r##"# `pattern_types` +Allows using pattern types. + The tracking issue for this feature is: [#123646] [#123646]: https://github.com/rust-lang/rust/issues/123646 @@ -9107,6 +11802,8 @@ The tracking issue for this feature is: [#123646] label: "peer_credentials_unix_socket", description: r##"# `peer_credentials_unix_socket` + + The tracking issue for this feature is: [#42839] [#42839]: https://github.com/rust-lang/rust/issues/42839 @@ -9118,12 +11815,44 @@ The tracking issue for this feature is: [#42839] deny_since: None, }, Lint { + label: "phantom_variance_markers", + description: r##"# `phantom_variance_markers` + + + +The tracking issue for this feature is: [#135806] + +[#135806]: https://github.com/rust-lang/rust/issues/135806 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "pin_coerce_unsized_trait", description: r##"# `pin_coerce_unsized_trait` -The tracking issue for this feature is: [#123430] -[#123430]: https://github.com/rust-lang/rust/issues/123430 + +The tracking issue for this feature is: [#150112] + +[#150112]: https://github.com/rust-lang/rust/issues/150112 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "pin_derefmut_internals", + description: r##"# `pin_derefmut_internals` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -9135,6 +11864,8 @@ The tracking issue for this feature is: [#123430] label: "pin_ergonomics", description: r##"# `pin_ergonomics` +Experimental features that make `Pin` more ergonomic. + The tracking issue for this feature is: [#130494] [#130494]: https://github.com/rust-lang/rust/issues/130494 @@ -9149,6 +11880,8 @@ The tracking issue for this feature is: [#130494] label: "pointer_is_aligned_to", description: r##"# `pointer_is_aligned_to` + + The tracking issue for this feature is: [#96284] [#96284]: https://github.com/rust-lang/rust/issues/96284 @@ -9160,10 +11893,14 @@ The tracking issue for this feature is: [#96284] deny_since: None, }, Lint { - label: "pointer_like_trait", - description: r##"# `pointer_like_trait` + label: "pointer_try_cast_aligned", + description: r##"# `pointer_try_cast_aligned` + -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +The tracking issue for this feature is: [#141221] + +[#141221]: https://github.com/rust-lang/rust/issues/141221 ------------------------ "##, @@ -9175,6 +11912,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "portable_simd", description: r##"# `portable_simd` + + The tracking issue for this feature is: [#86656] [#86656]: https://github.com/rust-lang/rust/issues/86656 @@ -9222,9 +11961,11 @@ get_foo().match { label: "powerpc_target_feature", description: r##"# `powerpc_target_feature` -The tracking issue for this feature is: [#44839] +Target features on powerpc. -[#44839]: https://github.com/rust-lang/rust/issues/44839 +The tracking issue for this feature is: [#150255] + +[#150255]: https://github.com/rust-lang/rust/issues/150255 ------------------------ "##, @@ -9233,12 +11974,12 @@ The tracking issue for this feature is: [#44839] deny_since: None, }, Lint { - label: "precise_capturing_in_traits", - description: r##"# `precise_capturing_in_traits` + label: "prelude_future", + description: r##"# `prelude_future` + -The tracking issue for this feature is: [#130044] -[#130044]: https://github.com/rust-lang/rust/issues/130044 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -9247,12 +11988,12 @@ The tracking issue for this feature is: [#130044] deny_since: None, }, Lint { - label: "prelude_2024", - description: r##"# `prelude_2024` + label: "prelude_import", + description: r##"# `prelude_import` -The tracking issue for this feature is: [#121042] +Allows using `#[prelude_import]` on glob `use` items. -[#121042]: https://github.com/rust-lang/rust/issues/121042 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -9261,8 +12002,10 @@ The tracking issue for this feature is: [#121042] deny_since: None, }, Lint { - label: "prelude_import", - description: r##"# `prelude_import` + label: "prelude_next", + description: r##"# `prelude_next` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. @@ -9276,9 +12019,11 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "prfchw_target_feature", description: r##"# `prfchw_target_feature` -The tracking issue for this feature is: [#44839] +The prfchw target feature on x86. -[#44839]: https://github.com/rust-lang/rust/issues/44839 +The tracking issue for this feature is: [#150256] + +[#150256]: https://github.com/rust-lang/rust/issues/150256 ------------------------ "##, @@ -9302,6 +12047,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "proc_macro_def_site", description: r##"# `proc_macro_def_site` + + The tracking issue for this feature is: [#54724] [#54724]: https://github.com/rust-lang/rust/issues/54724 @@ -9316,6 +12063,8 @@ The tracking issue for this feature is: [#54724] label: "proc_macro_diagnostic", description: r##"# `proc_macro_diagnostic` + + The tracking issue for this feature is: [#54140] [#54140]: https://github.com/rust-lang/rust/issues/54140 @@ -9330,6 +12079,8 @@ The tracking issue for this feature is: [#54140] label: "proc_macro_expand", description: r##"# `proc_macro_expand` + + The tracking issue for this feature is: [#90765] [#90765]: https://github.com/rust-lang/rust/issues/90765 @@ -9344,6 +12095,8 @@ The tracking issue for this feature is: [#90765] label: "proc_macro_hygiene", description: r##"# `proc_macro_hygiene` +Allows macro attributes on expressions, statements and non-inline modules. + The tracking issue for this feature is: [#54727] [#54727]: https://github.com/rust-lang/rust/issues/54727 @@ -9358,6 +12111,8 @@ The tracking issue for this feature is: [#54727] label: "proc_macro_internals", description: r##"# `proc_macro_internals` + + The tracking issue for this feature is: [#27812] [#27812]: https://github.com/rust-lang/rust/issues/27812 @@ -9372,6 +12127,8 @@ The tracking issue for this feature is: [#27812] label: "proc_macro_quote", description: r##"# `proc_macro_quote` + + The tracking issue for this feature is: [#54722] [#54722]: https://github.com/rust-lang/rust/issues/54722 @@ -9386,6 +12143,8 @@ The tracking issue for this feature is: [#54722] label: "proc_macro_span", description: r##"# `proc_macro_span` + + The tracking issue for this feature is: [#54725] [#54725]: https://github.com/rust-lang/rust/issues/54725 @@ -9400,6 +12159,8 @@ The tracking issue for this feature is: [#54725] label: "proc_macro_totokens", description: r##"# `proc_macro_totokens` + + The tracking issue for this feature is: [#130977] [#130977]: https://github.com/rust-lang/rust/issues/130977 @@ -9414,6 +12175,8 @@ The tracking issue for this feature is: [#130977] label: "proc_macro_tracked_env", description: r##"# `proc_macro_tracked_env` + + The tracking issue for this feature is: [#99515] [#99515]: https://github.com/rust-lang/rust/issues/99515 @@ -9425,9 +12188,59 @@ The tracking issue for this feature is: [#99515] deny_since: None, }, Lint { + label: "proc_macro_tracked_path", + description: r##"# `proc_macro_tracked_path` + + + +The tracking issue for this feature is: [#99515] + +[#99515]: https://github.com/rust-lang/rust/issues/99515 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "proc_macro_value", + description: r##"# `proc_macro_value` + + + +The tracking issue for this feature is: [#136652] + +[#136652]: https://github.com/rust-lang/rust/issues/136652 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "process_chroot", + description: r##"# `process_chroot` + + + +The tracking issue for this feature is: [#141298] + +[#141298]: https://github.com/rust-lang/rust/issues/141298 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "process_exitcode_internals", description: r##"# `process_exitcode_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -9440,6 +12253,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "process_internals", description: r##"# `process_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -9449,6 +12264,22 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { + label: "process_setsid", + description: r##"# `process_setsid` + + + +The tracking issue for this feature is: [#105376] + +[#105376]: https://github.com/rust-lang/rust/issues/105376 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "profiler_runtime", description: r##"# `profiler_runtime` @@ -9473,12 +12304,14 @@ This feature is internal to the Rust compiler and is not intended for general us deny_since: None, }, Lint { - label: "ptr_alignment_type", - description: r##"# `ptr_alignment_type` + label: "profiling_marker_api", + description: r##"# `profiling_marker_api` -The tracking issue for this feature is: [#102070] -[#102070]: https://github.com/rust-lang/rust/issues/102070 + +The tracking issue for this feature is: [#148197] + +[#148197]: https://github.com/rust-lang/rust/issues/148197 ------------------------ "##, @@ -9487,12 +12320,14 @@ The tracking issue for this feature is: [#102070] deny_since: None, }, Lint { - label: "ptr_as_ref_unchecked", - description: r##"# `ptr_as_ref_unchecked` + label: "ptr_alignment_type", + description: r##"# `ptr_alignment_type` + + -The tracking issue for this feature is: [#122034] +The tracking issue for this feature is: [#102070] -[#122034]: https://github.com/rust-lang/rust/issues/122034 +[#102070]: https://github.com/rust-lang/rust/issues/102070 ------------------------ "##, @@ -9504,6 +12339,8 @@ The tracking issue for this feature is: [#122034] label: "ptr_as_uninit", description: r##"# `ptr_as_uninit` + + The tracking issue for this feature is: [#75402] [#75402]: https://github.com/rust-lang/rust/issues/75402 @@ -9515,9 +12352,43 @@ The tracking issue for this feature is: [#75402] deny_since: None, }, Lint { + label: "ptr_cast_array", + description: r##"# `ptr_cast_array` + + + +The tracking issue for this feature is: [#144514] + +[#144514]: https://github.com/rust-lang/rust/issues/144514 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "ptr_cast_slice", + description: r##"# `ptr_cast_slice` + + + +The tracking issue for this feature is: [#149103] + +[#149103]: https://github.com/rust-lang/rust/issues/149103 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "ptr_internals", description: r##"# `ptr_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -9530,6 +12401,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "ptr_mask", description: r##"# `ptr_mask` + + The tracking issue for this feature is: [#98290] [#98290]: https://github.com/rust-lang/rust/issues/98290 @@ -9544,6 +12417,8 @@ The tracking issue for this feature is: [#98290] label: "ptr_metadata", description: r##"# `ptr_metadata` + + The tracking issue for this feature is: [#81513] [#81513]: https://github.com/rust-lang/rust/issues/81513 @@ -9555,12 +12430,12 @@ The tracking issue for this feature is: [#81513] deny_since: None, }, Lint { - label: "ptr_sub_ptr", - description: r##"# `ptr_sub_ptr` + label: "pub_crate_should_not_need_unstable_attr", + description: r##"# `pub_crate_should_not_need_unstable_attr` + -The tracking issue for this feature is: [#95892] -[#95892]: https://github.com/rust-lang/rust/issues/95892 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -9569,10 +12444,14 @@ The tracking issue for this feature is: [#95892] deny_since: None, }, Lint { - label: "pub_crate_should_not_need_unstable_attr", - description: r##"# `pub_crate_should_not_need_unstable_attr` + label: "random", + description: r##"# `random` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + + +The tracking issue for this feature is: [#130703] + +[#130703]: https://github.com/rust-lang/rust/issues/130703 ------------------------ "##, @@ -9581,12 +12460,46 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "random", - description: r##"# `random` + label: "range_bounds_is_empty", + description: r##"# `range_bounds_is_empty` -The tracking issue for this feature is: [#130703] -[#130703]: https://github.com/rust-lang/rust/issues/130703 + +The tracking issue for this feature is: [#137300] + +[#137300]: https://github.com/rust-lang/rust/issues/137300 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "range_into_bounds", + description: r##"# `range_into_bounds` + + + +The tracking issue for this feature is: [#136903] + +[#136903]: https://github.com/rust-lang/rust/issues/136903 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "raw_dylib_elf", + description: r##"# `raw_dylib_elf` + +Allows the use of raw-dylibs on ELF platforms + +The tracking issue for this feature is: [#135694] + +[#135694]: https://github.com/rust-lang/rust/issues/135694 ------------------------ "##, @@ -9598,6 +12511,8 @@ The tracking issue for this feature is: [#130703] label: "raw_os_error_ty", description: r##"# `raw_os_error_ty` + + The tracking issue for this feature is: [#107792] [#107792]: https://github.com/rust-lang/rust/issues/107792 @@ -9612,6 +12527,8 @@ The tracking issue for this feature is: [#107792] label: "raw_slice_split", description: r##"# `raw_slice_split` + + The tracking issue for this feature is: [#95595] [#95595]: https://github.com/rust-lang/rust/issues/95595 @@ -9626,6 +12543,8 @@ The tracking issue for this feature is: [#95595] label: "raw_vec_internals", description: r##"# `raw_vec_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -9635,9 +12554,27 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { + label: "read_array", + description: r##"# `read_array` + + + +The tracking issue for this feature is: [#148848] + +[#148848]: https://github.com/rust-lang/rust/issues/148848 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "read_buf", description: r##"# `read_buf` + + The tracking issue for this feature is: [#78485] [#78485]: https://github.com/rust-lang/rust/issues/78485 @@ -9649,9 +12586,43 @@ The tracking issue for this feature is: [#78485] deny_since: None, }, Lint { + label: "read_buf_at", + description: r##"# `read_buf_at` + + + +The tracking issue for this feature is: [#140771] + +[#140771]: https://github.com/rust-lang/rust/issues/140771 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "reborrow", + description: r##"# `reborrow` + + + +The tracking issue for this feature is: [#145612] + +[#145612]: https://github.com/rust-lang/rust/issues/145612 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "reentrant_lock", description: r##"# `reentrant_lock` + + The tracking issue for this feature is: [#121440] [#121440]: https://github.com/rust-lang/rust/issues/121440 @@ -9663,6 +12634,22 @@ The tracking issue for this feature is: [#121440] deny_since: None, }, Lint { + label: "reentrant_lock_data_ptr", + description: r##"# `reentrant_lock_data_ptr` + + + +The tracking issue for this feature is: [#140368] + +[#140368]: https://github.com/rust-lang/rust/issues/140368 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "ref_pat_eat_one_layer_2024", description: r##"# `ref_pat_eat_one_layer_2024` @@ -9670,7 +12657,41 @@ The tracking issue for this feature is: [#123076] [#123076]: https://github.com/rust-lang/rust/issues/123076 ------------------------- +--- + +This feature is incomplete and not yet intended for general use. + +This implements experimental, Edition-dependent match ergonomics under consideration for inclusion +in Rust, allowing `&` patterns in more places. For example: + +```rust,edition2024 +#![feature(ref_pat_eat_one_layer_2024)] +#![allow(incomplete_features)] +# +# // Tests type equality in a way that avoids coercing `&&T` or `&mut T` to `&T`. +# trait Eq<T> {} +# impl<T> Eq<T> for T {} +# fn has_type<T>(_: impl Eq<T>) {} + +// `&` can match against a `ref` binding mode instead of a reference type: +let (x, &y) = &(0, 1); +has_type::<&u8>(x); +has_type::<u8>(y); + +// `&` can match against `&mut` references: +let &z = &mut 2; +has_type::<u8>(z); +``` + +For specifics, see the corresponding typing rules for [Editions 2021 and earlier] and for +[Editions 2024 and later]. For more information on binding modes, see [The Rust Reference]. + +For alternative experimental match ergonomics, see the feature +[`ref_pat_eat_one_layer_2024_structural`](./ref-pat-eat-one-layer-2024-structural.md). + +[Editions 2021 and earlier]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAQIBAQABAAAAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false +[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAAABAQABAgIAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false +[The Rust Reference]: https://doc.rust-lang.org/reference/patterns.html#binding-modes "##, default_severity: Severity::Allow, warn_since: None, @@ -9684,19 +12705,54 @@ The tracking issue for this feature is: [#123076] [#123076]: https://github.com/rust-lang/rust/issues/123076 ------------------------- +--- + +This feature is incomplete and not yet intended for general use. + +This implements experimental, Edition-dependent match ergonomics under consideration for inclusion +in Rust, allowing `&` patterns in more places. For example: +```rust,edition2024 +#![feature(ref_pat_eat_one_layer_2024_structural)] +#![allow(incomplete_features)] +# +# // Tests type equality in a way that avoids coercing `&&T` or `&mut T` to `&T`. +# trait Eq<T> {} +# impl<T> Eq<T> for T {} +# fn has_type<T>(_: impl Eq<T>) {} + +// `&` can match against a `ref` binding mode instead of a reference type: +let (x, &y) = &(0, 1); +has_type::<&u8>(x); +has_type::<u8>(y); + +// `&` can match against `&mut` references: +let &z = &mut 2; +has_type::<u8>(z); +``` + +For specifics, see the corresponding typing rules for [Editions 2021 and earlier] and for +[Editions 2024 and later]. For more information on binding modes, see [The Rust Reference]. + +For alternative experimental match ergonomics, see the feature +[`ref_pat_eat_one_layer_2024`](./ref-pat-eat-one-layer-2024.md). + +[Editions 2021 and earlier]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAQIBAQEBAAAAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false +[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAgEBAQEBAgIAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false +[The Rust Reference]: https://doc.rust-lang.org/reference/patterns.html#binding-modes "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "register_tool", - description: r##"# `register_tool` + label: "refcell_try_map", + description: r##"# `refcell_try_map` -The tracking issue for this feature is: [#66079] -[#66079]: https://github.com/rust-lang/rust/issues/66079 + +The tracking issue for this feature is: [#143801] + +[#143801]: https://github.com/rust-lang/rust/issues/143801 ------------------------ "##, @@ -9705,25 +12761,62 @@ The tracking issue for this feature is: [#66079] deny_since: None, }, Lint { - label: "repr128", - description: r##"# `repr128` + label: "register_tool", + description: r##"# `register_tool` -The tracking issue for this feature is: [#56071] +The tracking issue for this feature is: [#66079] -[#56071]: https://github.com/rust-lang/rust/issues/56071 +[#66079]: https://github.com/rust-lang/rust/issues/66079 ------------------------ -The `repr128` feature adds support for `#[repr(u128)]` on `enum`s. +The `register_tool` language feature informs the compiler that attributes in your code are meant to be used with tools other than the compiler itself. This can be useful if your code has semantic meaning without the external tool, but enables additional features when the tool is present. -```rust -#![feature(repr128)] +`register_tool` also allows configuring lint levels for external tools. -#[repr(u128)] -enum Foo { - Bar(u64), +Tool attributes are only meant for ignorable attributes. If your code *changes* meaning when the attribute is present, it should not use a tool attribute (because it cannot be compiled with anything other than the external tool, and in a sense is a fork of the language). + +------------------------ + +`#![register_tool(tool)]` is an attribute, and is only valid at the crate root. +Attributes using the registered tool are checked for valid syntax, and lint attributes are checked to be in a valid format. However, the compiler cannot validate the semantics of the attribute, nor can it tell whether the configured lint is present in the external tool. + +Semantically, `clippy::*`, `rustdoc::*`, and `rustfmt::*` lints and attributes all behave as if `#![register_tool(clippy, rustdoc, rustfmt)]` were injected into the crate root, except that the `rustdoc` namespace can only be used for lints, not for attributes. +When compiling with `-Z unstable-features`, `rustc::*` lints can also be used. Like `rustdoc`, the `rustc` namespace can only be used with lints, not attributes. + +The compiler will emit an error if it encounters a lint/attribute whose namespace isn't a registered tool. + +Tool namespaces cannot be nested; `register_tool(main_tool::subtool)` is an error. + +## Examples + +Tool attributes: + +```rust +#![feature(register_tool)] +#![register_tool(c2rust)] + +// Mark which C header file this module was generated from. +#[c2rust::header_src = "operations.h"] +pub mod operations_h { + use std::ffi::c_int; + + // Mark which source line this struct was generated from. + #[c2rust::src_loc = "11:0"] + pub struct Point { + pub x: c_int, + pub y: c_int, + } } ``` + +Tool lints: + +``` +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::duplicate_bevy_dependencies)] +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -9733,6 +12826,8 @@ enum Foo { label: "repr_simd", description: r##"# `repr_simd` +Allows `repr(simd)` and importing the various simd intrinsics. + The tracking issue for this feature is: [#27731] [#27731]: https://github.com/rust-lang/rust/issues/27731 @@ -9747,6 +12842,8 @@ The tracking issue for this feature is: [#27731] label: "restricted_std", description: r##"# `restricted_std` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -9756,12 +12853,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "result_flattening", - description: r##"# `result_flattening` + label: "result_option_map_or_default", + description: r##"# `result_option_map_or_default` + + -The tracking issue for this feature is: [#70142] +The tracking issue for this feature is: [#138099] -[#70142]: https://github.com/rust-lang/rust/issues/70142 +[#138099]: https://github.com/rust-lang/rust/issues/138099 ------------------------ "##, @@ -9773,6 +12872,8 @@ The tracking issue for this feature is: [#70142] label: "return_type_notation", description: r##"# `return_type_notation` +Allows bounding the return type of AFIT/RPITIT. + The tracking issue for this feature is: [#109417] [#109417]: https://github.com/rust-lang/rust/issues/109417 @@ -9784,12 +12885,14 @@ The tracking issue for this feature is: [#109417] deny_since: None, }, Lint { - label: "riscv_target_feature", - description: r##"# `riscv_target_feature` + label: "rev_into_inner", + description: r##"# `rev_into_inner` + -The tracking issue for this feature is: [#44839] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +The tracking issue for this feature is: [#144277] + +[#144277]: https://github.com/rust-lang/rust/issues/144277 ------------------------ "##, @@ -9798,12 +12901,14 @@ The tracking issue for this feature is: [#44839] deny_since: None, }, Lint { - label: "round_char_boundary", - description: r##"# `round_char_boundary` + label: "riscv_target_feature", + description: r##"# `riscv_target_feature` + +Target features on riscv. -The tracking issue for this feature is: [#93743] +The tracking issue for this feature is: [#150257] -[#93743]: https://github.com/rust-lang/rust/issues/93743 +[#150257]: https://github.com/rust-lang/rust/issues/150257 ------------------------ "##, @@ -9827,9 +12932,11 @@ This feature is internal to the Rust compiler and is not intended for general us label: "rtm_target_feature", description: r##"# `rtm_target_feature` -The tracking issue for this feature is: [#44839] +The rtm target feature on x86. -[#44839]: https://github.com/rust-lang/rust/issues/44839 +The tracking issue for this feature is: [#150258] + +[#150258]: https://github.com/rust-lang/rust/issues/150258 ------------------------ "##, @@ -9841,6 +12948,8 @@ The tracking issue for this feature is: [#44839] label: "rust_cold_cc", description: r##"# `rust_cold_cc` +Allows `extern "rust-cold"`. + The tracking issue for this feature is: [#97544] [#97544]: https://github.com/rust-lang/rust/issues/97544 @@ -9852,12 +12961,14 @@ The tracking issue for this feature is: [#97544] deny_since: None, }, Lint { - label: "rustc_allow_const_fn_unstable", - description: r##"# `rustc_allow_const_fn_unstable` + label: "rust_preserve_none_cc", + description: r##"# `rust_preserve_none_cc` -The tracking issue for this feature is: [#69399] +Allows `extern "rust-preserve-none"`. -[#69399]: https://github.com/rust-lang/rust/issues/69399 +The tracking issue for this feature is: [#151401] + +[#151401]: https://github.com/rust-lang/rust/issues/151401 ------------------------ "##, @@ -9926,18 +13037,6 @@ error: aborting due to 2 previous errors deny_since: None, }, Lint { - label: "rustc_encodable_decodable", - description: r##"# `rustc_encodable_decodable` - -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "rustc_private", description: r##"# `rustc_private` @@ -9947,9 +13046,42 @@ The tracking issue for this feature is: [#27812] ------------------------ -This feature allows access to unstable internal compiler crates. +This feature allows access to unstable internal compiler crates such as `rustc_driver`. + +The presence of this feature changes the way the linkage format for dylibs is calculated in a way +that is necessary for linking against dylibs that statically link `std` (such as `rustc_driver`). +This makes this feature "viral" in linkage; its use in a given crate makes its use required in +dependent crates which link to it (including integration tests, which are built as separate crates). + +## Common linker failures related to missing LLVM libraries + +### When using `rustc-private` with Official Toolchains + +When using the `rustc_private` feature with official toolchains distributed via rustup, you'll need to install: + +1. The `rustc-dev` component (provides compiler libraries) +2. The `llvm-tools` component (provides LLVM libraries needed for linking) + +You can install these components using `rustup`: + +```text +rustup component add rustc-dev llvm-tools +``` + +Without the `llvm-tools` component, you may encounter linking errors like: + +```text +error: linking with `cc` failed: exit status: 1 + | + = note: rust-lld: error: unable to find library -lLLVM-{version} +``` + +### When using `rustc-private` with Custom Toolchains -Additionally it changes the linking behavior of crates which have this feature enabled. It will prevent linking to a dylib if there's a static variant of it already statically linked into another dylib dependency. This is required to successfully link to `rustc_driver`. +For custom-built toolchains or environments not using rustup, different configuration may be required: + +- Ensure LLVM libraries are available in your library search paths +- You might need to configure library paths explicitly depending on your LLVM installation "##, default_severity: Severity::Allow, warn_since: None, @@ -9959,6 +13091,8 @@ Additionally it changes the linking behavior of crates which have this feature e label: "rustdoc_internals", description: r##"# `rustdoc_internals` +Allows using internal rustdoc features like `doc(keyword)`. + The tracking issue for this feature is: [#90418] [#90418]: https://github.com/rust-lang/rust/issues/90418 @@ -9973,6 +13107,8 @@ The tracking issue for this feature is: [#90418] label: "rustdoc_missing_doc_code_examples", description: r##"# `rustdoc_missing_doc_code_examples` +Allows using the `rustdoc::missing_doc_code_examples` lint + The tracking issue for this feature is: [#101730] [#101730]: https://github.com/rust-lang/rust/issues/101730 @@ -9984,12 +13120,14 @@ The tracking issue for this feature is: [#101730] deny_since: None, }, Lint { - label: "rwlock_downgrade", - description: r##"# `rwlock_downgrade` + label: "rwlock_data_ptr", + description: r##"# `rwlock_data_ptr` + -The tracking issue for this feature is: [#128203] -[#128203]: https://github.com/rust-lang/rust/issues/128203 +The tracking issue for this feature is: [#140368] + +[#140368]: https://github.com/rust-lang/rust/issues/140368 ------------------------ "##, @@ -10001,9 +13139,11 @@ The tracking issue for this feature is: [#128203] label: "s390x_target_feature", description: r##"# `s390x_target_feature` -The tracking issue for this feature is: [#44839] +Target features on s390x. + +The tracking issue for this feature is: [#150259] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +[#150259]: https://github.com/rust-lang/rust/issues/150259 ------------------------ "##, @@ -10012,9 +13152,91 @@ The tracking issue for this feature is: [#44839] deny_since: None, }, Lint { + label: "sanitize", + description: r##"# `sanitize` + +The tracking issue for this feature is: [#39699] + +[#39699]: https://github.com/rust-lang/rust/issues/39699 + +------------------------ + +The `sanitize` attribute can be used to selectively disable or enable sanitizer +instrumentation in an annotated function. This might be useful to: avoid +instrumentation overhead in a performance critical function, or avoid +instrumenting code that contains constructs unsupported by given sanitizer. + +The precise effect of this annotation depends on particular sanitizer in use. +For example, with `sanitize(thread = "off")`, the thread sanitizer will no +longer instrument non-atomic store / load operations, but it will instrument +atomic operations to avoid reporting false positives and provide meaning full +stack traces. + +This attribute was previously named `no_sanitize`. + +## Examples + +``` rust +#![feature(sanitize)] + +#[sanitize(address = "off")] +fn foo() { + // ... +} +``` + +It is also possible to disable sanitizers for entire modules and enable them +for single items or functions. + +```rust +#![feature(sanitize)] + +#[sanitize(address = "off")] +mod foo { + fn unsanitized() { + // ... + } + + #[sanitize(address = "on")] + fn sanitized() { + // ... + } +} +``` + +It's also applicable to impl blocks. + +```rust +#![feature(sanitize)] + +trait MyTrait { + fn foo(&self); + fn bar(&self); +} + +#[sanitize(address = "off")] +impl MyTrait for () { + fn foo(&self) { + // ... + } + + #[sanitize(address = "on")] + fn bar(&self) { + // ... + } +} +``` +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "sealed", description: r##"# `sealed` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -10024,9 +13246,27 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { + label: "seek_io_take_position", + description: r##"# `seek_io_take_position` + + + +The tracking issue for this feature is: [#97227] + +[#97227]: https://github.com/rust-lang/rust/issues/97227 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "seek_stream_len", description: r##"# `seek_stream_len` + + The tracking issue for this feature is: [#59359] [#59359]: https://github.com/rust-lang/rust/issues/59359 @@ -10038,9 +13278,27 @@ The tracking issue for this feature is: [#59359] deny_since: None, }, Lint { + label: "set_permissions_nofollow", + description: r##"# `set_permissions_nofollow` + + + +The tracking issue for this feature is: [#141607] + +[#141607]: https://github.com/rust-lang/rust/issues/141607 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "set_ptr_value", description: r##"# `set_ptr_value` + + The tracking issue for this feature is: [#75091] [#75091]: https://github.com/rust-lang/rust/issues/75091 @@ -10055,6 +13313,8 @@ The tracking issue for this feature is: [#75091] label: "setgroups", description: r##"# `setgroups` + + The tracking issue for this feature is: [#90747] [#90747]: https://github.com/rust-lang/rust/issues/90747 @@ -10069,6 +13329,8 @@ The tracking issue for this feature is: [#90747] label: "sgx_platform", description: r##"# `sgx_platform` + + The tracking issue for this feature is: [#56975] [#56975]: https://github.com/rust-lang/rust/issues/56975 @@ -10080,12 +13342,14 @@ The tracking issue for this feature is: [#56975] deny_since: None, }, Lint { - label: "sha512_sm_x86", - description: r##"# `sha512_sm_x86` + label: "signed_bigint_helpers", + description: r##"# `signed_bigint_helpers` -The tracking issue for this feature is: [#126624] -[#126624]: https://github.com/rust-lang/rust/issues/126624 + +The tracking issue for this feature is: [#151989] + +[#151989]: https://github.com/rust-lang/rust/issues/151989 ------------------------ "##, @@ -10097,6 +13361,8 @@ The tracking issue for this feature is: [#126624] label: "simd_ffi", description: r##"# `simd_ffi` +Allows the use of SIMD types in functions declared in `extern` blocks. + The tracking issue for this feature is: [#27731] [#27731]: https://github.com/rust-lang/rust/issues/27731 @@ -10108,24 +13374,14 @@ The tracking issue for this feature is: [#27731] deny_since: None, }, Lint { - label: "sized_type_properties", - description: r##"# `sized_type_properties` - -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + label: "sized_hierarchy", + description: r##"# `sized_hierarchy` ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "slice_as_array", - description: r##"# `slice_as_array` +Introduces a hierarchy of `Sized` traits (RFC 3729). -The tracking issue for this feature is: [#133508] +The tracking issue for this feature is: [#144404] -[#133508]: https://github.com/rust-lang/rust/issues/133508 +[#144404]: https://github.com/rust-lang/rust/issues/144404 ------------------------ "##, @@ -10134,12 +13390,12 @@ The tracking issue for this feature is: [#133508] deny_since: None, }, Lint { - label: "slice_as_chunks", - description: r##"# `slice_as_chunks` + label: "sized_type_properties", + description: r##"# `sized_type_properties` -The tracking issue for this feature is: [#74985] -[#74985]: https://github.com/rust-lang/rust/issues/74985 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -10151,6 +13407,8 @@ The tracking issue for this feature is: [#74985] label: "slice_concat_ext", description: r##"# `slice_concat_ext` + + The tracking issue for this feature is: [#27747] [#27747]: https://github.com/rust-lang/rust/issues/27747 @@ -10165,6 +13423,8 @@ The tracking issue for this feature is: [#27747] label: "slice_concat_trait", description: r##"# `slice_concat_trait` + + The tracking issue for this feature is: [#27747] [#27747]: https://github.com/rust-lang/rust/issues/27747 @@ -10179,6 +13439,8 @@ The tracking issue for this feature is: [#27747] label: "slice_from_ptr_range", description: r##"# `slice_from_ptr_range` + + The tracking issue for this feature is: [#89792] [#89792]: https://github.com/rust-lang/rust/issues/89792 @@ -10193,6 +13455,8 @@ The tracking issue for this feature is: [#89792] label: "slice_index_methods", description: r##"# `slice_index_methods` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -10205,6 +13469,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "slice_internals", description: r##"# `slice_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -10217,6 +13483,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "slice_iter_mut_as_mut_slice", description: r##"# `slice_iter_mut_as_mut_slice` + + The tracking issue for this feature is: [#93079] [#93079]: https://github.com/rust-lang/rust/issues/93079 @@ -10228,9 +13496,27 @@ The tracking issue for this feature is: [#93079] deny_since: None, }, Lint { + label: "slice_partial_sort_unstable", + description: r##"# `slice_partial_sort_unstable` + + + +The tracking issue for this feature is: [#149046] + +[#149046]: https://github.com/rust-lang/rust/issues/149046 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "slice_partition_dedup", description: r##"# `slice_partition_dedup` + + The tracking issue for this feature is: [#54279] [#54279]: https://github.com/rust-lang/rust/issues/54279 @@ -10245,6 +13531,8 @@ The tracking issue for this feature is: [#54279] label: "slice_pattern", description: r##"# `slice_pattern` + + The tracking issue for this feature is: [#56345] [#56345]: https://github.com/rust-lang/rust/issues/56345 @@ -10259,6 +13547,8 @@ The tracking issue for this feature is: [#56345] label: "slice_ptr_get", description: r##"# `slice_ptr_get` + + The tracking issue for this feature is: [#74265] [#74265]: https://github.com/rust-lang/rust/issues/74265 @@ -10273,6 +13563,8 @@ The tracking issue for this feature is: [#74265] label: "slice_range", description: r##"# `slice_range` + + The tracking issue for this feature is: [#76393] [#76393]: https://github.com/rust-lang/rust/issues/76393 @@ -10284,9 +13576,27 @@ The tracking issue for this feature is: [#76393] deny_since: None, }, Lint { + label: "slice_shift", + description: r##"# `slice_shift` + + + +The tracking issue for this feature is: [#151772] + +[#151772]: https://github.com/rust-lang/rust/issues/151772 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "slice_split_once", description: r##"# `slice_split_once` + + The tracking issue for this feature is: [#112811] [#112811]: https://github.com/rust-lang/rust/issues/112811 @@ -10301,6 +13611,8 @@ The tracking issue for this feature is: [#112811] label: "slice_swap_unchecked", description: r##"# `slice_swap_unchecked` + + The tracking issue for this feature is: [#88539] [#88539]: https://github.com/rust-lang/rust/issues/88539 @@ -10312,12 +13624,30 @@ The tracking issue for this feature is: [#88539] deny_since: None, }, Lint { - label: "slice_take", - description: r##"# `slice_take` + label: "sliceindex_wrappers", + description: r##"# `sliceindex_wrappers` + + + +The tracking issue for this feature is: [#146179] + +[#146179]: https://github.com/rust-lang/rust/issues/146179 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "smart_pointer_try_map", + description: r##"# `smart_pointer_try_map` + + -The tracking issue for this feature is: [#62280] +The tracking issue for this feature is: [#144419] -[#62280]: https://github.com/rust-lang/rust/issues/62280 +[#144419]: https://github.com/rust-lang/rust/issues/144419 ------------------------ "##, @@ -10329,6 +13659,8 @@ The tracking issue for this feature is: [#62280] label: "solid_ext", description: r##"# `solid_ext` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -10341,6 +13673,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "sort_floats", description: r##"# `sort_floats` + + The tracking issue for this feature is: [#93396] [#93396]: https://github.com/rust-lang/rust/issues/93396 @@ -10355,6 +13689,8 @@ The tracking issue for this feature is: [#93396] label: "sparc_target_feature", description: r##"# `sparc_target_feature` +Target features on sparc. + The tracking issue for this feature is: [#132783] [#132783]: https://github.com/rust-lang/rust/issues/132783 @@ -10369,6 +13705,8 @@ The tracking issue for this feature is: [#132783] label: "specialization", description: r##"# `specialization` +Allows specialization of implementations (RFC 1210). + The tracking issue for this feature is: [#31844] [#31844]: https://github.com/rust-lang/rust/issues/31844 @@ -10383,6 +13721,8 @@ The tracking issue for this feature is: [#31844] label: "split_array", description: r##"# `split_array` + + The tracking issue for this feature is: [#90091] [#90091]: https://github.com/rust-lang/rust/issues/90091 @@ -10397,6 +13737,8 @@ The tracking issue for this feature is: [#90091] label: "split_as_slice", description: r##"# `split_as_slice` + + The tracking issue for this feature is: [#96137] [#96137]: https://github.com/rust-lang/rust/issues/96137 @@ -10408,12 +13750,12 @@ The tracking issue for this feature is: [#96137] deny_since: None, }, Lint { - label: "sse4a_target_feature", - description: r##"# `sse4a_target_feature` + label: "staged_api", + description: r##"# `staged_api` -The tracking issue for this feature is: [#44839] +Allows using the `#[stable]` and `#[unstable]` attributes. -[#44839]: https://github.com/rust-lang/rust/issues/44839 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -10422,10 +13764,14 @@ The tracking issue for this feature is: [#44839] deny_since: None, }, Lint { - label: "staged_api", - description: r##"# `staged_api` + label: "static_align", + description: r##"# `static_align` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +Allows using `#[rustc_align_static(...)]` on static items. + +The tracking issue for this feature is: [#146177] + +[#146177]: https://github.com/rust-lang/rust/issues/146177 ------------------------ "##, @@ -10434,74 +13780,56 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "start", - description: r##"# `start` - -The tracking issue for this feature is: [#29633] - -[#29633]: https://github.com/rust-lang/rust/issues/29633 - ------------------------- + label: "std_internals", + description: r##"# `std_internals` -Allows you to mark a function as the entry point of the executable, which is -necessary in `#![no_std]` environments. -The function marked `#[start]` is passed the command line parameters in the same -format as the C main function (aside from the integer types being used). -It has to be non-generic and have the following signature: -```rust,ignore (only-for-syntax-highlight) -# let _: -fn(isize, *const *const u8) -> isize -# ; -``` +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. -This feature should not be confused with the `start` *lang item* which is -defined by the `std` crate and is written `#[lang = "start"]`. +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "stdarch_aarch64_feature_detection", + description: r##"# `stdarch_aarch64_feature_detection` -## Usage together with the `std` crate -`#[start]` can be used in combination with the `std` crate, in which case the -normal `main` function (which would get called from the `std` crate) won't be -used as an entry point. -The initialization code in `std` will be skipped this way. -Example: +The tracking issue for this feature is: [#127764] -```rust -#![feature(start)] +[#127764]: https://github.com/rust-lang/rust/issues/127764 -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} -``` +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "stdarch_arm_feature_detection", + description: r##"# `stdarch_arm_feature_detection` -Unwinding the stack past the `#[start]` function is currently considered -Undefined Behavior (for any unwinding implementation): -```rust,ignore (UB) -#![feature(start)] -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - std::panic::catch_unwind(|| { - panic!(); // panic safely gets caught or safely aborts execution - }); +The tracking issue for this feature is: [#111190] - panic!(); // UB! +[#111190]: https://github.com/rust-lang/rust/issues/111190 - 0 -} -``` +------------------------ "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "std_internals", - description: r##"# `std_internals` + label: "stdarch_internal", + description: r##"# `stdarch_internal` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. @@ -10512,12 +13840,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "stdarch_arm_feature_detection", - description: r##"# `stdarch_arm_feature_detection` + label: "stdarch_loongarch_feature_detection", + description: r##"# `stdarch_loongarch_feature_detection` -The tracking issue for this feature is: [#111190] -[#111190]: https://github.com/rust-lang/rust/issues/111190 + +The tracking issue for this feature is: [#117425] + +[#117425]: https://github.com/rust-lang/rust/issues/117425 ------------------------ "##, @@ -10529,6 +13859,8 @@ The tracking issue for this feature is: [#111190] label: "stdarch_mips_feature_detection", description: r##"# `stdarch_mips_feature_detection` + + The tracking issue for this feature is: [#111188] [#111188]: https://github.com/rust-lang/rust/issues/111188 @@ -10543,6 +13875,8 @@ The tracking issue for this feature is: [#111188] label: "stdarch_powerpc_feature_detection", description: r##"# `stdarch_powerpc_feature_detection` + + The tracking issue for this feature is: [#111191] [#111191]: https://github.com/rust-lang/rust/issues/111191 @@ -10554,9 +13888,43 @@ The tracking issue for this feature is: [#111191] deny_since: None, }, Lint { + label: "stdarch_riscv_feature_detection", + description: r##"# `stdarch_riscv_feature_detection` + + + +The tracking issue for this feature is: [#111192] + +[#111192]: https://github.com/rust-lang/rust/issues/111192 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "stdio_fd_consts", + description: r##"# `stdio_fd_consts` + + + +The tracking issue for this feature is: [#150836] + +[#150836]: https://github.com/rust-lang/rust/issues/150836 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "stdio_makes_pipe", description: r##"# `stdio_makes_pipe` + + The tracking issue for this feature is: [#98288] [#98288]: https://github.com/rust-lang/rust/issues/98288 @@ -10568,9 +13936,27 @@ The tracking issue for this feature is: [#98288] deny_since: None, }, Lint { + label: "stdio_swap", + description: r##"# `stdio_swap` + + + +The tracking issue for this feature is: [#150667] + +[#150667]: https://github.com/rust-lang/rust/issues/150667 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "step_trait", description: r##"# `step_trait` + + The tracking issue for this feature is: [#42168] [#42168]: https://github.com/rust-lang/rust/issues/42168 @@ -10585,6 +13971,8 @@ The tracking issue for this feature is: [#42168] label: "stmt_expr_attributes", description: r##"# `stmt_expr_attributes` +Allows attributes on expressions and non-item statements. + The tracking issue for this feature is: [#15701] [#15701]: https://github.com/rust-lang/rust/issues/15701 @@ -10599,6 +13987,8 @@ The tracking issue for this feature is: [#15701] label: "str_as_str", description: r##"# `str_as_str` + + The tracking issue for this feature is: [#130366] [#130366]: https://github.com/rust-lang/rust/issues/130366 @@ -10613,6 +14003,8 @@ The tracking issue for this feature is: [#130366] label: "str_from_raw_parts", description: r##"# `str_from_raw_parts` + + The tracking issue for this feature is: [#119206] [#119206]: https://github.com/rust-lang/rust/issues/119206 @@ -10627,6 +14019,8 @@ The tracking issue for this feature is: [#119206] label: "str_from_utf16_endian", description: r##"# `str_from_utf16_endian` + + The tracking issue for this feature is: [#116258] [#116258]: https://github.com/rust-lang/rust/issues/116258 @@ -10653,6 +14047,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "str_lines_remainder", description: r##"# `str_lines_remainder` + + The tracking issue for this feature is: [#77998] [#77998]: https://github.com/rust-lang/rust/issues/77998 @@ -10667,6 +14063,8 @@ The tracking issue for this feature is: [#77998] label: "str_split_inclusive_remainder", description: r##"# `str_split_inclusive_remainder` + + The tracking issue for this feature is: [#77998] [#77998]: https://github.com/rust-lang/rust/issues/77998 @@ -10681,6 +14079,8 @@ The tracking issue for this feature is: [#77998] label: "str_split_remainder", description: r##"# `str_split_remainder` + + The tracking issue for this feature is: [#77998] [#77998]: https://github.com/rust-lang/rust/issues/77998 @@ -10695,23 +14095,11 @@ The tracking issue for this feature is: [#77998] label: "str_split_whitespace_remainder", description: r##"# `str_split_whitespace_remainder` -The tracking issue for this feature is: [#77998] - -[#77998]: https://github.com/rust-lang/rust/issues/77998 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "strict_provenance_atomic_ptr", - description: r##"# `strict_provenance_atomic_ptr` -The tracking issue for this feature is: [#99108] +The tracking issue for this feature is: [#77998] -[#99108]: https://github.com/rust-lang/rust/issues/99108 +[#77998]: https://github.com/rust-lang/rust/issues/77998 ------------------------ "##, @@ -10748,64 +14136,46 @@ fn main() { deny_since: None, }, Lint { - label: "string_deref_patterns", - description: r##"# `string_deref_patterns` + label: "string_from_utf8_lossy_owned", + description: r##"# `string_from_utf8_lossy_owned` -The tracking issue for this feature is: [#87121] -[#87121]: https://github.com/rust-lang/rust/issues/87121 ------------------------- +The tracking issue for this feature is: [#129436] -This feature permits pattern matching `String` to `&str` through [its `Deref` implementation]. +[#129436]: https://github.com/rust-lang/rust/issues/129436 -```rust -#![feature(string_deref_patterns)] +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "string_into_chars", + description: r##"# `string_into_chars` -pub enum Value { - String(String), - Number(u32), -} -pub fn is_it_the_answer(value: Value) -> bool { - match value { - Value::String("42") => true, - Value::Number(42) => true, - _ => false, - } -} -``` -Without this feature other constructs such as match guards have to be used. +The tracking issue for this feature is: [#133125] -```rust -# pub enum Value { -# String(String), -# Number(u32), -# } -# -pub fn is_it_the_answer(value: Value) -> bool { - match value { - Value::String(s) if s == "42" => true, - Value::Number(42) => true, - _ => false, - } -} -``` +[#133125]: https://github.com/rust-lang/rust/issues/133125 -[its `Deref` implementation]: https://doc.rust-lang.org/std/string/struct.String.html#impl-Deref-for-String +------------------------ "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "string_extend_from_within", - description: r##"# `string_extend_from_within` + label: "string_remove_matches", + description: r##"# `string_remove_matches` -The tracking issue for this feature is: [#103806] -[#103806]: https://github.com/rust-lang/rust/issues/103806 + +The tracking issue for this feature is: [#72826] + +[#72826]: https://github.com/rust-lang/rust/issues/72826 ------------------------ "##, @@ -10814,12 +14184,14 @@ The tracking issue for this feature is: [#103806] deny_since: None, }, Lint { - label: "string_from_utf8_lossy_owned", - description: r##"# `string_from_utf8_lossy_owned` + label: "string_replace_in_place", + description: r##"# `string_replace_in_place` -The tracking issue for this feature is: [#129436] -[#129436]: https://github.com/rust-lang/rust/issues/129436 + +The tracking issue for this feature is: [#147949] + +[#147949]: https://github.com/rust-lang/rust/issues/147949 ------------------------ "##, @@ -10828,12 +14200,14 @@ The tracking issue for this feature is: [#129436] deny_since: None, }, Lint { - label: "string_remove_matches", - description: r##"# `string_remove_matches` + label: "strip_circumfix", + description: r##"# `strip_circumfix` -The tracking issue for this feature is: [#72826] -[#72826]: https://github.com/rust-lang/rust/issues/72826 + +The tracking issue for this feature is: [#147946] + +[#147946]: https://github.com/rust-lang/rust/issues/147946 ------------------------ "##, @@ -10845,6 +14219,8 @@ The tracking issue for this feature is: [#72826] label: "structural_match", description: r##"# `structural_match` +Allows using `#[structural_match]` which indicates that a type is structurally matchable. FIXME: Subsumed by trait `StructuralPartialEq`, cannot move to removed until a library feature with the same name exists. + The tracking issue for this feature is: [#31434] [#31434]: https://github.com/rust-lang/rust/issues/31434 @@ -10859,6 +14235,8 @@ The tracking issue for this feature is: [#31434] label: "substr_range", description: r##"# `substr_range` + + The tracking issue for this feature is: [#126769] [#126769]: https://github.com/rust-lang/rust/issues/126769 @@ -10870,12 +14248,14 @@ The tracking issue for this feature is: [#126769] deny_since: None, }, Lint { - label: "sync_unsafe_cell", - description: r##"# `sync_unsafe_cell` + label: "super_let", + description: r##"# `super_let` -The tracking issue for this feature is: [#95439] +Allows `super let` statements. -[#95439]: https://github.com/rust-lang/rust/issues/95439 +The tracking issue for this feature is: [#139076] + +[#139076]: https://github.com/rust-lang/rust/issues/139076 ------------------------ "##, @@ -10884,12 +14264,14 @@ The tracking issue for this feature is: [#95439] deny_since: None, }, Lint { - label: "target_feature_11", - description: r##"# `target_feature_11` + label: "supertrait_item_shadowing", + description: r##"# `supertrait_item_shadowing` -The tracking issue for this feature is: [#69098] +Allows subtrait items to shadow supertrait items. -[#69098]: https://github.com/rust-lang/rust/issues/69098 +The tracking issue for this feature is: [#89151] + +[#89151]: https://github.com/rust-lang/rust/issues/89151 ------------------------ "##, @@ -10898,12 +14280,62 @@ The tracking issue for this feature is: [#69098] deny_since: None, }, Lint { - label: "tbm_target_feature", - description: r##"# `tbm_target_feature` + label: "sync_nonpoison", + description: r##"# `sync_nonpoison` + -The tracking issue for this feature is: [#44839] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +The tracking issue for this feature is: [#134645] + +[#134645]: https://github.com/rust-lang/rust/issues/134645 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "sync_poison_mod", + description: r##"# `sync_poison_mod` + + + +The tracking issue for this feature is: [#134646] + +[#134646]: https://github.com/rust-lang/rust/issues/134646 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "sync_unsafe_cell", + description: r##"# `sync_unsafe_cell` + + + +The tracking issue for this feature is: [#95439] + +[#95439]: https://github.com/rust-lang/rust/issues/95439 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "target_feature_inline_always", + description: r##"# `target_feature_inline_always` + +Allows the use of target_feature when a function is marked inline(always). + +The tracking issue for this feature is: [#145574] + +[#145574]: https://github.com/rust-lang/rust/issues/145574 ------------------------ "##, @@ -10915,6 +14347,8 @@ The tracking issue for this feature is: [#44839] label: "tcp_deferaccept", description: r##"# `tcp_deferaccept` + + The tracking issue for this feature is: [#119639] [#119639]: https://github.com/rust-lang/rust/issues/119639 @@ -10929,6 +14363,8 @@ The tracking issue for this feature is: [#119639] label: "tcp_linger", description: r##"# `tcp_linger` + + The tracking issue for this feature is: [#88494] [#88494]: https://github.com/rust-lang/rust/issues/88494 @@ -10940,12 +14376,14 @@ The tracking issue for this feature is: [#88494] deny_since: None, }, Lint { - label: "tcp_quickack", - description: r##"# `tcp_quickack` + label: "tcplistener_into_incoming", + description: r##"# `tcplistener_into_incoming` + + -The tracking issue for this feature is: [#96256] +The tracking issue for this feature is: [#88373] -[#96256]: https://github.com/rust-lang/rust/issues/96256 +[#88373]: https://github.com/rust-lang/rust/issues/88373 ------------------------ "##, @@ -10954,12 +14392,12 @@ The tracking issue for this feature is: [#96256] deny_since: None, }, Lint { - label: "tcplistener_into_incoming", - description: r##"# `tcplistener_into_incoming` + label: "temporary_niche_types", + description: r##"# `temporary_niche_types` -The tracking issue for this feature is: [#88373] -[#88373]: https://github.com/rust-lang/rust/issues/88373 + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -11133,9 +14571,25 @@ even when using either of the above. deny_since: None, }, Lint { + label: "test_incomplete_feature", + description: r##"# `test_incomplete_feature` + +Perma-unstable, only used to test the `incomplete_features` lint. + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "test_unstable_lint", description: r##"# `test_unstable_lint` +Added for testing unstable lints; perma-unstable. + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -11148,6 +14602,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "thin_box", description: r##"# `thin_box` + + The tracking issue for this feature is: [#92791] [#92791]: https://github.com/rust-lang/rust/issues/92791 @@ -11162,6 +14618,8 @@ The tracking issue for this feature is: [#92791] label: "thread_id_value", description: r##"# `thread_id_value` + + The tracking issue for this feature is: [#67939] [#67939]: https://github.com/rust-lang/rust/issues/67939 @@ -11176,6 +14634,8 @@ The tracking issue for this feature is: [#67939] label: "thread_local", description: r##"# `thread_local` +Allows using `#[thread_local]` on `static` items. + The tracking issue for this feature is: [#29594] [#29594]: https://github.com/rust-lang/rust/issues/29594 @@ -11202,6 +14662,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "thread_raw", description: r##"# `thread_raw` + + The tracking issue for this feature is: [#97523] [#97523]: https://github.com/rust-lang/rust/issues/97523 @@ -11216,6 +14678,8 @@ The tracking issue for this feature is: [#97523] label: "thread_sleep_until", description: r##"# `thread_sleep_until` + + The tracking issue for this feature is: [#113752] [#113752]: https://github.com/rust-lang/rust/issues/113752 @@ -11230,6 +14694,8 @@ The tracking issue for this feature is: [#113752] label: "thread_spawn_hook", description: r##"# `thread_spawn_hook` + + The tracking issue for this feature is: [#132951] [#132951]: https://github.com/rust-lang/rust/issues/132951 @@ -11241,6 +14707,54 @@ The tracking issue for this feature is: [#132951] deny_since: None, }, Lint { + label: "time_saturating_systemtime", + description: r##"# `time_saturating_systemtime` + + + +The tracking issue for this feature is: [#151199] + +[#151199]: https://github.com/rust-lang/rust/issues/151199 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "time_systemtime_limits", + description: r##"# `time_systemtime_limits` + + + +The tracking issue for this feature is: [#149067] + +[#149067]: https://github.com/rust-lang/rust/issues/149067 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "titlecase", + description: r##"# `titlecase` + + + +The tracking issue for this feature is: [#153892] + +[#153892]: https://github.com/rust-lang/rust/issues/153892 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "trace_macros", description: r##"# `trace_macros` @@ -11287,20 +14801,6 @@ note: trace_macro deny_since: None, }, Lint { - label: "track_path", - description: r##"# `track_path` - -The tracking issue for this feature is: [#99515] - -[#99515]: https://github.com/rust-lang/rust/issues/99515 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "trait_alias", description: r##"# `trait_alias` @@ -11342,42 +14842,11 @@ pub fn main() { deny_since: None, }, Lint { - label: "trait_upcasting", - description: r##"# `trait_upcasting` - -The tracking issue for this feature is: [#65991] - -[#65991]: https://github.com/rust-lang/rust/issues/65991 - ------------------------- - -The `trait_upcasting` feature adds support for trait upcasting coercion. This allows a -trait object of type `dyn Bar` to be cast to a trait object of type `dyn Foo` -so long as `Bar: Foo`. - -```rust,edition2018 -#![feature(trait_upcasting)] - -trait Foo {} - -trait Bar: Foo {} - -impl Foo for i32 {} - -impl<T: Foo + ?Sized> Bar for T {} - -let bar: &dyn Bar = &123; -let foo: &dyn Foo = bar; -``` -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "transmutability", description: r##"# `transmutability` + + The tracking issue for this feature is: [#99571] [#99571]: https://github.com/rust-lang/rust/issues/99571 @@ -11392,6 +14861,8 @@ The tracking issue for this feature is: [#99571] label: "transmute_generic_consts", description: r##"# `transmute_generic_consts` +Allows for transmuting between arrays with sizes that contain generic consts. + The tracking issue for this feature is: [#109929] [#109929]: https://github.com/rust-lang/rust/issues/109929 @@ -11493,9 +14964,27 @@ their application of these optimizations. deny_since: None, }, Lint { + label: "trim_prefix_suffix", + description: r##"# `trim_prefix_suffix` + + + +The tracking issue for this feature is: [#142312] + +[#142312]: https://github.com/rust-lang/rust/issues/142312 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "trivial_bounds", description: r##"# `trivial_bounds` +Allows inconsistent bounds in where clauses. + The tracking issue for this feature is: [#48214] [#48214]: https://github.com/rust-lang/rust/issues/48214 @@ -11507,9 +14996,25 @@ The tracking issue for this feature is: [#48214] deny_since: None, }, Lint { + label: "trivial_clone", + description: r##"# `trivial_clone` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "trusted_fused", description: r##"# `trusted_fused` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -11522,6 +15027,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "trusted_len", description: r##"# `trusted_len` + + The tracking issue for this feature is: [#37572] [#37572]: https://github.com/rust-lang/rust/issues/37572 @@ -11536,6 +15043,8 @@ The tracking issue for this feature is: [#37572] label: "trusted_len_next_unchecked", description: r##"# `trusted_len_next_unchecked` + + The tracking issue for this feature is: [#37572] [#37572]: https://github.com/rust-lang/rust/issues/37572 @@ -11550,6 +15059,8 @@ The tracking issue for this feature is: [#37572] label: "trusted_random_access", description: r##"# `trusted_random_access` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -11562,6 +15073,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "trusted_step", description: r##"# `trusted_step` + + The tracking issue for this feature is: [#85731] [#85731]: https://github.com/rust-lang/rust/issues/85731 @@ -11573,6 +15086,22 @@ The tracking issue for this feature is: [#85731] deny_since: None, }, Lint { + label: "try_as_dyn", + description: r##"# `try_as_dyn` + + + +The tracking issue for this feature is: [#144361] + +[#144361]: https://github.com/rust-lang/rust/issues/144361 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "try_blocks", description: r##"# `try_blocks` @@ -11610,9 +15139,27 @@ assert!(result.is_err()); deny_since: None, }, Lint { + label: "try_blocks_heterogeneous", + description: r##"# `try_blocks_heterogeneous` + +Allows using `try bikeshed TargetType {...}` expressions. + +The tracking issue for this feature is: [#149488] + +[#149488]: https://github.com/rust-lang/rust/issues/149488 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "try_find", description: r##"# `try_find` + + The tracking issue for this feature is: [#63178] [#63178]: https://github.com/rust-lang/rust/issues/63178 @@ -11627,6 +15174,8 @@ The tracking issue for this feature is: [#63178] label: "try_reserve_kind", description: r##"# `try_reserve_kind` + + The tracking issue for this feature is: [#48043] [#48043]: https://github.com/rust-lang/rust/issues/48043 @@ -11641,6 +15190,8 @@ The tracking issue for this feature is: [#48043] label: "try_trait_v2", description: r##"# `try_trait_v2` + + The tracking issue for this feature is: [#84277] [#84277]: https://github.com/rust-lang/rust/issues/84277 @@ -11655,6 +15206,8 @@ The tracking issue for this feature is: [#84277] label: "try_trait_v2_residual", description: r##"# `try_trait_v2_residual` + + The tracking issue for this feature is: [#91285] [#91285]: https://github.com/rust-lang/rust/issues/91285 @@ -11669,6 +15222,8 @@ The tracking issue for this feature is: [#91285] label: "try_trait_v2_yeet", description: r##"# `try_trait_v2_yeet` + + The tracking issue for this feature is: [#96374] [#96374]: https://github.com/rust-lang/rust/issues/96374 @@ -11683,6 +15238,8 @@ The tracking issue for this feature is: [#96374] label: "try_with_capacity", description: r##"# `try_with_capacity` + + The tracking issue for this feature is: [#91913] [#91913]: https://github.com/rust-lang/rust/issues/91913 @@ -11697,6 +15254,8 @@ The tracking issue for this feature is: [#91913] label: "tuple_trait", description: r##"# `tuple_trait` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -11711,9 +15270,161 @@ This feature has no tracking issue, and is therefore likely internal to the comp The tracking issue for this feature is: [#63063] -[#63063]: https://github.com/rust-lang/rust/issues/63063 - ------------------------ + +> This feature is not to be confused with [`trait_alias`] or [`impl_trait_in_assoc_type`]. + +### What is `impl Trait`? + +`impl Trait` in return position is useful for declaring types that are constrained by traits, but whose concrete type should be hidden: + +```rust +use std::fmt::Debug; + +fn new() -> impl Debug { + 42 +} + +fn main() { + let thing = new(); + // What actually is a `thing`? + // No idea but we know it implements `Debug`, so we can debug print it + println!("{thing:?}"); +} +``` + +See the [reference] for more information about `impl Trait` in return position. + +### `type_alias_impl_trait` + +However, we might want to use an `impl Trait` in multiple locations but actually use the same concrete type everywhere while keeping it hidden. +This can be useful in libraries where you want to hide implementation details. + +The `#[define_opaque]` attribute must be used to explicitly list opaque items constrained by the item it's on. + +```rust +#![feature(type_alias_impl_trait)] +# #![allow(unused_variables, dead_code)] +trait Trait {} + +struct MyType; + +impl Trait for MyType {} + +type Alias = impl Trait; + +#[define_opaque(Alias)] // To constrain the type alias to `MyType` +fn new() -> Alias { + MyType +} + +#[define_opaque(Alias)] // So we can name the concrete type inside this item +fn main() { + let thing: MyType = new(); +} + +// It can be a part of a struct too +struct HaveAlias { + stuff: String, + thing: Alias, +} +``` + +In this example, the concrete type referred to by `Alias` is guaranteed to be the same wherever `Alias` occurs. + +> Originally this feature included type aliases as an associated type of a trait. In [#110237] this was split off to [`impl_trait_in_assoc_type`]. + +### `type_alias_impl_trait` in argument position. + +Note that using `Alias` as an argument type is *not* the same as argument-position `impl Trait`, as `Alias` refers to a unique type, whereas the concrete type for argument-position `impl Trait` is chosen by the caller. + +```rust +# #![feature(type_alias_impl_trait)] +# #![allow(unused_variables)] +# pub mod x { +# pub trait Trait {} +# +# struct MyType; +# +# impl Trait for MyType {} +# +# pub type Alias = impl Trait; +# +# #[define_opaque(Alias)] +# pub fn new() -> Alias { +# MyType +# } +# } +# use x::*; +// this... +pub fn take_alias(x: Alias) { + // ... +} + +// ...is *not* the same as +pub fn take_impl(x: impl Trait) { + // ... +} +# fn main(){} +``` + +```rust,compile_fail,E0308 +# #![feature(type_alias_impl_trait)] +# #![allow(unused_variables)] +# pub mod x { +# pub trait Trait {} +# +# struct MyType; +# +# impl Trait for MyType {} +# +# pub type Alias = impl Trait; +# +# #[define_opaque(Alias)] +# pub fn new() -> Alias { +# MyType +# } +# } +# use x::*; +# pub fn take_alias(x: Alias) { +# // ... +# } +# +# pub fn take_impl(x: impl Trait) { +# // ... +# } +# +// a user's crate using the trait and type alias +struct UserType; +impl Trait for UserType {} + +# fn main(){ +let x = UserType; +take_alias(x); +// ERROR expected opaque type, found `UserType` +// this function *actually* takes a `MyType` as is constrained in `new` + +let x = UserType; +take_impl(x); +// OK + +let x = new(); +take_alias(x); +// OK + +let x = new(); +take_impl(x); +// OK +# } +``` + +Note that the user cannot use `#[define_opaque(Alias)]` to reify the opaque type because only the crate where the type alias is declared may do so. But if this happened in the same crate and the opaque type was reified, they'd get a familiar error: "expected `MyType`, got `UserType`". + +[#63063]: https://github.com/rust-lang/rust/issues/63063 +[#110237]: https://github.com/rust-lang/rust/pull/110237 +[reference]: https://doc.rust-lang.org/stable/reference/types/impl-trait.html#abstract-return-types +[`trait_alias`]: ./trait-alias.md +[`impl_trait_in_assoc_type`]: ./impl-trait-in-assoc-type.md "##, default_severity: Severity::Allow, warn_since: None, @@ -11723,6 +15434,8 @@ The tracking issue for this feature is: [#63063] label: "type_ascription", description: r##"# `type_ascription` + + The tracking issue for this feature is: [#23416] [#23416]: https://github.com/rust-lang/rust/issues/23416 @@ -11774,9 +15487,27 @@ fn main () { deny_since: None, }, Lint { + label: "type_info", + description: r##"# `type_info` + + + +The tracking issue for this feature is: [#146922] + +[#146922]: https://github.com/rust-lang/rust/issues/146922 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "ub_checks", description: r##"# `ub_checks` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -11789,6 +15520,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "uefi_std", description: r##"# `uefi_std` + + The tracking issue for this feature is: [#100499] [#100499]: https://github.com/rust-lang/rust/issues/100499 @@ -11800,12 +15533,46 @@ The tracking issue for this feature is: [#100499] deny_since: None, }, Lint { - label: "unbounded_shifts", - description: r##"# `unbounded_shifts` + label: "uint_bit_width", + description: r##"# `uint_bit_width` + + + +The tracking issue for this feature is: [#142326] + +[#142326]: https://github.com/rust-lang/rust/issues/142326 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "uint_carryless_mul", + description: r##"# `uint_carryless_mul` + + + +The tracking issue for this feature is: [#152080] + +[#152080]: https://github.com/rust-lang/rust/issues/152080 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "uint_gather_scatter_bits", + description: r##"# `uint_gather_scatter_bits` + + -The tracking issue for this feature is: [#129375] +The tracking issue for this feature is: [#149069] -[#129375]: https://github.com/rust-lang/rust/issues/129375 +[#149069]: https://github.com/rust-lang/rust/issues/149069 ------------------------ "##, @@ -11849,6 +15616,8 @@ fn main() {} label: "unicode_internals", description: r##"# `unicode_internals` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -11861,6 +15630,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "unique_rc_arc", description: r##"# `unique_rc_arc` + + The tracking issue for this feature is: [#112566] [#112566]: https://github.com/rust-lang/rust/issues/112566 @@ -11875,6 +15646,8 @@ The tracking issue for this feature is: [#112566] label: "unix_file_vectored_at", description: r##"# `unix_file_vectored_at` + + The tracking issue for this feature is: [#89517] [#89517]: https://github.com/rust-lang/rust/issues/89517 @@ -11886,9 +15659,43 @@ The tracking issue for this feature is: [#89517] deny_since: None, }, Lint { + label: "unix_mkfifo", + description: r##"# `unix_mkfifo` + + + +The tracking issue for this feature is: [#139324] + +[#139324]: https://github.com/rust-lang/rust/issues/139324 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "unix_send_signal", + description: r##"# `unix_send_signal` + + + +The tracking issue for this feature is: [#141975] + +[#141975]: https://github.com/rust-lang/rust/issues/141975 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "unix_set_mark", description: r##"# `unix_set_mark` + + The tracking issue for this feature is: [#96467] [#96467]: https://github.com/rust-lang/rust/issues/96467 @@ -11903,6 +15710,8 @@ The tracking issue for this feature is: [#96467] label: "unix_socket_ancillary_data", description: r##"# `unix_socket_ancillary_data` + + The tracking issue for this feature is: [#76915] [#76915]: https://github.com/rust-lang/rust/issues/76915 @@ -11914,9 +15723,27 @@ The tracking issue for this feature is: [#76915] deny_since: None, }, Lint { + label: "unix_socket_exclbind", + description: r##"# `unix_socket_exclbind` + + + +The tracking issue for this feature is: [#123481] + +[#123481]: https://github.com/rust-lang/rust/issues/123481 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "unix_socket_peek", description: r##"# `unix_socket_peek` + + The tracking issue for this feature is: [#76923] [#76923]: https://github.com/rust-lang/rust/issues/76923 @@ -11931,7 +15758,11 @@ The tracking issue for this feature is: [#76923] label: "unqualified_local_imports", description: r##"# `unqualified_local_imports` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +Helps with formatting for `group_imports = "StdExternalCrate"`. + +The tracking issue for this feature is: [#138299] + +[#138299]: https://github.com/rust-lang/rust/issues/138299 ------------------------ "##, @@ -11940,12 +15771,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "unsafe_fields", - description: r##"# `unsafe_fields` + label: "unsafe_binders", + description: r##"# `unsafe_binders` -The tracking issue for this feature is: [#132922] +Allows using `unsafe<'a> &'a T` unsafe binder types. -[#132922]: https://github.com/rust-lang/rust/issues/132922 +The tracking issue for this feature is: [#130516] + +[#130516]: https://github.com/rust-lang/rust/issues/130516 ------------------------ "##, @@ -11954,10 +15787,14 @@ The tracking issue for this feature is: [#132922] deny_since: None, }, Lint { - label: "unsafe_pin_internals", - description: r##"# `unsafe_pin_internals` + label: "unsafe_cell_access", + description: r##"# `unsafe_cell_access` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + + +The tracking issue for this feature is: [#136327] + +[#136327]: https://github.com/rust-lang/rust/issues/136327 ------------------------ "##, @@ -11966,12 +15803,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "unsigned_is_multiple_of", - description: r##"# `unsigned_is_multiple_of` + label: "unsafe_fields", + description: r##"# `unsafe_fields` -The tracking issue for this feature is: [#128101] +Allows declaring fields `unsafe`. + +The tracking issue for this feature is: [#132922] -[#128101]: https://github.com/rust-lang/rust/issues/128101 +[#132922]: https://github.com/rust-lang/rust/issues/132922 ------------------------ "##, @@ -11980,12 +15819,14 @@ The tracking issue for this feature is: [#128101] deny_since: None, }, Lint { - label: "unsigned_nonzero_div_ceil", - description: r##"# `unsigned_nonzero_div_ceil` + label: "unsafe_pinned", + description: r##"# `unsafe_pinned` + -The tracking issue for this feature is: [#132968] -[#132968]: https://github.com/rust-lang/rust/issues/132968 +The tracking issue for this feature is: [#125735] + +[#125735]: https://github.com/rust-lang/rust/issues/125735 ------------------------ "##, @@ -11994,12 +15835,14 @@ The tracking issue for this feature is: [#132968] deny_since: None, }, Lint { - label: "unsigned_signed_diff", - description: r##"# `unsigned_signed_diff` + label: "unsafe_unpin", + description: r##"# `unsafe_unpin` + -The tracking issue for this feature is: [#126041] -[#126041]: https://github.com/rust-lang/rust/issues/126041 +The tracking issue for this feature is: [#125735] + +[#125735]: https://github.com/rust-lang/rust/issues/125735 ------------------------ "##, @@ -12011,6 +15854,8 @@ The tracking issue for this feature is: [#126041] label: "unsize", description: r##"# `unsize` + + The tracking issue for this feature is: [#18598] [#18598]: https://github.com/rust-lang/rust/issues/18598 @@ -12025,6 +15870,8 @@ The tracking issue for this feature is: [#18598] label: "unsized_const_params", description: r##"# `unsized_const_params` +Allows const generic parameters to be defined with types that are not `Sized`, e.g. `fn foo<const N: [u8]>() {`. + The tracking issue for this feature is: [#95174] [#95174]: https://github.com/rust-lang/rust/issues/95174 @@ -12039,6 +15886,8 @@ The tracking issue for this feature is: [#95174] label: "unsized_fn_params", description: r##"# `unsized_fn_params` +Allows unsized fn parameters. + The tracking issue for this feature is: [#48055] [#48055]: https://github.com/rust-lang/rust/issues/48055 @@ -12050,194 +15899,170 @@ The tracking issue for this feature is: [#48055] deny_since: None, }, Lint { - label: "unsized_locals", - description: r##"# `unsized_locals` + label: "unwrap_infallible", + description: r##"# `unwrap_infallible` -The tracking issue for this feature is: [#48055] -[#48055]: https://github.com/rust-lang/rust/issues/48055 + +The tracking issue for this feature is: [#61695] + +[#61695]: https://github.com/rust-lang/rust/issues/61695 ------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "update_panic_count", + description: r##"# `update_panic_count` -This implements [RFC1909]. When turned on, you can have unsized arguments and locals: +This feature is internal to the Rust compiler and is not intended for general use. -[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "used_with_arg", + description: r##"# `used_with_arg` -```rust -#![allow(incomplete_features)] -#![feature(unsized_locals, unsized_fn_params)] +Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute. -use std::any::Any; +The tracking issue for this feature is: [#93798] -fn main() { - let x: Box<dyn Any> = Box::new(42); - let x: dyn Any = *x; - // ^ unsized local variable - // ^^ unsized temporary - foo(x); -} +[#93798]: https://github.com/rust-lang/rust/issues/93798 -fn foo(_: dyn Any) {} -// ^^^^^^ unsized argument -``` +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "utf16_extra", + description: r##"# `utf16_extra` -The RFC still forbids the following unsized expressions: -```rust,compile_fail -#![feature(unsized_locals)] -use std::any::Any; +The tracking issue for this feature is: [#94919] -struct MyStruct<T: ?Sized> { - content: T, -} +[#94919]: https://github.com/rust-lang/rust/issues/94919 -struct MyTupleStruct<T: ?Sized>(T); +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "variant_count", + description: r##"# `variant_count` -fn answer() -> Box<dyn Any> { - Box::new(42) -} -fn main() { - // You CANNOT have unsized statics. - static X: dyn Any = *answer(); // ERROR - const Y: dyn Any = *answer(); // ERROR - - // You CANNOT have struct initialized unsized. - MyStruct { content: *answer() }; // ERROR - MyTupleStruct(*answer()); // ERROR - (42, *answer()); // ERROR - - // You CANNOT have unsized return types. - fn my_function() -> dyn Any { *answer() } // ERROR - - // You CAN have unsized local variables... - let mut x: dyn Any = *answer(); // OK - // ...but you CANNOT reassign to them. - x = *answer(); // ERROR - - // You CANNOT even initialize them separately. - let y: dyn Any; // OK - y = *answer(); // ERROR - - // Not mentioned in the RFC, but by-move captured variables are also Sized. - let x: dyn Any = *answer(); - (move || { // ERROR - let y = x; - })(); - - // You CAN create a closure with unsized arguments, - // but you CANNOT call it. - // This is an implementation detail and may be changed in the future. - let f = |x: dyn Any| {}; - f(*answer()); // ERROR -} -``` -## By-value trait objects +The tracking issue for this feature is: [#73662] -With this feature, you can have by-value `self` arguments without `Self: Sized` bounds. +[#73662]: https://github.com/rust-lang/rust/issues/73662 -```rust -#![feature(unsized_fn_params)] +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "vec_deque_extract_if", + description: r##"# `vec_deque_extract_if` -trait Foo { - fn foo(self) {} -} -impl<T: ?Sized> Foo for T {} -fn main() { - let slice: Box<[i32]> = Box::new([1, 2, 3]); - <[i32] as Foo>::foo(*slice); -} -``` +The tracking issue for this feature is: [#147750] -And `Foo` will also be object-safe. +[#147750]: https://github.com/rust-lang/rust/issues/147750 -```rust -#![feature(unsized_fn_params)] +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "vec_deque_iter_as_slices", + description: r##"# `vec_deque_iter_as_slices` -trait Foo { - fn foo(self) {} -} -impl<T: ?Sized> Foo for T {} -fn main () { - let slice: Box<dyn Foo> = Box::new([1, 2, 3]); - // doesn't compile yet - <dyn Foo as Foo>::foo(*slice); -} -``` +The tracking issue for this feature is: [#123947] -One of the objectives of this feature is to allow `Box<dyn FnOnce>`. +[#123947]: https://github.com/rust-lang/rust/issues/123947 -## Variable length arrays +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "vec_deque_truncate_front", + description: r##"# `vec_deque_truncate_front` -The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`. -```rust,ignore (not-yet-implemented) -#![feature(unsized_locals)] -fn mergesort<T: Ord>(a: &mut [T]) { - let mut tmp = [T; dyn a.len()]; - // ... -} +The tracking issue for this feature is: [#140667] -fn main() { - let mut a = [3, 1, 5, 6]; - mergesort(&mut a); - assert_eq!(a, [1, 3, 5, 6]); -} -``` +[#140667]: https://github.com/rust-lang/rust/issues/140667 -VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`. +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "vec_fallible_shrink", + description: r##"# `vec_fallible_shrink` -## Advisory on stack usage -It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are: -- When you need a by-value trait objects. -- When you really need a fast allocation of small temporary arrays. +The tracking issue for this feature is: [#152350] -Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code +[#152350]: https://github.com/rust-lang/rust/issues/152350 -```rust -#![feature(unsized_locals)] +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "vec_from_fn", + description: r##"# `vec_from_fn` -fn main() { - let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); - let _x = {{{{{{{{{{*x}}}}}}}}}}; -} -``` -and the code -```rust -#![feature(unsized_locals)] +The tracking issue for this feature is: [#149698] -fn main() { - for _ in 0..10 { - let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); - let _x = *x; - } -} -``` +[#149698]: https://github.com/rust-lang/rust/issues/149698 -will unnecessarily extend the stack frame. +------------------------ "##, default_severity: Severity::Allow, warn_since: None, deny_since: None, }, Lint { - label: "unwrap_infallible", - description: r##"# `unwrap_infallible` + label: "vec_into_chunks", + description: r##"# `vec_into_chunks` -The tracking issue for this feature is: [#61695] -[#61695]: https://github.com/rust-lang/rust/issues/61695 + +The tracking issue for this feature is: [#142137] + +[#142137]: https://github.com/rust-lang/rust/issues/142137 ------------------------ "##, @@ -12246,10 +16071,14 @@ The tracking issue for this feature is: [#61695] deny_since: None, }, Lint { - label: "update_panic_count", - description: r##"# `update_panic_count` + label: "vec_peek_mut", + description: r##"# `vec_peek_mut` + -This feature is internal to the Rust compiler and is not intended for general use. + +The tracking issue for this feature is: [#122742] + +[#122742]: https://github.com/rust-lang/rust/issues/122742 ------------------------ "##, @@ -12258,12 +16087,14 @@ This feature is internal to the Rust compiler and is not intended for general us deny_since: None, }, Lint { - label: "used_with_arg", - description: r##"# `used_with_arg` + label: "vec_push_within_capacity", + description: r##"# `vec_push_within_capacity` -The tracking issue for this feature is: [#93798] -[#93798]: https://github.com/rust-lang/rust/issues/93798 + +The tracking issue for this feature is: [#100486] + +[#100486]: https://github.com/rust-lang/rust/issues/100486 ------------------------ "##, @@ -12272,12 +16103,14 @@ The tracking issue for this feature is: [#93798] deny_since: None, }, Lint { - label: "utf16_extra", - description: r##"# `utf16_extra` + label: "vec_recycle", + description: r##"# `vec_recycle` -The tracking issue for this feature is: [#94919] -[#94919]: https://github.com/rust-lang/rust/issues/94919 + +The tracking issue for this feature is: [#148227] + +[#148227]: https://github.com/rust-lang/rust/issues/148227 ------------------------ "##, @@ -12286,12 +16119,14 @@ The tracking issue for this feature is: [#94919] deny_since: None, }, Lint { - label: "variant_count", - description: r##"# `variant_count` + label: "vec_split_at_spare", + description: r##"# `vec_split_at_spare` -The tracking issue for this feature is: [#73662] -[#73662]: https://github.com/rust-lang/rust/issues/73662 + +The tracking issue for this feature is: [#81944] + +[#81944]: https://github.com/rust-lang/rust/issues/81944 ------------------------ "##, @@ -12300,12 +16135,14 @@ The tracking issue for this feature is: [#73662] deny_since: None, }, Lint { - label: "vec_deque_iter_as_slices", - description: r##"# `vec_deque_iter_as_slices` + label: "vec_try_remove", + description: r##"# `vec_try_remove` -The tracking issue for this feature is: [#123947] -[#123947]: https://github.com/rust-lang/rust/issues/123947 + +The tracking issue for this feature is: [#146954] + +[#146954]: https://github.com/rust-lang/rust/issues/146954 ------------------------ "##, @@ -12314,12 +16151,14 @@ The tracking issue for this feature is: [#123947] deny_since: None, }, Lint { - label: "vec_into_raw_parts", - description: r##"# `vec_into_raw_parts` + label: "waker_fn", + description: r##"# `waker_fn` + + -The tracking issue for this feature is: [#65816] +The tracking issue for this feature is: [#149580] -[#65816]: https://github.com/rust-lang/rust/issues/65816 +[#149580]: https://github.com/rust-lang/rust/issues/149580 ------------------------ "##, @@ -12328,12 +16167,14 @@ The tracking issue for this feature is: [#65816] deny_since: None, }, Lint { - label: "vec_pop_if", - description: r##"# `vec_pop_if` + label: "waker_from_fn_ptr", + description: r##"# `waker_from_fn_ptr` -The tracking issue for this feature is: [#122741] -[#122741]: https://github.com/rust-lang/rust/issues/122741 + +The tracking issue for this feature is: [#148457] + +[#148457]: https://github.com/rust-lang/rust/issues/148457 ------------------------ "##, @@ -12342,12 +16183,14 @@ The tracking issue for this feature is: [#122741] deny_since: None, }, Lint { - label: "vec_push_within_capacity", - description: r##"# `vec_push_within_capacity` + label: "wasi_ext", + description: r##"# `wasi_ext` -The tracking issue for this feature is: [#100486] -[#100486]: https://github.com/rust-lang/rust/issues/100486 + +The tracking issue for this feature is: [#71213] + +[#71213]: https://github.com/rust-lang/rust/issues/71213 ------------------------ "##, @@ -12356,12 +16199,14 @@ The tracking issue for this feature is: [#100486] deny_since: None, }, Lint { - label: "vec_split_at_spare", - description: r##"# `vec_split_at_spare` + label: "wasm_target_feature", + description: r##"# `wasm_target_feature` -The tracking issue for this feature is: [#81944] +Target features on wasm. -[#81944]: https://github.com/rust-lang/rust/issues/81944 +The tracking issue for this feature is: [#150260] + +[#150260]: https://github.com/rust-lang/rust/issues/150260 ------------------------ "##, @@ -12370,12 +16215,14 @@ The tracking issue for this feature is: [#81944] deny_since: None, }, Lint { - label: "wasi_ext", - description: r##"# `wasi_ext` + label: "where_clause_attrs", + description: r##"# `where_clause_attrs` -The tracking issue for this feature is: [#71213] +Allows use of attributes in `where` clauses. -[#71213]: https://github.com/rust-lang/rust/issues/71213 +The tracking issue for this feature is: [#115590] + +[#115590]: https://github.com/rust-lang/rust/issues/115590 ------------------------ "##, @@ -12384,12 +16231,14 @@ The tracking issue for this feature is: [#71213] deny_since: None, }, Lint { - label: "wasm_target_feature", - description: r##"# `wasm_target_feature` + label: "widening_mul", + description: r##"# `widening_mul` + + -The tracking issue for this feature is: [#44839] +The tracking issue for this feature is: [#152016] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +[#152016]: https://github.com/rust-lang/rust/issues/152016 ------------------------ "##, @@ -12401,6 +16250,8 @@ The tracking issue for this feature is: [#44839] label: "windows_by_handle", description: r##"# `windows_by_handle` + + The tracking issue for this feature is: [#63010] [#63010]: https://github.com/rust-lang/rust/issues/63010 @@ -12427,6 +16278,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "windows_change_time", description: r##"# `windows_change_time` + + The tracking issue for this feature is: [#121478] [#121478]: https://github.com/rust-lang/rust/issues/121478 @@ -12438,6 +16291,22 @@ The tracking issue for this feature is: [#121478] deny_since: None, }, Lint { + label: "windows_freeze_file_times", + description: r##"# `windows_freeze_file_times` + + + +The tracking issue for this feature is: [#149715] + +[#149715]: https://github.com/rust-lang/rust/issues/149715 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "windows_handle", description: r##"# `windows_handle` @@ -12465,6 +16334,8 @@ This feature is internal to the Rust compiler and is not intended for general us label: "windows_process_exit_code_from", description: r##"# `windows_process_exit_code_from` + + The tracking issue for this feature is: [#111688] [#111688]: https://github.com/rust-lang/rust/issues/111688 @@ -12479,6 +16350,8 @@ The tracking issue for this feature is: [#111688] label: "windows_process_extensions_async_pipes", description: r##"# `windows_process_extensions_async_pipes` + + The tracking issue for this feature is: [#98289] [#98289]: https://github.com/rust-lang/rust/issues/98289 @@ -12493,6 +16366,8 @@ The tracking issue for this feature is: [#98289] label: "windows_process_extensions_force_quotes", description: r##"# `windows_process_extensions_force_quotes` + + The tracking issue for this feature is: [#82227] [#82227]: https://github.com/rust-lang/rust/issues/82227 @@ -12504,9 +16379,27 @@ The tracking issue for this feature is: [#82227] deny_since: None, }, Lint { + label: "windows_process_extensions_inherit_handles", + description: r##"# `windows_process_extensions_inherit_handles` + + + +The tracking issue for this feature is: [#146407] + +[#146407]: https://github.com/rust-lang/rust/issues/146407 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "windows_process_extensions_main_thread_handle", description: r##"# `windows_process_extensions_main_thread_handle` + + The tracking issue for this feature is: [#96723] [#96723]: https://github.com/rust-lang/rust/issues/96723 @@ -12521,6 +16414,8 @@ The tracking issue for this feature is: [#96723] label: "windows_process_extensions_raw_attribute", description: r##"# `windows_process_extensions_raw_attribute` + + The tracking issue for this feature is: [#114854] [#114854]: https://github.com/rust-lang/rust/issues/114854 @@ -12535,6 +16430,8 @@ The tracking issue for this feature is: [#114854] label: "windows_process_extensions_show_window", description: r##"# `windows_process_extensions_show_window` + + The tracking issue for this feature is: [#127544] [#127544]: https://github.com/rust-lang/rust/issues/127544 @@ -12546,6 +16443,22 @@ The tracking issue for this feature is: [#127544] deny_since: None, }, Lint { + label: "windows_process_extensions_startupinfo", + description: r##"# `windows_process_extensions_startupinfo` + + + +The tracking issue for this feature is: [#141010] + +[#141010]: https://github.com/rust-lang/rust/issues/141010 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "windows_stdio", description: r##"# `windows_stdio` @@ -12558,9 +16471,27 @@ This feature is internal to the Rust compiler and is not intended for general us deny_since: None, }, Lint { + label: "windows_unix_domain_sockets", + description: r##"# `windows_unix_domain_sockets` + + + +The tracking issue for this feature is: [#150487] + +[#150487]: https://github.com/rust-lang/rust/issues/150487 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "with_negative_coherence", description: r##"# `with_negative_coherence` +Use for stable + negative coherence and strict coherence depending on trait's rustc_strict_coherence value. + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -12573,6 +16504,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp label: "wrapping_int_impl", description: r##"# `wrapping_int_impl` + + The tracking issue for this feature is: [#32463] [#32463]: https://github.com/rust-lang/rust/issues/32463 @@ -12587,6 +16520,8 @@ The tracking issue for this feature is: [#32463] label: "wrapping_next_power_of_two", description: r##"# `wrapping_next_power_of_two` + + The tracking issue for this feature is: [#32463] [#32463]: https://github.com/rust-lang/rust/issues/32463 @@ -12601,6 +16536,8 @@ The tracking issue for this feature is: [#32463] label: "write_all_vectored", description: r##"# `write_all_vectored` + + The tracking issue for this feature is: [#70436] [#70436]: https://github.com/rust-lang/rust/issues/70436 @@ -12612,9 +16549,25 @@ The tracking issue for this feature is: [#70436] deny_since: None, }, Lint { + label: "wtf8_internals", + description: r##"# `wtf8_internals` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "x86_amx_intrinsics", description: r##"# `x86_amx_intrinsics` +Allows use of x86 `AMX` target-feature attributes and intrinsics + The tracking issue for this feature is: [#126622] [#126622]: https://github.com/rust-lang/rust/issues/126622 @@ -12626,9 +16579,27 @@ The tracking issue for this feature is: [#126622] deny_since: None, }, Lint { + label: "x87_target_feature", + description: r##"# `x87_target_feature` + +The x87 target feature on x86. + +The tracking issue for this feature is: [#150261] + +[#150261]: https://github.com/rust-lang/rust/issues/150261 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "xop_target_feature", description: r##"# `xop_target_feature` +Allows use of the `xop` target-feature + The tracking issue for this feature is: [#127208] [#127208]: https://github.com/rust-lang/rust/issues/127208 @@ -12643,6 +16614,8 @@ The tracking issue for this feature is: [#127208] label: "yeet_desugar_details", description: r##"# `yeet_desugar_details` + + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ @@ -12684,6 +16657,22 @@ assert_eq!(bar(), None); warn_since: None, deny_since: None, }, + Lint { + label: "yield_expr", + description: r##"# `yield_expr` + + + +The tracking issue for this feature is: [#43122] + +[#43122]: https://github.com/rust-lang/rust/issues/43122 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, ]; pub const CLIPPY_LINTS: &[Lint] = &[ @@ -14967,7 +18956,7 @@ cannot be represented as the underlying type without loss."##, }, Lint { label: "clippy::manual_bits", - description: r##"Checks for usage of `size_of::<T>() * 8` when + description: r##"Checks for usage of `std::mem::size_of::<T>() * 8` when `T::BITS` is available."##, default_severity: Severity::Allow, warn_since: None, @@ -17309,7 +21298,7 @@ count of elements of type `T`"##, }, Lint { label: "clippy::size_of_ref", - description: r##"Checks for calls to `size_of_val()` where the argument is + description: r##"Checks for calls to `std::mem::size_of_val()` where the argument is a reference to a reference."##, default_severity: Severity::Allow, warn_since: None, diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 1c48527027..9018552afb 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -8,8 +8,10 @@ use hir::{ SemanticsScope, Trait, Type, }; use itertools::Itertools; +use parser::SyntaxKind; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{SmallVec, smallvec}; +use stdx::never; use syntax::{ AstNode, SyntaxNode, ast::{self, HasName, make}, @@ -61,6 +63,105 @@ pub struct TraitImportCandidate<'db> { pub assoc_item_name: NameToImport, } +#[derive(Debug)] +struct PathDefinitionKinds { + modules: bool, + bang_macros: bool, + // FIXME: Distinguish between attr and derive macros. + attr_macros: bool, + value_namespace: bool, + type_namespace: bool, + /// Unions, record structs and record enum variants. Note that unions and structs + /// can also be enabled by `type_namespace` (either works). + records: bool, + /// Tuple structs and tuple enum variants. Both are also controlled by `value_namespace` + /// (either works). Structs are also covered by `type_namespace`. + tuple_structs: bool, + /// Structs, enum variants and consts. + structs_and_consts: bool, +} + +impl PathDefinitionKinds { + const ALL_DISABLED: Self = Self { + modules: false, + bang_macros: false, + attr_macros: false, + value_namespace: false, + type_namespace: false, + records: false, + tuple_structs: false, + structs_and_consts: false, + }; + const ALL_ENABLED: Self = Self { + modules: true, + bang_macros: true, + attr_macros: true, + value_namespace: true, + type_namespace: true, + records: true, + tuple_structs: true, + structs_and_consts: true, + }; + // While a path pattern only allows unit structs/enum variants, parentheses/braces may be written later. + const PATH_PAT_KINDS: PathDefinitionKinds = + Self { structs_and_consts: true, bang_macros: true, ..Self::ALL_DISABLED }; + + fn deduce_from_path(path: &ast::Path, exact: bool) -> Self { + let Some(parent) = path.syntax().parent() else { + return Self::ALL_ENABLED; + }; + let mut result = match parent.kind() { + // When there are following segments, it can be a type (with a method) or a module. + // Technically, a type can only have up to 2 segments following (an associated type + // then a method), but most paths are shorter than 3 segments anyway, and we'll also + // validate that the following segment resolve. + SyntaxKind::PATH => Self { modules: true, type_namespace: true, ..Self::ALL_DISABLED }, + SyntaxKind::MACRO_CALL => Self { bang_macros: true, ..Self::ALL_DISABLED }, + SyntaxKind::PATH_META | SyntaxKind::KEY_VALUE_META | SyntaxKind::TOKEN_TREE_META => { + Self { attr_macros: true, ..Self::ALL_DISABLED } + } + SyntaxKind::USE_TREE => { + if ast::UseTree::cast(parent).unwrap().use_tree_list().is_some() { + Self { modules: true, ..Self::ALL_DISABLED } + } else { + Self::ALL_ENABLED + } + } + SyntaxKind::VISIBILITY => Self { modules: true, ..Self::ALL_DISABLED }, + SyntaxKind::ASM_SYM => Self { value_namespace: true, ..Self::ALL_DISABLED }, + // `bang_macros = true` because you can still type the `!`. + // `type_namespace = true` because you can type `::method()`. + SyntaxKind::PATH_EXPR => Self { + value_namespace: true, + bang_macros: true, + type_namespace: true, + ..Self::ALL_DISABLED + }, + SyntaxKind::PATH_PAT => Self::PATH_PAT_KINDS, + SyntaxKind::TUPLE_STRUCT_PAT => { + Self { tuple_structs: true, bang_macros: true, ..Self::ALL_DISABLED } + } + SyntaxKind::RECORD_EXPR | SyntaxKind::RECORD_PAT => { + Self { records: true, bang_macros: true, ..Self::ALL_DISABLED } + } + SyntaxKind::PATH_TYPE => { + Self { type_namespace: true, bang_macros: true, ..Self::ALL_DISABLED } + } + SyntaxKind::ERROR => Self::ALL_ENABLED, + _ => { + never!("this match should cover all possible parents of paths\nparent={parent:#?}"); + Self::ALL_ENABLED + } + }; + if !exact { + // When the path is not required to be exact, there could be additional segments to be filled. + result.modules = true; + result.type_namespace = true; + } + result + } +} + /// Path import for a given name, qualified or not. #[derive(Debug)] pub struct PathImportCandidate { @@ -70,6 +171,8 @@ pub struct PathImportCandidate { pub name: NameToImport, /// Potentially more segments that should resolve in the candidate. pub after: Vec<Name>, + /// The kind of definitions that we can include. + definition_kinds: PathDefinitionKinds, } /// A name that will be used during item lookups. @@ -168,13 +271,14 @@ impl<'db> ImportAssets<'db> { pub fn for_fuzzy_path( module_with_candidate: Module, + path: Option<&ast::Path>, qualifier: Option<ast::Path>, fuzzy_name: String, sema: &Semantics<'db, RootDatabase>, candidate_node: SyntaxNode, ) -> Option<Self> { Some(Self { - import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?, + import_candidate: ImportCandidate::for_fuzzy_path(path, qualifier, fuzzy_name, sema)?, module_with_candidate, candidate_node, }) @@ -394,6 +498,9 @@ fn path_applicable_imports( // see also an ignored test under FIXME comment in the qualify_path.rs module AssocSearchMode::Exclude, ) + .filter(|(item, _)| { + filter_by_definition_kind(db, *item, &path_candidate.definition_kinds) + }) .filter_map(|(item, do_not_complete)| { if !scope_filter(item) { return None; @@ -442,6 +549,46 @@ fn path_applicable_imports( result } +fn filter_by_definition_kind( + db: &RootDatabase, + item: ItemInNs, + allowed: &PathDefinitionKinds, +) -> bool { + let item = item.into_module_def(); + let struct_per_kind = |struct_kind| { + allowed.structs_and_consts + || match struct_kind { + hir::StructKind::Record => allowed.records, + hir::StructKind::Tuple => allowed.value_namespace || allowed.tuple_structs, + hir::StructKind::Unit => allowed.value_namespace, + } + }; + match item { + ModuleDef::Module(_) => allowed.modules, + ModuleDef::Function(_) => allowed.value_namespace, + ModuleDef::Adt(hir::Adt::Struct(item)) => { + allowed.type_namespace || struct_per_kind(item.kind(db)) + } + ModuleDef::Adt(hir::Adt::Enum(_)) => allowed.type_namespace, + ModuleDef::Adt(hir::Adt::Union(_)) => { + allowed.type_namespace || allowed.records || allowed.structs_and_consts + } + ModuleDef::EnumVariant(item) => struct_per_kind(item.kind(db)), + ModuleDef::Const(_) => allowed.value_namespace || allowed.structs_and_consts, + ModuleDef::Static(_) => allowed.value_namespace, + ModuleDef::Trait(_) => allowed.type_namespace, + ModuleDef::TypeAlias(_) => allowed.type_namespace, + ModuleDef::BuiltinType(_) => allowed.type_namespace, + ModuleDef::Macro(item) => { + if item.is_fn_like(db) { + allowed.bang_macros + } else { + allowed.attr_macros + } + } + } +} + fn filter_candidates_by_after_path( db: &RootDatabase, scope: &SemanticsScope<'_>, @@ -835,6 +982,7 @@ impl<'db> ImportCandidate<'db> { .collect::<Option<_>>()?; path_import_candidate( sema, + Some(path), path.qualifier(), NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()), after, @@ -853,25 +1001,31 @@ impl<'db> ImportCandidate<'db> { qualifier: vec![], name: NameToImport::exact_case_sensitive(name.to_string()), after: vec![], + definition_kinds: PathDefinitionKinds::PATH_PAT_KINDS, })) } fn for_fuzzy_path( + path: Option<&ast::Path>, qualifier: Option<ast::Path>, fuzzy_name: String, sema: &Semantics<'db, RootDatabase>, ) -> Option<Self> { // Assume a fuzzy match does not want the segments after. Because... I guess why not? - path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name), Vec::new()) + path_import_candidate(sema, path, qualifier, NameToImport::fuzzy(fuzzy_name), Vec::new()) } } fn path_import_candidate<'db>( sema: &Semantics<'db, RootDatabase>, + path: Option<&ast::Path>, qualifier: Option<ast::Path>, name: NameToImport, after: Vec<Name>, ) -> Option<ImportCandidate<'db>> { + let definition_kinds = path.map_or(PathDefinitionKinds::ALL_ENABLED, |path| { + PathDefinitionKinds::deduce_from_path(path, matches!(name, NameToImport::Exact(..))) + }); Some(match qualifier { Some(qualifier) => match sema.resolve_path(&qualifier) { Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => { @@ -880,7 +1034,12 @@ fn path_import_candidate<'db>( .segments() .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text()))) .collect::<Option<Vec<_>>>()?; - ImportCandidate::Path(PathImportCandidate { qualifier, name, after }) + ImportCandidate::Path(PathImportCandidate { + qualifier, + name, + after, + definition_kinds, + }) } else { return None; } @@ -904,7 +1063,12 @@ fn path_import_candidate<'db>( } Some(_) => return None, }, - None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name, after }), + None => ImportCandidate::Path(PathImportCandidate { + qualifier: vec![], + name, + after, + definition_kinds, + }), }) } diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index da8525d1fb..9318c3e132 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs @@ -101,14 +101,12 @@ impl ImportScope { { block = b.stmt_list(); } - if has_attrs - .attrs() - .any(|attr| attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")) + if has_attrs.attrs().any(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))) { if let Some(b) = block.clone() { - let current_cfgs = has_attrs.attrs().filter(|attr| { - attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg") - }); + let current_cfgs = has_attrs + .attrs() + .filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); let total_cfgs: Vec<_> = required_cfgs.iter().cloned().chain(current_cfgs).collect(); @@ -118,7 +116,7 @@ impl ImportScope { if let Some(parent) = parent { can_merge = parent.children().filter_map(ast::Use::cast).any(|u| { let u_attrs = u.attrs().filter(|attr| { - attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg") + matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))) }); crate::imports::merge_imports::eq_attrs( u_attrs, @@ -134,9 +132,11 @@ impl ImportScope { }); } } - required_cfgs.extend(has_attrs.attrs().filter(|attr| { - attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg") - })); + required_cfgs.extend( + has_attrs + .attrs() + .filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))), + ); } } } @@ -305,10 +305,8 @@ fn insert_use_with_alias_option_with_editor( if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { use_tree.wrap_in_tree_list(); } - let use_item = make::use_(None, None, use_tree).clone_for_update(); - for attr in - scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update()) - { + let use_item = make::use_(None, None, use_tree); + for attr in scope.required_cfgs.iter().map(|attr| attr.syntax().clone()) { syntax_editor.insert(Position::first_child_of(use_item.syntax()), attr); } @@ -711,7 +709,11 @@ fn insert_use_with_editor_( Some(b) => { cov_mark::hit!(insert_empty_module); syntax_editor.insert(Position::after(&b), syntax_factory.whitespace("\n")); - syntax_editor.insert(Position::after(&b), use_item.syntax()); + syntax_editor.insert_with_whitespace( + Position::after(&b), + use_item.syntax(), + syntax_factory, + ); } None => { cov_mark::hit!(insert_empty_file); diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs index 3301719f5c..76645464dd 100644 --- a/crates/ide-db/src/imports/merge_imports.rs +++ b/crates/ide-db/src/imports/merge_imports.rs @@ -256,16 +256,6 @@ pub fn try_normalize_import(use_item: &ast::Use, style: NormalizationStyle) -> O Some(use_item) } -/// Normalizes a use tree (see [`try_normalize_import`] doc). -pub fn try_normalize_use_tree( - use_tree: &ast::UseTree, - style: NormalizationStyle, -) -> Option<ast::UseTree> { - let use_tree = use_tree.clone_subtree().clone_for_update(); - try_normalize_use_tree_mut(&use_tree, style)?; - Some(use_tree) -} - pub fn try_normalize_use_tree_mut( use_tree: &ast::UseTree, style: NormalizationStyle, diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index cde0705d8a..8d16826e19 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -60,8 +60,8 @@ use salsa::Durability; use std::{fmt, mem::ManuallyDrop}; use base_db::{ - CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, Nonce, RootQueryDb, - SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, query_group, + CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, Nonce, SourceDatabase, + SourceRoot, SourceRootId, SourceRootInput, query_group, set_all_crates_with_durability, }; use hir::{ FilePositionWrapper, FileRangeWrapper, @@ -197,7 +197,7 @@ impl RootDatabase { nonce: Nonce::new(), }; // This needs to be here otherwise `CrateGraphBuilder` will panic. - db.set_all_crates(Arc::new(Box::new([]))); + set_all_crates_with_durability(&mut db, std::iter::empty(), Durability::HIGH); CrateGraphBuilder::default().set_in_db(&mut db); db.set_proc_macros_with_durability(Default::default(), Durability::MEDIUM); _ = base_db::LibraryRoots::builder(Default::default()) @@ -253,7 +253,7 @@ impl RootDatabase { } #[query_group::query_group] -pub trait LineIndexDatabase: base_db::RootQueryDb { +pub trait LineIndexDatabase: base_db::SourceDatabase { #[salsa::invoke_interned(line_index)] fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; } diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 508f841340..407276a2de 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -197,7 +197,7 @@ impl<'a> PathTransform<'a> { && let Some(default) = &default.display_source_code(db, source_module.into(), false).ok() { - type_substs.insert(k, make::ty(default).clone_for_update()); + type_substs.insert(k, make::ty(default)); defaulted_params.push(Either::Left(k)); } } @@ -222,7 +222,7 @@ impl<'a> PathTransform<'a> { k.default(db, target_module.krate(db).to_display_target(db)) && let Some(default) = default.expr() { - const_substs.insert(k, default.syntax().clone_for_update()); + const_substs.insert(k, default.syntax().clone()); defaulted_params.push(Either::Right(k)); } } @@ -278,12 +278,10 @@ 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 item = self.transform_path(item).clone_subtree(); - let mut editor = SyntaxEditor::new(item.clone()); + let (mut editor, item) = SyntaxEditor::new(self.transform_path(item)); preorder_rev(&item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) { - editor - .replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax()); + editor.replace(lifetime.syntax(), subst.clone().syntax()); } }); @@ -331,18 +329,14 @@ impl Ctx<'_> { result } - let root_path = path.clone_subtree(); - + let (mut editor, root_path) = SyntaxEditor::new(path.clone()); let result = find_child_paths_and_ident_pats(&root_path); - let mut editor = SyntaxEditor::new(root_path.clone()); for sub_path in result { let new = self.transform_path(sub_path.syntax()); editor.replace(sub_path.syntax(), new); } - - let update_sub_item = editor.finish().new_root().clone().clone_subtree(); + let (mut editor, update_sub_item) = SyntaxEditor::new(editor.finish().new_root().clone()); let item = find_child_paths_and_ident_pats(&update_sub_item); - let mut editor = SyntaxEditor::new(update_sub_item); for sub_path in item { self.transform_path_or_ident_pat(&mut editor, &sub_path); } @@ -411,33 +405,27 @@ impl Ctx<'_> { let segment = make::path_segment_ty(subst.clone(), trait_ref); let qualified = make::path_from_segments(std::iter::once(segment), false); - editor.replace(path.syntax(), qualified.clone_for_update().syntax()); + editor.replace(path.syntax(), qualified.clone().syntax()); } else if let Some(path_ty) = ast::PathType::cast(parent) { let old = path_ty.syntax(); if old.parent().is_some() { - editor.replace(old, subst.clone_subtree().clone_for_update().syntax()); + editor.replace(old, subst.clone().syntax()); } else { - // Some `path_ty` has no parent, especially ones made for default value - // of type parameters. - // In this case, `ted` cannot replace `path_ty` with `subst` directly. - // So, just replace its children as long as the `subst` is the same type. - let new = subst.clone_subtree().clone_for_update(); - if !matches!(new, ast::Type::PathType(..)) { - return None; - } let start = path_ty.syntax().first_child().map(NodeOrToken::Node)?; let end = path_ty.syntax().last_child().map(NodeOrToken::Node)?; editor.replace_all( start..=end, - new.syntax().children().map(NodeOrToken::Node).collect::<Vec<_>>(), + subst + .clone() + .syntax() + .children() + .map(NodeOrToken::Node) + .collect::<Vec<_>>(), ); } } else { - editor.replace( - path.syntax(), - subst.clone_subtree().clone_for_update().syntax(), - ); + editor.replace(path.syntax(), subst.clone().syntax()); } } } @@ -459,18 +447,17 @@ impl Ctx<'_> { allow_unstable: true, }; let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; - let res = mod_path_to_ast(&found_path, self.target_edition).clone_for_update(); - let mut res_editor = SyntaxEditor::new(res.syntax().clone_subtree()); + let res = mod_path_to_ast(&found_path, self.target_edition); + let (mut res_editor, res) = SyntaxEditor::with_ast_node(&res); if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) && let Some(segment) = res.segment() { if let Some(old) = segment.generic_arg_list() { - res_editor - .replace(old.syntax(), args.clone_subtree().syntax().clone_for_update()) + res_editor.replace(old.syntax(), args.syntax().clone()) } else { res_editor.insert( syntax_editor::Position::last_child_of(segment.syntax()), - args.clone_subtree().syntax().clone_for_update(), + args.syntax().clone(), ); } } @@ -479,7 +466,7 @@ impl Ctx<'_> { } hir::PathResolution::ConstParam(cp) => { if let Some(subst) = self.const_substs.get(&cp) { - editor.replace(path.syntax(), subst.clone_subtree().clone_for_update()); + editor.replace(path.syntax(), subst.clone()); } } hir::PathResolution::SelfType(imp) => { @@ -496,7 +483,7 @@ impl Ctx<'_> { true, ) .ok()?; - let ast_ty = make::ty(ty_str).clone_for_update(); + let ast_ty = make::ty(ty_str); if let Some(adt) = ty.as_adt() && let ast::Type::PathType(path_ty) = &ast_ty @@ -516,8 +503,10 @@ impl Ctx<'_> { if let Some(qual) = mod_path_to_ast(&found_path, self.target_edition).qualifier() { - let res = make::path_concat(qual, path_ty.path()?).clone_for_update(); - editor.replace(path.syntax(), res.syntax()); + editor.replace( + path.syntax(), + make::path_concat(qual, path_ty.path()?).syntax(), + ); return Some(()); } } @@ -593,8 +582,10 @@ impl Ctx<'_> { allow_unstable: true, }; let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; - let res = mod_path_to_ast(&found_path, self.target_edition).clone_for_update(); - editor.replace(ident_pat.syntax(), res.syntax()); + editor.replace( + ident_pat.syntax(), + mod_path_to_ast(&found_path, self.target_edition).syntax(), + ); Some(()) } _ => None, diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs index d264428212..12a48d65ac 100644 --- a/crates/ide-db/src/prime_caches.rs +++ b/crates/ide-db/src/prime_caches.rs @@ -4,15 +4,12 @@ //! various caches, it's not really advanced at the moment. use std::panic::AssertUnwindSafe; +use base_db::all_crates; use hir::{Symbol, import_map::ImportMap}; use rustc_hash::FxHashMap; use salsa::{Cancelled, Database}; -use crate::{ - FxIndexMap, RootDatabase, - base_db::{Crate, RootQueryDb}, - symbol_index::SymbolIndex, -}; +use crate::{FxIndexMap, RootDatabase, base_db::Crate, symbol_index::SymbolIndex}; /// We're indexing many crates. #[derive(Debug)] @@ -56,7 +53,7 @@ pub fn parallel_prime_caches( // to compute the symbols/import map of an already computed def map in that time. let (reverse_deps, mut to_be_done_deps) = { - let all_crates = db.all_crates(); + let all_crates = all_crates(db); let to_be_done_deps = all_crates .iter() .map(|&krate| (krate, krate.data(db).dependencies.len() as u32)) @@ -200,7 +197,7 @@ pub fn parallel_prime_caches( ) }; - let crate_def_maps_total = db.all_crates().len(); + let crate_def_maps_total = all_crates(db).len(); let mut crate_def_maps_done = 0; let (mut crate_import_maps_total, mut crate_import_maps_done) = (0usize, 0usize); let (mut module_symbols_total, mut module_symbols_done) = (0usize, 0usize); diff --git a/crates/ide-db/src/ra_fixture.rs b/crates/ide-db/src/ra_fixture.rs index c9a670b2d1..2f4d319ec8 100644 --- a/crates/ide-db/src/ra_fixture.rs +++ b/crates/ide-db/src/ra_fixture.rs @@ -52,6 +52,18 @@ impl RootDatabase { } } +#[derive(Debug, Clone, Copy)] +pub struct RaFixtureConfig<'a> { + pub minicore: MiniCore<'a>, + pub disable_ra_fixture: bool, +} + +impl<'a> RaFixtureConfig<'a> { + pub const fn default() -> Self { + Self { minicore: MiniCore::default(), disable_ra_fixture: false } + } +} + pub struct RaFixtureAnalysis { pub db: RootDatabase, tmp_file_ids: Vec<(FileId, usize)>, @@ -69,9 +81,14 @@ impl RaFixtureAnalysis { sema: &Semantics<'_, RootDatabase>, literal: ast::String, expanded: &ast::String, - minicore: MiniCore<'_>, + config: &RaFixtureConfig<'_>, on_cursor: &mut dyn FnMut(TextRange), ) -> Option<RaFixtureAnalysis> { + if config.disable_ra_fixture { + return None; + } + let minicore = config.minicore; + if !literal.is_raw() { return None; } diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 25acb47f7b..69459a4b72 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -7,7 +7,7 @@ use std::mem; use std::{cell::LazyCell, cmp::Reverse}; -use base_db::{RootQueryDb, SourceDatabase}; +use base_db::{SourceDatabase, all_crates}; use either::Either; use hir::{ Adt, AsAssocItem, DefWithBody, EditionedFileId, ExpressionStoreOwner, FileRange, @@ -161,7 +161,7 @@ impl SearchScope { fn crate_graph(db: &RootDatabase) -> SearchScope { let mut entries = FxHashMap::default(); - let all_crates = db.all_crates(); + let all_crates = all_crates(db); for &krate in all_crates.iter() { let crate_data = krate.data(db); let source_root = db.file_source_root(crate_data.root_file_id).source_root_id(db); diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs index 57072bb5ba..4a83f707fc 100644 --- a/crates/ide-db/src/source_change.rs +++ b/crates/ide-db/src/source_change.rs @@ -282,7 +282,7 @@ impl SourceChangeBuilder { } pub fn make_editor(&self, node: &SyntaxNode) -> SyntaxEditor { - SyntaxEditor::new(node.ancestors().last().unwrap_or_else(|| node.clone())) + SyntaxEditor::new(node.ancestors().last().unwrap_or_else(|| node.clone())).0 } pub fn add_file_edits(&mut self, file_id: impl Into<FileId>, edit: SyntaxEditor) { diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index 183f6b6495..2ad3a51c3d 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -27,7 +27,10 @@ use std::{ ops::ControlFlow, }; -use base_db::{CrateOrigin, LangCrateOrigin, LibraryRoots, LocalRoots, RootQueryDb, SourceRootId}; +use base_db::{ + CrateOrigin, InternedSourceRootId, LangCrateOrigin, LibraryRoots, LocalRoots, SourceRootId, + source_root_crates, +}; use fst::{Automaton, Streamer, raw::IndexedValue}; use hir::{ Crate, Module, @@ -255,7 +258,7 @@ pub fn world_symbols(db: &RootDatabase, mut query: Query) -> Vec<FileSymbol<'_>> let mut crates = Vec::new(); for &root in LocalRoots::get(db).roots(db).iter() { - crates.extend(db.source_root_crates(root).iter().copied()) + crates.extend(source_root_crates(db, root).iter().copied()) } crates .par_iter() @@ -322,7 +325,7 @@ fn resolve_path_to_modules( // If not anchored to crate, also search for modules matching first segment in local crates if !anchor_to_crate { for &root in LocalRoots::get(db).roots(db).iter() { - for &krate in db.source_root_crates(root).iter() { + for &krate in source_root_crates(db, root).iter() { let root_module = Crate::from(krate).root_module(db); for child in root_module.children(db) { if let Some(name) = child.name(db) @@ -369,11 +372,6 @@ impl<'db> SymbolIndex<'db> { db: &'db dyn HirDatabase, source_root_id: SourceRootId, ) -> &'db SymbolIndex<'db> { - // FIXME: - #[salsa::interned] - struct InternedSourceRootId { - id: SourceRootId, - } #[salsa::tracked(returns(ref))] fn library_symbols<'db>( db: &'db dyn HirDatabase, @@ -385,7 +383,7 @@ impl<'db> SymbolIndex<'db> { hir::attach_db(db, || { let mut symbol_collector = SymbolCollector::new(db, true); - db.source_root_crates(source_root_id.id(db)) + source_root_crates(db, source_root_id.id(db)) .iter() .flat_map(|&krate| Crate::from(krate).modules(db)) // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing, diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt index fc98ebb069..17d002e8bf 100644 --- a/crates/ide-db/src/test_data/test_doc_alias.txt +++ b/crates/ide-db/src/test_data/test_doc_alias.txt @@ -2,7 +2,7 @@ ( Module { id: ModuleIdLt { - [salsa id]: Id(3400), + [salsa id]: Id(3000), }, }, [ @@ -12,7 +12,7 @@ Struct( Struct { id: StructId( - 3c01, + 3801, ), }, ), @@ -20,7 +20,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -49,7 +49,7 @@ Struct( Struct { id: StructId( - 3c00, + 3800, ), }, ), @@ -57,7 +57,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -86,7 +86,7 @@ Struct( Struct { id: StructId( - 3c00, + 3800, ), }, ), @@ -94,7 +94,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -123,7 +123,7 @@ Struct( Struct { id: StructId( - 3c00, + 3800, ), }, ), @@ -131,7 +131,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -160,7 +160,7 @@ Struct( Struct { id: StructId( - 3c00, + 3800, ), }, ), @@ -168,7 +168,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -197,7 +197,7 @@ Struct( Struct { id: StructId( - 3c01, + 3801, ), }, ), @@ -205,7 +205,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -234,7 +234,7 @@ Struct( Struct { id: StructId( - 3c00, + 3800, ), }, ), @@ -242,7 +242,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 02a023038a..1b20a574bd 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -2,7 +2,7 @@ ( Module { id: ModuleIdLt { - [salsa id]: Id(3400), + [salsa id]: Id(3000), }, }, [ @@ -11,14 +11,14 @@ def: EnumVariant( EnumVariant { id: EnumVariantId( - 7c00, + 7800, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -48,14 +48,14 @@ def: TypeAlias( TypeAlias { id: TypeAliasId( - 7000, + 6c00, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -83,14 +83,14 @@ def: EnumVariant( EnumVariant { id: EnumVariantId( - 7c01, + 7801, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -120,14 +120,14 @@ def: Const( Const { id: ConstId( - 6800, + 6400, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -155,14 +155,14 @@ def: Const( Const { id: ConstId( - 6802, + 6402, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -191,7 +191,7 @@ Enum( Enum { id: EnumId( - 5400, + 5000, ), }, ), @@ -199,7 +199,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -228,7 +228,7 @@ Macro { id: Macro2Id( Macro2Id( - 5000, + 4c00, ), ), }, @@ -236,7 +236,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -265,7 +265,7 @@ Macro { id: Macro2Id( Macro2Id( - 5000, + 4c00, ), ), }, @@ -273,7 +273,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -301,14 +301,14 @@ def: Static( Static { id: StaticId( - 6c00, + 6800, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -337,7 +337,7 @@ Struct( Struct { id: StructId( - 4c01, + 4801, ), }, ), @@ -345,7 +345,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -374,7 +374,7 @@ Struct( Struct { id: StructId( - 4c00, + 4800, ), }, ), @@ -382,7 +382,7 @@ loc: DeclarationLocation { hir_file_id: MacroFile( MacroCallId( - Id(4400), + Id(4000), ), ), ptr: SyntaxNodePtr { @@ -411,7 +411,7 @@ Struct( Struct { id: StructId( - 4c05, + 4805, ), }, ), @@ -419,7 +419,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -450,7 +450,7 @@ Struct( Struct { id: StructId( - 4c06, + 4806, ), }, ), @@ -458,7 +458,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -489,7 +489,7 @@ Struct( Struct { id: StructId( - 4c07, + 4807, ), }, ), @@ -497,7 +497,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -526,7 +526,7 @@ Struct( Struct { id: StructId( - 4c02, + 4802, ), }, ), @@ -534,7 +534,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -562,14 +562,14 @@ def: Trait( Trait { id: TraitId( - 6000, + 5c00, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -598,7 +598,7 @@ Macro { id: Macro2Id( Macro2Id( - 5000, + 4c00, ), ), }, @@ -606,7 +606,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -635,7 +635,7 @@ Union( Union { id: UnionId( - 5800, + 5400, ), }, ), @@ -643,7 +643,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -671,14 +671,14 @@ def: Module( Module { id: ModuleIdLt { - [salsa id]: Id(3401), + [salsa id]: Id(3001), }, }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -706,14 +706,14 @@ def: Module( Module { id: ModuleIdLt { - [salsa id]: Id(3402), + [salsa id]: Id(3002), }, }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -742,7 +742,7 @@ Macro { id: MacroRulesId( MacroRulesId( - 4001, + 3c01, ), ), }, @@ -750,7 +750,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -778,14 +778,14 @@ def: Function( FunctionId( FunctionId( - 6402, + 6002, ), ), ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -815,14 +815,14 @@ def: Function( FunctionId( FunctionId( - 6401, + 6001, ), ), ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -853,7 +853,7 @@ Macro { id: MacroRulesId( MacroRulesId( - 4000, + 3c00, ), ), }, @@ -861,7 +861,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -889,14 +889,14 @@ def: Function( FunctionId( FunctionId( - 6400, + 6000, ), ), ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -925,7 +925,7 @@ Macro { id: MacroRulesId( MacroRulesId( - 4001, + 3c01, ), ), }, @@ -933,7 +933,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -961,14 +961,14 @@ def: Function( FunctionId( FunctionId( - 6403, + 6003, ), ), ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -998,7 +998,7 @@ ( Module { id: ModuleIdLt { - [salsa id]: Id(3401), + [salsa id]: Id(3001), }, }, [ @@ -1008,7 +1008,7 @@ Struct( Struct { id: StructId( - 4c03, + 4803, ), }, ), @@ -1016,7 +1016,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { @@ -1044,7 +1044,7 @@ ( Module { id: ModuleIdLt { - [salsa id]: Id(3402), + [salsa id]: Id(3002), }, }, [ @@ -1053,14 +1053,14 @@ def: Trait( Trait { id: TraitId( - 6000, + 5c00, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3001), + Id(2c01), ), ), ptr: SyntaxNodePtr { @@ -1089,7 +1089,7 @@ Macro { id: Macro2Id( Macro2Id( - 5000, + 4c00, ), ), }, @@ -1097,7 +1097,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3001), + Id(2c01), ), ), ptr: SyntaxNodePtr { @@ -1126,7 +1126,7 @@ Struct( Struct { id: StructId( - 4c04, + 4804, ), }, ), @@ -1134,7 +1134,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3001), + Id(2c01), ), ), ptr: SyntaxNodePtr { @@ -1163,7 +1163,7 @@ Macro { id: Macro2Id( Macro2Id( - 5000, + 4c00, ), ), }, @@ -1171,7 +1171,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3001), + Id(2c01), ), ), ptr: SyntaxNodePtr { @@ -1200,7 +1200,7 @@ Struct( Struct { id: StructId( - 4c04, + 4804, ), }, ), @@ -1208,7 +1208,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3001), + Id(2c01), ), ), ptr: SyntaxNodePtr { diff --git a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt index aff1d56c56..f8ae687b78 100644 --- a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt +++ b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt @@ -5,7 +5,7 @@ Struct( Struct { id: StructId( - 4000, + 3c00, ), }, ), @@ -13,7 +13,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3001), + Id(2c01), ), ), ptr: SyntaxNodePtr { diff --git a/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/crates/ide-db/src/test_data/test_symbols_with_imports.txt index bf5d81cfb1..2282815a61 100644 --- a/crates/ide-db/src/test_data/test_symbols_with_imports.txt +++ b/crates/ide-db/src/test_data/test_symbols_with_imports.txt @@ -5,7 +5,7 @@ Struct( Struct { id: StructId( - 4000, + 3c00, ), }, ), @@ -13,7 +13,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3001), + Id(2c01), ), ), ptr: SyntaxNodePtr { @@ -42,7 +42,7 @@ Struct( Struct { id: StructId( - 4000, + 3c00, ), }, ), @@ -50,7 +50,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(3000), + Id(2c00), ), ), ptr: SyntaxNodePtr { diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs index dfa9639f6e..be4fe763a0 100644 --- a/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -40,7 +40,10 @@ pub(crate) fn inactive_code( #[cfg(test)] mod tests { - use crate::{DiagnosticsConfig, tests::check_diagnostics_with_config}; + use ide_db::RootDatabase; + use test_fixture::WithFixture; + + use crate::{DiagnosticCode, DiagnosticsConfig, tests::check_diagnostics_with_config}; #[track_caller] pub(crate) fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { @@ -206,10 +209,47 @@ union FooBar { #[cfg(true)] fn active() {} - #[cfg(any(not(true)), false)] fn inactive2() {} -//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: true is enabled + #[cfg(any(not(true), false))] fn inactive2() {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: true is enabled and false is disabled "#, ); } + + #[test] + fn inactive_crate() { + let db = RootDatabase::with_files( + r#" +#![cfg(false)] + +fn foo() {} + "#, + ); + let file_id = db.test_crate().root_file_id(&db); + let diagnostics = hir::attach_db(&db, || { + crate::full_diagnostics( + &db, + &DiagnosticsConfig::test_sample(), + &ide_db::assists::AssistResolveStrategy::All, + file_id.file_id(&db), + ) + }); + let [inactive_code] = &*diagnostics else { + panic!("expected one inactive_code diagnostic, found {diagnostics:#?}"); + }; + assert_eq!( + inactive_code.code, + DiagnosticCode::Ra("inactive-code", ide_db::Severity::WeakWarning) + ); + assert_eq!( + inactive_code.message, + "code is inactive due to #[cfg] directives: false is disabled", + ); + assert!(inactive_code.fixes.is_none()); + let full_file_range = file_id.parse(&db).syntax_node().text_range(); + assert_eq!( + inactive_code.range, + ide_db::FileRange { file_id: file_id.file_id(&db), range: full_file_range }, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 050d5477f6..85368cc09f 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -1,6 +1,6 @@ use either::Either; use hir::{ - AssocItem, FindPathConfig, HirDisplay, InFile, Type, + AssocItem, FindPathConfig, HasVisibility, HirDisplay, InFile, Type, db::{ExpandDatabase, HirDatabase}, sym, }; @@ -35,7 +35,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // ``` pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic { let mut message = String::from("missing structure fields:\n"); - for field in &d.missed_fields { + for (field, _) in &d.missed_fields { format_to!(message, "- {}\n", field.display(ctx.sema.db, ctx.edition)); } @@ -57,7 +57,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass // `struct A(usize);` // `let a = A { 0: () }` // but it is uncommon usage and it should not be encouraged. - if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { + if d.missed_fields.iter().any(|(name, _)| name.as_tuple_index().is_some()) { return None; } @@ -68,6 +68,12 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass let range = InFile::new(d.file, d.field_list_parent.text_range()) .original_node_file_range_rooted_opt(ctx.sema.db)?; + if let Some(current_module) = current_module + && d.missed_fields.iter().any(|(_, field)| !field.is_visible_from(ctx.db(), current_module)) + { + return None; + } + let build_text_edit = |new_syntax: &SyntaxNode, old_syntax| { let edit = { let old_range = ctx.sema.original_range_opt(old_syntax)?; @@ -120,7 +126,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) { cov_mark::hit!(field_shorthand); let candidate_ty = local_candidate.ty(ctx.sema.db); - if ty.could_unify_with(ctx.sema.db, &candidate_ty) { + if candidate_ty.could_coerce_to(ctx.sema.db, ty) { None } else { Some(generate_fill_expr(ty)) @@ -254,7 +260,7 @@ fn get_default_constructor( #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_fix}; + use crate::tests::{check_diagnostics, check_fix, check_no_fix}; #[test] fn missing_record_pat_field_diagnostic() { @@ -934,4 +940,45 @@ fn main() { "#, ); } + + #[test] + fn coerce_existing_local() { + check_fix( + r#" +struct A { + v: f64, +} + +fn f() -> A { + let v = loop {}; + A {$0} +} + "#, + r#" +struct A { + v: f64, +} + +fn f() -> A { + let v = loop {}; + A { v } +} + "#, + ); + } + + #[test] + fn inaccessible_fields() { + check_no_fix( + r#" +mod foo { + pub struct Bar { baz: i32 } +} + +fn qux() { + foo::Bar {$0}; +} + "#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index f4054610f2..6a380481d4 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -5,8 +5,10 @@ use ide_db::{ label::Label, source_change::SourceChangeBuilder, }; -use syntax::ToSmolStr; -use syntax::ast::edit::AstNodeEdit; +use syntax::{ + AstNode, ToSmolStr, + ast::{HasName, edit::AstNodeEdit}, +}; use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; @@ -82,16 +84,18 @@ fn quickfix_for_redundant_assoc_item( let db = ctx.sema.db; let root = db.parse_or_expand(d.file_id); // don't modify trait def in outer crate - let current_crate = ctx.sema.scope(&d.impl_.syntax_node_ptr().to_node(&root))?.krate(); + let impl_def = d.impl_.to_node(&root); + let current_crate = ctx.sema.scope(impl_def.syntax())?.krate(); let trait_def_crate = d.trait_.module(db).krate(db); if trait_def_crate != current_crate { return None; } let trait_def = d.trait_.source(db)?.value; - let l_curly = trait_def.assoc_item_list()?.l_curly_token()?.text_range(); + let insert_after = find_insert_after(range, &impl_def, &trait_def)?; + let where_to_insert = - hir::InFile::new(d.file_id, l_curly).original_node_file_range_rooted_opt(db)?; + hir::InFile::new(d.file_id, insert_after).original_node_file_range_rooted_opt(db)?; if where_to_insert.file_id != file_id { return None; } @@ -112,6 +116,41 @@ fn quickfix_for_redundant_assoc_item( }]) } +fn find_insert_after( + redundant_range: TextRange, + impl_def: &syntax::ast::Impl, + trait_def: &syntax::ast::Trait, +) -> Option<TextRange> { + let impl_items_before_redundant = impl_def + .assoc_item_list()? + .assoc_items() + .take_while(|it| it.syntax().text_range().start() < redundant_range.start()) + .filter_map(|it| name_of(&it)) + .collect::<Vec<_>>(); + + let after_item = trait_def + .assoc_item_list()? + .assoc_items() + .filter(|it| { + name_of(it).is_some_and(|name| { + impl_items_before_redundant.iter().any(|it| it.text() == name.text()) + }) + }) + .last() + .map(|it| it.syntax().text_range()); + + return after_item.or_else(|| Some(trait_def.assoc_item_list()?.l_curly_token()?.text_range())); + + fn name_of(it: &syntax::ast::AssocItem) -> Option<syntax::ast::Name> { + match it { + syntax::ast::AssocItem::Const(it) => it.name(), + syntax::ast::AssocItem::Fn(it) => it.name(), + syntax::ast::AssocItem::TypeAlias(it) => it.name(), + syntax::ast::AssocItem::MacroCall(_) => None, + } + } +} + #[cfg(test)] mod tests { use crate::tests::{check_diagnostics, check_fix, check_no_fix}; @@ -275,6 +314,69 @@ mod indent { } #[test] + fn quickfix_order() { + check_fix( + r#" +trait Marker { + fn foo(); + fn baz(); +} +struct Foo; +impl Marker for Foo { + fn foo() {} + fn missing() {}$0 + fn baz() {} +} + "#, + r#" +trait Marker { + fn foo(); + fn missing(); + fn baz(); +} +struct Foo; +impl Marker for Foo { + fn foo() {} + fn missing() {} + fn baz() {} +} + "#, + ); + + check_fix( + r#" +trait Marker { + type Item; + fn bar(); + fn baz(); +} +struct Foo; +impl Marker for Foo { + type Item = Foo; + fn missing() {}$0 + fn bar() {} + fn baz() {} +} + "#, + r#" +trait Marker { + type Item; + fn missing(); + fn bar(); + fn baz(); +} +struct Foo; +impl Marker for Foo { + type Item = Foo; + fn missing() {} + fn bar() {} + fn baz() {} +} + "#, + ); + } + + #[test] fn quickfix_dont_work() { check_no_fix( r#" diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index f443dc08f5..ff0e6a254b 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -20,7 +20,14 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_dis // // This diagnostic is triggered when the type of an expression or pattern does not match // the expected type. -pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Diagnostic { +pub(crate) fn type_mismatch( + ctx: &DiagnosticsContext<'_>, + d: &hir::TypeMismatch<'_>, +) -> Option<Diagnostic> { + if d.expected.is_unknown() || d.actual.is_unknown() { + return None; + } + let display_range = adjusted_display_range(ctx, d.expr_or_pat, &|node| { let Either::Left(expr) = node else { return None }; let salient_token_range = match expr { @@ -39,21 +46,23 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch< cov_mark::hit!(type_mismatch_range_adjustment); Some(salient_token_range) }); - Diagnostic::new( - DiagnosticCode::RustcHardError("E0308"), - format!( - "expected {}, found {}", - d.expected - .display(ctx.sema.db, ctx.display_target) - .with_closure_style(ClosureStyle::ClosureWithId), - d.actual - .display(ctx.sema.db, ctx.display_target) - .with_closure_style(ClosureStyle::ClosureWithId), - ), - display_range, + Some( + Diagnostic::new( + DiagnosticCode::RustcHardError("E0308"), + format!( + "expected {}, found {}", + d.expected + .display(ctx.sema.db, ctx.display_target) + .with_closure_style(ClosureStyle::ClosureWithId), + d.actual + .display(ctx.sema.db, ctx.display_target) + .with_closure_style(ClosureStyle::ClosureWithId), + ), + display_range, + ) + .stable() + .with_fixes(fixes(ctx, d)), ) - .stable() - .with_fixes(fixes(ctx, d)) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> { @@ -101,7 +110,8 @@ fn add_missing_ok_or_some( ) -> Option<()> { let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); - let expr_range = expr.syntax().text_range(); + let hir::FileRange { file_id, range: expr_range } = + ctx.sema.original_range_opt(expr.syntax())?; let scope = ctx.sema.scope(expr.syntax())?; let expected_adt = d.expected.as_adt()?; @@ -124,6 +134,8 @@ fn add_missing_ok_or_some( return None; } + let file_id = file_id.file_id(ctx.sema.db); + if d.actual.is_unit() { if let Expr::BlockExpr(block) = &expr { if block.tail_expr().is_none() { @@ -135,21 +147,18 @@ fn add_missing_ok_or_some( // Empty block let indent = block_indent + 1; builder.insert( - block.syntax().text_range().start() + TextSize::from(1), + expr_range.start() + TextSize::from(1), format!("\n{indent}{variant_name}(())\n{block_indent}"), ); } else { let indent = IndentLevel::from(1); builder.insert( - block.syntax().text_range().end() - TextSize::from(1), + expr_range.end() - TextSize::from(1), format!("{indent}{variant_name}(())\n{block_indent}"), ); } - let source_change = SourceChange::from_text_edit( - expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db), - builder.finish(), - ); + let source_change = SourceChange::from_text_edit(file_id, builder.finish()); let name = format!("Insert {variant_name}(()) as the tail of this block"); acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range)); } @@ -158,26 +167,31 @@ fn add_missing_ok_or_some( // Fix for forms like `fn foo() -> Result<(), String> { return; }` if ret_expr.expr().is_none() { let mut builder = TextEdit::builder(); - builder - .insert(ret_expr.syntax().text_range().end(), format!(" {variant_name}(())")); - let source_change = SourceChange::from_text_edit( - expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db), - builder.finish(), - ); + builder.insert(expr_range.end(), format!(" {variant_name}(())")); + let source_change = SourceChange::from_text_edit(file_id, builder.finish()); let name = format!("Insert {variant_name}(()) as the return value"); acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range)); } return Some(()); + } else if expr.is_block_like() + && expr.syntax().parent().and_then(ast::StmtList::cast).is_some() + { + // Fix for forms like `fn foo() -> Result<(), String> { for _ in 0..8 {} }` + let mut builder = TextEdit::builder(); + let indent = expr.indent_level(); + builder.insert(expr_range.end(), format!("\n{indent}{variant_name}(())")); + + let source_change = SourceChange::from_text_edit(file_id, builder.finish()); + let name = format!("Insert {variant_name}(()) as the tail of this block"); + acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range)); + return Some(()); } } let mut builder = TextEdit::builder(); - builder.insert(expr.syntax().text_range().start(), format!("{variant_name}(")); - builder.insert(expr.syntax().text_range().end(), ")".to_owned()); - let source_change = SourceChange::from_text_edit( - expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db), - builder.finish(), - ); + builder.insert(expr_range.start(), format!("{variant_name}(")); + builder.insert(expr_range.end(), ")".to_owned()); + let source_change = SourceChange::from_text_edit(file_id, builder.finish()); let name = format!("Wrap in {variant_name}"); acc.push(fix("wrap_in_constructor", &name, source_change, expr_range)); Some(()) @@ -192,6 +206,7 @@ fn remove_unnecessary_wrapper( let db = ctx.sema.db; let root = db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); + // FIXME: support inside MacroCall? let expr = ctx.sema.original_ast_node(expr)?; let Expr::CallExpr(call_expr) = expr else { @@ -278,6 +293,7 @@ fn remove_semicolon( return None; } let block = BlockExpr::cast(expr.syntax().clone())?; + // FIXME: support inside MacroCall? let expr_before_semi = block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?; let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original(); @@ -311,16 +327,13 @@ fn str_ref_to_owned( let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); - let expr_range = expr.syntax().text_range(); + let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(expr.syntax())?; let to_owned = ".to_owned()".to_owned(); - let edit = TextEdit::insert(expr.syntax().text_range().end(), to_owned); - let source_change = SourceChange::from_text_edit( - expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db), - edit, - ); - acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, expr_range)); + let edit = TextEdit::insert(range.end(), to_owned); + let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit); + acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, range)); Some(()) } @@ -568,6 +581,32 @@ fn div(x: i32, y: i32) -> Result<i32, ()> { } "#, ); + + check_fix( + r#" +//- minicore: option, result +macro_rules! identity { ($($t:tt)*) => ($($t)*) } +identity! { + fn div(x: i32, y: i32) -> Result<i32, ()> { + if y == 0 { + return Err(()); + } + x / y$0 + } +} +"#, + r#" +macro_rules! identity { ($($t:tt)*) => ($($t)*) } +identity! { + fn div(x: i32, y: i32) -> Result<i32, ()> { + if y == 0 { + return Err(()); + } + Ok(x / y) + } +} +"#, + ); } #[test] @@ -700,6 +739,21 @@ fn foo() -> Result<(), ()> { } "#, ); + + check_fix( + r#" +//- minicore: result +fn foo() -> Result<(), ()> { + for _ in 0..5 {}$0 +} + "#, + r#" +fn foo() -> Result<(), ()> { + for _ in 0..5 {} + Ok(()) +} + "#, + ); } #[test] @@ -1040,6 +1094,29 @@ fn test() -> String { } "#, ); + + check_fix( + r#" +macro_rules! identity { ($($t:tt)*) => ($($t)*) } +struct String; + +identity! { + fn test() -> String { + "a"$0 + } +} + "#, + r#" +macro_rules! identity { ($($t:tt)*) => ($($t)*) } +struct String; + +identity! { + fn test() -> String { + "a".to_owned() + } +} + "#, + ); } #[test] @@ -1253,4 +1330,23 @@ fn main() { "#, ); } + + #[test] + fn test_ignore_unknown_mismatch() { + check_diagnostics( + r#" +pub trait Foo { + type Out; +} +impl Foo for [i32; 1] { + type Out = (); +} +pub fn foo<T: Foo>(_: T) -> (T::Out,) { loop { } } + +fn main() { + let _x = foo(2); +} +"#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 1283a11700..d7a0a3b0f5 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -4,7 +4,7 @@ use std::iter; use hir::crate_def_map; use hir::{InFile, ModuleSource}; -use ide_db::base_db::RootQueryDb; +use ide_db::base_db; use ide_db::text_edit::TextEdit; use ide_db::{ FileId, FileRange, LineIndexDatabase, base_db::SourceDatabase, source_change::SourceChange, @@ -101,8 +101,8 @@ fn fixes( }; // check crate roots, i.e. main.rs, lib.rs, ... - let relevant_crates = db.relevant_crates(file_id); - 'crates: for &krate in &*relevant_crates { + let relevant_crates = base_db::relevant_crates(db, file_id); + 'crates: for &krate in relevant_crates { // FIXME: This shouldnt need to access the crate def map directly let crate_def_map = crate_def_map(ctx.sema.db, krate); @@ -157,7 +157,7 @@ fn fixes( paths.into_iter().find_map(|path| source_root.file_for_path(&path)) })?; stack.pop(); - let relevant_crates = db.relevant_crates(parent_id); + let relevant_crates = base_db::relevant_crates(db, parent_id); 'crates: for &krate in relevant_crates.iter() { let crate_def_map = crate_def_map(ctx.sema.db, krate); let Some((_, module)) = crate_def_map.modules().find(|(_, module)| { diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 0c6953419f..09c9f8eab0 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -96,7 +96,7 @@ use hir::{ use ide_db::{ FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode}, - base_db::{ReleaseChannel, RootQueryDb as _}, + base_db::{ReleaseChannel, all_crates, toolchain_channel}, generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup}, imports::insert_use::InsertUseConfig, label::Label, @@ -285,6 +285,12 @@ struct DiagnosticsContext<'a> { is_nightly: bool, } +impl<'a> DiagnosticsContext<'a> { + fn db(&self) -> &'a RootDatabase { + self.sema.db + } +} + /// Request parser level diagnostics for the given [`FileId`]. pub fn syntax_diagnostics( db: &RootDatabase, @@ -303,7 +309,8 @@ pub fn syntax_diagnostics( let (file_id, _) = editioned_file_id.unpack(db); // [#3434] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. - db.parse_errors(editioned_file_id) + editioned_file_id + .parse_errors(db) .into_iter() .flatten() .take(128) @@ -353,14 +360,14 @@ pub fn semantic_diagnostics( let module = sema.file_to_module_def(file_id); let is_nightly = matches!( - module.and_then(|m| db.toolchain_channel(m.krate(db).into())), + module.and_then(|m| toolchain_channel(db, m.krate(db).into())), Some(ReleaseChannel::Nightly) | None ); let krate = match module { Some(module) => module.krate(db), None => { - match db.all_crates().last() { + match all_crates(db).last() { Some(last) => (*last).into(), // short-circuit, return an empty vec of diagnostics None => return vec![], @@ -375,7 +382,7 @@ pub fn semantic_diagnostics( // A bunch of parse errors in a file indicate some bigger structural parse changes in the // file, so we skip semantic diagnostics so we can show these faster. Some(m) => { - if db.parse_errors(editioned_file_id).is_none_or(|es| es.len() < 16) { + if editioned_file_id.parse_errors(db).is_none_or(|es| es.len() < 16) { m.diagnostics(db, &mut diags, config.style_lints); } } @@ -430,7 +437,10 @@ pub fn semantic_diagnostics( AnyDiagnostic::TraitImplRedundantAssocItems(d) => handlers::trait_impl_redundant_assoc_item::trait_impl_redundant_assoc_item(&ctx, &d), AnyDiagnostic::TraitImplOrphan(d) => handlers::trait_impl_orphan::trait_impl_orphan(&ctx, &d), AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d), - AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d), + AnyDiagnostic::TypeMismatch(d) => match handlers::type_mismatch::type_mismatch(&ctx, &d) { + Some(diag) => diag, + None => continue, + }, AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d), AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), AnyDiagnostic::UnreachableLabel(d) => handlers::unreachable_label::unreachable_label(&ctx, &d), diff --git a/crates/ide-ssr/src/from_comment.rs b/crates/ide-ssr/src/from_comment.rs index 181cc74a51..83b8c3dc81 100644 --- a/crates/ide-ssr/src/from_comment.rs +++ b/crates/ide-ssr/src/from_comment.rs @@ -1,7 +1,7 @@ //! This module allows building an SSR MatchFinder by parsing the SSR rule //! from a comment. -use ide_db::{EditionedFileId, FilePosition, FileRange, RootDatabase, base_db::RootQueryDb}; +use ide_db::{EditionedFileId, FilePosition, FileRange, RootDatabase}; use syntax::{ TextRange, ast::{self, AstNode, AstToken}, @@ -19,7 +19,7 @@ pub fn ssr_from_comment( let comment = { let file_id = EditionedFileId::current_edition(db, frange.file_id); - let file = db.parse(file_id); + let file = file_id.parse(db); file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast) }?; let comment_text_without_prefix = comment.text().strip_prefix(comment.prefix()).unwrap(); diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs index 264f0660d7..ab5a0f70f5 100644 --- a/crates/ide-ssr/src/matching.rs +++ b/crates/ide-ssr/src/matching.rs @@ -7,7 +7,7 @@ use crate::{ resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo}, }; use hir::{FileRange, FindPathConfig, Semantics}; -use ide_db::{FxHashMap, base_db::RootQueryDb}; +use ide_db::{FxHashMap, base_db::all_crates}; use std::{cell::Cell, iter::Peekable}; use syntax::{ SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken, @@ -621,7 +621,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> { })? .original; let krate = self.sema.scope(expr.syntax()).map(|it| it.krate()).unwrap_or_else(|| { - hir::Crate::from(*self.sema.db.all_crates().last().expect("no crate graph present")) + hir::Crate::from(*all_crates(self.sema.db).last().expect("no crate graph present")) }); code_type diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 6fb8dedea4..21b2339c72 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -1,7 +1,7 @@ use hir::{HasSource, InFile, InRealFile, Semantics}; use ide_db::{ - FileId, FilePosition, FileRange, FxIndexSet, MiniCore, RootDatabase, defs::Definition, - helpers::visit_file_defs, + FileId, FilePosition, FileRange, FxIndexSet, RootDatabase, defs::Definition, + helpers::visit_file_defs, ra_fixture::RaFixtureConfig, }; use itertools::Itertools; use syntax::{AstNode, TextRange, ast::HasName}; @@ -45,7 +45,7 @@ pub struct AnnotationConfig<'a> { pub annotate_enum_variant_references: bool, pub location: AnnotationLocation, pub filter_adjacent_derive_implementations: bool, - pub minicore: MiniCore<'a>, + pub ra_fixture: RaFixtureConfig<'a>, } pub enum AnnotationLocation { @@ -216,7 +216,7 @@ pub(crate) fn resolve_annotation( *data = find_all_refs( &Semantics::new(db), pos, - &FindAllRefsConfig { search_scope: None, minicore: config.minicore }, + &FindAllRefsConfig { search_scope: None, ra_fixture: config.ra_fixture }, ) .map(|result| { result @@ -244,7 +244,7 @@ fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool { #[cfg(test)] mod tests { use expect_test::{Expect, expect}; - use ide_db::MiniCore; + use ide_db::ra_fixture::RaFixtureConfig; use crate::{Annotation, AnnotationConfig, fixture}; @@ -258,7 +258,7 @@ mod tests { annotate_method_references: true, annotate_enum_variant_references: true, location: AnnotationLocation::AboveName, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), filter_adjacent_derive_implementations: false, }; @@ -898,9 +898,6 @@ mod tests { test_id: Path( "tests::my_cool_test", ), - attr: TestAttr { - ignore: false, - }, }, cfg: None, update_test: UpdateTest { diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index aded911a8d..402764f112 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs @@ -4,9 +4,10 @@ use std::iter; use hir::Semantics; use ide_db::{ - FileRange, FxIndexMap, MiniCore, RootDatabase, + FileRange, FxIndexMap, RootDatabase, defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, + ra_fixture::RaFixtureConfig, search::FileReference, }; use syntax::{AstNode, SyntaxKind::IDENT, ast}; @@ -25,7 +26,7 @@ pub struct CallItem { pub struct CallHierarchyConfig<'a> { /// Whether to exclude tests from the call hierarchy pub exclude_tests: bool, - pub minicore: MiniCore<'a>, + pub ra_fixture: RaFixtureConfig<'a>, } pub(crate) fn call_hierarchy( @@ -36,7 +37,7 @@ pub(crate) fn call_hierarchy( goto_definition::goto_definition( db, position, - &GotoDefinitionConfig { minicore: config.minicore }, + &GotoDefinitionConfig { ra_fixture: config.ra_fixture }, ) } @@ -174,7 +175,7 @@ impl CallLocations { #[cfg(test)] mod tests { use expect_test::{Expect, expect}; - use ide_db::{FilePosition, MiniCore}; + use ide_db::{FilePosition, ra_fixture::RaFixtureConfig}; use itertools::Itertools; use crate::fixture; @@ -197,7 +198,8 @@ mod tests { ) } - let config = crate::CallHierarchyConfig { exclude_tests, minicore: MiniCore::default() }; + let config = + crate::CallHierarchyConfig { exclude_tests, ra_fixture: RaFixtureConfig::default() }; let (analysis, pos) = fixture::position(ra_fixture); let mut navs = analysis.call_hierarchy(pos, &config).unwrap().unwrap().info; diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 33bed9501a..fd462d003d 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -17,7 +17,7 @@ use hir::{ }; use ide_db::{ RootDatabase, - base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, RootQueryDb}, + base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, toolchain_channel}, defs::{Definition, NameClass, NameRefClass}, documentation::{Documentation, HasDocs}, helpers::pick_best_token, @@ -552,7 +552,7 @@ fn get_doc_base_urls( .and_then(|it| Url::parse(&it).ok()); let krate = def.krate(db); let channel = krate - .and_then(|krate| db.toolchain_channel(krate.into())) + .and_then(|krate| toolchain_channel(db, krate.into())) .unwrap_or(ReleaseChannel::Nightly) .as_str(); diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 44285d9315..fb885c2ad1 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -54,8 +54,9 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< let InFile { file_id, value: tokens } = hir::InMacroFile::new(macro_file, descended).upmap_once(db); let token = sema.parse_or_expand(file_id).covering_element(tokens[0]).into_token()?; - let attr = token.parent_ancestors().find_map(ast::Attr::cast)?; + let attr = token.parent_ancestors().find_map(ast::Meta::cast)?; let expansions = sema.expand_derive_macro(&attr)?; + let ast::Meta::TokenTreeMeta(attr) = attr else { return None }; let idx = attr .token_tree()? .token_trees_and_tokens() @@ -235,7 +236,7 @@ fn _format( file_id: FileId, expansion: &str, ) -> Option<String> { - use ide_db::base_db::RootQueryDb; + use ide_db::base_db::relevant_crates; // hack until we get hygiene working (same character amount to preserve formatting as much as possible) const DOLLAR_CRATE_REPLACE: &str = "__r_a_"; @@ -250,7 +251,7 @@ fn _format( }; let expansion = format!("{prefix}{expansion}{suffix}"); - let &crate_id = db.relevant_crates(file_id).iter().next()?; + let &crate_id = relevant_crates(db, file_id).iter().next()?; let edition = crate_id.data(db).edition; #[allow(clippy::disallowed_methods)] diff --git a/crates/ide/src/fetch_crates.rs b/crates/ide/src/fetch_crates.rs index 956379e722..ad5af8bfe1 100644 --- a/crates/ide/src/fetch_crates.rs +++ b/crates/ide/src/fetch_crates.rs @@ -1,6 +1,6 @@ use ide_db::{ FileId, FxIndexSet, RootDatabase, - base_db::{CrateOrigin, RootQueryDb}, + base_db::{CrateOrigin, all_crates}, }; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -20,7 +20,7 @@ pub struct CrateInfo { // //  pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> { - db.all_crates() + all_crates(db) .iter() .copied() .map(|crate_id| (crate_id.data(db), crate_id.extra_data(db))) diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index 3969490e8d..375e42cc83 100644 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs @@ -1,10 +1,11 @@ use ide_db::{FxHashSet, syntax_helpers::node_ext::vis_eq}; use syntax::{ - Direction, NodeOrToken, SourceFile, - SyntaxKind::{self, *}, + Direction, NodeOrToken, SourceFile, SyntaxElement, + SyntaxKind::*, SyntaxNode, TextRange, TextSize, ast::{self, AstNode, AstToken}, match_ast, + syntax_editor::Element, }; use std::hash::Hash; @@ -31,19 +32,33 @@ pub enum FoldKind { TypeAliases, ExternCrates, // endregion: item runs + Stmt(ast::Stmt), + TailExpr(ast::Expr), } #[derive(Debug)] pub struct Fold { pub range: TextRange, pub kind: FoldKind, + pub collapsed_text: Option<String>, +} + +impl Fold { + pub fn new(range: TextRange, kind: FoldKind) -> Self { + Self { range, kind, collapsed_text: None } + } + + pub fn with_text(mut self, text: Option<String>) -> Self { + self.collapsed_text = text; + self + } } // Feature: Folding // // Defines folding regions for curly braced blocks, runs of consecutive use, mod, const or static // items, and `region` / `endregion` comment markers. -pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { +pub(crate) fn folding_ranges(file: &SourceFile, add_collapsed_text: bool) -> Vec<Fold> { let mut res = vec![]; let mut visited_comments = FxHashSet::default(); let mut visited_nodes = FxHashSet::default(); @@ -53,39 +68,41 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { for element in file.syntax().descendants_with_tokens() { // Fold items that span multiple lines - if let Some(kind) = fold_kind(element.kind()) { + if let Some(kind) = fold_kind(element.clone()) { let is_multiline = match &element { NodeOrToken::Node(node) => node.text().contains_char('\n'), NodeOrToken::Token(token) => token.text().contains('\n'), }; + if is_multiline { - // for the func with multiline param list - if matches!(element.kind(), FN) - && let NodeOrToken::Node(node) = &element - && let Some(fn_node) = ast::Fn::cast(node.clone()) + if let NodeOrToken::Node(node) = &element + && let Some(fn_) = ast::Fn::cast(node.clone()) { - if !fn_node + if !fn_ .param_list() .map(|param_list| param_list.syntax().text().contains_char('\n')) - .unwrap_or(false) + .unwrap_or_default() { continue; } - if fn_node.body().is_some() { + if let Some(body) = fn_.body() { // Get the actual start of the function (excluding doc comments) - let fn_start = fn_node + let fn_start = fn_ .fn_token() .map(|token| token.text_range().start()) .unwrap_or(node.text_range().start()); - res.push(Fold { - range: TextRange::new(fn_start, node.text_range().end()), - kind: FoldKind::Function, - }); + res.push(Fold::new( + TextRange::new(fn_start, body.syntax().text_range().end()), + FoldKind::Function, + )); continue; } } - res.push(Fold { range: element.text_range(), kind }); + + let collapsed_text = if add_collapsed_text { collapsed_text(&kind) } else { None }; + let fold = Fold::new(element.text_range(), kind).with_text(collapsed_text); + res.push(fold); continue; } } @@ -102,15 +119,15 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { region_starts.push(comment.syntax().text_range().start()); } else if text.starts_with(REGION_END) { if let Some(region) = region_starts.pop() { - res.push(Fold { - range: TextRange::new(region, comment.syntax().text_range().end()), - kind: FoldKind::Region, - }) + res.push(Fold::new( + TextRange::new(region, comment.syntax().text_range().end()), + FoldKind::Region, + )); } } else if let Some(range) = contiguous_range_for_comment(comment, &mut visited_comments) { - res.push(Fold { range, kind: FoldKind::Comment }) + res.push(Fold::new(range, FoldKind::Comment)); } } } @@ -123,37 +140,37 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { module, &mut visited_nodes, ) { - res.push(Fold { range, kind: FoldKind::Modules }) + res.push(Fold::new(range, FoldKind::Modules)); } }, ast::Use(use_) => { if let Some(range) = contiguous_range_for_item_group(use_, &mut visited_nodes) { - res.push(Fold { range, kind: FoldKind::Imports }) + res.push(Fold::new(range, FoldKind::Imports)); } }, ast::Const(konst) => { if let Some(range) = contiguous_range_for_item_group(konst, &mut visited_nodes) { - res.push(Fold { range, kind: FoldKind::Consts }) + res.push(Fold::new(range, FoldKind::Consts)); } }, ast::Static(statik) => { if let Some(range) = contiguous_range_for_item_group(statik, &mut visited_nodes) { - res.push(Fold { range, kind: FoldKind::Statics }) + res.push(Fold::new(range, FoldKind::Statics)); } }, ast::TypeAlias(alias) => { if let Some(range) = contiguous_range_for_item_group(alias, &mut visited_nodes) { - res.push(Fold { range, kind: FoldKind::TypeAliases }) + res.push(Fold::new(range, FoldKind::TypeAliases)); } }, ast::ExternCrate(extern_crate) => { if let Some(range) = contiguous_range_for_item_group(extern_crate, &mut visited_nodes) { - res.push(Fold { range, kind: FoldKind::ExternCrates }) + res.push(Fold::new(range, FoldKind::ExternCrates)); } }, ast::MatchArm(match_arm) => { if let Some(range) = fold_range_for_multiline_match_arm(match_arm) { - res.push(Fold {range, kind: FoldKind::MatchArm}) + res.push(Fold::new(range, FoldKind::MatchArm)); } }, _ => (), @@ -166,8 +183,66 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { res } -fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { +fn collapsed_text(kind: &FoldKind) -> Option<String> { match kind { + FoldKind::TailExpr(expr) => collapse_expr(expr.clone()), + FoldKind::Stmt(stmt) => { + match stmt { + ast::Stmt::ExprStmt(expr_stmt) => { + expr_stmt.expr().and_then(collapse_expr).map(|text| format!("{text};")) + } + ast::Stmt::LetStmt(let_stmt) => 'blk: { + if let_stmt.let_else().is_some() { + break 'blk None; + } + + let Some(expr) = let_stmt.initializer() else { + break 'blk None; + }; + + // If the `let` statement spans multiple lines, we do not collapse it. + // We use the `eq_token` to check whether the `let` statement is a single line, + // as the formatter may place the initializer on a new line for better readability. + // + // Example: + // ```rust + // let complex_pat = + // complex_expr; + // ``` + // + // In this case, we should generate the collapsed text. + let Some(eq_token) = let_stmt.eq_token() else { + break 'blk None; + }; + let eq_token_offset = + eq_token.text_range().end() - let_stmt.syntax().text_range().start(); + let text_until_eq_token = let_stmt.syntax().text().slice(..eq_token_offset); + if text_until_eq_token.contains_char('\n') { + break 'blk None; + } + + collapse_expr(expr).map(|text| format!("{text_until_eq_token} {text};")) + } + // handling `items` in external matches. + ast::Stmt::Item(_) => None, + } + } + _ => None, + } +} + +fn fold_kind(element: SyntaxElement) -> Option<FoldKind> { + // handle tail_expr + if let Some(node) = element.as_node() + // tail_expr -> stmt_list -> block + && let Some(block) = node.parent().and_then(|it| it.parent()).and_then(ast::BlockExpr::cast) + && let Some(tail_expr) = block.tail_expr() + && tail_expr.syntax() == node + { + return Some(FoldKind::TailExpr(tail_expr)); + } + + match element.kind() { COMMENT => Some(FoldKind::Comment), ARG_LIST | PARAM_LIST | GENERIC_ARG_LIST | GENERIC_PARAM_LIST => Some(FoldKind::ArgList), ARRAY_EXPR => Some(FoldKind::Array), @@ -185,10 +260,73 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | MATCH_ARM_LIST | VARIANT_LIST | TOKEN_TREE => Some(FoldKind::Block), + EXPR_STMT | LET_STMT => Some(FoldKind::Stmt(ast::Stmt::cast(element.as_node()?.clone())?)), _ => None, } } +const COLLAPSE_EXPR_MAX_LEN: usize = 100; + +fn collapse_expr(expr: ast::Expr) -> Option<String> { + let mut text = String::with_capacity(COLLAPSE_EXPR_MAX_LEN * 2); + + let mut preorder = expr.syntax().preorder_with_tokens(); + while let Some(element) = preorder.next() { + match element { + syntax::WalkEvent::Enter(NodeOrToken::Node(node)) => { + if let Some(arg_list) = ast::ArgList::cast(node.clone()) { + let content = if arg_list.args().next().is_some() { "(…)" } else { "()" }; + text.push_str(content); + preorder.skip_subtree(); + } else if let Some(expr) = ast::Expr::cast(node) { + match expr { + ast::Expr::AwaitExpr(_) + | ast::Expr::BecomeExpr(_) + | ast::Expr::BinExpr(_) + | ast::Expr::BreakExpr(_) + | ast::Expr::CallExpr(_) + | ast::Expr::CastExpr(_) + | ast::Expr::ContinueExpr(_) + | ast::Expr::FieldExpr(_) + | ast::Expr::IndexExpr(_) + | ast::Expr::LetExpr(_) + | ast::Expr::Literal(_) + | ast::Expr::MethodCallExpr(_) + | ast::Expr::OffsetOfExpr(_) + | ast::Expr::ParenExpr(_) + | ast::Expr::PathExpr(_) + | ast::Expr::PrefixExpr(_) + | ast::Expr::RangeExpr(_) + | ast::Expr::RefExpr(_) + | ast::Expr::ReturnExpr(_) + | ast::Expr::TryExpr(_) + | ast::Expr::UnderscoreExpr(_) + | ast::Expr::YeetExpr(_) + | ast::Expr::YieldExpr(_) => {} + + // Some other exprs (e.g. `while` loop) are too complex to have a collapsed text + _ => return None, + } + } + } + syntax::WalkEvent::Enter(NodeOrToken::Token(token)) => { + if !token.kind().is_trivia() { + text.push_str(token.text()); + } + } + syntax::WalkEvent::Leave(_) => {} + } + + if text.len() > COLLAPSE_EXPR_MAX_LEN { + return None; + } + } + + text.shrink_to_fit(); + + Some(text) +} + fn contiguous_range_for_item_group<N>( first: N, visited: &mut FxHashSet<SyntaxNode>, @@ -297,7 +435,7 @@ fn contiguous_range_for_comment( } fn fold_range_for_multiline_match_arm(match_arm: ast::MatchArm) -> Option<TextRange> { - if fold_kind(match_arm.expr()?.syntax().kind()).is_some() { + if fold_kind(match_arm.expr()?.syntax().syntax_element()).is_some() { None } else if match_arm.expr()?.syntax().text().contains_char('\n') { Some(match_arm.expr()?.syntax().text_range()) @@ -314,10 +452,33 @@ mod tests { #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { + check_inner(ra_fixture, true); + } + + fn check_without_collapsed_text(#[rust_analyzer::rust_fixture] ra_fixture: &str) { + check_inner(ra_fixture, false); + } + + fn check_inner(ra_fixture: &str, enable_collapsed_text: bool) { let (ranges, text) = extract_tags(ra_fixture, "fold"); + let ranges: Vec<_> = ranges + .into_iter() + .map(|(range, text)| { + let (attr, collapsed_text) = match text { + Some(text) => match text.split_once(':') { + Some((attr, collapsed_text)) => { + (Some(attr.to_owned()), Some(collapsed_text.to_owned())) + } + None => (Some(text), None), + }, + None => (None, None), + }; + (range, attr, collapsed_text) + }) + .collect(); let parse = SourceFile::parse(&text, span::Edition::CURRENT); - let mut folds = folding_ranges(&parse.tree()); + let mut folds = folding_ranges(&parse.tree(), enable_collapsed_text); folds.sort_by_key(|fold| (fold.range.start(), fold.range.end())); assert_eq!( @@ -326,7 +487,7 @@ mod tests { "The amount of folds is different than the expected amount" ); - for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) { + for (fold, (range, attr, collapsed_text)) in folds.iter().zip(ranges.into_iter()) { assert_eq!(fold.range.start(), range.start(), "mismatched start of folding ranges"); assert_eq!(fold.range.end(), range.end(), "mismatched end of folding ranges"); @@ -346,8 +507,15 @@ mod tests { FoldKind::MatchArm => "matcharm", FoldKind::Function => "function", FoldKind::ExternCrates => "externcrates", + FoldKind::Stmt(_) => "stmt", + FoldKind::TailExpr(_) => "tailexpr", }; assert_eq!(kind, &attr.unwrap()); + if enable_collapsed_text { + assert_eq!(fold.collapsed_text, collapsed_text); + } else { + assert_eq!(fold.collapsed_text, None); + } } } @@ -511,10 +679,10 @@ macro_rules! foo <fold block>{ check( r#" fn main() <fold block>{ - match 0 <fold block>{ + <fold tailexpr>match 0 <fold block>{ 0 => 0, _ => 1, - }</fold> + }</fold></fold> }</fold> "#, ); @@ -525,7 +693,7 @@ fn main() <fold block>{ check( r#" fn main() <fold block>{ - match foo <fold block>{ + <fold tailexpr>match foo <fold block>{ block => <fold block>{ }</fold>, matcharm => <fold matcharm>some. @@ -544,7 +712,7 @@ fn main() <fold block>{ structS => <fold matcharm>StructS <fold block>{ a: 31, }</fold></fold>, - }</fold> + }</fold></fold> }</fold> "#, ) @@ -555,11 +723,11 @@ fn main() <fold block>{ check( r#" fn main() <fold block>{ - frobnicate<fold arglist>( + <fold tailexpr:frobnicate(…)>frobnicate<fold arglist>( 1, 2, 3, - )</fold> + )</fold></fold> }</fold> "#, ) @@ -698,4 +866,51 @@ type Foo<T, U> = foo<fold arglist>< "#, ); } + + #[test] + fn test_fold_tail_expr() { + check( + r#" +fn f() <fold block>{ + let x = 1; + + <fold tailexpr:some_function().chain().method()>some_function() + .chain() + .method()</fold> +}</fold> +"#, + ) + } + + #[test] + fn test_fold_let_stmt_with_chained_methods() { + check( + r#" +fn main() <fold block>{ + <fold stmt:let result = some_value.method1().method2()?.method3();>let result = some_value + .method1() + .method2()? + .method3();</fold> + + println!("{}", result); +}</fold> +"#, + ) + } + + #[test] + fn test_fold_let_stmt_with_chained_methods_without_collapsed_text() { + check_without_collapsed_text( + r#" +fn main() <fold block>{ + <fold stmt>let result = some_value + .method1() + .method2()? + .method3();</fold> + + println!("{}", result); +}</fold> +"#, + ) + } } diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs index 375ce94bf6..d2b47a37c7 100644 --- a/crates/ide/src/goto_declaration.rs +++ b/crates/ide/src/goto_declaration.rs @@ -79,13 +79,13 @@ pub(crate) fn goto_declaration( #[cfg(test)] mod tests { - use ide_db::{FileRange, MiniCore}; + use ide_db::{FileRange, ra_fixture::RaFixtureConfig}; use itertools::Itertools; use crate::{GotoDefinitionConfig, fixture}; const TEST_CONFIG: GotoDefinitionConfig<'_> = - GotoDefinitionConfig { minicore: MiniCore::default() }; + GotoDefinitionConfig { ra_fixture: RaFixtureConfig::default() }; fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position, expected) = fixture::annotations(ra_fixture); diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 3890bcad7f..4cdf0eac75 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -9,7 +9,7 @@ use crate::{ use hir::{ AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym, }; -use ide_db::{MiniCore, ra_fixture::UpmapFromRaFixture}; +use ide_db::ra_fixture::{RaFixtureConfig, UpmapFromRaFixture}; use ide_db::{ RootDatabase, SymbolKind, base_db::{AnchoredPath, SourceDatabase}, @@ -26,7 +26,7 @@ use syntax::{ #[derive(Debug)] pub struct GotoDefinitionConfig<'a> { - pub minicore: MiniCore<'a>, + pub ra_fixture: RaFixtureConfig<'a>, } // Feature: Go to Definition @@ -105,7 +105,7 @@ pub(crate) fn goto_definition( if let Some(token) = ast::String::cast(token.value.clone()) && let Some(original_token) = ast::String::cast(original_token.clone()) && let Some((analysis, fixture_analysis)) = - Analysis::from_ra_fixture(sema, original_token, &token, config.minicore) + Analysis::from_ra_fixture(sema, original_token, &token, &config.ra_fixture) && let Some((virtual_file_id, file_offset)) = fixture_analysis.map_offset_down(offset) { return hir::attach_db_allow_change(&analysis.db, || { @@ -605,11 +605,11 @@ fn expr_to_nav( #[cfg(test)] mod tests { use crate::{GotoDefinitionConfig, fixture}; - use ide_db::{FileRange, MiniCore}; + use ide_db::{FileRange, ra_fixture::RaFixtureConfig}; use itertools::Itertools; const TEST_CONFIG: GotoDefinitionConfig<'_> = - GotoDefinitionConfig { minicore: MiniCore::default() }; + GotoDefinitionConfig { ra_fixture: RaFixtureConfig::default() }; #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 958de8930d..df1fcecc99 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -8,11 +8,11 @@ use std::{iter, ops::Not}; use either::Either; use hir::{DisplayTarget, GenericDef, GenericSubstitution, HasCrate, HasSource, Semantics}; use ide_db::{ - FileRange, FxIndexSet, MiniCore, Ranker, RootDatabase, + FileRange, FxIndexSet, Ranker, RootDatabase, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, - ra_fixture::UpmapFromRaFixture, + ra_fixture::{RaFixtureConfig, UpmapFromRaFixture}, }; use itertools::{Itertools, multizip}; use macros::UpmapFromRaFixture; @@ -44,7 +44,7 @@ pub struct HoverConfig<'a> { pub max_enum_variants_count: Option<usize>, pub max_subst_ty_len: SubstTyLen, pub show_drop_glue: bool, - pub minicore: MiniCore<'a>, + pub ra_fixture: RaFixtureConfig<'a>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -221,7 +221,7 @@ fn hover_offset( if let Some(literal) = ast::String::cast(original_token.clone()) && let Some((analysis, fixture_analysis)) = - Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore) + Analysis::from_ra_fixture(sema, literal.clone(), &literal, &config.ra_fixture) { let (virtual_file_id, virtual_offset) = fixture_analysis.map_offset_down(offset)?; return analysis @@ -422,7 +422,7 @@ fn hover_ranged( Either::Left(ast::Expr::Literal(literal)) => { if let Some(literal) = ast::String::cast(literal.token()) && let Some((analysis, fixture_analysis)) = - Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore) + Analysis::from_ra_fixture(sema, literal.clone(), &literal, &config.ra_fixture) { let (virtual_file_id, virtual_range) = fixture_analysis.map_range_down(range)?; return analysis diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 7fbbc576dd..9c53b05539 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -1,5 +1,5 @@ use expect_test::{Expect, expect}; -use ide_db::{FileRange, MiniCore, base_db::SourceDatabase}; +use ide_db::{FileRange, base_db::SourceDatabase, ra_fixture::RaFixtureConfig}; use syntax::TextRange; use crate::{ @@ -25,7 +25,7 @@ const HOVER_BASE_CONFIG: HoverConfig<'_> = HoverConfig { max_enum_variants_count: Some(5), max_subst_ty_len: super::SubstTyLen::Unlimited, show_drop_glue: true, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) { @@ -3526,9 +3526,6 @@ fn foo_$0test() {} test_id: Path( "foo_test", ), - attr: TestAttr { - ignore: false, - }, }, cfg: None, update_test: UpdateTest { @@ -10707,9 +10704,6 @@ macro_rules! str { test_id: Path( "test", ), - attr: TestAttr { - ignore: false, - }, }, cfg: None, update_test: UpdateTest { @@ -10778,9 +10772,6 @@ pub use expect_test; test_id: Path( "test", ), - attr: TestAttr { - ignore: false, - }, }, cfg: None, update_test: UpdateTest { @@ -11404,3 +11395,299 @@ pub trait MyTrait { "#]], ); } + +#[test] +fn test_hover_doc_attr_macro_generated_method_stringify_self_ty() { + check( + r#" +#[rustc_builtin_macro] +macro_rules! concat {} + +#[rustc_builtin_macro] +macro_rules! stringify {} + +macro_rules! bar { + ($SelfT:ident) => { + struct $SelfT; + impl $SelfT { + #[doc = concat!("Do the foo for ", stringify!($SelfT))] + fn foo(&self) {} + } + } +} + +bar!(Bar); + +fn foo() { let bar = Bar; bar.fo$0o(); } +"#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture::Bar + ``` + + ```rust + fn foo(&self) + ``` + + --- + + Do the foo for Bar + "#]], + ); +} + +#[test] +fn test_hover_doc_attr_macro_argument_expr_issue_7688() { + check( + r#" +#[rustc_builtin_macro] +macro_rules! concat {} + +macro_rules! doc_comment { + ($x:expr, $($tt:tt)*) => { + #[doc = $x] + $($tt)* + }; +} + +doc_comment! { + concat!("Hello", " world"), + struct Ba$0r; +} +"#, + expect![[r#" + *Bar* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Bar + ``` + + --- + + size = 0, align = 1, no Drop + + --- + + Hello world + "#]], + ); +} + +#[test] +fn test_hover_doc_attr_concat_macro() { + check( + r#" +#[rustc_builtin_macro] +macro_rules! concat {} + +#[doc = concat!("Hello", " ", "World")] +struct Ba$0r; +"#, + expect![[r#" + *Bar* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Bar + ``` + + --- + + size = 0, align = 1, no Drop + + --- + + Hello World + "#]], + ); +} + +#[test] +fn test_hover_doc_attr_include_str_macro() { + check( + r#" +//- /main.rs +#[rustc_builtin_macro] +macro_rules! include_str {} + +#[doc = include_str!("docs.md")] +struct Ba$0r; + +//- /docs.md +Included docs from file. +Multiple lines of docs. +"#, + expect![[r#" + *Bar* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Bar + ``` + + --- + + size = 0, align = 1, no Drop + + --- + + Included docs from file. + Multiple lines of docs. + "#]], + ); +} + +#[test] +fn test_hover_doc_attr_user_macro_returning_string() { + check( + r#" +macro_rules! doc_str { + () => { "Documentation from macro" }; +} + +#[doc = doc_str!()] +struct Ba$0r; +"#, + expect![[r#" + *Bar* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Bar + ``` + + --- + + size = 0, align = 1, no Drop + + --- + + Documentation from macro + "#]], + ); +} + +#[test] +fn test_hover_doc_attr_mixed_literal_and_macro() { + check( + r#" +#[rustc_builtin_macro] +macro_rules! concat {} + +/// First line +#[doc = concat!("Second", " line")] +struct Ba$0r; +"#, + expect![[r#" + *Bar* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Bar + ``` + + --- + + size = 0, align = 1, no Drop + + --- + + First line + Second line + "#]], + ); +} + +#[test] +fn test_hover_doc_attr_field_with_macro() { + check( + r#" +#[rustc_builtin_macro] +macro_rules! concat {} + +struct Bar { + #[doc = concat!("field", " docs")] + ba$0z: i32, +} +"#, + expect![[r#" + *baz* + + ```rust + ra_test_fixture::Bar + ``` + + ```rust + baz: i32 + ``` + + --- + + size = 4, align = 4, offset = 0, no Drop + + --- + + field docs + "#]], + ); +} + +#[test] +fn test_hover_doc_attr_macro_on_outlined_mod() { + // Outer doc-macro on `mod foo;` resolves from inside the module's scope + // (matching rustc behavior), and combines with inner `//!` docs from the module file. + check( + r#" +//- /main.rs +mod mac { + macro_rules! doc_str { + () => { "expanded from macro" }; + } + pub(crate) use doc_str; +} + +/// plain outer doc +#[doc = super::mac::doc_str!()] +mod foo$0; + +//- /foo.rs +//! inner module docs +pub struct Bar; +"#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture + ``` + + ```rust + mod foo + ``` + + --- + + plain outer doc + expanded from macro + inner module docs + "#]], + ); +} diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index a58dc6f030..f51d7f5207 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -9,7 +9,8 @@ use hir::{ HirDisplay, HirDisplayError, HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym, }; use ide_db::{ - FileRange, MiniCore, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder, + FileRange, RootDatabase, famous_defs::FamousDefs, ra_fixture::RaFixtureConfig, + text_edit::TextEditBuilder, }; use ide_db::{FxHashSet, text_edit::TextEdit}; use itertools::Itertools; @@ -302,6 +303,7 @@ fn hints( pub struct InlayHintsConfig<'a> { pub render_colons: bool, pub type_hints: bool, + pub type_hints_placement: TypeHintsPlacement, pub sized_bound: bool, pub discriminant_hints: DiscriminantHints, pub parameter_hints: bool, @@ -328,7 +330,13 @@ pub struct InlayHintsConfig<'a> { pub max_length: Option<usize>, pub closing_brace_hints_min_lines: Option<usize>, pub fields_to_resolve: InlayFieldsToResolve, - pub minicore: MiniCore<'a>, + pub ra_fixture: RaFixtureConfig<'a>, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum TypeHintsPlacement { + Inline, + EndOfLine, } impl InlayHintsConfig<'_> { @@ -899,7 +907,7 @@ mod tests { use expect_test::Expect; use hir::ClosureStyle; - use ide_db::MiniCore; + use ide_db::ra_fixture::RaFixtureConfig; use itertools::Itertools; use test_utils::extract_annotations; @@ -907,12 +915,15 @@ mod tests { use crate::inlay_hints::{AdjustmentHints, AdjustmentHintsMode}; use crate::{LifetimeElisionHints, fixture, inlay_hints::InlayHintsConfig}; - use super::{ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve}; + use super::{ + ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve, TypeHintsPlacement, + }; pub(super) const DISABLED_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig { discriminant_hints: DiscriminantHints::Never, render_colons: false, type_hints: false, + type_hints_placement: TypeHintsPlacement::Inline, parameter_hints: false, parameter_hints_for_missing_arguments: false, sized_bound: false, @@ -942,10 +953,11 @@ mod tests { implicit_drop_hints: false, implied_dyn_trait_hints: false, range_exclusive_hints: false, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; pub(super) const TEST_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig { type_hints: true, + type_hints_placement: TypeHintsPlacement::Inline, parameter_hints: true, chaining_hints: true, closure_return_type_hints: ClosureReturnTypeHints::WithBlock, diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index caf7cc714d..b901c6b67d 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -8,10 +8,12 @@ use ide_db::{RootDatabase, famous_defs::FamousDefs}; use itertools::Itertools; use syntax::{ + TextRange, ast::{self, AstNode, HasGenericArgs, HasName}, match_ast, }; +use super::TypeHintsPlacement; use crate::{ InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, @@ -29,6 +31,7 @@ pub(super) fn hints( } let parent = pat.syntax().parent()?; + let mut enclosing_let_stmt = None; let type_ascriptable = match_ast! { match parent { ast::Param(it) => { @@ -41,6 +44,7 @@ pub(super) fn hints( Some(it.colon_token()) }, ast::LetStmt(it) => { + enclosing_let_stmt = Some(it.clone()); if config.hide_closure_initialization_hints && let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() && closure_has_block_body(&closure) { @@ -101,16 +105,26 @@ pub(super) fn hints( Some(name) => name.syntax().text_range(), None => pat.syntax().text_range(), }; + let mut range = match type_ascriptable { + Some(Some(t)) => text_range.cover(t.text_range()), + _ => text_range, + }; + + let mut pad_left = !render_colons; + if matches!(config.type_hints_placement, TypeHintsPlacement::EndOfLine) + && let Some(let_stmt) = enclosing_let_stmt + { + let stmt_range = let_stmt.syntax().text_range(); + range = TextRange::new(range.start(), stmt_range.end()); + pad_left = true; + } acc.push(InlayHint { - range: match type_ascriptable { - Some(Some(t)) => text_range.cover(t.text_range()), - _ => text_range, - }, + range, kind: InlayKind::Type, label, text_edit, position: InlayHintPosition::After, - pad_left: !render_colons, + pad_left, pad_right: false, resolve_parent: Some(pat.syntax().text_range()), }); @@ -182,6 +196,7 @@ mod tests { use crate::{ClosureReturnTypeHints, fixture, inlay_hints::InlayHintsConfig}; + use super::TypeHintsPlacement; use crate::inlay_hints::tests::{ DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_expect, check_no_edit, check_with_config, @@ -205,6 +220,76 @@ fn main() { } #[test] + fn type_hints_end_of_line_placement() { + let mut config = InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }; + config.type_hints_placement = TypeHintsPlacement::EndOfLine; + check_expect( + config, + r#" +fn main() { + let foo = 92_i32; +} + "#, + expect![[r#" + [ + ( + 20..33, + [ + "i32", + ], + ), + ] + "#]], + ); + } + + #[test] + fn type_hints_end_of_line_placement_chain_expr() { + let mut config = InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }; + config.type_hints_placement = TypeHintsPlacement::EndOfLine; + check_expect( + config, + r#" +fn main() { + struct Builder; + impl Builder { + fn iter(self) -> Builder { Builder } + fn map(self) -> Builder { Builder } + } + fn make() -> Builder { Builder } + + let foo = make() + .iter() + .map(); +} +"#, + expect![[r#" + [ + ( + 192..236, + [ + InlayHintLabelPart { + text: "Builder", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 23..30, + }, + ), + ), + tooltip: "", + }, + ], + ), + ] + "#]], + ); + } + + #[test] fn type_hints_bindings_after_at() { check_types( r#" diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index cf3149c946..4b06f83971 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -2,13 +2,13 @@ use hir::DisplayTarget; use ide_db::famous_defs::FamousDefs; use syntax::{ - Direction, NodeOrToken, SyntaxKind, T, + Direction, NodeOrToken, SyntaxKind, T, TextRange, ast::{self, AstNode}, }; use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; -use super::label_of_ty; +use super::{TypeHintsPlacement, label_of_ty}; pub(super) fn hints( acc: &mut Vec<InlayHint>, @@ -40,13 +40,14 @@ pub(super) fn hints( // Chaining can be defined as an expression whose next sibling tokens are newline and dot // Ignoring extra whitespace and comments - let next = tokens.next()?.kind(); - if next == SyntaxKind::WHITESPACE { - let mut next_next = tokens.next()?.kind(); - while next_next == SyntaxKind::WHITESPACE { - next_next = tokens.next()?.kind(); + let next_token = tokens.next()?; + if next_token.kind() == SyntaxKind::WHITESPACE { + let newline_token = next_token; + let mut next_next = tokens.next()?; + while next_next.kind() == SyntaxKind::WHITESPACE { + next_next = tokens.next()?; } - if next_next == T![.] { + if next_next.kind() == T![.] { let ty = sema.type_of_expr(desc_expr)?.original; if ty.is_unknown() { return None; @@ -58,8 +59,18 @@ pub(super) fn hints( return None; } let label = label_of_ty(famous_defs, config, &ty, display_target)?; + let range = { + let mut range = expr.syntax().text_range(); + if config.type_hints_placement == TypeHintsPlacement::EndOfLine { + range = TextRange::new( + range.start(), + newline_token.text_range().start().max(range.end()), + ); + } + range + }; acc.push(InlayHint { - range: expr.syntax().text_range(), + range, kind: InlayKind::Chaining, label, text_edit: None, @@ -79,7 +90,7 @@ mod tests { use ide_db::text_edit::{TextRange, TextSize}; use crate::{ - InlayHintsConfig, fixture, + InlayHintsConfig, TypeHintsPlacement, fixture, inlay_hints::{ LazyProperty, tests::{DISABLED_CONFIG, TEST_CONFIG, check_expect, check_with_config}, @@ -686,4 +697,80 @@ fn main() { "#]], ); } + + #[test] + fn chaining_hints_end_of_line_placement() { + check_expect( + InlayHintsConfig { + chaining_hints: true, + type_hints_placement: TypeHintsPlacement::EndOfLine, + ..DISABLED_CONFIG + }, + r#" +fn main() { + let baz = make() + .into_bar() + .into_baz(); +} + +struct Foo; +struct Bar; +struct Baz; + +impl Foo { + fn into_bar(self) -> Bar { Bar } +} + +impl Bar { + fn into_baz(self) -> Baz { Baz } +} + +fn make() -> Foo { + Foo +} +"#, + expect![[r#" + [ + ( + 26..52, + [ + InlayHintLabelPart { + text: "Bar", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 96..99, + }, + ), + ), + tooltip: "", + }, + ], + ), + ( + 26..32, + [ + InlayHintLabelPart { + text: "Foo", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 84..87, + }, + ), + ), + tooltip: "", + }, + ], + ), + ] + "#]], + ); + } } diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index 08588bbed0..8dddf9d37e 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs @@ -37,8 +37,9 @@ pub(super) fn hints( let hints = callable .params() .into_iter() - .zip(arg_list.args()) + .zip(arg_list.args_maybe_empty()) .filter_map(|(p, arg)| { + let arg = arg?; // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; if range.file_id != file_id { @@ -562,6 +563,19 @@ fn main() { } #[test] + fn param_name_hints_show_after_empty_arg() { + check_params( + r#"pub fn test(a: i32, b: i32, c: i32) {} +fn main() { + test(, 2,); + //^ b + test(, , 3); + //^ c +}"#, + ) + } + + #[test] fn function_call_parameter_hint() { check_params( r#" diff --git a/crates/ide/src/inlay_hints/ra_fixture.rs b/crates/ide/src/inlay_hints/ra_fixture.rs index bee1841642..701c8a8612 100644 --- a/crates/ide/src/inlay_hints/ra_fixture.rs +++ b/crates/ide/src/inlay_hints/ra_fixture.rs @@ -16,7 +16,7 @@ pub(super) fn hints( let file_id = file_id.file_id(sema.db); let literal = ast::String::cast(literal.token())?; let (analysis, fixture_analysis) = - Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)?; + Analysis::from_ra_fixture(sema, literal.clone(), &literal, &config.ra_fixture)?; for virtual_file_id in fixture_analysis.files() { acc.extend( analysis diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 81a771fec8..270998cdf7 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -63,15 +63,16 @@ use std::panic::{AssertUnwindSafe, UnwindSafe}; use cfg::CfgOptions; use fetch_crates::CrateInfo; use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym}; +use ide_db::base_db::relevant_crates; +use ide_db::ra_fixture::RaFixtureAnalysis; use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ - CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath, + CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath, salsa::{Cancelled, Database}, }, prime_caches, symbol_index, }; -use ide_db::{MiniCore, ra_fixture::RaFixtureAnalysis}; use macros::UpmapFromRaFixture; use syntax::{AstNode, SourceFile, ast}; use triomphe::Arc; @@ -96,7 +97,7 @@ pub use crate::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty, - LifetimeElisionHints, + LifetimeElisionHints, TypeHintsPlacement, }, join_lines::JoinLinesConfig, markup::Markup, @@ -135,6 +136,7 @@ pub use ide_db::{ label::Label, line_index::{LineCol, LineIndex}, prime_caches::ParallelPrimeCachesProgress, + ra_fixture::RaFixtureConfig, search::{ReferenceCategory, SearchScope}, source_change::{FileSystemEdit, SnippetEdit, SourceChange}, symbol_index::Query, @@ -289,9 +291,9 @@ impl Analysis { sema: &Semantics<'_, RootDatabase>, literal: ast::String, expanded: &ast::String, - minicore: MiniCore<'_>, + config: &RaFixtureConfig<'_>, ) -> Option<(Analysis, RaFixtureAnalysis)> { - Self::from_ra_fixture_with_on_cursor(sema, literal, expanded, minicore, &mut |_| {}) + Self::from_ra_fixture_with_on_cursor(sema, literal, expanded, config, &mut |_| {}) } /// Like [`Analysis::from_ra_fixture()`], but also calls `on_cursor` with the cursor position. @@ -299,11 +301,11 @@ impl Analysis { sema: &Semantics<'_, RootDatabase>, literal: ast::String, expanded: &ast::String, - minicore: MiniCore<'_>, + config: &RaFixtureConfig<'_>, on_cursor: &mut dyn FnMut(TextRange), ) -> Option<(Analysis, RaFixtureAnalysis)> { let analysis = - RaFixtureAnalysis::analyze_ra_fixture(sema, literal, expanded, minicore, on_cursor)?; + RaFixtureAnalysis::analyze_ra_fixture(sema, literal, expanded, config, on_cursor)?; Some((Analysis { db: analysis.db.clone() }, analysis)) } @@ -341,7 +343,7 @@ impl Analysis { self.with_db(|db| { let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); - db.parse(editioned_file_id_wrapper).tree() + editioned_file_id_wrapper.parse(db).tree() }) } @@ -369,7 +371,7 @@ impl Analysis { pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> { self.with_db(|db| { let file_id = EditionedFileId::current_edition(&self.db, position.file_id); - let parse = db.parse(file_id); + let parse = file_id.parse(db); let file = parse.tree(); matching_brace::matching_brace(&file, position.offset) }) @@ -412,7 +414,7 @@ impl Analysis { } /// Renders the crate graph to GraphViz "dot" syntax. - pub fn view_crate_graph(&self, full: bool) -> Cancellable<Result<String, String>> { + pub fn view_crate_graph(&self, full: bool) -> Cancellable<String> { self.with_db(|db| view_crate_graph::view_crate_graph(db, full)) } @@ -430,7 +432,7 @@ impl Analysis { self.with_db(|db| { let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, frange.file_id); - let parse = db.parse(editioned_file_id_wrapper); + let parse = editioned_file_id_wrapper.parse(db); join_lines::join_lines(config, &parse.tree(), frange.range) }) } @@ -471,7 +473,7 @@ impl Analysis { // FIXME: Edition self.with_db(|db| { let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); - let source_file = db.parse(editioned_file_id_wrapper).tree(); + let source_file = editioned_file_id_wrapper.parse(db).tree(); file_structure::file_structure(&source_file, config) }) } @@ -499,11 +501,14 @@ impl Analysis { } /// Returns the set of folding ranges. - pub fn folding_ranges(&self, file_id: FileId) -> Cancellable<Vec<Fold>> { + pub fn folding_ranges(&self, file_id: FileId, collapsed_text: bool) -> Cancellable<Vec<Fold>> { self.with_db(|db| { let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); - folding_ranges::folding_ranges(&db.parse(editioned_file_id_wrapper).tree()) + folding_ranges::folding_ranges( + &editioned_file_id_wrapper.parse(db).tree(), + collapsed_text, + ) }) } @@ -654,7 +659,7 @@ impl Analysis { /// Returns crates that this file *might* belong to. pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> { - self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect()) + self.with_db(|db| relevant_crates(db, file_id).to_vec()) } /// Returns the edition of the given crate. diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs index b2b91d6e3c..5079b0c4f9 100644 --- a/crates/ide/src/matching_brace.rs +++ b/crates/ide/src/matching_brace.rs @@ -17,25 +17,37 @@ use syntax::{ pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { const BRACES: &[SyntaxKind] = &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; - let (brace_token, brace_idx) = file - .syntax() - .token_at_offset(offset) + let current = file.syntax().token_at_offset(offset); + if let Some((brace_token, brace_idx)) = current + .clone() .filter_map(|node| { let idx = BRACES.iter().position(|&brace| brace == node.kind())?; Some((node, idx)) }) - .last()?; - let parent = brace_token.parent()?; - if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { - cov_mark::hit!(pipes_not_braces); - return None; + .last() + { + let parent = brace_token.parent()?; + if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { + cov_mark::hit!(pipes_not_braces); + return None; + } + let matching_kind = BRACES[brace_idx ^ 1]; + let matching_node = parent + .children_with_tokens() + .filter_map(|it| it.into_token()) + .find(|node| node.kind() == matching_kind && node != &brace_token)?; + Some(matching_node.text_range().start()) + } else { + // when the offset is not at a brace, find first parent + current.last()?.parent_ancestors().find_map(|x| { + x.children_with_tokens() + .filter_map(|it| it.into_token()) + // with ending brace + .filter(|node| BRACES.contains(&node.kind())) + .last() + .map(|x| x.text_range().start()) + }) } - let matching_kind = BRACES[brace_idx ^ 1]; - let matching_node = parent - .children_with_tokens() - .filter_map(|it| it.into_token()) - .find(|node| node.kind() == matching_kind && node != &brace_token)?; - Some(matching_node.text_range().start()) } #[cfg(test)] @@ -64,6 +76,14 @@ mod tests { "fn func(x) { return (2 * (x + 3)$0) + 5;}", "fn func(x) { return $0(2 * (x + 3)) + 5;}", ); + do_check( + "fn func(x) { return (2 * (x $0+ 3)) + 5;}", + "fn func(x) { return (2 * (x + 3$0)) + 5;}", + ); + do_check( + "fn func(x) { re$0turn (2 * (x + 3)) + 5;}", + "fn func(x) { return (2 * (x + 3)) + 5;$0}", + ); { cov_mark::check!(pipes_not_braces); diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 92020321f4..99f8634bcb 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -11,7 +11,7 @@ use hir::{ }; use ide_db::{ FileId, FileRange, RootDatabase, SymbolKind, - base_db::{CrateOrigin, LangCrateOrigin, RootQueryDb}, + base_db::{CrateOrigin, LangCrateOrigin, all_crates}, defs::{Definition, find_std_module}, documentation::{Documentation, HasDocs}, famous_defs::FamousDefs, @@ -861,8 +861,7 @@ impl TryToNav for hir::BuiltinType { sema: &Semantics<'_, RootDatabase>, ) -> Option<UpmappingResult<NavigationTarget>> { let db = sema.db; - let krate = db - .all_crates() + let krate = all_crates(db) .iter() .copied() .find(|&krate| matches!(krate.data(db).origin, CrateOrigin::Lang(LangCrateOrigin::Std))) diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 96d829d126..509ec2ab40 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs @@ -1,7 +1,7 @@ use hir::{Semantics, crate_def_map}; use ide_db::{ FileId, FilePosition, RootDatabase, - base_db::{Crate, RootQueryDb}, + base_db::{Crate, relevant_crates}, }; use itertools::Itertools; use syntax::{ @@ -53,7 +53,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na /// This returns `Vec` because a module may be included from several places. pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec<Crate> { - db.relevant_crates(file_id) + relevant_crates(db, file_id) .iter() .copied() .filter(|&crate_id| { diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 9392651c17..0288099bbc 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -19,10 +19,10 @@ use hir::{PathResolution, Semantics}; use ide_db::{ - FileId, MiniCore, RootDatabase, + FileId, RootDatabase, defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, - ra_fixture::UpmapFromRaFixture, + ra_fixture::{RaFixtureConfig, UpmapFromRaFixture}, search::{ReferenceCategory, SearchScope, UsageSearchResult}, }; use itertools::Itertools; @@ -90,7 +90,7 @@ pub struct Declaration { #[derive(Debug)] pub struct FindAllRefsConfig<'a> { pub search_scope: Option<SearchScope>, - pub minicore: MiniCore<'a>, + pub ra_fixture: RaFixtureConfig<'a>, } /// Find all references to the item at the given position. @@ -179,7 +179,7 @@ pub(crate) fn find_all_refs( if let Some(token) = syntax.token_at_offset(position.offset).left_biased() && let Some(token) = ast::String::cast(token.clone()) && let Some((analysis, fixture_analysis)) = - Analysis::from_ra_fixture(sema, token.clone(), &token, config.minicore) + Analysis::from_ra_fixture(sema, token.clone(), &token, &config.ra_fixture) && let Some((virtual_file_id, file_offset)) = fixture_analysis.map_offset_down(position.offset) { @@ -462,7 +462,7 @@ fn handle_control_flow_keywords( mod tests { use expect_test::{Expect, expect}; use hir::EditionedFileId; - use ide_db::{FileId, MiniCore, RootDatabase}; + use ide_db::{FileId, RootDatabase, ra_fixture::RaFixtureConfig}; use stdx::format_to; use crate::{SearchScope, fixture, references::FindAllRefsConfig}; @@ -1567,7 +1567,7 @@ fn main() { let (analysis, pos) = fixture::position(ra_fixture); let config = FindAllRefsConfig { search_scope: search_scope.map(|it| it(&analysis.db)), - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; let refs = analysis.find_all_refs(pos, &config).unwrap().unwrap(); diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index a0a6a24559..3b472390d2 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -3,12 +3,12 @@ use std::{fmt, sync::OnceLock}; use arrayvec::ArrayVec; use ast::HasName; use cfg::{CfgAtom, CfgExpr}; -use hir::{AsAssocItem, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase, sym}; +use hir::{AsAssocItem, HasAttrs, HasCrate, HasSource, Semantics, Symbol, sym}; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; +use ide_db::base_db::all_crates; use ide_db::impl_empty_upmap_from_ra_fixture; use ide_db::{ FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind, - base_db::RootQueryDb, defs::Definition, helpers::visit_file_defs, search::{FileReferenceNode, SearchScope}, @@ -55,7 +55,7 @@ impl fmt::Display for TestId { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum RunnableKind { TestMod { path: String }, - Test { test_id: TestId, attr: TestAttr }, + Test { test_id: TestId }, Bench { test_id: TestId }, DocTest { test_id: TestId }, Bin, @@ -334,8 +334,7 @@ pub(crate) fn runnable_fn( }; if def.is_test(sema.db) { - let attr = TestAttr::from_fn(sema.db, def); - RunnableKind::Test { test_id: test_id(), attr } + RunnableKind::Test { test_id: test_id() } } else if def.is_bench(sema.db) { RunnableKind::Bench { test_id: test_id() } } else { @@ -506,7 +505,7 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op let krate = def.krate(db); let edition = krate.map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); let display_target = krate - .unwrap_or_else(|| (*db.all_crates().last().expect("no crate graph present")).into()) + .unwrap_or_else(|| (*all_crates(db).last().expect("no crate graph present")).into()) .to_display_target(db); if !has_runnable_doc_test(db, &attrs) { return None; @@ -558,17 +557,6 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op Some(res) } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct TestAttr { - pub ignore: bool, -} - -impl TestAttr { - fn from_fn(db: &dyn HirDatabase, fn_def: hir::Function) -> TestAttr { - TestAttr { ignore: fn_def.is_ignore(db) } - } -} - fn has_runnable_doc_test(db: &RootDatabase, attrs: &hir::AttrsWithOwner) -> bool { const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 9eb01b12f2..cf796b2715 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -1975,8 +1975,8 @@ trait Sub: Super + Super { fn f() -> impl Sub<$0 "#, expect![[r#" - trait Sub<SuperTy = …, SubTy = …> - ^^^^^^^^^^^ --------- + trait Sub<SubTy = …, SuperTy = …> + ^^^^^^^^^ ----------- "#]], ); } diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index aba6b64f97..4b2c9ceef9 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -4,11 +4,12 @@ use arrayvec::ArrayVec; use hir::{Crate, Module, Semantics, db::HirDatabase}; use ide_db::{ - FileId, FileRange, FxHashMap, FxHashSet, MiniCore, RootDatabase, - base_db::{RootQueryDb, SourceDatabase, VfsPath}, + FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, + base_db::{SourceDatabase, VfsPath}, defs::{Definition, IdentClass}, documentation::Documentation, famous_defs::FamousDefs, + ra_fixture::RaFixtureConfig, }; use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange}; @@ -16,7 +17,7 @@ use crate::navigation_target::UpmappingResult; use crate::{ Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav, hover::{SubstTyLen, hover_for_definition}, - inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve}, + inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve, TypeHintsPlacement}, moniker::{MonikerResult, SymbolInformationKind, def_to_kind, def_to_moniker}, parent_module::crates_for, }; @@ -123,16 +124,8 @@ fn documentation_for_definition( _ => None, }; - def.docs( - sema.db, - famous_defs.as_ref(), - def.krate(sema.db) - .unwrap_or_else(|| { - (*sema.db.all_crates().last().expect("no crate graph present")).into() - }) - .to_display_target(sema.db), - ) - .map(Documentation::into_owned) + def.docs(sema.db, famous_defs.as_ref(), def.krate(sema.db)?.to_display_target(sema.db)) + .map(Documentation::into_owned) } // FIXME: This is a weird function @@ -159,7 +152,7 @@ pub enum VendoredLibrariesConfig<'a> { impl StaticIndex<'_> { fn add_file(&mut self, file_id: FileId) { let current_crate = crates_for(self.db, file_id).pop().map(Into::into); - let folds = self.analysis.folding_ranges(file_id).unwrap(); + let folds = self.analysis.folding_ranges(file_id, true).unwrap(); let inlay_hints = self .analysis .inlay_hints( @@ -167,6 +160,7 @@ impl StaticIndex<'_> { render_colons: true, discriminant_hints: crate::DiscriminantHints::Fieldless, type_hints: true, + type_hints_placement: TypeHintsPlacement::Inline, sized_bound: false, parameter_hints: true, parameter_hints_for_missing_arguments: false, @@ -196,7 +190,7 @@ impl StaticIndex<'_> { closing_brace_hints_min_lines: Some(25), fields_to_resolve: InlayFieldsToResolve::empty(), range_exclusive_hints: false, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }, file_id, None, @@ -225,7 +219,7 @@ impl StaticIndex<'_> { max_enum_variants_count: Some(5), max_subst_ty_len: SubstTyLen::Unlimited, show_drop_glue: true, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; let mut result = StaticIndexedFile { file_id, inlay_hints, folds, tokens: vec![] }; diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 217b13b4ef..9fd3f005ec 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -17,7 +17,7 @@ use either::Either; use hir::{ DefWithBody, EditionedFileId, ExpressionStoreOwner, InFile, InRealFile, MacroKind, Semantics, }; -use ide_db::{FxHashMap, FxHashSet, MiniCore, Ranker, RootDatabase, SymbolKind}; +use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind, ra_fixture::RaFixtureConfig}; use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::*, @@ -65,7 +65,7 @@ pub struct HighlightConfig<'a> { pub macro_bang: bool, /// Whether to highlight unresolved things be their syntax pub syntactic_name_ref_highlighting: bool, - pub minicore: MiniCore<'a>, + pub ra_fixture: RaFixtureConfig<'a>, } // Feature: Semantic Syntax Highlighting diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index 74567e8213..423c0c349c 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs @@ -1,7 +1,7 @@ //! Renders a bit of code as HTML. use hir::Semantics; -use ide_db::MiniCore; +use ide_db::ra_fixture::RaFixtureConfig; use oorandom::Rand32; use stdx::format_to; use syntax::AstNode; @@ -69,7 +69,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo inject_doc_comment: true, macro_bang: true, syntactic_name_ref_highlighting: false, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }, file_id, rainbow, diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 74a8d93dfe..6afe5681a9 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -27,7 +27,7 @@ pub(super) fn ra_fixture( sema, literal.clone(), expanded, - config.minicore, + &config.ra_fixture, &mut |range| { hl.add(HlRange { range, @@ -56,7 +56,7 @@ pub(super) fn ra_fixture( macro_bang: config.macro_bang, // What if there is a fixture inside a fixture? It's fixtures all the way down. // (In fact, we have a fixture inside a fixture in our test suite!) - minicore: config.minicore, + ra_fixture: config.ra_fixture, }, tmp_file_id, ) @@ -186,7 +186,7 @@ pub(super) fn doc_comment( specialize_operator: config.operator, inject_doc_comment: config.inject_doc_comment, macro_bang: config.macro_bang, - minicore: config.minicore, + ra_fixture: config.ra_fixture, }, tmp_file_id, None, diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index ce9ec7431a..dcfe4dd41e 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -166,12 +166,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="comment documentation">///</span> <span class="comment documentation">/// ```</span> <span class="comment documentation">///</span><span class="none injected"> </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="keyword attribute">cfg_attr</span><span class="parenthesis attribute">(</span>not<span class="parenthesis attribute">(</span>feature <span class="operator attribute">=</span> <span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute_bracket attribute">]</span> <span class="comment documentation">/// ```</span> <span class="comment documentation">///</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="keyword attribute">cfg_attr</span><span class="parenthesis attribute">(</span>feature <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="keyword attribute">cfg_attr</span><span class="parenthesis attribute">(</span>not<span class="parenthesis attribute">(</span>feature <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> <span class="comment documentation">///</span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> <span class="comment documentation">/// ```</span> <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index aecd1d3fdb..e8d185b7b6 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -1,7 +1,7 @@ use std::time::Instant; use expect_test::{ExpectFile, expect_file}; -use ide_db::{MiniCore, SymbolKind}; +use ide_db::{SymbolKind, ra_fixture::RaFixtureConfig}; use span::Edition; use test_utils::{AssertLinear, bench, bench_fixture, skip_slow_tests}; @@ -17,7 +17,7 @@ const HL_CONFIG: HighlightConfig<'_> = HighlightConfig { inject_doc_comment: true, macro_bang: true, syntactic_name_ref_highlighting: false, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; #[test] diff --git a/crates/ide/src/test_explorer.rs b/crates/ide/src/test_explorer.rs index 4792566f5f..02040ef138 100644 --- a/crates/ide/src/test_explorer.rs +++ b/crates/ide/src/test_explorer.rs @@ -1,8 +1,8 @@ //! Discovers tests use hir::{Crate, Module, ModuleDef, Semantics}; -use ide_db::base_db; -use ide_db::{FileId, RootDatabase, base_db::RootQueryDb}; +use ide_db::base_db::{self, all_crates}; +use ide_db::{FileId, RootDatabase}; use syntax::TextRange; use crate::{NavigationTarget, Runnable, TryToNav, runnables::runnable_fn}; @@ -26,7 +26,7 @@ pub struct TestItem { } pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> { - db.all_crates() + all_crates(db) .iter() .copied() .filter(|&id| id.data(db).origin.is_local()) @@ -48,7 +48,7 @@ pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> { fn find_crate_by_id(db: &RootDatabase, crate_id: &str) -> Option<base_db::Crate> { // here, we use display_name as the crate id. This is not super ideal, but it works since we // only show tests for the local crates. - db.all_crates().iter().copied().find(|&id| { + all_crates(db).iter().copied().find(|&id| { id.data(db).origin.is_local() && id.extra_data(db).display_name.as_ref().is_some_and(|x| x.to_string() == crate_id) }) diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index e8b0c92dcb..a49a85fe78 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs @@ -17,7 +17,7 @@ mod on_enter; use either::Either; use hir::EditionedFileId; -use ide_db::{FilePosition, RootDatabase, base_db::RootQueryDb}; +use ide_db::{FilePosition, RootDatabase, base_db::relevant_crates}; use span::Edition; use std::iter; @@ -70,13 +70,12 @@ pub(crate) fn on_char_typed( if !TRIGGER_CHARS.contains(&char_typed) { return None; } - let edition = db - .relevant_crates(position.file_id) + let edition = relevant_crates(db, position.file_id) .first() .copied() .map_or(Edition::CURRENT, |krate| krate.data(db).edition); let editioned_file_id_wrapper = EditionedFileId::new(db, position.file_id, edition); - let file = &db.parse(editioned_file_id_wrapper); + let file = &editioned_file_id_wrapper.parse(db); let char_matches_position = file.tree().syntax().text().char_at(position.offset) == Some(char_typed); if !stdx::always!(char_matches_position) { @@ -1240,12 +1239,6 @@ sdasdasdasdasd #[test] fn parenthesis_noop_in_item_position_with_macro() { type_char_noop('(', r#"$0println!();"#); - type_char_noop( - '(', - r#" -fn main() $0println!("hello"); -}"#, - ); } #[test] diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index fdc583a15c..7d04594a5b 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs @@ -1,13 +1,11 @@ -//! Handles the `Enter` key press. At the momently, this only continues -//! comments, but should handle indent some time in the future as well. +//! Handles the `Enter` key press, including comment continuation and +//! indentation in brace-delimited constructs. -use ide_db::base_db::RootQueryDb; use ide_db::{FilePosition, RootDatabase}; use syntax::{ AstNode, SmolStr, SourceFile, SyntaxKind::*, - SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, - algo::find_node_at_offset, + SyntaxToken, TextRange, TextSize, TokenAtOffset, ast::{self, AstToken, edit::IndentLevel}, }; @@ -20,7 +18,8 @@ use ide_db::text_edit::TextEdit; // - <kbd>Enter</kbd> inside triple-slash comments automatically inserts `///` // - <kbd>Enter</kbd> in the middle or after a trailing space in `//` inserts `//` // - <kbd>Enter</kbd> inside `//!` doc comments automatically inserts `//!` -// - <kbd>Enter</kbd> after `{` indents contents and closing `}` of single-line block +// - <kbd>Enter</kbd> after `{` reformats single-line brace-delimited contents by +// moving the text between `{` and the matching `}` onto an indented line // // This action needs to be assigned to shortcut explicitly. // @@ -52,7 +51,7 @@ use ide_db::text_edit::TextEdit; pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { let editioned_file_id_wrapper = ide_db::base_db::EditionedFileId::current_edition(db, position.file_id); - let parse = db.parse(editioned_file_id_wrapper); + let parse = editioned_file_id_wrapper.parse(db); let file = parse.tree(); let token = file.syntax().token_at_offset(position.offset).left_biased()?; @@ -60,22 +59,11 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text return on_enter_in_comment(&comment, &file, position.offset); } - if token.kind() == L_CURLY { - // Typing enter after the `{` of a block expression, where the `}` is on the same line - if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{')) - .and_then(|block| on_enter_in_block(block, position)) - { - cov_mark::hit!(indent_block_contents); - return Some(edit); - } - - // Typing enter after the `{` of a use tree list. - if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{')) - .and_then(|list| on_enter_in_use_tree_list(list, position)) - { - cov_mark::hit!(indent_block_contents); - return Some(edit); - } + if token.kind() == L_CURLY + && let Some(edit) = on_enter_in_braces(token, position) + { + cov_mark::hit!(indent_block_contents); + return Some(edit); } None @@ -120,44 +108,54 @@ fn on_enter_in_comment( Some(edit) } -fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<TextEdit> { - let contents = block_contents(&block)?; - - if block.syntax().text().contains_char('\n') { - return None; - } - - let indent = IndentLevel::from_node(block.syntax()); - let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1)); - edit.union(TextEdit::insert(contents.text_range().end(), format!("\n{indent}"))).ok()?; - Some(edit) -} - -fn on_enter_in_use_tree_list(list: ast::UseTreeList, position: FilePosition) -> Option<TextEdit> { - if list.syntax().text().contains_char('\n') { +fn on_enter_in_braces(l_curly: SyntaxToken, position: FilePosition) -> Option<TextEdit> { + if l_curly.text_range().end() != position.offset { return None; } - let indent = IndentLevel::from_node(list.syntax()); - let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1)); - edit.union(TextEdit::insert(list.r_curly_token()?.text_range().start(), format!("\n{indent}"))) - .ok()?; - Some(edit) + let (r_curly, content) = brace_contents_on_same_line(&l_curly)?; + let indent = IndentLevel::from_token(&l_curly); + Some(TextEdit::replace( + TextRange::new(position.offset, r_curly.text_range().start()), + format!("\n{}$0{}\n{indent}", indent + 1, content), + )) } -fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> { - let mut node = block.tail_expr().map(|e| e.syntax().clone()); +fn brace_contents_on_same_line(l_curly: &SyntaxToken) -> Option<(SyntaxToken, String)> { + let mut depth = 0_u32; + let mut tokens = Vec::new(); + let mut token = l_curly.next_token()?; - for stmt in block.statements() { - if node.is_some() { - // More than 1 node in the block + loop { + if token.kind() == WHITESPACE && token.text().contains('\n') { return None; } - node = Some(stmt.syntax().clone()); - } + match token.kind() { + L_CURLY => { + depth += 1; + tokens.push(token.clone()); + } + R_CURLY if depth == 0 => { + let first = tokens.iter().position(|it| it.kind() != WHITESPACE); + let last = tokens.iter().rposition(|it| it.kind() != WHITESPACE); + let content = match first.zip(last) { + Some((first, last)) => { + tokens[first..=last].iter().map(|it| it.text()).collect() + } + None => String::new(), + }; + return Some((token, content)); + } + R_CURLY => { + depth -= 1; + tokens.push(token.clone()); + } + _ => tokens.push(token.clone()), + } - node + token = token.next_token()?; + } } fn followed_by_comment(comment: &ast::Comment) -> bool { @@ -383,10 +381,58 @@ fn main() { } #[test] - fn indents_fn_body_block() { + fn indents_empty_brace_pairs() { cov_mark::check!(indent_block_contents); do_check( r#" +fn f() {$0} + "#, + r#" +fn f() { + $0 +} + "#, + ); + do_check( + r#" +fn f() { + let x = {$0}; +} + "#, + r#" +fn f() { + let x = { + $0 + }; +} + "#, + ); + do_check( + r#" +use crate::{$0}; + "#, + r#" +use crate::{ + $0 +}; + "#, + ); + do_check( + r#" +mod m {$0} + "#, + r#" +mod m { + $0 +} + "#, + ); + } + + #[test] + fn indents_fn_body_block() { + do_check( + r#" fn f() {$0()} "#, r#" @@ -478,29 +524,39 @@ fn f() { } #[test] - fn does_not_indent_empty_block() { - do_check_noop( + fn indents_block_with_multiple_statements() { + do_check( r#" -fn f() {$0} +fn f() {$0 a = b; ()} + "#, + r#" +fn f() { + $0a = b; () +} "#, ); - do_check_noop( + do_check( r#" -fn f() {{$0}} +fn f() {$0 a = b; a = b; } + "#, + r#" +fn f() { + $0a = b; a = b; +} "#, ); } #[test] - fn does_not_indent_block_with_too_much_content() { - do_check_noop( + fn trims_spaces_around_brace_contents() { + do_check( r#" -fn f() {$0 a = b; ()} +fn f() {$0 () } "#, - ); - do_check_noop( r#" -fn f() {$0 a = b; a = b; } +fn f() { + $0() +} "#, ); } @@ -571,6 +627,20 @@ use { } #[test] + fn indents_item_lists() { + do_check( + r#" +mod m {$0} + "#, + r#" +mod m { + $0 +} + "#, + ); + } + + #[test] fn does_not_indent_use_tree_list_when_not_at_curly_brace() { do_check_noop( r#" diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs index 25deffe10e..ecfdd09b24 100644 --- a/crates/ide/src/view_crate_graph.rs +++ b/crates/ide/src/view_crate_graph.rs @@ -1,10 +1,9 @@ use dot::{Id, LabelText}; +use ide_db::base_db::all_crates; use ide_db::base_db::salsa::plumbing::AsId; use ide_db::{ FxHashMap, RootDatabase, - base_db::{ - BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, RootQueryDb, SourceDatabase, - }, + base_db::{BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, SourceDatabase}, }; // Feature: View Crate Graph @@ -17,8 +16,8 @@ use ide_db::{ // | Editor | Action Name | // |---------|-------------| // | VS Code | **rust-analyzer: View Crate Graph** | -pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, String> { - let all_crates = db.all_crates(); +pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> String { + let all_crates = all_crates(db); let crates_to_render = all_crates .iter() .copied() @@ -37,7 +36,7 @@ pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, let mut dot = Vec::new(); dot::render(&graph, &mut dot).unwrap(); - Ok(String::from_utf8(dot).unwrap()) + String::from_utf8(dot).unwrap() } struct DotCrateGraph<'db> { diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 8753eab43a..68bf78e037 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -283,6 +283,19 @@ impl ProjectFolders { } } + // Collect workspace roots not already covered by a local PackageRoot + // (e.g. virtual workspaces where no package lives at the workspace root). + // We need these to load workspace-root rust-analyzer.toml into a local source root. + let uncovered_ws_roots: Vec<AbsPathBuf> = workspaces + .iter() + .filter_map(|ws| { + let ws_root = ws.workspace_root().to_path_buf(); + let dominated = + roots.iter().any(|root| root.is_local && root.include.contains(&ws_root)); + (!dominated).then_some(ws_root) + }) + .collect(); + for root in roots.into_iter().filter(|it| !it.include.is_empty()) { let file_set_roots: Vec<VfsPath> = root.include.iter().cloned().map(VfsPath::from).collect(); @@ -291,6 +304,7 @@ impl ProjectFolders { let mut dirs = vfs::loader::Directories::default(); dirs.extensions.push("rs".into()); dirs.extensions.push("toml".into()); + dirs.extensions.push("md".into()); dirs.include.extend(root.include); dirs.exclude.extend(root.exclude); for excl in global_excludes { @@ -335,6 +349,20 @@ impl ProjectFolders { } } + // For virtual workspaces, the workspace root has no local PackageRoot, so + // rust-analyzer.toml there would fall into a library source root and be + // ignored. Load it explicitly via Entry::Files and register the workspace + // root as a local file-set root so the file is classified as local. + for ws_root in &uncovered_ws_roots { + let ratoml_path = ws_root.join("rust-analyzer.toml"); + let file_set_roots = vec![VfsPath::from(ws_root.clone())]; + let entry = vfs::loader::Entry::Files(vec![ratoml_path]); + res.watch.push(res.load.len()); + res.load.push(entry); + local_filesets.push(fsc.len() as u64); + fsc.add_file_set(file_set_roots); + } + if let Some(user_config_path) = user_config_dir_path { let ratoml_path = { let mut p = user_config_path.to_path_buf(); @@ -738,7 +766,7 @@ fn resolve_sub_span( #[cfg(test)] mod tests { - use ide_db::base_db::RootQueryDb; + use ide_db::base_db::all_crates; use vfs::file_set::FileSetConfigBuilder; use super::*; @@ -766,7 +794,7 @@ mod tests { let (db, _vfs, _proc_macro) = load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config).unwrap(); - let n_crates = db.all_crates().len(); + let n_crates = all_crates(&db).len(); // RA has quite a few crates, but the exact count doesn't matter assert!(n_crates > 20); } diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index e481bbe9bc..1ff8a56b58 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs @@ -303,6 +303,18 @@ fn opt_ret_type(p: &mut Parser<'_>) -> bool { } } +fn opt_no_arrow_ret_type(p: &mut Parser<'_>) -> bool { + if p.at_ts(PATH_NAME_REF_KINDS) { + let m = p.start(); + p.error("missing thin-arrow `->`"); + types::type_no_bounds(p); + m.complete(p, RET_TYPE); + true + } else { + false + } +} + fn name_r(p: &mut Parser<'_>, recovery: TokenSet) { if p.at(IDENT) { let m = p.start(); diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs index c0cf43a87b..2eeaa25257 100644 --- a/crates/parser/src/grammar/attributes.rs +++ b/crates/parser/src/grammar/attributes.rs @@ -40,6 +40,86 @@ fn attr(p: &mut Parser<'_>, inner: bool) { // #![unsafe] // #![unsafe =] +fn cfg_attr_meta(p: &mut Parser<'_>, m: Marker) { + // test cfg_attr + // #![cfg_attr(not(foo), unsafe(bar()), cfg_attr(all(true, foo = "bar"), baz = "baz"))] + p.eat_contextual_kw(T![cfg_attr]); + p.bump(T!['(']); + cfg_predicate(p); + p.expect(T![,]); + while !p.at(T![')']) && !p.at(EOF) { + meta(p); + if !p.eat(T![,]) { + break; + } + } + p.expect(T![')']); + m.complete(p, CFG_ATTR_META); +} + +const CFG_PREDICATE_FIRST_SET: TokenSet = TokenSet::new(&[T![true], T![false], T![ident]]); + +fn cfg_predicate(p: &mut Parser<'_>) { + let m = p.start(); + if p.eat(T![true]) || p.eat(T![false]) { + // test cfg_true_false_pred + // #![cfg(true)] + // #![cfg(false)] + m.complete(p, CFG_ATOM); + return; + } + p.expect(T![ident]); + if p.eat(T![=]) { + if p.at(T![ident]) { + // This is required for completion, that inserts an identifier, to work in cases like + // `#[cfg(key = $0)]`, and also makes sense on itself. + + // test_err key_ident_cfg_predicate + // #![cfg(key = value)] + p.err_and_bump("expected a string literal"); + } else { + // test cfg_key_value_pred + // #![cfg(key = "value")] + p.expect(T![string]); + } + m.complete(p, CFG_ATOM); + } else if p.at(T!['(']) { + // test cfg_composite_pred + // #![cfg(any(a, all(b = "c", d)))] + delimited( + p, + T!['('], + T![')'], + T![,], + || "expected a cfg predicate".to_owned(), + CFG_PREDICATE_FIRST_SET, + |p| { + if p.at_ts(CFG_PREDICATE_FIRST_SET) { + cfg_predicate(p); + true + } else { + false + } + }, + ); + m.complete(p, CFG_COMPOSITE); + } else { + m.complete(p, CFG_ATOM); + } +} + +fn cfg_meta(p: &mut Parser<'_>, m: Marker) { + // test cfg_meta + // #![cfg(foo)] + // #![cfg(foo = "bar",)] + p.eat_contextual_kw(T![cfg]); + p.bump(T!['(']); + cfg_predicate(p); + p.eat(T![,]); + p.expect(T![')']); + m.complete(p, CFG_META); +} + // test metas // #![simple_ident] // #![simple::path] @@ -62,11 +142,23 @@ fn attr(p: &mut Parser<'_>, inner: bool) { // #![unsafe(simple::path::tt[a b c])] // #![unsafe(simple::path::tt{a b c})] pub(super) fn meta(p: &mut Parser<'_>) { - let meta = p.start(); - let is_unsafe = p.eat(T![unsafe]); - if is_unsafe { + let m = p.start(); + if p.eat(T![unsafe]) { p.expect(T!['(']); + meta(p); + p.expect(T![')']); + m.complete(p, UNSAFE_META); + return; + } + + if p.nth_at(1, T!['(']) { + if p.at_contextual_kw(T![cfg_attr]) { + return cfg_attr_meta(p, m); + } else if p.at_contextual_kw(T![cfg]) { + return cfg_meta(p, m); + } } + paths::attr_path(p); match p.current() { @@ -75,13 +167,14 @@ pub(super) fn meta(p: &mut Parser<'_>) { if expressions::expr(p).is_none() { p.error("expected expression"); } + m.complete(p, KEY_VALUE_META); + } + T!['('] | T!['['] | T!['{'] => { + items::token_tree(p); + m.complete(p, TOKEN_TREE_META); + } + _ => { + m.complete(p, PATH_META); } - T!['('] | T!['['] | T!['{'] => items::token_tree(p), - _ => {} - } - if is_unsafe { - p.expect(T![')']); } - - meta.complete(p, META); } diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs index c609f9383e..c0acdde2a7 100644 --- a/crates/parser/src/grammar/items.rs +++ b/crates/parser/src/grammar/items.rs @@ -422,7 +422,12 @@ fn fn_(p: &mut Parser<'_>, m: Marker) { // test function_ret_type // fn foo() {} // fn bar() -> () {} - opt_ret_type(p); + if !opt_ret_type(p) { + // test_err function_ret_type_missing_arrow + // fn foo() usize {} + // fn bar() super::Foo {} + opt_no_arrow_ret_type(p); + } // test_err fn_ret_recovery // fn foo() -> A>]) { let x = 1; } diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs index c62356d5c9..667bb68c64 100644 --- a/crates/parser/src/grammar/types.rs +++ b/crates/parser/src/grammar/types.rs @@ -59,6 +59,9 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) { } _ if paths::is_path_start(p) => path_or_macro_type(p, allow_bounds), LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p), + T!['{'] => { + p.err_recover("expected type, found `{`", TYPE_RECOVERY_SET); + } _ => { p.err_recover("expected type", TYPE_RECOVERY_SET); } diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index a2295e4495..9cd48f2aa4 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs @@ -116,6 +116,8 @@ pub enum SyntaxKind { AWAIT_KW, BIKESHED_KW, BUILTIN_KW, + CFG_ATTR_KW, + CFG_KW, CLOBBER_ABI_KW, DEFAULT_KW, DYN_KW, @@ -186,6 +188,10 @@ pub enum SyntaxKind { BREAK_EXPR, CALL_EXPR, CAST_EXPR, + CFG_ATOM, + CFG_ATTR_META, + CFG_COMPOSITE, + CFG_META, CLOSURE_EXPR, CONST, CONST_ARG, @@ -216,6 +222,7 @@ pub enum SyntaxKind { INDEX_EXPR, INFER_TYPE, ITEM_LIST, + KEY_VALUE_META, LABEL, LET_ELSE, LET_EXPR, @@ -238,7 +245,6 @@ pub enum SyntaxKind { MATCH_ARM_LIST, MATCH_EXPR, MATCH_GUARD, - META, METHOD_CALL_EXPR, MODULE, NAME, @@ -254,6 +260,7 @@ pub enum SyntaxKind { PAREN_TYPE, PATH, PATH_EXPR, + PATH_META, PATH_PAT, PATH_SEGMENT, PATH_TYPE, @@ -285,6 +292,7 @@ pub enum SyntaxKind { STMT_LIST, STRUCT, TOKEN_TREE, + TOKEN_TREE_META, TRAIT, TRY_BLOCK_MODIFIER, TRY_EXPR, @@ -302,6 +310,7 @@ pub enum SyntaxKind { TYPE_PARAM, UNDERSCORE_EXPR, UNION, + UNSAFE_META, USE, USE_BOUND_GENERIC_ARGS, USE_TREE, @@ -360,6 +369,10 @@ impl SyntaxKind { | BREAK_EXPR | CALL_EXPR | CAST_EXPR + | CFG_ATOM + | CFG_ATTR_META + | CFG_COMPOSITE + | CFG_META | CLOSURE_EXPR | CONST | CONST_ARG @@ -390,6 +403,7 @@ impl SyntaxKind { | INDEX_EXPR | INFER_TYPE | ITEM_LIST + | KEY_VALUE_META | LABEL | LET_ELSE | LET_EXPR @@ -412,7 +426,6 @@ impl SyntaxKind { | MATCH_ARM_LIST | MATCH_EXPR | MATCH_GUARD - | META | METHOD_CALL_EXPR | MODULE | NAME @@ -428,6 +441,7 @@ impl SyntaxKind { | PAREN_TYPE | PATH | PATH_EXPR + | PATH_META | PATH_PAT | PATH_SEGMENT | PATH_TYPE @@ -459,6 +473,7 @@ impl SyntaxKind { | STMT_LIST | STRUCT | TOKEN_TREE + | TOKEN_TREE_META | TRAIT | TRY_BLOCK_MODIFIER | TRY_EXPR @@ -476,6 +491,7 @@ impl SyntaxKind { | TYPE_PARAM | UNDERSCORE_EXPR | UNION + | UNSAFE_META | USE | USE_BOUND_GENERIC_ARGS | USE_TREE @@ -601,6 +617,8 @@ impl SyntaxKind { AUTO_KW => "auto", BIKESHED_KW => "bikeshed", BUILTIN_KW => "builtin", + CFG_KW => "cfg", + CFG_ATTR_KW => "cfg_attr", CLOBBER_ABI_KW => "clobber_abi", DEFAULT_KW => "default", DYN_KW => "dyn", @@ -704,6 +722,8 @@ impl SyntaxKind { AUTO_KW => true, BIKESHED_KW => true, BUILTIN_KW => true, + CFG_KW => true, + CFG_ATTR_KW => true, CLOBBER_ABI_KW => true, DEFAULT_KW => true, DYN_KW if edition < Edition::Edition2018 => true, @@ -795,6 +815,8 @@ impl SyntaxKind { AUTO_KW => true, BIKESHED_KW => true, BUILTIN_KW => true, + CFG_KW => true, + CFG_ATTR_KW => true, CLOBBER_ABI_KW => true, DEFAULT_KW => true, DYN_KW if edition < Edition::Edition2018 => true, @@ -949,6 +971,8 @@ impl SyntaxKind { "auto" => AUTO_KW, "bikeshed" => BIKESHED_KW, "builtin" => BUILTIN_KW, + "cfg" => CFG_KW, + "cfg_attr" => CFG_ATTR_KW, "clobber_abi" => CLOBBER_ABI_KW, "default" => DEFAULT_KW, "dyn" if edition < Edition::Edition2018 => DYN_KW, @@ -1121,6 +1145,8 @@ macro_rules ! T_ { [auto] => { $ crate :: SyntaxKind :: AUTO_KW }; [bikeshed] => { $ crate :: SyntaxKind :: BIKESHED_KW }; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW }; + [cfg] => { $ crate :: SyntaxKind :: CFG_KW }; + [cfg_attr] => { $ crate :: SyntaxKind :: CFG_ATTR_KW }; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW }; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW }; [dyn] => { $ crate :: SyntaxKind :: DYN_KW }; diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index 4c001104fe..71978390df 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs @@ -87,6 +87,22 @@ mod ok { #[test] fn cast_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/cast_expr.rs"); } #[test] + fn cfg_attr() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_attr.rs"); } + #[test] + fn cfg_composite_pred() { + run_and_expect_no_errors("test_data/parser/inline/ok/cfg_composite_pred.rs"); + } + #[test] + fn cfg_key_value_pred() { + run_and_expect_no_errors("test_data/parser/inline/ok/cfg_key_value_pred.rs"); + } + #[test] + fn cfg_meta() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_meta.rs"); } + #[test] + fn cfg_true_false_pred() { + run_and_expect_no_errors("test_data/parser/inline/ok/cfg_true_false_pred.rs"); + } + #[test] fn closure_binder() { run_and_expect_no_errors("test_data/parser/inline/ok/closure_binder.rs"); } @@ -793,6 +809,10 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/fn_ret_recovery.rs"); } #[test] + fn function_ret_type_missing_arrow() { + run_and_expect_errors("test_data/parser/inline/err/function_ret_type_missing_arrow.rs"); + } + #[test] fn gen_fn() { run_and_expect_errors_with_edition( "test_data/parser/inline/err/gen_fn.rs", @@ -822,6 +842,10 @@ mod err { ); } #[test] + fn key_ident_cfg_predicate() { + run_and_expect_errors("test_data/parser/inline/err/key_ident_cfg_predicate.rs"); + } + #[test] fn let_else_right_curly_brace() { run_and_expect_errors("test_data/parser/inline/err/let_else_right_curly_brace.rs"); } diff --git a/crates/parser/test_data/parser/err/0005_attribute_recover.rast b/crates/parser/test_data/parser/err/0005_attribute_recover.rast index 77b4d06321..cf45dcf522 100644 --- a/crates/parser/test_data/parser/err/0005_attribute_recover.rast +++ b/crates/parser/test_data/parser/err/0005_attribute_recover.rast @@ -3,7 +3,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -37,7 +37,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/err/0025_nope.rast b/crates/parser/test_data/parser/err/0025_nope.rast index b6bc008837..23964ab9d9 100644 --- a/crates/parser/test_data/parser/err/0025_nope.rast +++ b/crates/parser/test_data/parser/err/0025_nope.rast @@ -194,7 +194,7 @@ SOURCE_FILE WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" -error 95: expected type +error 95: expected type, found `{` error 95: expected COMMA error 96: expected field error 98: expected field declaration diff --git a/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast b/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast index 3768a55d53..31db794d9f 100644 --- a/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast +++ b/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast @@ -26,5 +26,5 @@ SOURCE_FILE L_CURLY "{" R_CURLY "}" WHITESPACE "\n" -error 26: expected type +error 26: expected type, found `{` error 26: expected colon diff --git a/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast index b657e98341..2334b730e4 100644 --- a/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast +++ b/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast @@ -136,15 +136,12 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "test" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE "\n " ATTR diff --git a/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast b/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast index b5bc3d84df..acacee2348 100644 --- a/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast +++ b/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast @@ -48,15 +48,12 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "test" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE "\n " R_CURLY "}" diff --git a/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast new file mode 100644 index 0000000000..c0bca6ed1c --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast @@ -0,0 +1,50 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "usize" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "bar" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + PATH_TYPE + PATH + PATH + PATH_SEGMENT + NAME_REF + SUPER_KW "super" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "Foo" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 9: missing thin-arrow `->` +error 27: missing thin-arrow `->` diff --git a/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs new file mode 100644 index 0000000000..f48e539df5 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs @@ -0,0 +1,2 @@ +fn foo() usize {} +fn bar() super::Foo {} diff --git a/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast new file mode 100644 index 0000000000..de5fc7d5bd --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast @@ -0,0 +1,19 @@ +SOURCE_FILE + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM + IDENT "key" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ERROR + IDENT "value" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" +error 13: expected a string literal diff --git a/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs new file mode 100644 index 0000000000..9a981bf939 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs @@ -0,0 +1 @@ +#![cfg(key = value)] diff --git a/crates/parser/test_data/parser/inline/err/meta_recovery.rast b/crates/parser/test_data/parser/inline/err/meta_recovery.rast index b5c16e0798..9e456c9855 100644 --- a/crates/parser/test_data/parser/inline/err/meta_recovery.rast +++ b/crates/parser/test_data/parser/inline/err/meta_recovery.rast @@ -3,14 +3,14 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + PATH_META R_BRACK "]" WHITESPACE "\n" ATTR POUND "#" BANG "!" L_BRACK "[" - META + KEY_VALUE_META PATH PATH_SEGMENT NAME_REF @@ -24,7 +24,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + PATH_META PATH PATH PATH_SEGMENT @@ -37,7 +37,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + KEY_VALUE_META PATH PATH PATH_SEGMENT @@ -52,18 +52,20 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" + PATH_META R_BRACK "]" WHITESPACE "\n" ATTR POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" WHITESPACE " " - EQ "=" + KEY_VALUE_META + EQ "=" R_BRACK "]" WHITESPACE "\n" error 3: expected identifier, `self`, `super`, `crate`, or `Self` @@ -77,7 +79,7 @@ error 41: expected L_PAREN error 41: expected identifier, `self`, `super`, `crate`, or `Self` error 41: expected R_PAREN error 52: expected L_PAREN -error 52: expected identifier, `self`, `super`, `crate`, or `Self` +error 53: expected identifier, `self`, `super`, `crate`, or `Self` error 54: expected expression error 54: expected expression error 54: expected R_PAREN diff --git a/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast b/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast index ae1074c368..672f2c2f7f 100644 --- a/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast +++ b/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast @@ -24,7 +24,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/array_attrs.rast b/crates/parser/test_data/parser/inline/ok/array_attrs.rast index 6eb8af3311..2812bbf71b 100644 --- a/crates/parser/test_data/parser/inline/ok/array_attrs.rast +++ b/crates/parser/test_data/parser/inline/ok/array_attrs.rast @@ -31,15 +31,12 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "test" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE " " INT_NUMBER "2" diff --git a/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast b/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast index 9cb3c8a5c3..7c6dbf65cf 100644 --- a/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast +++ b/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast @@ -15,7 +15,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast b/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast index 81b7f2b3cb..248e6d1360 100644 --- a/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast +++ b/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast @@ -17,7 +17,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -39,7 +39,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -61,7 +61,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -71,7 +71,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -87,7 +87,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/cfg_attr.rast b/crates/parser/test_data/parser/inline/ok/cfg_attr.rast new file mode 100644 index 0000000000..9af94f447d --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_attr.rast @@ -0,0 +1,63 @@ +SOURCE_FILE + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + CFG_ATTR_META + CFG_ATTR_KW "cfg_attr" + L_PAREN "(" + CFG_COMPOSITE + IDENT "not" + L_PAREN "(" + CFG_ATOM + IDENT "foo" + R_PAREN ")" + COMMA "," + WHITESPACE " " + UNSAFE_META + UNSAFE_KW "unsafe" + L_PAREN "(" + TOKEN_TREE_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "bar" + TOKEN_TREE + L_PAREN "(" + R_PAREN ")" + R_PAREN ")" + COMMA "," + WHITESPACE " " + CFG_ATTR_META + CFG_ATTR_KW "cfg_attr" + L_PAREN "(" + CFG_COMPOSITE + IDENT "all" + L_PAREN "(" + CFG_ATOM + TRUE_KW "true" + COMMA "," + WHITESPACE " " + CFG_ATOM + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + STRING "\"bar\"" + R_PAREN ")" + COMMA "," + WHITESPACE " " + KEY_VALUE_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "baz" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"baz\"" + R_PAREN ")" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/cfg_attr.rs b/crates/parser/test_data/parser/inline/ok/cfg_attr.rs new file mode 100644 index 0000000000..5fe2776144 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_attr.rs @@ -0,0 +1 @@ +#![cfg_attr(not(foo), unsafe(bar()), cfg_attr(all(true, foo = "bar"), baz = "baz"))] diff --git a/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast new file mode 100644 index 0000000000..89d06d134f --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast @@ -0,0 +1,33 @@ +SOURCE_FILE + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_COMPOSITE + IDENT "any" + L_PAREN "(" + CFG_ATOM + IDENT "a" + COMMA "," + WHITESPACE " " + CFG_COMPOSITE + IDENT "all" + L_PAREN "(" + CFG_ATOM + IDENT "b" + WHITESPACE " " + EQ "=" + WHITESPACE " " + STRING "\"c\"" + COMMA "," + WHITESPACE " " + CFG_ATOM + IDENT "d" + R_PAREN ")" + R_PAREN ")" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs new file mode 100644 index 0000000000..7d830c1288 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs @@ -0,0 +1 @@ +#![cfg(any(a, all(b = "c", d)))] diff --git a/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast new file mode 100644 index 0000000000..e48d39bf55 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast @@ -0,0 +1,17 @@ +SOURCE_FILE + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM + IDENT "key" + WHITESPACE " " + EQ "=" + WHITESPACE " " + STRING "\"value\"" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs new file mode 100644 index 0000000000..dc194ed86b --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs @@ -0,0 +1 @@ +#![cfg(key = "value")] diff --git a/crates/parser/test_data/parser/inline/ok/cfg_meta.rast b/crates/parser/test_data/parser/inline/ok/cfg_meta.rast new file mode 100644 index 0000000000..f024cfd1aa --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_meta.rast @@ -0,0 +1,30 @@ +SOURCE_FILE + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM + IDENT "foo" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + STRING "\"bar\"" + COMMA "," + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/cfg_meta.rs b/crates/parser/test_data/parser/inline/ok/cfg_meta.rs new file mode 100644 index 0000000000..ef0030e75f --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_meta.rs @@ -0,0 +1,2 @@ +#![cfg(foo)] +#![cfg(foo = "bar",)] diff --git a/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast new file mode 100644 index 0000000000..e33595a93d --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast @@ -0,0 +1,25 @@ +SOURCE_FILE + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM + TRUE_KW "true" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM + FALSE_KW "false" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs new file mode 100644 index 0000000000..473582164a --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs @@ -0,0 +1,2 @@ +#![cfg(true)] +#![cfg(false)] diff --git a/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast b/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast index 28a216e873..5567a53c56 100644 --- a/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast +++ b/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast @@ -10,7 +10,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -25,7 +25,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast b/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast index 6fd9f42467..edb0438733 100644 --- a/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast +++ b/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast @@ -26,7 +26,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -41,7 +41,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -56,7 +56,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast b/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast index 0f7580c1a3..321db782d1 100644 --- a/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast +++ b/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast @@ -26,19 +26,16 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "feature" WHITESPACE " " EQ "=" WHITESPACE " " STRING "\"some\"" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE "\n " WILDCARD_PAT @@ -55,19 +52,16 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "feature" WHITESPACE " " EQ "=" WHITESPACE " " STRING "\"other\"" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE "\n " WILDCARD_PAT @@ -84,55 +78,46 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "feature" WHITESPACE " " EQ "=" WHITESPACE " " STRING "\"many\"" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE "\n " ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "feature" WHITESPACE " " EQ "=" WHITESPACE " " STRING "\"attributes\"" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE "\n " ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "feature" WHITESPACE " " EQ "=" WHITESPACE " " STRING "\"before\"" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE "\n " WILDCARD_PAT diff --git a/crates/parser/test_data/parser/inline/ok/metas.rast b/crates/parser/test_data/parser/inline/ok/metas.rast index b1ac60b530..6360552a6f 100644 --- a/crates/parser/test_data/parser/inline/ok/metas.rast +++ b/crates/parser/test_data/parser/inline/ok/metas.rast @@ -3,7 +3,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -14,7 +14,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + PATH_META PATH PATH PATH_SEGMENT @@ -30,7 +30,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + KEY_VALUE_META PATH PATH_SEGMENT NAME_REF @@ -46,7 +46,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + KEY_VALUE_META PATH PATH PATH @@ -72,7 +72,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -91,7 +91,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -110,7 +110,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -129,7 +129,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH PATH @@ -158,7 +158,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH PATH @@ -187,7 +187,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH PATH @@ -216,13 +216,14 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - IDENT "simple_ident" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" @@ -230,18 +231,19 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH + PATH_META PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "simple" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "path" + IDENT "path" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" @@ -249,18 +251,19 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - IDENT "simple_ident_expr" - WHITESPACE " " - EQ "=" - WHITESPACE " " - LITERAL - STRING "\"\"" + KEY_VALUE_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" @@ -268,28 +271,29 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH + KEY_VALUE_META PATH PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "simple" + IDENT "path" COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "path" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "Expr" - WHITESPACE " " - EQ "=" - WHITESPACE " " - LITERAL - STRING "\"\"" + IDENT "Expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" @@ -297,21 +301,22 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - IDENT "simple_ident_tt" - TOKEN_TREE - L_PAREN "(" - IDENT "a" - WHITESPACE " " - IDENT "b" - WHITESPACE " " - IDENT "c" - R_PAREN ")" + TOKEN_TREE_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" @@ -319,21 +324,22 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - IDENT "simple_ident_tt" - TOKEN_TREE - L_BRACK "[" - IDENT "a" - WHITESPACE " " - IDENT "b" - WHITESPACE " " - IDENT "c" - R_BRACK "]" + TOKEN_TREE_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" @@ -341,21 +347,22 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - IDENT "simple_ident_tt" - TOKEN_TREE - L_CURLY "{" - IDENT "a" - WHITESPACE " " - IDENT "b" - WHITESPACE " " - IDENT "c" - R_CURLY "}" + TOKEN_TREE_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" @@ -363,31 +370,32 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH + TOKEN_TREE_META PATH PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "simple" + IDENT "path" COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "path" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "tt" - TOKEN_TREE - L_PAREN "(" - IDENT "a" - WHITESPACE " " - IDENT "b" - WHITESPACE " " - IDENT "c" - R_PAREN ")" + IDENT "tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" @@ -395,31 +403,32 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH + TOKEN_TREE_META PATH PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "simple" + IDENT "path" COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "path" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "tt" - TOKEN_TREE - L_BRACK "[" - IDENT "a" - WHITESPACE " " - IDENT "b" - WHITESPACE " " - IDENT "c" - R_BRACK "]" + IDENT "tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" @@ -427,31 +436,32 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + UNSAFE_META UNSAFE_KW "unsafe" L_PAREN "(" - PATH + TOKEN_TREE_META PATH PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "simple" + IDENT "path" COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "path" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "tt" - TOKEN_TREE - L_CURLY "{" - IDENT "a" - WHITESPACE " " - IDENT "b" - WHITESPACE " " - IDENT "c" - R_CURLY "}" + IDENT "tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" R_PAREN ")" R_BRACK "]" WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast b/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast index c63ea020a3..7495ba7b31 100644 --- a/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast +++ b/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast @@ -10,7 +10,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast b/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast index 639ee0eb77..cfa2694fd1 100644 --- a/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast +++ b/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast @@ -12,7 +12,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast b/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast index a1df70841e..717dee8c9f 100644 --- a/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast +++ b/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast @@ -25,15 +25,12 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "test" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE " " NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/record_pat_field.rast b/crates/parser/test_data/parser/inline/ok/record_pat_field.rast index f3d2fde466..7fba529be9 100644 --- a/crates/parser/test_data/parser/inline/ok/record_pat_field.rast +++ b/crates/parser/test_data/parser/inline/ok/record_pat_field.rast @@ -88,18 +88,14 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_COMPOSITE IDENT "any" - TOKEN_TREE - L_PAREN "(" - R_PAREN ")" + L_PAREN "(" R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE " " NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast b/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast index f69ae1d644..af5b82b889 100644 --- a/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast +++ b/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast @@ -146,18 +146,14 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_COMPOSITE IDENT "any" - TOKEN_TREE - L_PAREN "(" - R_PAREN ")" + L_PAREN "(" R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE " " DOT2 ".." diff --git a/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast b/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast index db583f7d52..3a163e5b8e 100644 --- a/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast +++ b/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast @@ -10,7 +10,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast b/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast index 39857b23c6..76954927d5 100644 --- a/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast +++ b/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast @@ -34,15 +34,12 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "test" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE " " INT_NUMBER "2" diff --git a/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast b/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast index 1699602f4f..1f7100c46d 100644 --- a/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast +++ b/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast @@ -11,7 +11,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0006_inner_attributes.rast b/crates/parser/test_data/parser/ok/0006_inner_attributes.rast index cb63ba80e7..ddab028c06 100644 --- a/crates/parser/test_data/parser/ok/0006_inner_attributes.rast +++ b/crates/parser/test_data/parser/ok/0006_inner_attributes.rast @@ -3,7 +3,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -14,7 +14,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -29,7 +29,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -44,7 +44,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -89,7 +89,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -104,7 +104,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -123,7 +123,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -138,7 +138,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -153,7 +153,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -175,7 +175,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0008_mod_item.rast b/crates/parser/test_data/parser/ok/0008_mod_item.rast index adee67181b..cb6da8717d 100644 --- a/crates/parser/test_data/parser/ok/0008_mod_item.rast +++ b/crates/parser/test_data/parser/ok/0008_mod_item.rast @@ -48,7 +48,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0011_outer_attribute.rast b/crates/parser/test_data/parser/ok/0011_outer_attribute.rast index dbb9bc54da..47a5cfce08 100644 --- a/crates/parser/test_data/parser/ok/0011_outer_attribute.rast +++ b/crates/parser/test_data/parser/ok/0011_outer_attribute.rast @@ -3,21 +3,18 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "test" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE "\n" ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -41,7 +38,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + KEY_VALUE_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast b/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast index 7c914e2542..c5d054702f 100644 --- a/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast +++ b/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast @@ -3,7 +3,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0035_weird_exprs.rast b/crates/parser/test_data/parser/ok/0035_weird_exprs.rast index 318d492ab4..15ce6c70be 100644 --- a/crates/parser/test_data/parser/ok/0035_weird_exprs.rast +++ b/crates/parser/test_data/parser/ok/0035_weird_exprs.rast @@ -11,7 +11,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -26,7 +26,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -41,7 +41,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -56,7 +56,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -71,7 +71,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + KEY_VALUE_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0044_let_attrs.rast b/crates/parser/test_data/parser/ok/0044_let_attrs.rast index f3c20337e4..fcdc1a9895 100644 --- a/crates/parser/test_data/parser/ok/0044_let_attrs.rast +++ b/crates/parser/test_data/parser/ok/0044_let_attrs.rast @@ -18,19 +18,16 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "feature" WHITESPACE " " EQ "=" WHITESPACE " " STRING "\"backtrace\"" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE "\n " LET_KW "let" diff --git a/crates/parser/test_data/parser/ok/0045_block_attrs.rast b/crates/parser/test_data/parser/ok/0045_block_attrs.rast index c22d99f1ae..f26bb85df2 100644 --- a/crates/parser/test_data/parser/ok/0045_block_attrs.rast +++ b/crates/parser/test_data/parser/ok/0045_block_attrs.rast @@ -16,7 +16,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -38,7 +38,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -53,7 +53,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -77,7 +77,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -119,7 +119,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF @@ -211,7 +211,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast b/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast index 4eb51cfdf0..3d33eb4ff7 100644 --- a/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast +++ b/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast @@ -14,7 +14,7 @@ SOURCE_FILE POUND "#" BANG "!" L_BRACK "[" - META + TOKEN_TREE_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast index eafee90db4..24d4392282 100644 --- a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast +++ b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast @@ -10,7 +10,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -20,7 +20,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -55,7 +55,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -116,7 +116,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -158,7 +158,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -181,7 +181,7 @@ SOURCE_FILE POUND "#" WHITESPACE " " L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -228,7 +228,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -255,7 +255,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -282,7 +282,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -316,7 +316,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -352,7 +352,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -389,7 +389,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -422,7 +422,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -456,7 +456,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast b/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast index b94d43beb3..c300b7af50 100644 --- a/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast +++ b/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast @@ -5,7 +5,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0062_macro_2.0.rast b/crates/parser/test_data/parser/ok/0062_macro_2.0.rast index 1415a866b6..b92d78e5bd 100644 --- a/crates/parser/test_data/parser/ok/0062_macro_2.0.rast +++ b/crates/parser/test_data/parser/ok/0062_macro_2.0.rast @@ -54,7 +54,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0063_variadic_fun.rast b/crates/parser/test_data/parser/ok/0063_variadic_fun.rast index e36399123b..f1c6d2efeb 100644 --- a/crates/parser/test_data/parser/ok/0063_variadic_fun.rast +++ b/crates/parser/test_data/parser/ok/0063_variadic_fun.rast @@ -96,15 +96,12 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META - PATH - PATH_SEGMENT - NAME_REF - IDENT "cfg" - TOKEN_TREE - L_PAREN "(" + CFG_META + CFG_KW "cfg" + L_PAREN "(" + CFG_ATOM IDENT "never" - R_PAREN ")" + R_PAREN ")" R_BRACK "]" WHITESPACE " " SLICE_PAT diff --git a/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast b/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast index 3d00b27ab8..5229b97eb2 100644 --- a/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast +++ b/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast @@ -19,7 +19,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -39,7 +39,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast b/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast index 1cafc775cd..c0685448f2 100644 --- a/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast +++ b/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast @@ -17,7 +17,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -31,7 +31,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF @@ -56,7 +56,7 @@ SOURCE_FILE ATTR POUND "#" L_BRACK "[" - META + PATH_META PATH PATH_SEGMENT NAME_REF diff --git a/crates/proc-macro-srv/src/dylib/proc_macros.rs b/crates/proc-macro-srv/src/dylib/proc_macros.rs index 4065dbd0b4..cf00be0327 100644 --- a/crates/proc-macro-srv/src/dylib/proc_macros.rs +++ b/crates/proc-macro-srv/src/dylib/proc_macros.rs @@ -7,7 +7,7 @@ pub(crate) struct ProcMacros([bridge::client::ProcMacro]); impl From<bridge::PanicMessage> for crate::PanicMessage { fn from(p: bridge::PanicMessage) -> Self { - Self { message: p.as_str().map(|s| s.to_owned()) } + Self { message: p.into_string() } } } diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 4828419003..8377e94c8d 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -16,8 +16,8 @@ doctest = false cfg-if = "1.0.1" jemalloc-ctl = { version = "0.5.4", package = "tikv-jemalloc-ctl", optional = true } -[target.'cfg(all(target_os = "linux", not(target_env = "ohos")))'.dependencies] -perf-event = "=0.4.7" +[target.'cfg(all(target_os = "linux", not(target_env = "ohos"), any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))'.dependencies] +perf-event = "=0.4.8" [target.'cfg(all(target_os = "linux", target_env = "gnu"))'.dependencies] libc.workspace = true diff --git a/crates/profile/src/stop_watch.rs b/crates/profile/src/stop_watch.rs index 00c37c01d2..a1c1383ad5 100644 --- a/crates/profile/src/stop_watch.rs +++ b/crates/profile/src/stop_watch.rs @@ -11,7 +11,11 @@ use crate::MemoryUsage; pub struct StopWatch { time: Instant, - #[cfg(all(target_os = "linux", not(target_env = "ohos")))] + #[cfg(all( + target_os = "linux", + not(target_env = "ohos"), + any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64") + ))] counter: Option<perf_event::Counter>, memory: MemoryUsage, } @@ -24,7 +28,11 @@ pub struct StopWatchSpan { impl StopWatch { pub fn start() -> StopWatch { - #[cfg(all(target_os = "linux", not(target_env = "ohos")))] + #[cfg(all( + target_os = "linux", + not(target_env = "ohos"), + any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64") + ))] let counter = { // When debugging rust-analyzer using rr, the perf-related syscalls cause it to abort. // We allow disabling perf by setting the env var `RA_DISABLE_PERF`. @@ -51,7 +59,11 @@ impl StopWatch { let time = Instant::now(); StopWatch { time, - #[cfg(all(target_os = "linux", not(target_env = "ohos")))] + #[cfg(all( + target_os = "linux", + not(target_env = "ohos"), + any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64") + ))] counter, memory, } @@ -60,13 +72,19 @@ impl StopWatch { pub fn elapsed(&mut self) -> StopWatchSpan { let time = self.time.elapsed(); - #[cfg(all(target_os = "linux", not(target_env = "ohos")))] + #[cfg(all( + target_os = "linux", + not(target_env = "ohos"), + any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64") + ))] let instructions = self.counter.as_mut().and_then(|it| { it.read().map_err(|err| eprintln!("Failed to read perf counter: {err}")).ok() }); - #[cfg(all(target_os = "linux", target_env = "ohos"))] - let instructions = None; - #[cfg(not(target_os = "linux"))] + #[cfg(not(all( + target_os = "linux", + not(target_env = "ohos"), + any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64") + )))] let instructions = None; let memory = MemoryUsage::now() - self.memory; diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 792206b74f..5d8273832b 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -131,6 +131,8 @@ pub struct CargoConfig { pub run_build_script_command: Option<Vec<String>>, /// Extra args to pass to the cargo command. pub extra_args: Vec<String>, + /// Extra args passed only to `cargo metadata`, not other cargo commands. + pub metadata_extra_args: Vec<String>, /// Extra env vars to set when invoking the cargo command pub extra_env: FxHashMap<String, Option<String>>, pub invocation_strategy: InvocationStrategy, @@ -320,6 +322,8 @@ pub struct CargoMetadataConfig { pub targets: Vec<String>, /// Extra args to pass to the cargo command. pub extra_args: Vec<String>, + /// Extra args passed directly to `cargo metadata` without filtering. + pub metadata_extra_args: Vec<String>, /// Extra env vars to set when invoking the cargo command pub extra_env: FxHashMap<String, Option<String>>, /// What kind of metadata are we fetching: workspace, rustc, or sysroot. @@ -679,6 +683,7 @@ impl FetchMetadata { other_options.push(arg.to_owned()); } } + other_options.extend(config.metadata_extra_args.iter().cloned()); let mut lockfile_copy = None; if cargo_toml.is_rust_manifest() { diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs index 51c447945c..ab45917a56 100644 --- a/crates/project-model/src/env.rs +++ b/crates/project-model/src/env.rs @@ -79,18 +79,31 @@ pub(crate) fn cargo_config_env( for (key, entry) in env_toml { let key = key.as_ref().as_ref(); let value = match entry.as_ref() { - DeValue::String(s) => String::from(s.clone()), + DeValue::String(s) => { + // Plain string entries have no `force` option, so they should not + // override existing environment variables (matching Cargo behavior). + if extra_env.get(key).is_some_and(Option::is_some) { + continue; + } + if let Ok(val) = std::env::var(key) { val } else { String::from(s.clone()) } + } DeValue::Table(entry) => { // Each entry MUST have a `value` key. let Some(map) = entry.get("value").and_then(|v| v.as_ref().as_str()) else { continue; }; - // If the entry already exists in the environment AND the `force` key is not set to - // true, then don't overwrite the value. - if extra_env.get(key).is_some_and(Option::is_some) - && !entry.get("force").and_then(|v| v.as_ref().as_bool()).unwrap_or(false) - { - continue; + let is_forced = + entry.get("force").and_then(|v| v.as_ref().as_bool()).unwrap_or(false); + // If the entry already exists in the environment AND the `force` key is not set + // to true, use the existing value instead of the config value. + if !is_forced { + if extra_env.get(key).is_some_and(Option::is_some) { + continue; + } + if let Ok(val) = std::env::var(key) { + env.insert(key, val); + continue; + } } if let Some(base) = entry.get("relative").and_then(|v| { @@ -124,38 +137,80 @@ fn parse_output_cargo_config_env_works() { .unwrap(); let config_path = cwd.join(".cargo").join("config.toml"); let raw = r#" -env.CARGO_WORKSPACE_DIR.relative = true -env.CARGO_WORKSPACE_DIR.value = "" -env.INVALID.relative = "invalidbool" -env.INVALID.value = "../relative" -env.RELATIVE.relative = true -env.RELATIVE.value = "../relative" -env.TEST.value = "test" -env.FORCED.value = "test" -env.FORCED.force = true -env.UNFORCED.value = "test" -env.UNFORCED.forced = false -env.OVERWRITTEN.value = "test" -env.NOT_AN_OBJECT = "value" +env.RA_TEST_WORKSPACE_DIR.relative = true +env.RA_TEST_WORKSPACE_DIR.value = "" +env.RA_TEST_INVALID.relative = "invalidbool" +env.RA_TEST_INVALID.value = "../relative" +env.RA_TEST_RELATIVE.relative = true +env.RA_TEST_RELATIVE.value = "../relative" +env.RA_TEST_UNSET.value = "test" +env.RA_TEST_FORCED.value = "test" +env.RA_TEST_FORCED.force = true +env.RA_TEST_UNFORCED.value = "test" +env.RA_TEST_UNFORCED.forced = false +env.RA_TEST_OVERWRITTEN.value = "test" +env.RA_TEST_NOT_AN_OBJECT = "value" "#; let raw = raw.lines().map(|l| format!("{l} # {config_path}")).join("\n"); let config = CargoConfigFile::from_string_for_test(raw); let extra_env = [ - ("FORCED", Some("ignored")), - ("UNFORCED", Some("newvalue")), - ("OVERWRITTEN", Some("newvalue")), - ("TEST", None), + ("RA_TEST_FORCED", Some("ignored")), + ("RA_TEST_UNFORCED", Some("newvalue")), + ("RA_TEST_OVERWRITTEN", Some("newvalue")), + ("RA_TEST_UNSET", None), ] .iter() .map(|(k, v)| (k.to_string(), v.map(ToString::to_string))) .collect(); let env = cargo_config_env(&Some(config), &extra_env); - assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str())); - assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str())); - assert_eq!(env.get("INVALID").as_deref(), Some("../relative")); - assert_eq!(env.get("TEST").as_deref(), Some("test")); - assert_eq!(env.get("FORCED").as_deref(), Some("test")); - assert_eq!(env.get("UNFORCED").as_deref(), Some("newvalue")); - assert_eq!(env.get("OVERWRITTEN").as_deref(), Some("newvalue")); - assert_eq!(env.get("NOT_AN_OBJECT").as_deref(), Some("value")); + assert_eq!(env.get("RA_TEST_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str())); + assert_eq!(env.get("RA_TEST_RELATIVE").as_deref(), Some(cwd.join("../relative").as_str())); + assert_eq!(env.get("RA_TEST_INVALID").as_deref(), Some("../relative")); + assert_eq!(env.get("RA_TEST_UNSET").as_deref(), Some("test")); + assert_eq!(env.get("RA_TEST_FORCED").as_deref(), Some("test")); + assert_eq!(env.get("RA_TEST_UNFORCED").as_deref(), Some("newvalue")); + assert_eq!(env.get("RA_TEST_OVERWRITTEN").as_deref(), Some("newvalue")); + assert_eq!(env.get("RA_TEST_NOT_AN_OBJECT").as_deref(), Some("value")); +} + +#[test] +fn cargo_config_env_respects_process_env() { + use itertools::Itertools; + + let cwd = paths::AbsPathBuf::try_from( + paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap(), + ) + .unwrap(); + let config_path = cwd.join(".cargo").join("config.toml"); + + // SAFETY: this test is not run in parallel with other tests that depend on these env vars. + unsafe { + std::env::set_var("RA_TEST_PROCESS_ENV_STRING", "from_process"); + std::env::set_var("RA_TEST_PROCESS_ENV_TABLE", "from_process"); + std::env::set_var("RA_TEST_PROCESS_ENV_FORCED", "from_process"); + } + + let raw = r#" +env.RA_TEST_PROCESS_ENV_STRING = "from_config" +env.RA_TEST_PROCESS_ENV_TABLE.value = "from_config" +env.RA_TEST_PROCESS_ENV_FORCED.value = "from_config" +env.RA_TEST_PROCESS_ENV_FORCED.force = true +"#; + let raw = raw.lines().map(|l| format!("{l} # {config_path}")).join("\n"); + let config = CargoConfigFile::from_string_for_test(raw); + let extra_env = FxHashMap::default(); + let env = cargo_config_env(&Some(config), &extra_env); + + // Plain string form should use process env value, not config value + assert_eq!(env.get("RA_TEST_PROCESS_ENV_STRING").as_deref(), Some("from_process")); + // Table form without force should use process env value, not config value + assert_eq!(env.get("RA_TEST_PROCESS_ENV_TABLE").as_deref(), Some("from_process")); + // Table form with force=true should override process env + assert_eq!(env.get("RA_TEST_PROCESS_ENV_FORCED").as_deref(), Some("from_config")); + + unsafe { + std::env::remove_var("RA_TEST_PROCESS_ENV_STRING"); + std::env::remove_var("RA_TEST_PROCESS_ENV_TABLE"); + std::env::remove_var("RA_TEST_PROCESS_ENV_FORCED"); + } } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 581b5fa514..29a19bc32e 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -216,6 +216,7 @@ impl ProjectWorkspace { features, rustc_source, extra_args, + metadata_extra_args, extra_env, set_test, cfg_overrides, @@ -289,6 +290,7 @@ impl ProjectWorkspace { features: features.clone(), targets: targets.clone(), extra_args: extra_args.clone(), + metadata_extra_args: metadata_extra_args.clone(), extra_env: extra_env.clone(), toolchain_version: toolchain.clone(), kind: "workspace", @@ -343,6 +345,7 @@ impl ProjectWorkspace { features: crate::CargoFeatures::default(), targets: targets.clone(), extra_args: extra_args.clone(), + metadata_extra_args: metadata_extra_args.clone(), extra_env: extra_env.clone(), toolchain_version: toolchain.clone(), kind: "rustc-dev" @@ -575,6 +578,7 @@ impl ProjectWorkspace { features: config.features.clone(), targets, extra_args: config.extra_args.clone(), + metadata_extra_args: config.metadata_extra_args.clone(), extra_env: config.extra_env.clone(), toolchain_version: toolchain.clone(), kind: "detached-file", @@ -1942,6 +1946,7 @@ fn sysroot_metadata_config( features: Default::default(), targets, extra_args: Default::default(), + metadata_extra_args: config.metadata_extra_args.clone(), extra_env: config.extra_env.clone(), toolchain_version, kind: "sysroot", diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 74828cba02..e56727d39d 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -23,10 +23,10 @@ use hir_def::{ use hir_ty::InferenceResult; use ide::{ Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve, - InlayHintsConfig, LineCol, RootDatabase, + InlayHintsConfig, LineCol, RaFixtureConfig, RootDatabase, }; use ide_db::{ - EditionedFileId, LineIndexDatabase, MiniCore, SnippetCap, + EditionedFileId, LineIndexDatabase, SnippetCap, base_db::{SourceDatabase, salsa::Database}, }; use itertools::Itertools; @@ -1367,6 +1367,7 @@ impl flags::AnalysisStats { &InlayHintsConfig { render_colons: false, type_hints: true, + type_hints_placement: ide::TypeHintsPlacement::Inline, sized_bound: false, discriminant_hints: ide::DiscriminantHints::Always, parameter_hints: true, @@ -1397,7 +1398,7 @@ impl flags::AnalysisStats { closing_brace_hints_min_lines: Some(20), fields_to_resolve: InlayFieldsToResolve::empty(), range_exclusive_hints: true, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }, analysis.editioned_file_id_to_vfs(file_id), None, @@ -1416,7 +1417,7 @@ impl flags::AnalysisStats { annotate_enum_variant_references: false, location: ide::AnnotationLocation::AboveName, filter_adjacent_derive_implementations: false, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; for &file_id in file_ids { let msg = format!("annotations: {}", vfs.file_path(file_id.file_id(db))); diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2ccd85f0e3..3a88a8fe84 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -12,7 +12,8 @@ use ide::{ CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, GotoDefinitionConfig, GotoImplementationConfig, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, - MemoryLayoutHoverRenderKind, RenameConfig, Snippet, SnippetScope, SourceRootId, + MemoryLayoutHoverRenderKind, RaFixtureConfig, RenameConfig, Snippet, SnippetScope, + SourceRootId, }; use ide_db::{ MiniCore, SnippetCap, @@ -313,6 +314,9 @@ config_data! { /// Hide inlay type hints for constructors. inlayHints_typeHints_hideNamedConstructor: bool = false, + /// Where to render type hints relative to their binding pattern. + inlayHints_typeHints_location: TypeHintsLocation = TypeHintsLocation::Inline, + /// Enable the experimental support for interpreting tests. interpret_tests: bool = false, @@ -727,6 +731,11 @@ config_data! { /// the `Problems Panel`. diagnostics_warningsAsInfo: Vec<String> = vec![], + /// Disable support for `#[rust_analyzer::rust_fixture]` snippets. + /// + /// If you are not working on rust-analyzer itself, you should ignore this config. + disableFixtureSupport: bool = false, + /// Enforce the import granularity setting for all files. If set to false rust-analyzer will /// try to keep import styles consistent per file. imports_granularity_enforce: bool = false, @@ -822,6 +831,9 @@ config_data! { /// /// Set this to `"all"` to pass `--all-features` to cargo. cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]), + /// Extra arguments passed only to `cargo metadata`, not to other cargo invocations. + /// Useful for flags like `--config` that `cargo metadata` supports. + cargo_metadataExtraArgs: Vec<String> = vec![], /// Whether to pass `--no-default-features` to cargo. cargo_noDefaultFeatures: bool = false, /// Whether to skip fetching dependencies. If set to "true", the analysis is performed @@ -948,18 +960,30 @@ config_data! { /// Override the command used for bench runnables. /// The first element of the array should be the program to execute (for example, `cargo`). /// - /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically - /// replace the package name, target option (such as `--bin` or `--example`), the target name and - /// the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). + /// Use the placeholders: + /// - `${package}`: package name. + /// - `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc. + /// - `${target}`: target name (empty for `--lib`). + /// - `${test_name}`: the test path filter, e.g. `module::bench_func`. + /// - `${exact}`: `--exact` for single benchmarks, empty for modules. + /// - `${include_ignored}`: always empty for benchmarks. + /// - `${executable_args}`: all of the above binary args bundled together + /// (includes `rust-analyzer.runnables.extraTestBinaryArgs`). runnables_bench_overrideCommand: Option<Vec<String>> = None, /// Command to be executed instead of 'cargo' for runnables. runnables_command: Option<String> = None, - /// Override the command used for bench runnables. + /// Override the command used for doc-test runnables. /// The first element of the array should be the program to execute (for example, `cargo`). /// - /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically - /// replace the package name, target option (such as `--bin` or `--example`), the target name and - /// the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). + /// Use the placeholders: + /// - `${package}`: package name. + /// - `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc. + /// - `${target}`: target name (empty for `--lib`). + /// - `${test_name}`: the test path filter, e.g. `module::func`. + /// - `${exact}`: always empty for doc-tests. + /// - `${include_ignored}`: always empty for doc-tests. + /// - `${executable_args}`: all of the above binary args bundled together + /// (includes `rust-analyzer.runnables.extraTestBinaryArgs`). runnables_doctest_overrideCommand: Option<Vec<String>> = None, /// Additional arguments to be passed to cargo for runnables such as /// tests or binaries. For example, it may be `--release`. @@ -977,9 +1001,15 @@ config_data! { /// Override the command used for test runnables. /// The first element of the array should be the program to execute (for example, `cargo`). /// - /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically - /// replace the package name, target option (such as `--bin` or `--example`), the target name and - /// the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). + /// Available placeholders: + /// - `${package}`: package name. + /// - `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc. + /// - `${target}`: target name (empty for `--lib`). + /// - `${test_name}`: the test path filter, e.g. `module::test_func`. + /// - `${exact}`: `--exact` for single tests, empty for modules. + /// - `${include_ignored}`: `--include-ignored` for single tests, empty otherwise. + /// - `${executable_args}`: all of the above binary args bundled together + /// (includes `rust-analyzer.runnables.extraTestBinaryArgs`). runnables_test_overrideCommand: Option<Vec<String>> = None, /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private @@ -1061,6 +1091,7 @@ struct ClientInfo { version: Option<Version>, } +/// The configuration of this rust-analyzer instance. #[derive(Clone)] pub struct Config { /// Projects that have a Cargo.toml or a rust-project.json in a @@ -1070,11 +1101,16 @@ pub struct Config { /// Projects whose configuration was generated by a command /// configured in discoverConfig. discovered_projects_from_command: Vec<ProjectJsonFromCommand>, - /// The workspace roots as registered by the LSP client + /// The workspace roots as registered by the LSP client. workspace_roots: Vec<AbsPathBuf>, caps: ClientCapabilities, - /// The LSP root path, deprecated in favor of `workspace_roots` + + /// The root of the first project encountered. This is deprecated + /// because rust-analyzer might be handling multiple projects. + /// + /// Prefer `workspace_roots` and `workspace_root_for()`. root_path: AbsPathBuf, + snippets: Vec<Snippet>, client_info: Option<ClientInfo>, @@ -1504,6 +1540,8 @@ pub struct LensConfig { // annotations pub location: AnnotationLocation, pub filter_adjacent_derive_implementations: bool, + + disable_ra_fixture: bool, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -1559,7 +1597,7 @@ impl LensConfig { annotate_method_references: self.method_refs, annotate_enum_variant_references: self.enum_variant_refs, location: self.location.into(), - minicore, + ra_fixture: RaFixtureConfig { minicore, disable_ra_fixture: self.disable_ra_fixture }, filter_adjacent_derive_implementations: self.filter_adjacent_derive_implementations, } } @@ -1776,9 +1814,23 @@ impl Config { s } - pub fn root_path(&self) -> &AbsPathBuf { - // We should probably use `workspace_roots` here if set - &self.root_path + /// Find the workspace root that contains the given path, using the + /// longest prefix match. + pub fn workspace_root_for(&self, path: &AbsPath) -> &AbsPathBuf { + self.workspace_roots + .iter() + .filter(|root| path.starts_with(root.as_path())) + .max_by_key(|root| root.as_str().len()) + .unwrap_or(self.default_root_path()) + } + + /// Best-effort root path for the current project. + /// + /// Use `workspace_root_for` where possible, because + /// `default_root_path` may return the wrong path when a user has + /// multiple workspaces. + pub fn default_root_path(&self) -> &AbsPathBuf { + self.workspace_roots.first().unwrap_or(&self.root_path) } pub fn caps(&self) -> &ClientCapabilities { @@ -1816,8 +1868,15 @@ impl Config { } } + pub fn ra_fixture<'a>(&self, minicore: MiniCore<'a>) -> RaFixtureConfig<'a> { + RaFixtureConfig { minicore, disable_ra_fixture: *self.disableFixtureSupport(None) } + } + pub fn call_hierarchy<'a>(&self, minicore: MiniCore<'a>) -> CallHierarchyConfig<'a> { - CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned(), minicore } + CallHierarchyConfig { + exclude_tests: self.references_excludeTests().to_owned(), + ra_fixture: self.ra_fixture(minicore), + } } pub fn completion<'a>( @@ -1878,7 +1937,7 @@ impl Config { }) .collect(), exclude_traits: self.completion_excludeTraits(source_root), - minicore, + ra_fixture: self.ra_fixture(minicore), } } @@ -1987,12 +2046,12 @@ impl Config { None => ide::SubstTyLen::Unlimited, }, show_drop_glue: *self.hover_dropGlue_enable(), - minicore, + ra_fixture: self.ra_fixture(minicore), } } pub fn goto_definition<'a>(&self, minicore: MiniCore<'a>) -> GotoDefinitionConfig<'a> { - GotoDefinitionConfig { minicore } + GotoDefinitionConfig { ra_fixture: self.ra_fixture(minicore) } } pub fn inlay_hints<'a>(&self, minicore: MiniCore<'a>) -> InlayHintsConfig<'a> { @@ -2001,6 +2060,10 @@ impl Config { InlayHintsConfig { render_colons: self.inlayHints_renderColons().to_owned(), type_hints: self.inlayHints_typeHints_enable().to_owned(), + type_hints_placement: match self.inlayHints_typeHints_location() { + TypeHintsLocation::Inline => ide::TypeHintsPlacement::Inline, + TypeHintsLocation::EndOfLine => ide::TypeHintsPlacement::EndOfLine, + }, sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(), parameter_hints: self.inlayHints_parameterHints_enable().to_owned(), parameter_hints_for_missing_arguments: self @@ -2082,7 +2145,7 @@ impl Config { implicit_drop_hints: self.inlayHints_implicitDrops_enable().to_owned(), implied_dyn_trait_hints: self.inlayHints_impliedDynTraitHints_enable().to_owned(), range_exclusive_hints: self.inlayHints_rangeExclusiveHints_enable().to_owned(), - minicore, + ra_fixture: self.ra_fixture(minicore), } } @@ -2135,7 +2198,7 @@ impl Config { .to_owned(), inject_doc_comment: self.semanticHighlighting_doc_comment_inject_enable().to_owned(), syntactic_name_ref_highlighting: false, - minicore, + ra_fixture: self.ra_fixture(minicore), } } @@ -2402,6 +2465,7 @@ impl Config { target_dir_config: self.target_dir_from_config(source_root), set_test: *self.cfg_setTest(source_root), no_deps: *self.cargo_noDeps(source_root), + metadata_extra_args: self.cargo_metadataExtraArgs(source_root).clone(), } } @@ -2621,6 +2685,7 @@ impl Config { location: *self.lens_location(), filter_adjacent_derive_implementations: *self .gotoImplementations_filterAdjacentDerives(), + disable_ra_fixture: *self.disableFixtureSupport(None), } } @@ -2999,6 +3064,13 @@ enum ClosureStyle { #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] +enum TypeHintsLocation { + Inline, + EndOfLine, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] enum ReborrowHintsDef { Mutable, #[serde(with = "true_or_always")] @@ -3908,6 +3980,14 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "`hide`: Shows `...` for every closure type", ], }, + "TypeHintsLocation" => set! { + "type": "string", + "enum": ["inline", "end_of_line"], + "enumDescriptions": [ + "Render type hints directly after the binding identifier.", + "Render type hints after the end of the containing `let` statement when possible.", + ], + }, "Option<MemoryLayoutHoverRenderKindDef>" => set! { "anyOf": [ { diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index c41696bf3f..aad8bece95 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs @@ -673,27 +673,31 @@ impl FlycheckActor { if self.diagnostics_received == DiagnosticsReceived::NotYet { tracing::trace!(flycheck_id = self.id, "clearing diagnostics"); // We finished without receiving any diagnostics. - // Clear everything for good measure - match &self.scope { - FlycheckScope::Workspace => { - self.send(FlycheckMessage::ClearDiagnostics { - id: self.id, - kind: ClearDiagnosticsKind::All(ClearScope::Workspace), - }); - } - FlycheckScope::Package { package, workspace_deps } => { - for pkg in - std::iter::once(package).chain(workspace_deps.iter().flatten()) - { - self.send(FlycheckMessage::ClearDiagnostics { - id: self.id, - kind: ClearDiagnosticsKind::All(ClearScope::Package( - pkg.clone(), - )), - }); - } - } - } + // + // `cargo check` generally outputs something, even if there are no + // warnings/errors, so we always know which package was checked. + // + // ```text + // $ cargo check --message-format=json 2>/dev/null + // {"reason":"compiler-artifact","package_id":"path+file:///Users/wilfred/tmp/scratch#0.1.0",...} + // ``` + // + // However, rustc only returns JSON if there are diagnostics present, so a + // build without warnings or errors has an empty output. + // + // ``` + // $ rustc --error-format=json bad.rs + // {"$message_type":"diagnostic","message":"mismatched types","...} + // + // $ rustc --error-format=json good.rs + // ``` + // + // So if we got zero diagnostics, it was almost certainly a check that + // wasn't specific to a package. + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::All(ClearScope::Workspace), + }); } else if res.is_ok() { // We clear diagnostics for packages on // `[CargoCheckMessage::CompilerArtifact]` but there seem to be setups where diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index ad07da7759..c1806c82c7 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -332,7 +332,7 @@ pub(crate) fn handle_view_crate_graph( params: ViewCrateGraphParams, ) -> anyhow::Result<String> { let _p = tracing::info_span!("handle_view_crate_graph").entered(); - let dot = snap.analysis.view_crate_graph(params.full)?.map_err(anyhow::Error::msg)?; + let dot = snap.analysis.view_crate_graph(params.full)?; Ok(dot) } @@ -1264,11 +1264,15 @@ pub(crate) fn handle_folding_range( params: FoldingRangeParams, ) -> anyhow::Result<Option<Vec<FoldingRange>>> { let _p = tracing::info_span!("handle_folding_range").entered(); + let file_id = try_default!(from_proto::file_id(&snap, ¶ms.text_document.uri)?); - let folds = snap.analysis.folding_ranges(file_id)?; + let collapsed_text = snap.config.folding_range_collapsed_text(); + let folds = snap.analysis.folding_ranges(file_id, collapsed_text)?; + let text = snap.analysis.file_text(file_id)?; let line_index = snap.file_line_index(file_id)?; let line_folding_only = snap.config.line_folding_only(); + let res = folds .into_iter() .map(|it| to_proto::folding_range(&text, &line_index, line_folding_only, it)) @@ -1395,7 +1399,10 @@ pub(crate) fn handle_references( let Some(refs) = snap.analysis.find_all_refs( position, - &FindAllRefsConfig { search_scope: None, minicore: snap.minicore() }, + &FindAllRefsConfig { + search_scope: None, + ra_fixture: snap.config.ra_fixture(snap.minicore()), + }, )? else { return Ok(None); @@ -2202,7 +2209,10 @@ fn show_ref_command_link( .analysis .find_all_refs( *position, - &FindAllRefsConfig { search_scope: None, minicore: snap.minicore() }, + &FindAllRefsConfig { + search_scope: None, + ra_fixture: snap.config.ra_fixture(snap.minicore()), + }, ) .unwrap_or(None) { @@ -2432,8 +2442,7 @@ fn run_rustfmt( } RustfmtConfig::CustomCommand { command, args } => { let cmd = Utf8PathBuf::from(&command); - let target_spec = - crates.first().and_then(|&crate_id| snap.target_spec_for_file(file_id, crate_id)); + let target_spec = TargetSpec::for_file(snap, file_id).ok().flatten(); let extra_env = snap.config.extra_env(source_root_id); let mut cmd = match target_spec { Some(TargetSpec::Cargo(_)) => { @@ -2443,7 +2452,14 @@ fn run_rustfmt( let cmd_path = if command.contains(std::path::MAIN_SEPARATOR) || (cfg!(windows) && command.contains('/')) { - snap.config.root_path().join(cmd).into() + let project_root = Utf8PathBuf::from_path_buf(current_dir.clone()) + .ok() + .and_then(|p| AbsPathBuf::try_from(p).ok()); + let project_root = project_root + .as_ref() + .map(|dir| snap.config.workspace_root_for(dir)) + .unwrap_or(snap.config.default_root_path()); + project_root.join(cmd).into() } else { cmd }; diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 6a74b8a54d..af449c473a 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -13,10 +13,10 @@ use hir::ChangeWithProcMacros; use ide::{ AnalysisHost, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, DiagnosticsConfig, - FilePosition, TextSize, + FilePosition, RaFixtureConfig, TextSize, }; use ide_db::{ - MiniCore, SnippetCap, + SnippetCap, imports::insert_use::{ImportGranularity, InsertUseConfig}, }; use project_model::CargoConfig; @@ -190,7 +190,7 @@ fn integrated_completion_benchmark() { exclude_traits: &[], enable_auto_await: true, enable_auto_iter: true, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -245,7 +245,7 @@ fn integrated_completion_benchmark() { exclude_traits: &[], enable_auto_await: true, enable_auto_iter: true, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -298,7 +298,7 @@ fn integrated_completion_benchmark() { exclude_traits: &[], enable_auto_await: true, enable_auto_iter: true, - minicore: MiniCore::default(), + ra_fixture: RaFixtureConfig::default(), }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs index d6a694be91..3ad4cb70b4 100644 --- a/crates/rust-analyzer/src/lsp/capabilities.rs +++ b/crates/rust-analyzer/src/lsp/capabilities.rs @@ -335,6 +335,20 @@ impl ClientCapabilities { .unwrap_or_default() } + pub fn folding_range_collapsed_text(&self) -> bool { + (|| -> _ { + self.0 + .text_document + .as_ref()? + .folding_range + .as_ref()? + .folding_range + .as_ref()? + .collapsed_text + })() + .unwrap_or_default() + } + pub fn hierarchical_symbols(&self) -> bool { (|| -> _ { self.0 diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index e5b983dcbf..5fa95252e7 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -913,9 +913,9 @@ pub(crate) fn folding_range( text: &str, line_index: &LineIndex, line_folding_only: bool, - fold: Fold, + Fold { range: text_range, kind, collapsed_text }: Fold, ) -> lsp_types::FoldingRange { - let kind = match fold.kind { + let kind = match kind { FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), FoldKind::Region => Some(lsp_types::FoldingRangeKind::Region), @@ -930,17 +930,19 @@ pub(crate) fn folding_range( | FoldKind::Array | FoldKind::ExternCrates | FoldKind::MatchArm - | FoldKind::Function => None, + | FoldKind::Function + | FoldKind::Stmt(_) + | FoldKind::TailExpr(_) => None, }; - let range = range(line_index, fold.range); + let range = range(line_index, text_range); if line_folding_only { // Clients with line_folding_only == true (such as VSCode) will fold the whole end line // even if it contains text not in the folding range. To prevent that we exclude // range.end.line from the folding region if there is more text after range.end // on the same line. - let has_more_text_on_end_line = text[TextRange::new(fold.range.end(), TextSize::of(text))] + let has_more_text_on_end_line = text[TextRange::new(text_range.end(), TextSize::of(text))] .chars() .take_while(|it| *it != '\n') .any(|it| !it.is_whitespace()); @@ -951,13 +953,20 @@ pub(crate) fn folding_range( range.end.line }; + let collapsed_text = collapsed_text.map(|collapsed_text| { + let range_start = text_range.start(); + let line_start = range_start - TextSize::from(range.start.character); + let text_before_range = &text[TextRange::new(line_start, range_start)]; + format!("{text_before_range}{collapsed_text}") + }); + lsp_types::FoldingRange { start_line: range.start.line, start_character: None, end_line, end_character: None, kind, - collapsed_text: None, + collapsed_text, } } else { lsp_types::FoldingRange { @@ -966,7 +975,7 @@ pub(crate) fn folding_range( end_line: range.end.line, end_character: Some(range.end.character), kind, - collapsed_text: None, + collapsed_text, } } } @@ -2037,8 +2046,8 @@ fn main() { }"#; let (analysis, file_id) = Analysis::from_single_file(text.to_owned()); - let folds = analysis.folding_ranges(file_id).unwrap(); - assert_eq!(folds.len(), 4); + let folds = analysis.folding_ranges(file_id, true).unwrap(); + assert_eq!(folds.len(), 5); let line_index = LineIndex { index: Arc::new(ide::LineIndex::new(text)), @@ -2048,7 +2057,7 @@ fn main() { let converted: Vec<lsp_types::FoldingRange> = folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect(); - let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; + let expected_lines = [(0, 2), (4, 10), (5, 9), (5, 6), (7, 9)]; assert_eq!(converted.len(), expected_lines.len()); for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) { assert_eq!(folding_range.start_line, *start_line); diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 7c494de6f7..a8c3d062d0 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -830,12 +830,19 @@ impl GlobalState { let command = cfg.command.clone(); let discover = DiscoverCommand::new(self.discover_sender.clone(), command); + let discover_path = match &arg { + DiscoverProjectParam::Buildfile(it) => it, + DiscoverProjectParam::Path(it) => it, + }; + let current_dir = + self.config.workspace_root_for(discover_path.as_path()).clone(); + let arg = match arg { DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it), DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), }; - match discover.spawn(arg, self.config.root_path().as_ref()) { + match discover.spawn(arg, current_dir.as_ref()) { Ok(handle) => { if self.discover_jobs_active == 0 { let title = &cfg.progress_label.clone(); @@ -953,7 +960,7 @@ impl GlobalState { if let Some(dir) = dir { message += &format!( ": {}", - match dir.strip_prefix(self.config.root_path()) { + match dir.strip_prefix(self.config.workspace_root_for(&dir)) { Some(relative_path) => relative_path.as_utf8_path(), None => dir.as_ref(), } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 83f4a19b39..1832275eb3 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -390,7 +390,7 @@ impl GlobalState { info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); let config = self.config.cargo(None); - let root_path = self.config.root_path().clone(); + let root_path = self.config.default_root_path().clone(); self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); @@ -582,7 +582,8 @@ impl GlobalState { [ (base.clone(), "**/*.rs"), (base.clone(), "**/Cargo.{lock,toml}"), - (base, "**/rust-analyzer.toml"), + (base.clone(), "**/rust-analyzer.toml"), + (base, "**/*.md"), ] }) }) @@ -607,6 +608,7 @@ impl GlobalState { format!("{base}/**/*.rs"), format!("{base}/**/Cargo.{{toml,lock}}"), format!("{base}/**/rust-analyzer.toml"), + format!("{base}/**/*.md"), ] }) }) @@ -883,7 +885,7 @@ impl GlobalState { config, crate::flycheck::FlycheckConfigJson::default(), None, - self.config.root_path().clone(), + self.config.default_root_path().clone(), None, None, )] @@ -975,6 +977,7 @@ pub(crate) fn should_refresh_for_change( change_kind: ChangeKind, additional_paths: &[&str], ) -> bool { + // Note: build scripts are retriggered on file save, no refresh is necessary const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"]; @@ -991,15 +994,20 @@ pub(crate) fn should_refresh_for_change( return true; } + // .cargo/config{.toml} + if matches!(file_name, "config.toml" | "config") + && path.parent().map(|parent| parent.as_str().ends_with(".cargo")).unwrap_or(false) + { + return true; + } + + // Everything below only matters when files are created or deleted if change_kind == ChangeKind::Modify { return false; } - // .cargo/config{.toml} if path.extension().unwrap_or_default() != "rs" { - let is_cargo_config = matches!(file_name, "config.toml" | "config") - && path.parent().map(|parent| parent.as_str().ends_with(".cargo")).unwrap_or(false); - return is_cargo_config; + return false; } if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_str().ends_with(it)) { diff --git a/crates/rust-analyzer/src/target_spec.rs b/crates/rust-analyzer/src/target_spec.rs index 8be061cacf..5bdc9d8ca3 100644 --- a/crates/rust-analyzer/src/target_spec.rs +++ b/crates/rust-analyzer/src/target_spec.rs @@ -230,6 +230,18 @@ impl CargoTargetSpec { }; let test_name = test_name.unwrap_or_default(); + let exact = match kind { + RunnableKind::Test { test_id } | RunnableKind::Bench { test_id } => match test_id { + TestId::Path(_) => "--exact", + TestId::Name(_) => "", + }, + _ => "", + }; + let include_ignored = match kind { + RunnableKind::Test { .. } => "--include-ignored", + _ => "", + }; + let target_arg = |kind| match kind { TargetKind::Bin => "--bin", TargetKind::Test => "--test", @@ -249,7 +261,9 @@ impl CargoTargetSpec { .replace("${package}", &spec.package) .replace("${target_arg}", target_arg(spec.target_kind)) .replace("${target}", target(spec.target_kind, &spec.target)) - .replace("${test_name}", &test_name), + .replace("${test_name}", &test_name) + .replace("${exact}", exact) + .replace("${include_ignored}", include_ignored), _ => arg, }; @@ -274,15 +288,13 @@ impl CargoTargetSpec { let mut executable_args = Vec::new(); match kind { - RunnableKind::Test { test_id, attr } => { + RunnableKind::Test { test_id } => { executable_args.push(test_id.to_string()); if let TestId::Path(_) = test_id { executable_args.push("--exact".to_owned()); } executable_args.extend(extra_test_binary_args); - if attr.ignore { - executable_args.push("--ignored".to_owned()); - } + executable_args.push("--include-ignored".to_owned()); } RunnableKind::TestMod { path } => { executable_args.push(path.clone()); @@ -369,23 +381,13 @@ mod tests { SmolStr, ast::{self, AstNode}, }; - use syntax_bridge::{ - DocCommentDesugarMode, - dummy_test_span_utils::{DUMMY, DummyTestSpanMap}, - syntax_node_to_token_tree, - }; fn check(cfg: &str, expected_features: &[&str]) { let cfg_expr = { let source_file = ast::SourceFile::parse(cfg, Edition::CURRENT).ok().unwrap(); - let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree( - tt.syntax(), - &DummyTestSpanMap, - DUMMY, - DocCommentDesugarMode::Mbe, - ); - CfgExpr::parse(&tt) + let cfg_predicate = + source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap(); + CfgExpr::parse_from_ast(cfg_predicate) }; let mut features = vec![]; diff --git a/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/crates/rust-analyzer/tests/slow-tests/flycheck.rs index c1d53fb33a..c6f1f81139 100644 --- a/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -110,3 +110,45 @@ fn main() {} diagnostics.diagnostics, ); } + +#[test] +fn test_flycheck_diagnostics_with_override_command_cleared_after_fix() { + if skip_slow_tests() { + return; + } + + // Start with a program that is lint clean. + let server = Project::with_fixture( + r#" +//- /Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- /src/main.rs +fn main() {} +"#, + ) + .with_config(serde_json::json!({ + "checkOnSave": true, + "check": { + "overrideCommand": ["rustc", "--error-format=json", "$saved_file"] + } + })) + .server() + .wait_until_workspace_is_loaded(); + + // Introduce an unused variable. + server.write_file_and_save("src/main.rs", "fn main() {\n let x = 1;\n}\n".to_owned()); + + let diags = server.wait_for_diagnostics(); + assert!( + diags.diagnostics.iter().any(|d| d.message.contains("unused variable")), + "expected unused variable diagnostic, got: {:?}", + diags.diagnostics, + ); + + // Fix it and verify that diagnostics are cleared. + server.write_file_and_save("src/main.rs", "fn main() {\n let _x = 1;\n}\n".to_owned()); + server.wait_for_diagnostics_cleared(); +} diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index fcdc8bb7cd..3c57e36b4f 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -262,7 +262,7 @@ fn main() {} { "args": { "cargoArgs": ["test", "--package", "foo", "--test", "spam"], - "executableArgs": ["test_eggs", "--exact", "--nocapture"], + "executableArgs": ["test_eggs", "--exact", "--nocapture", "--include-ignored"], "overrideCargo": null, "cwd": server.path().join("foo"), "workspaceRoot": server.path().join("foo") diff --git a/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/crates/rust-analyzer/tests/slow-tests/ratoml.rs index cac7efd84a..dd113babff 100644 --- a/crates/rust-analyzer/tests/slow-tests/ratoml.rs +++ b/crates/rust-analyzer/tests/slow-tests/ratoml.rs @@ -1008,3 +1008,45 @@ fn main() { InternalTestingFetchConfigResponse::CheckWorkspace(true), ); } + +#[test] +fn ratoml_virtual_workspace() { + if skip_slow_tests() { + return; + } + + let server = RatomlTest::new( + vec![ + r#" +//- /p1/Cargo.toml +[workspace] +members = ["member"] +"#, + r#" +//- /p1/rust-analyzer.toml +assist.emitMustUse = true +"#, + r#" +//- /p1/member/Cargo.toml +[package] +name = "member" +version = "0.1.0" +edition = "2021" +"#, + r#" +//- /p1/member/src/lib.rs +pub fn add(left: usize, right: usize) -> usize { + left + right +} +"#, + ], + vec!["p1"], + None, + ); + + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); +} diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 7ee31f3d53..7390403673 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -173,7 +173,8 @@ impl Project<'_> { chalk_filter: std::env::var("CHALK_DEBUG").ok(), profile_filter: std::env::var("RA_PROFILE").ok(), json_profile_filter: std::env::var("RA_PROFILE_JSON").ok(), - }; + } + .init(); }); let FixtureWithProjectMeta { diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 8909fb423c..e65836ed8d 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -21,6 +21,7 @@ rustc-literal-escaper.workspace = true smol_str.workspace = true triomphe.workspace = true tracing.workspace = true +smallvec.workspace = true parser.workspace = true stdx.workspace = true diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 3113fc7430..324b2bbd58 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -126,9 +126,41 @@ MacroStmts = Attr = '#' '!'? '[' Meta ']' +CfgAttrMeta = + 'cfg_attr' '(' CfgPredicate ',' (Meta (',' Meta)* ','?) ')' + +CfgMeta = + 'cfg' '(' CfgPredicate ','? ')' + +CfgPredicate = + CfgAtom +| CfgComposite + +CfgAtom = + ('#ident' | 'true' | 'false') ('=' '@string')? + +CfgComposite = + keyword:'#ident' '(' (CfgPredicate (',' CfgPredicate)* ','?) ')' + Meta = - 'unsafe' '(' Path ('=' Expr | TokenTree)? ')' -| Path ('=' Expr | TokenTree)? + CfgAttrMeta +| CfgMeta +| UnsafeMeta +| PathMeta +| KeyValueMeta +| TokenTreeMeta + +UnsafeMeta = + 'unsafe' '(' Meta ')' + +PathMeta = + Path + +KeyValueMeta = + Path '=' Expr + +TokenTreeMeta = + Path TokenTree //*************************// // Items // diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 5d67fd4491..dc592a4372 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs @@ -25,9 +25,9 @@ pub use self::{ expr_ext::{ArrayExprKind, BlockModifier, CallableExpr, ElseBranch, LiteralKind}, generated::{nodes::*, tokens::*}, node_ext::{ - AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind, - SlicePatComponents, StructKind, TokenTreeChildren, TypeBoundKind, TypeOrConstParam, - VisibilityKind, + AttrKind, CfgAtomKey, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, + SelfParamKind, SlicePatComponents, StructKind, TokenTreeChildren, TypeBoundKind, + TypeOrConstParam, VisibilityKind, }, operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}, token_ext::{ diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index b706d7f722..23a0411ead 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -105,8 +105,7 @@ impl IndentLevel { } pub(super) fn clone_increase_indent(self, node: &SyntaxNode) -> SyntaxNode { - let node = node.clone_subtree(); - let mut editor = SyntaxEditor::new(node.clone()); + let (mut editor, node) = SyntaxEditor::new(node.clone()); let tokens = node .preorder_with_tokens() .filter_map(|event| match event { @@ -140,8 +139,7 @@ impl IndentLevel { } pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode { - let node = node.clone_subtree(); - let mut editor = SyntaxEditor::new(node.clone()); + let (mut editor, node) = SyntaxEditor::new(node.clone()); let tokens = node .preorder_with_tokens() .filter_map(|event| match event { diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 7334de0fd9..cd7f6a018a 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -377,6 +377,68 @@ impl CastExpr { #[inline] pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) } } +pub struct CfgAtom { + pub(crate) syntax: SyntaxNode, +} +impl CfgAtom { + #[inline] + pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } + #[inline] + pub fn false_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![false]) } + #[inline] + pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) } + #[inline] + pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) } + #[inline] + pub fn true_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![true]) } +} +pub struct CfgAttrMeta { + pub(crate) syntax: SyntaxNode, +} +impl CfgAttrMeta { + #[inline] + pub fn cfg_predicate(&self) -> Option<CfgPredicate> { support::child(&self.syntax) } + #[inline] + pub fn metas(&self) -> AstChildren<Meta> { support::children(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } + #[inline] + pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) } + #[inline] + pub fn cfg_attr_token(&self) -> Option<SyntaxToken> { + support::token(&self.syntax, T![cfg_attr]) + } +} +pub struct CfgComposite { + pub(crate) syntax: SyntaxNode, +} +impl CfgComposite { + #[inline] + pub fn cfg_predicates(&self) -> AstChildren<CfgPredicate> { support::children(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } + #[inline] + pub fn keyword(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) } +} +pub struct CfgMeta { + pub(crate) syntax: SyntaxNode, +} +impl CfgMeta { + #[inline] + pub fn cfg_predicate(&self) -> Option<CfgPredicate> { support::child(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } + #[inline] + pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) } + #[inline] + pub fn cfg_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![cfg]) } +} pub struct ClosureExpr { pub(crate) syntax: SyntaxNode, } @@ -783,6 +845,17 @@ impl ItemList { #[inline] pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } } +pub struct KeyValueMeta { + pub(crate) syntax: SyntaxNode, +} +impl KeyValueMeta { + #[inline] + pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } + #[inline] + pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } + #[inline] + pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } +} pub struct Label { pub(crate) syntax: SyntaxNode, } @@ -1012,25 +1085,6 @@ impl MatchGuard { #[inline] pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) } } -pub struct Meta { - pub(crate) syntax: SyntaxNode, -} -impl Meta { - #[inline] - pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } - #[inline] - pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } - #[inline] - pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) } - #[inline] - pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } - #[inline] - pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } - #[inline] - pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } - #[inline] - pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) } -} pub struct MethodCallExpr { pub(crate) syntax: SyntaxNode, } @@ -1225,6 +1279,13 @@ impl PathExpr { #[inline] pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } } +pub struct PathMeta { + pub(crate) syntax: SyntaxNode, +} +impl PathMeta { + #[inline] + pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } +} pub struct PathPat { pub(crate) syntax: SyntaxNode, } @@ -1607,6 +1668,15 @@ impl TokenTree { #[inline] pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } } +pub struct TokenTreeMeta { + pub(crate) syntax: SyntaxNode, +} +impl TokenTreeMeta { + #[inline] + pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } + #[inline] + pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) } +} pub struct Trait { pub(crate) syntax: SyntaxNode, } @@ -1834,6 +1904,19 @@ impl Union { #[inline] pub fn union_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![union]) } } +pub struct UnsafeMeta { + pub(crate) syntax: SyntaxNode, +} +impl UnsafeMeta { + #[inline] + pub fn meta(&self) -> Option<Meta> { support::child(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } + #[inline] + pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) } +} pub struct Use { pub(crate) syntax: SyntaxNode, } @@ -2025,6 +2108,12 @@ impl ast::HasAttrs for AssocItem {} impl ast::HasDocComments for AssocItem {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum CfgPredicate { + CfgAtom(CfgAtom), + CfgComposite(CfgComposite), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Expr { ArrayExpr(ArrayExpr), AsmExpr(AsmExpr), @@ -2119,6 +2208,16 @@ pub enum Item { impl ast::HasAttrs for Item {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Meta { + CfgAttrMeta(CfgAttrMeta), + CfgMeta(CfgMeta), + KeyValueMeta(KeyValueMeta), + PathMeta(PathMeta), + TokenTreeMeta(TokenTreeMeta), + UnsafeMeta(UnsafeMeta), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Pat { BoxPat(BoxPat), ConstBlockPat(ConstBlockPat), @@ -3133,6 +3232,134 @@ impl fmt::Debug for CastExpr { f.debug_struct("CastExpr").field("syntax", &self.syntax).finish() } } +impl AstNode for CfgAtom { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CFG_ATOM + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_ATOM } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for CfgAtom { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for CfgAtom {} +impl PartialEq for CfgAtom { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for CfgAtom { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for CfgAtom { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CfgAtom").field("syntax", &self.syntax).finish() + } +} +impl AstNode for CfgAttrMeta { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CFG_ATTR_META + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_ATTR_META } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for CfgAttrMeta { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for CfgAttrMeta {} +impl PartialEq for CfgAttrMeta { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for CfgAttrMeta { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for CfgAttrMeta { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CfgAttrMeta").field("syntax", &self.syntax).finish() + } +} +impl AstNode for CfgComposite { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CFG_COMPOSITE + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_COMPOSITE } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for CfgComposite { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for CfgComposite {} +impl PartialEq for CfgComposite { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for CfgComposite { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for CfgComposite { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CfgComposite").field("syntax", &self.syntax).finish() + } +} +impl AstNode for CfgMeta { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CFG_META + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_META } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for CfgMeta { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for CfgMeta {} +impl PartialEq for CfgMeta { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for CfgMeta { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for CfgMeta { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CfgMeta").field("syntax", &self.syntax).finish() + } +} impl AstNode for ClosureExpr { #[inline] fn kind() -> SyntaxKind @@ -4093,6 +4320,38 @@ impl fmt::Debug for ItemList { f.debug_struct("ItemList").field("syntax", &self.syntax).finish() } } +impl AstNode for KeyValueMeta { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + KEY_VALUE_META + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == KEY_VALUE_META } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for KeyValueMeta { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for KeyValueMeta {} +impl PartialEq for KeyValueMeta { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for KeyValueMeta { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for KeyValueMeta { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("KeyValueMeta").field("syntax", &self.syntax).finish() + } +} impl AstNode for Label { #[inline] fn kind() -> SyntaxKind @@ -4797,38 +5056,6 @@ impl fmt::Debug for MatchGuard { f.debug_struct("MatchGuard").field("syntax", &self.syntax).finish() } } -impl AstNode for Meta { - #[inline] - fn kind() -> SyntaxKind - where - Self: Sized, - { - META - } - #[inline] - fn can_cast(kind: SyntaxKind) -> bool { kind == META } - #[inline] - fn cast(syntax: SyntaxNode) -> Option<Self> { - if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } - } - #[inline] - fn syntax(&self) -> &SyntaxNode { &self.syntax } -} -impl hash::Hash for Meta { - fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } -} -impl Eq for Meta {} -impl PartialEq for Meta { - fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } -} -impl Clone for Meta { - fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } -} -impl fmt::Debug for Meta { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Meta").field("syntax", &self.syntax).finish() - } -} impl AstNode for MethodCallExpr { #[inline] fn kind() -> SyntaxKind @@ -5309,6 +5536,38 @@ impl fmt::Debug for PathExpr { f.debug_struct("PathExpr").field("syntax", &self.syntax).finish() } } +impl AstNode for PathMeta { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH_META + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_META } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for PathMeta { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for PathMeta {} +impl PartialEq for PathMeta { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for PathMeta { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for PathMeta { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PathMeta").field("syntax", &self.syntax).finish() + } +} impl AstNode for PathPat { #[inline] fn kind() -> SyntaxKind @@ -6301,6 +6560,38 @@ impl fmt::Debug for TokenTree { f.debug_struct("TokenTree").field("syntax", &self.syntax).finish() } } +impl AstNode for TokenTreeMeta { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TOKEN_TREE_META + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == TOKEN_TREE_META } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for TokenTreeMeta { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for TokenTreeMeta {} +impl PartialEq for TokenTreeMeta { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for TokenTreeMeta { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for TokenTreeMeta { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TokenTreeMeta").field("syntax", &self.syntax).finish() + } +} impl AstNode for Trait { #[inline] fn kind() -> SyntaxKind @@ -6845,6 +7136,38 @@ impl fmt::Debug for Union { f.debug_struct("Union").field("syntax", &self.syntax).finish() } } +impl AstNode for UnsafeMeta { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + UNSAFE_META + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == UNSAFE_META } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for UnsafeMeta { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for UnsafeMeta {} +impl PartialEq for UnsafeMeta { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for UnsafeMeta { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for UnsafeMeta { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UnsafeMeta").field("syntax", &self.syntax).finish() + } +} impl AstNode for Use { #[inline] fn kind() -> SyntaxKind @@ -7413,6 +7736,34 @@ impl AstNode for AssocItem { } } } +impl From<CfgAtom> for CfgPredicate { + #[inline] + fn from(node: CfgAtom) -> CfgPredicate { CfgPredicate::CfgAtom(node) } +} +impl From<CfgComposite> for CfgPredicate { + #[inline] + fn from(node: CfgComposite) -> CfgPredicate { CfgPredicate::CfgComposite(node) } +} +impl AstNode for CfgPredicate { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, CFG_ATOM | CFG_COMPOSITE) } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + let res = match syntax.kind() { + CFG_ATOM => CfgPredicate::CfgAtom(CfgAtom { syntax }), + CFG_COMPOSITE => CfgPredicate::CfgComposite(CfgComposite { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + CfgPredicate::CfgAtom(it) => &it.syntax, + CfgPredicate::CfgComposite(it) => &it.syntax, + } + } +} impl From<ArrayExpr> for Expr { #[inline] fn from(node: ArrayExpr) -> Expr { Expr::ArrayExpr(node) } @@ -7970,6 +8321,63 @@ impl AstNode for Item { } } } +impl From<CfgAttrMeta> for Meta { + #[inline] + fn from(node: CfgAttrMeta) -> Meta { Meta::CfgAttrMeta(node) } +} +impl From<CfgMeta> for Meta { + #[inline] + fn from(node: CfgMeta) -> Meta { Meta::CfgMeta(node) } +} +impl From<KeyValueMeta> for Meta { + #[inline] + fn from(node: KeyValueMeta) -> Meta { Meta::KeyValueMeta(node) } +} +impl From<PathMeta> for Meta { + #[inline] + fn from(node: PathMeta) -> Meta { Meta::PathMeta(node) } +} +impl From<TokenTreeMeta> for Meta { + #[inline] + fn from(node: TokenTreeMeta) -> Meta { Meta::TokenTreeMeta(node) } +} +impl From<UnsafeMeta> for Meta { + #[inline] + fn from(node: UnsafeMeta) -> Meta { Meta::UnsafeMeta(node) } +} +impl AstNode for Meta { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + CFG_ATTR_META | CFG_META | KEY_VALUE_META | PATH_META | TOKEN_TREE_META | UNSAFE_META + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + let res = match syntax.kind() { + CFG_ATTR_META => Meta::CfgAttrMeta(CfgAttrMeta { syntax }), + CFG_META => Meta::CfgMeta(CfgMeta { syntax }), + KEY_VALUE_META => Meta::KeyValueMeta(KeyValueMeta { syntax }), + PATH_META => Meta::PathMeta(PathMeta { syntax }), + TOKEN_TREE_META => Meta::TokenTreeMeta(TokenTreeMeta { syntax }), + UNSAFE_META => Meta::UnsafeMeta(UnsafeMeta { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + Meta::CfgAttrMeta(it) => &it.syntax, + Meta::CfgMeta(it) => &it.syntax, + Meta::KeyValueMeta(it) => &it.syntax, + Meta::PathMeta(it) => &it.syntax, + Meta::TokenTreeMeta(it) => &it.syntax, + Meta::UnsafeMeta(it) => &it.syntax, + } + } +} impl From<BoxPat> for Pat { #[inline] fn from(node: BoxPat) -> Pat { Pat::BoxPat(node) } @@ -9334,6 +9742,11 @@ impl std::fmt::Display for AssocItem { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for CfgPredicate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -9364,6 +9777,11 @@ impl std::fmt::Display for Item { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for Meta { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Pat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -9524,6 +9942,26 @@ impl std::fmt::Display for CastExpr { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for CfgAtom { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CfgAttrMeta { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CfgComposite { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CfgMeta { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ClosureExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -9674,6 +10112,11 @@ impl std::fmt::Display for ItemList { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for KeyValueMeta { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Label { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -9784,11 +10227,6 @@ impl std::fmt::Display for MatchGuard { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for Meta { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self.syntax(), f) - } -} impl std::fmt::Display for MethodCallExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -9864,6 +10302,11 @@ impl std::fmt::Display for PathExpr { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for PathMeta { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for PathPat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10019,6 +10462,11 @@ impl std::fmt::Display for TokenTree { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for TokenTreeMeta { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Trait { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10104,6 +10552,11 @@ impl std::fmt::Display for Union { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for UnsafeMeta { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Use { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 00971569a2..ac02cc9e43 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -1322,6 +1322,18 @@ pub fn meta_path(path: ast::Path) -> ast::Meta { ast_from_text(&format!("#[{path}]")) } +pub fn cfg_attr_meta( + predicate: ast::CfgPredicate, + inner: impl IntoIterator<Item = ast::Meta>, +) -> ast::CfgAttrMeta { + let inner = inner.into_iter().join(", "); + ast_from_text(&format!("#![cfg_attr({predicate}, {inner})]")) +} + +pub fn cfg_flag(flag: &str) -> ast::CfgPredicate { + ast_from_text(&format!("#![cfg({flag})]")) +} + pub fn token_tree( delimiter: SyntaxKind, tt: impl IntoIterator<Item = NodeOrToken<ast::TokenTree, SyntaxToken>>, diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 63e4608d0f..03118d01dc 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -8,6 +8,7 @@ use std::{borrow::Cow, fmt, iter::successors}; use itertools::Itertools; use parser::SyntaxKind; use rowan::{GreenNodeData, GreenTokenData}; +use smallvec::{SmallVec, smallvec}; use crate::{ NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText, @@ -77,6 +78,15 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { } } +fn into_comma(it: NodeOrToken<SyntaxNode, SyntaxToken>) -> Option<SyntaxToken> { + let token = match it { + NodeOrToken::Token(it) => it, + NodeOrToken::Node(node) if node.kind() == SyntaxKind::ERROR => node.first_token()?, + NodeOrToken::Node(_) => return None, + }; + (token.kind() == T![,]).then_some(token) +} + impl ast::Abi { pub fn abi_string(&self) -> Option<ast::String> { support::token(&self.syntax, SyntaxKind::STRING).and_then(ast::String::cast) @@ -192,53 +202,95 @@ impl AttrKind { } } -impl ast::Attr { +impl ast::Meta { pub fn as_simple_atom(&self) -> Option<SmolStr> { - let meta = self.meta()?; - if meta.eq_token().is_some() || meta.token_tree().is_some() { - return None; - } - self.simple_name() + Some(self.as_simple_path()?.as_single_name_ref()?.text().into()) } pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> { - let tt = self.meta()?.token_tree()?; - Some((self.simple_name()?, tt)) + let ast::Meta::TokenTreeMeta(meta) = self else { return None }; + Some((meta.path()?.as_single_name_ref()?.text().into(), meta.token_tree()?)) } pub fn as_simple_path(&self) -> Option<ast::Path> { - let meta = self.meta()?; - if meta.eq_token().is_some() || meta.token_tree().is_some() { - return None; - } - self.path() + let ast::Meta::PathMeta(meta) = self else { return None }; + meta.path() } pub fn simple_name(&self) -> Option<SmolStr> { - let path = self.meta()?.path()?; - match (path.segment(), path.qualifier()) { - (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()), - _ => None, + match self { + ast::Meta::CfgAttrMeta(_) => Some(SmolStr::new_static("cfg_attr")), + ast::Meta::CfgMeta(_) => Some(SmolStr::new_static("cfg")), + _ => { + let path = self.path()?; + match (path.segment(), path.qualifier()) { + (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()), + _ => None, + } + } } } - pub fn kind(&self) -> AttrKind { - match self.excl_token() { - Some(_) => AttrKind::Inner, - None => AttrKind::Outer, + pub fn path(&self) -> Option<ast::Path> { + match self { + ast::Meta::CfgAttrMeta(_) | ast::Meta::CfgMeta(_) => None, + ast::Meta::KeyValueMeta(it) => it.path(), + ast::Meta::PathMeta(it) => it.path(), + ast::Meta::TokenTreeMeta(it) => it.path(), + ast::Meta::UnsafeMeta(it) => it.meta()?.path(), } } + /// Includes `cfg_attr()` inner metas (without considering the predicate). + pub fn skip_cfg_attrs(self) -> SmallVec<[ast::Meta; 1]> { + match self { + ast::Meta::CfgAttrMeta(meta) => { + meta.metas().flat_map(|meta| meta.skip_cfg_attrs()).collect() + } + _ => smallvec![self], + } + } + + /// FIXME: Calling this is almost always incorrect, as `cfg_attr` can contains multiple `Meta`s. + pub fn parent_attr(&self) -> Option<ast::Attr> { + self.syntax().ancestors().find_map(ast::Attr::cast) + } +} + +impl ast::Attr { + pub fn as_simple_atom(&self) -> Option<SmolStr> { + self.meta().and_then(|meta| meta.as_simple_atom()) + } + + pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> { + self.meta().and_then(|meta| meta.as_simple_call()) + } + + pub fn as_simple_path(&self) -> Option<ast::Path> { + self.meta().and_then(|meta| meta.as_simple_path()) + } + + pub fn simple_name(&self) -> Option<SmolStr> { + self.meta().and_then(|meta| meta.simple_name()) + } + pub fn path(&self) -> Option<ast::Path> { - self.meta()?.path() + self.meta().and_then(|meta| meta.path()) } - pub fn expr(&self) -> Option<ast::Expr> { - self.meta()?.expr() + pub fn kind(&self) -> AttrKind { + match self.excl_token() { + Some(_) => AttrKind::Inner, + None => AttrKind::Outer, + } } - pub fn token_tree(&self) -> Option<ast::TokenTree> { - self.meta()?.token_tree() + /// Includes `cfg_attr()` inner metas (without considering the predicate). + pub fn skip_cfg_attrs(&self) -> SmallVec<[ast::Meta; 1]> { + match self.meta() { + Some(meta) => meta.skip_cfg_attrs(), + None => SmallVec::new(), + } } } @@ -1006,12 +1058,6 @@ impl ast::TokenTree { } } -impl ast::Meta { - pub fn parent_attr(&self) -> Option<ast::Attr> { - self.syntax().parent().and_then(ast::Attr::cast) - } -} - impl ast::GenericArgList { pub fn lifetime_args(&self) -> impl Iterator<Item = ast::LifetimeArg> { self.generic_args().filter_map(|arg| match arg { @@ -1037,6 +1083,21 @@ impl ast::GenericParamList { } } +impl ast::ArgList { + /// Comma separated args, argument may be empty + pub fn args_maybe_empty(&self) -> impl Iterator<Item = Option<ast::Expr>> { + // (Expr? ','?)* + let mut after_arg = false; + self.syntax().children_with_tokens().filter_map(move |it| { + if into_comma(it.clone()).is_some() { + if std::mem::take(&mut after_arg) { None } else { Some(None) } + } else { + Some(ast::Expr::cast(it.into_node()?).inspect(|_| after_arg = true)) + } + }) + } +} + impl ast::ForExpr { pub fn iterable(&self) -> Option<ast::Expr> { // If the iterable is a BlockExpr, check if the body is missing. @@ -1140,6 +1201,25 @@ impl ast::OrPat { } } +#[derive(Debug, Clone)] +pub enum CfgAtomKey { + True, + False, + Ident(SyntaxToken), +} + +impl ast::CfgAtom { + pub fn key(&self) -> Option<CfgAtomKey> { + if self.true_token().is_some() { + Some(CfgAtomKey::True) + } else if self.false_token().is_some() { + Some(CfgAtomKey::False) + } else { + self.ident_token().map(CfgAtomKey::Ident) + } + } +} + /// An iterator over the elements in an [`ast::TokenTree`]. /// /// Does not yield trivia or the delimiters. diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 44114a7802..c66f096e83 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1848,7 +1848,36 @@ impl SyntaxFactory { if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone()); - builder.map_node(tt.syntax().clone(), ast.token_tree().unwrap().syntax().clone()); + let ast::Meta::TokenTreeMeta(meta) = &ast else { unreachable!() }; + builder.map_node(tt.syntax().clone(), meta.token_tree().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn cfg_flag(&self, flag: &str) -> ast::CfgPredicate { + make::cfg_flag(flag).clone_for_update() + } + + pub fn cfg_attr_meta( + &self, + predicate: ast::CfgPredicate, + inner: impl IntoIterator<Item = ast::Meta>, + ) -> ast::CfgAttrMeta { + let inner = Vec::from_iter(inner); + let ast = make::cfg_attr_meta(predicate.clone(), inner.iter().cloned()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node( + predicate.syntax().clone(), + ast.cfg_predicate().unwrap().syntax().clone(), + ); + builder.map_children( + inner.iter().map(|it| it.syntax().clone()), + ast.metas().map(|it| it.syntax().clone()), + ); builder.finish(&mut mapping); } @@ -1960,6 +1989,47 @@ impl SyntaxFactory { ast } + pub fn trait_( + &self, + is_unsafe: bool, + ident: &str, + generic_param_list: Option<ast::GenericParamList>, + where_clause: Option<ast::WhereClause>, + assoc_items: ast::AssocItemList, + ) -> ast::Trait { + let ast = make::trait_( + is_unsafe, + ident, + generic_param_list.clone(), + where_clause.clone(), + assoc_items.clone(), + ) + .clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + if let Some(generic_param_list) = generic_param_list { + builder.map_node( + generic_param_list.syntax().clone(), + ast.generic_param_list().unwrap().syntax().clone(), + ); + } + if let Some(where_clause) = where_clause { + builder.map_node( + where_clause.syntax().clone(), + ast.where_clause().unwrap().syntax().clone(), + ); + } + builder.map_node( + assoc_items.syntax().clone(), + ast.assoc_item_list().unwrap().syntax().clone(), + ); + builder.finish(&mut mapping); + } + + ast + } + pub fn ret_type(&self, ty: ast::Type) -> ast::RetType { let ast = make::ret_type(ty.clone()).clone_for_update(); @@ -2053,6 +2123,17 @@ impl SyntaxFactory { self.path_unqualified(self.path_segment(self.name_ref(ident))) } + pub fn path_from_idents<'a>( + &self, + parts: impl IntoIterator<Item = &'a str>, + ) -> Option<ast::Path> { + make::ext::path_from_idents(parts).map(|path| path.clone_for_update()) + } + + pub fn token_tree_from_node(&self, node: &SyntaxNode) -> ast::TokenTree { + make::ext::token_tree_from_node(node).clone_for_update() + } + pub fn expr_unit(&self) -> ast::Expr { self.expr_tuple([]).into() } diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs index 2f4109a2c9..6fe5abb84e 100644 --- a/crates/syntax/src/ast/traits.rs +++ b/crates/syntax/src/ast/traits.rs @@ -73,9 +73,6 @@ pub trait HasAttrs: AstNode { fn attrs(&self) -> AstChildren<ast::Attr> { support::children(self.syntax()) } - fn has_atom_attr(&self, atom: &str) -> bool { - self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom) - } /// This may return the same node as called with (with `SourceFile`). The caller has the responsibility /// to avoid duplicate attributes. diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index e6937e4d0f..8e4dc75d22 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs @@ -14,7 +14,10 @@ use std::{ use rowan::TextRange; use rustc_hash::FxHashMap; -use crate::{SyntaxElement, SyntaxNode, SyntaxToken}; +use crate::{ + AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, + ast::{self, edit::IndentLevel, syntax_factory::SyntaxFactory}, +}; mod edit_algo; mod edits; @@ -32,9 +35,37 @@ pub struct SyntaxEditor { } impl SyntaxEditor { - /// Creates a syntax editor to start editing from `root` - pub fn new(root: SyntaxNode) -> Self { - Self { root, changes: vec![], mappings: SyntaxMapping::default(), annotations: vec![] } + /// Creates a syntax editor from `root`. + /// + /// The returned `root` is guaranteed to be a detached, immutable node. + /// If the provided node is not a root (i.e., has a parent) or is already + /// mutable, it is cloned into a fresh subtree to satisfy syntax editor + /// invariants. + pub fn new(root: SyntaxNode) -> (Self, SyntaxNode) { + let mut root = root; + + if root.parent().is_some() || root.is_mutable() { + root = root.clone_subtree() + }; + + let editor = Self { + root: root.clone(), + changes: Vec::new(), + mappings: SyntaxMapping::default(), + annotations: Vec::new(), + }; + + (editor, root) + } + + /// Typed-node variant of [`SyntaxEditor::new`]. + pub fn with_ast_node<T>(root: &T) -> (Self, T) + where + T: AstNode, + { + let (editor, root) = Self::new(root.syntax().clone()); + + (editor, T::cast(root).unwrap()) } pub fn add_annotation(&mut self, element: impl Element, annotation: SyntaxAnnotation) { @@ -73,6 +104,34 @@ impl SyntaxEditor { self.changes.push(Change::InsertAll(position, elements)) } + pub fn insert_with_whitespace( + &mut self, + position: Position, + element: impl Element, + factory: &SyntaxFactory, + ) { + self.insert_all_with_whitespace(position, vec![element.syntax_element()], factory) + } + + pub fn insert_all_with_whitespace( + &mut self, + position: Position, + mut elements: Vec<SyntaxElement>, + factory: &SyntaxFactory, + ) { + if let Some(first) = elements.first() + && let Some(ws) = ws_before(&position, first, factory) + { + elements.insert(0, ws.into()); + } + if let Some(last) = elements.last() + && let Some(ws) = ws_after(&position, last, factory) + { + elements.push(ws.into()); + } + self.insert_all(position, elements) + } + pub fn delete(&mut self, element: impl Element) { let element = element.syntax_element(); debug_assert!(is_ancestor_or_self_of_element(&element, &self.root)); @@ -384,6 +443,86 @@ impl Element for SyntaxToken { } } +fn ws_before( + position: &Position, + new: &SyntaxElement, + factory: &SyntaxFactory, +) -> Option<SyntaxToken> { + let prev = match &position.repr { + PositionRepr::FirstChild(_) => return None, + PositionRepr::After(it) => it, + }; + + if prev.kind() == T!['{'] + && new.kind() == SyntaxKind::USE + && let Some(item_list) = prev.parent().and_then(ast::ItemList::cast) + { + let mut indent = IndentLevel::from_element(&item_list.syntax().clone().into()); + indent.0 += 1; + return Some(factory.whitespace(&format!("\n{indent}"))); + } + + if prev.kind() == T!['{'] + && ast::Stmt::can_cast(new.kind()) + && let Some(stmt_list) = prev.parent().and_then(ast::StmtList::cast) + { + let mut indent = IndentLevel::from_element(&stmt_list.syntax().clone().into()); + indent.0 += 1; + return Some(factory.whitespace(&format!("\n{indent}"))); + } + + ws_between(prev, new, factory) +} + +fn ws_after( + position: &Position, + new: &SyntaxElement, + factory: &SyntaxFactory, +) -> Option<SyntaxToken> { + let next = match &position.repr { + PositionRepr::FirstChild(parent) => parent.first_child_or_token()?, + PositionRepr::After(sibling) => sibling.next_sibling_or_token()?, + }; + ws_between(new, &next, factory) +} + +fn ws_between( + left: &SyntaxElement, + right: &SyntaxElement, + factory: &SyntaxFactory, +) -> Option<SyntaxToken> { + if left.kind() == SyntaxKind::WHITESPACE || right.kind() == SyntaxKind::WHITESPACE { + return None; + } + if right.kind() == T![;] || right.kind() == T![,] { + return None; + } + if left.kind() == T![<] || right.kind() == T![>] { + return None; + } + if left.kind() == T![&] && right.kind() == SyntaxKind::LIFETIME { + return None; + } + if right.kind() == SyntaxKind::GENERIC_ARG_LIST { + return None; + } + if right.kind() == SyntaxKind::USE { + let mut indent = IndentLevel::from_element(left); + if left.kind() == SyntaxKind::USE { + indent.0 = IndentLevel::from_element(right).0.max(indent.0); + } + return Some(factory.whitespace(&format!("\n{indent}"))); + } + if left.kind() == SyntaxKind::ATTR { + let mut indent = IndentLevel::from_element(right); + if right.kind() == SyntaxKind::ATTR { + indent.0 = IndentLevel::from_element(left).0.max(indent.0); + } + return Some(factory.whitespace(&format!("\n{indent}"))); + } + Some(factory.whitespace(" ")) +} + fn is_ancestor_or_self(node: &SyntaxNode, ancestor: &SyntaxNode) -> bool { node == ancestor || node.ancestors().any(|it| &it == ancestor) } @@ -420,10 +559,11 @@ mod tests { .into(), ); + let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap(); - let mut editor = SyntaxEditor::new(root.syntax().clone()); let make = SyntaxFactory::with_mappings(); let name = make::name("var_name"); @@ -478,9 +618,8 @@ mod tests { None, ); + let (mut editor, root) = SyntaxEditor::with_ast_node(&root); let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); - - let mut editor = SyntaxEditor::new(root.syntax().clone()); let make = SyntaxFactory::without_mappings(); editor.insert( @@ -530,11 +669,12 @@ mod tests { ), ); + let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + let inner_block = root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap(); let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); - let mut editor = SyntaxEditor::new(root.syntax().clone()); let make = SyntaxFactory::with_mappings(); let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); @@ -584,9 +724,9 @@ mod tests { None, ); - let inner_block = root.clone(); + let (mut editor, root) = SyntaxEditor::with_ast_node(&root); - let mut editor = SyntaxEditor::new(root.syntax().clone()); + let inner_block = root; let make = SyntaxFactory::with_mappings(); let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); @@ -632,7 +772,7 @@ mod tests { false, ); - let mut editor = SyntaxEditor::new(parent_fn.syntax().clone()); + let (mut editor, parent_fn) = SyntaxEditor::with_ast_node(&parent_fn); if let Some(ret_ty) = parent_fn.ret_type() { editor.delete(ret_ty.syntax().clone()); @@ -659,7 +799,8 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let mut editor = SyntaxEditor::new(arg_list.syntax().clone()); + let (mut editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let target_expr = make::token(parser::SyntaxKind::UNDERSCORE); for arg in arg_list.args() { @@ -677,7 +818,8 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let mut editor = SyntaxEditor::new(arg_list.syntax().clone()); + let (mut editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let target_expr = make::expr_literal("3").clone_for_update(); for arg in arg_list.args() { @@ -695,7 +837,8 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let mut editor = SyntaxEditor::new(arg_list.syntax().clone()); + let (mut editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let target_expr = make::ext::expr_unit().clone_for_update(); for arg in arg_list.args() { diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs index e697d97061..78e7083f97 100644 --- a/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/crates/syntax/src/syntax_editor/edit_algo.rs @@ -192,11 +192,8 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } }; } - Change::Replace(SyntaxElement::Node(target), Some(SyntaxElement::Node(new_target))) => { + Change::Replace(SyntaxElement::Node(target), Some(SyntaxElement::Node(_))) => { *target = tree_mutator.make_syntax_mut(target); - if new_target.ancestors().any(|node| node == tree_mutator.immutable) { - *new_target = new_target.clone_for_update(); - } } Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { *target = tree_mutator.make_element_mut(target); @@ -210,6 +207,56 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } match &mut changes[index as usize] { + Change::Insert(_, SyntaxElement::Node(node)) + | Change::Replace(_, Some(SyntaxElement::Node(node))) => { + if node.parent().is_some() { + *node = node.clone_subtree().clone_for_update(); + } else if !node.is_mutable() { + *node = node.clone_for_update(); + } + } + Change::Insert(_, SyntaxElement::Token(token)) + | Change::Replace(_, Some(SyntaxElement::Token(token))) => { + if let Some(parent) = token.parent() { + let idx = token.index(); + let new_parent = parent.clone_subtree().clone_for_update(); + *token = new_parent + .children_with_tokens() + .nth(idx) + .and_then(SyntaxElement::into_token) + .unwrap(); + } + } + Change::InsertAll(_, elements) + | Change::ReplaceWithMany(_, elements) + | Change::ReplaceAll(_, elements) => { + for element in elements { + match element { + SyntaxElement::Node(node) => { + if node.parent().is_some() { + *node = node.clone_subtree().clone_for_update(); + } else if !node.is_mutable() { + *node = node.clone_for_update(); + } + } + SyntaxElement::Token(token) => { + if let Some(parent) = token.parent() { + let idx = token.index(); + let new_parent = parent.clone_subtree().clone_for_update(); + *token = new_parent + .children_with_tokens() + .nth(idx) + .and_then(SyntaxElement::into_token) + .unwrap(); + } + } + } + } + } + _ => {} + } + + match &mut changes[index as usize] { Change::Insert(_, element) | Change::Replace(_, Some(element)) => { deduplicate_node(element); } diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs index 44f0a8038e..d741adb6e3 100644 --- a/crates/syntax/src/syntax_editor/edits.rs +++ b/crates/syntax/src/syntax_editor/edits.rs @@ -229,6 +229,25 @@ impl ast::AssocItemList { } } +impl ast::Impl { + pub fn get_or_create_assoc_item_list_with_editor( + &self, + editor: &mut SyntaxEditor, + make: &SyntaxFactory, + ) -> ast::AssocItemList { + if let Some(list) = self.assoc_item_list() { + list + } else { + let list = make.assoc_item_list_empty(); + editor.insert_all( + Position::last_child_of(self.syntax()), + vec![make.whitespace(" ").into(), list.syntax().clone().into()], + ); + list + } + } +} + impl ast::VariantList { pub fn add_variant(&self, editor: &mut SyntaxEditor, variant: &ast::Variant) { let make = SyntaxFactory::without_mappings(); @@ -473,8 +492,7 @@ enum Foo { } fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) { - let enum_ = ast_from_text::<ast::Enum>(before); - let mut editor = SyntaxEditor::new(enum_.syntax().clone()); + let (mut editor, enum_) = SyntaxEditor::with_ast_node(&ast_from_text::<ast::Enum>(before)); if let Some(it) = enum_.variant_list() { it.add_variant(&mut editor, &variant) } diff --git a/crates/syntax/src/syntax_editor/mapping.rs b/crates/syntax/src/syntax_editor/mapping.rs index 6257bf4e57..180c2e69fa 100644 --- a/crates/syntax/src/syntax_editor/mapping.rs +++ b/crates/syntax/src/syntax_editor/mapping.rs @@ -161,7 +161,7 @@ impl SyntaxMapping { // Try to follow the mapping tree, if it exists let input_mapping = self.upmap_node_single(input); let input_ancestor = - input.ancestors().find_map(|ancestor| self.upmap_node_single(&ancestor)); + input.ancestors().find(|ancestor| self.upmap_node_single(ancestor).is_some()); match (input_mapping, input_ancestor) { (Some(input_mapping), _) => { diff --git a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast index 50057a02d8..5fdde93c60 100644 --- a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast +++ b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast @@ -29,7 +29,7 @@ [email protected] @@ -60,7 +60,7 @@ [email protected] @@ -75,7 +75,7 @@ [email protected] @@ -104,7 +104,7 @@ [email protected] diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs index e271c32c86..f346535ca1 100644 --- a/crates/test-fixture/src/lib.rs +++ b/crates/test-fixture/src/lib.rs @@ -11,7 +11,7 @@ use base_db::target::TargetData; use base_db::{ Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase, - SourceRoot, Version, VfsPath, + SourceRoot, Version, VfsPath, all_crates, }; use cfg::CfgOptions; use hir_expand::{ @@ -227,7 +227,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { } fn test_crate(&self) -> Crate { - self.all_crates().iter().copied().find(|&krate| !krate.data(self).origin.is_lang()).unwrap() + all_crates(self).iter().copied().find(|&krate| !krate.data(self).origin.is_lang()).unwrap() } } diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index 428b19c50b..6465a85d2d 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -14,7 +14,7 @@ use std::{ }; use crossbeam_channel::{Receiver, Sender, select, unbounded}; -use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; +use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher, event::AccessKind}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rayon::iter::{IndexedParallelIterator as _, IntoParallelIterator as _, ParallelIterator}; use rustc_hash::FxHashSet; @@ -63,6 +63,7 @@ struct NotifyActor { sender: loader::Sender, watched_file_entries: FxHashSet<AbsPathBuf>, watched_dir_entries: Vec<loader::Directories>, + seen_paths: FxHashSet<AbsPathBuf>, // Drop order is significant. watcher: Option<(RecommendedWatcher, Receiver<NotifyEvent>)>, } @@ -79,6 +80,7 @@ impl NotifyActor { sender, watched_dir_entries: Vec::new(), watched_file_entries: FxHashSet::default(), + seen_paths: FxHashSet::default(), watcher: None, } } @@ -120,6 +122,7 @@ impl NotifyActor { let n_total = config.load.len(); self.watched_dir_entries.clear(); self.watched_file_entries.clear(); + self.seen_paths.clear(); self.send(loader::Message::Progress { n_total, @@ -195,10 +198,12 @@ impl NotifyActor { }, Event::NotifyEvent(event) => { if let Some(event) = log_notify_error(event) - && let EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) = - event.kind + && let EventKind::Create(_) + | EventKind::Modify(_) + | EventKind::Remove(_) + | EventKind::Access(AccessKind::Open(_)) = event.kind { - let files = event + let abs_paths: Vec<AbsPathBuf> = event .paths .into_iter() .filter_map(|path| { @@ -207,6 +212,28 @@ impl NotifyActor { .expect("path is absolute"), ) }) + .collect(); + + let mut saw_new_file = false; + for abs_path in &abs_paths { + if self.seen_paths.insert(abs_path.clone()) { + saw_new_file = true; + } + } + + // Only consider access events for files that we haven't seen + // before. + // + // This is important on FUSE filesystems, where we may not get a + // Create event. In other cases we're about to access the file, so + // we don't want an infinite loop where processing an Access event + // creates another Access event. + if matches!(event.kind, EventKind::Access(_)) && !saw_new_file { + continue; + } + + let files = abs_paths + .into_iter() .filter_map(|path| -> Option<(AbsPathBuf, Option<Vec<u8>>)> { // Ignore events for files/directories that we're not watching. if !(self.watched_file_entries.contains(&path) diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 50e388d780..d48b984407 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -157,7 +157,7 @@ pub enum Change { } /// Kind of [file change](ChangedFile). -#[derive(Eq, PartialEq, Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ChangeKind { /// The file was (re-)created Create, diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md index 35fba5accd..da37fc1582 100644 --- a/docs/book/src/configuration_generated.md +++ b/docs/book/src/configuration_generated.md @@ -166,6 +166,14 @@ List of features to activate. Set this to `"all"` to pass `--all-features` to cargo. +## rust-analyzer.cargo.metadataExtraArgs {#cargo.metadataExtraArgs} + +Default: `[]` + +Extra arguments passed only to `cargo metadata`, not to other cargo invocations. +Useful for flags like `--config` that `cargo metadata` supports. + + ## rust-analyzer.cargo.noDefaultFeatures {#cargo.noDefaultFeatures} Default: `false` @@ -618,6 +626,15 @@ The warnings will be indicated by a blue squiggly underline in code and a blue i the `Problems Panel`. +## rust-analyzer.disableFixtureSupport {#disableFixtureSupport} + +Default: `false` + +Disable support for `#[rust_analyzer::rust_fixture]` snippets. + +If you are not working on rust-analyzer itself, you should ignore this config. + + ## rust-analyzer.document.symbol.search.excludeLocals {#document.symbol.search.excludeLocals} Default: `true` @@ -1147,6 +1164,13 @@ Default: `false` Hide inlay type hints for constructors. +## rust-analyzer.inlayHints.typeHints.location {#inlayHints.typeHints.location} + +Default: `"inline"` + +Where to render type hints relative to their binding pattern. + + ## rust-analyzer.interpret.tests {#interpret.tests} Default: `false` @@ -1380,9 +1404,15 @@ Default: `null` Override the command used for bench runnables. The first element of the array should be the program to execute (for example, `cargo`). -Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically -replace the package name, target option (such as `--bin` or `--example`), the target name and -the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). +Use the placeholders: +- `${package}`: package name. +- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc. +- `${target}`: target name (empty for `--lib`). +- `${test_name}`: the test path filter, e.g. `module::bench_func`. +- `${exact}`: `--exact` for single benchmarks, empty for modules. +- `${include_ignored}`: always empty for benchmarks. +- `${executable_args}`: all of the above binary args bundled together + (includes `rust-analyzer.runnables.extraTestBinaryArgs`). ## rust-analyzer.runnables.command {#runnables.command} @@ -1396,12 +1426,18 @@ Command to be executed instead of 'cargo' for runnables. Default: `null` -Override the command used for bench runnables. +Override the command used for doc-test runnables. The first element of the array should be the program to execute (for example, `cargo`). -Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically -replace the package name, target option (such as `--bin` or `--example`), the target name and -the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). +Use the placeholders: +- `${package}`: package name. +- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc. +- `${target}`: target name (empty for `--lib`). +- `${test_name}`: the test path filter, e.g. `module::func`. +- `${exact}`: always empty for doc-tests. +- `${include_ignored}`: always empty for doc-tests. +- `${executable_args}`: all of the above binary args bundled together + (includes `rust-analyzer.runnables.extraTestBinaryArgs`). ## rust-analyzer.runnables.extraArgs {#runnables.extraArgs} @@ -1444,9 +1480,15 @@ Default: `null` Override the command used for test runnables. The first element of the array should be the program to execute (for example, `cargo`). -Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically -replace the package name, target option (such as `--bin` or `--example`), the target name and -the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`). +Available placeholders: +- `${package}`: package name. +- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc. +- `${target}`: target name (empty for `--lib`). +- `${test_name}`: the test path filter, e.g. `module::test_func`. +- `${exact}`: `--exact` for single tests, empty for modules. +- `${include_ignored}`: `--include-ignored` for single tests, empty otherwise. +- `${executable_args}`: all of the above binary args bundled together + (includes `rust-analyzer.runnables.extraTestBinaryArgs`). ## rust-analyzer.rustc.source {#rustc.source} diff --git a/docs/book/src/contributing/lsp-extensions.md b/docs/book/src/contributing/lsp-extensions.md index 5d21c37806..22c1784ac2 100644 --- a/docs/book/src/contributing/lsp-extensions.md +++ b/docs/book/src/contributing/lsp-extensions.md @@ -236,7 +236,7 @@ fn main() { ``` The primary goal of `onEnter` is to handle automatic indentation when opening a new line. -This is not yet implemented. +This is partially implemented for single-line brace-delimited contents, in addition to comment continuation. The secondary goal is to handle fixing up syntax, like continuing doc strings and comments, and escaping `\n` in string literals. As proper cursor positioning is raison d'être for `onEnter`, it uses `SnippetTextEdit`. diff --git a/docs/book/src/other_editors.md b/docs/book/src/other_editors.md index f7116fc19a..1cb2a44063 100644 --- a/docs/book/src/other_editors.md +++ b/docs/book/src/other_editors.md @@ -137,24 +137,22 @@ To use the LSP server in [ale](https://github.com/dense-analysis/ale): ### nvim-lsp -Neovim 0.5 has built-in language server support. For a quick start -configuration of rust-analyzer, use -[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig#rust_analyzer). -Once `neovim/nvim-lspconfig` is installed, use -`lua require'lspconfig'.rust_analyzer.setup({})` in your `init.vim`. - -You can also pass LSP settings to the server: +Neovim 0.5+ added build-in support for language server with most of the heavy +lifting happening in "framework" plugins such as +[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig). +Since v0.11+ Neovim has full featured LSP support. nvim-lspconfig is +still recommended to get the +[rust-analyzer config](https://github.com/neovim/nvim-lspconfig/blob/master/lsp/rust_analyzer.lua) +for free. + +1. Install [neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) +2. Add `lua vim.lsp.enable('rust-analyzer')` to your `init.vim` +3. Customize your setup. ```lua lua << EOF -local lspconfig = require'lspconfig' - -local on_attach = function(client) - require'completion'.on_attach(client) -end - -lspconfig.rust_analyzer.setup({ - on_attach = on_attach, +-- You can pass LSP settings to the server: +vim.lsp.config("rust_analyzer", { settings = { ["rust-analyzer"] = { imports = { @@ -171,30 +169,35 @@ lspconfig.rust_analyzer.setup({ procMacro = { enable = true }, - } - } + }, + }, }) -EOF -``` - -If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`: -```lua -lspconfig.rust_analyzer.setup({ - on_attach = function(client, bufnr) - vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) - end +-- You can enable different LSP features +vim.api.nvim_create_autocmd("LspAttach", { + callback = function(ev) + local client = assert(vim.lsp.get_client_by_id(ev.data.client_id)) + -- Inlay hints display inferred types, etc. + if client:supports_method("inlayHint/resolve") then + vim.lsp.inlay_hint.enable(true, { bufnr = ev.buf }) + end + -- Completion can be invoked via ctrl+x ctrl+o. It displays a list of + -- names inferred from the context (e.g. method names, variables, etc.) + if client:supports_method("textDocument/completion") then + vim.lsp.completion.enable(true, client.id, ev.buf, {}) + end + end, }) +EOF ``` -Note that the hints are only visible after `rust-analyzer` has finished loading **and** you have to -edit the file to trigger a re-render. - -See <https://sharksforarms.dev/posts/neovim-rust/> for more tips on -getting started. +Note that the hints are only visible after `rust-analyzer` has finished loading +**and** you have to edit the file to trigger a re-render. -Check out <https://github.com/mrcjkb/rustaceanvim> for a batteries -included rust-analyzer setup for Neovim. +The instructions here use the 0.11+ API, if you're running an older version, you +can follow this guide <https://sharksforarms.dev/posts/neovim-rust/> or check +out <https://github.com/mrcjkb/rustaceanvim> for a batteries included +rust-analyzer setup for Neovim. ### vim-lsp diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index b51dc4d132..5755f0708f 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -17,8 +17,8 @@ "vscode-languageclient": "^9.0.1" }, "devDependencies": { - "@eslint/js": "^9.21.0", - "@stylistic/eslint-plugin": "^4.1.0", + "@eslint/js": "^10.0.1", + "@stylistic/eslint-plugin": "^5.10.0", "@stylistic/eslint-plugin-js": "^4.1.0", "@tsconfig/strictest": "^2.0.5", "@types/lodash": "^4.17.20", @@ -27,16 +27,16 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-electron": "^2.4.1", - "@vscode/vsce": "^3.6.0", + "@vscode/vsce": "^3.7.1", "esbuild": "^0.25.0", - "eslint": "^9.21.0", - "eslint-config-prettier": "^10.0.2", + "eslint": "^10.2.0", + "eslint-config-prettier": "^10.1.8", "eslint-define-config": "^2.1.0", - "ovsx": "0.10.1", - "prettier": "^3.5.2", + "ovsx": "0.10.10", + "prettier": "^3.8.1", "tslib": "^2.8.1", - "typescript": "^5.7.3", - "typescript-eslint": "^8.25.0" + "typescript": "^6.0.2", + "typescript-eslint": "^8.58.0" }, "engines": { "vscode": "^1.93.0" @@ -255,6 +255,40 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", @@ -681,9 +715,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -713,9 +747,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -723,137 +757,89 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.4.tgz", + "integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^3.0.4", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^10.2.4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "node_modules/@eslint/config-helpers": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.4.tgz", + "integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==", "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@eslint/core": "^1.2.0" }, "engines": { - "node": "*" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.0.tgz", + "integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==", "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", - "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" + "url": "https://eslint.org/donate" }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.21.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz", - "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.4.tgz", + "integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", - "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz", + "integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.12.0", + "@eslint/core": "^1.2.0", "levn": "^0.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@hpcc-js/wasm": { @@ -952,6 +938,287 @@ "node": ">=12" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@node-rs/crc32": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32/-/crc32-1.10.6.tgz", + "integrity": "sha512-+llXfqt+UzgoDzT9of5vPQPGqTAVCohU74I9zIBkNo5TH6s2P31DFJOGsJQKN207f0GHnYv5pV3wh3BCY/un/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@node-rs/crc32-android-arm-eabi": "1.10.6", + "@node-rs/crc32-android-arm64": "1.10.6", + "@node-rs/crc32-darwin-arm64": "1.10.6", + "@node-rs/crc32-darwin-x64": "1.10.6", + "@node-rs/crc32-freebsd-x64": "1.10.6", + "@node-rs/crc32-linux-arm-gnueabihf": "1.10.6", + "@node-rs/crc32-linux-arm64-gnu": "1.10.6", + "@node-rs/crc32-linux-arm64-musl": "1.10.6", + "@node-rs/crc32-linux-x64-gnu": "1.10.6", + "@node-rs/crc32-linux-x64-musl": "1.10.6", + "@node-rs/crc32-wasm32-wasi": "1.10.6", + "@node-rs/crc32-win32-arm64-msvc": "1.10.6", + "@node-rs/crc32-win32-ia32-msvc": "1.10.6", + "@node-rs/crc32-win32-x64-msvc": "1.10.6" + } + }, + "node_modules/@node-rs/crc32-android-arm-eabi": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-android-arm-eabi/-/crc32-android-arm-eabi-1.10.6.tgz", + "integrity": "sha512-vZAMuJXm3TpWPOkkhxdrofWDv+Q+I2oO7ucLRbXyAPmXFNDhHtBxbO1rk9Qzz+M3eep8ieS4/+jCL1Q0zacNMQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-android-arm64": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-android-arm64/-/crc32-android-arm64-1.10.6.tgz", + "integrity": "sha512-Vl/JbjCinCw/H9gEpZveWCMjxjcEChDcDBM8S4hKay5yyoRCUHJPuKr4sjVDBeOm+1nwU3oOm6Ca8dyblwp4/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-darwin-arm64": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-darwin-arm64/-/crc32-darwin-arm64-1.10.6.tgz", + "integrity": "sha512-kARYANp5GnmsQiViA5Qu74weYQ3phOHSYQf0G+U5wB3NB5JmBHnZcOc46Ig21tTypWtdv7u63TaltJQE41noyg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-darwin-x64": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-darwin-x64/-/crc32-darwin-x64-1.10.6.tgz", + "integrity": "sha512-Q99bevJVMfLTISpkpKBlXgtPUItrvTWKFyiqoKH5IvscZmLV++NH4V13Pa17GTBmv9n18OwzgQY4/SRq6PQNVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-freebsd-x64": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-freebsd-x64/-/crc32-freebsd-x64-1.10.6.tgz", + "integrity": "sha512-66hpawbNjrgnS9EDMErta/lpaqOMrL6a6ee+nlI2viduVOmRZWm9Rg9XdGTK/+c4bQLdtC6jOd+Kp4EyGRYkAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-linux-arm-gnueabihf": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-arm-gnueabihf/-/crc32-linux-arm-gnueabihf-1.10.6.tgz", + "integrity": "sha512-E8Z0WChH7X6ankbVm8J/Yym19Cq3otx6l4NFPS6JW/cWdjv7iw+Sps2huSug+TBprjbcEA+s4TvEwfDI1KScjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-linux-arm64-gnu": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-arm64-gnu/-/crc32-linux-arm64-gnu-1.10.6.tgz", + "integrity": "sha512-LmWcfDbqAvypX0bQjQVPmQGazh4dLiVklkgHxpV4P0TcQ1DT86H/SWpMBMs/ncF8DGuCQ05cNyMv1iddUDugoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-linux-arm64-musl": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-arm64-musl/-/crc32-linux-arm64-musl-1.10.6.tgz", + "integrity": "sha512-k8ra/bmg0hwRrIEE8JL1p32WfaN9gDlUUpQRWsbxd1WhjqvXea7kKO6K4DwVxyxlPhBS9Gkb5Urq7Y4mXANzaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-linux-x64-gnu": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-x64-gnu/-/crc32-linux-x64-gnu-1.10.6.tgz", + "integrity": "sha512-IfjtqcuFK7JrSZ9mlAFhb83xgium30PguvRjIMI45C3FJwu18bnLk1oR619IYb/zetQT82MObgmqfKOtgemEKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-linux-x64-musl": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-x64-musl/-/crc32-linux-x64-musl-1.10.6.tgz", + "integrity": "sha512-LbFYsA5M9pNunOweSt6uhxenYQF94v3bHDAQRPTQ3rnjn+mK6IC7YTAYoBjvoJP8lVzcvk9hRj8wp4Jyh6Y80g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-wasm32-wasi": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-wasm32-wasi/-/crc32-wasm32-wasi-1.10.6.tgz", + "integrity": "sha512-KaejdLgHMPsRaxnM+OG9L9XdWL2TabNx80HLdsCOoX9BVhEkfh39OeahBo8lBmidylKbLGMQoGfIKDjq0YMStw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/crc32-win32-arm64-msvc": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-win32-arm64-msvc/-/crc32-win32-arm64-msvc-1.10.6.tgz", + "integrity": "sha512-x50AXiSxn5Ccn+dCjLf1T7ZpdBiV1Sp5aC+H2ijhJO4alwznvXgWbopPRVhbp2nj0i+Gb6kkDUEyU+508KAdGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-win32-ia32-msvc": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-win32-ia32-msvc/-/crc32-win32-ia32-msvc-1.10.6.tgz", + "integrity": "sha512-DpDxQLaErJF9l36aghe1Mx+cOnYLKYo6qVPqPL9ukJ5rAGLtCdU0C+Zoi3gs9ySm8zmbFgazq/LvmsZYU42aBw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/crc32-win32-x64-msvc": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@node-rs/crc32-win32-x64-msvc/-/crc32-win32-x64-msvc-1.10.6.tgz", + "integrity": "sha512-5B1vXosIIBw1m2Rcnw62IIfH7W9s9f7H7Ma0rRuhT8HR4Xh8QCgw6NJSI2S2MCngsGktYnAhyUvs81b7efTyQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1202,23 +1469,24 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.1.0.tgz", - "integrity": "sha512-bytbL7qiici7yPyEiId0fGPK9kjQbzcPMj2aftPfzTCyJ/CRSKdtI+iVjM0LSGzGxfunflI+MDDU9vyIIeIpoQ==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz", + "integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.23.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/types": "^8.56.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "estraverse": "^5.3.0", - "picomatch": "^4.0.2" + "picomatch": "^4.0.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "eslint": ">=9.0.0" + "eslint": "^9.0.0 || ^10.0.0" } }, "node_modules/@stylistic/eslint-plugin-js": { @@ -1238,6 +1506,37 @@ "eslint": ">=9.0.0" } }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@textlint/ast-node-types": { "version": "15.2.1", "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.1.tgz", @@ -1375,10 +1674,28 @@ "dev": true, "license": "MIT" }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, @@ -1428,21 +1745,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.25.0.tgz", - "integrity": "sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.25.0", - "@typescript-eslint/type-utils": "8.25.0", - "@typescript-eslint/utils": "8.25.0", - "@typescript-eslint/visitor-keys": "8.25.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1452,23 +1768,33 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "@typescript-eslint/parser": "^8.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.25.0.tgz", - "integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.25.0", - "@typescript-eslint/types": "8.25.0", - "@typescript-eslint/typescript-estree": "8.25.0", - "@typescript-eslint/visitor-keys": "8.25.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1478,19 +1804,41 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.25.0.tgz", - "integrity": "sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.25.0", - "@typescript-eslint/visitor-keys": "8.25.0" + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1500,17 +1848,35 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.25.0.tgz", - "integrity": "sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.25.0", - "@typescript-eslint/utils": "8.25.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1520,14 +1886,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.25.0.tgz", - "integrity": "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", "dev": true, "license": "MIT", "engines": { @@ -1539,20 +1905,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.25.0.tgz", - "integrity": "sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.25.0", - "@typescript-eslint/visitor-keys": "8.25.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1562,20 +1929,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.25.0.tgz", - "integrity": "sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.25.0", - "@typescript-eslint/types": "8.25.0", - "@typescript-eslint/typescript-estree": "8.25.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1585,19 +1952,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.25.0.tgz", - "integrity": "sha512-kCYXKAum9CecGVHGij7muybDfTS2sD3t0L4bJsEZLkyrXUImiCTq1M3LG2SRtOhiHFwMR9wAFplpT6XHYjTkwQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.25.0", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1607,6 +1974,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vscode/test-electron": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", @@ -1625,17 +2005,17 @@ } }, "node_modules/@vscode/vsce": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.6.0.tgz", - "integrity": "sha512-u2ZoMfymRNJb14aHNawnXJtXHLXDVKc1oKZaH4VELKT/9iWKRVgtQOdwxCgtwSxJoqYvuK4hGlBWQJ05wxADhg==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.7.1.tgz", + "integrity": "sha512-OTm2XdMt2YkpSn2Nx7z2EJtSuhRHsTPYsSK59hr3v8jRArK+2UEoju4Jumn1CmpgoBLGI6ReHLJ/czYltNUW3g==", "dev": true, "license": "MIT", "dependencies": { "@azure/identity": "^4.1.0", - "@secretlint/node": "^10.1.1", - "@secretlint/secretlint-formatter-sarif": "^10.1.1", - "@secretlint/secretlint-rule-no-dotenv": "^10.1.1", - "@secretlint/secretlint-rule-preset-recommend": "^10.1.1", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", "@vscode/vsce-sign": "^2.0.0", "azure-devops-node-api": "^12.5.0", "chalk": "^4.1.2", @@ -1652,7 +2032,7 @@ "minimatch": "^3.0.3", "parse-semver": "^1.1.1", "read": "^1.0.7", - "secretlint": "^10.1.1", + "secretlint": "^10.1.2", "semver": "^7.5.2", "tmp": "^0.2.3", "typed-rest-client": "^1.8.4", @@ -1841,9 +2221,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { @@ -1874,9 +2254,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -2170,16 +2550,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2900,9 +3270,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -2982,6 +3352,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -2995,6 +3383,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delaunator": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", @@ -3291,33 +3697,30 @@ } }, "node_modules/eslint": { - "version": "9.21.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", - "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", - "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.0", - "@eslint/js": "9.21.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", + "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -3327,8 +3730,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -3336,7 +3738,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" @@ -3351,13 +3753,16 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.2.tgz", - "integrity": "sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { - "eslint-config-prettier": "build/bin/cli.js" + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" }, "peerDependencies": { "eslint": ">=7.0.0" @@ -3367,6 +3772,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-define-config/-/eslint-define-config-2.1.0.tgz", "integrity": "sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "funding": [ { @@ -3386,17 +3792,19 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -3415,17 +3823,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3439,17 +3836,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "brace-expansion": "^1.1.7" + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "*" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { @@ -3485,9 +3900,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3639,6 +4054,24 @@ "pend": "~1.2.0" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3884,56 +4317,21 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/globby": { @@ -3987,13 +4385,6 @@ "dev": true, "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4004,6 +4395,19 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -4157,23 +4561,6 @@ "dev": true, "license": "MIT" }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -4314,6 +4701,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-it-type": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/is-it-type/-/is-it-type-5.1.3.tgz", + "integrity": "sha512-AX2uU0HW+TxagTgQXOJY7+2fbFHemC7YFBwN1XqD8qQMKdtfbOC8OC3fUb4s5NU59a3662Dzwto8tWDdZYRXxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "globalthis": "^1.0.2" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4650,9 +5050,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, @@ -4698,13 +5098,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -4895,21 +5288,44 @@ } }, "node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimatch/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -5065,6 +5481,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5192,23 +5618,23 @@ } }, "node_modules/ovsx": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.10.1.tgz", - "integrity": "sha512-8i7+MJMMeq73m1zPEIClSFe17SNuuzU5br7G77ZIfOC24elB4pGQs0N1qRd+gnnbyhL5Qu96G21nFOVOBa2OBg==", + "version": "0.10.10", + "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.10.10.tgz", + "integrity": "sha512-/X5J4VLKPUGGaMynW9hgvsGg9jmwsK/3RhODeA2yzdeDbb8PUSNcg5GQ9aPDJW/znlqNvAwQcXAyE+Cq0RRvAQ==", "dev": true, "license": "EPL-2.0", "dependencies": { - "@vscode/vsce": "^3.2.1", + "@vscode/vsce": "^3.7.1", "commander": "^6.2.1", "follow-redirects": "^1.14.6", "is-ci": "^2.0.0", "leven": "^3.1.0", "semver": "^7.6.0", "tmp": "^0.2.3", - "yauzl": "^3.1.3" + "yauzl-promise": "^4.0.0" }, "bin": { - "ovsx": "lib/ovsx" + "ovsx": "bin/ovsx" }, "engines": { "node": ">= 20" @@ -5224,20 +5650,6 @@ "node": ">= 6" } }, - "node_modules/ovsx/node_modules/yauzl": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", - "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "pend": "~1.2.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5297,19 +5709,6 @@ "dev": true, "license": "(MIT AND Zlib)" }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parse-json": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", @@ -5524,9 +5923,9 @@ } }, "node_modules/prettier": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", - "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", "bin": { @@ -5744,16 +6143,6 @@ "node": ">=0.10.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/restore-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", @@ -5895,9 +6284,9 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -6074,6 +6463,16 @@ "simple-concat": "^1.0.0" } }, + "node_modules/simple-invariant": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/simple-invariant/-/simple-invariant-2.0.1.tgz", + "integrity": "sha512-1sbhsxqI+I2tqlmjbz99GXNmZtr6tKIyEgGGnJw/MKGblalqk/XoOYYFJlBzTKZCxx8kLaD3FD5s9BEEjx5Pyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -6296,19 +6695,6 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/structured-source": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", @@ -6562,6 +6948,23 @@ "url": "https://bevry.me/fund" } }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tmp": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", @@ -6586,9 +6989,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", - "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -6668,9 +7071,9 @@ } }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6682,15 +7085,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.25.0.tgz", - "integrity": "sha512-TxRdQQLH4g7JkoFlYG3caW5v1S6kEkz8rqt80iQJZUYPq1zD1Ra7HfQBJJ88ABRaMvHAXnwRvRB4V+6sQ9xN5Q==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.25.0", - "@typescript-eslint/parser": "8.25.0", - "@typescript-eslint/utils": "8.25.0" + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6700,8 +7104,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/uc.micro": { @@ -7138,6 +7542,21 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/yauzl-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yauzl-promise/-/yauzl-promise-4.0.0.tgz", + "integrity": "sha512-/HCXpyHXJQQHvFq9noqrjfa/WpQC2XYs3vI7tBiAi4QiIU1knvYhZGaO1QPjwIVMdqflxbmwgMXtYeaRiAE0CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@node-rs/crc32": "^1.7.0", + "is-it-type": "^5.1.2", + "simple-invariant": "^2.0.1" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/yazl": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", diff --git a/editors/code/package.json b/editors/code/package.json index 1dd513c9de..29cbc8bd4f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -31,6 +31,9 @@ "vscode": "^1.93.0" }, "enabledApiProposals": [], + "extensionKind": [ + "workspace" + ], "scripts": { "vscode:prepublish": "npm run build-base -- --minify", "package": "vsce package -o rust-analyzer.vsix", @@ -54,8 +57,8 @@ "vscode-languageclient": "^9.0.1" }, "devDependencies": { - "@eslint/js": "^9.21.0", - "@stylistic/eslint-plugin": "^4.1.0", + "@eslint/js": "^10.0.1", + "@stylistic/eslint-plugin": "^5.10.0", "@stylistic/eslint-plugin-js": "^4.1.0", "@tsconfig/strictest": "^2.0.5", "@types/lodash": "^4.17.20", @@ -64,16 +67,16 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-electron": "^2.4.1", - "@vscode/vsce": "^3.6.0", + "@vscode/vsce": "^3.7.1", "esbuild": "^0.25.0", - "eslint": "^9.21.0", - "eslint-config-prettier": "^10.0.2", + "eslint": "^10.2.0", + "eslint-config-prettier": "^10.1.8", "eslint-define-config": "^2.1.0", - "ovsx": "0.10.1", - "prettier": "^3.5.2", + "ovsx": "0.10.10", + "prettier": "^3.8.1", "tslib": "^2.8.1", - "typescript": "^5.7.3", - "typescript-eslint": "^8.25.0" + "typescript": "^6.0.2", + "typescript-eslint": "^8.58.0" }, "activationEvents": [ "workspaceContains:Cargo.toml", @@ -993,6 +996,19 @@ { "title": "Cargo", "properties": { + "rust-analyzer.cargo.metadataExtraArgs": { + "markdownDescription": "Extra arguments passed only to `cargo metadata`, not to other cargo invocations.\nUseful for flags like `--config` that `cargo metadata` supports.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "title": "Cargo", + "properties": { "rust-analyzer.cargo.noDefaultFeatures": { "markdownDescription": "Whether to pass `--no-default-features` to cargo.", "default": false, @@ -1592,6 +1608,16 @@ } }, { + "title": "rust-analyzer", + "properties": { + "rust-analyzer.disableFixtureSupport": { + "markdownDescription": "Disable support for `#[rust_analyzer::rust_fixture]` snippets.\n\nIf you are not working on rust-analyzer itself, you should ignore this config.", + "default": false, + "type": "boolean" + } + } + }, + { "title": "Document", "properties": { "rust-analyzer.document.symbol.search.excludeLocals": { @@ -2502,6 +2528,24 @@ } }, { + "title": "Inlay Hints", + "properties": { + "rust-analyzer.inlayHints.typeHints.location": { + "markdownDescription": "Where to render type hints relative to their binding pattern.", + "default": "inline", + "type": "string", + "enum": [ + "inline", + "end_of_line" + ], + "enumDescriptions": [ + "Render type hints directly after the binding identifier.", + "Render type hints after the end of the containing `let` statement when possible." + ] + } + } + }, + { "title": "Interpret", "properties": { "rust-analyzer.interpret.tests": { @@ -2865,7 +2909,7 @@ "title": "Runnables", "properties": { "rust-analyzer.runnables.bench.overrideCommand": { - "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).", + "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders:\n- `${package}`: package name.\n- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.\n- `${target}`: target name (empty for `--lib`).\n- `${test_name}`: the test path filter, e.g. `module::bench_func`.\n- `${exact}`: `--exact` for single benchmarks, empty for modules.\n- `${include_ignored}`: always empty for benchmarks.\n- `${executable_args}`: all of the above binary args bundled together\n (includes `rust-analyzer.runnables.extraTestBinaryArgs`).", "default": null, "type": [ "null", @@ -2894,7 +2938,7 @@ "title": "Runnables", "properties": { "rust-analyzer.runnables.doctest.overrideCommand": { - "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).", + "markdownDescription": "Override the command used for doc-test runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders:\n- `${package}`: package name.\n- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.\n- `${target}`: target name (empty for `--lib`).\n- `${test_name}`: the test path filter, e.g. `module::func`.\n- `${exact}`: always empty for doc-tests.\n- `${include_ignored}`: always empty for doc-tests.\n- `${executable_args}`: all of the above binary args bundled together\n (includes `rust-analyzer.runnables.extraTestBinaryArgs`).", "default": null, "type": [ "null", @@ -2948,7 +2992,7 @@ "title": "Runnables", "properties": { "rust-analyzer.runnables.test.overrideCommand": { - "markdownDescription": "Override the command used for test runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).", + "markdownDescription": "Override the command used for test runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nAvailable placeholders:\n- `${package}`: package name.\n- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.\n- `${target}`: target name (empty for `--lib`).\n- `${test_name}`: the test path filter, e.g. `module::test_func`.\n- `${exact}`: `--exact` for single tests, empty for modules.\n- `${include_ignored}`: `--include-ignored` for single tests, empty otherwise.\n- `${executable_args}`: all of the above binary args bundled together\n (includes `rust-analyzer.runnables.extraTestBinaryArgs`).", "default": null, "type": [ "null", diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 5b358e3211..e265cff391 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -1,4 +1,4 @@ -import * as anser from "anser"; +import anser from "anser"; import * as lc from "vscode-languageclient/node"; import * as vscode from "vscode"; import * as ra from "../src/lsp_ext"; diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index c1b6f31030..302f51dee4 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -1194,9 +1194,8 @@ export function runSingle(ctx: CtxInit): Cmd { } export function copyRunCommandLine(ctx: CtxInit) { - let prevRunnable: RunnableQuickPick | undefined; return async () => { - const item = await selectRunnable(ctx, prevRunnable); + const item = await selectRunnable(ctx, undefined); if (!item || !isCargoRunnableArgs(item.runnable.args)) return; const args = createCargoArgs(item.runnable.args); const commandLine = ["cargo", ...args].join(" "); diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 5dc2c419ef..d65f011c75 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -485,7 +485,7 @@ export function substituteVariablesInEnv(env: Env): Env { Object.entries(env).map(([key, value]) => { const deps = new Set<string>(); if (value) { - let match = undefined; + let match; while ((match = depRe.exec(value))) { const depName = unwrapUndefinable(match.groups?.["depName"]); deps.add(depName); diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 24f8d90873..9bc3adad2f 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -48,7 +48,7 @@ export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise< } export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promise<boolean> { - let debugConfig: vscode.DebugConfiguration | undefined = undefined; + let debugConfig: vscode.DebugConfiguration | undefined; let message = ""; const wsLaunchSection = vscode.workspace.getConfiguration("launch"); diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index 203ef5cc85..3c04f2ef64 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -6,9 +6,9 @@ import * as ra from "./lsp_ext"; import type { FetchDependencyListResult } from "./lsp_ext"; import { unwrapUndefinable } from "./util"; -export class RustDependenciesProvider - implements vscode.TreeDataProvider<Dependency | DependencyFile> -{ +export class RustDependenciesProvider implements vscode.TreeDataProvider< + Dependency | DependencyFile +> { dependenciesMap: { [id: string]: Dependency | DependencyFile }; ctx: CtxInit; diff --git a/editors/code/src/diagnostics.ts b/editors/code/src/diagnostics.ts index cd0e43b212..32a41745ed 100644 --- a/editors/code/src/diagnostics.ts +++ b/editors/code/src/diagnostics.ts @@ -1,4 +1,4 @@ -import * as anser from "anser"; +import anser from "anser"; import * as vscode from "vscode"; import { type ProviderResult, diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 9712bd4b7b..cf190ea3ce 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -300,14 +300,14 @@ export type SsrParams = { }; export type RecursiveMemoryLayoutNode = { - item_name: string; + itemName: string; typename: string; size: number; alignment: number; offset: number; - parent_idx: number; - children_start: number; - children_len: number; + parentIdx: number; + childrenStart: number; + childrenLen: number; }; export type RecursiveMemoryLayout = { nodes: RecursiveMemoryLayoutNode[]; diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts index a469a9cd1f..6d75428eaa 100644 --- a/editors/code/src/snippets.ts +++ b/editors/code/src/snippets.ts @@ -53,7 +53,7 @@ export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vs } function hasSnippet(snip: string): boolean { - const m = snip.match(/\$\d+|\{\d+:[^}]*\}/); + const m = snip.match(/\$\d+|\$\{\d+:[^}]*\}/); return m != null; } diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 06f75a8f8d..76946d1510 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -100,7 +100,7 @@ export class Cargo { ); } catch (err) { log.error(`Cargo invocation has failed: ${err}`); - throw new Error(`Cargo invocation has failed: ${err}`); + throw new Error(`Cargo invocation has failed: ${err}`, { cause: err }); } return spec.filter?.(artifacts) ?? artifacts; diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json index a13afab170..380acec59d 100644 --- a/editors/code/tsconfig.json +++ b/editors/code/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "@tsconfig/strictest/tsconfig.json", "compilerOptions": { - "esModuleInterop": false, "module": "NodeNext", "moduleResolution": "nodenext", "target": "ES2024", diff --git a/rust-version b/rust-version index 68f38716db..38f153f78d 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -1174f784096deb8e4ba93f7e4b5ccb7bb4ba2c55 +4c4205163abcbd08948b3efab796c543ba1ea687 diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs index 4e980bb3d9..257429c426 100644 --- a/xtask/src/codegen/grammar.rs +++ b/xtask/src/codegen/grammar.rs @@ -780,7 +780,7 @@ impl Field { } fn token_kind(&self) -> Option<proc_macro2::TokenStream> { match self { - Field::Token(token) => { + Field::Token { token, .. } => { let token: proc_macro2::TokenStream = token.parse().unwrap(); Some(quote! { T![#token] }) } @@ -789,8 +789,11 @@ impl Field { } fn method_name(&self) -> String { match self { - Field::Token(name) => { - let name = match name.as_str() { + Field::Token { name, token, .. } => { + if let Some(name) = name { + return name.clone(); + } + let name = match token.as_str() { ";" => "semicolon", "->" => "thin_arrow", "'{'" => "l_curly", @@ -820,7 +823,7 @@ impl Field { "," => "comma", "|" => "pipe", "~" => "tilde", - _ => name, + _ => token, }; format!("{name}_token",) } @@ -835,7 +838,7 @@ impl Field { } fn ty(&self) -> proc_macro2::Ident { match self { - Field::Token(_) => format_ident!("SyntaxToken"), + Field::Token { .. } => format_ident!("SyntaxToken"), Field::Node { ty, .. } => format_ident!("{}", ty), } } @@ -885,7 +888,7 @@ fn lower(grammar: &Grammar) -> AstSrc { res.nodes.iter_mut().for_each(|it| { it.traits.sort(); it.fields.sort_by_key(|it| match it { - Field::Token(name) => (true, name.clone()), + Field::Token { token, .. } => (true, token.clone()), Field::Node { name, .. } => (false, name.clone()), }); }); @@ -925,12 +928,11 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r acc.push(field); } Rule::Token(token) => { - assert!(label.is_none()); - let mut name = clean_token_name(&grammar[*token].name); - if "[]{}()".contains(&name) { - name = format!("'{name}'"); + let mut token = clean_token_name(&grammar[*token].name); + if "[]{}()".contains(&token) { + token = format!("'{token}'"); } - let field = Field::Token(name); + let field = Field::Token { name: label.cloned(), token }; acc.push(field); } Rule::Rep(inner) => { @@ -1018,8 +1020,8 @@ fn lower_separated_list( } match nt { Either::Right(token) => { - let name = clean_token_name(&grammar[*token].name); - let field = Field::Token(name); + let token = clean_token_name(&grammar[*token].name); + let field = Field::Token { token, name: None }; acc.push(field); } Either::Left(node) => { diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs index 564d9cc24e..a0abdf09d3 100644 --- a/xtask/src/codegen/grammar/ast_src.rs +++ b/xtask/src/codegen/grammar/ast_src.rs @@ -111,8 +111,19 @@ const RESERVED: &[&str] = &[ ]; // keywords that are keywords only in specific parse contexts #[doc(alias = "WEAK_KEYWORDS")] -const CONTEXTUAL_KEYWORDS: &[&str] = - &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe", "bikeshed"]; +const CONTEXTUAL_KEYWORDS: &[&str] = &[ + "macro_rules", + "union", + "default", + "raw", + "dyn", + "auto", + "yeet", + "safe", + "bikeshed", + "cfg_attr", + "cfg", +]; // keywords we use for special macro expansions const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ "asm", @@ -261,7 +272,7 @@ pub(crate) struct AstNodeSrc { #[derive(Debug, Eq, PartialEq)] pub(crate) enum Field { - Token(String), + Token { name: Option<String>, token: String }, Node { name: String, ty: String, cardinality: Cardinality }, } diff --git a/xtask/src/codegen/lints.rs b/xtask/src/codegen/lints.rs index 3b4c2e8da3..788ae8d6c1 100644 --- a/xtask/src/codegen/lints.rs +++ b/xtask/src/codegen/lints.rs @@ -83,7 +83,7 @@ pub struct LintGroup { let lints_json = project_root().join("./target/clippy_lints.json"); cmd!( sh, - "curl https://rust-lang.github.io/rust-clippy/stable/lints.json --output {lints_json}" + "curl -f https://raw.githubusercontent.com/rust-lang/rust-clippy/21fd71e3fe6eb063cfb619ecc37b1023f5283894/beta/lints.json --output {lints_json}" ) .run() .unwrap(); |