diff options
206 files changed, 5673 insertions, 3895 deletions
diff --git a/src/tools/rust-analyzer/.git-blame-ignore-revs b/src/tools/rust-analyzer/.git-blame-ignore-revs index 2ccdc8c0425..651502965ef 100644 --- a/src/tools/rust-analyzer/.git-blame-ignore-revs +++ b/src/tools/rust-analyzer/.git-blame-ignore-revs @@ -14,3 +14,4 @@ f247090558c9ba3c551566eae5882b7ca865225f b2f6fd4f961fc7e4fbfdb80cae2e6065f8436f15 c48062fe2ab9a2d913d1985a6b0aec4bf936bfc1 f532576ac53ddcc666bc8d59e0b6437065e2f599 +4704881b641884de50645637108b6b6f5b68aaf9 diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml index 5258d9ddd3a..e4fa94643ba 100644 --- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml +++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml @@ -11,6 +11,7 @@ on: jobs: publish: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest steps: diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml index f1533bf26e5..5023a634fde 100644 --- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml +++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml @@ -9,6 +9,7 @@ on: jobs: publish-libs: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest steps: diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 39ac652de0f..3f1c8b2a9c1 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -22,6 +22,7 @@ env: jobs: dist: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} strategy: matrix: include: @@ -138,6 +139,7 @@ jobs: path: ./dist dist-x86_64-unknown-linux-musl: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} name: dist (x86_64-unknown-linux-musl) runs-on: ubuntu-latest env: @@ -183,6 +185,7 @@ jobs: path: ./dist publish: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest needs: ["dist", "dist-x86_64-unknown-linux-musl"] @@ -257,24 +260,24 @@ jobs: working-directory: ./editors/code - name: Publish Extension (Code Marketplace, release) - if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') + if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' working-directory: ./editors/code # token from https://dev.azure.com/rust-analyzer/ run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix - name: Publish Extension (OpenVSX, release) - if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') + if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' working-directory: ./editors/code run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix timeout-minutes: 2 - name: Publish Extension (Code Marketplace, nightly) - if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') + if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' working-directory: ./editors/code run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release - name: Publish Extension (OpenVSX, nightly) - if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') + if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' working-directory: ./editors/code run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix timeout-minutes: 2 diff --git a/src/tools/rust-analyzer/.gitignore b/src/tools/rust-analyzer/.gitignore index c4470a45078..7192e685e29 100644 --- a/src/tools/rust-analyzer/.gitignore +++ b/src/tools/rust-analyzer/.gitignore @@ -6,10 +6,11 @@ target/ *.log *.iml .vscode/settings.json -generated_assists.adoc -generated_features.adoc -generated_diagnostic.adoc .DS_Store /out/ /dump.lsif .envrc +docs/book/book +docs/book/src/assists_generated.md +docs/book/src/diagnostics_generated.md +docs/book/src/features_generated.md diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 2dfca7c4803..98268d8f844 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1408,9 +1408,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -1514,9 +1514,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5246e9e1f450333a990877eabbc36fe0567e7cedd56d5365db319e14079cf2a" +checksum = "2fa4333df7b71217edb44a36702cafd2bcfc9850677bdf78b32c9f2c98e5df40" dependencies = [ "bitflags 2.7.0", "ra-ap-rustc_index", @@ -1525,9 +1525,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fd8e4f5b34c434ec111efb0e0614954db048b9307d3b2e4cc3c915da9d2160" +checksum = "d200275ff3d952cc11108f4dc6a692473659758623d63f2bdcea6104a7f1cec8" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1535,9 +1535,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d34973fe081392bd1edb022e865e9952fcaa093f9cdae183edce64472e5e889" +checksum = "06eb63df8c1ce2dcb07647305bed811c9c5ebd157def01a81c1b9479b8592b3b" dependencies = [ "proc-macro2", "quote", @@ -1546,19 +1546,20 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fa42c582e21b35e8f61a5afe3c63a9c722d995826762eb19b18beeccf5157f" +checksum = "b7a4d402b2f85650e8c1f78e2e2defc241b03948d6e30d9f5254c9b82755cc4d" dependencies = [ + "memchr", "unicode-properties", "unicode-xid", ] [[package]] name = "ra-ap-rustc_parse_format" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740383328d7033393e5385f4a6073b880d5811b0fc0fd2559e481f905940f2f8" +checksum = "a23a382dbe392beb26360c1a8ce9193155ef74eeac59bcda0fa0a233e047323a" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1566,9 +1567,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39f544728f32cebffb1a8b92ba3c1f3dcb4144081438d192137ed197d479a9d" +checksum = "d746955d67f315ab79767f1d0bbc17fee4f0970d4a00b9ad76bf09cc7d3cd17e" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.0.0", @@ -2001,9 +2002,9 @@ dependencies = [ [[package]] name = "tenthash" -version = "0.4.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d67f9f3cf70e0852941d7bc3cb884b49b24b8ee956baf91ad0abae31f5ef11fb" +checksum = "2d092d622df8bb64e5de8dc86a3667702d5f1e0fe2f0604c6035540703c8cd1e" [[package]] name = "test-fixture" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index c42ae171d86..44c628e8294 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -87,11 +87,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.91", default-features = false } -ra-ap-rustc_parse_format = { version = "0.91", default-features = false } -ra-ap-rustc_index = { version = "0.91", default-features = false } -ra-ap-rustc_abi = { version = "0.91", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.91", default-features = false } +ra-ap-rustc_lexer = { version = "0.94", default-features = false } +ra-ap-rustc_parse_format = { version = "0.94", default-features = false } +ra-ap-rustc_index = { version = "0.94", default-features = false } +ra-ap-rustc_abi = { version = "0.94", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.94", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/PRIVACY.md b/src/tools/rust-analyzer/PRIVACY.md index 89e252be731..ef9c2437ab7 100644 --- a/src/tools/rust-analyzer/PRIVACY.md +++ b/src/tools/rust-analyzer/PRIVACY.md @@ -1 +1 @@ -See the [Privacy](https://rust-analyzer.github.io/manual.html#privacy) section of the user manual. +See the [Privacy](https://rust-analyzer.github.io/book/privacy.html) section of the user manual. diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md index 552f71f1518..56883723c93 100644 --- a/src/tools/rust-analyzer/README.md +++ b/src/tools/rust-analyzer/README.md @@ -1,6 +1,6 @@ <p align="center"> <img - src="https://raw.githubusercontent.com/rust-analyzer/rust-analyzer/master/assets/logo-wide.svg" + src="https://raw.githubusercontent.com/rust-lang/rust-analyzer/master/assets/logo-wide.svg" alt="rust-analyzer logo"> </p> @@ -9,7 +9,7 @@ It is a part of a larger rls-2.0 effort to create excellent IDE support for Rust ## Quick Start -https://rust-analyzer.github.io/manual.html#installation +https://rust-analyzer.github.io/book/installation.html ## Documentation @@ -18,12 +18,13 @@ if you are just curious about how things work under the hood, check the [./docs/ folder. If you want to **use** rust-analyzer's language server with your editor of -choice, check [the manual](https://rust-analyzer.github.io/manual.html) folder. +choice, check [the manual](https://rust-analyzer.github.io/book/). It also contains some tips & tricks to help you be more productive when using rust-analyzer. ## Security and Privacy -See the corresponding sections of [the manual](https://rust-analyzer.github.io/manual.html#security). +See the [security](https://rust-analyzer.github.io/book/security.html) and +[privacy](https://rust-analyzer.github.io/book/privacy.html) sections of the manual. ## Communication diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index a0fc8c31eaf..c2cea071905 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -129,9 +129,9 @@ impl fmt::Display for CrateName { } impl ops::Deref for CrateName { - type Target = str; - fn deref(&self) -> &str { - self.0.as_str() + type Target = Symbol; + fn deref(&self) -> &Symbol { + &self.0 } } @@ -230,8 +230,8 @@ impl fmt::Display for CrateDisplayName { } impl ops::Deref for CrateDisplayName { - type Target = str; - fn deref(&self) -> &str { + type Target = Symbol; + fn deref(&self) -> &Symbol { &self.crate_name } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 12f5f6ad79a..c5bbd4edba9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -95,10 +95,14 @@ impl FunctionData { .map(Box::new); let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists(); if flags.contains(FnFlags::HAS_UNSAFE_KW) - && !crate_graph[krate].edition.at_least_2024() && attrs.by_key(&sym::rustc_deprecated_safe_2024).exists() { flags.remove(FnFlags::HAS_UNSAFE_KW); + flags.insert(FnFlags::DEPRECATED_SAFE_2024); + } + + if attrs.by_key(&sym::target_feature).exists() { + flags.insert(FnFlags::HAS_TARGET_FEATURE); } Arc::new(FunctionData { @@ -148,6 +152,10 @@ impl FunctionData { self.flags.contains(FnFlags::HAS_UNSAFE_KW) } + pub fn is_deprecated_safe_2024(&self) -> bool { + self.flags.contains(FnFlags::DEPRECATED_SAFE_2024) + } + pub fn is_safe(&self) -> bool { self.flags.contains(FnFlags::HAS_SAFE_KW) } @@ -155,6 +163,10 @@ impl FunctionData { pub fn is_varargs(&self) -> bool { self.flags.contains(FnFlags::IS_VARARGS) } + + pub fn has_target_feature(&self) -> bool { + self.flags.contains(FnFlags::HAS_TARGET_FEATURE) + } } fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index bf6cc1dcade..598a850898b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -10,12 +10,12 @@ use triomphe::Arc; use crate::{ attr::{Attrs, AttrsWithOwner}, - body::{scope::ExprScopes, Body, BodySourceMap}, data::{ adt::{EnumData, EnumVariantData, StructData, VariantData}, ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData, }, + expr_store::{scope::ExprScopes, Body, BodySourceMap}, generics::GenericParams, import_map::ImportMap, item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps}, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index de439249306..9df6eaade75 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -1,18 +1,19 @@ -//! Defines `Body`: a lowered representation of bodies of functions, statics and +//! Defines `ExpressionStore`: a lowered representation of functions, statics and //! consts. +mod body; mod lower; mod pretty; pub mod scope; + #[cfg(test)] mod tests; use std::ops::{Deref, Index}; -use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{name::Name, ExpandError, InFile}; -use la_arena::{Arena, ArenaMap, Idx, RawIdx}; +use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashMap; use smallvec::SmallVec; use span::{Edition, MacroFileId, SyntaxContextData}; @@ -22,19 +23,18 @@ use tt::TextRange; use crate::{ db::DefDatabase, - expander::Expander, hir::{ - dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, - LabelId, Pat, PatId, RecordFieldPat, Statement, + Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, + PatId, RecordFieldPat, Statement, }, - item_tree::AttrOwner, nameres::DefMap, path::{ModPath, Path}, - src::HasSource, type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap}, - BlockId, DefWithBodyId, HasModule, Lookup, SyntheticSyntax, + BlockId, DefWithBodyId, Lookup, SyntheticSyntax, }; +pub use self::body::{Body, BodySourceMap}; + /// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct HygieneId(span::SyntaxContextId); @@ -58,9 +58,29 @@ impl HygieneId { } } -/// The body of an item (function, const etc.). +pub type ExprPtr = AstPtr<ast::Expr>; +pub type ExprSource = InFile<ExprPtr>; + +pub type PatPtr = AstPtr<ast::Pat>; +pub type PatSource = InFile<PatPtr>; + +pub type LabelPtr = AstPtr<ast::Label>; +pub type LabelSource = InFile<LabelPtr>; + +pub type FieldPtr = AstPtr<ast::RecordExprField>; +pub type FieldSource = InFile<FieldPtr>; + +pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>; +pub type PatFieldSource = InFile<PatFieldPtr>; + +pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>; +pub type ExprOrPatSource = InFile<ExprOrPatPtr>; + +pub type SelfParamPtr = AstPtr<ast::SelfParam>; +pub type MacroCallPtr = AstPtr<ast::MacroCall>; + #[derive(Debug, Eq, PartialEq)] -pub struct Body { +pub struct ExpressionStore { pub exprs: Arena<Expr>, pub pats: Arena<Pat>, pub bindings: Arena<Binding>, @@ -68,19 +88,9 @@ pub struct Body { /// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the /// top level expression, it will not be listed in here. pub binding_owners: FxHashMap<BindingId, ExprId>, - /// The patterns for the function's parameters. While the parameter types are - /// part of the function signature, the patterns are not (they don't change - /// the external type of the function). - /// - /// If this `Body` is for the body of a constant, this will just be - /// empty. - pub params: Box<[PatId]>, - pub self_param: Option<BindingId>, - /// The `ExprId` of the actual body expression. - pub body_expr: ExprId, pub types: TypesMap, - /// Block expressions in this body that may contain inner items. - block_scopes: Vec<BlockId>, + /// Block expressions in this store that may contain inner items. + block_scopes: Box<[BlockId]>, /// A map from binding to its hygiene ID. /// @@ -92,44 +102,13 @@ pub struct Body { binding_hygiene: FxHashMap<BindingId, HygieneId>, /// A map from an variable usages to their hygiene ID. /// - /// Expressions that can be recorded here are single segment path, although not all single segments path refer + /// Expressions (and destructuing patterns) that can be recorded here are single segment path, although not all single segments path refer /// to variables and have hygiene (some refer to items, we don't know at this stage). - expr_hygiene: FxHashMap<ExprId, HygieneId>, - /// A map from a destructuring assignment possible variable usages to their hygiene ID. - pat_hygiene: FxHashMap<PatId, HygieneId>, + ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, } -pub type ExprPtr = AstPtr<ast::Expr>; -pub type ExprSource = InFile<ExprPtr>; - -pub type PatPtr = AstPtr<ast::Pat>; -pub type PatSource = InFile<PatPtr>; - -pub type LabelPtr = AstPtr<ast::Label>; -pub type LabelSource = InFile<LabelPtr>; - -pub type FieldPtr = AstPtr<ast::RecordExprField>; -pub type FieldSource = InFile<FieldPtr>; - -pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>; -pub type PatFieldSource = InFile<PatFieldPtr>; - -pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>; -pub type ExprOrPatSource = InFile<ExprOrPatPtr>; - -/// An item body together with the mapping from syntax nodes to HIR expression -/// IDs. This is needed to go from e.g. a position in a file to the HIR -/// expression containing it; but for type inference etc., we want to operate on -/// a structure that is agnostic to the actual positions of expressions in the -/// file, so that we don't recompute types whenever some whitespace is typed. -/// -/// One complication here is that, due to macro expansion, a single `Body` might -/// be spread across several files. So, for each ExprId and PatId, we record -/// both the HirFileId and the position inside the file. However, we only store -/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle -/// this properly for macros. -#[derive(Default, Debug, Eq, PartialEq)] -pub struct BodySourceMap { +#[derive(Debug, Eq, PartialEq, Default)] +pub struct ExpressionStoreSourceMap { // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected). expr_map: FxHashMap<ExprSource, ExprOrPatId>, @@ -141,7 +120,6 @@ pub struct BodySourceMap { label_map: FxHashMap<LabelSource, LabelId>, label_map_back: ArenaMap<LabelId, LabelSource>, - self_param: Option<InFile<AstPtr<ast::SelfParam>>>, binding_definitions: FxHashMap<BindingId, SmallVec<[PatId; 4]>>, /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). @@ -153,11 +131,25 @@ pub struct BodySourceMap { template_map: Option<Box<FormatTemplate>>, - expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>, + expansions: FxHashMap<InFile<MacroCallPtr>, MacroFileId>, - /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in + /// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in /// the source map (since they're just as volatile). - diagnostics: Vec<BodyDiagnostic>, + diagnostics: Vec<ExpressionStoreDiagnostics>, +} + +/// The body of an item (function, const etc.). +#[derive(Debug, Eq, PartialEq, Default)] +pub struct ExpressionStoreBuilder { + pub exprs: Arena<Expr>, + pub pats: Arena<Pat>, + pub bindings: Arena<Binding>, + pub labels: Arena<Label>, + pub binding_owners: FxHashMap<BindingId, ExprId>, + pub types: TypesMap, + block_scopes: Vec<BlockId>, + binding_hygiene: FxHashMap<BindingId, HygieneId>, + ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, } #[derive(Default, Debug, Eq, PartialEq)] @@ -171,166 +163,62 @@ struct FormatTemplate { /// The value stored for each capture is its template literal and offset inside it. The template literal /// is from the `format_args[_nl]!()` macro and so needs to be mapped up once to go to the user-written /// template. - implicit_capture_to_source: FxHashMap<ExprId, InFile<(AstPtr<ast::Expr>, TextRange)>>, + implicit_capture_to_source: FxHashMap<ExprId, InFile<(ExprPtr, TextRange)>>, } #[derive(Debug, Eq, PartialEq)] -pub enum BodyDiagnostic { +pub enum ExpressionStoreDiagnostics { InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions }, - MacroError { node: InFile<AstPtr<ast::MacroCall>>, err: ExpandError }, - UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath }, + MacroError { node: InFile<MacroCallPtr>, err: ExpandError }, + UnresolvedMacroCall { node: InFile<MacroCallPtr>, path: ModPath }, UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String }, UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, } -impl Body { - pub(crate) fn body_with_source_map_query( - db: &dyn DefDatabase, - def: DefWithBodyId, - ) -> (Arc<Body>, Arc<BodySourceMap>) { - let _p = tracing::info_span!("body_with_source_map_query").entered(); - let mut params = None; - - let mut is_async_fn = false; - let InFile { file_id, value: body } = { - match def { - DefWithBodyId::FunctionId(f) => { - let data = db.function_data(f); - let f = f.lookup(db); - let src = f.source(db); - params = src.value.param_list().map(move |param_list| { - let item_tree = f.id.item_tree(db); - let func = &item_tree[f.id.value]; - let krate = f.container.module(db).krate; - let crate_graph = db.crate_graph(); - ( - param_list, - (0..func.params.len()).map(move |idx| { - item_tree - .attrs( - db, - krate, - AttrOwner::Param( - f.id.value, - Idx::from_raw(RawIdx::from(idx as u32)), - ), - ) - .is_cfg_enabled(&crate_graph[krate].cfg_options) - }), - ) - }); - is_async_fn = data.is_async(); - src.map(|it| it.body().map(ast::Expr::from)) - } - DefWithBodyId::ConstId(c) => { - let c = c.lookup(db); - let src = c.source(db); - src.map(|it| it.body()) - } - DefWithBodyId::StaticId(s) => { - let s = s.lookup(db); - let src = s.source(db); - src.map(|it| it.body()) - } - DefWithBodyId::VariantId(v) => { - let s = v.lookup(db); - let src = s.source(db); - src.map(|it| it.expr()) - } - DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()), - } - }; - let module = def.module(db); - let expander = Expander::new(db, file_id, module); - let (mut body, mut source_map) = - Body::new(db, def, expander, params, body, module.krate, is_async_fn); - body.shrink_to_fit(); - source_map.shrink_to_fit(); - - (Arc::new(body), Arc::new(source_map)) - } - - pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> { - db.body_with_source_map(def).0 - } - - /// Returns an iterator over all block expressions in this body that define inner items. - pub fn blocks<'a>( - &'a self, - db: &'a dyn DefDatabase, - ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + 'a { - self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block))) - } - - pub fn pretty_print( - &self, - db: &dyn DefDatabase, - owner: DefWithBodyId, - edition: Edition, - ) -> String { - pretty::print_body_hir(db, self, owner, edition) - } - - pub fn pretty_print_expr( - &self, - db: &dyn DefDatabase, - owner: DefWithBodyId, - expr: ExprId, - edition: Edition, - ) -> String { - pretty::print_expr_hir(db, self, owner, expr, edition) - } - - pub fn pretty_print_pat( - &self, - db: &dyn DefDatabase, - owner: DefWithBodyId, - pat: PatId, - oneline: bool, - edition: Edition, - ) -> String { - pretty::print_pat_hir(db, self, owner, pat, oneline, edition) - } - - fn new( - db: &dyn DefDatabase, - owner: DefWithBodyId, - expander: Expander, - params: Option<(ast::ParamList, impl Iterator<Item = bool>)>, - body: Option<ast::Expr>, - krate: CrateId, - is_async_fn: bool, - ) -> (Body, BodySourceMap) { - lower::lower(db, owner, expander, params, body, krate, is_async_fn) - } - - fn shrink_to_fit(&mut self) { +impl ExpressionStoreBuilder { + fn finish(self) -> ExpressionStore { let Self { - body_expr: _, - params: _, - self_param: _, block_scopes, - exprs, - labels, - pats, - bindings, - binding_owners, - binding_hygiene, - expr_hygiene, - pat_hygiene, - types, + mut exprs, + mut labels, + mut pats, + mut bindings, + mut binding_owners, + mut binding_hygiene, + mut ident_hygiene, + mut types, } = self; - block_scopes.shrink_to_fit(); exprs.shrink_to_fit(); labels.shrink_to_fit(); pats.shrink_to_fit(); bindings.shrink_to_fit(); binding_owners.shrink_to_fit(); binding_hygiene.shrink_to_fit(); - expr_hygiene.shrink_to_fit(); - pat_hygiene.shrink_to_fit(); + ident_hygiene.shrink_to_fit(); types.shrink_to_fit(); + + ExpressionStore { + exprs, + pats, + bindings, + labels, + binding_owners, + types, + block_scopes: block_scopes.into_boxed_slice(), + binding_hygiene, + ident_hygiene, + } + } +} + +impl ExpressionStore { + /// Returns an iterator over all block expressions in this store that define inner items. + pub fn blocks<'a>( + &'a self, + db: &'a dyn DefDatabase, + ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + 'a { + self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block))) } pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) { @@ -658,11 +546,11 @@ impl Body { } pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId { - self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT) + self.ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT) } pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId { - self.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT) + self.ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT) } pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId { @@ -673,27 +561,7 @@ impl Body { } } -impl Default for Body { - fn default() -> Self { - Self { - body_expr: dummy_expr_id(), - exprs: Default::default(), - pats: Default::default(), - bindings: Default::default(), - labels: Default::default(), - params: Default::default(), - block_scopes: Default::default(), - binding_owners: Default::default(), - self_param: Default::default(), - binding_hygiene: Default::default(), - expr_hygiene: Default::default(), - pat_hygiene: Default::default(), - types: Default::default(), - } - } -} - -impl Index<ExprId> for Body { +impl Index<ExprId> for ExpressionStore { type Output = Expr; fn index(&self, expr: ExprId) -> &Expr { @@ -701,7 +569,7 @@ impl Index<ExprId> for Body { } } -impl Index<PatId> for Body { +impl Index<PatId> for ExpressionStore { type Output = Pat; fn index(&self, pat: PatId) -> &Pat { @@ -709,7 +577,7 @@ impl Index<PatId> for Body { } } -impl Index<LabelId> for Body { +impl Index<LabelId> for ExpressionStore { type Output = Label; fn index(&self, label: LabelId) -> &Label { @@ -717,7 +585,7 @@ impl Index<LabelId> for Body { } } -impl Index<BindingId> for Body { +impl Index<BindingId> for ExpressionStore { type Output = Binding; fn index(&self, b: BindingId) -> &Binding { @@ -725,7 +593,7 @@ impl Index<BindingId> for Body { } } -impl Index<TypeRefId> for Body { +impl Index<TypeRefId> for ExpressionStore { type Output = TypeRef; fn index(&self, b: TypeRefId) -> &TypeRef { @@ -735,7 +603,7 @@ impl Index<TypeRefId> for Body { // FIXME: Change `node_` prefix to something more reasonable. // Perhaps `expr_syntax` and `expr_id`? -impl BodySourceMap { +impl ExpressionStoreSourceMap { pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> { match id { ExprOrPatId::ExprId(id) => self.expr_syntax(id).map(|it| it.map(AstPtr::wrap_left)), @@ -757,9 +625,7 @@ impl BodySourceMap { self.expansions.get(&src).cloned() } - pub fn macro_calls( - &self, - ) -> impl Iterator<Item = (InFile<AstPtr<ast::MacroCall>>, MacroFileId)> + '_ { + pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroFileId)> + '_ { self.expansions.iter().map(|(&a, &b)| (a, b)) } @@ -767,10 +633,6 @@ impl BodySourceMap { self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax) } - pub fn self_param_syntax(&self) -> Option<InFile<AstPtr<ast::SelfParam>>> { - self.self_param - } - pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> { self.pat_map.get(&node.map(AstPtr::new)).cloned() } @@ -801,9 +663,7 @@ impl BodySourceMap { self.expr_map.get(&src).copied() } - pub fn expansions( - &self, - ) -> impl Iterator<Item = (&InFile<AstPtr<ast::MacroCall>>, &MacroFileId)> { + pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroFileId)> { self.expansions.iter() } @@ -823,7 +683,7 @@ impl BodySourceMap { pub fn format_args_implicit_capture( &self, capture_expr: ExprId, - ) -> Option<InFile<(AstPtr<ast::Expr>, TextRange)>> { + ) -> Option<InFile<(ExprPtr, TextRange)>> { self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied() } @@ -837,14 +697,13 @@ impl BodySourceMap { .zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref)) } - /// Get a reference to the body source map's diagnostics. - pub fn diagnostics(&self) -> &[BodyDiagnostic] { + /// Get a reference to the source map's diagnostics. + pub fn diagnostics(&self) -> &[ExpressionStoreDiagnostics] { &self.diagnostics } fn shrink_to_fit(&mut self) { let Self { - self_param: _, expr_map, expr_map_back, pat_map, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs new file mode 100644 index 00000000000..a55fec4f8b1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs @@ -0,0 +1,175 @@ +//! Defines `Body`: a lowered representation of functions, statics and +//! consts. +use std::ops; + +use hir_expand::{InFile, Lookup}; +use la_arena::{Idx, RawIdx}; +use span::Edition; +use syntax::ast; +use triomphe::Arc; + +use crate::{ + db::DefDatabase, + expander::Expander, + expr_store::{lower, pretty, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr}, + hir::{BindingId, ExprId, PatId}, + item_tree::AttrOwner, + src::HasSource, + DefWithBodyId, HasModule, +}; + +/// The body of an item (function, const etc.). +#[derive(Debug, Eq, PartialEq)] +pub struct Body { + pub store: ExpressionStore, + /// The patterns for the function's parameters. While the parameter types are + /// part of the function signature, the patterns are not (they don't change + /// the external type of the function). + /// + /// If this `Body` is for the body of a constant, this will just be + /// empty. + pub params: Box<[PatId]>, + pub self_param: Option<BindingId>, + /// The `ExprId` of the actual body expression. + pub body_expr: ExprId, +} + +impl ops::Deref for Body { + type Target = ExpressionStore; + + fn deref(&self) -> &Self::Target { + &self.store + } +} + +/// An item body together with the mapping from syntax nodes to HIR expression +/// IDs. This is needed to go from e.g. a position in a file to the HIR +/// expression containing it; but for type inference etc., we want to operate on +/// a structure that is agnostic to the actual positions of expressions in the +/// file, so that we don't recompute types whenever some whitespace is typed. +/// +/// One complication here is that, due to macro expansion, a single `Body` might +/// be spread across several files. So, for each ExprId and PatId, we record +/// both the HirFileId and the position inside the file. However, we only store +/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle +/// this properly for macros. +#[derive(Default, Debug, Eq, PartialEq)] +pub struct BodySourceMap { + pub self_param: Option<InFile<SelfParamPtr>>, + pub store: ExpressionStoreSourceMap, +} + +impl ops::Deref for BodySourceMap { + type Target = ExpressionStoreSourceMap; + + fn deref(&self) -> &Self::Target { + &self.store + } +} + +impl Body { + pub(crate) fn body_with_source_map_query( + db: &dyn DefDatabase, + def: DefWithBodyId, + ) -> (Arc<Body>, Arc<BodySourceMap>) { + let _p = tracing::info_span!("body_with_source_map_query").entered(); + let mut params = None; + + let mut is_async_fn = false; + let InFile { file_id, value: body } = { + match def { + DefWithBodyId::FunctionId(f) => { + let data = db.function_data(f); + let f = f.lookup(db); + let src = f.source(db); + params = src.value.param_list().map(move |param_list| { + let item_tree = f.id.item_tree(db); + let func = &item_tree[f.id.value]; + let krate = f.container.module(db).krate; + let crate_graph = db.crate_graph(); + ( + param_list, + (0..func.params.len()).map(move |idx| { + item_tree + .attrs( + db, + krate, + AttrOwner::Param( + f.id.value, + Idx::from_raw(RawIdx::from(idx as u32)), + ), + ) + .is_cfg_enabled(&crate_graph[krate].cfg_options) + }), + ) + }); + is_async_fn = data.is_async(); + src.map(|it| it.body().map(ast::Expr::from)) + } + DefWithBodyId::ConstId(c) => { + let c = c.lookup(db); + let src = c.source(db); + src.map(|it| it.body()) + } + DefWithBodyId::StaticId(s) => { + let s = s.lookup(db); + let src = s.source(db); + src.map(|it| it.body()) + } + DefWithBodyId::VariantId(v) => { + let s = v.lookup(db); + let src = s.source(db); + src.map(|it| it.expr()) + } + DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()), + } + }; + let module = def.module(db); + let expander = Expander::new(db, file_id, module); + let (body, mut source_map) = + lower::lower_body(db, def, expander, params, body, module.krate, is_async_fn); + source_map.store.shrink_to_fit(); + + (Arc::new(body), Arc::new(source_map)) + } + + pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> { + db.body_with_source_map(def).0 + } + + pub fn pretty_print( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + edition: Edition, + ) -> String { + pretty::print_body_hir(db, self, owner, edition) + } + + pub fn pretty_print_expr( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + expr: ExprId, + edition: Edition, + ) -> String { + pretty::print_expr_hir(db, self, owner, expr, edition) + } + + pub fn pretty_print_pat( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, + ) -> String { + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) + } +} + +impl BodySourceMap { + pub fn self_param_syntax(&self) -> Option<InFile<SelfParamPtr>> { + self.self_param + } +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 16c7b5ca00a..88f770da02a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -29,11 +29,14 @@ use triomphe::Arc; use crate::{ attr::Attrs, - body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr}, builtin_type::BuiltinUint, data::adt::StructKind, db::DefDatabase, expander::Expander, + expr_store::{ + Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder, + ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, PatPtr, + }, hir::{ format_args::{ self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, @@ -55,11 +58,11 @@ use crate::{ type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>; -pub(super) fn lower( +pub(super) fn lower_body( db: &dyn DefDatabase, owner: DefWithBodyId, expander: Expander, - params: Option<(ast::ParamList, impl Iterator<Item = bool>)>, + parameters: Option<(ast::ParamList, impl Iterator<Item = bool>)>, body: Option<ast::Expr>, krate: CrateId, is_async_fn: bool, @@ -75,35 +78,146 @@ pub(super) fn lower( }; Arc::clone(span_map) }); - ExprCollector { - db, - owner, - krate, - def_map: expander.module.def_map(db), - source_map: BodySourceMap::default(), - ast_id_map: db.ast_id_map(expander.current_file_id()), - body: Body::default(), - expander, - current_try_block_label: None, - is_lowering_coroutine: false, - label_ribs: Vec::new(), - current_binding_owner: None, - awaitable_context: None, - current_span_map: span_map, - current_block_legacy_macro_defs_count: FxHashMap::default(), - } - .collect(params, body, is_async_fn) + + let mut self_param = None; + let mut source_map_self_param = None; + let mut params = vec![]; + let mut collector = ExprCollector::new(db, owner, expander, krate, span_map); + + let skip_body = match owner { + DefWithBodyId::FunctionId(it) => db.attrs(it.into()), + DefWithBodyId::StaticId(it) => db.attrs(it.into()), + DefWithBodyId::ConstId(it) => db.attrs(it.into()), + DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY, + DefWithBodyId::VariantId(it) => db.attrs(it.into()), + } + .rust_analyzer_tool() + .any(|attr| *attr.path() == tool_path![skip]); + // If #[rust_analyzer::skip] annotated, only construct enough information for the signature + // and skip the body. + if skip_body { + if let Some((param_list, mut attr_enabled)) = parameters { + if let Some(self_param_syn) = + param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) + { + let is_mutable = + self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none(); + let binding_id: la_arena::Idx<Binding> = collector.alloc_binding( + Name::new_symbol_root(sym::self_.clone()), + BindingAnnotation::new(is_mutable, false), + ); + self_param = Some(binding_id); + source_map_self_param = + Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); + } + params = param_list + .params() + .zip(attr_enabled) + .filter(|(_, enabled)| *enabled) + .map(|_| collector.missing_pat()) + .collect(); + }; + let body_expr = collector.missing_expr(); + return ( + Body { + store: collector.store.finish(), + params: params.into_boxed_slice(), + self_param, + body_expr, + }, + BodySourceMap { self_param: source_map_self_param, store: collector.source_map }, + ); + } + + if let Some((param_list, mut attr_enabled)) = parameters { + if let Some(self_param_syn) = + param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) + { + let is_mutable = + self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none(); + let binding_id: la_arena::Idx<Binding> = collector.alloc_binding( + Name::new_symbol_root(sym::self_.clone()), + BindingAnnotation::new(is_mutable, false), + ); + let hygiene = self_param_syn + .name() + .map(|name| collector.hygiene_id_for(name.syntax().text_range().start())) + .unwrap_or(HygieneId::ROOT); + if !hygiene.is_root() { + collector.store.binding_hygiene.insert(binding_id, hygiene); + } + self_param = Some(binding_id); + source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); + } + + for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) { + let param_pat = collector.collect_pat_top(param.pat()); + params.push(param_pat); + } + }; + + let body_expr = collector.collect( + body, + if is_async_fn { + Awaitable::Yes + } else { + match owner { + DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"), + DefWithBodyId::StaticId(..) => Awaitable::No("static"), + DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => { + Awaitable::No("constant") + } + DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), + } + }, + ); + + ( + Body { + store: collector.store.finish(), + params: params.into_boxed_slice(), + self_param, + body_expr, + }, + BodySourceMap { self_param: source_map_self_param, store: collector.source_map }, + ) } +#[allow(dead_code)] +pub(super) fn lower( + db: &dyn DefDatabase, + owner: ExprStoreOwnerId, + expander: Expander, + body: Option<ast::Expr>, + krate: CrateId, +) -> (ExpressionStore, ExpressionStoreSourceMap) { + // We cannot leave the root span map empty and let any identifier from it be treated as root, + // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved + // with the inner macro, and that will cause confusion because they won't be the same as `ROOT` + // even though they should be the same. Also, when the body comes from multiple expansions, their + // hygiene is different. + let span_map = expander.current_file_id().macro_file().map(|_| { + let SpanMap::ExpansionSpanMap(span_map) = expander.span_map(db) else { + panic!("in a macro file there should be `ExpansionSpanMap`"); + }; + Arc::clone(span_map) + }); + let mut expr_collector = ExprCollector::new(db, owner, expander, krate, span_map); + expr_collector.collect(body, Awaitable::No("?")); + (expr_collector.store.finish(), expr_collector.source_map) +} + +type ExprStoreOwnerId = DefWithBodyId; + struct ExprCollector<'a> { db: &'a dyn DefDatabase, expander: Expander, - owner: DefWithBodyId, + owner: ExprStoreOwnerId, def_map: Arc<DefMap>, ast_id_map: Arc<AstIdMap>, krate: CrateId, - body: Body, - source_map: BodySourceMap, + store: ExpressionStoreBuilder, + source_map: ExpressionStoreSourceMap, is_lowering_coroutine: bool, @@ -157,6 +271,7 @@ impl RibKind { } } +#[derive(PartialEq, Eq, Debug, Copy, Clone)] enum Awaitable { Yes, No(&'static str), @@ -180,12 +295,12 @@ impl BindingList { let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| { let id = ec.alloc_binding(name.clone(), mode); if !hygiene.is_root() { - ec.body.binding_hygiene.insert(id, hygiene); + ec.store.binding_hygiene.insert(id, hygiene); } id }); - if ec.body.bindings[id].mode != mode { - ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently); + if ec.store.bindings[id].mode != mode { + ec.store.bindings[id].problems = Some(BindingProblems::BoundInconsistently); } self.check_is_used(ec, id); id @@ -195,11 +310,11 @@ impl BindingList { match self.is_used.get(&id) { None => { if self.reject_new { - ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); + ec.store.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); } } Some(true) => { - ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce); + ec.store.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce); } Some(false) => {} } @@ -208,93 +323,37 @@ impl BindingList { } impl ExprCollector<'_> { - fn collect( - mut self, - param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>, - body: Option<ast::Expr>, - is_async_fn: bool, - ) -> (Body, BodySourceMap) { - let skip_body = match self.owner { - DefWithBodyId::FunctionId(it) => self.db.attrs(it.into()), - DefWithBodyId::StaticId(it) => self.db.attrs(it.into()), - DefWithBodyId::ConstId(it) => self.db.attrs(it.into()), - DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY, - DefWithBodyId::VariantId(it) => self.db.attrs(it.into()), + fn new( + db: &dyn DefDatabase, + owner: ExprStoreOwnerId, + expander: Expander, + krate: CrateId, + span_map: Option<Arc<ExpansionSpanMap>>, + ) -> ExprCollector<'_> { + ExprCollector { + db, + owner, + krate, + def_map: expander.module.def_map(db), + source_map: ExpressionStoreSourceMap::default(), + ast_id_map: db.ast_id_map(expander.current_file_id()), + store: ExpressionStoreBuilder::default(), + expander, + current_try_block_label: None, + is_lowering_coroutine: false, + label_ribs: Vec::new(), + current_binding_owner: None, + awaitable_context: None, + current_span_map: span_map, + current_block_legacy_macro_defs_count: FxHashMap::default(), } - .rust_analyzer_tool() - .any(|attr| *attr.path() == tool_path![skip]); - // If #[rust_analyzer::skip] annotated, only construct enough information for the signature - // and skip the body. - if skip_body { - self.body.body_expr = self.missing_expr(); - if let Some((param_list, mut attr_enabled)) = param_list { - if let Some(self_param) = - param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) - { - let is_mutable = - self_param.mut_token().is_some() && self_param.amp_token().is_none(); - let binding_id: la_arena::Idx<Binding> = self.alloc_binding( - Name::new_symbol_root(sym::self_.clone()), - BindingAnnotation::new(is_mutable, false), - ); - self.body.self_param = Some(binding_id); - self.source_map.self_param = - Some(self.expander.in_file(AstPtr::new(&self_param))); - } - self.body.params = param_list - .params() - .zip(attr_enabled) - .filter(|(_, enabled)| *enabled) - .map(|_| self.missing_pat()) - .collect(); - }; - return (self.body, self.source_map); - } - - self.awaitable_context.replace(if is_async_fn { - Awaitable::Yes - } else { - match self.owner { - DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"), - DefWithBodyId::StaticId(..) => Awaitable::No("static"), - DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => { - Awaitable::No("constant") - } - DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), - } - }); - if let Some((param_list, mut attr_enabled)) = param_list { - let mut params = vec![]; - if let Some(self_param) = - param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) - { - let is_mutable = - self_param.mut_token().is_some() && self_param.amp_token().is_none(); - let binding_id: la_arena::Idx<Binding> = self.alloc_binding( - Name::new_symbol_root(sym::self_.clone()), - BindingAnnotation::new(is_mutable, false), - ); - let hygiene = self_param - .name() - .map(|name| self.hygiene_id_for(name.syntax().text_range().start())) - .unwrap_or(HygieneId::ROOT); - if !hygiene.is_root() { - self.body.binding_hygiene.insert(binding_id, hygiene); - } - self.body.self_param = Some(binding_id); - self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param))); - } + } - for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) - { - let param_pat = self.collect_pat_top(param.pat()); - params.push(param_pat); - } - self.body.params = params.into_boxed_slice(); - }; - self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| { - if is_async_fn { - match body { + fn collect(&mut self, expr: Option<ast::Expr>, awaitable: Awaitable) -> ExprId { + self.awaitable_context.replace(awaitable); + self.with_label_rib(RibKind::Closure, |this| { + if awaitable == Awaitable::Yes { + match expr { Some(e) => { let syntax_ptr = AstPtr::new(&e); let expr = this.collect_expr(e); @@ -306,15 +365,13 @@ impl ExprCollector<'_> { None => this.missing_expr(), } } else { - this.collect_expr_opt(body) + this.collect_expr_opt(expr) } - }); - - (self.body, self.source_map) + }) } fn ctx(&mut self) -> LowerCtx<'_> { - self.expander.ctx(self.db, &mut self.body.types, &mut self.source_map.types) + self.expander.ctx(self.db, &mut self.store.types, &mut self.source_map.types) } fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { @@ -390,7 +447,7 @@ impl ExprCollector<'_> { parent: this.owner, root: inner_expr, }); - this.body.exprs[result_expr_id] = Expr::Const(it); + this.store.exprs[result_expr_id] = Expr::Const(it); this.current_binding_owner = prev_binding_owner; result_expr_id }) @@ -480,7 +537,7 @@ impl ExprCollector<'_> { .unwrap_or((Expr::Missing, HygieneId::ROOT)); let expr_id = self.alloc_expr(path, syntax_ptr); if !hygiene.is_root() { - self.body.expr_hygiene.insert(expr_id, hygiene); + self.store.ident_hygiene.insert(expr_id.into(), hygiene); } expr_id } @@ -562,10 +619,12 @@ impl ExprCollector<'_> { ast::Expr::AwaitExpr(e) => { let expr = self.collect_expr_opt(e.expr()); if let Awaitable::No(location) = self.is_lowering_awaitable_block() { - self.source_map.diagnostics.push(BodyDiagnostic::AwaitOutsideOfAsync { - node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)), - location: location.to_string(), - }); + self.source_map.diagnostics.push( + ExpressionStoreDiagnostics::AwaitOutsideOfAsync { + node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)), + location: location.to_string(), + }, + ); } self.alloc_expr(Expr::Await { expr }, syntax_ptr) } @@ -646,7 +705,7 @@ impl ExprCollector<'_> { this.is_lowering_coroutine = prev_is_lowering_coroutine; this.current_binding_owner = prev_binding_owner; this.current_try_block_label = prev_try_block_label; - this.body.exprs[result_expr_id] = Expr::Closure { + this.store.exprs[result_expr_id] = Expr::Closure { args: args.into(), arg_types: arg_types.into(), ret_type, @@ -752,7 +811,7 @@ impl ExprCollector<'_> { } fn parse_path(&mut self, path: ast::Path) -> Option<Path> { - self.expander.parse_path(self.db, path, &mut self.body.types, &mut self.source_map.types) + self.expander.parse_path(self.db, path, &mut self.store.types, &mut self.source_map.types) } fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> { @@ -781,7 +840,7 @@ impl ExprCollector<'_> { let src = self.expander.in_file(AstPtr::new(&expr).wrap_left()); let expr = self.collect_expr(expr); // Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`. - let id = self.body.pats.alloc(Pat::Expr(expr)); + let id = self.store.pats.alloc(Pat::Expr(expr)); self.source_map.pat_map_back.insert(id, src); id }) @@ -835,7 +894,7 @@ impl ExprCollector<'_> { .unwrap_or((Pat::Missing, HygieneId::ROOT)); let pat_id = self.alloc_pat_from_expr(path, syntax_ptr); if !hygiene.is_root() { - self.body.pat_hygiene.insert(pat_id, hygiene); + self.store.ident_hygiene.insert(pat_id.into(), hygiene); } pat_id } @@ -967,7 +1026,7 @@ impl ExprCollector<'_> { ) -> ExprId { let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr); let tmp = job(self); - self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing); + self.store.exprs[id] = mem::replace(&mut self.store.exprs[tmp], Expr::Missing); self.current_binding_owner = prev_owner; id } @@ -979,8 +1038,9 @@ impl ExprCollector<'_> { let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else { return self.collect_block(e); }; - let label = self - .alloc_label_desugared(Label { name: Name::generate_new_name(self.body.labels.len()) }); + let label = self.alloc_label_desugared(Label { + name: Name::generate_new_name(self.store.labels.len()), + }); let old_label = self.current_try_block_label.replace(label); let ptr = AstPtr::new(&e).upcast(); @@ -1006,7 +1066,7 @@ impl ExprCollector<'_> { ) } }; - let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else { + let Expr::Block { tail, .. } = &mut self.store.exprs[expr_id] else { unreachable!("block was lowered to non-block"); }; *tail = Some(next_tail); @@ -1112,7 +1172,7 @@ impl ExprCollector<'_> { this.collect_expr_opt(e.loop_body().map(|it| it.into())) }), }; - let iter_name = Name::generate_new_name(self.body.exprs.len()); + let iter_name = Name::generate_new_name(self.store.exprs.len()); let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); let iter_expr_mut = self.alloc_expr( Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, @@ -1177,7 +1237,7 @@ impl ExprCollector<'_> { let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr); let expr = self .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr); - let continue_name = Name::generate_new_name(self.body.bindings.len()); + let continue_name = Name::generate_new_name(self.store.bindings.len()); let continue_binding = self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated); let continue_bpat = @@ -1192,7 +1252,7 @@ impl ExprCollector<'_> { guard: None, expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr), }; - let break_name = Name::generate_new_name(self.body.bindings.len()); + let break_name = Name::generate_new_name(self.store.bindings.len()); let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated); let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None }); self.add_definition_to_binding(break_binding, break_bpat); @@ -1261,17 +1321,19 @@ impl ExprCollector<'_> { Ok(res) => res, Err(UnresolvedMacro { path }) => { if record_diagnostics { - self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall { - node: InFile::new(outer_file, syntax_ptr), - path, - }); + self.source_map.diagnostics.push( + ExpressionStoreDiagnostics::UnresolvedMacroCall { + node: InFile::new(outer_file, syntax_ptr), + path, + }, + ); } return collector(self, None); } }; if record_diagnostics { if let Some(err) = res.err { - self.source_map.diagnostics.push(BodyDiagnostic::MacroError { + self.source_map.diagnostics.push(ExpressionStoreDiagnostics::MacroError { node: InFile::new(outer_file, syntax_ptr), err, }); @@ -1464,7 +1526,7 @@ impl ExprCollector<'_> { let (module, def_map) = match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) { Some((def_map, block_id)) => { - self.body.block_scopes.push(block_id); + self.store.block_scopes.push(block_id); (def_map.module_id(DefMap::ROOT), def_map) } None => (self.expander.module, self.def_map.clone()), @@ -1621,7 +1683,7 @@ impl ExprCollector<'_> { pats.push(self.collect_pat(rest, binding_list)); for (&id, &is_used) in binding_list.is_used.iter() { if !is_used { - self.body.bindings[id].problems = + self.store.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); } } @@ -1825,7 +1887,7 @@ impl ExprCollector<'_> { return Some(()); } - self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode { + self.source_map.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode { node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())), cfg, opts: self.expander.cfg_options().clone(), @@ -1853,7 +1915,7 @@ impl ExprCollector<'_> { fn resolve_label( &self, lifetime: Option<ast::Lifetime>, - ) -> Result<Option<LabelId>, BodyDiagnostic> { + ) -> Result<Option<LabelId>, ExpressionStoreDiagnostics> { let Some(lifetime) = lifetime else { return Ok(None) }; let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map { None => (HygieneId::ROOT, None), @@ -1877,7 +1939,7 @@ impl ExprCollector<'_> { return if self.is_label_valid_from_rib(rib_idx) { Ok(Some(*id)) } else { - Err(BodyDiagnostic::UnreachableLabel { + Err(ExpressionStoreDiagnostics::UnreachableLabel { name, node: self.expander.in_file(AstPtr::new(&lifetime)), }) @@ -1903,7 +1965,7 @@ impl ExprCollector<'_> { } } - Err(BodyDiagnostic::UndeclaredLabel { + Err(ExpressionStoreDiagnostics::UndeclaredLabel { name, node: self.expander.in_file(AstPtr::new(&lifetime)), }) @@ -1934,7 +1996,7 @@ impl ExprCollector<'_> { f: impl FnOnce(&mut Self) -> T, ) -> T { self.label_ribs.push(LabelRib::new(RibKind::Normal( - self.body[label].name.clone(), + self.store.labels[label].name.clone(), label, hygiene, ))); @@ -2023,7 +2085,7 @@ impl ExprCollector<'_> { ); } if !hygiene.is_root() { - self.body.expr_hygiene.insert(expr_id, hygiene); + self.store.ident_hygiene.insert(expr_id.into(), hygiene); } expr_id }, @@ -2171,17 +2233,27 @@ impl ExprCollector<'_> { let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new)); let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() }); - let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe { + let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe { id: None, - // We collect the unused expressions here so that we still infer them instead of - // dropping them out of the expression tree - statements: fmt - .orphans - .into_iter() - .map(|expr| Statement::Expr { expr, has_semi: true }) - .collect(), + statements: Box::new([]), tail: Some(unsafe_arg_new), }); + if !fmt.orphans.is_empty() { + unsafe_arg_new = self.alloc_expr_desugared(Expr::Block { + id: None, + // We collect the unused expressions here so that we still infer them instead of + // dropping them out of the expression tree. We cannot store them in the `Unsafe` + // block because then unsafe blocks within them will get a false "unused unsafe" + // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't). + statements: fmt + .orphans + .into_iter() + .map(|expr| Statement::Expr { expr, has_semi: true }) + .collect(), + tail: Some(unsafe_arg_new), + label: None, + }); + } let idx = self.alloc_expr( Expr::Call { @@ -2417,7 +2489,7 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> impl ExprCollector<'_> { fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { let src = self.expander.in_file(ptr); - let id = self.body.exprs.alloc(expr); + let id = self.store.exprs.alloc(expr); self.source_map.expr_map_back.insert(id, src); self.source_map.expr_map.insert(src, id.into()); id @@ -2425,11 +2497,11 @@ impl ExprCollector<'_> { // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed. // Migrate to alloc_expr_desugared_with_ptr and then rename back fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { - self.body.exprs.alloc(expr) + self.store.exprs.alloc(expr) } fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { let src = self.expander.in_file(ptr); - let id = self.body.exprs.alloc(expr); + let id = self.store.exprs.alloc(expr); self.source_map.expr_map_back.insert(id, src); // We intentionally don't fill this as it could overwrite a non-desugared entry // self.source_map.expr_map.insert(src, id); @@ -2440,45 +2512,45 @@ impl ExprCollector<'_> { } fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId { - let binding = self.body.bindings.alloc(Binding { name, mode, problems: None }); + let binding = self.store.bindings.alloc(Binding { name, mode, problems: None }); if let Some(owner) = self.current_binding_owner { - self.body.binding_owners.insert(binding, owner); + self.store.binding_owners.insert(binding, owner); } binding } fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId { let src = self.expander.in_file(ptr); - let id = self.body.pats.alloc(pat); + let id = self.store.pats.alloc(pat); self.source_map.expr_map.insert(src, id.into()); self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left)); id } fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId { let src = self.expander.in_file(ptr); - let id = self.body.pats.alloc(pat); + let id = self.store.pats.alloc(pat); self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right)); self.source_map.pat_map.insert(src, id); id } // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow. fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId { - self.body.pats.alloc(pat) + self.store.pats.alloc(pat) } fn missing_pat(&mut self) -> PatId { - self.body.pats.alloc(Pat::Missing) + self.store.pats.alloc(Pat::Missing) } fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId { let src = self.expander.in_file(ptr); - let id = self.body.labels.alloc(label); + let id = self.store.labels.alloc(label); self.source_map.label_map_back.insert(id, src); self.source_map.label_map.insert(src, id); id } // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow. fn alloc_label_desugared(&mut self, label: Label) -> LabelId { - self.body.labels.alloc(label) + self.store.labels.alloc(label) } fn is_lowering_awaitable_block(&self) -> &Awaitable { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs index 994ba2aa069..032c18688ea 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs @@ -9,7 +9,7 @@ use syntax::{ use tt::TextRange; use crate::{ - body::lower::{ExprCollector, FxIndexSet}, + expr_store::lower::{ExprCollector, FxIndexSet}, hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass}, }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 52b91b522a4..6a0b1e51979 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -60,7 +60,7 @@ pub(super) fn print_body_hir( let mut p = Printer { db, - body, + store: body, buf: header, indent_level: 0, line_format: LineFormat::Newline, @@ -103,14 +103,14 @@ pub(super) fn print_body_hir( pub(super) fn print_expr_hir( db: &dyn DefDatabase, - body: &Body, + store: &ExpressionStore, _owner: DefWithBodyId, expr: ExprId, edition: Edition, ) -> String { let mut p = Printer { db, - body, + store, buf: String::new(), indent_level: 0, line_format: LineFormat::Newline, @@ -122,7 +122,7 @@ pub(super) fn print_expr_hir( pub(super) fn print_pat_hir( db: &dyn DefDatabase, - body: &Body, + store: &ExpressionStore, _owner: DefWithBodyId, pat: PatId, oneline: bool, @@ -130,7 +130,7 @@ pub(super) fn print_pat_hir( ) -> String { let mut p = Printer { db, - body, + store, buf: String::new(), indent_level: 0, line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline }, @@ -157,7 +157,7 @@ macro_rules! wln { struct Printer<'a> { db: &'a dyn DefDatabase, - body: &'a Body, + store: &'a ExpressionStore, buf: String, indent_level: usize, line_format: LineFormat, @@ -233,7 +233,7 @@ impl Printer<'_> { } fn print_expr(&mut self, expr: ExprId) { - let expr = &self.body[expr]; + let expr = &self.store[expr]; match expr { Expr::Missing => w!(self, "�"), @@ -241,7 +241,7 @@ impl Printer<'_> { Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"), Expr::OffsetOf(offset_of) => { w!(self, "builtin#offset_of("); - self.print_type_ref(offset_of.container, &self.body.types); + self.print_type_ref(offset_of.container, &self.store.types); let edition = self.edition; w!( self, @@ -271,7 +271,7 @@ impl Printer<'_> { } Expr::Loop { body, label } => { if let Some(lbl) = label { - w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast(), self.edition)); + w!(self, "{}: ", self.store[*lbl].name.display(self.db.upcast(), self.edition)); } w!(self, "loop "); self.print_expr(*body); @@ -295,7 +295,7 @@ impl Printer<'_> { if let Some(args) = generic_args { w!(self, "::<"); let edition = self.edition; - print_generic_args(self.db, args, &self.body.types, self, edition).unwrap(); + print_generic_args(self.db, args, &self.store.types, self, edition).unwrap(); w!(self, ">"); } w!(self, "("); @@ -330,13 +330,13 @@ impl Printer<'_> { Expr::Continue { label } => { w!(self, "continue"); if let Some(lbl) = label { - w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition)); + w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition)); } } Expr::Break { expr, label } => { w!(self, "break"); if let Some(lbl) = label { - w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition)); + w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition)); } if let Some(expr) = expr { self.whitespace(); @@ -404,7 +404,7 @@ impl Printer<'_> { Expr::Cast { expr, type_ref } => { self.print_expr(*expr); w!(self, " as "); - self.print_type_ref(*type_ref, &self.body.types); + self.print_type_ref(*type_ref, &self.store.types); } Expr::Ref { expr, rawness, mutability } => { w!(self, "&"); @@ -492,13 +492,13 @@ impl Printer<'_> { self.print_pat(*pat); if let Some(ty) = ty { w!(self, ": "); - self.print_type_ref(*ty, &self.body.types); + self.print_type_ref(*ty, &self.store.types); } } w!(self, "|"); if let Some(ret_ty) = ret_type { w!(self, " -> "); - self.print_type_ref(*ret_ty, &self.body.types); + self.print_type_ref(*ret_ty, &self.store.types); } self.whitespace(); self.print_expr(*body); @@ -534,7 +534,7 @@ impl Printer<'_> { Expr::Literal(lit) => self.print_literal(lit), Expr::Block { id: _, statements, tail, label } => { let label = label.map(|lbl| { - format!("{}: ", self.body[lbl].name.display(self.db.upcast(), self.edition)) + format!("{}: ", self.store[lbl].name.display(self.db.upcast(), self.edition)) }); self.print_block(label.as_deref(), statements, tail); } @@ -581,7 +581,7 @@ impl Printer<'_> { } fn print_pat(&mut self, pat: PatId) { - let pat = &self.body[pat]; + let pat = &self.store[pat]; match pat { Pat::Missing => w!(self, "�"), @@ -623,9 +623,9 @@ impl Printer<'_> { let field_name = arg.name.display(self.db.upcast(), edition).to_string(); let mut same_name = false; - if let Pat::Bind { id, subpat: None } = &self.body[arg.pat] { + if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] { if let Binding { name, mode: BindingAnnotation::Unannotated, .. } = - &self.body.bindings[*id] + &self.store.bindings[*id] { if name.as_str() == field_name { same_name = true; @@ -734,7 +734,7 @@ impl Printer<'_> { self.print_pat(*pat); if let Some(ty) = type_ref { w!(self, ": "); - self.print_type_ref(*ty, &self.body.types); + self.print_type_ref(*ty, &self.store.types); } if let Some(init) = initializer { w!(self, " = "); @@ -799,11 +799,11 @@ impl Printer<'_> { fn print_path(&mut self, path: &Path) { let edition = self.edition; - print_path(self.db, path, &self.body.types, self, edition).unwrap(); + print_path(self.db, path, &self.store.types, self, edition).unwrap(); } fn print_binding(&mut self, id: BindingId) { - let Binding { name, mode, .. } = &self.body.bindings[id]; + let Binding { name, mode, .. } = &self.store.bindings[id]; let mode = match mode { BindingAnnotation::Unannotated => "", BindingAnnotation::Mutable => "mut ", diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index 08af470b965..859a706177a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -4,8 +4,8 @@ use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use triomphe::Arc; use crate::{ - body::{Body, HygieneId}, db::DefDatabase, + expr_store::{Body, ExpressionStore, HygieneId}, hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement}, BlockId, ConstBlockId, DefWithBodyId, }; @@ -53,7 +53,7 @@ pub struct ScopeData { impl ExprScopes { pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> { let body = db.body(def); - let mut scopes = ExprScopes::new(&body, |const_block| { + let mut scopes = ExprScopes::new_body(&body, |const_block| { db.lookup_intern_anonymous_const(const_block).root }); scopes.shrink_to_fit(); @@ -104,7 +104,7 @@ fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> { } impl ExprScopes { - fn new( + fn new_body( body: &Body, resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, ) -> ExprScopes { @@ -179,28 +179,28 @@ impl ExprScopes { fn add_bindings( &mut self, - body: &Body, + store: &ExpressionStore, scope: ScopeId, binding: BindingId, hygiene: HygieneId, ) { - let Binding { name, .. } = &body.bindings[binding]; + let Binding { name, .. } = &store.bindings[binding]; let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene }); self.scopes[scope].entries = IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry); } - fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { - let pattern = &body[pat]; + fn add_pat_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, pat: PatId) { + let pattern = &store[pat]; if let Pat::Bind { id, .. } = *pattern { - self.add_bindings(body, scope, id, body.binding_hygiene(id)); + self.add_bindings(store, scope, id, store.binding_hygiene(id)); } - pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat)); + pattern.walk_child_pats(|pat| self.add_pat_bindings(store, scope, pat)); } - fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) { - params.iter().for_each(|pat| self.add_pat_bindings(body, scope, *pat)); + fn add_params_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, params: &[PatId]) { + params.iter().for_each(|pat| self.add_pat_bindings(store, scope, *pat)); } fn set_scope(&mut self, node: ExprId, scope: ScopeId) { @@ -218,7 +218,7 @@ impl ExprScopes { fn compute_block_scopes( statements: &[Statement], tail: Option<ExprId>, - body: &Body, + store: &ExpressionStore, scopes: &mut ExprScopes, scope: &mut ScopeId, resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, @@ -227,17 +227,17 @@ fn compute_block_scopes( match stmt { Statement::Let { pat, initializer, else_branch, .. } => { if let Some(expr) = initializer { - compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); + compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block); } if let Some(expr) = else_branch { - compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); + compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block); } *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(body, *scope, *pat); + scopes.add_pat_bindings(store, *scope, *pat); } Statement::Expr { expr, .. } => { - compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); + compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block); } Statement::Item(Item::MacroDef(macro_id)) => { *scope = scopes.new_macro_def_scope(*scope, macro_id.clone()); @@ -246,32 +246,32 @@ fn compute_block_scopes( } } if let Some(expr) = tail { - compute_expr_scopes(expr, body, scopes, scope, resolve_const_block); + compute_expr_scopes(expr, store, scopes, scope, resolve_const_block); } } fn compute_expr_scopes( expr: ExprId, - body: &Body, + store: &ExpressionStore, scopes: &mut ExprScopes, scope: &mut ScopeId, resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, ) { let make_label = - |label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone())); + |label: &Option<LabelId>| label.map(|label| (label, store.labels[label].name.clone())); let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| { - compute_expr_scopes(expr, body, scopes, scope, resolve_const_block) + compute_expr_scopes(expr, store, scopes, scope, resolve_const_block) }; scopes.set_scope(expr, *scope); - match &body[expr] { + match &store[expr] { Expr::Block { statements, tail, id, label } => { let mut scope = scopes.new_block_scope(*scope, *id, make_label(label)); // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block); + compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block); } Expr::Const(id) => { let mut scope = scopes.root_scope(); @@ -282,7 +282,7 @@ fn compute_expr_scopes( // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block); + compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block); } Expr::Loop { body: body_expr, label } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); @@ -290,14 +290,14 @@ fn compute_expr_scopes( } Expr::Closure { args, body: body_expr, .. } => { let mut scope = scopes.new_scope(*scope); - scopes.add_params_bindings(body, scope, args); + scopes.add_params_bindings(store, scope, args); compute_expr_scopes(scopes, *body_expr, &mut scope); } Expr::Match { expr, arms } => { compute_expr_scopes(scopes, *expr, scope); for arm in arms.iter() { let mut scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(body, scope, arm.pat); + scopes.add_pat_bindings(store, scope, arm.pat); if let Some(guard) = arm.guard { scope = scopes.new_scope(scope); compute_expr_scopes(scopes, guard, &mut scope); @@ -316,9 +316,9 @@ fn compute_expr_scopes( &Expr::Let { pat, expr } => { compute_expr_scopes(scopes, expr, scope); *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(body, *scope, pat); + scopes.add_pat_bindings(store, *scope, pat); } - _ => body.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)), + _ => store.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)), }; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs index edc7c4c1f21..9bf1ddb4793 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs @@ -1,6 +1,7 @@ mod block; use expect_test::{expect, Expect}; +use la_arena::RawIdx; use test_fixture::WithFixture; use crate::{test_db::TestDB, ModuleDefId}; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/block.rs index e136dd18a55..e136dd18a55 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/block.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index 7b3f1d06d21..e2b36da79b2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -433,7 +433,7 @@ impl GenericParams { GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params), GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params), GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params), - GenericDefId::ConstId(_) => ( + GenericDefId::ConstId(_) | GenericDefId::StaticId(_) => ( Arc::new(GenericParams { type_or_consts: Default::default(), lifetimes: Default::default(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 85963469430..0dcddf162b2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -19,7 +19,7 @@ use std::fmt; use hir_expand::{name::Name, MacroDefId}; use intern::Symbol; -use la_arena::{Idx, RawIdx}; +use la_arena::Idx; use rustc_apfloat::ieee::{Half as f16, Quad as f128}; use syntax::ast; use type_ref::TypeRefId; @@ -37,13 +37,10 @@ pub type BindingId = Idx<Binding>; pub type ExprId = Idx<Expr>; -/// FIXME: this is a hacky function which should be removed -pub(crate) fn dummy_expr_id() -> ExprId { - ExprId::from_raw(RawIdx::from(u32::MAX)) -} - pub type PatId = Idx<Pat>; +// FIXME: Encode this as a single u32, we won't ever reach all 32 bits especially given these counts +// are local to the body. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum ExprOrPatId { ExprId(ExprId), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index 34635997bdf..6137bd34d64 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -519,7 +519,7 @@ mod tests { crate_graph[krate] .display_name .as_ref() - .is_some_and(|it| &**it.crate_name() == crate_name) + .is_some_and(|it| it.crate_name().as_str() == crate_name) }) .expect("could not find crate"); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index b5bf2feb82a..8d5b3eeb28e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -937,7 +937,7 @@ pub struct Param { bitflags::bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] - pub(crate) struct FnFlags: u8 { + pub(crate) struct FnFlags: u16 { const HAS_SELF_PARAM = 1 << 0; const HAS_BODY = 1 << 1; const HAS_DEFAULT_KW = 1 << 2; @@ -946,6 +946,12 @@ bitflags::bitflags! { const HAS_UNSAFE_KW = 1 << 5; const IS_VARARGS = 1 << 6; const HAS_SAFE_KW = 1 << 7; + /// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396), + /// but keeping it for all functions will consume a lot of memory when there are + /// only very few functions with it. So we only encode its existence here, and lookup + /// it if needed. + const HAS_TARGET_FEATURE = 1 << 8; + const DEPRECATED_SAFE_2024 = 1 << 9; } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 38733577d1c..59f51db9f74 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -502,4 +502,5 @@ language_item_table! { String, sym::String, string, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; + Ordering, sym::Ordering, ordering, Target::Enum, GenericRequirement::None; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index c78818c642c..c8efd904320 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -42,7 +42,7 @@ pub mod lang_item; pub mod hir; pub use self::hir::type_ref; -pub mod body; +pub mod expr_store; pub mod resolver; pub mod nameres; @@ -693,6 +693,7 @@ impl TypeOwnerId { Some(match self { TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it), TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it), + TypeOwnerId::StaticId(it) => GenericDefId::StaticId(it), TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it), TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it), TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it), @@ -701,7 +702,7 @@ impl TypeOwnerId { TypeOwnerId::EnumVariantId(it) => { GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent)) } - TypeOwnerId::InTypeConstId(_) | TypeOwnerId::StaticId(_) => return None, + TypeOwnerId::InTypeConstId(_) => return None, }) } } @@ -743,6 +744,7 @@ impl From<GenericDefId> for TypeOwnerId { GenericDefId::TypeAliasId(it) => it.into(), GenericDefId::ImplId(it) => it.into(), GenericDefId::ConstId(it) => it.into(), + GenericDefId::StaticId(it) => it.into(), } } } @@ -851,7 +853,7 @@ impl GeneralConstId { pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> { match self { GeneralConstId::ConstId(it) => Some(it.into()), - GeneralConstId::StaticId(_) => None, + GeneralConstId::StaticId(it) => Some(it.into()), GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db), GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db), } @@ -897,7 +899,7 @@ impl DefWithBodyId { pub fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option<GenericDefId> { match self { DefWithBodyId::FunctionId(f) => Some(f.into()), - DefWithBodyId::StaticId(_) => None, + DefWithBodyId::StaticId(s) => Some(s.into()), DefWithBodyId::ConstId(c) => Some(c.into()), DefWithBodyId::VariantId(c) => Some(c.lookup(db).parent.into()), // FIXME: stable rust doesn't allow generics in constants, but we should @@ -922,23 +924,28 @@ impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId); #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum GenericDefId { - FunctionId(FunctionId), AdtId(AdtId), - TraitId(TraitId), - TraitAliasId(TraitAliasId), - TypeAliasId(TypeAliasId), - ImplId(ImplId), // consts can have type parameters from their parents (i.e. associated consts of traits) ConstId(ConstId), + FunctionId(FunctionId), + ImplId(ImplId), + // can't actually have generics currently, but they might in the future + // More importantly, this completes the set of items that contain type references + // which is to be used by the signature expression store in the future. + StaticId(StaticId), + TraitAliasId(TraitAliasId), + TraitId(TraitId), + TypeAliasId(TypeAliasId), } impl_from!( - FunctionId, AdtId(StructId, EnumId, UnionId), - TraitId, - TraitAliasId, - TypeAliasId, + ConstId, + FunctionId, ImplId, - ConstId + StaticId, + TraitAliasId, + TraitId, + TypeAliasId for GenericDefId ); @@ -969,6 +976,7 @@ impl GenericDefId { GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ConstId(it) => (it.lookup(db).id.file_id(), None), + GenericDefId::StaticId(it) => (it.lookup(db).id.file_id(), None), } } @@ -1350,6 +1358,7 @@ impl HasModule for GenericDefId { GenericDefId::TypeAliasId(it) => it.module(db), GenericDefId::ImplId(it) => it.module(db), GenericDefId::ConstId(it) => it.module(db), + GenericDefId::StaticId(it) => it.module(db), } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 39d383f0159..3b6e3c5916e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -337,7 +337,7 @@ impl DefMap { pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: CrateId) -> Arc<DefMap> { let crate_graph = db.crate_graph(); let krate = &crate_graph[crate_id]; - let name = krate.display_name.as_deref().unwrap_or_default(); + let name = krate.display_name.as_deref().map(Symbol::as_str).unwrap_or_default(); let _p = tracing::info_span!("crate_def_map_query", ?name).entered(); let module_data = ModuleData::new( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 8c556d8a8c3..7e13ae2f7a1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -10,13 +10,13 @@ use smallvec::{smallvec, SmallVec}; use triomphe::Arc; use crate::{ - body::{ - scope::{ExprScopes, ScopeId}, - HygieneId, - }, builtin_type::BuiltinType, data::ExternCrateDeclData, db::DefDatabase, + expr_store::{ + scope::{ExprScopes, ScopeId}, + HygieneId, + }, generics::{GenericParams, TypeOrConstParamData}, hir::{BindingId, ExprId, LabelId}, item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE}, @@ -1264,6 +1264,7 @@ impl HasResolver for GenericDefId { GenericDefId::TypeAliasId(inner) => inner.resolver(db), GenericDefId::ImplId(inner) => inner.resolver(db), GenericDefId::ConstId(inner) => inner.resolver(db), + GenericDefId::StaticId(inner) => inner.resolver(db), } } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index 848870c3a38..21e5fb5ef9e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -262,6 +262,6 @@ impl AsName for ast::FieldKind { impl AsName for base_db::Dependency { fn as_name(&self) -> Name { - Name::new_root(&self.name) + Name::new_symbol_root((*self.name).clone()) } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs index 6ff7831fd81..c744fbce77b 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -41,9 +41,9 @@ pub fn prettify_macro_expansion( } else if let Some(dep) = target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate) { - make::tokens::ident(&dep.name) + make::tokens::ident(dep.name.as_str()) } else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name { - make::tokens::ident(crate_name.crate_name()) + make::tokens::ident(crate_name.crate_name().as_str()) } else { return dollar_crate.clone(); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 62feca5f8cb..e0e366f4501 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -194,7 +194,11 @@ pub(crate) fn deref_by_trait( } let trait_id = || { - if use_receiver_trait { + // FIXME: Remove the `false` once `Receiver` needs to be stabilized, doing so will + // effectively bump the MSRV of rust-analyzer to 1.84 due to 1.83 and below lacking the + // blanked impl on `Deref`. + #[expect(clippy::overly_complex_bool_expr)] + if use_receiver_trait && false { if let Some(receiver) = db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait()) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 142766c039b..7839589994b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -3,7 +3,7 @@ use base_db::{ra_salsa::Cycle, CrateId}; use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex}; use hir_def::{ - body::{Body, HygieneId}, + expr_store::{Body, HygieneId}, hir::{Expr, ExprId}, path::Path, resolver::{Resolver, ValueNs}, @@ -124,6 +124,7 @@ pub(crate) fn path_to_const<'g>( ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)), expected_ty, )), + // FIXME: With feature(adt_const_params), we also need to consider other things here, e.g. struct constructors. _ => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 7f9f0c0de19..0b5f1319243 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -36,7 +36,7 @@ use crate::{ }; pub(crate) use hir_def::{ - body::Body, + expr_store::Body, hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement}, LocalFieldId, VariantId, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index c5d8c956615..b0f9fc53e29 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -11,7 +11,8 @@ pub(crate) mod pat_analysis; use chalk_ir::Mutability; use hir_def::{ - body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId, + data::adt::VariantData, expr_store::Body, hir::PatId, AdtId, EnumVariantId, LocalFieldId, + VariantId, }; use hir_expand::name::Name; use span::Edition; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 6bba83fac98..ac849b0762d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -5,28 +5,31 @@ use std::mem; use either::Either; use hir_def::{ - body::Body, + expr_store::Body, hir::{Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp}, path::Path, resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs}, type_ref::Rawness, - AdtId, DefWithBodyId, FieldId, VariantId, + AdtId, DefWithBodyId, FieldId, FunctionId, VariantId, }; +use span::Edition; use crate::{ - db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind, + db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TargetFeatures, TyExt, + TyKind, }; -/// Returns `(unsafe_exprs, fn_is_unsafe)`. -/// -/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`. -pub fn missing_unsafe( - db: &dyn HirDatabase, - def: DefWithBodyId, -) -> (Vec<(ExprOrPatId, UnsafetyReason)>, bool) { +#[derive(Debug, Default)] +pub struct MissingUnsafeResult { + pub unsafe_exprs: Vec<(ExprOrPatId, UnsafetyReason)>, + /// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`. + pub fn_is_unsafe: bool, + pub deprecated_safe_calls: Vec<ExprId>, +} + +pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> MissingUnsafeResult { let _p = tracing::info_span!("missing_unsafe").entered(); - let mut res = Vec::new(); let is_unsafe = match def { DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), DefWithBodyId::StaticId(_) @@ -35,11 +38,19 @@ pub fn missing_unsafe( | DefWithBodyId::InTypeConstId(_) => false, }; + let mut res = MissingUnsafeResult { fn_is_unsafe: is_unsafe, ..MissingUnsafeResult::default() }; let body = db.body(def); let infer = db.infer(def); - let mut callback = |node, inside_unsafe_block, reason| { - if inside_unsafe_block == InsideUnsafeBlock::No { - res.push((node, reason)); + let mut callback = |diag| match diag { + UnsafeDiagnostic::UnsafeOperation { node, inside_unsafe_block, reason } => { + if inside_unsafe_block == InsideUnsafeBlock::No { + res.unsafe_exprs.push((node, reason)); + } + } + UnsafeDiagnostic::DeprecatedSafe2024 { node, inside_unsafe_block } => { + if inside_unsafe_block == InsideUnsafeBlock::No { + res.deprecated_safe_calls.push(node) + } } }; let mut visitor = UnsafeVisitor::new(db, &infer, &body, def, &mut callback); @@ -54,7 +65,7 @@ pub fn missing_unsafe( } } - (res, is_unsafe) + res } #[derive(Debug, Clone, Copy)] @@ -73,15 +84,31 @@ pub enum InsideUnsafeBlock { Yes, } +#[derive(Debug)] +enum UnsafeDiagnostic { + UnsafeOperation { + node: ExprOrPatId, + inside_unsafe_block: InsideUnsafeBlock, + reason: UnsafetyReason, + }, + /// A lint. + DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock }, +} + pub fn unsafe_expressions( db: &dyn HirDatabase, infer: &InferenceResult, def: DefWithBodyId, body: &Body, current: ExprId, - unsafe_expr_cb: &mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason), + callback: &mut dyn FnMut(InsideUnsafeBlock), ) { - let mut visitor = UnsafeVisitor::new(db, infer, body, def, unsafe_expr_cb); + let mut visitor_callback = |diag| { + if let UnsafeDiagnostic::UnsafeOperation { inside_unsafe_block, .. } = diag { + callback(inside_unsafe_block); + } + }; + let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback); _ = visitor.resolver.update_to_inner_scope(db.upcast(), def, current); visitor.walk_expr(current); } @@ -95,7 +122,10 @@ struct UnsafeVisitor<'a> { inside_unsafe_block: InsideUnsafeBlock, inside_assignment: bool, inside_union_destructure: bool, - unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason), + callback: &'a mut dyn FnMut(UnsafeDiagnostic), + def_target_features: TargetFeatures, + // FIXME: This needs to be the edition of the span of each call. + edition: Edition, } impl<'a> UnsafeVisitor<'a> { @@ -104,9 +134,14 @@ impl<'a> UnsafeVisitor<'a> { infer: &'a InferenceResult, body: &'a Body, def: DefWithBodyId, - unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason), + unsafe_expr_cb: &'a mut dyn FnMut(UnsafeDiagnostic), ) -> Self { let resolver = def.resolver(db.upcast()); + let def_target_features = match def { + DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())), + _ => TargetFeatures::default(), + }; + let edition = db.crate_graph()[resolver.module().krate()].edition; Self { db, infer, @@ -116,12 +151,34 @@ impl<'a> UnsafeVisitor<'a> { inside_unsafe_block: InsideUnsafeBlock::No, inside_assignment: false, inside_union_destructure: false, - unsafe_expr_cb, + callback: unsafe_expr_cb, + def_target_features, + edition, } } - fn call_cb(&mut self, node: ExprOrPatId, reason: UnsafetyReason) { - (self.unsafe_expr_cb)(node, self.inside_unsafe_block, reason); + fn on_unsafe_op(&mut self, node: ExprOrPatId, reason: UnsafetyReason) { + (self.callback)(UnsafeDiagnostic::UnsafeOperation { + node, + inside_unsafe_block: self.inside_unsafe_block, + reason, + }); + } + + fn check_call(&mut self, node: ExprId, func: FunctionId) { + let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition); + match unsafety { + crate::utils::Unsafety::Safe => {} + crate::utils::Unsafety::Unsafe => { + self.on_unsafe_op(node.into(), UnsafetyReason::UnsafeFnCall) + } + crate::utils::Unsafety::DeprecatedSafe2024 => { + (self.callback)(UnsafeDiagnostic::DeprecatedSafe2024 { + node, + inside_unsafe_block: self.inside_unsafe_block, + }) + } + } } fn walk_pats_top(&mut self, pats: impl Iterator<Item = PatId>, parent_expr: ExprId) { @@ -146,7 +203,9 @@ impl<'a> UnsafeVisitor<'a> { | Pat::Ref { .. } | Pat::Box { .. } | Pat::Expr(..) - | Pat::ConstBlock(..) => self.call_cb(current.into(), UnsafetyReason::UnionField), + | Pat::ConstBlock(..) => { + self.on_unsafe_op(current.into(), UnsafetyReason::UnionField) + } // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read. Pat::Missing | Pat::Wild | Pat::Or(_) => {} } @@ -180,9 +239,13 @@ impl<'a> UnsafeVisitor<'a> { let inside_assignment = mem::replace(&mut self.inside_assignment, false); match expr { &Expr::Call { callee, .. } => { - if let Some(func) = self.infer[callee].as_fn_def(self.db) { - if is_fn_unsafe_to_call(self.db, func) { - self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall); + let callee = &self.infer[callee]; + if let Some(func) = callee.as_fn_def(self.db) { + self.check_call(current, func); + } + if let TyKind::Function(fn_ptr) = callee.kind(Interner) { + if fn_ptr.sig.safety == chalk_ir::Safety::Unsafe { + self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall); } } } @@ -209,18 +272,13 @@ impl<'a> UnsafeVisitor<'a> { } } Expr::MethodCall { .. } => { - if self - .infer - .method_resolution(current) - .map(|(func, _)| is_fn_unsafe_to_call(self.db, func)) - .unwrap_or(false) - { - self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall); + if let Some((func, _)) = self.infer.method_resolution(current) { + self.check_call(current, func); } } Expr::UnaryOp { expr, op: UnaryOp::Deref } => { if let TyKind::Raw(..) = &self.infer[*expr].kind(Interner) { - self.call_cb(current.into(), UnsafetyReason::RawPtrDeref); + self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref); } } Expr::Unsafe { .. } => { @@ -235,7 +293,7 @@ impl<'a> UnsafeVisitor<'a> { self.walk_pats_top(std::iter::once(target), current); self.inside_assignment = old_inside_assignment; } - Expr::InlineAsm(_) => self.call_cb(current.into(), UnsafetyReason::InlineAsm), + Expr::InlineAsm(_) => self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm), // rustc allows union assignment to propagate through field accesses and casts. Expr::Cast { .. } => self.inside_assignment = inside_assignment, Expr::Field { .. } => { @@ -244,7 +302,7 @@ impl<'a> UnsafeVisitor<'a> { if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) = self.infer.field_resolution(current) { - self.call_cb(current.into(), UnsafetyReason::UnionField); + self.on_unsafe_op(current.into(), UnsafetyReason::UnionField); } } } @@ -279,9 +337,9 @@ impl<'a> UnsafeVisitor<'a> { if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { let static_data = self.db.static_data(id); if static_data.mutable { - self.call_cb(node, UnsafetyReason::MutableStatic); + self.on_unsafe_op(node, UnsafetyReason::MutableStatic); } else if static_data.is_extern && !static_data.has_safe_kw { - self.call_cb(node, UnsafetyReason::ExternStatic); + self.on_unsafe_op(node, UnsafetyReason::ExternStatic); } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index abbf2a4f2ef..18cf6e5ce36 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -262,7 +262,8 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic GenericDefId::FunctionId(it) => it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).container, GenericDefId::ConstId(it) => it.lookup(db).container, - GenericDefId::AdtId(_) + GenericDefId::StaticId(_) + | GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) | GenericDefId::TraitAliasId(_) => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 25bb3a76de2..617ebba8811 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -34,9 +34,9 @@ use chalk_ir::{ }; use either::Either; use hir_def::{ - body::{Body, HygieneId}, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, data::{ConstData, StaticData}, + expr_store::{Body, HygieneId}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::{LangItem, LangItemTarget}, layout::Integer, @@ -466,6 +466,9 @@ pub struct InferenceResult { pub type_of_for_iterator: FxHashMap<ExprId, Ty>, type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, /// Whether there are any type-mismatching errors in the result. + // FIXME: This isn't as useful as initially thought due to us falling back placeholders to + // `TyKind::Error`. + // Which will then mark this field. pub(crate) has_errors: bool, /// Interned common types to return references to. // FIXME: Move this into `InferenceContext` @@ -943,7 +946,7 @@ impl<'a> InferenceContext<'a> { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); - self.infer_top_pat(*pat, &ty); + self.infer_top_pat(*pat, &ty, None); if ty .data(Interner) .flags diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 21d0be6ed5f..eb193686e96 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -374,6 +374,7 @@ enum PointerKind { fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> { let ty = table.resolve_ty_shallow(ty); + let ty = table.normalize_associated_types_in(ty); if table.is_sized(&ty) { return Ok(Some(PointerKind::Thin)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs index 032dc37899d..b85378531ad 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -5,7 +5,7 @@ use std::cell::RefCell; use std::ops::{Deref, DerefMut}; -use hir_def::body::HygieneId; +use hir_def::expr_store::HygieneId; use hir_def::hir::ExprOrPatId; use hir_def::path::{Path, PathSegment, PathSegments}; use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index b951443897c..86e5afdb509 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -43,9 +43,9 @@ use crate::{ primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, - Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer, - FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, - TyBuilder, TyExt, TyKind, + Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, + DeclOrigin, FnAbi, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, + TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, }; use super::{ @@ -334,7 +334,11 @@ impl InferenceContext<'_> { ExprIsRead::No }; let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read); - self.infer_top_pat(pat, &input_ty); + self.infer_top_pat( + pat, + &input_ty, + Some(DeclContext { origin: DeclOrigin::LetExpr }), + ); self.result.standard_types.bool_.clone() } Expr::Block { statements, tail, label, id } => { @@ -461,7 +465,7 @@ impl InferenceContext<'_> { // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(&sig_tys) { - self.infer_top_pat(*arg_pat, arg_ty); + self.infer_top_pat(*arg_pat, arg_ty, None); } // FIXME: lift these out into a struct @@ -582,7 +586,7 @@ impl InferenceContext<'_> { let mut all_arms_diverge = Diverges::Always; for arm in arms.iter() { let input_ty = self.resolve_ty_shallow(&input_ty); - self.infer_top_pat(arm.pat, &input_ty); + self.infer_top_pat(arm.pat, &input_ty, None); } let expected = expected.adjust_for_branches(&mut self.table); @@ -927,7 +931,7 @@ impl InferenceContext<'_> { let resolver_guard = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr); self.inside_assignment = true; - self.infer_top_pat(target, &rhs_ty); + self.infer_top_pat(target, &rhs_ty, None); self.inside_assignment = false; self.resolver.reset_to_guard(resolver_guard); } @@ -1632,8 +1636,11 @@ impl InferenceContext<'_> { decl_ty }; - this.infer_top_pat(*pat, &ty); + let decl = DeclContext { + origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() }, + }; + this.infer_top_pat(*pat, &ty, Some(decl)); if let Some(expr) = else_branch { let previous_diverges = mem::replace(&mut this.diverges, Diverges::Maybe); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 00398f019da..5ff22bea34d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -3,23 +3,24 @@ use std::iter::repeat_with; use hir_def::{ - body::Body, + expr_store::Body, hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId}, path::Path, + HasModule, }; use hir_expand::name::Name; use stdx::TupleExt; use crate::{ - consteval::{try_const_usize, usize_const}, + consteval::{self, try_const_usize, usize_const}, infer::{ coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext, TypeMismatch, }, lower::lower_to_chalk_mutability, primitive::UintTy, - static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty, - TyBuilder, TyExt, TyKind, + static_lifetime, DeclContext, DeclOrigin, InferenceDiagnostic, Interner, Mutability, Scalar, + Substitution, Ty, TyBuilder, TyExt, TyKind, }; impl InferenceContext<'_> { @@ -34,6 +35,7 @@ impl InferenceContext<'_> { id: PatId, ellipsis: Option<u32>, subs: &[PatId], + decl: Option<DeclContext>, ) -> Ty { let (ty, def) = self.resolve_variant(id.into(), path, true); let var_data = def.map(|it| it.variant_data(self.db.upcast())); @@ -92,13 +94,13 @@ impl InferenceContext<'_> { } }; - self.infer_pat(subpat, &expected_ty, default_bm); + self.infer_pat(subpat, &expected_ty, default_bm, decl); } } None => { let err_ty = self.err_ty(); for &inner in subs { - self.infer_pat(inner, &err_ty, default_bm); + self.infer_pat(inner, &err_ty, default_bm, decl); } } } @@ -114,6 +116,7 @@ impl InferenceContext<'_> { default_bm: BindingMode, id: PatId, subs: impl ExactSizeIterator<Item = (Name, PatId)>, + decl: Option<DeclContext>, ) -> Ty { let (ty, def) = self.resolve_variant(id.into(), path, false); if let Some(variant) = def { @@ -162,13 +165,13 @@ impl InferenceContext<'_> { } }; - self.infer_pat(inner, &expected_ty, default_bm); + self.infer_pat(inner, &expected_ty, default_bm, decl); } } None => { let err_ty = self.err_ty(); for (_, inner) in subs { - self.infer_pat(inner, &err_ty, default_bm); + self.infer_pat(inner, &err_ty, default_bm, decl); } } } @@ -185,6 +188,7 @@ impl InferenceContext<'_> { default_bm: BindingMode, ellipsis: Option<u32>, subs: &[PatId], + decl: Option<DeclContext>, ) -> Ty { let expected = self.resolve_ty_shallow(expected); let expectations = match expected.as_tuple() { @@ -209,12 +213,12 @@ impl InferenceContext<'_> { // Process pre for (ty, pat) in inner_tys.iter_mut().zip(pre) { - *ty = self.infer_pat(*pat, ty, default_bm); + *ty = self.infer_pat(*pat, ty, default_bm, decl); } // Process post for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) { - *ty = self.infer_pat(*pat, ty, default_bm); + *ty = self.infer_pat(*pat, ty, default_bm, decl); } TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys)) @@ -223,11 +227,17 @@ impl InferenceContext<'_> { /// The resolver needs to be updated to the surrounding expression when inside assignment /// (because there, `Pat::Path` can refer to a variable). - pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) { - self.infer_pat(pat, expected, BindingMode::default()); + pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty, decl: Option<DeclContext>) { + self.infer_pat(pat, expected, BindingMode::default(), decl); } - fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty { + fn infer_pat( + &mut self, + pat: PatId, + expected: &Ty, + mut default_bm: BindingMode, + decl: Option<DeclContext>, + ) -> Ty { let mut expected = self.resolve_ty_shallow(expected); if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment { @@ -261,11 +271,11 @@ impl InferenceContext<'_> { let ty = match &self.body[pat] { Pat::Tuple { args, ellipsis } => { - self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args) + self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args, decl) } Pat::Or(pats) => { for pat in pats.iter() { - self.infer_pat(*pat, &expected, default_bm); + self.infer_pat(*pat, &expected, default_bm, decl); } expected.clone() } @@ -274,6 +284,7 @@ impl InferenceContext<'_> { lower_to_chalk_mutability(mutability), &expected, default_bm, + decl, ), Pat::TupleStruct { path: p, args: subpats, ellipsis } => self .infer_tuple_struct_pat_like( @@ -283,10 +294,11 @@ impl InferenceContext<'_> { pat, *ellipsis, subpats, + decl, ), Pat::Record { path: p, args: fields, ellipsis: _ } => { let subs = fields.iter().map(|f| (f.name.clone(), f.pat)); - self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs) + self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs, decl) } Pat::Path(path) => { let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()); @@ -319,10 +331,10 @@ impl InferenceContext<'_> { } } Pat::Bind { id, subpat } => { - return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected); + return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected, decl); } Pat::Slice { prefix, slice, suffix } => { - self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm) + self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm, decl) } Pat::Wild => expected.clone(), Pat::Range { .. } => { @@ -345,7 +357,7 @@ impl InferenceContext<'_> { _ => (self.result.standard_types.unknown.clone(), None), }; - let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm); + let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm, decl); let mut b = TyBuilder::adt(self.db, box_adt).push(inner_ty); if let Some(alloc_ty) = alloc_ty { @@ -420,6 +432,7 @@ impl InferenceContext<'_> { mutability: Mutability, expected: &Ty, default_bm: BindingMode, + decl: Option<DeclContext>, ) -> Ty { let (expectation_type, expectation_lt) = match expected.as_reference() { Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()), @@ -433,7 +446,7 @@ impl InferenceContext<'_> { (inner_ty, inner_lt) } }; - let subty = self.infer_pat(inner_pat, &expectation_type, default_bm); + let subty = self.infer_pat(inner_pat, &expectation_type, default_bm, decl); TyKind::Ref(mutability, expectation_lt, subty).intern(Interner) } @@ -444,6 +457,7 @@ impl InferenceContext<'_> { default_bm: BindingMode, subpat: Option<PatId>, expected: &Ty, + decl: Option<DeclContext>, ) -> Ty { let Binding { mode, .. } = self.body.bindings[binding]; let mode = if mode == BindingAnnotation::Unannotated { @@ -454,7 +468,7 @@ impl InferenceContext<'_> { self.result.binding_modes.insert(pat, mode); let inner_ty = match subpat { - Some(subpat) => self.infer_pat(subpat, expected, default_bm), + Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl), None => expected.clone(), }; let inner_ty = self.insert_type_vars_shallow(inner_ty); @@ -478,14 +492,28 @@ impl InferenceContext<'_> { slice: &Option<PatId>, suffix: &[PatId], default_bm: BindingMode, + decl: Option<DeclContext>, ) -> Ty { + let expected = self.resolve_ty_shallow(expected); + + // If `expected` is an infer ty, we try to equate it to an array if the given pattern + // allows it. See issue #16609 + if self.pat_is_irrefutable(decl) && expected.is_ty_var() { + if let Some(resolved_array_ty) = + self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice) + { + self.unify(&expected, &resolved_array_ty); + } + } + + let expected = self.resolve_ty_shallow(&expected); let elem_ty = match expected.kind(Interner) { TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), _ => self.err_ty(), }; for &pat_id in prefix.iter().chain(suffix.iter()) { - self.infer_pat(pat_id, &elem_ty, default_bm); + self.infer_pat(pat_id, &elem_ty, default_bm, decl); } if let &Some(slice_pat_id) = slice { @@ -499,7 +527,7 @@ impl InferenceContext<'_> { _ => TyKind::Slice(elem_ty.clone()), } .intern(Interner); - self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm); + self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm, decl); } match expected.kind(Interner) { @@ -528,7 +556,7 @@ impl InferenceContext<'_> { self.infer_expr(expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes) } - fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool { + fn is_non_ref_pat(&mut self, body: &hir_def::expr_store::Body, pat: PatId) -> bool { match &body[pat] { Pat::Tuple { .. } | Pat::TupleStruct { .. } @@ -553,6 +581,59 @@ impl InferenceContext<'_> { | Pat::Expr(_) => false, } } + + fn try_resolve_slice_ty_to_array_ty( + &mut self, + before: &[PatId], + suffix: &[PatId], + slice: &Option<PatId>, + ) -> Option<Ty> { + if !slice.is_none() { + return None; + } + + let len = before.len() + suffix.len(); + let size = + consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db.upcast())); + + let elem_ty = self.table.new_type_var(); + let array_ty = TyKind::Array(elem_ty.clone(), size).intern(Interner); + Some(array_ty) + } + + /// Used to determine whether we can infer the expected type in the slice pattern to be of type array. + /// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable + /// patterns we wouldn't e.g. report ambiguity in the following situation: + /// + /// ```ignore(rust) + /// struct Zeroes; + /// const ARR: [usize; 2] = [0; 2]; + /// const ARR2: [usize; 2] = [2; 2]; + /// + /// impl Into<&'static [usize; 2]> for Zeroes { + /// fn into(self) -> &'static [usize; 2] { + /// &ARR + /// } + /// } + /// + /// impl Into<&'static [usize]> for Zeroes { + /// fn into(self) -> &'static [usize] { + /// &ARR2 + /// } + /// } + /// + /// fn main() { + /// let &[a, b]: &[usize] = Zeroes.into() else { + /// .. + /// }; + /// } + /// ``` + /// + /// If we're in an irrefutable pattern we prefer the array impl candidate given that + /// the slice impl candidate would be rejected anyway (if no ambiguity existed). + fn pat_is_irrefutable(&self, decl_ctxt: Option<DeclContext>) -> bool { + matches!(decl_ctxt, Some(DeclContext { origin: DeclOrigin::LocalDecl { has_else: false } })) + } } pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 73bcefaf2a9..36ec60a7a2f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -86,10 +86,9 @@ impl InferenceContext<'_> { } }; - let generic_def_id = value_def.to_generic_def_id(self.db); - let Some(generic_def) = generic_def_id else { - // `value_def` is the kind of item that can never be generic (i.e. statics, at least - // currently). We can just skip the binders to get its type. + let generic_def = value_def.to_generic_def_id(self.db); + if let GenericDefId::StaticId(_) = generic_def { + // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type. let (ty, binders) = self.db.value_ty(value_def)?.into_value_and_skipped_binders(); stdx::always!(binders.is_empty(Interner), "non-empty binders for non-generic def",); return Some(ValuePathResolution::NonGeneric(ty)); @@ -122,7 +121,7 @@ impl InferenceContext<'_> { } let parent_substs = self_subst.or_else(|| { - let generics = generics(self.db.upcast(), generic_def_id?); + let generics = generics(self.db.upcast(), generic_def); let parent_params_len = generics.parent_generics()?.len(); let parent_args = &substs[substs.len() - parent_params_len..]; Some(Substitution::from_iter(Interner, parent_args)) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 3c18ea92816..55d81875a2b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -100,7 +100,9 @@ pub use mapping::{ }; pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; -pub use utils::{all_super_traits, direct_super_traits, is_fn_unsafe_to_call}; +pub use utils::{ + all_super_traits, direct_super_traits, is_fn_unsafe_to_call, TargetFeatures, Unsafety, +}; pub use variance::Variance; pub use chalk_ir::{ @@ -1047,3 +1049,20 @@ pub fn known_const_to_ast( } Some(make::expr_const_value(konst.display(db, edition).to_string().as_str())) } + +#[derive(Debug, Copy, Clone)] +pub(crate) enum DeclOrigin { + LetExpr, + /// from `let x = ..` + LocalDecl { + has_else: bool, + }, +} + +/// Provides context for checking patterns in declarations. More specifically this +/// allows us to infer array types if the pattern is irrefutable and allows us to infer +/// the size of the array. See issue rust-lang/rust#76342. +#[derive(Debug, Copy, Clone)] +pub(crate) struct DeclContext { + pub(crate) origin: DeclOrigin, +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 432b8f4d94e..db13e1fd354 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -23,10 +23,10 @@ use chalk_ir::{ use either::Either; use hir_def::{ - body::HygieneId, builtin_type::BuiltinType, data::{adt::StructKind, TraitFlags}, expander::Expander, + expr_store::HygieneId, generics::{ GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, @@ -2471,14 +2471,14 @@ pub enum ValueTyDefId { impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId); impl ValueTyDefId { - pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> Option<GenericDefId> { + pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> GenericDefId { match self { - Self::FunctionId(id) => Some(id.into()), - Self::StructId(id) => Some(id.into()), - Self::UnionId(id) => Some(id.into()), - Self::EnumVariantId(var) => Some(var.lookup(db.upcast()).parent.into()), - Self::ConstId(id) => Some(id.into()), - Self::StaticId(_) => None, + Self::FunctionId(id) => id.into(), + Self::StructId(id) => id.into(), + Self::UnionId(id) => id.into(), + Self::EnumVariantId(var) => var.lookup(db.upcast()).parent.into(), + Self::ConstId(id) => id.into(), + Self::StaticId(id) => id.into(), } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 1cea67ee964..db94351dcc9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -4,6 +4,7 @@ //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. use std::ops::ControlFlow; +use arrayvec::ArrayVec; use base_db::CrateId; use chalk_ir::{cast::Cast, UniverseIndex, WithKind}; use hir_def::{ @@ -732,15 +733,27 @@ fn lookup_impl_assoc_item_for_trait_ref( let self_ty = trait_ref.self_type_parameter(Interner); let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?; let impls = db.trait_impls_in_deps(env.krate); - let self_impls = match self_ty.kind(Interner) { - TyKind::Adt(id, _) => { - id.0.module(db.upcast()).containing_block().and_then(|it| db.trait_impls_in_block(it)) + + let trait_module = hir_trait_id.module(db.upcast()); + let type_module = match self_ty_fp { + TyFingerprint::Adt(adt_id) => Some(adt_id.module(db.upcast())), + TyFingerprint::ForeignType(type_id) => { + Some(from_foreign_def_id(type_id).module(db.upcast())) } + TyFingerprint::Dyn(trait_id) => Some(trait_id.module(db.upcast())), _ => None, }; + + let def_blocks: ArrayVec<_, 2> = + [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())] + .into_iter() + .flatten() + .filter_map(|block_id| db.trait_impls_in_block(block_id)) + .collect(); + let impls = impls .iter() - .chain(self_impls.as_ref()) + .chain(&def_blocks) .flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp)); let table = InferenceTable::new(db, env); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 59c583afb2a..84d8950b1aa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -16,7 +16,7 @@ use base_db::CrateId; use chalk_ir::Mutability; use either::Either; use hir_def::{ - body::Body, + expr_store::Body, hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId}, DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index dcae6877ba8..6b20522cf34 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -6,9 +6,9 @@ use base_db::CrateId; use chalk_ir::{cast::Cast, Mutability}; use either::Either; use hir_def::{ - body::HygieneId, builtin_type::BuiltinType, data::adt::{StructFlags, VariantData}, + expr_store::HygieneId, lang_item::LangItem, layout::{TagEncoding, Variants}, resolver::{HasResolver, TypeNs, ValueNs}, @@ -1644,14 +1644,15 @@ impl Evaluator<'_> { Variants::Multiple { tag, tag_encoding, variants, .. } => { let size = tag.size(&*self.target_data_layout).bytes_usize(); let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field + let is_signed = tag.is_signed(); match tag_encoding { TagEncoding::Direct => { let tag = &bytes[offset..offset + size]; - Ok(i128::from_le_bytes(pad16(tag, false))) + Ok(i128::from_le_bytes(pad16(tag, is_signed))) } TagEncoding::Niche { untagged_variant, niche_start, .. } => { let tag = &bytes[offset..offset + size]; - let candidate_tag = i128::from_le_bytes(pad16(tag, false)) + let candidate_tag = i128::from_le_bytes(pad16(tag, is_signed)) .wrapping_sub(*niche_start as i128) as usize; let idx = variants @@ -2943,10 +2944,10 @@ pub fn render_const_using_debug_impl( // a3 = ::core::fmt::Arguments::new_v1(a1, a2) // FIXME: similarly, we should call function here, not directly working with memory. let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size())?; - evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a1.to_bytes())?; + evaluator.write_memory(a3, &a1.to_bytes())?; + evaluator.write_memory(a3.offset(evaluator.ptr_size()), &[1])?; + evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a2.to_bytes())?; evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?; - evaluator.write_memory(a3.offset(4 * evaluator.ptr_size()), &a2.to_bytes())?; - evaluator.write_memory(a3.offset(5 * evaluator.ptr_size()), &[1])?; let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully( db.upcast(), &hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 0a78f4a5b24..38b189a517f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1,11 +1,12 @@ //! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation //! is not available. //! -use std::cmp; +use std::cmp::{self, Ordering}; use chalk_ir::TyKind; use hir_def::{ builtin_type::{BuiltinInt, BuiltinUint}, + lang_item::LangItemTarget, resolver::HasResolver, }; use hir_expand::name::Name; @@ -1317,6 +1318,82 @@ impl Evaluator<'_> { self.write_memory_using_ref(dst, size)?.fill(val); Ok(()) } + "ptr_metadata" => { + let [ptr] = args else { + return Err(MirEvalError::InternalError( + "ptr_metadata args are not provided".into(), + )); + }; + let arg = ptr.interval.get(self)?.to_owned(); + let metadata = &arg[self.ptr_size()..]; + destination.write_from_bytes(self, metadata)?; + Ok(()) + } + "three_way_compare" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::InternalError( + "three_way_compare args are not provided".into(), + )); + }; + let Some(ty) = + generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::InternalError( + "three_way_compare generic arg is not provided".into(), + )); + }; + let signed = match ty.as_builtin().unwrap() { + BuiltinType::Int(_) => true, + BuiltinType::Uint(_) => false, + _ => { + return Err(MirEvalError::InternalError( + "three_way_compare expects an integral type".into(), + )) + } + }; + let rhs = rhs.get(self)?; + let lhs = lhs.get(self)?; + let mut result = Ordering::Equal; + for (l, r) in lhs.iter().zip(rhs).rev() { + let it = l.cmp(r); + if it != Ordering::Equal { + result = it; + break; + } + } + if signed { + if let Some((&l, &r)) = lhs.iter().zip(rhs).next_back() { + if l != r { + result = (l as i8).cmp(&(r as i8)); + } + } + } + if let Some(LangItemTarget::EnumId(e)) = + self.db.lang_item(self.crate_id, LangItem::Ordering) + { + let ty = self.db.ty(e.into()); + let r = self + .compute_discriminant(ty.skip_binders().clone(), &[result as i8 as u8])?; + destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])?; + Ok(()) + } else { + Err(MirEvalError::InternalError("Ordering enum not found".into())) + } + } + "aggregate_raw_ptr" => { + let [data, meta] = args else { + return Err(MirEvalError::InternalError( + "aggregate_raw_ptr args are not provided".into(), + )); + }; + destination.write_from_interval(self, data.interval)?; + Interval { + addr: destination.addr.offset(data.interval.size), + size: destination.size - data.interval.size, + } + .write_from_interval(self, meta.interval)?; + Ok(()) + } _ if needs_override => not_supported!("intrinsic {name} is not implemented"), _ => return Ok(false), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 1d1044df6e9..549450e9beb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -5,8 +5,8 @@ use std::{fmt::Write, iter, mem}; use base_db::ra_salsa::Cycle; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ - body::{Body, HygieneId}, data::adt::{StructKind, VariantData}, + expr_store::{Body, HygieneId}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, @@ -2156,7 +2156,7 @@ pub fn lower_to_mir( // need to take this input explicitly. root_expr: ExprId, ) -> Result<MirBody> { - if infer.has_errors { + if infer.type_mismatches().next().is_some() { return Err(MirLowerError::HasErrors); } let mut ctx = MirLowerCtx::new(db, owner, body, infer); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 06765a104cb..2a26101ac43 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -6,7 +6,7 @@ use std::{ }; use either::Either; -use hir_def::{body::Body, hir::BindingId}; +use hir_def::{expr_store::Body, hir::BindingId}; use hir_expand::{name::Name, Lookup}; use la_arena::ArenaMap; use span::Edition; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 00da9b25176..69ec35f406d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -18,8 +18,8 @@ use std::sync::LazyLock; use base_db::SourceDatabaseFileInputExt as _; use expect_test::Expect; use hir_def::{ - body::{Body, BodySourceMap}, db::DefDatabase, + expr_store::{Body, BodySourceMap}, hir::{ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 8866de22dfb..e5f791ea6ff 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -2163,9 +2163,9 @@ impl Receiver for Bar { fn main() { let bar = Bar; let _v1 = bar.foo1(); - //^^^ type: i32 + //^^^ type: {unknown} let _v2 = bar.foo2(); - //^^^ type: bool + //^^^ type: {unknown} } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 15636604570..50a1ecd006d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3814,3 +3814,50 @@ async fn foo(a: (), b: i32) -> u32 { "#, ); } + +#[test] +fn irrefutable_slices() { + check_infer( + r#" +//- minicore: from +struct A; + +impl From<A> for [u8; 2] { + fn from(a: A) -> Self { + [0; 2] + } +} +impl From<A> for [u8; 3] { + fn from(a: A) -> Self { + [0; 3] + } +} + + +fn main() { + let a = A; + let [b, c] = a.into(); +} +"#, + expect![[r#" + 50..51 'a': A + 64..86 '{ ... }': [u8; 2] + 74..80 '[0; 2]': [u8; 2] + 75..76 '0': u8 + 78..79 '2': usize + 128..129 'a': A + 142..164 '{ ... }': [u8; 3] + 152..158 '[0; 3]': [u8; 3] + 153..154 '0': u8 + 156..157 '3': usize + 179..224 '{ ...o(); }': () + 189..190 'a': A + 193..194 'A': A + 204..210 '[b, c]': [u8; 2] + 205..206 'b': u8 + 208..209 'c': u8 + 213..214 'a': A + 213..221 'a.into()': [u8; 2] + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index bf7892f69bd..c131e97bc4c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -9,19 +9,22 @@ use chalk_ir::{ DebruijnIndex, }; use hir_def::{ + attr::Attrs, db::DefDatabase, generics::{WherePredicate, WherePredicateTypeTarget}, lang_item::LangItem, resolver::{HasResolver, TypeNs}, + tt, type_ref::{TraitBoundModifier, TypeRef}, EnumId, EnumVariantId, FunctionId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, }; use hir_expand::name::Name; -use intern::sym; +use intern::{sym, Symbol}; use rustc_abi::TargetDataLayout; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; +use span::Edition; use stdx::never; use crate::{ @@ -264,10 +267,65 @@ impl<'a> ClosureSubst<'a> { } } -pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { +#[derive(Debug, Default)] +pub struct TargetFeatures { + enabled: FxHashSet<Symbol>, +} + +impl TargetFeatures { + pub fn from_attrs(attrs: &Attrs) -> Self { + let enabled = attrs + .by_key(&sym::target_feature) + .tt_values() + .filter_map(|tt| { + match tt.token_trees().flat_tokens() { + [ + tt::TokenTree::Leaf(tt::Leaf::Ident(enable_ident)), + tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. })), + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { kind: tt::LitKind::Str, symbol: features, .. })), + ] if enable_ident.sym == sym::enable => Some(features), + _ => None, + } + }) + .flat_map(|features| features.as_str().split(',').map(Symbol::intern)) + .collect(); + Self { enabled } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Unsafety { + Safe, + Unsafe, + /// A lint. + DeprecatedSafe2024, +} + +pub fn is_fn_unsafe_to_call( + db: &dyn HirDatabase, + func: FunctionId, + caller_target_features: &TargetFeatures, + call_edition: Edition, +) -> Unsafety { let data = db.function_data(func); if data.is_unsafe() { - return true; + return Unsafety::Unsafe; + } + + if data.has_target_feature() { + // RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>. + let callee_target_features = TargetFeatures::from_attrs(&db.attrs(func.into())); + if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) { + return Unsafety::Unsafe; + } + } + + if data.is_deprecated_safe_2024() { + if call_edition.at_least_2024() { + return Unsafety::Unsafe; + } else { + return Unsafety::DeprecatedSafe2024; + } } let loc = func.lookup(db.upcast()); @@ -279,14 +337,22 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { if is_intrinsic_block { // legacy intrinsics // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute - !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() + if db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() { + Unsafety::Safe + } else { + Unsafety::Unsafe + } } else { // Function in an `extern` block are always unsafe to call, except when // it is marked as `safe`. - !data.is_safe() + if data.is_safe() { + Unsafety::Safe + } else { + Unsafety::Unsafe + } } } - _ => false, + _ => Unsafety::Safe, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index afd163fbd96..3a22158ce6f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -1028,6 +1028,7 @@ struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V); } GenericDefId::ImplId(_) => return None, GenericDefId::ConstId(_) => return None, + GenericDefId::StaticId(_) => return None, }, )) }) diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index fc77d1889c8..64e982c42d7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -271,11 +271,17 @@ pub struct PrivateField { pub field: Field, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UnsafeLint { + HardError, + UnsafeOpInUnsafeFn, + DeprecatedSafe2024, +} + #[derive(Debug)] pub struct MissingUnsafe { pub node: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>, - /// If true, the diagnostics is an `unsafe_op_in_unsafe_fn` lint instead of a hard error. - pub only_lint: bool, + pub lint: UnsafeLint, pub reason: UnsafetyReason, } @@ -411,7 +417,7 @@ impl AnyDiagnostic { pub(crate) fn body_validation_diagnostic( db: &dyn HirDatabase, diagnostic: BodyValidationDiagnostic, - source_map: &hir_def::body::BodySourceMap, + source_map: &hir_def::expr_store::BodySourceMap, ) -> Option<AnyDiagnostic> { match diagnostic { BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => { @@ -547,7 +553,7 @@ impl AnyDiagnostic { def: DefWithBodyId, d: &InferenceDiagnostic, outer_types_source_map: &TypesSourceMap, - source_map: &hir_def::body::BodySourceMap, + source_map: &hir_def::expr_store::BodySourceMap, ) -> Option<AnyDiagnostic> { let expr_syntax = |expr| { source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok() diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index b29c91694d3..6f4168ab086 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -80,7 +80,9 @@ impl HirDisplay for Function { if data.is_async() { f.write_str("async ")?; } - if self.is_unsafe_to_call(db) { + // FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe + // (they are conditionally unsafe to call). We probably should show something else. + if self.is_unsafe_to_call(db, None, f.edition()) { f.write_str("unsafe ")?; } if let Some(abi) = &data.abi { diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs index 2ad39817b2f..537401afdc3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -183,6 +183,7 @@ impl From<GenericDef> for GenericDefId { GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id), GenericDef::Impl(it) => GenericDefId::ImplId(it.id), GenericDef::Const(it) => GenericDefId::ConstId(it.id), + GenericDef::Static(it) => GenericDefId::StaticId(it.id), } } } @@ -197,6 +198,7 @@ impl From<GenericDefId> for GenericDef { GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()), GenericDefId::ImplId(it) => GenericDef::Impl(it.into()), GenericDefId::ConstId(it) => GenericDef::Const(it.into()), + GenericDefId::StaticId(it) => GenericDef::Static(it.into()), } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 0cbc75726bf..56090bc6b60 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -42,8 +42,8 @@ use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin}; use either::Either; use hir_def::{ - body::BodyDiagnostic, data::{adt::VariantData, TraitFlags}, + expr_store::ExpressionStoreDiagnostics, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, @@ -1892,10 +1892,10 @@ impl DefWithBody { for diag in source_map.diagnostics() { acc.push(match diag { - BodyDiagnostic::InactiveCode { node, cfg, opts } => { + ExpressionStoreDiagnostics::InactiveCode { node, cfg, opts } => { InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into() } - BodyDiagnostic::MacroError { node, err } => { + ExpressionStoreDiagnostics::MacroError { node, err } => { let RenderedExpandError { message, error, kind } = err.render_to_string(db.upcast()); @@ -1919,20 +1919,22 @@ impl DefWithBody { } .into() } - BodyDiagnostic::UnresolvedMacroCall { node, path } => UnresolvedMacroCall { - macro_call: (*node).map(|ast_ptr| ast_ptr.into()), - precise_location: None, - path: path.clone(), - is_bang: true, + ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => { + UnresolvedMacroCall { + macro_call: (*node).map(|ast_ptr| ast_ptr.into()), + precise_location: None, + path: path.clone(), + is_bang: true, + } + .into() } - .into(), - BodyDiagnostic::AwaitOutsideOfAsync { node, location } => { + ExpressionStoreDiagnostics::AwaitOutsideOfAsync { node, location } => { AwaitOutsideOfAsync { node: *node, location: location.clone() }.into() } - BodyDiagnostic::UnreachableLabel { node, name } => { + ExpressionStoreDiagnostics::UnreachableLabel { node, name } => { UnreachableLabel { node: *node, name: name.clone() }.into() } - BodyDiagnostic::UndeclaredLabel { node, name } => { + ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => { UndeclaredLabel { node: *node, name: name.clone() }.into() } }); @@ -1976,16 +1978,40 @@ impl DefWithBody { ); } - let (unsafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into()); - for (node, reason) in unsafe_exprs { + let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, self.into()); + for (node, reason) in missing_unsafe.unsafe_exprs { match source_map.expr_or_pat_syntax(node) { - Ok(node) => acc.push(MissingUnsafe { node, only_lint, reason }.into()), + Ok(node) => acc.push( + MissingUnsafe { + node, + lint: if missing_unsafe.fn_is_unsafe { + UnsafeLint::UnsafeOpInUnsafeFn + } else { + UnsafeLint::HardError + }, + reason, + } + .into(), + ), Err(SyntheticSyntax) => { // FIXME: Here and elsewhere in this file, the `expr` was // desugared, report or assert that this doesn't happen. } } } + for node in missing_unsafe.deprecated_safe_calls { + match source_map.expr_syntax(node) { + Ok(node) => acc.push( + MissingUnsafe { + node: node.map(|it| it.wrap_left()), + lint: UnsafeLint::DeprecatedSafe2024, + reason: UnsafetyReason::UnsafeFnCall, + } + .into(), + ), + Err(SyntheticSyntax) => never!("synthetic DeprecatedSafe2024"), + } + } if let Ok(borrowck_results) = db.borrowck(self.into()) { for borrowck_result in borrowck_results.iter() { @@ -2361,8 +2387,19 @@ impl Function { db.attrs(self.id.into()).is_unstable() } - pub fn is_unsafe_to_call(self, db: &dyn HirDatabase) -> bool { - hir_ty::is_fn_unsafe_to_call(db, self.id) + pub fn is_unsafe_to_call( + self, + db: &dyn HirDatabase, + caller: Option<Function>, + call_edition: Edition, + ) -> bool { + let target_features = caller + .map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into()))) + .unwrap_or_default(); + matches!( + hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features, call_edition), + hir_ty::Unsafety::Unsafe + ) } /// Whether this function declaration has a definition. @@ -3453,6 +3490,7 @@ pub enum GenericDef { Impl(Impl), // consts can have type parameters from their parents (i.e. associated consts of traits) Const(Const), + Static(Static), } impl_from!( Function, @@ -3461,7 +3499,8 @@ impl_from!( TraitAlias, TypeAlias, Impl, - Const + Const, + Static for GenericDef ); @@ -3511,6 +3550,7 @@ impl GenericDef { GenericDef::TypeAlias(it) => it.id.into(), GenericDef::Impl(it) => it.id.into(), GenericDef::Const(it) => it.id.into(), + GenericDef::Static(it) => it.id.into(), } } @@ -3568,6 +3608,7 @@ impl GenericDef { item_tree_source_maps.impl_(id.value).generics() } GenericDefId::ConstId(_) => return, + GenericDefId::StaticId(_) => return, }, }; @@ -4624,17 +4665,6 @@ impl Type { Type { env: TraitEnvironment::empty(krate), ty } } - pub fn reference(inner: &Type, m: Mutability) -> Type { - inner.derived( - TyKind::Ref( - if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not }, - hir_ty::error_lifetime(), - inner.ty.clone(), - ) - .intern(Interner), - ) - } - fn new(db: &dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Type { let resolver = lexical_env.resolver(db.upcast()); let environment = resolver @@ -4866,6 +4896,17 @@ impl Type { self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } + pub fn impls_iterator(self, db: &dyn HirDatabase) -> bool { + let Some(iterator_trait) = + db.lang_item(self.env.krate, LangItem::Iterator).and_then(|it| it.as_trait()) + else { + return false; + }; + let canonical_ty = + Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; + method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, iterator_trait) + } + /// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> { let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 09470bed9cf..882a27182f0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -2040,6 +2040,13 @@ impl SemanticsScope<'_> { Crate { id: self.resolver.krate() } } + pub fn containing_function(&self) -> Option<Function> { + self.resolver.body_owner().and_then(|owner| match owner { + DefWithBodyId::FunctionId(id) => Some(id.into()), + _ => None, + }) + } + pub(crate) fn resolver(&self) -> &Resolver { &self.resolver } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index b699ccde412..23e7518883b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -14,7 +14,7 @@ use crate::{ }; use either::Either; use hir_def::{ - body::{ + expr_store::{ scope::{ExprScopes, ScopeId}, Body, BodySourceMap, HygieneId, }, @@ -1105,16 +1105,9 @@ impl SourceAnalyzer { if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) { let mut is_unsafe = false; let mut walk_expr = |expr_id| { - unsafe_expressions( - db, - infer, - *def, - body, - expr_id, - &mut |_, inside_unsafe_block, _| { - is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No - }, - ) + unsafe_expressions(db, infer, *def, body, expr_id, &mut |inside_unsafe_block| { + is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No + }) }; match expanded_expr { ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr), @@ -1259,7 +1252,11 @@ fn scope_for( node: InFile<&SyntaxNode>, ) -> Option<ScopeId> { node.ancestors_with_macros(db.upcast()) - .take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind())) + .take_while(|it| { + !ast::Item::can_cast(it.kind()) + || ast::MacroCall::can_cast(it.kind()) + || ast::Use::can_cast(it.kind()) + }) .filter_map(|it| it.map(ast::Expr::cast).transpose()) .filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr()) .find_map(|it| scopes.scope_for(it)) diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index 6f845137084..af72179305c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -145,7 +145,7 @@ impl LookupTable { self.data .iter() .find(|(t, _)| { - Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, ty) + t.add_reference(Mutability::Shared).could_unify_with_deeply(db, ty) }) .map(|(t, it)| { it.exprs(t) diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 1b0e6f8bd5b..847304d503a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -15,6 +15,7 @@ use hir_ty::mir::BorrowKind; use hir_ty::TyBuilder; use itertools::Itertools; use rustc_hash::FxHashSet; +use span::Edition; use crate::{ Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef, @@ -365,7 +366,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); // Filter out private and unsafe functions if !it.is_visible_from(db, module) - || it.is_unsafe_to_call(db) + || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME) || it.is_unstable(db) || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() @@ -470,7 +471,10 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( } // Filter out private and unsafe functions - if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) { + if !it.is_visible_from(db, module) + || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME) + || it.is_unstable(db) + { return None; } @@ -658,7 +662,10 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( } // Filter out private and unsafe functions - if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) { + if !it.is_visible_from(db, module) + || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME) + || it.is_unstable(db) + { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 70fb5680052..491727a30a8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -15,7 +15,7 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin // Assist: apply_demorgan // -// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law]. +// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). // This transforms expressions of the form `!l || !r` into `!(l && r)`. // This also works with `&&`. This assist can only be applied with the cursor // on either `||` or `&&`. @@ -131,7 +131,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti // Assist: apply_demorgan_iterator // -// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law] to +// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to // `Iterator::all` and `Iterator::any`. // // This transforms expressions of the form `!iter.any(|x| predicate(x))` into diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index d86948818b1..a92a000c3fb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // use super::AssistContext; // ``` // -// .Import Granularity +// #### Import Granularity // // It is possible to configure how use-trees are merged with the `imports.granularity.group` setting. // It has the following configurations: @@ -54,7 +54,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // // In `VS Code` the configuration for this is `rust-analyzer.imports.granularity.group`. // -// .Import Prefix +// #### Import Prefix // // The style of imports in the same crate is configurable through the `imports.prefix` setting. // It has the following configurations: @@ -68,7 +68,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // // In `VS Code` the configuration for this is `rust-analyzer.imports.prefix`. // -// image::https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif[] +//  // Assist: auto_import // diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 3c84f83906a..f6e516db888 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -140,8 +140,10 @@ fn edit_struct_references( match_ast! { match node { ast::TupleStructPat(tuple_struct_pat) => { + let file_range = ctx.sema.original_range_opt(&node)?; + edit.edit_file(file_range.file_id); edit.replace( - tuple_struct_pat.syntax().text_range(), + file_range.range, ast::make::record_pat_with_fields( tuple_struct_pat.path()?, ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map( @@ -924,4 +926,102 @@ pub struct Foo { #[my_custom_attr] field1: u32 } "#, ); } + + #[test] + fn convert_in_macro_pattern_args() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} +enum Expr { + A$0(usize), +} +fn main() { + let e = Expr::A(0); + foo!(e, Expr::A(0)); +} +"#, + r#" +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} +enum Expr { + A { field1: usize }, +} +fn main() { + let e = Expr::A { field1: 0 }; + foo!(e, Expr::A { field1: 0 }); +} +"#, + ); + } + + #[test] + fn convert_in_multi_file_macro_pattern_args() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +//- /main.rs +mod foo; + +enum Test { + A$0(i32) +} + +//- /foo.rs +use crate::Test; + +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} + +fn foo() { + let a = Test::A(0); + foo!(a, Test::A(0)); +} +"#, + r#" +//- /main.rs +mod foo; + +enum Test { + A { field1: i32 } +} + +//- /foo.rs +use crate::Test; + +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} + +fn foo() { + let a = Test::A { field1: 0 }; + foo!(a, Test::A { field1: 0 }); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 54e42f126bc..4b0fa704a22 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen assists-doc-tests`, do not edit by hand. +//! Generated by `cargo xtask codegen assists-doc-tests`, do not edit by hand. use super::check_doc_test; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 78ff4417913..c1332d99bff 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -793,8 +793,8 @@ pub(crate) fn convert_reference_type( } fn could_deref_to_target(ty: &hir::Type, target: &hir::Type, db: &dyn HirDatabase) -> bool { - let ty_ref = hir::Type::reference(ty, hir::Mutability::Shared); - let target_ref = hir::Type::reference(target, hir::Mutability::Shared); + let ty_ref = ty.add_reference(hir::Mutability::Shared); + let target_ref = target.add_reference(hir::Mutability::Shared); ty_ref.could_coerce_to(db, &target_ref) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index d12654665ce..b38b9ac1f53 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -89,7 +89,7 @@ pub(crate) fn complete_dot( acc.add_method(ctx, dot_access, func, None, None) }); - if ctx.config.enable_auto_iter { + if ctx.config.enable_auto_iter && !receiver_ty.strip_references().impls_iterator(ctx.db) { // FIXME: // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. @@ -1500,9 +1500,31 @@ fn main() { bar.$0 } "#, + expect![[r#""#]], + ); + } + + #[test] + fn no_iter_suggestion_on_iterator() { + check_no_kw( + r#" +//- minicore: iterator +struct MyIter; +impl Iterator for MyIter { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { None } +} + +fn main() { + MyIter.$0 +} +"#, expect![[r#" - me foo() fn(self: Bar) -"#]], + me by_ref() (as Iterator) fn(&mut self) -> &mut Self + me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter + me next() (as Iterator) fn(&mut self) -> Option<<Self as Iterator>::Item> + me nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item> + "#]], ); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 24243f57b46..b5555e66102 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -83,19 +83,19 @@ use crate::{ // NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path, // no imports will be proposed. // -// .Fuzzy search details +// #### Fuzzy search details // // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only // (i.e. in `HashMap` in the `std::collections::HashMap` path). // For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols // (but shows all associated items for any input length). // -// .Import configuration +// #### Import configuration // // It is possible to configure how use-trees are merged with the `imports.granularity.group` setting. // Mimics the corresponding behavior of the `Auto Import` feature. // -// .LSP and performance implications +// #### LSP and performance implications // // The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits` // (case-sensitive) resolve client capability in its client capabilities. @@ -103,7 +103,7 @@ use crate::{ // For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, // which might be slow ergo the feature is automatically disabled. // -// .Feature toggle +// #### Feature toggle // // The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag. // Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs index 2755329bb31..c612170eb54 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs @@ -14,7 +14,7 @@ // ** `logw` -> `log::warn!(...)` // ** `loge` -> `log::error!(...)` // -// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] +//  use ide_db::{ syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders, Arg}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 2f1860cbb59..7862b258789 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -442,6 +442,8 @@ pub(crate) struct CompletionContext<'a> { pub(crate) krate: hir::Crate, /// The module of the `scope`. pub(crate) module: hir::Module, + /// The function where we're completing, if inside a function. + pub(crate) containing_function: Option<hir::Function>, /// Whether nightly toolchain is used. Cached since this is looked up a lot. pub(crate) is_nightly: bool, /// The edition of the current crate @@ -760,6 +762,7 @@ impl<'a> CompletionContext<'a> { let krate = scope.krate(); let module = scope.module(); + let containing_function = scope.containing_function(); let edition = krate.edition(db); let toolchain = db.toolchain_channel(krate.into()); @@ -874,6 +877,7 @@ impl<'a> CompletionContext<'a> { token, krate, module, + containing_function, is_nightly, edition, expected_name, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index f5a50ae8190..eecd412bc43 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -59,7 +59,7 @@ pub(super) fn expand_and_analyze( // make the offset point to the start of the original token, as that is what the // intermediate offsets calculated in expansion always points to let offset = offset - relative_offset; - let expansion = expand( + let expansion = expand_maybe_stop( sema, original_file.clone(), speculative_file.clone(), @@ -118,7 +118,7 @@ fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Opt /// that we check, we subtract `COMPLETION_MARKER.len()`. This may not be accurate because proc macros /// can insert the text of the completion marker in other places while removing the span, but this is /// the best we can do. -fn expand( +fn expand_maybe_stop( sema: &Semantics<'_, RootDatabase>, original_file: SyntaxNode, speculative_file: SyntaxNode, @@ -126,23 +126,48 @@ fn expand( fake_ident_token: SyntaxToken, relative_offset: TextSize, ) -> Option<ExpansionResult> { - let _p = tracing::info_span!("CompletionContext::expand").entered(); + if let result @ Some(_) = expand( + sema, + original_file.clone(), + speculative_file.clone(), + original_offset, + fake_ident_token.clone(), + relative_offset, + ) { + return result; + } + // This needs to come after the recursive call, because our "inside macro" detection is subtly wrong + // with regard to attribute macros named `test` that are not std's test. So hopefully we will expand + // them successfully above and be able to analyze. // Left biased since there may already be an identifier token there, and we appended to it. if !sema.might_be_inside_macro_call(&fake_ident_token) && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset) .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token)) { // Recursion base case. - return Some(ExpansionResult { + Some(ExpansionResult { original_file, speculative_file, original_offset, speculative_offset: fake_ident_token.text_range().start(), fake_ident_token, derive_ctx: None, - }); + }) + } else { + None } +} + +fn expand( + sema: &Semantics<'_, RootDatabase>, + original_file: SyntaxNode, + speculative_file: SyntaxNode, + original_offset: TextSize, + fake_ident_token: SyntaxToken, + relative_offset: TextSize, +) -> Option<ExpansionResult> { + let _p = tracing::info_span!("CompletionContext::expand").entered(); let parent_item = |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); @@ -197,7 +222,7 @@ fn expand( // stop here to prevent problems from happening return None; } - let result = expand( + let result = expand_maybe_stop( sema, actual_expansion.clone(), fake_expansion.clone(), @@ -317,7 +342,7 @@ fn expand( // stop here to prevent problems from happening return None; } - let result = expand( + let result = expand_maybe_stop( sema, actual_expansion.clone(), fake_expansion.clone(), @@ -386,7 +411,7 @@ fn expand( // stop here to prevent problems from happening return None; } - let result = expand( + let result = expand_maybe_stop( sema, actual_expansion.clone(), fake_expansion.clone(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 8051d48ca5f..a1f2eaeb1b6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -106,11 +106,13 @@ impl CompletionFieldsToResolve { // // There also snippet completions: // -// .Expressions +// #### Expressions +// // - `pd` -> `eprintln!(" = {:?}", );` // - `ppd` -> `eprintln!(" = {:#?}", );` // -// .Items +// #### Items +// // - `tfn` -> `#[test] fn feature(){}` // - `tmod` -> // ```rust @@ -127,7 +129,7 @@ impl CompletionFieldsToResolve { // Those are the additional completion options with automatic `use` import and options from all project importable items, // fuzzy matched against the completion input. // -// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[] +//  /// Main entry point for completion. We run completion as a two-phase process. /// diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index c3354902c3b..fd90613964a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -144,7 +144,7 @@ fn render( let detail = if ctx.completion.config.full_function_signatures { detail_full(db, func, ctx.completion.edition) } else { - detail(db, func, ctx.completion.edition) + detail(ctx.completion, func, ctx.completion.edition) }; item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) @@ -307,26 +307,26 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'sta "" } -fn detail(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String { - let mut ret_ty = func.ret_type(db); +fn detail(ctx: &CompletionContext<'_>, func: hir::Function, edition: Edition) -> String { + let mut ret_ty = func.ret_type(ctx.db); let mut detail = String::new(); - if func.is_const(db) { + if func.is_const(ctx.db) { format_to!(detail, "const "); } - if func.is_async(db) { + if func.is_async(ctx.db) { format_to!(detail, "async "); - if let Some(async_ret) = func.async_ret_type(db) { + if let Some(async_ret) = func.async_ret_type(ctx.db) { ret_ty = async_ret; } } - if func.is_unsafe_to_call(db) { + if func.is_unsafe_to_call(ctx.db, ctx.containing_function, ctx.edition) { format_to!(detail, "unsafe "); } - format_to!(detail, "fn({})", params_display(db, func, edition)); + format_to!(detail, "fn({})", params_display(ctx.db, func, edition)); if !ret_ty.is_unit() { - format_to!(detail, " -> {}", ret_ty.display(db, edition)); + format_to!(detail, " -> {}", ret_ty.display(ctx.db, edition)); } detail } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 866b83a6146..07f33a826e4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -8,8 +8,7 @@ // // A custom snippet can be defined by adding it to the `rust-analyzer.completion.snippets.custom` object respectively. // -// [source,json] -// ---- +// ```json // { // "rust-analyzer.completion.snippets.custom": { // "thread spawn": { @@ -25,7 +24,7 @@ // } // } // } -// ---- +// ``` // // In the example above: // @@ -39,6 +38,7 @@ // * `description` is an optional description of the snippet, if unset the snippet name will be used. // // * `requires` is an optional list of item paths that have to be resolvable in the current crate where the completion is rendered. + // On failure of resolution the snippet won't be applicable, otherwise the snippet will insert an import for the items on insertion if // the items aren't yet in scope. // @@ -55,8 +55,8 @@ // // For the VSCode editor, rust-analyzer also ships with a small set of defaults which can be removed // by overwriting the settings object mentioned above, the defaults are: -// [source,json] -// ---- +// +// ```json // { // "Arc::new": { // "postfix": "arc", @@ -98,7 +98,7 @@ // "scope": "expr" // } // } -// ---- +// ```` use hir::{ModPath, Name, Symbol}; use ide_db::imports::import_assets::LocatedImport; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 663a038580d..37557512837 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1986,3 +1986,53 @@ fn foo() { "#]], ); } + +#[test] +fn non_std_test_attr_macro() { + check( + r#" +//- proc_macros: identity +use proc_macros::identity as test; + +#[test] +fn foo() { + $0 +} + "#, + expect![[r#" + fn foo() fn() + md proc_macros + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index 35e3a8d9bf7..46ff4fbf9e9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -44,12 +44,11 @@ impl RootDatabase { // // Clears rust-analyzer's internal database and prints memory usage statistics. // - // |=== - // | Editor | Action Name - // + // | Editor | Action Name | + // |---------|-------------| // | VS Code | **rust-analyzer: Memory Usage (Clears Database)** - // |=== - // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[] + + //  pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> { let mut acc: Vec<(String, Bytes, usize)> = vec![]; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index d12bda0816f..bad53608056 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -986,6 +986,7 @@ impl From<GenericDef> for Definition { GenericDef::TypeAlias(it) => it.into(), GenericDef::Impl(it) => it.into(), GenericDef::Const(it) => it.into(), + GenericDef::Static(it) => it.into(), } } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 0002fda0ba7..22dc3d9e29d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -6,12 +6,13 @@ mod topologic_sort; use std::time::Duration; -use hir::db::DefDatabase; +use hir::{db::DefDatabase, Symbol}; +use itertools::Itertools; use crate::{ base_db::{ ra_salsa::{Database, ParallelDatabase, Snapshot}, - Cancelled, CrateId, SourceDatabase, SourceRootDatabase, + Cancelled, CrateId, SourceDatabase, }, symbol_index::SymbolsDatabase, FxIndexMap, RootDatabase, @@ -21,11 +22,12 @@ use crate::{ #[derive(Debug)] pub struct ParallelPrimeCachesProgress { /// the crates that we are currently priming. - pub crates_currently_indexing: Vec<String>, + pub crates_currently_indexing: Vec<Symbol>, /// the total number of crates we want to prime. pub crates_total: usize, /// the total number of crates that have finished priming pub crates_done: usize, + pub work_type: &'static str, } pub fn parallel_prime_caches( @@ -47,41 +49,32 @@ pub fn parallel_prime_caches( }; enum ParallelPrimeCacheWorkerProgress { - BeginCrate { crate_id: CrateId, crate_name: String }, + BeginCrate { crate_id: CrateId, crate_name: Symbol }, EndCrate { crate_id: CrateId }, } + // We split off def map computation from other work, + // as the def map is the relevant one. Once the defmaps are computed + // the project is ready to go, the other indices are just nice to have for some IDE features. + #[derive(PartialOrd, Ord, PartialEq, Eq, Copy, Clone)] + enum PrimingPhase { + DefMap, + ImportMap, + CrateSymbols, + } + let (work_sender, progress_receiver) = { let (progress_sender, progress_receiver) = crossbeam_channel::unbounded(); let (work_sender, work_receiver) = crossbeam_channel::unbounded(); - let graph = graph.clone(); - let local_roots = db.local_roots(); let prime_caches_worker = move |db: Snapshot<RootDatabase>| { - while let Ok((crate_id, crate_name)) = work_receiver.recv() { + while let Ok((crate_id, crate_name, kind)) = work_receiver.recv() { progress_sender .send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?; - // Compute the DefMap and possibly ImportMap - let file_id = graph[crate_id].root_file_id; - let root_id = db.file_source_root(file_id); - if db.source_root(root_id).is_library { - db.crate_def_map(crate_id); - } else { - // This also computes the DefMap - db.import_map(crate_id); - } - - // Compute the symbol search index. - // This primes the cache for `ide_db::symbol_index::world_symbols()`. - // - // We do this for workspace crates only (members of local_roots), because doing it - // for all dependencies could be *very* unnecessarily slow in a large project. - // - // FIXME: We should do it unconditionally if the configuration is set to default to - // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we - // would need to pipe that configuration information down here. - if local_roots.contains(&root_id) { - db.crate_symbols(crate_id.into()); + match kind { + PrimingPhase::DefMap => _ = db.crate_def_map(crate_id), + PrimingPhase::ImportMap => _ = db.import_map(crate_id), + PrimingPhase::CrateSymbols => _ = db.crate_symbols(crate_id.into()), } progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?; @@ -112,16 +105,34 @@ pub fn parallel_prime_caches( let mut crates_currently_indexing = FxIndexMap::with_capacity_and_hasher(num_worker_threads, Default::default()); + let mut additional_phases = vec![]; + while crates_done < crates_total { db.unwind_if_cancelled(); for crate_id in &mut crates_to_prime { - work_sender - .send(( - crate_id, - graph[crate_id].display_name.as_deref().unwrap_or_default().to_owned(), - )) - .ok(); + let krate = &graph[crate_id]; + let name = krate + .display_name + .as_deref() + .cloned() + .unwrap_or_else(|| Symbol::integer(crate_id.into_raw().into_u32() as usize)); + if krate.origin.is_lang() { + additional_phases.push((crate_id, name.clone(), PrimingPhase::ImportMap)); + } else if krate.origin.is_local() { + // Compute the symbol search index. + // This primes the cache for `ide_db::symbol_index::world_symbols()`. + // + // We do this for workspace crates only (members of local_roots), because doing it + // for all dependencies could be *very* unnecessarily slow in a large project. + // + // FIXME: We should do it unconditionally if the configuration is set to default to + // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we + // would need to pipe that configuration information down here. + additional_phases.push((crate_id, name.clone(), PrimingPhase::CrateSymbols)); + } + + work_sender.send((crate_id, name, PrimingPhase::DefMap)).ok(); } // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision @@ -153,6 +164,50 @@ pub fn parallel_prime_caches( crates_currently_indexing: crates_currently_indexing.values().cloned().collect(), crates_done, crates_total, + work_type: "Indexing", + }; + + cb(progress); + } + + let mut crates_done = 0; + let crates_total = additional_phases.len(); + for w in additional_phases.into_iter().sorted_by_key(|&(_, _, phase)| phase) { + work_sender.send(w).ok(); + } + + while crates_done < crates_total { + db.unwind_if_cancelled(); + + // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision + // is cancelled on a regular basis. workers will only exit if they are processing a task that is cancelled, or + // if this thread exits, and closes the work channel. + let worker_progress = match progress_receiver.recv_timeout(Duration::from_millis(10)) { + Ok(p) => p, + Err(crossbeam_channel::RecvTimeoutError::Timeout) => { + continue; + } + Err(crossbeam_channel::RecvTimeoutError::Disconnected) => { + // our workers may have died from a cancelled task, so we'll check and re-raise here. + db.unwind_if_cancelled(); + break; + } + }; + match worker_progress { + ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name } => { + crates_currently_indexing.insert(crate_id, crate_name); + } + ParallelPrimeCacheWorkerProgress::EndCrate { crate_id } => { + crates_currently_indexing.swap_remove(&crate_id); + crates_done += 1; + } + }; + + let progress = ParallelPrimeCachesProgress { + crates_currently_indexing: crates_currently_indexing.values().cloned().collect(), + crates_done, + crates_total, + work_type: "Populating symbols", }; cb(progress); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 7fc563a4241..7963e8ae4f7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -354,6 +354,7 @@ impl Definition { hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()), + hir::GenericDef::Static(it) => it.source(db).map(|src| src.syntax().cloned()), }; return match def { Some(def) => SearchScope::file_range( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index e5ce10a771e..bb4c289c908 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -193,11 +193,9 @@ impl<DB> std::ops::Deref for Snap<DB> { // `rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed // with `__` are hidden from the search results unless configured otherwise. // -// |=== -// | Editor | Shortcut -// -// | VS Code | kbd:[Ctrl+T] -// |=== +// | Editor | Shortcut | +// |---------|-----------| +// | VS Code | <kbd>Ctrl+T</kbd> pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { let _p = tracing::info_span!("world_symbols", query = ?query.query).entered(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index bbdeb7cf085..246330e6efa 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -12,7 +12,7 @@ use crate::{ // Diagnostic: incorrect-ident-case // -// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. +// This diagnostic is triggered if an item name doesn't follow [Rust naming convention](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html). pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { let code = match d.expected_case { CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"), @@ -936,6 +936,7 @@ fn func() { fn override_lint_level() { check_diagnostics( r#" +#![allow(unused_variables)] #[warn(nonstandard_style)] fn foo() { let BAR; @@ -992,6 +993,7 @@ struct QUX; const foo: i32 = 0; fn BAR() { let BAZ; + _ = BAZ; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index c7cdcf49820..5730508436d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -1129,4 +1129,39 @@ fn main() { "#, ); } + + #[test] + fn regression_18682() { + check_diagnostics( + r#" +//- minicore: coerce_unsized +struct Flexible { + body: [u8], +} + +trait Field { + type Type: ?Sized; +} + +impl Field for Flexible { + type Type = [u8]; +} + +trait KnownLayout { + type MaybeUninit: ?Sized; +} + + +impl<T> KnownLayout for [T] { + type MaybeUninit = [T]; +} + +struct ZerocopyKnownLayoutMaybeUninit(<<Flexible as Field>::Type as KnownLayout>::MaybeUninit); + +fn test(ptr: *mut [u8]) -> *mut ZerocopyKnownLayoutMaybeUninit { + ptr as *mut _ +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 8117401a534..323a5723d4a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -1,5 +1,5 @@ use hir::db::ExpandDatabase; -use hir::{HirFileIdExt, UnsafetyReason}; +use hir::{HirFileIdExt, UnsafeLint, UnsafetyReason}; use ide_db::text_edit::TextEdit; use ide_db::{assists::Assist, source_change::SourceChange}; use syntax::{ast, SyntaxNode}; @@ -11,10 +11,10 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { - let code = if d.only_lint { - DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn") - } else { - DiagnosticCode::RustcHardError("E0133") + let code = match d.lint { + UnsafeLint::HardError => DiagnosticCode::RustcHardError("E0133"), + UnsafeLint::UnsafeOpInUnsafeFn => DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn"), + UnsafeLint::DeprecatedSafe2024 => DiagnosticCode::RustcLint("deprecated_safe_2024"), }; let operation = display_unsafety_reason(d.reason); Diagnostic::new_with_syntax_node_ptr( @@ -585,25 +585,59 @@ fn main() { r#" //- /ed2021.rs crate:ed2021 edition:2021 #[rustc_deprecated_safe_2024] -unsafe fn safe() -> u8 { +unsafe fn deprecated_safe() -> u8 { 0 } + //- /ed2024.rs crate:ed2024 edition:2024 #[rustc_deprecated_safe_2024] -unsafe fn not_safe() -> u8 { +unsafe fn deprecated_safe() -> u8 { 0 } -//- /main.rs crate:main deps:ed2021,ed2024 + +//- /dep1.rs crate:dep1 deps:ed2021,ed2024 edition:2021 +fn main() { + ed2021::deprecated_safe(); + ed2024::deprecated_safe(); +} + +//- /dep2.rs crate:dep2 deps:ed2021,ed2024 edition:2024 +fn main() { + ed2021::deprecated_safe(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block + ed2024::deprecated_safe(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block +} + +//- /dep3.rs crate:dep3 deps:ed2021,ed2024 edition:2021 +#![warn(deprecated_safe)] + fn main() { - ed2021::safe(); - ed2024::not_safe(); - //^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block + ed2021::deprecated_safe(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block + ed2024::deprecated_safe(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block } "#, ) } #[test] + fn orphan_unsafe_format_args() { + // Checks that we don't place orphan arguments for formatting under an unsafe block. + check_diagnostics( + r#" +//- minicore: fmt +fn foo() { + let p = 0xDEADBEEF as *const i32; + format_args!("", *p); + // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block +} + "#, + ); + } + + #[test] fn unsafe_op_in_unsafe_fn_allowed_by_default_in_edition_2021() { check_diagnostics( r#" @@ -812,4 +846,36 @@ fn main() { "#, ) } + + #[test] + fn target_feature() { + check_diagnostics( + r#" +#[target_feature(enable = "avx")] +fn foo() {} + +#[target_feature(enable = "avx,avx2")] +fn bar() { + foo(); +} + +fn baz() { + foo(); + // ^^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block +} + "#, + ); + } + + #[test] + fn unsafe_fn_ptr_call() { + check_diagnostics( + r#" +fn f(it: unsafe fn()){ + it(); + // ^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 13979791444..0e3c4c7aa36 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -831,13 +831,14 @@ fn f() { #[test] fn or_pattern() { - // FIXME: `None` is inferred as unknown here for some reason check_diagnostics( r#" //- minicore: option fn f(_: i32) {} fn main() { let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return }; + //^^^^^ 💡 warn: variable does not need to be mutable + f(x); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 56afb38cc81..73dcbc13b79 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type}; +use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile}; use ide_db::{ famous_defs::FamousDefs, source_change::{SourceChange, SourceChangeBuilder}, @@ -88,7 +88,7 @@ fn add_reference( let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into())); let (_, mutability) = d.expected.as_reference()?; - let actual_with_ref = Type::reference(&d.actual, mutability); + let actual_with_ref = d.actual.add_reference(mutability); if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs index 6b654f89345..889258c94c5 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs @@ -33,12 +33,10 @@ // // Supported constraints: // -// |=== -// | Constraint | Restricts placeholder -// -// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`) -// | not(a) | Negates the constraint `a` -// |=== +// | Constraint | Restricts placeholder | +// |---------------|------------------------| +// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`) | +// | not(a) | Negates the constraint `a` | // // Available via the command `rust-analyzer.ssr`. // @@ -52,11 +50,9 @@ // String::from((y + 5).foo(z)) // ``` // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Structural Search Replace** -// |=== +// | Editor | Action Name | +// |---------|--------------| +// | VS Code | **rust-analyzer: Structural Search Replace** | // // Also available as an assist, by writing a comment containing the structural // search and replace rule. You will only see the assist if the comment can diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index 18f866eb9fc..006e6e8246e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -21,7 +21,7 @@ mod fn_references; // Provides user with annotations above items for looking up references or impl blocks // and running/debugging binaries. // -// image::https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png[] +//  #[derive(Debug, Hash, PartialEq, Eq)] pub struct Annotation { pub range: TextRange, diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index cfd8919730a..e35e47e7471 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -122,11 +122,9 @@ pub(crate) fn remove_links(markdown: &str) -> String { // The simplest way to use this feature is via the context menu. Right-click on // the selected item. The context menu opens. Select **Open Docs**. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Open Docs** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Open Docs** | pub(crate) fn external_docs( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 0ad894427b2..ad4308e06a1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -19,13 +19,11 @@ pub struct ExpandedMacro { // // Shows the full macro expansion of the macro at the current caret position. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Expand macro recursively at caret** | // -// | VS Code | **rust-analyzer: Expand macro recursively at caret** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[] +//  pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { let sema = Semantics::new(db); let file = sema.parse_guess_edition(position.file_id); diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs index 3d49082f285..76414854e91 100644 --- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs +++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs @@ -17,13 +17,11 @@ use crate::FileRange; // Extends or shrinks the current selection to the encompassing syntactic construct // (expression, statement, item, module, etc). It works with multiple cursors. // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>Alt+Shift+→</kbd>, <kbd>Alt+Shift+←</kbd> | // -// | VS Code | kbd:[Alt+Shift+→], kbd:[Alt+Shift+←] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif[] +//  pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { let sema = Semantics::new(db); let src = sema.parse_guess_edition(frange.file_id); diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs index 37b3cb03b33..5ed21444307 100644 --- a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs +++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs @@ -14,13 +14,11 @@ pub struct CrateInfo { // // Shows a view tree with all the dependencies of this project // -// |=== -// | Editor | Panel Name +// | Editor | Panel Name | +// |---------|------------| +// | VS Code | **Rust Dependencies** | // -// | VS Code | **Rust Dependencies** -// |=== -// -// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[] +//  pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> { let crate_graph = db.crate_graph(); crate_graph diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 50977ee840c..52fbab6fa12 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -31,14 +31,11 @@ pub enum StructureNodeKind { // * draw breadcrumbs to describe the context around the cursor // * draw outline of the file // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>Ctrl+Shift+O</kbd> | // -// | VS Code | kbd:[Ctrl+Shift+O] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif[] - +//  pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> { let mut res = Vec::new(); let mut stack = Vec::new(); diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index d18732a6b84..bdafb701ff5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -31,13 +31,11 @@ use syntax::{ // // For outline modules, this will navigate to the source file of the module. // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>F12</kbd> | // -// | VS Code | kbd:[F12] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[] +//  pub(crate) fn goto_definition( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, @@ -3274,4 +3272,22 @@ fn f() { "#, ); } + + #[test] + fn use_inside_body() { + check( + r#" +fn main() { + mod nice_module { + pub(super) struct NiceStruct; + // ^^^^^^^^^^ + } + + use nice_module::NiceStruct$0; + + let _ = NiceStruct; +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index e926378367e..e1d834b5d1c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -12,13 +12,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // // Navigates to the impl items of types. // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>Ctrl+F12</kbd> // -// | VS Code | kbd:[Ctrl+F12] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[] +//  pub(crate) fn goto_implementation( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index 2610d6c8863..ddc274a8303 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -8,13 +8,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // // Navigates to the type of an identifier. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **Go to Type Definition** | // -// | VS Code | **Go to Type Definition** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[] +//  pub(crate) fn goto_type_definition( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 612bc36f628..6463206596a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -43,12 +43,12 @@ pub struct HighlightRelatedConfig { // // Highlights constructs related to the thing under the cursor: // -// . if on an identifier, highlights all references to that identifier in the current file -// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope -// . if on an `async` or `await` token, highlights all yield points for that async context -// . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context -// . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context -// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure. +// 1. if on an identifier, highlights all references to that identifier in the current file +// * additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope +// 1. if on an `async` or `await` token, highlights all yield points for that async context +// 1. if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context +// 1. if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context +// 1. if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure. // // Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor. pub(crate) fn highlight_related( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 9d4c103fc2e..95a720e7e45 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -118,7 +118,7 @@ pub struct HoverResult { // Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code. // Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. // -// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[] +//  pub(crate) fn hover( db: &RootDatabase, frange @ FileRange { file_id, range }: FileRange, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 40f3406b72d..c996230c3a1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -434,6 +434,7 @@ fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) - None => it.name(db), } } + hir::GenericDef::Static(it) => Some(it.name(db)), }, Definition::DeriveHelper(derive_helper) => Some(derive_helper.derive().name(db)), d => { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 1f723c85df7..63039b1cd34 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -59,7 +59,7 @@ mod range_exclusive; // // Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if // any of the -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria] +// [following criteria](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99) // are met: // // * the parameter name is a suffix of the function's name @@ -68,13 +68,13 @@ mod range_exclusive; // of argument with _ splitting it off // * the parameter name starts with `ra_fixture` // * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name] +// [well known name](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200) // in a unary function // * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character] +// [single character](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201) // in a unary function // -// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] +//  pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, @@ -294,6 +294,7 @@ pub struct InlayHintsConfig { pub param_names_for_lifetime_elision_hints: bool, pub hide_named_constructor_hints: bool, pub hide_closure_initialization_hints: bool, + pub hide_closure_parameter_hints: bool, pub range_exclusive_hints: bool, pub closure_style: ClosureStyle, pub max_length: Option<usize>, @@ -860,6 +861,7 @@ mod tests { binding_mode_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, + hide_closure_parameter_hints: false, closure_style: ClosureStyle::ImplFn, param_names_for_lifetime_elision_hints: false, max_length: None, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 2acd4021cc1..d3b95750f7e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -13,11 +13,7 @@ use ide_db::famous_defs::FamousDefs; use ide_db::text_edit::TextEditBuilder; use span::EditionedFileId; -use stdx::never; -use syntax::{ - ast::{self, make, AstNode}, - ted, -}; +use syntax::ast::{self, prec::ExprPrecedence, AstNode}; use crate::{ AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart, @@ -104,12 +100,14 @@ pub(super) fn hints( }; let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _); + let mut has_adjustments = false; let mut allow_edit = !postfix; for Adjustment { source, target, kind } in iter { if source == target { cov_mark::hit!(same_type_adjustment); continue; } + has_adjustments = true; // FIXME: Add some nicer tooltips to each of these let (text, coercion) = match kind { @@ -172,6 +170,10 @@ pub(super) fn hints( }; if postfix { &mut post } else { &mut pre }.label.append_part(label); } + if !has_adjustments { + return None; + } + if !postfix && needs_inner_parens { pre.label.append_str("("); } @@ -254,71 +256,31 @@ fn mode_and_needs_parens_for_adjustment_hints( /// Returns whatever we need to add parentheses on the inside and/or outside of `expr`, /// if we are going to add (`postfix`) adjustments hints to it. fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) { - // This is a very miserable pile of hacks... - // - // `Expr::needs_parens_in` requires that the expression is the child of the other expression, - // that is supposed to be its parent. - // - // But we want to check what would happen if we add `*`/`.*` to the inner expression. - // To check for inner we need `` expr.needs_parens_in(`*expr`) ``, - // to check for outer we need `` `*expr`.needs_parens_in(parent) ``, - // where "expr" is the `expr` parameter, `*expr` is the edited `expr`, - // and "parent" is the parent of the original expression... - // - // For this we utilize mutable trees, which is a HACK, but it works. - // - // FIXME: comeup with a better API for `needs_parens_in`, so that we don't have to do *this* - - // Make `&expr`/`expr?` - let dummy_expr = { - // `make::*` function go through a string, so they parse wrongly. - // for example `` make::expr_try(`|| a`) `` would result in a - // `|| (a?)` and not `(|| a)?`. - // - // Thus we need dummy parens to preserve the relationship we want. - // The parens are then simply ignored by the following code. - let dummy_paren = make::expr_paren(expr.clone()); - if postfix { - make::expr_try(dummy_paren) - } else { - make::expr_ref(dummy_paren, false) - } - }; - - // Do the dark mutable tree magic. - // This essentially makes `dummy_expr` and `expr` switch places (families), - // so that `expr`'s parent is not `dummy_expr`'s parent. - let dummy_expr = dummy_expr.clone_for_update(); - let expr = expr.clone_for_update(); - ted::replace(expr.syntax(), dummy_expr.syntax()); - - let parent = dummy_expr.syntax().parent(); - let Some(expr) = (|| { - if postfix { - let ast::Expr::TryExpr(e) = &dummy_expr else { return None }; - let Some(ast::Expr::ParenExpr(e)) = e.expr() else { return None }; - - e.expr() - } else { - let ast::Expr::RefExpr(e) = &dummy_expr else { return None }; - let Some(ast::Expr::ParenExpr(e)) = e.expr() else { return None }; - - e.expr() - } - })() else { - never!("broken syntax tree?\n{:?}\n{:?}", expr, dummy_expr); - return (true, true); - }; - - // At this point - // - `parent` is the parent of the original expression - // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`) - // - `expr` is the clone of the original expression (with `dummy_expr` as the parent) - - let needs_outer_parens = parent.is_some_and(|p| dummy_expr.needs_parens_in(p)); - let needs_inner_parens = expr.needs_parens_in(dummy_expr.syntax().clone()); - - (needs_outer_parens, needs_inner_parens) + let prec = expr.precedence(); + if postfix { + // postfix ops have higher precedence than any other operator, so we need to wrap + // any inner expression that is below (except for jumps if they don't have a value) + let needs_inner_parens = prec < ExprPrecedence::Unambiguous && { + prec != ExprPrecedence::Jump || !expr.is_ret_like_with_no_value() + }; + // given we are the higher precedence, no parent expression will have stronger requirements + let needs_outer_parens = false; + (needs_outer_parens, needs_inner_parens) + } else { + // We need to wrap all binary like things, thats everything below prefix except for jumps + let needs_inner_parens = prec < ExprPrecedence::Prefix && prec != ExprPrecedence::Jump; + let parent = expr + .syntax() + .parent() + .and_then(ast::Expr::cast) + // if we are already wrapped, great, no need to wrap again + .filter(|it| !matches!(it, ast::Expr::ParenExpr(_))) + .map(|it| it.precedence()); + // if we have no parent, we don't need outer parens to disambiguate + // otherwise anything with higher precedence than what we insert needs to wrap us + let needs_outer_parens = parent.is_some_and(|prec| prec > ExprPrecedence::Prefix); + (needs_outer_parens, needs_inner_parens) + } } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 01a1a4545c4..c2986a9aa66 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -36,6 +36,9 @@ pub(super) fn hints( if it.ty().is_some() { return None; } + if config.hide_closure_parameter_hints && it.syntax().ancestors().nth(2).is_none_or(|n| matches!(ast::Expr::cast(n), Some(ast::Expr::ClosureExpr(_)))) { + return None; + } Some(it.colon_token()) }, ast::LetStmt(it) => { @@ -950,6 +953,36 @@ fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 { } #[test] + fn skip_closure_parameter_hints() { + check_with_config( + InlayHintsConfig { + type_hints: true, + hide_closure_parameter_hints: true, + ..DISABLED_CONFIG + }, + r#" +//- minicore: fn +struct Foo; +impl Foo { + fn foo(self: Self) {} + fn bar(self: &Self) {} +} +fn main() { + let closure = |x, y| x + y; + // ^^^^^^^ impl Fn(i32, i32) -> {unknown} + closure(2, 3); + let point = (10, 20); + // ^^^^^ (i32, i32) + let (x, y) = point; + // ^ i32 ^ i32 + Foo::foo(Foo); + Foo::bar(&Foo); +} +"#, + ); + } + + #[test] fn hint_truncation() { check_with_config( InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG }, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 3e91618d08e..9b981c0a3ac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -1,4 +1,4 @@ -//! Implementation of "closure return type" inlay hints. +//! Implementation of "closure captures" inlay hints. //! //! Tests live in [`bind_pat`][super::bind_pat] module. use ide_db::famous_defs::FamousDefs; diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret.rs b/src/tools/rust-analyzer/crates/ide/src/interpret.rs index e0fdc3dd6f9..ae11072e34b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/interpret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/interpret.rs @@ -7,11 +7,9 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange}; // Feature: Interpret A Function, Static Or Const. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Interpret** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Interpret** | pub(crate) fn interpret(db: &RootDatabase, position: FilePosition) -> String { match find_and_interpret(db, position) { Some((duration, mut result)) => { diff --git a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs index e4670177ecf..ea18a97070c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs +++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs @@ -21,17 +21,13 @@ pub struct JoinLinesConfig { // // Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. // -// See -// https://user-images.githubusercontent.com/1711539/124515923-4504e800-dde9-11eb-8d58-d97945a1a785.gif[this gif] -// for the cases handled specially by joined lines. +// See [this gif](https://user-images.githubusercontent.com/1711539/124515923-4504e800-dde9-11eb-8d58-d97945a1a785.gif) for the cases handled specially by joined lines. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Join lines** | // -// | VS Code | **rust-analyzer: Join lines** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[] +//  pub(crate) fn join_lines( config: &JoinLinesConfig, file: &SourceFile, diff --git a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs index 57356152836..67346ea9cf9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs @@ -9,13 +9,11 @@ use syntax::{ // moves cursor to the matching brace. It uses the actual parser to determine // braces, so it won't confuse generics with comparisons. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Find matching brace** | // -// | VS Code | **rust-analyzer: Find matching brace** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[] +//  pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { const BRACES: &[SyntaxKind] = &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index d97c12ebafb..66ea49a98a0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -289,7 +289,10 @@ fn def_to_non_local_moniker( definition: Definition, from_crate: Crate, ) -> Option<Moniker> { - let module = definition.module(db)?; + let module = match definition { + Definition::Module(module) if module.is_crate_root() => module, + _ => definition.module(db)?, + }; let krate = module.krate(); let edition = krate.edition(db); @@ -322,12 +325,18 @@ fn def_to_non_local_moniker( name: name.display(db, edition).to_string(), desc: def_to_kind(db, def).into(), }); - } else if reverse_description.is_empty() { - // Don't allow the last descriptor to be absent. - return None; } else { match def { - Definition::Module(module) if module.is_crate_root() => {} + Definition::Module(module) if module.is_crate_root() => { + // only include `crate` namespace by itself because we prefer + // `rust-analyzer cargo foo . bar/` over `rust-analyzer cargo foo . crate/bar/` + if reverse_description.is_empty() { + reverse_description.push(MonikerDescriptor { + name: "crate".to_owned(), + desc: MonikerDescriptorKind::Namespace, + }); + } + } _ => { tracing::error!(?def, "Encountered enclosing definition with no name"); } @@ -340,6 +349,9 @@ fn def_to_non_local_moniker( }; def = next_def; } + if reverse_description.is_empty() { + return None; + } reverse_description.reverse(); let description = reverse_description; diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs index b0df9257ba1..3fb3a788b91 100644 --- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs +++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs @@ -17,14 +17,12 @@ pub enum Direction { // // Move item under cursor or selection up and down. // -// |=== -// | Editor | Action Name -// +// | Editor | Action Name | +// |---------|-------------| // | VS Code | **rust-analyzer: Move item up** // | VS Code | **rust-analyzer: Move item down** -// |=== // -// image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[] +//  pub(crate) fn move_item( db: &RootDatabase, range: FileRange, diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs index 7a0c28d925a..6d82f9b0634 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -15,13 +15,11 @@ use crate::NavigationTarget; // // Navigates to the parent module of the current module. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Locate parent module** | // -// | VS Code | **rust-analyzer: Locate parent module** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[] +//  /// This returns `Vec` because a module may be included from several places. pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index b1079312d3b..069818d50e7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -43,13 +43,11 @@ pub struct Declaration { // // Shows all references of the item at the cursor location // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>Shift+Alt+F12</kbd> | // -// | VS Code | kbd:[Shift+Alt+F12] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif[] +//  pub(crate) fn find_all_refs( sema: &Semantics<'_, RootDatabase>, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 07dfd83c4eb..3e8295e3f08 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -71,13 +71,11 @@ pub(crate) fn prepare_rename( // // Renames the item below the cursor and all of its references // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>F2</kbd> | // -// | VS Code | kbd:[F2] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif[] +//  pub(crate) fn rename( db: &RootDatabase, position: FilePosition, @@ -2003,13 +2001,11 @@ impl Foo { "foo", r#" fn f($0self) -> i32 { - use self as _; self.i } "#, r#" fn f(foo: _) -> i32 { - use self as _; foo.i } "#, @@ -2017,6 +2013,26 @@ fn f(foo: _) -> i32 { } #[test] + fn no_type_value_ns_confuse() { + // Test that we don't rename items from different namespaces. + check( + "bar", + r#" +struct foo {} +fn f(foo$0: i32) -> i32 { + use foo as _; +} +"#, + r#" +struct foo {} +fn f(bar: i32) -> i32 { + use foo as _; +} +"#, + ); + } + + #[test] fn test_self_in_path_to_parameter() { check( "foo", diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 32edacee51c..78c9f2309a0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -119,12 +119,11 @@ impl Runnable { // location**. Super useful for repeatedly running just a single test. Do bind this // to a shortcut! // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Run** | // -// | VS Code | **rust-analyzer: Run** -// |=== -// image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[] +//  pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { let sema = Semantics::new(db); @@ -207,11 +206,9 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { // The simplest way to use this feature is via the context menu. Right-click on // the selected item. The context menu opens. Select **Peek Related Tests**. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Peek Related Tests** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Peek Related Tests** | pub(crate) fn related_tests( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index f8c60418eb0..f9972116004 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -321,7 +321,9 @@ fn signature_help_for_generics( format_to!(res.signature, "type {}", it.name(db).display(db, edition)); } // These don't have generic args that can be specified - hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None, + hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) | hir::GenericDef::Static(_) => { + return None + } } let params = generics_def.params(sema.db); diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 8050a38b3ca..07553a87d28 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -154,6 +154,7 @@ impl StaticIndex<'_> { implicit_drop_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, + hide_closure_parameter_hints: false, closure_style: hir::ClosureStyle::ImplFn, param_names_for_lifetime_elision_hints: false, binding_mode_hints: false, @@ -169,10 +170,10 @@ impl StaticIndex<'_> { .unwrap(); // hovers let sema = hir::Semantics::new(self.db); - let tokens_or_nodes = sema.parse_guess_edition(file_id).syntax().clone(); + let root = sema.parse_guess_edition(file_id).syntax().clone(); let edition = sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT); - let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|it| match it { + let tokens = root.descendants_with_tokens().filter_map(|it| match it { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(it) => Some(it), }); @@ -194,24 +195,19 @@ impl StaticIndex<'_> { ) }); let mut result = StaticIndexedFile { file_id, inlay_hints, folds, tokens: vec![] }; - for token in tokens { - let range = token.text_range(); - let node = token.parent().unwrap(); - let def = match get_definition(&sema, token.clone()) { - Some(it) => it, - None => continue, - }; + + let mut add_token = |def: Definition, range: TextRange, scope_node: &SyntaxNode| { let id = if let Some(it) = self.def_map.get(&def) { *it } else { let it = self.tokens.insert(TokenStaticData { - documentation: documentation_for_definition(&sema, def, &node), + documentation: documentation_for_definition(&sema, def, scope_node), hover: Some(hover_for_definition( &sema, file_id, def, None, - &node, + scope_node, None, false, &hover_config, @@ -240,6 +236,22 @@ impl StaticIndex<'_> { }, }); result.tokens.push((range, id)); + }; + + if let Some(module) = sema.file_to_module_def(file_id) { + let def = Definition::Module(module); + let range = root.text_range(); + add_token(def, range, &root); + } + + for token in tokens { + let range = token.text_range(); + let node = token.parent().unwrap(); + let def = match get_definition(&sema, token.clone()) { + Some(it) => it, + None => continue, + }; + add_token(def, range, &node); } self.files.push(result); } @@ -300,6 +312,10 @@ mod tests { let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect(); for f in s.files { for (range, _) in f.tokens { + if range.start() == TextSize::from(0) { + // ignore whole file range corresponding to module definition + continue; + } let it = FileRange { file_id: f.file_id, range }; if !range_set.contains(&it) { panic!("additional range {it:?}"); diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index 9e823daa2be..b0022cfac76 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -29,12 +29,11 @@ use triomphe::Arc; // // Shows internal statistic about memory usage of rust-analyzer. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Status** | // -// | VS Code | **rust-analyzer: Status** -// |=== -// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[] +//  pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { let mut buf = String::new(); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index f53f0aec098..1853e3a3407 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -76,113 +76,118 @@ pub struct HighlightConfig { // We also give special modifier for `mut` and `&mut` local variables. // // -// .Token Tags +// #### Token Tags // // Rust-analyzer currently emits the following token tags: // // - For items: -// + -// [horizontal] -// attribute:: Emitted for attribute macros. -// enum:: Emitted for enums. -// function:: Emitted for free-standing functions. -// derive:: Emitted for derive macros. -// macro:: Emitted for function-like macros. -// method:: Emitted for associated functions, also knowns as methods. -// namespace:: Emitted for modules. -// struct:: Emitted for structs. -// trait:: Emitted for traits. -// typeAlias:: Emitted for type aliases and `Self` in `impl`s. -// union:: Emitted for unions. +// +// | | | +// |-----------|--------------------------------| +// | attribute | Emitted for attribute macros. | +// |enum| Emitted for enums. | +// |function| Emitted for free-standing functions. | +// |derive| Emitted for derive macros. | +// |macro| Emitted for function-like macros. | +// |method| Emitted for associated functions, also knowns as methods. | +// |namespace| Emitted for modules. | +// |struct| Emitted for structs.| +// |trait| Emitted for traits.| +// |typeAlias| Emitted for type aliases and `Self` in `impl`s.| +// |union| Emitted for unions.| // // - For literals: -// + -// [horizontal] -// boolean:: Emitted for the boolean literals `true` and `false`. -// character:: Emitted for character literals. -// number:: Emitted for numeric literals. -// string:: Emitted for string literals. -// escapeSequence:: Emitted for escaped sequences inside strings like `\n`. -// formatSpecifier:: Emitted for format specifiers `{:?}` in `format!`-like macros. +// +// | | | +// |-----------|--------------------------------| +// | boolean| Emitted for the boolean literals `true` and `false`.| +// | character| Emitted for character literals.| +// | number| Emitted for numeric literals.| +// | string| Emitted for string literals.| +// | escapeSequence| Emitted for escaped sequences inside strings like `\n`.| +// | formatSpecifier| Emitted for format specifiers `{:?}` in `format!`-like macros.| // // - For operators: -// + -// [horizontal] -// operator:: Emitted for general operators. -// arithmetic:: Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`. -// bitwise:: Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`. -// comparison:: Emitted for the comparison operators `>`, `<`, `==`, `>=`, `<=`, `!=`. -// logical:: Emitted for the logical operators `||`, `&&`, `!`. +// +// | | | +// |-----------|--------------------------------| +// |operator| Emitted for general operators.| +// |arithmetic| Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.| +// |bitwise| Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.| +// |comparison| Emitted for the comparison oerators `>`, `<`, `==`, `>=`, `<=`, `!=`.| +// |logical| Emitted for the logical operatos `||`, `&&`, `!`.| // // - For punctuation: -// + -// [horizontal] -// punctuation:: Emitted for general punctuation. -// attributeBracket:: Emitted for attribute invocation brackets, that is the `#[` and `]` tokens. -// angle:: Emitted for `<>` angle brackets. -// brace:: Emitted for `{}` braces. -// bracket:: Emitted for `[]` brackets. -// parenthesis:: Emitted for `()` parentheses. -// colon:: Emitted for the `:` token. -// comma:: Emitted for the `,` token. -// dot:: Emitted for the `.` token. -// semi:: Emitted for the `;` token. -// macroBang:: Emitted for the `!` token in macro calls. // -// //- +// | | | +// |-----------|--------------------------------| +// |punctuation| Emitted for general punctuation.| +// |attributeBracket| Emitted for attribute invocation brackets, that is the `#[` and `]` tokens.| +// |angle| Emitted for `<>` angle brackets.| +// |brace| Emitted for `{}` braces.| +// |bracket| Emitted for `[]` brackets.| +// |parenthesis| Emitted for `()` parentheses.| +// |colon| Emitted for the `:` token.| +// |comma| Emitted for the `,` token.| +// |dot| Emitted for the `.` token.| +// |semi| Emitted for the `;` token.| +// |macroBang| Emitted for the `!` token in macro calls.| // -// [horizontal] -// builtinAttribute:: Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example. -// builtinType:: Emitted for builtin types like `u32`, `str` and `f32`. -// comment:: Emitted for comments. -// constParameter:: Emitted for const parameters. -// deriveHelper:: Emitted for derive helper attributes. -// enumMember:: Emitted for enum variants. -// generic:: Emitted for generic tokens that have no mapping. -// keyword:: Emitted for keywords. -// label:: Emitted for labels. -// lifetime:: Emitted for lifetimes. -// parameter:: Emitted for non-self function parameters. -// property:: Emitted for struct and union fields. -// selfKeyword:: Emitted for the self function parameter and self path-specifier. -// selfTypeKeyword:: Emitted for the Self type parameter. -// toolModule:: Emitted for tool modules. -// typeParameter:: Emitted for type parameters. -// unresolvedReference:: Emitted for unresolved references, names that rust-analyzer can't find the definition of. -// variable:: Emitted for locals, constants and statics. +//- // +// | | | +// |-----------|--------------------------------| +// |builtinAttribute| Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example.| +// |builtinType| Emitted for builtin types like `u32`, `str` and `f32`.| +// |comment| Emitted for comments.| +// |constParameter| Emitted for const parameters.| +// |deriveHelper| Emitted for derive helper attributes.| +// |enumMember| Emitted for enum variants.| +// |generic| Emitted for generic tokens that have no mapping.| +// |keyword| Emitted for keywords.| +// |label| Emitted for labels.| +// |lifetime| Emitted for lifetimes.| +// |parameter| Emitted for non-self function parameters.| +// |property| Emitted for struct and union fields.| +// |selfKeyword| Emitted for the self function parameter and self path-specifier.| +// |selfTypeKeyword| Emitted for the Self type parameter.| +// |toolModule| Emitted for tool modules.| +// |typeParameter| Emitted for type parameters.| +// |unresolvedReference| Emitted for unresolved references, names that rust-analyzer can't find the definition of.| +// |variable| Emitted for locals, constants and statics.| // -// .Token Modifiers +// +// #### Token Modifiers // // Token modifiers allow to style some elements in the source code more precisely. // // Rust-analyzer currently emits the following token modifiers: // -// [horizontal] -// async:: Emitted for async functions and the `async` and `await` keywords. -// attribute:: Emitted for tokens inside attributes. -// callable:: Emitted for locals whose types implements one of the `Fn*` traits. -// constant:: Emitted for consts. -// consuming:: Emitted for locals that are being consumed when use in a function call. -// controlFlow:: Emitted for control-flow related tokens, this includes the `?` operator. -// crateRoot:: Emitted for crate names, like `serde` and `crate`. -// declaration:: Emitted for names of definitions, like `foo` in `fn foo() {}`. -// defaultLibrary:: Emitted for items from built-in crates (std, core, alloc, test and proc_macro). -// documentation:: Emitted for documentation comments. -// injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation. -// intraDocLink:: Emitted for intra doc links in doc-strings. -// library:: Emitted for items that are defined outside of the current crate. -// macro:: Emitted for tokens inside macro calls. -// mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`. -// public:: Emitted for items that are from the current crate and are `pub`. -// reference:: Emitted for locals behind a reference and functions taking `self` by reference. -// static:: Emitted for "static" functions, also known as functions that do not take a `self` param, as well as statics and consts. -// trait:: Emitted for associated trait items. -// unsafe:: Emitted for unsafe operations, like unsafe function calls, as well as the `unsafe` token. -// +// | | | +// |-----------|--------------------------------| +// |async| Emitted for async functions and the `async` and `await` keywords.| +// |attribute| Emitted for tokens inside attributes.| +// |callable| Emitted for locals whose types implements one of the `Fn*` traits.| +// |constant| Emitted for const.| +// |consuming| Emitted for locals that are being consumed when use in a function call.| +// |controlFlow| Emitted for control-flow related tokens, this includes th `?` operator.| +// |crateRoot| Emitted for crate names, like `serde` and `crate.| +// |declaration| Emitted for names of definitions, like `foo` in `fn foo(){}`.| +// |defaultLibrary| Emitted for items from built-in crates (std, core, allc, test and proc_macro).| +// |documentation| Emitted for documentation comment.| +// |injected| Emitted for doc-string injected highlighting like rust source blocks in documentation.| +// |intraDocLink| Emitted for intra doc links in doc-string.| +// |library| Emitted for items that are defined outside of the current crae.| +// |macro| Emitted for tokens inside macro call.| +// |mutable| Emitted for mutable locals and statics as well as functions taking `&mut self`.| +// |public| Emitted for items that are from the current crate and are `pub.| +// |reference| Emitted for locals behind a reference and functions taking self` by reference.| +// |static| Emitted for "static" functions, also known as functions that d not take a `self` param, as well as statics and consts.| +// |trait| Emitted for associated trait item.| +// |unsafe| Emitted for unsafe operations, like unsafe function calls, as ell as the `unsafe` token.| // -// image::https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png[] -// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[] +//  +//  pub(crate) fn highlight( db: &RootDatabase, config: HighlightConfig, @@ -478,7 +483,15 @@ fn traverse( { continue; } - highlight_format_string(hl, sema, krate, &string, &expanded_string, range); + highlight_format_string( + hl, + sema, + krate, + &string, + &expanded_string, + range, + file_id.edition(), + ); if !string.is_raw() { highlight_escape_string(hl, &string, range.start()); @@ -526,6 +539,7 @@ fn traverse( &mut bindings_shadow_count, config.syntactic_name_ref_highlighting, name_like, + file_id.edition(), ), NodeOrToken::Token(token) => { highlight::token(sema, token, file_id.edition()).zip(Some(None)) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs index 7234108701a..43a6bdad7e9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs @@ -4,6 +4,7 @@ use ide_db::{ syntax_helpers::format_string::{is_format_string, lex_format_specifiers, FormatSpecifier}, SymbolKind, }; +use span::Edition; use syntax::{ast, TextRange}; use crate::{ @@ -18,6 +19,7 @@ pub(super) fn highlight_format_string( string: &ast::String, expanded_string: &ast::String, range: TextRange, + edition: Edition, ) { if is_format_string(expanded_string) { // FIXME: Replace this with the HIR info we have now. @@ -39,7 +41,7 @@ pub(super) fn highlight_format_string( if let Some(res) = res { stack.add(HlRange { range, - highlight: highlight_def(sema, krate, Definition::from(res)), + highlight: highlight_def(sema, krate, Definition::from(res), edition), binding_hash: None, }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 22a2fe4e9eb..479c0b381a4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -58,6 +58,7 @@ pub(super) fn name_like( bindings_shadow_count: &mut FxHashMap<hir::Name, u32>, syntactic_name_ref_highlighting: bool, name_like: ast::NameLike, + edition: Edition, ) -> Option<(Highlight, Option<u64>)> { let mut binding_hash = None; let highlight = match name_like { @@ -68,16 +69,17 @@ pub(super) fn name_like( &mut binding_hash, syntactic_name_ref_highlighting, name_ref, + edition, ), ast::NameLike::Name(name) => { - highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name) + highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name, edition) } ast::NameLike::Lifetime(lifetime) => match IdentClass::classify_lifetime(sema, &lifetime) { Some(IdentClass::NameClass(NameClass::Definition(def))) => { - highlight_def(sema, krate, def) | HlMod::Definition + highlight_def(sema, krate, def, edition) | HlMod::Definition } Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => { - highlight_def(sema, krate, def) + highlight_def(sema, krate, def, edition) } // FIXME: Fallback for 'static and '_, as we do not resolve these yet _ => SymbolKind::LifetimeParam.into(), @@ -234,16 +236,17 @@ fn highlight_name_ref( binding_hash: &mut Option<u64>, syntactic_name_ref_highlighting: bool, name_ref: ast::NameRef, + edition: Edition, ) -> Highlight { let db = sema.db; - if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref) { + if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref, edition) { return res; } let name_class = match NameRefClass::classify(sema, &name_ref) { Some(name_kind) => name_kind, None if syntactic_name_ref_highlighting => { - return highlight_name_ref_by_syntax(name_ref, sema, krate) + return highlight_name_ref_by_syntax(name_ref, sema, krate, edition) } // FIXME: This is required for helper attributes used by proc-macros, as those do not map down // to anything when used. @@ -267,7 +270,7 @@ fn highlight_name_ref( *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) }; - let mut h = highlight_def(sema, krate, def); + let mut h = highlight_def(sema, krate, def, edition); match def { Definition::Local(local) if is_consumed_lvalue(name_ref.syntax(), &local, db) => { @@ -305,7 +308,7 @@ fn highlight_name_ref( h } NameRefClass::FieldShorthand { field_ref, .. } => { - highlight_def(sema, krate, field_ref.into()) + highlight_def(sema, krate, field_ref.into(), edition) } NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => { let mut h = HlTag::Symbol(SymbolKind::Module).into(); @@ -341,6 +344,7 @@ fn highlight_name( binding_hash: &mut Option<u64>, krate: hir::Crate, name: ast::Name, + edition: Edition, ) -> Highlight { let name_kind = NameClass::classify(sema, &name); if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { @@ -351,7 +355,7 @@ fn highlight_name( }; match name_kind { Some(NameClass::Definition(def)) => { - let mut h = highlight_def(sema, krate, def) | HlMod::Definition; + let mut h = highlight_def(sema, krate, def, edition) | HlMod::Definition; if let Definition::Trait(trait_) = &def { if trait_.is_unsafe(sema.db) { h |= HlMod::Unsafe; @@ -359,7 +363,7 @@ fn highlight_name( } h } - Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def), + Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def, edition), Some(NameClass::PatFieldShorthand { field_ref, .. }) => { let mut h = HlTag::Symbol(SymbolKind::Field).into(); if let hir::VariantDef::Union(_) = field_ref.parent_def(sema.db) { @@ -379,6 +383,7 @@ pub(super) fn highlight_def( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, def: Definition, + edition: Edition, ) -> Highlight { let db = sema.db; let mut h = match def { @@ -427,7 +432,12 @@ pub(super) fn highlight_def( } } - if func.is_unsafe_to_call(db) { + // FIXME: Passing `None` here means not-unsafe functions with `#[target_feature]` will be + // highlighted as unsafe, even when the current target features set is a superset (RFC 2396). + // We probably should consider checking the current function, but I found no easy way to do + // that (also I'm worried about perf). There's also an instance below. + // FIXME: This should be the edition of the call. + if func.is_unsafe_to_call(db, None, edition) { h |= HlMod::Unsafe; } if func.is_async(db) { @@ -575,21 +585,23 @@ fn highlight_method_call_by_name_ref( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, name_ref: &ast::NameRef, + edition: Edition, ) -> Option<Highlight> { let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; - highlight_method_call(sema, krate, &mc) + highlight_method_call(sema, krate, &mc, edition) } fn highlight_method_call( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, method_call: &ast::MethodCallExpr, + edition: Edition, ) -> Option<Highlight> { let func = sema.resolve_method_call(method_call)?; let mut h = SymbolKind::Method.into(); - if func.is_unsafe_to_call(sema.db) || sema.is_unsafe_method_call(method_call) { + if func.is_unsafe_to_call(sema.db, None, edition) || sema.is_unsafe_method_call(method_call) { h |= HlMod::Unsafe; } if func.is_async(sema.db) { @@ -665,6 +677,12 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { STATIC => SymbolKind::Static, IDENT_PAT => SymbolKind::Local, FORMAT_ARGS_ARG => SymbolKind::Local, + RENAME => SymbolKind::Local, + MACRO_RULES => SymbolKind::Macro, + CONST_PARAM => SymbolKind::ConstParam, + SELF_PARAM => SymbolKind::SelfParam, + TRAIT_ALIAS => SymbolKind::TraitAlias, + ASM_OPERAND_NAMED => SymbolKind::Local, _ => return default.into(), }; @@ -675,6 +693,7 @@ fn highlight_name_ref_by_syntax( name: ast::NameRef, sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, + edition: Edition, ) -> Highlight { let default = HlTag::UnresolvedReference; @@ -685,7 +704,7 @@ fn highlight_name_ref_by_syntax( match parent.kind() { METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) - .and_then(|it| highlight_method_call(sema, krate, &it)) + .and_then(|it| highlight_method_call(sema, krate, &it, edition)) .unwrap_or_else(|| SymbolKind::Method.into()), FIELD_EXPR => { let h = HlTag::Symbol(SymbolKind::Field); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 0a7e273950d..1794d7dbfe2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -82,6 +82,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="colon">:</span>literal<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span><span class="brace">{</span>stringify<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="semicolon">;</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span> <span class="brace">}</span> +<span class="keyword">use</span> <span class="unresolved_reference">foo</span><span class="operator">::</span><span class="unresolved_reference">bar</span> <span class="keyword">as</span> <span class="variable declaration">baz</span><span class="semicolon">;</span> +<span class="keyword">trait</span> <span class="trait_alias declaration">Bar</span> <span class="operator">=</span> <span class="unresolved_reference">Baz</span><span class="semicolon">;</span> +<span class="keyword">trait</span> <span class="trait_alias declaration">Foo</span> <span class="operator">=</span> <span class="trait_alias">Bar</span><span class="semicolon">;</span> + <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\n</span><span class="char_literal">'</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\t</span><span class="char_literal">'</span><span class="semicolon">;</span> diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index af52b33de64..b9520ae2bba 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -466,6 +466,10 @@ macro_rules! reuse_twice { ($literal:literal) => {{stringify!($literal); format_args!($literal)}}; } +use foo::bar as baz; +trait Bar = Baz; +trait Foo = Bar; + fn main() { let a = '\n'; let a = '\t'; diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index 47d75f1c957..8c9dd051452 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -51,16 +51,15 @@ struct ExtendedTextEdit { // - typing `{` in a use item adds a closing `}` in the right place // - typing `>` to complete a return type `->` will insert a whitespace after it // -// VS Code:: +// #### VS Code // // Add the following to `settings.json`: -// [source,json] -// ---- +// ```json // "editor.formatOnType": true, -// ---- +// ``` // -// image::https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif[] -// image::https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif[] +//  +//  pub(crate) fn on_char_typed( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs index e249c38c73d..c6d1c283f4e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs @@ -16,12 +16,12 @@ use ide_db::text_edit::TextEdit; // Feature: On Enter // -// rust-analyzer can override kbd:[Enter] key to make it smarter: +// rust-analyzer can override <kbd>Enter</kbd> key to make it smarter: // -// - kbd:[Enter] inside triple-slash comments automatically inserts `///` -// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` -// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!` -// - kbd:[Enter] after `{` indents contents and closing `}` of single-line block +// - <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 // // This action needs to be assigned to shortcut explicitly. // @@ -29,29 +29,27 @@ use ide_db::text_edit::TextEdit; // Similarly, if rust-analyzer crashes or stops responding, `Enter` might not work. // In that case, you can still press `Shift-Enter` to insert a newline. // -// VS Code:: +// #### VS Code // // Add the following to `keybindings.json`: -// [source,json] -// ---- +// ```json // { // "key": "Enter", // "command": "rust-analyzer.onEnter", // "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust" // } -// ---- +// ```` // // When using the Vim plugin: -// [source,json] -// ---- +// ```json // { // "key": "Enter", // "command": "rust-analyzer.onEnter", // "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust && vim.mode == 'Insert'" // } -// ---- +// ```` // -// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[] +//  pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { let parse = db.parse(EditionedFileId::current_edition(position.file_id)); let file = parse.tree(); diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs index 9ff099f479e..eb6eb7da1e9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs @@ -12,11 +12,9 @@ use triomphe::Arc; // // Only workspace crates are included, no crates.io dependencies or sysroot crates. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: View Crate Graph** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: View Crate Graph** | pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, String> { let crate_graph = db.crate_graph(); let crates_to_render = crate_graph @@ -86,7 +84,8 @@ impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph { } fn node_label(&'a self, n: &CrateId) -> LabelText<'a> { - let name = self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name); + let name = + self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name.as_str()); LabelText::LabelStr(name.into()) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs index fe532f4cc55..bfdf9d0f337 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs @@ -4,12 +4,11 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode}; // Feature: View Hir // -// |=== -// | Editor | Action Name -// +// | Editor | Action Name | +// |---------|--------------| // | VS Code | **rust-analyzer: View Hir** -// |=== -// image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[] +// +//  pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String { body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs index a6352b99d4f..67c241cbb91 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs @@ -6,11 +6,9 @@ use span::EditionedFileId; // // Displays the ItemTree of the currently open file, for debugging. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Debug ItemTree** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Debug ItemTree** | pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String { let sema = Semantics::new(db); let file_id = sema diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index ff74e05e943..edb83bc4eac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -75,11 +75,9 @@ impl FieldOrTupleIdx { // // Displays the recursive memory layout of a datatype. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: View Memory Layout** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: View Memory Layout** | pub(crate) fn view_memory_layout( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs index 7a228375d5e..aa4ff64a819 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs @@ -4,11 +4,9 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode}; // Feature: View Mir // -// |=== -// | Editor | Action Name -// +// | Editor | Action Name | +// |---------|-------------| // | VS Code | **rust-analyzer: View Mir** -// |=== pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String { body_mir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs index 218ee15a7dd..407720864bf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs @@ -1,41 +1,50 @@ use hir::Semantics; -use ide_db::{FileId, RootDatabase}; -use span::TextRange; +use ide_db::{ + line_index::{LineCol, LineIndex}, + FileId, LineIndexDatabase, RootDatabase, +}; +use span::{TextRange, TextSize}; use stdx::format_to; use syntax::{ ast::{self, IsString}, AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, WalkEvent, }; +use triomphe::Arc; // Feature: Show Syntax Tree // // Shows a tree view with the syntax tree of the current file // -// |=== -// | Editor | Panel Name -// -// | VS Code | **Rust Syntax Tree** -// |=== +// | Editor | Panel Name | +// |---------|-------------| +// | VS Code | **Rust Syntax Tree** | pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String { let sema = Semantics::new(db); + let line_index = db.line_index(file_id); let parse = sema.parse_guess_edition(file_id); - syntax_node_to_json(parse.syntax(), None) + + let ctx = SyntaxTreeCtx { line_index, in_string: None }; + + syntax_node_to_json(parse.syntax(), &ctx) } -fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String { +fn syntax_node_to_json(node: &SyntaxNode, ctx: &SyntaxTreeCtx) -> String { let mut result = String::new(); for event in node.preorder_with_tokens() { match event { WalkEvent::Enter(it) => { let kind = it.kind(); - let (text_range, inner_range_str) = match &ctx { - Some(ctx) => { + let (text_range, inner_range_str) = match &ctx.in_string { + Some(in_string) => { + let start_pos = TextPosition::new(&ctx.line_index, it.text_range().start()); + let end_pos = TextPosition::new(&ctx.line_index, it.text_range().end()); + let inner_start: u32 = it.text_range().start().into(); - let inner_end: u32 = it.text_range().end().into(); + let inner_end: u32 = it.text_range().start().into(); - let mut true_start = inner_start + ctx.offset; - let mut true_end = inner_end + ctx.offset; - for pos in &ctx.marker_positions { + let mut true_start = inner_start + in_string.offset; + let mut true_end = inner_end + in_string.offset; + for pos in &in_string.marker_positions { if *pos >= inner_end { break; } @@ -48,39 +57,33 @@ fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String { let true_range = TextRange::new(true_start.into(), true_end.into()); - ( - true_range, - format!( - r#","istart":{:?},"iend":{:?}"#, - it.text_range().start(), - it.text_range().end() - ), - ) + (true_range, format!(r#","istart":{start_pos},"iend":{end_pos}"#,)) } None => (it.text_range(), "".to_owned()), }; - let start = text_range.start(); - let end = text_range.end(); + + let start = TextPosition::new(&ctx.line_index, text_range.start()); + let end = TextPosition::new(&ctx.line_index, text_range.end()); match it { NodeOrToken::Node(_) => { format_to!( result, - r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":["# + r#"{{"type":"Node","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str},"children":["# ); } NodeOrToken::Token(token) => { let comma = if token.next_sibling_or_token().is_some() { "," } else { "" }; - match parse_rust_string(token) { + match parse_rust_string(token, ctx) { Some(parsed) => { format_to!( result, - r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":[{parsed}]}}{comma}"# + r#"{{"type":"Node","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str},"children":[{parsed}]}}{comma}"# ); } None => format_to!( result, - r#"{{"type":"Token","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str}}}{comma}"# + r#"{{"type":"Token","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str}}}{comma}"# ), } } @@ -99,7 +102,26 @@ fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String { result } -fn parse_rust_string(token: SyntaxToken) -> Option<String> { +struct TextPosition { + offset: TextSize, + line: u32, + col: u32, +} + +impl std::fmt::Display for TextPosition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[{:?},{},{}]", self.offset, self.line, self.col) + } +} + +impl TextPosition { + pub(crate) fn new(line_index: &LineIndex, offset: TextSize) -> Self { + let LineCol { line, col } = line_index.line_col(offset); + Self { offset, line, col } + } +} + +fn parse_rust_string(token: SyntaxToken, ctx: &SyntaxTreeCtx) -> Option<String> { let string_node = ast::String::cast(token)?; let text = string_node.value().ok()?; @@ -128,13 +150,20 @@ fn parse_rust_string(token: SyntaxToken) -> Option<String> { return None; } - Some(syntax_node_to_json( - node, - Some(InStringCtx { + let ctx = SyntaxTreeCtx { + line_index: ctx.line_index.clone(), + in_string: Some(InStringCtx { offset: string_node.text_range_between_quotes()?.start().into(), marker_positions, }), - )) + }; + + Some(syntax_node_to_json(node, &ctx)) +} + +struct SyntaxTreeCtx { + line_index: Arc<LineIndex>, + in_string: Option<InStringCtx>, } struct InStringCtx { @@ -160,7 +189,7 @@ mod tests { check( r#"fn foo() {}"#, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":11,"children":[{"type":"Node","kind":"FN","start":0,"end":11,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":6,"children":[{"type":"Token","kind":"IDENT","start":3,"end":6}]},{"type":"Node","kind":"PARAM_LIST","start":6,"end":8,"children":[{"type":"Token","kind":"L_PAREN","start":6,"end":7},{"type":"Token","kind":"R_PAREN","start":7,"end":8}]},{"type":"Token","kind":"WHITESPACE","start":8,"end":9},{"type":"Node","kind":"BLOCK_EXPR","start":9,"end":11,"children":[{"type":"Node","kind":"STMT_LIST","start":9,"end":11,"children":[{"type":"Token","kind":"L_CURLY","start":9,"end":10},{"type":"Token","kind":"R_CURLY","start":10,"end":11}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[11,0,11],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[11,0,11],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[6,0,6],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[6,0,6]}]},{"type":"Node","kind":"PARAM_LIST","start":[6,0,6],"end":[8,0,8],"children":[{"type":"Token","kind":"L_PAREN","start":[6,0,6],"end":[7,0,7]},{"type":"Token","kind":"R_PAREN","start":[7,0,7],"end":[8,0,8]}]},{"type":"Token","kind":"WHITESPACE","start":[8,0,8],"end":[9,0,9]},{"type":"Node","kind":"BLOCK_EXPR","start":[9,0,9],"end":[11,0,11],"children":[{"type":"Node","kind":"STMT_LIST","start":[9,0,9],"end":[11,0,11],"children":[{"type":"Token","kind":"L_CURLY","start":[9,0,9],"end":[10,0,10]},{"type":"Token","kind":"R_CURLY","start":[10,0,10],"end":[11,0,11]}]}]}]}]}"# ]], ); @@ -173,7 +202,7 @@ fn test() { ", ""); }"#, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":60,"children":[{"type":"Node","kind":"FN","start":0,"end":60,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":60,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":60,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":58,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":57,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":57,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":57,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":52,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":51,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":30,"istart":0,"iend":5},{"type":"Node","kind":"FN","start":30,"end":46,"istart":5,"iend":21,"children":[{"type":"Token","kind":"FN_KW","start":30,"end":32,"istart":5,"iend":7},{"type":"Token","kind":"WHITESPACE","start":32,"end":33,"istart":7,"iend":8},{"type":"Node","kind":"NAME","start":33,"end":36,"istart":8,"iend":11,"children":[{"type":"Token","kind":"IDENT","start":33,"end":36,"istart":8,"iend":11}]},{"type":"Node","kind":"PARAM_LIST","start":36,"end":38,"istart":11,"iend":13,"children":[{"type":"Token","kind":"L_PAREN","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_PAREN","start":37,"end":38,"istart":12,"iend":13}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"BLOCK_EXPR","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Node","kind":"STMT_LIST","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Token","kind":"L_CURLY","start":39,"end":40,"istart":14,"iend":15},{"type":"Token","kind":"WHITESPACE","start":40,"end":45,"istart":15,"iend":20},{"type":"Token","kind":"R_CURLY","start":45,"end":46,"istart":20,"iend":21}]}]}]},{"type":"Token","kind":"WHITESPACE","start":46,"end":51,"istart":21,"iend":26}]}]},{"type":"Token","kind":"COMMA","start":52,"end":53},{"type":"Token","kind":"WHITESPACE","start":53,"end":54},{"type":"Token","kind":"STRING","start":54,"end":56},{"type":"Token","kind":"R_PAREN","start":56,"end":57}]}]}]},{"type":"Token","kind":"SEMICOLON","start":57,"end":58}]},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"R_CURLY","start":59,"end":60}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[60,5,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[60,5,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[60,5,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[60,5,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[58,4,11],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[57,4,10],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[57,4,10],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[57,4,10],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[52,4,5],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[26,2,0],"children":[{"type":"Token","kind":"WHITESPACE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[5,0,5]},{"type":"Node","kind":"FN","start":[30,2,4],"end":[30,2,4],"istart":[5,0,5],"iend":[21,1,9],"children":[{"type":"Token","kind":"FN_KW","start":[30,2,4],"end":[30,2,4],"istart":[5,0,5],"iend":[7,0,7]},{"type":"Token","kind":"WHITESPACE","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Node","kind":"NAME","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[11,0,11],"children":[{"type":"Token","kind":"IDENT","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[11,0,11]}]},{"type":"Node","kind":"PARAM_LIST","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_PAREN","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_PAREN","start":[37,2,11],"end":[37,2,11],"istart":[12,1,0],"iend":[13,1,1]}]},{"type":"Token","kind":"WHITESPACE","start":[38,2,12],"end":[38,2,12],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"BLOCK_EXPR","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[21,1,9],"children":[{"type":"Node","kind":"STMT_LIST","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[21,1,9],"children":[{"type":"Token","kind":"L_CURLY","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[15,1,3]},{"type":"Token","kind":"WHITESPACE","start":[40,2,14],"end":[40,2,14],"istart":[15,1,3],"iend":[20,1,8]},{"type":"Token","kind":"R_CURLY","start":[45,3,4],"end":[45,3,4],"istart":[20,1,8],"iend":[21,1,9]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[46,3,5],"end":[46,3,5],"istart":[21,1,9],"iend":[26,2,0]}]}]},{"type":"Token","kind":"COMMA","start":[52,4,5],"end":[53,4,6]},{"type":"Token","kind":"WHITESPACE","start":[53,4,6],"end":[54,4,7]},{"type":"Token","kind":"STRING","start":[54,4,7],"end":[56,4,9]},{"type":"Token","kind":"R_PAREN","start":[56,4,9],"end":[57,4,10]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[57,4,10],"end":[58,4,11]}]},{"type":"Token","kind":"WHITESPACE","start":[58,4,11],"end":[59,5,0]},{"type":"Token","kind":"R_CURLY","start":[59,5,0],"end":[60,5,1]}]}]}]}]}"# ]], ) } @@ -190,7 +219,7 @@ fn bar() { ", ""); }"#, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":65,"children":[{"type":"Node","kind":"FN","start":0,"end":65,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":65,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":65,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":63,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":62,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":62,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":62,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":57,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":56,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":26,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":26,"end":38,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":26,"end":28,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":28,"end":29,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":29,"end":32,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":29,"end":32,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":32,"end":34,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":32,"end":33,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":33,"end":34,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":34,"end":35,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":35,"end":36,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":37,"end":38,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":39,"end":51,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":39,"end":41,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":41,"end":42,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":42,"end":45,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":42,"end":45,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":45,"end":47,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":45,"end":46,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":46,"end":47,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":47,"end":48,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":48,"end":49,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":50,"end":51,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":51,"end":56,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":57,"end":58},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"STRING","start":59,"end":61},{"type":"Token","kind":"R_PAREN","start":61,"end":62}]}]}]},{"type":"Token","kind":"SEMICOLON","start":62,"end":63}]},{"type":"Token","kind":"WHITESPACE","start":63,"end":64},{"type":"Token","kind":"R_CURLY","start":64,"end":65}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[65,7,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[65,7,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[65,7,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[65,7,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[63,6,11],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[62,6,10],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[62,6,10],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[62,6,10],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[57,6,5],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[31,2,5],"children":[{"type":"Token","kind":"WHITESPACE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[26,2,0],"end":[26,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[26,2,0],"end":[26,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[28,2,2],"end":[28,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[29,2,3],"end":[29,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[29,2,3],"end":[29,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[34,2,8],"end":[34,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[37,3,0],"end":[37,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[38,3,1],"end":[38,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[39,4,0],"end":[39,4,0],"istart":[14,1,2],"iend":[26,2,0],"children":[{"type":"Token","kind":"FN_KW","start":[39,4,0],"end":[39,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[41,4,2],"end":[41,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[42,4,3],"end":[42,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[42,4,3],"end":[42,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[45,4,6],"end":[45,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[45,4,6],"end":[45,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[46,4,7],"end":[46,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[47,4,8],"end":[47,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[26,2,0],"children":[{"type":"Node","kind":"STMT_LIST","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[26,2,0],"children":[{"type":"Token","kind":"L_CURLY","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[49,4,10],"end":[49,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[50,5,0],"end":[50,5,0],"istart":[25,1,13],"iend":[26,2,0]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[51,5,1],"end":[51,5,1],"istart":[26,2,0],"iend":[31,2,5]}]}]},{"type":"Token","kind":"COMMA","start":[57,6,5],"end":[58,6,6]},{"type":"Token","kind":"WHITESPACE","start":[58,6,6],"end":[59,6,7]},{"type":"Token","kind":"STRING","start":[59,6,7],"end":[61,6,9]},{"type":"Token","kind":"R_PAREN","start":[61,6,9],"end":[62,6,10]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[62,6,10],"end":[63,6,11]}]},{"type":"Token","kind":"WHITESPACE","start":[63,6,11],"end":[64,7,0]},{"type":"Token","kind":"R_CURLY","start":[64,7,0],"end":[65,7,1]}]}]}]}]}"# ]], ); @@ -205,7 +234,7 @@ fn bar() { "#, ""); }"###, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":68,"children":[{"type":"Node","kind":"FN","start":0,"end":68,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":68,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":68,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":66,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":65,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":65,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":65,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":60,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":58,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":53,"end":58,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":60,"end":61},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"STRING","start":62,"end":64},{"type":"Token","kind":"R_PAREN","start":64,"end":65}]}]}]},{"type":"Token","kind":"SEMICOLON","start":65,"end":66}]},{"type":"Token","kind":"WHITESPACE","start":66,"end":67},{"type":"Token","kind":"R_CURLY","start":67,"end":68}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[68,7,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[68,7,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[68,7,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[68,7,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[66,6,12],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[65,6,11],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[65,6,11],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[65,6,11],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[60,6,6],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[31,2,3],"children":[{"type":"Token","kind":"WHITESPACE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[30,2,2],"end":[30,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[35,2,7],"end":[35,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[36,2,8],"end":[36,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[38,2,10],"end":[38,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[39,3,0],"end":[39,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[40,3,1],"end":[40,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[26,1,14],"children":[{"type":"Token","kind":"FN_KW","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[43,4,2],"end":[43,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[48,4,7],"end":[48,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[49,4,8],"end":[49,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Node","kind":"STMT_LIST","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Token","kind":"L_CURLY","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[51,4,10],"end":[51,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[52,5,0],"end":[52,5,0],"istart":[25,1,13],"iend":[26,1,14]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[53,5,1],"end":[53,5,1],"istart":[26,1,14],"iend":[31,2,3]}]}]},{"type":"Token","kind":"COMMA","start":[60,6,6],"end":[61,6,7]},{"type":"Token","kind":"WHITESPACE","start":[61,6,7],"end":[62,6,8]},{"type":"Token","kind":"STRING","start":[62,6,8],"end":[64,6,10]},{"type":"Token","kind":"R_PAREN","start":[64,6,10],"end":[65,6,11]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[65,6,11],"end":[66,6,12]}]},{"type":"Token","kind":"WHITESPACE","start":[66,6,12],"end":[67,7,0]},{"type":"Token","kind":"R_CURLY","start":[67,7,0],"end":[68,7,1]}]}]}]}]}"# ]], ); @@ -219,7 +248,7 @@ fn bar() { }"$0#, ""); }"###, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":63,"children":[{"type":"Node","kind":"FN","start":0,"end":63,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":63,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":63,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":61,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":60,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":60,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":60,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":55,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":53,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":55,"end":56},{"type":"Token","kind":"WHITESPACE","start":56,"end":57},{"type":"Token","kind":"STRING","start":57,"end":59},{"type":"Token","kind":"R_PAREN","start":59,"end":60}]}]}]},{"type":"Token","kind":"SEMICOLON","start":60,"end":61}]},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"R_CURLY","start":62,"end":63}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[63,6,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[63,6,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[63,6,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[63,6,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[61,5,9],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[60,5,8],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[60,5,8],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[60,5,8],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[55,5,3],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[26,1,14],"children":[{"type":"Token","kind":"WHITESPACE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[30,2,2],"end":[30,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[35,2,7],"end":[35,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[36,2,8],"end":[36,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[38,2,10],"end":[38,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[39,3,0],"end":[39,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[40,3,1],"end":[40,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[26,1,14],"children":[{"type":"Token","kind":"FN_KW","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[43,4,2],"end":[43,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[48,4,7],"end":[48,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[49,4,8],"end":[49,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Node","kind":"STMT_LIST","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Token","kind":"L_CURLY","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[51,4,10],"end":[51,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[52,5,0],"end":[52,5,0],"istart":[25,1,13],"iend":[26,1,14]}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":[55,5,3],"end":[56,5,4]},{"type":"Token","kind":"WHITESPACE","start":[56,5,4],"end":[57,5,5]},{"type":"Token","kind":"STRING","start":[57,5,5],"end":[59,5,7]},{"type":"Token","kind":"R_PAREN","start":[59,5,7],"end":[60,5,8]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[60,5,8],"end":[61,5,9]}]},{"type":"Token","kind":"WHITESPACE","start":[61,5,9],"end":[62,6,0]},{"type":"Token","kind":"R_CURLY","start":[62,6,0],"end":[63,6,1]}]}]}]}]}"# ]], ); } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index ae1c6efe0cb..66553a2661a 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -347,6 +347,7 @@ define_symbols! { option, Option, Ord, + Ordering, Output, CallRefFuture, CallOnceFuture, @@ -458,6 +459,8 @@ define_symbols! { system, sysv64, Target, + target_feature, + enable, termination, test_case, test, @@ -479,6 +482,7 @@ define_symbols! { u64, u8, unadjusted, + unknown, Unknown, unpin, unreachable_2015, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 389c01933c9..fe1316c9bfd 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -678,6 +678,8 @@ fn path_expr(p: &mut Parser<'_>, r: Restrictions) -> (CompletedMarker, BlockLike // S { x }; // S { x, y: 32, }; // S { x, y: 32, ..Default::default() }; +// S { x, y: 32, .. }; +// S { .. }; // S { x: ::default() }; // TupleStruct { 0: 1 }; // } @@ -709,6 +711,8 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // fn main() { // S { field ..S::default() } // S { 0 ..S::default() } + // S { field .. } + // S { 0 .. } // } name_ref_or_index(p); p.error("expected `:`"); @@ -739,7 +743,13 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // S { .. } = S {}; // } - // We permit `.. }` on the left-hand side of a destructuring assignment. + // test struct_initializer_with_defaults + // fn foo() { + // let _s = S { .. }; + // } + + // We permit `.. }` on the left-hand side of a destructuring assignment + // or defaults values. if !p.at(T!['}']) { expr(p); @@ -750,6 +760,12 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // S { ..x, a: 0 } // } + // test_err comma_after_default_values_syntax + // fn foo() { + // S { .., }; + // S { .., a: 0 } + // } + // Do not bump, so we can support additional fields after this comma. p.error("cannot use a comma after the base struct"); } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs index 21078175c0e..9a16c9db6da 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs @@ -135,6 +135,11 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) { name(p); p.expect(T![:]); types::type_(p); + // test record_field_default_values + // struct S { f: f32 = 0.0 } + if p.eat(T![=]) { + expressions::expr(p); + } m.complete(p, RECORD_FIELD); } else { m.abandon(p); diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 318f71a2d4d..79900425a17 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen grammar`, do not edit by hand. +//! Generated by `cargo xtask codegen grammar`, do not edit by hand. #![allow(bad_style, missing_docs, unreachable_pub)] use crate::Edition; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index b9f87b6af24..c8ea8c547a9 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -482,6 +482,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/record_field_attrs.rs"); } #[test] + fn record_field_default_values() { + run_and_expect_no_errors("test_data/parser/inline/ok/record_field_default_values.rs"); + } + #[test] fn record_field_list() { run_and_expect_no_errors("test_data/parser/inline/ok/record_field_list.rs"); } @@ -544,6 +548,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs"); } #[test] + fn struct_initializer_with_defaults() { + run_and_expect_no_errors("test_data/parser/inline/ok/struct_initializer_with_defaults.rs"); + } + #[test] fn struct_item() { run_and_expect_no_errors("test_data/parser/inline/ok/struct_item.rs"); } #[test] fn trait_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/trait_alias.rs"); } @@ -719,6 +727,10 @@ mod err { ); } #[test] + fn comma_after_default_values_syntax() { + run_and_expect_errors("test_data/parser/inline/err/comma_after_default_values_syntax.rs"); + } + #[test] fn crate_visibility_empty_recover() { run_and_expect_errors("test_data/parser/inline/err/crate_visibility_empty_recover.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast new file mode 100644 index 00000000000..feb617e1aa2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast @@ -0,0 +1,59 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + ERROR + COMMA "," + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + ERROR + COMMA "," + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "a" + COLON ":" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 21: expected expression +error 36: expected expression +error 37: expected COMMA diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs new file mode 100644 index 00000000000..f1ecdf89fab --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs @@ -0,0 +1,4 @@ +fn foo() { + S { .., }; + S { .., a: 0 } +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast index 08ae906421c..12b4e233e30 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast @@ -44,6 +44,56 @@ SOURCE_FILE WHITESPACE " " R_CURLY "}" WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + INT_NUMBER "0" + WHITESPACE " " + DOT2 ".." + CALL_EXPR + PATH_EXPR + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "default" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "field" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n " RECORD_EXPR PATH PATH_SEGMENT @@ -58,20 +108,6 @@ SOURCE_FILE INT_NUMBER "0" WHITESPACE " " DOT2 ".." - CALL_EXPR - PATH_EXPR - PATH - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "default" - ARG_LIST - L_PAREN "(" - R_PAREN ")" WHITESPACE " " R_CURLY "}" WHITESPACE "\n" @@ -82,3 +118,9 @@ error 25: expected COMMA error 42: expected SEMICOLON error 52: expected `:` error 52: expected COMMA +error 69: expected SEMICOLON +error 83: expected `:` +error 83: expected COMMA +error 88: expected SEMICOLON +error 98: expected `:` +error 98: expected COMMA diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs index 65398ccb88e..416cd763fdb 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs @@ -1,4 +1,6 @@ fn main() { S { field ..S::default() } S { 0 ..S::default() } + S { field .. } + S { 0 .. } } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast new file mode 100644 index 00000000000..33088f2cabf --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast @@ -0,0 +1,28 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "S" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_FIELD + NAME + IDENT "f" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "f32" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + FLOAT_NUMBER "0.0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs new file mode 100644 index 00000000000..d7b38944a8a --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs @@ -0,0 +1 @@ +struct S { f: f32 = 0.0 } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast index 00948c322f4..b868da55bce 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast @@ -131,6 +131,53 @@ SOURCE_FILE L_CURLY "{" WHITESPACE " " RECORD_EXPR_FIELD + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + COMMA "," + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "y" + COLON ":" + WHITESPACE " " + LITERAL + INT_NUMBER "32" + COMMA "," + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD NAME_REF IDENT "x" COLON ":" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs index 86411fbb7dc..42895f759b2 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs @@ -3,6 +3,8 @@ fn foo() { S { x }; S { x, y: 32, }; S { x, y: 32, ..Default::default() }; + S { x, y: 32, .. }; + S { .. }; S { x: ::default() }; TupleStruct { 0: 1 }; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast new file mode 100644 index 00000000000..987e219ae82 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast @@ -0,0 +1,39 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "_s" + WHITESPACE " " + EQ "=" + WHITESPACE " " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs new file mode 100644 index 00000000000..e08204f94c4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs @@ -0,0 +1,3 @@ +fn foo() { + let _s = S { .. }; +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index ba1fcd8e336..569070766f1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -21,18 +21,16 @@ pub(crate) fn run() -> io::Result<()> { } } - let read_request = - |buf: &mut String| msg::Request::read(read_json, &mut io::stdin().lock(), buf); - + let mut buf = String::new(); + let mut read_request = || msg::Request::read(read_json, &mut io::stdin().lock(), &mut buf); let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock()); let env = EnvSnapshot::default(); - let mut srv = proc_macro_srv::ProcMacroSrv::new(&env); - let mut buf = String::new(); + let srv = proc_macro_srv::ProcMacroSrv::new(&env); let mut span_mode = SpanMode::Id; - while let Some(req) = read_request(&mut buf)? { + while let Some(req) = read_request()? { let res = match req { msg::Request::ListMacros { dylib_path } => { msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 7ae75713ebf..f28821b4afc 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -35,6 +35,7 @@ use std::{ ffi::OsString, fs, path::{Path, PathBuf}, + sync::{Arc, Mutex, PoisonError}, thread, }; @@ -53,7 +54,7 @@ pub enum ProcMacroKind { pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION"); pub struct ProcMacroSrv<'env> { - expanders: HashMap<Utf8PathBuf, dylib::Expander>, + expanders: Mutex<HashMap<Utf8PathBuf, Arc<dylib::Expander>>>, env: &'env EnvSnapshot, } @@ -67,7 +68,7 @@ const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; impl ProcMacroSrv<'_> { pub fn expand<S: ProcMacroSrvSpan>( - &mut self, + &self, lib: impl AsRef<Utf8Path>, env: Vec<(String, String)>, current_dir: Option<impl AsRef<Path>>, @@ -118,29 +119,37 @@ impl ProcMacroSrv<'_> { } pub fn list_macros( - &mut self, + &self, dylib_path: &Utf8Path, ) -> Result<Vec<(String, ProcMacroKind)>, String> { let expander = self.expander(dylib_path)?; Ok(expander.list_macros()) } - fn expander(&mut self, path: &Utf8Path) -> Result<&dylib::Expander, String> { + fn expander(&self, path: &Utf8Path) -> Result<Arc<dylib::Expander>, String> { let expander = || { - dylib::Expander::new(path) - .map_err(|err| format!("Cannot create expander for {path}: {err}",)) + let expander = dylib::Expander::new(path) + .map_err(|err| format!("Cannot create expander for {path}: {err}",)); + expander.map(Arc::new) }; - Ok(match self.expanders.entry(path.to_path_buf()) { - Entry::Vacant(v) => v.insert(expander()?), - Entry::Occupied(mut e) => { - let time = fs::metadata(path).and_then(|it| it.modified()).ok(); - if Some(e.get().modified_time()) != time { - e.insert(expander()?); + Ok( + match self + .expanders + .lock() + .unwrap_or_else(PoisonError::into_inner) + .entry(path.to_path_buf()) + { + Entry::Vacant(v) => v.insert(expander()?).clone(), + Entry::Occupied(mut e) => { + let time = fs::metadata(path).and_then(|it| it.modified()).ok(); + if Some(e.get().modified_time()) != time { + e.insert(expander()?); + } + e.get().clone() } - e.into_mut() - } - }) + }, + ) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 4ce4544243a..1b085520d56 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -107,7 +107,7 @@ fn assert_expand_impl( pub(crate) fn list() -> Vec<String> { let dylib_path = proc_macro_test_dylib_path(); let env = EnvSnapshot::default(); - let mut srv = ProcMacroSrv::new(&env); + let srv = ProcMacroSrv::new(&env); let res = srv.list_macros(&dylib_path).unwrap(); res.into_iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect() } diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index a3963967610..feee40a1fc9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -508,5 +508,5 @@ fn serialize_crate_name<S>(name: &CrateName, se: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { - se.serialize_str(name) + se.serialize_str(name.as_str()) } diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index f1113831125..28560865436 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -230,7 +230,7 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() { let crate_data = &crate_graph[crate_id]; // Assert that the project crate with `is_proc_macro` has a dependency // on the proc_macro sysroot crate. - crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap(); + crate_data.dependencies.iter().find(|&dep| *dep.name.deref() == sym::proc_macro).unwrap(); } #[test] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index c24cbb4a311..b8ce2b7430b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -37,7 +37,7 @@ rustc-hash.workspace = true serde_json = { workspace = true, features = ["preserve_order"] } serde.workspace = true serde_derive.workspace = true -tenthash = "0.4.0" +tenthash = "1.0.0" num_cpus = "1.15.0" mimalloc = { version = "0.1.30", default-features = false, optional = true } lsp-server.workspace = true diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 18c27c84496..4fc6180920f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -13,7 +13,7 @@ use hir::{ ModuleDef, Name, }; use hir_def::{ - body::BodySourceMap, + expr_store::BodySourceMap, hir::{ExprId, PatId}, SyntheticSyntax, }; @@ -1072,6 +1072,7 @@ impl flags::AnalysisStats { param_names_for_lifetime_elision_hints: true, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, + hide_closure_parameter_hints: false, closure_style: hir::ClosureStyle::ImplFn, max_length: Some(25), closing_brace_hints_min_lines: Some(20), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index 28f25975d64..6a3ceb640b9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -4,7 +4,7 @@ use project_model::{CargoConfig, RustLibSource}; use rustc_hash::FxHashSet; -use hir::{db::HirDatabase, Crate, HirFileIdExt, Module}; +use hir::{db::HirDatabase, sym, Crate, HirFileIdExt, Module}; use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity}; use ide_db::{base_db::SourceRootDatabase, LineIndexDatabase}; use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; @@ -60,7 +60,7 @@ impl flags::Diagnostics { let file_id = module.definition_source_file_id(db).original_file(db); if !visited_files.contains(&file_id) { let crate_name = - module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned(); + module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned(); println!( "processing crate: {crate_name}, module: {}", _vfs.file_path(file_id.into()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index dc0f722aae6..fe75872105a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -139,45 +139,42 @@ impl flags::Scip { let mut occurrences = Vec::new(); let mut symbols = Vec::new(); - tokens.into_iter().for_each(|(text_range, id)| { + for (text_range, id) in tokens.into_iter() { let token = si.tokens.get(id).unwrap(); - let (symbol, enclosing_symbol, is_inherent_impl) = - if let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) = - symbol_generator.token_symbols(id, token) - { - (symbol, enclosing_symbol, is_inherent_impl) - } else { - ("".to_owned(), None, false) - }; + let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) = + symbol_generator.token_symbols(id, token) + else { + // token did not have a moniker, so there is no reasonable occurrence to emit + // see ide::moniker::def_to_moniker + continue; + }; - if !symbol.is_empty() { - let is_defined_in_this_document = match token.definition { - Some(def) => def.file_id == file_id, - _ => false, - }; - if is_defined_in_this_document { - if token_ids_emitted.insert(id) { - // token_ids_emitted does deduplication. This checks that this results - // in unique emitted symbols, as otherwise references are ambiguous. - let should_emit = record_error_if_symbol_already_used( + let is_defined_in_this_document = match token.definition { + Some(def) => def.file_id == file_id, + _ => false, + }; + if is_defined_in_this_document { + if token_ids_emitted.insert(id) { + // token_ids_emitted does deduplication. This checks that this results + // in unique emitted symbols, as otherwise references are ambiguous. + let should_emit = record_error_if_symbol_already_used( + symbol.clone(), + is_inherent_impl, + relative_path.as_str(), + &line_index, + text_range, + ); + if should_emit { + symbols.push(compute_symbol_info( symbol.clone(), - is_inherent_impl, - relative_path.as_str(), - &line_index, - text_range, - ); - if should_emit { - symbols.push(compute_symbol_info( - symbol.clone(), - enclosing_symbol, - token, - )); - } + enclosing_symbol, + token, + )); } - } else { - token_ids_referenced.insert(id); } + } else { + token_ids_referenced.insert(id); } // If the range of the def and the range of the token are the same, this must be the definition. @@ -202,7 +199,7 @@ impl flags::Scip { special_fields: Default::default(), enclosing_range: Vec::new(), }); - }); + } if occurrences.is_empty() { continue; @@ -444,14 +441,14 @@ impl SymbolGenerator { MonikerResult::Moniker(moniker) => TokenSymbols { symbol: scip::symbol::format_symbol(moniker_to_symbol(moniker)), enclosing_symbol: None, - is_inherent_impl: moniker - .identifier - .description - .get(moniker.identifier.description.len() - 2) - .is_some_and(|descriptor| { + is_inherent_impl: match &moniker.identifier.description[..] { + // inherent impls are represented as impl#[SelfType] + [.., descriptor, _] => { descriptor.desc == MonikerDescriptorKind::Type && descriptor.name == "impl" - }), + } + _ => false, + }, }, MonikerResult::Local { enclosing_moniker } => { let local_symbol = scip::types::Symbol::new_local(local_count); @@ -549,7 +546,9 @@ mod test { continue; } for &(range, id) in &file.tokens { - if range.contains(offset - TextSize::from(1)) { + // check if cursor is within token, ignoring token for the module defined by the file (whose range is the whole file) + if range.start() != TextSize::from(0) && range.contains(offset - TextSize::from(1)) + { let token = si.tokens.get(id).unwrap(); found_symbol = match token.moniker.as_ref() { None => None, @@ -885,7 +884,7 @@ pub mod example_mod { ); let file = si.files.first().unwrap(); - let (_, token_id) = file.tokens.first().unwrap(); + let (_, token_id) = file.tokens.get(1).unwrap(); // first token is file module, second is `bar` let token = si.tokens.get(*token_id).unwrap(); assert_eq!(token.documentation.as_ref().map(|d| d.as_str()), Some("foo")); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs index 986bd018b42..021b1bff393 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -1,5 +1,5 @@ //! Reports references in code that the IDE layer cannot resolve. -use hir::{db::HirDatabase, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics}; +use hir::{db::HirDatabase, sym, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics}; use ide::{AnalysisHost, RootDatabase, TextRange}; use ide_db::{ base_db::{SourceDatabase, SourceRootDatabase}, @@ -66,7 +66,7 @@ impl flags::UnresolvedReferences { let file_id = module.definition_source_file_id(db).original_file(db); if !visited_files.contains(&file_id) { let crate_name = - module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned(); + module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned(); let file_path = vfs.file_path(file_id.into()); eprintln!("processing crate: {crate_name}, module: {file_path}",); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 44325fa1a29..e915e55722b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -208,6 +208,8 @@ config_data! { /// Whether to hide inlay type hints for `let` statements that initialize to a closure. /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. inlayHints_typeHints_hideClosureInitialization: bool = false, + /// Whether to hide inlay parameter type hints for closures. + inlayHints_typeHints_hideClosureParameter:bool = false, /// Whether to hide inlay type hints for constructors. inlayHints_typeHints_hideNamedConstructor: bool = false, @@ -528,7 +530,7 @@ config_data! { imports_granularity_enforce: bool = false, /// How imports should be grouped into use statements. imports_granularity_group: ImportGranularityDef = ImportGranularityDef::Crate, - /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines. + /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. imports_group_enable: bool = true, /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. imports_merge_glob: bool = true, @@ -1666,6 +1668,9 @@ impl Config { hide_closure_initialization_hints: self .inlayHints_typeHints_hideClosureInitialization() .to_owned(), + hide_closure_parameter_hints: self + .inlayHints_typeHints_hideClosureParameter() + .to_owned(), closure_style: match self.inlayHints_closureStyle() { ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn, ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation, @@ -3624,21 +3629,9 @@ fn manual(fields: &[SchemaField]) -> String { let name = format!("rust-analyzer.{}", field.replace('_', ".")); let doc = doc_comment_to_string(doc); if default.contains('\n') { - format_to_acc!( - acc, - r#"[[{name}]]{name}:: -+ --- -Default: ----- -{default} ----- -{doc} --- -"# - ) + format_to_acc!(acc, " **{name}**\n\nDefault:\n\n```{default}\n\n```\n\n {doc}\n\n ") } else { - format_to_acc!(acc, "[[{name}]]{name} (default: `{default}`)::\n+\n--\n{doc}--\n") + format_to_acc!(acc, "**{name}** (default: {default})\n\n {doc}\n\n") } }) } @@ -3716,7 +3709,7 @@ mod tests { #[test] fn generate_config_documentation() { - let docs_path = project_root().join("docs/user/generated_config.adoc"); + let docs_path = project_root().join("docs/book/src/configuration_generated.md"); let expected = FullConfigInput::manual(); ensure_file_contents(docs_path.as_std_path(), &expected); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 0f2d7823b7e..b52f64aaace 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -396,6 +396,7 @@ impl GlobalState { || !self.config.same_source_root_parent_map(&self.local_roots_parent_map) { let config_change = { + let _p = span!(Level::INFO, "GlobalState::process_changes/config_change").entered(); let user_config_path = (|| { let mut p = Config::user_config_dir_path()?; p.push("rust-analyzer.toml"); @@ -569,12 +570,12 @@ impl GlobalState { if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) { if let Some(err) = &response.error { if err.message.starts_with("server panicked") { - self.poke_rust_analyzer_developer(format!("{}, check the log", err.message)) + self.poke_rust_analyzer_developer(format!("{}, check the log", err.message)); } } let duration = start.elapsed(); - tracing::debug!("handled {} - ({}) in {:0.2?}", method, response.id, duration); + tracing::debug!(name: "message response", method, %response.id, duration = format_args!("{:0.2?}", duration)); self.send(response.into()); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index ff50f7533a6..4683877db69 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -118,7 +118,7 @@ impl RequestDispatcher<'_> { } return self; } - self.on_with_thread_intent::<true, ALLOW_RETRYING, R>( + self.on_with_thread_intent::<false, ALLOW_RETRYING, R>( ThreadIntent::Worker, f, Self::content_modified_error, @@ -147,7 +147,7 @@ impl RequestDispatcher<'_> { } return self; } - self.on_with_thread_intent::<true, false, R>(ThreadIntent::Worker, f, on_cancelled) + self.on_with_thread_intent::<false, false, R>(ThreadIntent::Worker, f, on_cancelled) } /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not @@ -166,7 +166,7 @@ impl RequestDispatcher<'_> { } return self; } - self.on_with_thread_intent::<true, ALLOW_RETRYING, R>( + self.on_with_thread_intent::<false, ALLOW_RETRYING, R>( ThreadIntent::Worker, f, Self::content_modified_error, @@ -193,7 +193,7 @@ impl RequestDispatcher<'_> { } return self; } - self.on_with_thread_intent::<true, ALLOW_RETRYING, R>( + self.on_with_thread_intent::<false, ALLOW_RETRYING, R>( ThreadIntent::LatencySensitive, f, Self::content_modified_error, @@ -212,7 +212,7 @@ impl RequestDispatcher<'_> { R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, R::Result: Serialize, { - self.on_with_thread_intent::<false, false, R>( + self.on_with_thread_intent::<true, false, R>( ThreadIntent::LatencySensitive, f, Self::content_modified_error, @@ -231,7 +231,7 @@ impl RequestDispatcher<'_> { } } - fn on_with_thread_intent<const MAIN_POOL: bool, const ALLOW_RETRYING: bool, R>( + fn on_with_thread_intent<const RUSTFMT: bool, const ALLOW_RETRYING: bool, R>( &mut self, intent: ThreadIntent, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>, @@ -251,10 +251,10 @@ impl RequestDispatcher<'_> { tracing::debug!(?params); let world = self.global_state.snapshot(); - if MAIN_POOL { - &mut self.global_state.task_pool.handle - } else { + if RUSTFMT { &mut self.global_state.fmt_pool.handle + } else { + &mut self.global_state.task_pool.handle } .spawn(intent, move || { let result = panic::catch_unwind(move || { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 84ba89d9f31..48856d19e15 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -74,7 +74,8 @@ pub(crate) fn handle_did_open_text_document( tracing::error!("duplicate DidOpenTextDocument: {}", path); } - state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes())); + let contents = params.text_document.text.into_bytes(); + state.vfs.write().0.set_file_contents(path, Some(contents)); if state.config.discover_workspace_config().is_some() { tracing::debug!("queuing task"); let _ = state diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 39cbf53eaa2..ed028f1d37b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -2318,18 +2318,21 @@ fn run_rustfmt( } }; - tracing::debug!(?command, "created format command"); + let output = { + let _p = tracing::info_span!("rustfmt", ?command).entered(); - let mut rustfmt = command - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .context(format!("Failed to spawn {command:?}"))?; + let mut rustfmt = command + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context(format!("Failed to spawn {command:?}"))?; - rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; + rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; + + rustfmt.wait_with_output()? + }; - let output = rustfmt.wait_with_output()?; let captured_stdout = String::from_utf8(output.stdout)?; let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index ccffa7a671e..1221f7c7012 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -50,7 +50,7 @@ mod integrated_benchmarks; use hir::Mutability; use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance}; use serde::de::DeserializeOwned; -use tenthash::TentHasher; +use tenthash::TentHash; pub use crate::{ lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph, @@ -66,7 +66,7 @@ pub fn from_json<T: DeserializeOwned>( } fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] { - fn hash_completion_relevance(hasher: &mut TentHasher, relevance: &CompletionRelevance) { + fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) { use ide_completion::{ CompletionRelevancePostfixMatch, CompletionRelevanceReturnType, CompletionRelevanceTypeMatch, @@ -79,71 +79,97 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; u8::from(relevance.requires_import), u8::from(relevance.is_private_editable), ]); - if let Some(type_match) = &relevance.type_match { - let label = match type_match { - CompletionRelevanceTypeMatch::CouldUnify => "could_unify", - CompletionRelevanceTypeMatch::Exact => "exact", - }; - hasher.update(label); + + match relevance.type_match { + None => hasher.update([0u8]), + Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]), + Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]), } + + hasher.update([u8::from(relevance.trait_.is_some())]); if let Some(trait_) = &relevance.trait_ { hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]); } - if let Some(postfix_match) = &relevance.postfix_match { - let label = match postfix_match { - CompletionRelevancePostfixMatch::NonExact => "non_exact", - CompletionRelevancePostfixMatch::Exact => "exact", - }; - hasher.update(label); + + match relevance.postfix_match { + None => hasher.update([0u8]), + Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]), + Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]), } + + hasher.update([u8::from(relevance.function.is_some())]); if let Some(function) = &relevance.function { hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]); - let label = match function.return_type { - CompletionRelevanceReturnType::Other => "other", - CompletionRelevanceReturnType::DirectConstructor => "direct_constructor", - CompletionRelevanceReturnType::Constructor => "constructor", - CompletionRelevanceReturnType::Builder => "builder", + let discriminant: u8 = match function.return_type { + CompletionRelevanceReturnType::Other => 0, + CompletionRelevanceReturnType::DirectConstructor => 1, + CompletionRelevanceReturnType::Constructor => 2, + CompletionRelevanceReturnType::Builder => 3, }; - hasher.update(label); + hasher.update([discriminant]); } } - let mut hasher = TentHasher::new(); + let mut hasher = TentHash::new(); hasher.update([ u8::from(is_ref_completion), u8::from(item.is_snippet), u8::from(item.deprecated), u8::from(item.trigger_call_info), ]); + + hasher.update(item.label.primary.len().to_ne_bytes()); hasher.update(&item.label.primary); + + hasher.update([u8::from(item.label.detail_left.is_some())]); if let Some(label_detail) = &item.label.detail_left { + hasher.update(label_detail.len().to_ne_bytes()); hasher.update(label_detail); } + + hasher.update([u8::from(item.label.detail_right.is_some())]); if let Some(label_detail) = &item.label.detail_right { + hasher.update(label_detail.len().to_ne_bytes()); hasher.update(label_detail); } + // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request // and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different. // // Documentation hashing is skipped too, as it's a large blob to process, // while not really making completion properties more unique as they are already. - hasher.update(item.kind.tag()); + + let kind_tag = item.kind.tag(); + hasher.update(kind_tag.len().to_ne_bytes()); + hasher.update(kind_tag); + + hasher.update(item.lookup.len().to_ne_bytes()); hasher.update(&item.lookup); + + hasher.update([u8::from(item.detail.is_some())]); if let Some(detail) = &item.detail { + hasher.update(detail.len().to_ne_bytes()); hasher.update(detail); } + hash_completion_relevance(&mut hasher, &item.relevance); + + hasher.update([u8::from(item.ref_match.is_some())]); if let Some((ref_mode, text_size)) = &item.ref_match { - let prefix = match ref_mode { - CompletionItemRefMode::Reference(Mutability::Shared) => "&", - CompletionItemRefMode::Reference(Mutability::Mut) => "&mut ", - CompletionItemRefMode::Dereference => "*", + let discriminant = match ref_mode { + CompletionItemRefMode::Reference(Mutability::Shared) => 0u8, + CompletionItemRefMode::Reference(Mutability::Mut) => 1u8, + CompletionItemRefMode::Dereference => 2u8, }; - hasher.update(prefix); - hasher.update(u32::from(*text_size).to_le_bytes()); + hasher.update([discriminant]); + hasher.update(u32::from(*text_size).to_ne_bytes()); } + + hasher.update(item.import_to_add.len().to_ne_bytes()); for import_path in &item.import_to_add { + hasher.update(import_path.len().to_ne_bytes()); hasher.update(import_path); } + hasher.finalize() } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs index 991c10743f7..3c21e199252 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs @@ -24,7 +24,7 @@ macro_rules! define_semantic_token_types { } pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[ - $(SemanticTokenType::$standard,)* + $(self::types::$standard,)* $(self::types::$custom),* ]; @@ -32,7 +32,7 @@ macro_rules! define_semantic_token_types { use self::types::*; $( if token == $custom { - None $(.or(Some(SemanticTokenType::$fallback)))? + None $(.or(Some(self::types::$fallback)))? } else )* { Some(token )} @@ -60,6 +60,7 @@ define_semantic_token_types![ STRUCT, TYPE_PARAMETER, VARIABLE, + TYPE, } custom { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index d6dc8b521fd..ebc65373b52 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -253,6 +253,11 @@ impl GlobalState { &self, inbox: &Receiver<lsp_server::Message>, ) -> Result<Option<Event>, crossbeam_channel::RecvError> { + // Make sure we reply to formatting requests ASAP so the editor doesn't block + if let Ok(task) = self.fmt_pool.receiver.try_recv() { + return Ok(Some(Event::Task(task))); + } + select! { recv(inbox) -> msg => return Ok(msg.ok().map(Event::Lsp)), @@ -320,26 +325,30 @@ impl GlobalState { } for progress in prime_caches_progress { - let (state, message, fraction); + let (state, message, fraction, title); match progress { PrimeCachesProgress::Begin => { state = Progress::Begin; message = None; fraction = 0.0; + title = "Indexing"; } PrimeCachesProgress::Report(report) => { state = Progress::Report; + title = report.work_type; - message = match &report.crates_currently_indexing[..] { + message = match &*report.crates_currently_indexing { [crate_name] => Some(format!( - "{}/{} ({crate_name})", - report.crates_done, report.crates_total + "{}/{} ({})", + report.crates_done, + report.crates_total, + crate_name.as_str(), )), [crate_name, rest @ ..] => Some(format!( "{}/{} ({} + {} more)", report.crates_done, report.crates_total, - crate_name, + crate_name.as_str(), rest.len() )), _ => None, @@ -351,6 +360,7 @@ impl GlobalState { state = Progress::End; message = None; fraction = 1.0; + title = "Indexing"; self.prime_caches_queue.op_completed(()); if cancelled { @@ -360,7 +370,13 @@ impl GlobalState { } }; - self.report_progress("Indexing", state, message, Some(fraction), None); + self.report_progress( + title, + state, + message, + Some(fraction), + Some("rustAnalyzer/cachePriming".to_owned()), + ); } } Event::Vfs(message) => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 0add2cdf5a7..d18e5770477 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -701,8 +701,7 @@ impl GlobalState { let (crate_graph, proc_macro_paths, ws_data) = { // Create crate graph from all the workspaces - let vfs = &mut self.vfs.write().0; - + let vfs = &self.vfs.read().0; let load = |path: &AbsPath| { let vfs_path = vfs::VfsPath::from(path.to_path_buf()); self.crate_graph_file_dependencies.insert(vfs_path.clone()); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs index 2bcd8505e81..c5de69bb9fc 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs @@ -1,6 +1,8 @@ //! A thin wrapper around [`stdx::thread::Pool`] which threads a sender through spawned jobs. //! It is used in [`crate::global_state::GlobalState`] throughout the main loop. +use std::panic::UnwindSafe; + use crossbeam_channel::Sender; use stdx::thread::{Pool, ThreadIntent}; @@ -18,7 +20,7 @@ impl<T> TaskPool<T> { pub(crate) fn spawn<F>(&mut self, intent: ThreadIntent, task: F) where - F: FnOnce() -> T + Send + 'static, + F: FnOnce() -> T + Send + UnwindSafe + 'static, T: Send + 'static, { self.pool.spawn(intent, { @@ -29,7 +31,7 @@ impl<T> TaskPool<T> { pub(crate) fn spawn_with_sender<F>(&mut self, intent: ThreadIntent, task: F) where - F: FnOnce(Sender<T>) + Send + 'static, + F: FnOnce(Sender<T>) + Send + UnwindSafe + 'static, T: Send + 'static, { self.pool.spawn(intent, { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs index fba54666912..4ef930e9854 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs @@ -43,89 +43,93 @@ mod tests { expect![[r#" {"id":2,"type":"vertex","label":"foldingRangeResult","result":[{"startLine":2,"startCharacter":43,"endLine":6,"endCharacter":1},{"startLine":3,"startCharacter":19,"endLine":5,"endCharacter":5},{"startLine":9,"startCharacter":10,"endLine":12,"endCharacter":1}]} {"id":3,"type":"edge","label":"textDocument/foldingRange","inV":2,"outV":1} - {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}} + {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}} {"id":5,"type":"vertex","label":"resultSet"} {"id":6,"type":"edge","label":"next","inV":5,"outV":4} - {"id":7,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}} + {"id":7,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}} {"id":8,"type":"vertex","label":"resultSet"} {"id":9,"type":"edge","label":"next","inV":8,"outV":7} - {"id":10,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}} - {"id":11,"type":"edge","label":"next","inV":8,"outV":10} - {"id":12,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}} - {"id":13,"type":"vertex","label":"resultSet"} - {"id":14,"type":"edge","label":"next","inV":13,"outV":12} - {"id":15,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}} + {"id":10,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}} + {"id":11,"type":"vertex","label":"resultSet"} + {"id":12,"type":"edge","label":"next","inV":11,"outV":10} + {"id":13,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}} + {"id":14,"type":"edge","label":"next","inV":11,"outV":13} + {"id":15,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}} {"id":16,"type":"vertex","label":"resultSet"} {"id":17,"type":"edge","label":"next","inV":16,"outV":15} - {"id":18,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}} + {"id":18,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}} {"id":19,"type":"vertex","label":"resultSet"} {"id":20,"type":"edge","label":"next","inV":19,"outV":18} - {"id":21,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}} - {"id":22,"type":"edge","label":"next","inV":8,"outV":21} - {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}} - {"id":24,"type":"vertex","label":"resultSet"} - {"id":25,"type":"edge","label":"next","inV":24,"outV":23} - {"id":26,"type":"edge","label":"contains","inVs":[4,7,10,12,15,18,21,23],"outV":1} - {"id":27,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}} - {"id":28,"type":"edge","label":"textDocument/hover","inV":27,"outV":5} - {"id":29,"type":"vertex","label":"referenceResult"} - {"id":30,"type":"edge","label":"textDocument/references","inV":29,"outV":5} - {"id":31,"type":"edge","label":"item","document":1,"property":"references","inVs":[4],"outV":29} - {"id":32,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}} - {"id":33,"type":"edge","label":"textDocument/hover","inV":32,"outV":8} - {"id":34,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"} - {"id":35,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"} - {"id":36,"type":"edge","label":"packageInformation","inV":34,"outV":35} - {"id":37,"type":"edge","label":"moniker","inV":35,"outV":8} - {"id":38,"type":"vertex","label":"definitionResult"} - {"id":39,"type":"edge","label":"item","document":1,"inVs":[7],"outV":38} - {"id":40,"type":"edge","label":"textDocument/definition","inV":38,"outV":8} - {"id":41,"type":"vertex","label":"referenceResult"} - {"id":42,"type":"edge","label":"textDocument/references","inV":41,"outV":8} - {"id":43,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[7],"outV":41} - {"id":44,"type":"edge","label":"item","document":1,"property":"references","inVs":[10,21],"outV":41} - {"id":45,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}} - {"id":46,"type":"edge","label":"textDocument/hover","inV":45,"outV":13} - {"id":47,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"} - {"id":48,"type":"edge","label":"packageInformation","inV":34,"outV":47} - {"id":49,"type":"edge","label":"moniker","inV":47,"outV":13} - {"id":50,"type":"vertex","label":"definitionResult"} - {"id":51,"type":"edge","label":"item","document":1,"inVs":[12],"outV":50} - {"id":52,"type":"edge","label":"textDocument/definition","inV":50,"outV":13} - {"id":53,"type":"vertex","label":"referenceResult"} - {"id":54,"type":"edge","label":"textDocument/references","inV":53,"outV":13} - {"id":55,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[12],"outV":53} - {"id":56,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}} - {"id":57,"type":"edge","label":"textDocument/hover","inV":56,"outV":16} - {"id":58,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"} - {"id":59,"type":"edge","label":"packageInformation","inV":34,"outV":58} - {"id":60,"type":"edge","label":"moniker","inV":58,"outV":16} - {"id":61,"type":"vertex","label":"definitionResult"} - {"id":62,"type":"edge","label":"item","document":1,"inVs":[15],"outV":61} - {"id":63,"type":"edge","label":"textDocument/definition","inV":61,"outV":16} - {"id":64,"type":"vertex","label":"referenceResult"} - {"id":65,"type":"edge","label":"textDocument/references","inV":64,"outV":16} - {"id":66,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":64} - {"id":67,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}} - {"id":68,"type":"edge","label":"textDocument/hover","inV":67,"outV":19} - {"id":69,"type":"vertex","label":"definitionResult"} - {"id":70,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}} - {"id":71,"type":"edge","label":"contains","inVs":[70],"outV":1} - {"id":72,"type":"edge","label":"item","document":1,"inVs":[70],"outV":69} - {"id":73,"type":"edge","label":"textDocument/definition","inV":69,"outV":19} - {"id":74,"type":"vertex","label":"referenceResult"} - {"id":75,"type":"edge","label":"textDocument/references","inV":74,"outV":19} - {"id":76,"type":"edge","label":"item","document":1,"property":"references","inVs":[18],"outV":74} - {"id":77,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}} - {"id":78,"type":"edge","label":"textDocument/hover","inV":77,"outV":24} - {"id":79,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"} - {"id":80,"type":"edge","label":"packageInformation","inV":34,"outV":79} - {"id":81,"type":"edge","label":"moniker","inV":79,"outV":24} - {"id":82,"type":"vertex","label":"definitionResult"} - {"id":83,"type":"edge","label":"item","document":1,"inVs":[23],"outV":82} - {"id":84,"type":"edge","label":"textDocument/definition","inV":82,"outV":24} - {"id":85,"type":"vertex","label":"referenceResult"} - {"id":86,"type":"edge","label":"textDocument/references","inV":85,"outV":24} - {"id":87,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[23],"outV":85} + {"id":21,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}} + {"id":22,"type":"edge","label":"next","inV":5,"outV":21} + {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}} + {"id":24,"type":"edge","label":"next","inV":11,"outV":23} + {"id":25,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}} + {"id":26,"type":"vertex","label":"resultSet"} + {"id":27,"type":"edge","label":"next","inV":26,"outV":25} + {"id":28,"type":"edge","label":"contains","inVs":[4,7,10,13,15,18,21,23,25],"outV":1} + {"id":29,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}} + {"id":30,"type":"edge","label":"textDocument/hover","inV":29,"outV":5} + {"id":31,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"} + {"id":32,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::crate","unique":"scheme","kind":"export"} + {"id":33,"type":"edge","label":"packageInformation","inV":31,"outV":32} + {"id":34,"type":"edge","label":"moniker","inV":32,"outV":5} + {"id":35,"type":"vertex","label":"definitionResult"} + {"id":36,"type":"edge","label":"item","document":1,"inVs":[4],"outV":35} + {"id":37,"type":"edge","label":"textDocument/definition","inV":35,"outV":5} + {"id":38,"type":"vertex","label":"referenceResult"} + {"id":39,"type":"edge","label":"textDocument/references","inV":38,"outV":5} + {"id":40,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[4],"outV":38} + {"id":41,"type":"edge","label":"item","document":1,"property":"references","inVs":[21],"outV":38} + {"id":42,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}} + {"id":43,"type":"edge","label":"textDocument/hover","inV":42,"outV":8} + {"id":44,"type":"vertex","label":"referenceResult"} + {"id":45,"type":"edge","label":"textDocument/references","inV":44,"outV":8} + {"id":46,"type":"edge","label":"item","document":1,"property":"references","inVs":[7],"outV":44} + {"id":47,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}} + {"id":48,"type":"edge","label":"textDocument/hover","inV":47,"outV":11} + {"id":49,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"} + {"id":50,"type":"edge","label":"packageInformation","inV":31,"outV":49} + {"id":51,"type":"edge","label":"moniker","inV":49,"outV":11} + {"id":52,"type":"vertex","label":"definitionResult"} + {"id":53,"type":"edge","label":"item","document":1,"inVs":[10],"outV":52} + {"id":54,"type":"edge","label":"textDocument/definition","inV":52,"outV":11} + {"id":55,"type":"vertex","label":"referenceResult"} + {"id":56,"type":"edge","label":"textDocument/references","inV":55,"outV":11} + {"id":57,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[10],"outV":55} + {"id":58,"type":"edge","label":"item","document":1,"property":"references","inVs":[13,23],"outV":55} + {"id":59,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}} + {"id":60,"type":"edge","label":"textDocument/hover","inV":59,"outV":16} + {"id":61,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"} + {"id":62,"type":"edge","label":"packageInformation","inV":31,"outV":61} + {"id":63,"type":"edge","label":"moniker","inV":61,"outV":16} + {"id":64,"type":"vertex","label":"definitionResult"} + {"id":65,"type":"edge","label":"item","document":1,"inVs":[15],"outV":64} + {"id":66,"type":"edge","label":"textDocument/definition","inV":64,"outV":16} + {"id":67,"type":"vertex","label":"referenceResult"} + {"id":68,"type":"edge","label":"textDocument/references","inV":67,"outV":16} + {"id":69,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":67} + {"id":70,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}} + {"id":71,"type":"edge","label":"textDocument/hover","inV":70,"outV":19} + {"id":72,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"} + {"id":73,"type":"edge","label":"packageInformation","inV":31,"outV":72} + {"id":74,"type":"edge","label":"moniker","inV":72,"outV":19} + {"id":75,"type":"vertex","label":"definitionResult"} + {"id":76,"type":"edge","label":"item","document":1,"inVs":[18],"outV":75} + {"id":77,"type":"edge","label":"textDocument/definition","inV":75,"outV":19} + {"id":78,"type":"vertex","label":"referenceResult"} + {"id":79,"type":"edge","label":"textDocument/references","inV":78,"outV":19} + {"id":80,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[18],"outV":78} + {"id":81,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}} + {"id":82,"type":"edge","label":"textDocument/hover","inV":81,"outV":26} + {"id":83,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"} + {"id":84,"type":"edge","label":"packageInformation","inV":31,"outV":83} + {"id":85,"type":"edge","label":"moniker","inV":83,"outV":26} + {"id":86,"type":"vertex","label":"definitionResult"} + {"id":87,"type":"edge","label":"item","document":1,"inVs":[25],"outV":86} + {"id":88,"type":"edge","label":"textDocument/definition","inV":86,"outV":26} + {"id":89,"type":"vertex","label":"referenceResult"} + {"id":90,"type":"edge","label":"textDocument/references","inV":89,"outV":26} + {"id":91,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[25],"outV":89} "#]].assert_eq(stdout); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs index d113bd51278..409be2894fe 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs @@ -43,10 +43,15 @@ impl TestDir { } fs::create_dir_all(&path).unwrap(); - #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] + #[cfg(any( + target_os = "macos", + target_os = "linux", + target_os = "windows", + target_os = "freebsd" + ))] if symlink { let symlink_path = base.join(format!("{pid}_{cnt}_symlink")); - #[cfg(any(target_os = "macos", target_os = "linux"))] + #[cfg(any(target_os = "macos", target_os = "linux", target_os = "freebsd"))] std::os::unix::fs::symlink(path, &symlink_path).unwrap(); #[cfg(target_os = "windows")] diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs index 2ddd7da74c2..9acc1de922a 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs @@ -7,9 +7,12 @@ //! The thread pool is implemented entirely using //! the threading utilities in [`crate::thread`]. -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, +use std::{ + panic::{self, UnwindSafe}, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, }; use crossbeam_channel::{Receiver, Sender}; @@ -25,13 +28,13 @@ pub struct Pool { // so that the channel is actually closed // before we join the worker threads! job_sender: Sender<Job>, - _handles: Vec<JoinHandle>, + _handles: Box<[JoinHandle]>, extant_tasks: Arc<AtomicUsize>, } struct Job { requested_intent: ThreadIntent, - f: Box<dyn FnOnce() + Send + 'static>, + f: Box<dyn FnOnce() + Send + UnwindSafe + 'static>, } impl Pool { @@ -47,6 +50,7 @@ impl Pool { let handle = Builder::new(INITIAL_INTENT) .stack_size(STACK_SIZE) .name("Worker".into()) + .allow_leak(true) .spawn({ let extant_tasks = Arc::clone(&extant_tasks); let job_receiver: Receiver<Job> = job_receiver.clone(); @@ -58,7 +62,8 @@ impl Pool { current_intent = job.requested_intent; } extant_tasks.fetch_add(1, Ordering::SeqCst); - (job.f)(); + // discard the panic, we should've logged the backtrace already + _ = panic::catch_unwind(job.f); extant_tasks.fetch_sub(1, Ordering::SeqCst); } } @@ -68,12 +73,12 @@ impl Pool { handles.push(handle); } - Pool { _handles: handles, extant_tasks, job_sender } + Pool { _handles: handles.into_boxed_slice(), extant_tasks, job_sender } } pub fn spawn<F>(&self, intent: ThreadIntent, f: F) where - F: FnOnce() + Send + 'static, + F: FnOnce() + Send + UnwindSafe + 'static, { let f = Box::new(move || { if cfg!(debug_assertions) { diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 4e2a70d6cd9..bbb8413cbc0 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -241,7 +241,7 @@ RecordFieldList = RecordField = Attr* Visibility? - Name ':' Type + Name ':' Type ('=' Expr)? TupleFieldList = '(' fields:(TupleField (',' TupleField)* ','?)? ')' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 69e2a9f9c1b..58c76a456ab 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen grammar`, do not edit by hand. +//! Generated by `cargo xtask codegen grammar`, do not edit by hand. #![allow(non_snake_case)] use crate::{ @@ -1539,9 +1539,13 @@ impl ast::HasName for RecordField {} impl ast::HasVisibility for RecordField {} impl RecordField { #[inline] + pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } + #[inline] pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } #[inline] pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) } + #[inline] + pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs index 85d20c2bd8c..df2e9619db1 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen grammar`, do not edit by hand. +//! Generated by `cargo xtask codegen grammar`, do not edit by hand. use crate::{ ast::AstToken, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index 28089ffb377..5d33f132ac1 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -5,7 +5,122 @@ use crate::{ match_ast, AstNode, SyntaxNode, }; +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum ExprPrecedence { + // return, break, yield, closures + Jump, + // = += -= *= /= %= &= |= ^= <<= >>= + Assign, + // .. ..= + Range, + // || + LOr, + // && + LAnd, + // == != < > <= >= + Compare, + // | + BitOr, + // ^ + BitXor, + // & + BitAnd, + // << >> + Shift, + // + - + Sum, + // * / % + Product, + // as + Cast, + // unary - * ! & &mut + Prefix, + // paths, loops, function calls, array indexing, field expressions, method calls + Unambiguous, +} + +#[derive(PartialEq, Debug)] +pub enum Fixity { + /// The operator is left-associative + Left, + /// The operator is right-associative + Right, + /// The operator is not associative + None, +} + +pub fn precedence(expr: &ast::Expr) -> ExprPrecedence { + match expr { + Expr::ClosureExpr(closure) => match closure.ret_type() { + None => ExprPrecedence::Jump, + Some(_) => ExprPrecedence::Unambiguous, + }, + + Expr::BreakExpr(_) + | Expr::ContinueExpr(_) + | Expr::ReturnExpr(_) + | Expr::YeetExpr(_) + | Expr::YieldExpr(_) => ExprPrecedence::Jump, + + Expr::RangeExpr(..) => ExprPrecedence::Range, + + Expr::BinExpr(bin_expr) => match bin_expr.op_kind() { + Some(it) => match it { + BinaryOp::LogicOp(logic_op) => match logic_op { + ast::LogicOp::And => ExprPrecedence::LAnd, + ast::LogicOp::Or => ExprPrecedence::LOr, + }, + BinaryOp::ArithOp(arith_op) => match arith_op { + ast::ArithOp::Add | ast::ArithOp::Sub => ExprPrecedence::Sum, + ast::ArithOp::Div | ast::ArithOp::Rem | ast::ArithOp::Mul => { + ExprPrecedence::Product + } + ast::ArithOp::Shl | ast::ArithOp::Shr => ExprPrecedence::Shift, + ast::ArithOp::BitXor => ExprPrecedence::BitXor, + ast::ArithOp::BitOr => ExprPrecedence::BitOr, + ast::ArithOp::BitAnd => ExprPrecedence::BitAnd, + }, + BinaryOp::CmpOp(_) => ExprPrecedence::Compare, + BinaryOp::Assignment { .. } => ExprPrecedence::Assign, + }, + None => ExprPrecedence::Unambiguous, + }, + Expr::CastExpr(_) => ExprPrecedence::Cast, + + Expr::LetExpr(_) | Expr::PrefixExpr(_) | Expr::RefExpr(_) => ExprPrecedence::Prefix, + + Expr::ArrayExpr(_) + | Expr::AsmExpr(_) + | Expr::AwaitExpr(_) + | Expr::BecomeExpr(_) + | Expr::BlockExpr(_) + | Expr::CallExpr(_) + | Expr::FieldExpr(_) + | Expr::ForExpr(_) + | Expr::FormatArgsExpr(_) + | Expr::IfExpr(_) + | Expr::IndexExpr(_) + | Expr::Literal(_) + | Expr::LoopExpr(_) + | Expr::MacroExpr(_) + | Expr::MatchExpr(_) + | Expr::MethodCallExpr(_) + | Expr::OffsetOfExpr(_) + | Expr::ParenExpr(_) + | Expr::PathExpr(_) + | Expr::RecordExpr(_) + | Expr::TryExpr(_) + | Expr::TupleExpr(_) + | Expr::UnderscoreExpr(_) + | Expr::WhileExpr(_) => ExprPrecedence::Unambiguous, + } +} + impl Expr { + pub fn precedence(&self) -> ExprPrecedence { + precedence(self) + } + // Implementation is based on // - https://doc.rust-lang.org/reference/expressions.html#expression-precedence // - https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html @@ -261,7 +376,7 @@ impl Expr { } /// Returns true if self is one of `return`, `break`, `continue` or `yield` with **no associated value**. - fn is_ret_like_with_no_value(&self) -> bool { + pub fn is_ret_like_with_no_value(&self) -> bool { use Expr::*; match self { diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 866379d940e..ca596583590 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -258,15 +258,7 @@ impl ChangeFixture { let to_id = crates[&to]; let sysroot = crate_graph[to_id].origin.is_lang(); crate_graph - .add_dep( - from_id, - Dependency::with_prelude( - CrateName::new(&to).unwrap(), - to_id, - prelude, - sysroot, - ), - ) + .add_dep(from_id, Dependency::with_prelude(to.clone(), to_id, prelude, sysroot)) .unwrap(); } } diff --git a/src/tools/rust-analyzer/docs/book/README.md b/src/tools/rust-analyzer/docs/book/README.md new file mode 100644 index 00000000000..a9d10df6643 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/README.md @@ -0,0 +1,29 @@ +# rust-analyzer documentation + +The rust analyzer manual uses [mdbook](https://rust-lang.github.io/mdBook/). + +## Quick start + +To run the documentation site locally: + +```shell +cargo install mdbook +cd docs/book +mdbook serve +# make changes to documentation files in doc/book/src +# ... +``` + +mdbook will rebuild the documentation as changes are made. + +## Making updates + +While not required, installing the mdbook binary can be helfpul in order to see the changes. +Start with the mdbook [User Guide](https://rust-lang.github.io/mdBook/guide/installation.html) to familiarize yourself with the tool. + +## Generated documentation + +Four sections are generated dynamically: assists, configuration, diagnostics and features. Their content is found in the `generated.md` files +of the respective book section, for example `src/configuration_generated.md`, and are included in the book via mdbook's +[include](https://rust-lang.github.io/mdBook/format/mdbook.html#including-files) functionality. Generated files can be rebuilt by running the various +test cases that generate them, or by simply running all of the `rust-analyzer` tests with `cargo test`. diff --git a/src/tools/rust-analyzer/docs/book/book.toml b/src/tools/rust-analyzer/docs/book/book.toml new file mode 100644 index 00000000000..ba3c1dede5d --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/book.toml @@ -0,0 +1,41 @@ +[book] +authors = ["The rust-analyzer authors"] +language = "en" +multilingual = false +src = "src" +title = "rust-analyzer" + +[rust] +edition = "2021" + +[output.html] +edit-url-template = "https://github.com/rust-lang/rust-analyzer/edit/master/manual/{path}" +git-repository-url = "https://github.com/rust-lang/rust-analyzer/tree/master/manual" +mathjax-support = true +site-url = "/manual/" + +[output.html.playground] +editable = true +runnable = false +line-numbers = true + +[output.html.search] +boost-hierarchy = 2 +boost-paragraph = 1 +boost-title = 2 +expand = true +heading-split-level = 2 +limit-results = 20 +use-boolean-and = true + +[output.html.redirect] +"/manual.html" = "/index.html" + +[output.html.fold] +enable = true +level = 3 + +[preprocessor.toc] +command = "mdbook-toc" +renderer = ["html"] +max-level = 3 diff --git a/src/tools/rust-analyzer/docs/book/src/README.md b/src/tools/rust-analyzer/docs/book/src/README.md new file mode 100644 index 00000000000..71f34e03466 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/README.md @@ -0,0 +1,21 @@ +# rust-analyzer + +At its core, rust-analyzer is a **library** for semantic analysis of +Rust code as it changes over time. This manual focuses on a specific +usage of the library -- running it as part of a server that implements +the [Language Server +Protocol](https://microsoft.github.io/language-server-protocol/) (LSP). +The LSP allows various code editors, like VS Code, Emacs or Vim, to +implement semantic features like completion or goto definition by +talking to an external language server process. + +To improve this document, send a pull request: +[https://github.com/rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/blob/master/docs/book/README.md) + +The manual is written in markdown and includes +some extra files which are generated from the source code. Run +`cargo test` and `cargo xtask codegen` to create these. + +If you have questions about using rust-analyzer, please ask them in the +["IDEs and Editors"](https://users.rust-lang.org/c/ide/14) topic of Rust +users forum. diff --git a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md new file mode 100644 index 00000000000..b3ed1e6df0a --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md @@ -0,0 +1,13 @@ +# Summary + +- [Introduction](README.md) +- [Installation](installation.md) +- [Troubleshooting](troubleshooting.md) +- [Configuration](configuration.md) + - [Non-Cargo Based Projects](non_cargo_based_projects.md) +- [Security](security.md) +- [Privacy](privacy.md) +- [Features](features.md) + - [Assists (Code Actions)](assists.md) + - [Diagnostics](diagnostics.md) +- [Editor Features](editor_features.md) diff --git a/src/tools/rust-analyzer/docs/book/src/assists.md b/src/tools/rust-analyzer/docs/book/src/assists.md new file mode 100644 index 00000000000..9d7c3bc1d5b --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/assists.md @@ -0,0 +1,8 @@ +# Assists + +Assists, or code actions, are small local refactorings, available in a +particular context. They are usually triggered by a shortcut or by +clicking a light bulb icon in the editor. Cursor position or selection +is signified by `┃` character. + +{{#include assists_generated.md:2:}} diff --git a/src/tools/rust-analyzer/docs/book/src/configuration.md b/src/tools/rust-analyzer/docs/book/src/configuration.md new file mode 100644 index 00000000000..221a571c17c --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/configuration.md @@ -0,0 +1,51 @@ +# Configuration + +**Source:** +[config.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs) + +The [Installation](#_installation) section contains details on +configuration for some of the editors. In general `rust-analyzer` is +configured via LSP messages, which means that it’s up to the editor to +decide on the exact format and location of configuration files. + +Some clients, such as [VS Code](#vs-code) or [COC plugin in +Vim](#coc-rust-analyzer) provide `rust-analyzer` specific configuration +UIs. Others may require you to know a bit more about the interaction +with `rust-analyzer`. + +For the later category, it might help to know that the initial +configuration is specified as a value of the `initializationOptions` +field of the [`InitializeParams` message, in the LSP +protocol](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize). +The spec says that the field type is `any?`, but `rust-analyzer` is +looking for a JSON object that is constructed using settings from the +list below. Name of the setting, ignoring the `rust-analyzer.` prefix, +is used as a path, and value of the setting becomes the JSON property +value. + +For example, a very common configuration is to enable proc-macro +support, can be achieved by sending this JSON: + + { + "cargo": { + "buildScripts": { + "enable": true, + }, + }, + "procMacro": { + "enable": true, + } + } + +Please consult your editor’s documentation to learn more about how to +configure [LSP +servers](https://microsoft.github.io/language-server-protocol/). + +To verify which configuration is actually used by `rust-analyzer`, set +`RA_LOG` environment variable to `rust_analyzer=info` and look for +config-related messages. Logs should show both the JSON that +`rust-analyzer` sees as well as the updated config. + +This is the list of config options `rust-analyzer` supports: + +{{#include configuration_generated.md}} diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md new file mode 100644 index 00000000000..55678926609 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -0,0 +1,1206 @@ +**rust-analyzer.assist.emitMustUse** (default: false) + + Whether to insert #[must_use] when generating `as_` methods +for enum variants. + + +**rust-analyzer.assist.expressionFillDefault** (default: "todo") + + Placeholder expression to use for missing expressions in assists. + + +**rust-analyzer.assist.termSearch.borrowcheck** (default: true) + + Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. + + +**rust-analyzer.assist.termSearch.fuel** (default: 1800) + + Term search fuel in "units of work" for assists (Defaults to 1800). + + +**rust-analyzer.cachePriming.enable** (default: true) + + Warm up caches on project load. + + +**rust-analyzer.cachePriming.numThreads** (default: "physical") + + How many worker threads to handle priming caches. The default `0` means to pick automatically. + + +**rust-analyzer.cargo.allTargets** (default: true) + + Pass `--all-targets` to cargo invocation. + + +**rust-analyzer.cargo.autoreload** (default: true) + + Automatically refresh project info via `cargo metadata` on +`Cargo.toml` or `.cargo/config.toml` changes. + + +**rust-analyzer.cargo.buildScripts.enable** (default: true) + + Run build scripts (`build.rs`) for more precise code analysis. + + +**rust-analyzer.cargo.buildScripts.invocationStrategy** (default: "per_workspace") + + Specifies the invocation strategy to use when running the build scripts command. +If `per_workspace` is set, the command will be executed for each Rust workspace with the +workspace as the working directory. +If `once` is set, the command will be executed once with the opened project as the +working directory. +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +is set. + + +**rust-analyzer.cargo.buildScripts.overrideCommand** (default: null) + + Override the command rust-analyzer uses to run build scripts and +build procedural macros. The command is required to output json +and should therefore include `--message-format=json` or a similar +option. + +If there are multiple linked projects/workspaces, this command is invoked for +each of them, with the working directory being the workspace root +(i.e., the folder containing the `Cargo.toml`). This can be overwritten +by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. + +By default, a cargo invocation will be constructed for the configured +targets and features, with the following base command line: + +```bash +cargo check --quiet --workspace --message-format=json --all-targets --keep-going +``` +. + + +**rust-analyzer.cargo.buildScripts.rebuildOnSave** (default: true) + + Rerun proc-macros building/build-scripts running when proc-macro +or build-script sources change and are saved. + + +**rust-analyzer.cargo.buildScripts.useRustcWrapper** (default: true) + + Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to +avoid checking unnecessary things. + + + **rust-analyzer.cargo.cfgs** + +Default: + +```[ + "debug_assertions", + "miri" +] + +``` + + List of cfg options to enable with the given values. + + + **rust-analyzer.cargo.extraArgs** (default: []) + + Extra arguments that are passed to every cargo invocation. + + +**rust-analyzer.cargo.extraEnv** (default: {}) + + Extra environment variables that will be set when running cargo, rustc +or other commands within the workspace. Useful for setting RUSTFLAGS. + + +**rust-analyzer.cargo.features** (default: []) + + List of features to activate. + +Set this to `"all"` to pass `--all-features` to cargo. + + +**rust-analyzer.cargo.noDefaultFeatures** (default: false) + + Whether to pass `--no-default-features` to cargo. + + +**rust-analyzer.cargo.sysroot** (default: "discover") + + Relative path to the sysroot, or "discover" to try to automatically find it via +"rustc --print sysroot". + +Unsetting this disables sysroot loading. + +This option does not take effect until rust-analyzer is restarted. + + +**rust-analyzer.cargo.sysrootSrc** (default: null) + + Relative path to the sysroot library sources. If left unset, this will default to +`{cargo.sysroot}/lib/rustlib/src/rust/library`. + +This option does not take effect until rust-analyzer is restarted. + + +**rust-analyzer.cargo.target** (default: null) + + Compilation target override (target tuple). + + +**rust-analyzer.cargo.targetDir** (default: null) + + Optional path to a rust-analyzer specific target directory. +This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro +building from locking the `Cargo.lock` at the expense of duplicating build artifacts. + +Set to `true` to use a subdirectory of the existing target directory or +set to a path relative to the workspace to use that path. + + +**rust-analyzer.cfg.setTest** (default: true) + + Set `cfg(test)` for local crates. Defaults to true. + + +**rust-analyzer.checkOnSave** (default: true) + + Run the check command for diagnostics on save. + + +**rust-analyzer.check.allTargets** (default: null) + + Check all targets and tests (`--all-targets`). Defaults to +`#rust-analyzer.cargo.allTargets#`. + + +**rust-analyzer.check.command** (default: "check") + + Cargo command to use for `cargo check`. + + +**rust-analyzer.check.extraArgs** (default: []) + + Extra arguments for `cargo check`. + + +**rust-analyzer.check.extraEnv** (default: {}) + + Extra environment variables that will be set when running `cargo check`. +Extends `#rust-analyzer.cargo.extraEnv#`. + + +**rust-analyzer.check.features** (default: null) + + List of features to activate. Defaults to +`#rust-analyzer.cargo.features#`. + +Set to `"all"` to pass `--all-features` to Cargo. + + +**rust-analyzer.check.ignore** (default: []) + + List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. + +For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... + + +**rust-analyzer.check.invocationStrategy** (default: "per_workspace") + + Specifies the invocation strategy to use when running the check command. +If `per_workspace` is set, the command will be executed for each workspace. +If `once` is set, the command will be executed once. +This config only has an effect when `#rust-analyzer.check.overrideCommand#` +is set. + + +**rust-analyzer.check.noDefaultFeatures** (default: null) + + Whether to pass `--no-default-features` to Cargo. Defaults to +`#rust-analyzer.cargo.noDefaultFeatures#`. + + +**rust-analyzer.check.overrideCommand** (default: null) + + Override the command rust-analyzer uses instead of `cargo check` for +diagnostics on save. The command is required to output json and +should therefore include `--message-format=json` or a similar option +(if your client supports the `colorDiagnosticOutput` experimental +capability, you can use `--message-format=json-diagnostic-rendered-ansi`). + +If you're changing this because you're using some tool wrapping +Cargo, you might also want to change +`#rust-analyzer.cargo.buildScripts.overrideCommand#`. + +If there are multiple linked projects/workspaces, this command is invoked for +each of them, with the working directory being the workspace root +(i.e., the folder containing the `Cargo.toml`). This can be overwritten +by changing `#rust-analyzer.check.invocationStrategy#`. + +If `$saved_file` is part of the command, rust-analyzer will pass +the absolute path of the saved file to the provided command. This is +intended to be used with non-Cargo build systems. +Note that `$saved_file` is experimental and may be removed in the future. + +An example command would be: + +```bash +cargo check --workspace --message-format=json --all-targets +``` +. + + +**rust-analyzer.check.targets** (default: null) + + Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. + +Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. +`["aarch64-apple-darwin", "x86_64-apple-darwin"]`. + +Aliased as `"checkOnSave.targets"`. + + +**rust-analyzer.check.workspace** (default: true) + + Whether `--workspace` should be passed to `cargo check`. +If false, `-p <package>` will be passed instead if applicable. In case it is not, no +check will be performed. + + +**rust-analyzer.completion.addSemicolonToUnit** (default: true) + + Whether to automatically add a semicolon when completing unit-returning functions. + +In `match` arms it completes a comma instead. + + +**rust-analyzer.completion.autoAwait.enable** (default: true) + + Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. + + +**rust-analyzer.completion.autoIter.enable** (default: true) + + Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. + + +**rust-analyzer.completion.autoimport.enable** (default: true) + + Toggles the additional completions that automatically add imports when completed. +Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. + + + **rust-analyzer.completion.autoimport.exclude** + +Default: + +```[ + { + "path": "core::borrow::Borrow", + "type": "methods" + }, + { + "path": "core::borrow::BorrowMut", + "type": "methods" + } +] + +``` + + A list of full paths to items to exclude from auto-importing completions. + +Traits in this list won't have their methods suggested in completions unless the trait +is in scope. + +You can either specify a string path which defaults to type "always" or use the more verbose +form `{ "path": "path::to::item", type: "always" }`. + +For traits the type "methods" can be used to only exclude the methods but not the trait itself. + +This setting also inherits `#rust-analyzer.completion.excludeTraits#`. + + + **rust-analyzer.completion.autoself.enable** (default: true) + + Toggles the additional completions that automatically show method calls and field accesses +with `self` prefixed to them when inside a method. + + +**rust-analyzer.completion.callable.snippets** (default: "fill_arguments") + + Whether to add parenthesis and argument snippets when completing function. + + +**rust-analyzer.completion.excludeTraits** (default: []) + + A list of full paths to traits whose methods to exclude from completion. + +Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. + +Note that the trait themselves can still be completed. + + +**rust-analyzer.completion.fullFunctionSignatures.enable** (default: false) + + Whether to show full function/method signatures in completion docs. + + +**rust-analyzer.completion.hideDeprecated** (default: false) + + Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. + + +**rust-analyzer.completion.limit** (default: null) + + Maximum number of completions to return. If `None`, the limit is infinite. + + +**rust-analyzer.completion.postfix.enable** (default: true) + + Whether to show postfix snippets like `dbg`, `if`, `not`, etc. + + +**rust-analyzer.completion.privateEditable.enable** (default: false) + + Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. + + + **rust-analyzer.completion.snippets.custom** + +Default: + +```{ + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" + }, + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" + }, + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" + }, + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" + }, + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" + }, + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" + } +} + +``` + + Custom completion snippets. + + + **rust-analyzer.completion.termSearch.enable** (default: false) + + Whether to enable term search based snippets like `Some(foo.bar().baz())`. + + +**rust-analyzer.completion.termSearch.fuel** (default: 1000) + + Term search fuel in "units of work" for autocompletion (Defaults to 1000). + + +**rust-analyzer.diagnostics.disabled** (default: []) + + List of rust-analyzer diagnostics to disable. + + +**rust-analyzer.diagnostics.enable** (default: true) + + Whether to show native rust-analyzer diagnostics. + + +**rust-analyzer.diagnostics.experimental.enable** (default: false) + + Whether to show experimental rust-analyzer diagnostics that might +have more false positives than usual. + + +**rust-analyzer.diagnostics.remapPrefix** (default: {}) + + Map of prefixes to be substituted when parsing diagnostic file paths. +This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. + + +**rust-analyzer.diagnostics.styleLints.enable** (default: false) + + Whether to run additional style lints. + + +**rust-analyzer.diagnostics.warningsAsHint** (default: []) + + List of warnings that should be displayed with hint severity. + +The warnings will be indicated by faded text or three dots in code +and will not show up in the `Problems Panel`. + + +**rust-analyzer.diagnostics.warningsAsInfo** (default: []) + + List of warnings that should be displayed with info severity. + +The warnings will be indicated by a blue squiggly underline in code +and a blue icon in the `Problems Panel`. + + +**rust-analyzer.files.excludeDirs** (default: []) + + These directories will be ignored by rust-analyzer. They are +relative to the workspace root, and globs are not supported. You may +also need to add the folders to Code's `files.watcherExclude`. + + +**rust-analyzer.files.watcher** (default: "client") + + Controls file watching implementation. + + +**rust-analyzer.highlightRelated.breakPoints.enable** (default: true) + + Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. + + +**rust-analyzer.highlightRelated.closureCaptures.enable** (default: true) + + Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. + + +**rust-analyzer.highlightRelated.exitPoints.enable** (default: true) + + Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). + + +**rust-analyzer.highlightRelated.references.enable** (default: true) + + Enables highlighting of related references while the cursor is on any identifier. + + +**rust-analyzer.highlightRelated.yieldPoints.enable** (default: true) + + Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. + + +**rust-analyzer.hover.actions.debug.enable** (default: true) + + Whether to show `Debug` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.enable** (default: true) + + Whether to show HoverActions in Rust files. + + +**rust-analyzer.hover.actions.gotoTypeDef.enable** (default: true) + + Whether to show `Go to Type Definition` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.implementations.enable** (default: true) + + Whether to show `Implementations` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.references.enable** (default: false) + + Whether to show `References` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.run.enable** (default: true) + + Whether to show `Run` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.updateTest.enable** (default: true) + + Whether to show `Update Test` action. Only applies when +`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. + + +**rust-analyzer.hover.documentation.enable** (default: true) + + Whether to show documentation on hover. + + +**rust-analyzer.hover.documentation.keywords.enable** (default: true) + + Whether to show keyword hover popups. Only applies when +`#rust-analyzer.hover.documentation.enable#` is set. + + +**rust-analyzer.hover.links.enable** (default: true) + + Use markdown syntax for links on hover. + + +**rust-analyzer.hover.maxSubstitutionLength** (default: 20) + + Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. + +This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. + +The default is 20 characters. + + +**rust-analyzer.hover.memoryLayout.alignment** (default: "hexadecimal") + + How to render the align information in a memory layout hover. + + +**rust-analyzer.hover.memoryLayout.enable** (default: true) + + Whether to show memory layout data on hover. + + +**rust-analyzer.hover.memoryLayout.niches** (default: false) + + How to render the niche information in a memory layout hover. + + +**rust-analyzer.hover.memoryLayout.offset** (default: "hexadecimal") + + How to render the offset information in a memory layout hover. + + +**rust-analyzer.hover.memoryLayout.size** (default: "both") + + How to render the size information in a memory layout hover. + + +**rust-analyzer.hover.show.enumVariants** (default: 5) + + How many variants of an enum to display when hovering on. Show none if empty. + + +**rust-analyzer.hover.show.fields** (default: 5) + + How many fields of a struct, variant or union to display when hovering on. Show none if empty. + + +**rust-analyzer.hover.show.traitAssocItems** (default: null) + + How many associated items of a trait to display when hovering a trait. + + +**rust-analyzer.imports.granularity.enforce** (default: false) + + Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. + + +**rust-analyzer.imports.granularity.group** (default: "crate") + + How imports should be grouped into use statements. + + +**rust-analyzer.imports.group.enable** (default: true) + + Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. + + +**rust-analyzer.imports.merge.glob** (default: true) + + Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. + + +**rust-analyzer.imports.preferNoStd** (default: false) + + Prefer to unconditionally use imports of the core and alloc crate, over the std crate. + + +**rust-analyzer.imports.preferPrelude** (default: false) + + Whether to prefer import paths containing a `prelude` module. + + +**rust-analyzer.imports.prefix** (default: "plain") + + The path structure for newly inserted paths to use. + + +**rust-analyzer.imports.prefixExternPrelude** (default: false) + + Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". + + +**rust-analyzer.inlayHints.bindingModeHints.enable** (default: false) + + Whether to show inlay type hints for binding modes. + + +**rust-analyzer.inlayHints.chainingHints.enable** (default: true) + + Whether to show inlay type hints for method chains. + + +**rust-analyzer.inlayHints.closingBraceHints.enable** (default: true) + + Whether to show inlay hints after a closing `}` to indicate what item it belongs to. + + +**rust-analyzer.inlayHints.closingBraceHints.minLines** (default: 25) + + Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 +to always show them). + + +**rust-analyzer.inlayHints.closureCaptureHints.enable** (default: false) + + Whether to show inlay hints for closure captures. + + +**rust-analyzer.inlayHints.closureReturnTypeHints.enable** (default: "never") + + Whether to show inlay type hints for return types of closures. + + +**rust-analyzer.inlayHints.closureStyle** (default: "impl_fn") + + Closure notation in type and chaining inlay hints. + + +**rust-analyzer.inlayHints.discriminantHints.enable** (default: "never") + + Whether to show enum variant discriminant hints. + + +**rust-analyzer.inlayHints.expressionAdjustmentHints.enable** (default: "never") + + Whether to show inlay hints for type adjustments. + + +**rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe** (default: false) + + Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. + + +**rust-analyzer.inlayHints.expressionAdjustmentHints.mode** (default: "prefix") + + Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). + + +**rust-analyzer.inlayHints.genericParameterHints.const.enable** (default: true) + + Whether to show const generic parameter name inlay hints. + + +**rust-analyzer.inlayHints.genericParameterHints.lifetime.enable** (default: false) + + Whether to show generic lifetime parameter name inlay hints. + + +**rust-analyzer.inlayHints.genericParameterHints.type.enable** (default: false) + + Whether to show generic type parameter name inlay hints. + + +**rust-analyzer.inlayHints.implicitDrops.enable** (default: false) + + Whether to show implicit drop hints. + + +**rust-analyzer.inlayHints.implicitSizedBoundHints.enable** (default: false) + + Whether to show inlay hints for the implied type parameter `Sized` bound. + + +**rust-analyzer.inlayHints.lifetimeElisionHints.enable** (default: "never") + + Whether to show inlay type hints for elided lifetimes in function signatures. + + +**rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames** (default: false) + + Whether to prefer using parameter names as the name for elided lifetime hints if possible. + + +**rust-analyzer.inlayHints.maxLength** (default: 25) + + Maximum length for inlay hints. Set to null to have an unlimited length. + + +**rust-analyzer.inlayHints.parameterHints.enable** (default: true) + + Whether to show function parameter name inlay hints at the call +site. + + +**rust-analyzer.inlayHints.rangeExclusiveHints.enable** (default: false) + + Whether to show exclusive range inlay hints. + + +**rust-analyzer.inlayHints.reborrowHints.enable** (default: "never") + + Whether to show inlay hints for compiler inserted reborrows. +This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. + + +**rust-analyzer.inlayHints.renderColons** (default: true) + + Whether to render leading colons for type hints, and trailing colons for parameter hints. + + +**rust-analyzer.inlayHints.typeHints.enable** (default: true) + + Whether to show inlay type hints for variables. + + +**rust-analyzer.inlayHints.typeHints.hideClosureInitialization** (default: false) + + Whether to hide inlay type hints for `let` statements that initialize to a closure. +Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. + + +**rust-analyzer.inlayHints.typeHints.hideClosureParameter** (default: false) + + Whether to hide inlay parameter type hints for closures. + + +**rust-analyzer.inlayHints.typeHints.hideNamedConstructor** (default: false) + + Whether to hide inlay type hints for constructors. + + +**rust-analyzer.interpret.tests** (default: false) + + Enables the experimental support for interpreting tests. + + +**rust-analyzer.joinLines.joinAssignments** (default: true) + + Join lines merges consecutive declaration and initialization of an assignment. + + +**rust-analyzer.joinLines.joinElseIf** (default: true) + + Join lines inserts else between consecutive ifs. + + +**rust-analyzer.joinLines.removeTrailingComma** (default: true) + + Join lines removes trailing commas. + + +**rust-analyzer.joinLines.unwrapTrivialBlock** (default: true) + + Join lines unwraps trivial blocks. + + +**rust-analyzer.lens.debug.enable** (default: true) + + Whether to show `Debug` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.enable** (default: true) + + Whether to show CodeLens in Rust files. + + +**rust-analyzer.lens.implementations.enable** (default: true) + + Whether to show `Implementations` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.location** (default: "above_name") + + Where to render annotations. + + +**rust-analyzer.lens.references.adt.enable** (default: false) + + Whether to show `References` lens for Struct, Enum, and Union. +Only applies when `#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.references.enumVariant.enable** (default: false) + + Whether to show `References` lens for Enum Variants. +Only applies when `#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.references.method.enable** (default: false) + + Whether to show `Method References` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.references.trait.enable** (default: false) + + Whether to show `References` lens for Trait. +Only applies when `#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.run.enable** (default: true) + + Whether to show `Run` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.updateTest.enable** (default: true) + + Whether to show `Update Test` lens. Only applies when +`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. + + +**rust-analyzer.linkedProjects** (default: []) + + Disable project auto-discovery in favor of explicitly specified set +of projects. + +Elements must be paths pointing to `Cargo.toml`, +`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON +objects in `rust-project.json` format. + + +**rust-analyzer.lru.capacity** (default: null) + + Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. + + +**rust-analyzer.lru.query.capacities** (default: {}) + + Sets the LRU capacity of the specified queries. + + +**rust-analyzer.notifications.cargoTomlNotFound** (default: true) + + Whether to show `can't find Cargo.toml` error message. + + +**rust-analyzer.numThreads** (default: null) + + How many worker threads in the main loop. The default `null` means to pick automatically. + + +**rust-analyzer.procMacro.attributes.enable** (default: true) + + Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. + + +**rust-analyzer.procMacro.enable** (default: true) + + Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. + + +**rust-analyzer.procMacro.ignored** (default: {}) + + These proc-macros will be ignored when trying to expand them. + +This config takes a map of crate names with the exported proc-macro names to ignore as values. + + +**rust-analyzer.procMacro.server** (default: null) + + Internal config, path to proc-macro server executable. + + +**rust-analyzer.references.excludeImports** (default: false) + + Exclude imports from find-all-references. + + +**rust-analyzer.references.excludeTests** (default: false) + + Exclude tests from find-all-references and call-hierarchy. + + +**rust-analyzer.runnables.command** (default: null) + + Command to be executed instead of 'cargo' for runnables. + + +**rust-analyzer.runnables.extraArgs** (default: []) + + Additional arguments to be passed to cargo for runnables such as +tests or binaries. For example, it may be `--release`. + + + **rust-analyzer.runnables.extraTestBinaryArgs** + +Default: + +```[ + "--show-output" +] + +``` + + Additional arguments to be passed through Cargo to launched tests, benchmarks, or +doc-tests. + +Unless the launched target uses a +[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), +they will end up being interpreted as options to +[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). + + + **rust-analyzer.rustc.source** (default: null) + + Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private +projects, or "discover" to try to automatically find it if the `rustc-dev` component +is installed. + +Any project which uses rust-analyzer with the rustcPrivate +crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. + +This option does not take effect until rust-analyzer is restarted. + + +**rust-analyzer.rustfmt.extraArgs** (default: []) + + Additional arguments to `rustfmt`. + + +**rust-analyzer.rustfmt.overrideCommand** (default: null) + + Advanced option, fully override the command rust-analyzer uses for +formatting. This should be the equivalent of `rustfmt` here, and +not that of `cargo fmt`. The file contents will be passed on the +standard input and the formatted result will be read from the +standard output. + + +**rust-analyzer.rustfmt.rangeFormatting.enable** (default: false) + + Enables the use of rustfmt's unstable range formatting command for the +`textDocument/rangeFormatting` request. The rustfmt option is unstable and only +available on a nightly build. + + +**rust-analyzer.semanticHighlighting.doc.comment.inject.enable** (default: true) + + Inject additional highlighting into doc comments. + +When enabled, rust-analyzer will highlight rust source in doc comments as well as intra +doc links. + + +**rust-analyzer.semanticHighlighting.nonStandardTokens** (default: true) + + Whether the server is allowed to emit non-standard tokens and modifiers. + + +**rust-analyzer.semanticHighlighting.operator.enable** (default: true) + + Use semantic tokens for operators. + +When disabled, rust-analyzer will emit semantic tokens only for operator tokens when +they are tagged with modifiers. + + +**rust-analyzer.semanticHighlighting.operator.specialization.enable** (default: false) + + Use specialized semantic tokens for operators. + +When enabled, rust-analyzer will emit special token types for operator tokens instead +of the generic `operator` token type. + + +**rust-analyzer.semanticHighlighting.punctuation.enable** (default: false) + + Use semantic tokens for punctuation. + +When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when +they are tagged with modifiers or have a special role. + + +**rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang** (default: false) + + When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro +calls. + + +**rust-analyzer.semanticHighlighting.punctuation.specialization.enable** (default: false) + + Use specialized semantic tokens for punctuation. + +When enabled, rust-analyzer will emit special token types for punctuation tokens instead +of the generic `punctuation` token type. + + +**rust-analyzer.semanticHighlighting.strings.enable** (default: true) + + Use semantic tokens for strings. + +In some editors (e.g. vscode) semantic tokens override other highlighting grammars. +By disabling semantic tokens for strings, other grammars can be used to highlight +their contents. + + +**rust-analyzer.signatureInfo.detail** (default: "full") + + Show full signature of the callable. Only shows parameters if disabled. + + +**rust-analyzer.signatureInfo.documentation.enable** (default: true) + + Show documentation. + + +**rust-analyzer.typing.triggerChars** (default: "=.") + + Specify the characters allowed to invoke special on typing triggers. +- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression +- typing `=` between two expressions adds `;` when in statement position +- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position +- typing `.` in a chain method call auto-indents +- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression +- typing `{` in a use item adds a closing `}` in the right place +- typing `>` to complete a return type `->` will insert a whitespace after it +- typing `<` in a path or type position inserts a closing `>` after the path or type. + + +**rust-analyzer.vfs.extraIncludes** (default: []) + + Additional paths to include in the VFS. Generally for code that is +generated or otherwise managed by a build system outside of Cargo, +though Cargo might be the eventual consumer. + + +**rust-analyzer.workspace.discoverConfig** (default: null) + + Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. + +[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. +`progress_label` is used for the title in progress indicators, whereas `files_to_watch` +is used to determine which build system-specific files should be watched in order to +reload rust-analyzer. + +Below is an example of a valid configuration: +```json +"rust-analyzer.workspace.discoverConfig": { + "command": [ + "rust-project", + "develop-json" + ], + "progressLabel": "rust-analyzer", + "filesToWatch": [ + "BUCK" + ] +} +``` + +## On `DiscoverWorkspaceConfig::command` + +**Warning**: This format is provisional and subject to change. + +[`DiscoverWorkspaceConfig::command`] *must* return a JSON object +corresponding to `DiscoverProjectData::Finished`: + +```norun +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "kind")] +#[serde(rename_all = "snake_case")] +enum DiscoverProjectData { + Finished { buildfile: Utf8PathBuf, project: ProjectJsonData }, + Error { error: String, source: Option<String> }, + Progress { message: String }, +} +``` + +As JSON, `DiscoverProjectData::Finished` is: + +```json +{ + // the internally-tagged representation of the enum. + "kind": "finished", + // the file used by a non-Cargo build system to define + // a package or target. + "buildfile": "rust-analyzer/BUILD", + // the contents of a rust-project.json, elided for brevity + "project": { + "sysroot": "foo", + "crates": [] + } +} +``` + +It is encouraged, but not required, to use the other variants on +`DiscoverProjectData` to provide a more polished end-user experience. + +`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, +which will be substituted with the JSON-serialized form of the following +enum: + +```norun +#[derive(PartialEq, Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum DiscoverArgument { + Path(AbsPathBuf), + Buildfile(AbsPathBuf), +} +``` + +The JSON representation of `DiscoverArgument::Path` is: + +```json +{ + "path": "src/main.rs" +} +``` + +Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: + +``` +{ + "buildfile": "BUILD" +} +``` + +`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, +and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to +to update an existing workspace. As a reference for implementors, +buck2's `rust-project` will likely be useful: +https://github.com/facebook/buck2/tree/main/integrations/rust-project. + + +**rust-analyzer.workspace.symbol.search.kind** (default: "only_types") + + Workspace symbol search kind. + + +**rust-analyzer.workspace.symbol.search.limit** (default: 128) + + Limits the number of items returned from a workspace symbol search (Defaults to 128). +Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. +Other clients requires all results upfront and might require a higher limit. + + +**rust-analyzer.workspace.symbol.search.scope** (default: "workspace") + + Workspace symbol search scope. + + diff --git a/src/tools/rust-analyzer/docs/book/src/diagnostics.md b/src/tools/rust-analyzer/docs/book/src/diagnostics.md new file mode 100644 index 00000000000..60685c98da7 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/diagnostics.md @@ -0,0 +1,16 @@ +# Diagnostics + +While most errors and warnings provided by rust-analyzer come from the +`cargo check` integration, there’s a growing number of diagnostics +implemented using rust-analyzer’s own analysis. Some of these +diagnostics don’t respect `#[allow]` or `#[deny]` attributes yet, but +can be turned off using the `rust-analyzer.diagnostics.enable`, +`rust-analyzer.diagnostics.experimental.enable` or +`rust-analyzer.diagnostics.disabled` settings. + +## Clippy + +To run `cargo clippy` instead of `cargo check`, you can set +`"rust-analyzer.check.command": "clippy"`. + +{{#include diagnostics_generated.md:2:}} diff --git a/src/tools/rust-analyzer/docs/book/src/editor_features.md b/src/tools/rust-analyzer/docs/book/src/editor_features.md new file mode 100644 index 00000000000..73fe9f49a96 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/editor_features.md @@ -0,0 +1,204 @@ +# Editor Features + + +## VS Code + +### Color configurations + +It is possible to change the foreground/background color and font +family/size of inlay hints. Just add this to your `settings.json`: + +```json +{ + "editor.inlayHints.fontFamily": "Courier New", + "editor.inlayHints.fontSize": 11, + + "workbench.colorCustomizations": { + // Name of the theme you are currently using + "[Default Dark+]": { + "editorInlayHint.foreground": "#868686f0", + "editorInlayHint.background": "#3d3d3d48", + + // Overrides for specific kinds of inlay hints + "editorInlayHint.typeForeground": "#fdb6fdf0", + "editorInlayHint.parameterForeground": "#fdb6fdf0", + } + } +} +``` + +### Semantic style customizations + +You can customize the look of different semantic elements in the source +code. For example, mutable bindings are underlined by default and you +can override this behavior by adding the following section to your +`settings.json`: + +```json +{ + "editor.semanticTokenColorCustomizations": { + "rules": { + "*.mutable": { + "fontStyle": "", // underline is the default + }, + } + }, +} +``` + +Most themes doesn’t support styling unsafe operations differently yet. +You can fix this by adding overrides for the rules `operator.unsafe`, +`function.unsafe`, and `method.unsafe`: + +```json +{ + "editor.semanticTokenColorCustomizations": { + "rules": { + "operator.unsafe": "#ff6600", + "function.unsafe": "#ff6600", + "method.unsafe": "#ff6600" + } + }, +} +``` + +In addition to the top-level rules you can specify overrides for +specific themes. For example, if you wanted to use a darker text color +on a specific light theme, you might write: + +```json +{ + "editor.semanticTokenColorCustomizations": { + "rules": { + "operator.unsafe": "#ff6600" + }, + "[Ayu Light]": { + "rules": { + "operator.unsafe": "#572300" + } + } + }, +} +``` + +Make sure you include the brackets around the theme name. For example, +use `"[Ayu Light]"` to customize the theme Ayu Light. + +### Special `when` clause context for keybindings. + +You may use `inRustProject` context to configure keybindings for rust +projects only. For example: + +```json +{ + "key": "ctrl+alt+d", + "command": "rust-analyzer.openDocs", + "when": "inRustProject" +} +``` + +More about `when` clause contexts +[here](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts). + +### Setting runnable environment variables + +You can use "rust-analyzer.runnables.extraEnv" setting to define +runnable environment-specific substitution variables. The simplest way +for all runnables in a bunch: + +```json +"rust-analyzer.runnables.extraEnv": { + "RUN_SLOW_TESTS": "1" +} +``` + +Or it is possible to specify vars more granularly: + +```json +"rust-analyzer.runnables.extraEnv": [ + { + // "mask": null, // null mask means that this rule will be applied for all runnables + env: { + "APP_ID": "1", + "APP_DATA": "asdf" + } + }, + { + "mask": "test_name", + "env": { + "APP_ID": "2", // overwrites only APP_ID + } + } +] +``` + +You can use any valid regular expression as a mask. Also note that a +full runnable name is something like **run bin\_or\_example\_name**, +**test some::mod::test\_name** or **test-mod some::mod**, so it is +possible to distinguish binaries, single tests, and test modules with +this masks: `"^run"`, `"^test "` (the trailing space matters!), and +`"^test-mod"` respectively. + +If needed, you can set different values for different platforms: + +```json +"rust-analyzer.runnables.extraEnv": [ + { + "platform": "win32", // windows only + env: { + "APP_DATA": "windows specific data" + } + }, + { + "platform": ["linux"], + "env": { + "APP_DATA": "linux data", + } + }, + { // for all platforms + "env": { + "APP_COMMON_DATA": "xxx", + } + } +] +``` + +### Compiler feedback from external commands + +Instead of relying on the built-in `cargo check`, you can configure Code +to run a command in the background and use the `$rustc-watch` problem +matcher to generate inline error markers from its output. + +To do this you need to create a new [VS Code +Task](https://code.visualstudio.com/docs/editor/tasks) and set +`"rust-analyzer.checkOnSave": false` in preferences. + +For example, if you want to run +[`cargo watch`](https://crates.io/crates/cargo-watch) instead, you might +add the following to `.vscode/tasks.json`: + +```json +{ + "label": "Watch", + "group": "build", + "type": "shell", + "command": "cargo watch", + "problemMatcher": "$rustc-watch", + "isBackground": true +} +``` + +### Live Share + +VS Code Live Share has partial support for rust-analyzer. + +Live Share *requires* the official Microsoft build of VS Code, OSS +builds will not work correctly. + +The host’s rust-analyzer instance will be shared with all guests joining +the session. The guests do not have to have the rust-analyzer extension +installed for this to work. + +If you are joining a Live Share session and *do* have rust-analyzer +installed locally, commands from the command palette will not work +correctly since they will attempt to communicate with the local server. diff --git a/src/tools/rust-analyzer/docs/book/src/features.md b/src/tools/rust-analyzer/docs/book/src/features.md new file mode 100644 index 00000000000..0829a0213b7 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/features.md @@ -0,0 +1,3 @@ +# Features + +{{#include features_generated.md:2:}} diff --git a/src/tools/rust-analyzer/docs/book/src/installation.md b/src/tools/rust-analyzer/docs/book/src/installation.md new file mode 100644 index 00000000000..5b697e9bc33 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/installation.md @@ -0,0 +1,644 @@ +# Installation + +In theory, one should be able to just install the [`rust-analyzer` +binary](#rust-analyzer-language-server-binary) and have it automatically +work with any editor. We are not there yet, so some editor specific +setup is required. + +Additionally, rust-analyzer needs the sources of the standard library. +If the source code is not present, rust-analyzer will attempt to install +it automatically. + +To add the sources manually, run the following command: + + $ rustup component add rust-src + +## Toolchain + +Only the latest stable standard library source is officially supported +for use with rust-analyzer. If you are using an older toolchain or have +an override set, rust-analyzer may fail to understand the Rust source. +You will either need to update your toolchain or use an older version of +rust-analyzer that is compatible with your toolchain. + +If you are using an override in your project, you can still force +rust-analyzer to use the stable toolchain via the environment variable +`RUSTUP_TOOLCHAIN`. For example, with VS Code or coc-rust-analyzer: + + { "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } } + +## VS Code + +This is the best supported editor at the moment. The rust-analyzer +plugin for VS Code is maintained [in +tree](https://github.com/rust-lang/rust-analyzer/tree/master/editors/code). + +You can install the latest release of the plugin from [the +marketplace](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer). + +Note that the plugin may cause conflicts with the [previous official +Rust +plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust). +The latter is no longer maintained and should be uninstalled. + +The server binary is stored in the extension install directory, which +starts with `rust-lang.rust-analyzer-` and is located under: + +- Linux: `~/.vscode/extensions` + +- Linux (Remote, such as WSL): `~/.vscode-server/extensions` + +- macOS: `~/.vscode/extensions` + +- Windows: `%USERPROFILE%\.vscode\extensions` + +As an exception, on NixOS, the extension makes a copy of the server and +stores it under +`~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`. + +Note that we only support the two most recent versions of VS Code. + +### Updates + +The extension will be updated automatically as new versions become +available. It will ask your permission to download the matching language +server version binary if needed. + +#### Nightly + +We ship nightly releases for VS Code. To help us out by testing the +newest code, you can enable pre-release versions in the Code extension +page. + +### Manual installation + +Alternatively, download a VSIX corresponding to your platform from the +[releases](https://github.com/rust-lang/rust-analyzer/releases) page. + +Install the extension with the `Extensions: Install from VSIX` command +within VS Code, or from the command line via: + + $ code --install-extension /path/to/rust-analyzer.vsix + +If you are running an unsupported platform, you can install +`rust-analyzer-no-server.vsix` and compile or obtain a server binary. +Copy the server anywhere, then add the path to your settings.json, for +example: + + { "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" } + +### Building From Source + +Both the server and the Code plugin can be installed from source: + + $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer + $ cargo xtask install + +You’ll need Cargo, nodejs (matching a supported version of VS Code) and +npm for this. + +Note that installing via `xtask install` does not work for VS Code +Remote, instead you’ll need to install the `.vsix` manually. + +If you’re not using Code, you can compile and install only the LSP +server: + + $ cargo xtask install --server + +Make sure that `.cargo/bin` is in `$PATH` and precedes paths where +`rust-analyzer` may also be installed. Specifically, `rustup` includes a +proxy called `rust-analyzer`, which can cause problems if you’re +planning to use a source build or even a downloaded binary. + +## rust-analyzer Language Server Binary + +Other editors generally require the `rust-analyzer` binary to be in +`$PATH`. You can download pre-built binaries from the +[releases](https://github.com/rust-lang/rust-analyzer/releases) page. +You will need to uncompress and rename the binary for your platform, +e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to +`rust-analyzer`, make it executable, then move it into a directory in +your `$PATH`. + +On Linux to install the `rust-analyzer` binary into `~/.local/bin`, +these commands should work: + + $ mkdir -p ~/.local/bin + $ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer + $ chmod +x ~/.local/bin/rust-analyzer + +Make sure that `~/.local/bin` is listed in the `$PATH` variable and use +the appropriate URL if you’re not on a `x86-64` system. + +You don’t have to use `~/.local/bin`, any other path like `~/.cargo/bin` +or `/usr/local/bin` will work just as well. + +Alternatively, you can install it from source using the command below. +You’ll need the latest stable version of the Rust toolchain. + + $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer + $ cargo xtask install --server + +If your editor can’t find the binary even though the binary is on your +`$PATH`, the likely explanation is that it doesn’t see the same `$PATH` +as the shell, see [this +issue](https://github.com/rust-lang/rust-analyzer/issues/1811). On Unix, +running the editor from a shell or changing the `.desktop` file to set +the environment should help. + +### rustup + +`rust-analyzer` is available in `rustup`: + + $ rustup component add rust-analyzer + +### Arch Linux + +The `rust-analyzer` binary can be installed from the repos or AUR (Arch +User Repository): + +- [`rust-analyzer`](https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/) + (built from latest tagged source) + +- [`rust-analyzer-git`](https://aur.archlinux.org/packages/rust-analyzer-git) + (latest Git version) + +Install it with pacman, for example: + + $ pacman -S rust-analyzer + +### Gentoo Linux + +`rust-analyzer` is installed when the `rust-analyzer` use flag is set for dev-lang/rust or dev-lang/rust-bin. You also need to set the `rust-src` use flag. + +### macOS + +The `rust-analyzer` binary can be installed via +[Homebrew](https://brew.sh/). + + $ brew install rust-analyzer + +### Windows + +It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation. +Download links can be found +[here](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist). + +## VS Code or VSCodium in Flatpak + +Setting up `rust-analyzer` with a Flatpak version of Code is not trivial +because of the Flatpak sandbox. While the sandbox can be disabled for +some directories, `/usr/bin` will always be mounted under +`/run/host/usr/bin`. This prevents access to the system’s C compiler, a +system-wide installation of Rust, or any other libraries you might want +to link to. Some compilers and libraries can be acquired as Flatpak +SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or +`org.freedesktop.Sdk.Extension.llvm15`. + +If you use a Flatpak SDK for Rust, it must be in your `PATH`: + + * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08` + * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`) + +If you want to use Flatpak in combination with `rustup`, the following +steps might help: + +- both Rust and `rustup` have to be installed using + <https://rustup.rs>. Distro packages *will not* work. + +- you need to launch Code, open a terminal and run `echo $PATH` + +- using + [Flatseal](https://flathub.org/apps/details/com.github.tchx84.Flatseal), + you must add an environment variable called `PATH`. Set its value to + the output from above, appending `:~/.cargo/bin`, where `~` is the + path to your home directory. You must replace `~`, as it won’t be + expanded otherwise. + +- while Flatseal is open, you must enable access to "All user files" + +A C compiler should already be available via `org.freedesktop.Sdk`. Any +other tools or libraries you will need to acquire from Flatpak. + +## Emacs + +Prerequisites: You have installed the [`rust-analyzer` +binary](#rust-analyzer-language-server-binary). + +To use `rust-analyzer`, you need to install and enable one of the two +popular LSP client implementations for Emacs, +[Eglot](https://github.com/joaotavora/eglot) or [LSP +Mode](https://github.com/emacs-lsp/lsp-mode). Both enable +`rust-analyzer` by default in rust buffers if it is available. + +### Eglot + +Eglot is the more minimalistic and lightweight LSP client for Emacs, +integrates well with existing Emacs functionality and is built into +Emacs starting from release 29. + +After installing Eglot, e.g. via `M-x package-install` (not needed from +Emacs 29), you can enable it via the `M-x eglot` command or load it +automatically in `rust-mode` via + + (add-hook 'rust-mode-hook 'eglot-ensure) + +To enable clippy, you will need to configure the initialization options +to pass the `check.command` setting. + + (add-to-list 'eglot-server-programs + '((rust-ts-mode rust-mode) . + ("rust-analyzer" :initializationOptions (:check (:command "clippy"))))) + +For more detailed instructions and options see the [Eglot +manual](https://joaotavora.github.io/eglot) (also available from Emacs +via `M-x info`) and the [Eglot +readme](https://github.com/joaotavora/eglot/blob/master/README.md). + +Eglot does not support the rust-analyzer extensions to the +language-server protocol and does not aim to do so in the future. The +[eglot-x](https://github.com/nemethf/eglot-x#rust-analyzer-extensions) +package adds experimental support for those LSP extensions. + +### LSP Mode + +LSP-mode is the original LSP-client for emacs. Compared to Eglot it has +a larger codebase and supports more features, like LSP protocol +extensions. With extension packages like [LSP +UI](https://github.com/emacs-lsp/lsp-mode) it offers a lot of visual +eyecandy. Further it integrates well with [DAP +mode](https://github.com/emacs-lsp/dap-mode) for support of the Debug +Adapter Protocol. + +You can install LSP-mode via `M-x package-install` and then run it via +the `M-x lsp` command or load it automatically in rust buffers with + + (add-hook 'rust-mode-hook 'lsp-deferred) + +For more information on how to set up LSP mode and its extension package +see the instructions in the [LSP mode +manual](https://emacs-lsp.github.io/lsp-mode/page/installation). Also +see the [rust-analyzer +section](https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/) +for `rust-analyzer` specific options and commands, which you can +optionally bind to keys. + +Note the excellent +[guide](https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/) from +[@rksm](https://github.com/rksm) on how to set-up Emacs for Rust +development with LSP mode and several other packages. + +## Vim/Neovim + +Prerequisites: You have installed the [`rust-analyzer` +binary](#rust-analyzer-language-server-binary). Not needed if the +extension can install/update it on its own, coc-rust-analyzer is one +example. + +There are several LSP client implementations for Vim or Neovim: + +### coc-rust-analyzer + +1. Install coc.nvim by following the instructions at + [coc.nvim](https://github.com/neoclide/coc.nvim) (Node.js required) + +2. Run `:CocInstall coc-rust-analyzer` to install + [coc-rust-analyzer](https://github.com/fannheyward/coc-rust-analyzer), + this extension implements *most* of the features supported in the + VSCode extension: + + - automatically install and upgrade stable/nightly releases + + - same configurations as VSCode extension, + `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc. + + - same commands too, `rust-analyzer.analyzerStatus`, + `rust-analyzer.ssr` etc. + + - inlay hints for variables and method chaining, *Neovim Only* + +Note: for code actions, use `coc-codeaction-cursor` and +`coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` +are unlikely to be useful. + +### LanguageClient-neovim + +1. Install LanguageClient-neovim by following the instructions + [here](https://github.com/autozimu/LanguageClient-neovim) + + - The GitHub project wiki has extra tips on configuration + +2. Configure by adding this to your Vim/Neovim config file (replacing + the existing Rust-specific line if it exists): + + let g:LanguageClient_serverCommands = { + \ 'rust': ['rust-analyzer'], + \ } + +### YouCompleteMe + +Install YouCompleteMe by following the instructions +[here](https://github.com/ycm-core/YouCompleteMe#installation). + +rust-analyzer is the default in ycm, it should work out of the box. + +### ALE + +To use the LSP server in [ale](https://github.com/dense-analysis/ale): + + let g:ale_linters = {'rust': ['analyzer']} + +### 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: + + 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, + settings = { + ["rust-analyzer"] = { + imports = { + granularity = { + group = "module", + }, + prefix = "self", + }, + cargo = { + buildScripts = { + enable = true, + }, + }, + procMacro = { + enable = true + }, + } + } + }) + EOF + +If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`: + +```vim +lspconfig.rust_analyzer.setup({ + on_attach = function(client, bufnr) + vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) + end +}) +``` + +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. + +Check out <https://github.com/mrcjkb/rustaceanvim> for a batteries +included rust-analyzer setup for Neovim. + +### vim-lsp + +vim-lsp is installed by following [the plugin +instructions](https://github.com/prabirshrestha/vim-lsp). It can be as +simple as adding this line to your `.vimrc`: + + Plug 'prabirshrestha/vim-lsp' + +Next you need to register the `rust-analyzer` binary. If it is avim.lspvailable +in `$PATH`, you may want to add this to your `.vimrc`: + + if executable('rust-analyzer') + au User lsp_setup call lsp#register_server({ + \ 'name': 'Rust Language Server', + \ 'cmd': {server_info->['rust-analyzer']}, + \ 'whitelist': ['rust'], + \ }) + endif + +There is no dedicated UI for the server configuration, so you would need +to send any options as a value of the `initialization_options` field, as +described in the [Configuration](#configuration) section. Here is an +example of how to enable the proc-macro support: + + if executable('rust-analyzer') + au User lsp_setup call lsp#register_server({ + \ 'name': 'Rust Language Server', + \ 'cmd': {server_info->['rust-analyzer']}, + \ 'whitelist': ['rust'], + \ 'initialization_options': { + \ 'cargo': { + \ 'buildScripts': { + \ 'enable': v:true, + \ }, + \ }, + \ 'procMacro': { + \ 'enable': v:true, + \ }, + \ }, + \ }) + endif + +## Sublime Text + +### Sublime Text 4: + +- Follow the instructions in + [LSP-rust-analyzer](https://github.com/sublimelsp/LSP-rust-analyzer). + +Install +[LSP-file-watcher-chokidar](https://packagecontrol.io/packages/LSP-file-watcher-chokidar) +to enable file watching (`workspace/didChangeWatchedFiles`). + +### Sublime Text 3: + +- Install the [`rust-analyzer` + binary](#rust-analyzer-language-server-binary). + +- Install the [LSP package](https://packagecontrol.io/packages/LSP). + +- From the command palette, run `LSP: Enable Language Server Globally` + and select `rust-analyzer`. + +If it worked, you should see "rust-analyzer, Line X, Column Y" on the +left side of the status bar, and after waiting a bit, functionalities +like tooltips on hovering over variables should become available. + +If you get an error saying `No such file or directory: 'rust-analyzer'`, +see the [`rust-analyzer` binary](#rust-analyzer-language-server-binary) +section on installing the language server binary. + +## GNOME Builder + +GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. If +the LSP binary is not available, GNOME Builder can install it when +opening a Rust file. + +## Eclipse IDE + +Support for Rust development in the Eclipse IDE is provided by [Eclipse +Corrosion](https://github.com/eclipse/corrosion). If available in PATH +or in some standard location, `rust-analyzer` is detected and powers +editing of Rust files without further configuration. If `rust-analyzer` +is not detected, Corrosion will prompt you for configuration of your +Rust toolchain and language server with a link to the *Window > +Preferences > Rust* preference page; from here a button allows to +download and configure `rust-analyzer`, but you can also reference +another installation. You’ll need to close and reopen all .rs and Cargo +files, or to restart the IDE, for this change to take effect. + +## Kate Text Editor + +Support for the language server protocol is built into Kate through the +LSP plugin, which is included by default. It is preconfigured to use +rust-analyzer for Rust sources since Kate 21.12. + +To change rust-analyzer config options, start from the following example +and put it into Kate’s "User Server Settings" tab (located under the LSP +Client settings): + + { + "servers": { + "rust": { + "initializationOptions": { + "cachePriming": { + "enable": false + }, + "check": { + "allTargets": false + }, + "checkOnSave": false + } + } + } + } + +Then click on apply, and restart the LSP server for your rust project. + +## juCi++ + +[juCi++](https://gitlab.com/cppit/jucipp) has built-in support for the +language server protocol, and since version 1.7.0 offers installation of +both Rust and rust-analyzer when opening a Rust file. + +## Kakoune + +[Kakoune](https://kakoune.org/) supports LSP with the help of +[`kak-lsp`](https://github.com/kak-lsp/kak-lsp). Follow the +[instructions](https://github.com/kak-lsp/kak-lsp#installation) to +install `kak-lsp`. To configure `kak-lsp`, refer to the [configuration +section](https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp) which +is basically about copying the [configuration +file](https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml) in +the right place (latest versions should use `rust-analyzer` by default). + +Finally, you need to configure Kakoune to talk to `kak-lsp` (see [Usage +section](https://github.com/kak-lsp/kak-lsp#usage)). A basic +configuration will only get you LSP but you can also activate inlay +diagnostics and auto-formatting on save. The following might help you +get all of this. + + eval %sh{kak-lsp --kakoune -s $kak_session} # Not needed if you load it with plug.kak. + hook global WinSetOption filetype=rust %{ + # Enable LSP + lsp-enable-window + + # Auto-formatting on save + hook window BufWritePre .* lsp-formatting-sync + + # Configure inlay hints (only on save) + hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints + hook -once -always window WinSetOption filetype=.* %{ + remove-hooks window rust-inlay-hints + } + } + +## Helix + +[Helix](https://docs.helix-editor.com/) supports LSP by default. +However, it won’t install `rust-analyzer` automatically. You can follow +instructions for installing [`rust-analyzer` +binary](#rust-analyzer-language-server-binary). + +## Visual Studio 2022 + +There are multiple rust-analyzer extensions for Visual Studio 2022 on +Windows: + +### rust-analyzer.vs + +(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 +International) + +[Visual Studio +Marketplace](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer) + +[GitHub](https://github.com/kitamstudios/rust-analyzer/) + +Support for Rust development in the Visual Studio IDE is enabled by the +[rust-analyzer](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer) +package. Either click on the download link or install from IDE’s +extension manager. For now [Visual Studio +2022](https://visualstudio.microsoft.com/downloads/) is required. All +editions are supported viz. Community, Professional & Enterprise. The +package aims to provide 0-friction installation and therefore comes +loaded with most things required including rust-analyzer binary. If +anything it needs is missing, appropriate errors / warnings will guide +the user. E.g. cargo.exe needs to be in path and the package will tell +you as much. This package is under rapid active development. So if you +encounter any issues please file it at +[rust-analyzer.vs](https://github.com/kitamstudios/rust-analyzer/). + +### VS\_RustAnalyzer + +(License: GPL) + +[Visual Studio +Marketplace](https://marketplace.visualstudio.com/items?itemName=cchharris.vsrustanalyzer) + +[GitHub](https://github.com/cchharris/VS-RustAnalyzer) + +### SourceGear Rust + +(License: closed source) + +[Visual Studio +Marketplace](https://marketplace.visualstudio.com/items?itemName=SourceGear.SourceGearRust) + +[GitHub (docs, issues, +discussions)](https://github.com/sourcegear/rust-vs-extension) + +- Free (no-cost) + +- Supports all editions of Visual Studio 2022 on Windows: Community, + Professional, or Enterprise + +## Lapce + +[Lapce](https://lapce.dev/) has a Rust plugin which you can install +directly. Unfortunately, it downloads an old version of `rust-analyzer`, +but you can set the server path under Settings. + +## Crates + +There is a package named `ra_ap_rust_analyzer` available on +[crates.io](https://crates.io/crates/ra_ap_rust-analyzer), for someone +who wants to use it programmatically. + +For more details, see [the publish +workflow](https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/autopublish.yaml). + +## Zed + +[Zed](https://zed.dev) has native `rust-analyzer` support. If the LSP +binary is not available, Zed can install it when opening a Rust file. diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md new file mode 100644 index 00000000000..151f8758a17 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -0,0 +1,246 @@ +# Non-Cargo Based Projects + +rust-analyzer does not require Cargo. However, if you use some other +build system, you’ll have to describe the structure of your project for +rust-analyzer in the `rust-project.json` format: + +```typescript +interface JsonProject { + /// Path to the sysroot directory. + /// + /// The sysroot is where rustc looks for the + /// crates that are built-in to rust, such as + /// std. + /// + /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root + /// + /// To see the current value of sysroot, you + /// can query rustc: + /// + /// ``` + /// $ rustc --print sysroot + /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin + /// ``` + sysroot?: string; + /// Path to the directory with *source code* of + /// sysroot crates. + /// + /// By default, this is `lib/rustlib/src/rust/library` + /// relative to the sysroot. + /// + /// It should point to the directory where std, + /// core, and friends can be found: + /// + /// https://github.com/rust-lang/rust/tree/master/library. + /// + /// If provided, rust-analyzer automatically adds + /// dependencies on sysroot crates. Conversely, + /// if you omit this path, you can specify sysroot + /// dependencies yourself and, for example, have + /// several different "sysroots" in one graph of + /// crates. + sysroot_src?: string; + /// List of groups of common cfg values, to allow + /// sharing them between crates. + /// + /// Maps from group name to its cfgs. Cfg follow + /// the same format as `Crate.cfg`. + cfg_groups?: { [key: string]: string[]; }; + /// The set of crates comprising the current + /// project. Must include all transitive + /// dependencies as well as sysroot crate (libstd, + /// libcore and such). + crates: Crate[]; + /// Configuration for CLI commands. + /// + /// These are used for running and debugging binaries + /// and tests without encoding build system-specific + /// knowledge into rust-analyzer. + /// + /// # Example + /// + /// Below is an example of a test runnable. `{label}` and `{test_id}` + /// are explained in `Runnable::args`'s documentation below. + /// + /// ```json + /// { + /// "program": "buck", + /// "args": [ + /// "test", + /// "{label}", + /// "--", + /// "{test_id}", + /// "--print-passing-details" + /// ], + /// "cwd": "/home/user/repo-root/", + /// "kind": "testOne" + /// } + /// ``` + runnables?: Runnable[]; +} + +interface Crate { + /// Optional crate name used for display purposes, + /// without affecting semantics. See the `deps` + /// key for semantically-significant crate names. + display_name?: string; + /// Path to the root module of the crate. + root_module: string; + /// Edition of the crate. + edition: '2015' | '2018' | '2021' | '2024'; + /// The version of the crate. Used for calculating + /// the correct docs.rs URL. + version?: string; + /// Dependencies + deps: Dep[]; + /// Should this crate be treated as a member of + /// current "workspace". + /// + /// By default, inferred from the `root_module` + /// (members are the crates which reside inside + /// the directory opened in the editor). + /// + /// Set this to `false` for things like standard + /// library and 3rd party crates to enable + /// performance optimizations (rust-analyzer + /// assumes that non-member crates don't change). + is_workspace_member?: boolean; + /// Optionally specify the (super)set of `.rs` + /// files comprising this crate. + /// + /// By default, rust-analyzer assumes that only + /// files under `root_module.parent` can belong + /// to a crate. `include_dirs` are included + /// recursively, unless a subdirectory is in + /// `exclude_dirs`. + /// + /// Different crates can share the same `source`. + /// + /// If two crates share an `.rs` file in common, + /// they *must* have the same `source`. + /// rust-analyzer assumes that files from one + /// source can't refer to files in another source. + source?: { + include_dirs: string[]; + exclude_dirs: string[]; + }; + /// List of cfg groups this crate inherits. + /// + /// All cfg in these groups will be concatenated to + /// `cfg`. It is impossible to replace a value from + /// the groups. + cfg_groups?: string[]; + /// The set of cfgs activated for a given crate, like + /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. + cfg: string[]; + /// Target tuple for this Crate. + /// + /// Used when running `rustc --print cfg` + /// to get target-specific cfgs. + target?: string; + /// Environment variables, used for + /// the `env!` macro + env: { [key: string]: string; }; + + /// Whether the crate is a proc-macro crate. + is_proc_macro: boolean; + /// For proc-macro crates, path to compiled + /// proc-macro (.so file). + proc_macro_dylib_path?: string; + + /// Repository, matching the URL that would be used + /// in Cargo.toml. + repository?: string; + + /// Build-specific data about this crate. + build?: BuildInfo; +} + +interface Dep { + /// Index of a crate in the `crates` array. + crate: number; + /// Name as should appear in the (implicit) + /// `extern crate name` declaration. + name: string; +} + +interface BuildInfo { + /// The name associated with this crate. + /// + /// This is determined by the build system that produced + /// the `rust-project.json` in question. For instance, if buck were used, + /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. + /// + /// Do not attempt to parse the contents of this string; it is a build system-specific + /// identifier similar to `Crate::display_name`. + label: string; + /// Path corresponding to the build system-specific file defining the crate. + build_file: string; + /// The kind of target. + /// + /// This information is used to determine what sort + /// of runnable codelens to provide, if any. + target_kind: 'bin' | 'lib' | 'test'; +} + +interface Runnable { + /// The program invoked by the runnable. + /// + /// For example, this might be `cargo`, `buck`, or `bazel`. + program: string; + /// The arguments passed to `program`. + args: string[]; + /// The current working directory of the runnable. + cwd: string; + /// Used to decide what code lens to offer. + /// + /// `testOne`: This runnable will be used when the user clicks the 'Run Test' + /// CodeLens above a test. + /// + /// The args for testOne can contain two template strings: + /// `{label}` and `{test_id}`. `{label}` will be replaced + /// with the `Build::label` and `{test_id}` will be replaced + /// with the test name. + kind: 'testOne' | string; +} +``` + +This format is provisional and subject to change. Specifically, the +`roots` setup will be different eventually. + +There are three ways to feed `rust-project.json` to rust-analyzer: + +- Place `rust-project.json` file at the root of the project, and + rust-analyzer will discover it. + +- Specify + `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in + the settings (and make sure that your LSP client sends settings as a + part of initialize request). + +- Specify + `"rust-analyzer.linkedProjects": [ { "roots": […], "crates": […] }]` + inline. + +Relative paths are interpreted relative to `rust-project.json` file +location or (for inline JSON) relative to `rootUri`. + +You can set the `RA_LOG` environment variable to `rust_analyzer=info` to +inspect how rust-analyzer handles config and project loading. + +Note that calls to `cargo check` are disabled when using +`rust-project.json` by default, so compilation errors and warnings will +no longer be sent to your LSP client. To enable these compilation errors +you will need to specify explicitly what command rust-analyzer should +run to perform the checks using the +`rust-analyzer.check.overrideCommand` configuration. As an example, the +following configuration explicitly sets `cargo check` as the `check` +command. + + { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } + +`check.overrideCommand` requires the command specified to output json +error messages for rust-analyzer to consume. The `--message-format=json` +flag does this for `cargo check` so whichever command you use must also +output errors in this format. See the [Configuration](#_configuration) +section for more information. diff --git a/src/tools/rust-analyzer/docs/book/src/privacy.md b/src/tools/rust-analyzer/docs/book/src/privacy.md new file mode 100644 index 00000000000..602c68d6f67 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/privacy.md @@ -0,0 +1,15 @@ +# Privacy + +The LSP server performs no network access in itself, but runs +`cargo metadata` which will update or download the crate registry and +the source code of the project dependencies. If enabled (the default), +build scripts and procedural macros can do anything. + +The Code extension does not access the network. + +Any other editor plugins are not under the control of the +`rust-analyzer` developers. For any privacy concerns, you should check +with their respective developers. + +For `rust-analyzer` developers, `cargo xtask release` uses the GitHub +API to put together the release notes. diff --git a/src/tools/rust-analyzer/docs/book/src/security.md b/src/tools/rust-analyzer/docs/book/src/security.md new file mode 100644 index 00000000000..1444af03248 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/security.md @@ -0,0 +1,19 @@ +# Security + +At the moment, rust-analyzer assumes that all code is trusted. Here is a +**non-exhaustive** list of ways to make rust-analyzer execute arbitrary +code: + +- proc macros and build scripts are executed by default + +- `.cargo/config` can override `rustc` with an arbitrary executable + +- `rust-toolchain.toml` can override `rustc` with an arbitrary + executable + +- VS Code plugin reads configuration from project directory, and that + can be used to override paths to various executables, like `rustfmt` + or `rust-analyzer` itself. + +- rust-analyzer’s syntax trees library uses a lot of `unsafe` and + hasn’t been properly audited for memory safety. \ No newline at end of file diff --git a/src/tools/rust-analyzer/docs/book/src/troubleshooting.md b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md new file mode 100644 index 00000000000..4092b9de990 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md @@ -0,0 +1,50 @@ +# Troubleshooting + +Start with looking at the rust-analyzer version. Try **rust-analyzer: +Show RA Version** in VS Code (using **Command Palette** feature +typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the +command line. If the date is more than a week ago, it’s better to update +rust-analyzer version. + +The next thing to check would be panic messages in rust-analyzer’s log. +Log messages are printed to stderr, in VS Code you can see them in the +`Output > Rust Analyzer Language Server` tab of the panel. To see more +logs, set the `RA_LOG=info` environment variable, this can be done +either by setting the environment variable manually or by using +`rust-analyzer.server.extraEnv`, note that both of these approaches +require the server to be restarted. + +To fully capture LSP messages between the editor and the server, run +the `rust-analyzer: Toggle LSP Logs` command and check `Output > Rust +Analyzer Language Server Trace`. + +The root cause for many "nothing works" problems is that rust-analyzer +fails to understand the project structure. To debug that, first note the +`rust-analyzer` section in the status bar. If it has an error icon and +red, that’s the problem (hover will have somewhat helpful error +message). **rust-analyzer: Status** prints dependency information for +the current file. Finally, `RA_LOG=project_model=debug` enables verbose +logs during project loading. + +If rust-analyzer outright crashes, try running +`rust-analyzer analysis-stats /path/to/project/directory/` on the +command line. This command type checks the whole project in batch mode +bypassing LSP machinery. + +When filing issues, it is useful (but not necessary) to try to minimize +examples. An ideal bug reproduction looks like this: + +```shell +$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash +$ rust-analyzer --version +rust-analyzer dd12184e4 2021-05-08 dev +$ rust-analyzer analysis-stats . +💀 💀 💀 +``` + +It is especially useful when the `repo` doesn’t use external crates or +the standard library. + +If you want to go as far as to modify the source code to debug the +problem, be sure to take a look at the [dev +docs](https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev)! diff --git a/src/tools/rust-analyzer/docs/dev/style.md b/src/tools/rust-analyzer/docs/dev/style.md index 4c5299bde3e..51b60ab2ebb 100644 --- a/src/tools/rust-analyzer/docs/dev/style.md +++ b/src/tools/rust-analyzer/docs/dev/style.md @@ -873,7 +873,7 @@ Use `anyhow::format_err!` rather than `anyhow::anyhow`. **Rationale:** consistent, boring, avoids stuttering. There's no specific guidance on the formatting of error messages, see [anyhow/#209](https://github.com/dtolnay/anyhow/issues/209). -Do not end error and context messages with `.` though. +Do not end error and context messages with `.` though. ## Early Returns @@ -1172,7 +1172,7 @@ MergeBehavior::Last => { **Rationale:** writing a sentence (or maybe even a paragraph) rather just "a comment" creates a more appropriate frame of mind. It tricks you into writing down more of the context you keep in your head while coding. -For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. +For `.md` files prefer a sentence-per-line format, don't wrap lines. If the line is too long, you want to split the sentence in two :-) **Rationale:** much easier to edit the text and read the diff, see [this link](https://asciidoctor.org/docs/asciidoc-recommended-practices/#one-sentence-per-line). diff --git a/src/tools/rust-analyzer/docs/user/.gitignore b/src/tools/rust-analyzer/docs/user/.gitignore deleted file mode 100644 index c32b1bcec2e..00000000000 --- a/src/tools/rust-analyzer/docs/user/.gitignore +++ /dev/null @@ -1 +0,0 @@ -manual.html diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc deleted file mode 100644 index b33de1956b8..00000000000 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ /dev/null @@ -1,1197 +0,0 @@ -[[rust-analyzer.assist.emitMustUse]]rust-analyzer.assist.emitMustUse (default: `false`):: -+ --- -Whether to insert #[must_use] when generating `as_` methods -for enum variants. --- -[[rust-analyzer.assist.expressionFillDefault]]rust-analyzer.assist.expressionFillDefault (default: `"todo"`):: -+ --- -Placeholder expression to use for missing expressions in assists. --- -[[rust-analyzer.assist.termSearch.borrowcheck]]rust-analyzer.assist.termSearch.borrowcheck (default: `true`):: -+ --- -Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. --- -[[rust-analyzer.assist.termSearch.fuel]]rust-analyzer.assist.termSearch.fuel (default: `1800`):: -+ --- -Term search fuel in "units of work" for assists (Defaults to 1800). --- -[[rust-analyzer.cachePriming.enable]]rust-analyzer.cachePriming.enable (default: `true`):: -+ --- -Warm up caches on project load. --- -[[rust-analyzer.cachePriming.numThreads]]rust-analyzer.cachePriming.numThreads (default: `"physical"`):: -+ --- -How many worker threads to handle priming caches. The default `0` means to pick automatically. --- -[[rust-analyzer.cargo.allTargets]]rust-analyzer.cargo.allTargets (default: `true`):: -+ --- -Pass `--all-targets` to cargo invocation. --- -[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: -+ --- -Automatically refresh project info via `cargo metadata` on -`Cargo.toml` or `.cargo/config.toml` changes. --- -[[rust-analyzer.cargo.buildScripts.enable]]rust-analyzer.cargo.buildScripts.enable (default: `true`):: -+ --- -Run build scripts (`build.rs`) for more precise code analysis. --- -[[rust-analyzer.cargo.buildScripts.invocationStrategy]]rust-analyzer.cargo.buildScripts.invocationStrategy (default: `"per_workspace"`):: -+ --- -Specifies the invocation strategy to use when running the build scripts command. -If `per_workspace` is set, the command will be executed for each Rust workspace with the -workspace as the working directory. -If `once` is set, the command will be executed once with the opened project as the -working directory. -This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` -is set. --- -[[rust-analyzer.cargo.buildScripts.overrideCommand]]rust-analyzer.cargo.buildScripts.overrideCommand (default: `null`):: -+ --- -Override the command rust-analyzer uses to run build scripts and -build procedural macros. The command is required to output json -and should therefore include `--message-format=json` or a similar -option. - -If there are multiple linked projects/workspaces, this command is invoked for -each of them, with the working directory being the workspace root -(i.e., the folder containing the `Cargo.toml`). This can be overwritten -by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. - -By default, a cargo invocation will be constructed for the configured -targets and features, with the following base command line: - -```bash -cargo check --quiet --workspace --message-format=json --all-targets --keep-going -``` -. --- -[[rust-analyzer.cargo.buildScripts.rebuildOnSave]]rust-analyzer.cargo.buildScripts.rebuildOnSave (default: `true`):: -+ --- -Rerun proc-macros building/build-scripts running when proc-macro -or build-script sources change and are saved. --- -[[rust-analyzer.cargo.buildScripts.useRustcWrapper]]rust-analyzer.cargo.buildScripts.useRustcWrapper (default: `true`):: -+ --- -Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to -avoid checking unnecessary things. --- -[[rust-analyzer.cargo.cfgs]]rust-analyzer.cargo.cfgs:: -+ --- -Default: ----- -[ - "debug_assertions", - "miri" -] ----- -List of cfg options to enable with the given values. - --- -[[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`):: -+ --- -Extra arguments that are passed to every cargo invocation. --- -[[rust-analyzer.cargo.extraEnv]]rust-analyzer.cargo.extraEnv (default: `{}`):: -+ --- -Extra environment variables that will be set when running cargo, rustc -or other commands within the workspace. Useful for setting RUSTFLAGS. --- -[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: -+ --- -List of features to activate. - -Set this to `"all"` to pass `--all-features` to cargo. --- -[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: -+ --- -Whether to pass `--no-default-features` to cargo. --- -[[rust-analyzer.cargo.sysroot]]rust-analyzer.cargo.sysroot (default: `"discover"`):: -+ --- -Relative path to the sysroot, or "discover" to try to automatically find it via -"rustc --print sysroot". - -Unsetting this disables sysroot loading. - -This option does not take effect until rust-analyzer is restarted. --- -[[rust-analyzer.cargo.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`):: -+ --- -Relative path to the sysroot library sources. If left unset, this will default to -`{cargo.sysroot}/lib/rustlib/src/rust/library`. - -This option does not take effect until rust-analyzer is restarted. --- -[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: -+ --- -Compilation target override (target tuple). --- -[[rust-analyzer.cargo.targetDir]]rust-analyzer.cargo.targetDir (default: `null`):: -+ --- -Optional path to a rust-analyzer specific target directory. -This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro -building from locking the `Cargo.lock` at the expense of duplicating build artifacts. - -Set to `true` to use a subdirectory of the existing target directory or -set to a path relative to the workspace to use that path. --- -[[rust-analyzer.cfg.setTest]]rust-analyzer.cfg.setTest (default: `true`):: -+ --- -Set `cfg(test)` for local crates. Defaults to true. --- -[[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`):: -+ --- -Run the check command for diagnostics on save. --- -[[rust-analyzer.check.allTargets]]rust-analyzer.check.allTargets (default: `null`):: -+ --- -Check all targets and tests (`--all-targets`). Defaults to -`#rust-analyzer.cargo.allTargets#`. --- -[[rust-analyzer.check.command]]rust-analyzer.check.command (default: `"check"`):: -+ --- -Cargo command to use for `cargo check`. --- -[[rust-analyzer.check.extraArgs]]rust-analyzer.check.extraArgs (default: `[]`):: -+ --- -Extra arguments for `cargo check`. --- -[[rust-analyzer.check.extraEnv]]rust-analyzer.check.extraEnv (default: `{}`):: -+ --- -Extra environment variables that will be set when running `cargo check`. -Extends `#rust-analyzer.cargo.extraEnv#`. --- -[[rust-analyzer.check.features]]rust-analyzer.check.features (default: `null`):: -+ --- -List of features to activate. Defaults to -`#rust-analyzer.cargo.features#`. - -Set to `"all"` to pass `--all-features` to Cargo. --- -[[rust-analyzer.check.ignore]]rust-analyzer.check.ignore (default: `[]`):: -+ --- -List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. - -For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... --- -[[rust-analyzer.check.invocationStrategy]]rust-analyzer.check.invocationStrategy (default: `"per_workspace"`):: -+ --- -Specifies the invocation strategy to use when running the check command. -If `per_workspace` is set, the command will be executed for each workspace. -If `once` is set, the command will be executed once. -This config only has an effect when `#rust-analyzer.check.overrideCommand#` -is set. --- -[[rust-analyzer.check.noDefaultFeatures]]rust-analyzer.check.noDefaultFeatures (default: `null`):: -+ --- -Whether to pass `--no-default-features` to Cargo. Defaults to -`#rust-analyzer.cargo.noDefaultFeatures#`. --- -[[rust-analyzer.check.overrideCommand]]rust-analyzer.check.overrideCommand (default: `null`):: -+ --- -Override the command rust-analyzer uses instead of `cargo check` for -diagnostics on save. The command is required to output json and -should therefore include `--message-format=json` or a similar option -(if your client supports the `colorDiagnosticOutput` experimental -capability, you can use `--message-format=json-diagnostic-rendered-ansi`). - -If you're changing this because you're using some tool wrapping -Cargo, you might also want to change -`#rust-analyzer.cargo.buildScripts.overrideCommand#`. - -If there are multiple linked projects/workspaces, this command is invoked for -each of them, with the working directory being the workspace root -(i.e., the folder containing the `Cargo.toml`). This can be overwritten -by changing `#rust-analyzer.check.invocationStrategy#`. - -If `$saved_file` is part of the command, rust-analyzer will pass -the absolute path of the saved file to the provided command. This is -intended to be used with non-Cargo build systems. -Note that `$saved_file` is experimental and may be removed in the future. - -An example command would be: - -```bash -cargo check --workspace --message-format=json --all-targets -``` -. --- -[[rust-analyzer.check.targets]]rust-analyzer.check.targets (default: `null`):: -+ --- -Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. - -Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. -`["aarch64-apple-darwin", "x86_64-apple-darwin"]`. - -Aliased as `"checkOnSave.targets"`. --- -[[rust-analyzer.check.workspace]]rust-analyzer.check.workspace (default: `true`):: -+ --- -Whether `--workspace` should be passed to `cargo check`. -If false, `-p <package>` will be passed instead if applicable. In case it is not, no -check will be performed. --- -[[rust-analyzer.completion.addSemicolonToUnit]]rust-analyzer.completion.addSemicolonToUnit (default: `true`):: -+ --- -Whether to automatically add a semicolon when completing unit-returning functions. - -In `match` arms it completes a comma instead. --- -[[rust-analyzer.completion.autoAwait.enable]]rust-analyzer.completion.autoAwait.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. --- -[[rust-analyzer.completion.autoIter.enable]]rust-analyzer.completion.autoIter.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. --- -[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically add imports when completed. -Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. --- -[[rust-analyzer.completion.autoimport.exclude]]rust-analyzer.completion.autoimport.exclude:: -+ --- -Default: ----- -[ - { - "path": "core::borrow::Borrow", - "type": "methods" - }, - { - "path": "core::borrow::BorrowMut", - "type": "methods" - } -] ----- -A list of full paths to items to exclude from auto-importing completions. - -Traits in this list won't have their methods suggested in completions unless the trait -is in scope. - -You can either specify a string path which defaults to type "always" or use the more verbose -form `{ "path": "path::to::item", type: "always" }`. - -For traits the type "methods" can be used to only exclude the methods but not the trait itself. - -This setting also inherits `#rust-analyzer.completion.excludeTraits#`. - --- -[[rust-analyzer.completion.autoself.enable]]rust-analyzer.completion.autoself.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically show method calls and field accesses -with `self` prefixed to them when inside a method. --- -[[rust-analyzer.completion.callable.snippets]]rust-analyzer.completion.callable.snippets (default: `"fill_arguments"`):: -+ --- -Whether to add parenthesis and argument snippets when completing function. --- -[[rust-analyzer.completion.excludeTraits]]rust-analyzer.completion.excludeTraits (default: `[]`):: -+ --- -A list of full paths to traits whose methods to exclude from completion. - -Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. - -Note that the trait themselves can still be completed. --- -[[rust-analyzer.completion.fullFunctionSignatures.enable]]rust-analyzer.completion.fullFunctionSignatures.enable (default: `false`):: -+ --- -Whether to show full function/method signatures in completion docs. --- -[[rust-analyzer.completion.hideDeprecated]]rust-analyzer.completion.hideDeprecated (default: `false`):: -+ --- -Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. --- -[[rust-analyzer.completion.limit]]rust-analyzer.completion.limit (default: `null`):: -+ --- -Maximum number of completions to return. If `None`, the limit is infinite. --- -[[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`):: -+ --- -Whether to show postfix snippets like `dbg`, `if`, `not`, etc. --- -[[rust-analyzer.completion.privateEditable.enable]]rust-analyzer.completion.privateEditable.enable (default: `false`):: -+ --- -Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. --- -[[rust-analyzer.completion.snippets.custom]]rust-analyzer.completion.snippets.custom:: -+ --- -Default: ----- -{ - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - } -} ----- -Custom completion snippets. - --- -[[rust-analyzer.completion.termSearch.enable]]rust-analyzer.completion.termSearch.enable (default: `false`):: -+ --- -Whether to enable term search based snippets like `Some(foo.bar().baz())`. --- -[[rust-analyzer.completion.termSearch.fuel]]rust-analyzer.completion.termSearch.fuel (default: `1000`):: -+ --- -Term search fuel in "units of work" for autocompletion (Defaults to 1000). --- -[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: -+ --- -List of rust-analyzer diagnostics to disable. --- -[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: -+ --- -Whether to show native rust-analyzer diagnostics. --- -[[rust-analyzer.diagnostics.experimental.enable]]rust-analyzer.diagnostics.experimental.enable (default: `false`):: -+ --- -Whether to show experimental rust-analyzer diagnostics that might -have more false positives than usual. --- -[[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`):: -+ --- -Map of prefixes to be substituted when parsing diagnostic file paths. -This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. --- -[[rust-analyzer.diagnostics.styleLints.enable]]rust-analyzer.diagnostics.styleLints.enable (default: `false`):: -+ --- -Whether to run additional style lints. --- -[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: -+ --- -List of warnings that should be displayed with hint severity. - -The warnings will be indicated by faded text or three dots in code -and will not show up in the `Problems Panel`. --- -[[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: -+ --- -List of warnings that should be displayed with info severity. - -The warnings will be indicated by a blue squiggly underline in code -and a blue icon in the `Problems Panel`. --- -[[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`):: -+ --- -These directories will be ignored by rust-analyzer. They are -relative to the workspace root, and globs are not supported. You may -also need to add the folders to Code's `files.watcherExclude`. --- -[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: -+ --- -Controls file watching implementation. --- -[[rust-analyzer.highlightRelated.breakPoints.enable]]rust-analyzer.highlightRelated.breakPoints.enable (default: `true`):: -+ --- -Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. --- -[[rust-analyzer.highlightRelated.closureCaptures.enable]]rust-analyzer.highlightRelated.closureCaptures.enable (default: `true`):: -+ --- -Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. --- -[[rust-analyzer.highlightRelated.exitPoints.enable]]rust-analyzer.highlightRelated.exitPoints.enable (default: `true`):: -+ --- -Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). --- -[[rust-analyzer.highlightRelated.references.enable]]rust-analyzer.highlightRelated.references.enable (default: `true`):: -+ --- -Enables highlighting of related references while the cursor is on any identifier. --- -[[rust-analyzer.highlightRelated.yieldPoints.enable]]rust-analyzer.highlightRelated.yieldPoints.enable (default: `true`):: -+ --- -Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. --- -[[rust-analyzer.hover.actions.debug.enable]]rust-analyzer.hover.actions.debug.enable (default: `true`):: -+ --- -Whether to show `Debug` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.enable]]rust-analyzer.hover.actions.enable (default: `true`):: -+ --- -Whether to show HoverActions in Rust files. --- -[[rust-analyzer.hover.actions.gotoTypeDef.enable]]rust-analyzer.hover.actions.gotoTypeDef.enable (default: `true`):: -+ --- -Whether to show `Go to Type Definition` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.implementations.enable]]rust-analyzer.hover.actions.implementations.enable (default: `true`):: -+ --- -Whether to show `Implementations` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.references.enable]]rust-analyzer.hover.actions.references.enable (default: `false`):: -+ --- -Whether to show `References` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.run.enable]]rust-analyzer.hover.actions.run.enable (default: `true`):: -+ --- -Whether to show `Run` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.updateTest.enable]]rust-analyzer.hover.actions.updateTest.enable (default: `true`):: -+ --- -Whether to show `Update Test` action. Only applies when -`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. --- -[[rust-analyzer.hover.documentation.enable]]rust-analyzer.hover.documentation.enable (default: `true`):: -+ --- -Whether to show documentation on hover. --- -[[rust-analyzer.hover.documentation.keywords.enable]]rust-analyzer.hover.documentation.keywords.enable (default: `true`):: -+ --- -Whether to show keyword hover popups. Only applies when -`#rust-analyzer.hover.documentation.enable#` is set. --- -[[rust-analyzer.hover.links.enable]]rust-analyzer.hover.links.enable (default: `true`):: -+ --- -Use markdown syntax for links on hover. --- -[[rust-analyzer.hover.maxSubstitutionLength]]rust-analyzer.hover.maxSubstitutionLength (default: `20`):: -+ --- -Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. - -This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. - -The default is 20 characters. --- -[[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`):: -+ --- -How to render the align information in a memory layout hover. --- -[[rust-analyzer.hover.memoryLayout.enable]]rust-analyzer.hover.memoryLayout.enable (default: `true`):: -+ --- -Whether to show memory layout data on hover. --- -[[rust-analyzer.hover.memoryLayout.niches]]rust-analyzer.hover.memoryLayout.niches (default: `false`):: -+ --- -How to render the niche information in a memory layout hover. --- -[[rust-analyzer.hover.memoryLayout.offset]]rust-analyzer.hover.memoryLayout.offset (default: `"hexadecimal"`):: -+ --- -How to render the offset information in a memory layout hover. --- -[[rust-analyzer.hover.memoryLayout.size]]rust-analyzer.hover.memoryLayout.size (default: `"both"`):: -+ --- -How to render the size information in a memory layout hover. --- -[[rust-analyzer.hover.show.enumVariants]]rust-analyzer.hover.show.enumVariants (default: `5`):: -+ --- -How many variants of an enum to display when hovering on. Show none if empty. --- -[[rust-analyzer.hover.show.fields]]rust-analyzer.hover.show.fields (default: `5`):: -+ --- -How many fields of a struct, variant or union to display when hovering on. Show none if empty. --- -[[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`):: -+ --- -How many associated items of a trait to display when hovering a trait. --- -[[rust-analyzer.imports.granularity.enforce]]rust-analyzer.imports.granularity.enforce (default: `false`):: -+ --- -Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. --- -[[rust-analyzer.imports.granularity.group]]rust-analyzer.imports.granularity.group (default: `"crate"`):: -+ --- -How imports should be grouped into use statements. --- -[[rust-analyzer.imports.group.enable]]rust-analyzer.imports.group.enable (default: `true`):: -+ --- -Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines. --- -[[rust-analyzer.imports.merge.glob]]rust-analyzer.imports.merge.glob (default: `true`):: -+ --- -Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. --- -[[rust-analyzer.imports.preferNoStd]]rust-analyzer.imports.preferNoStd (default: `false`):: -+ --- -Prefer to unconditionally use imports of the core and alloc crate, over the std crate. --- -[[rust-analyzer.imports.preferPrelude]]rust-analyzer.imports.preferPrelude (default: `false`):: -+ --- -Whether to prefer import paths containing a `prelude` module. --- -[[rust-analyzer.imports.prefix]]rust-analyzer.imports.prefix (default: `"plain"`):: -+ --- -The path structure for newly inserted paths to use. --- -[[rust-analyzer.imports.prefixExternPrelude]]rust-analyzer.imports.prefixExternPrelude (default: `false`):: -+ --- -Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". --- -[[rust-analyzer.inlayHints.bindingModeHints.enable]]rust-analyzer.inlayHints.bindingModeHints.enable (default: `false`):: -+ --- -Whether to show inlay type hints for binding modes. --- -[[rust-analyzer.inlayHints.chainingHints.enable]]rust-analyzer.inlayHints.chainingHints.enable (default: `true`):: -+ --- -Whether to show inlay type hints for method chains. --- -[[rust-analyzer.inlayHints.closingBraceHints.enable]]rust-analyzer.inlayHints.closingBraceHints.enable (default: `true`):: -+ --- -Whether to show inlay hints after a closing `}` to indicate what item it belongs to. --- -[[rust-analyzer.inlayHints.closingBraceHints.minLines]]rust-analyzer.inlayHints.closingBraceHints.minLines (default: `25`):: -+ --- -Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 -to always show them). --- -[[rust-analyzer.inlayHints.closureCaptureHints.enable]]rust-analyzer.inlayHints.closureCaptureHints.enable (default: `false`):: -+ --- -Whether to show inlay hints for closure captures. --- -[[rust-analyzer.inlayHints.closureReturnTypeHints.enable]]rust-analyzer.inlayHints.closureReturnTypeHints.enable (default: `"never"`):: -+ --- -Whether to show inlay type hints for return types of closures. --- -[[rust-analyzer.inlayHints.closureStyle]]rust-analyzer.inlayHints.closureStyle (default: `"impl_fn"`):: -+ --- -Closure notation in type and chaining inlay hints. --- -[[rust-analyzer.inlayHints.discriminantHints.enable]]rust-analyzer.inlayHints.discriminantHints.enable (default: `"never"`):: -+ --- -Whether to show enum variant discriminant hints. --- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.enable]]rust-analyzer.inlayHints.expressionAdjustmentHints.enable (default: `"never"`):: -+ --- -Whether to show inlay hints for type adjustments. --- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe]]rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe (default: `false`):: -+ --- -Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. --- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.mode]]rust-analyzer.inlayHints.expressionAdjustmentHints.mode (default: `"prefix"`):: -+ --- -Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). --- -[[rust-analyzer.inlayHints.genericParameterHints.const.enable]]rust-analyzer.inlayHints.genericParameterHints.const.enable (default: `true`):: -+ --- -Whether to show const generic parameter name inlay hints. --- -[[rust-analyzer.inlayHints.genericParameterHints.lifetime.enable]]rust-analyzer.inlayHints.genericParameterHints.lifetime.enable (default: `false`):: -+ --- -Whether to show generic lifetime parameter name inlay hints. --- -[[rust-analyzer.inlayHints.genericParameterHints.type.enable]]rust-analyzer.inlayHints.genericParameterHints.type.enable (default: `false`):: -+ --- -Whether to show generic type parameter name inlay hints. --- -[[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`):: -+ --- -Whether to show implicit drop hints. --- -[[rust-analyzer.inlayHints.implicitSizedBoundHints.enable]]rust-analyzer.inlayHints.implicitSizedBoundHints.enable (default: `false`):: -+ --- -Whether to show inlay hints for the implied type parameter `Sized` bound. --- -[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`):: -+ --- -Whether to show inlay type hints for elided lifetimes in function signatures. --- -[[rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames]]rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames (default: `false`):: -+ --- -Whether to prefer using parameter names as the name for elided lifetime hints if possible. --- -[[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `25`):: -+ --- -Maximum length for inlay hints. Set to null to have an unlimited length. --- -[[rust-analyzer.inlayHints.parameterHints.enable]]rust-analyzer.inlayHints.parameterHints.enable (default: `true`):: -+ --- -Whether to show function parameter name inlay hints at the call -site. --- -[[rust-analyzer.inlayHints.rangeExclusiveHints.enable]]rust-analyzer.inlayHints.rangeExclusiveHints.enable (default: `false`):: -+ --- -Whether to show exclusive range inlay hints. --- -[[rust-analyzer.inlayHints.reborrowHints.enable]]rust-analyzer.inlayHints.reborrowHints.enable (default: `"never"`):: -+ --- -Whether to show inlay hints for compiler inserted reborrows. -This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. --- -[[rust-analyzer.inlayHints.renderColons]]rust-analyzer.inlayHints.renderColons (default: `true`):: -+ --- -Whether to render leading colons for type hints, and trailing colons for parameter hints. --- -[[rust-analyzer.inlayHints.typeHints.enable]]rust-analyzer.inlayHints.typeHints.enable (default: `true`):: -+ --- -Whether to show inlay type hints for variables. --- -[[rust-analyzer.inlayHints.typeHints.hideClosureInitialization]]rust-analyzer.inlayHints.typeHints.hideClosureInitialization (default: `false`):: -+ --- -Whether to hide inlay type hints for `let` statements that initialize to a closure. -Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. --- -[[rust-analyzer.inlayHints.typeHints.hideNamedConstructor]]rust-analyzer.inlayHints.typeHints.hideNamedConstructor (default: `false`):: -+ --- -Whether to hide inlay type hints for constructors. --- -[[rust-analyzer.interpret.tests]]rust-analyzer.interpret.tests (default: `false`):: -+ --- -Enables the experimental support for interpreting tests. --- -[[rust-analyzer.joinLines.joinAssignments]]rust-analyzer.joinLines.joinAssignments (default: `true`):: -+ --- -Join lines merges consecutive declaration and initialization of an assignment. --- -[[rust-analyzer.joinLines.joinElseIf]]rust-analyzer.joinLines.joinElseIf (default: `true`):: -+ --- -Join lines inserts else between consecutive ifs. --- -[[rust-analyzer.joinLines.removeTrailingComma]]rust-analyzer.joinLines.removeTrailingComma (default: `true`):: -+ --- -Join lines removes trailing commas. --- -[[rust-analyzer.joinLines.unwrapTrivialBlock]]rust-analyzer.joinLines.unwrapTrivialBlock (default: `true`):: -+ --- -Join lines unwraps trivial blocks. --- -[[rust-analyzer.lens.debug.enable]]rust-analyzer.lens.debug.enable (default: `true`):: -+ --- -Whether to show `Debug` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`):: -+ --- -Whether to show CodeLens in Rust files. --- -[[rust-analyzer.lens.implementations.enable]]rust-analyzer.lens.implementations.enable (default: `true`):: -+ --- -Whether to show `Implementations` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.location]]rust-analyzer.lens.location (default: `"above_name"`):: -+ --- -Where to render annotations. --- -[[rust-analyzer.lens.references.adt.enable]]rust-analyzer.lens.references.adt.enable (default: `false`):: -+ --- -Whether to show `References` lens for Struct, Enum, and Union. -Only applies when `#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.references.enumVariant.enable]]rust-analyzer.lens.references.enumVariant.enable (default: `false`):: -+ --- -Whether to show `References` lens for Enum Variants. -Only applies when `#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.references.method.enable]]rust-analyzer.lens.references.method.enable (default: `false`):: -+ --- -Whether to show `Method References` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.references.trait.enable]]rust-analyzer.lens.references.trait.enable (default: `false`):: -+ --- -Whether to show `References` lens for Trait. -Only applies when `#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.run.enable]]rust-analyzer.lens.run.enable (default: `true`):: -+ --- -Whether to show `Run` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.updateTest.enable]]rust-analyzer.lens.updateTest.enable (default: `true`):: -+ --- -Whether to show `Update Test` lens. Only applies when -`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. --- -[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: -+ --- -Disable project auto-discovery in favor of explicitly specified set -of projects. - -Elements must be paths pointing to `Cargo.toml`, -`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON -objects in `rust-project.json` format. --- -[[rust-analyzer.lru.capacity]]rust-analyzer.lru.capacity (default: `null`):: -+ --- -Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. --- -[[rust-analyzer.lru.query.capacities]]rust-analyzer.lru.query.capacities (default: `{}`):: -+ --- -Sets the LRU capacity of the specified queries. --- -[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: -+ --- -Whether to show `can't find Cargo.toml` error message. --- -[[rust-analyzer.numThreads]]rust-analyzer.numThreads (default: `null`):: -+ --- -How many worker threads in the main loop. The default `null` means to pick automatically. --- -[[rust-analyzer.procMacro.attributes.enable]]rust-analyzer.procMacro.attributes.enable (default: `true`):: -+ --- -Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. --- -[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `true`):: -+ --- -Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. --- -[[rust-analyzer.procMacro.ignored]]rust-analyzer.procMacro.ignored (default: `{}`):: -+ --- -These proc-macros will be ignored when trying to expand them. - -This config takes a map of crate names with the exported proc-macro names to ignore as values. --- -[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: -+ --- -Internal config, path to proc-macro server executable. --- -[[rust-analyzer.references.excludeImports]]rust-analyzer.references.excludeImports (default: `false`):: -+ --- -Exclude imports from find-all-references. --- -[[rust-analyzer.references.excludeTests]]rust-analyzer.references.excludeTests (default: `false`):: -+ --- -Exclude tests from find-all-references and call-hierarchy. --- -[[rust-analyzer.runnables.command]]rust-analyzer.runnables.command (default: `null`):: -+ --- -Command to be executed instead of 'cargo' for runnables. --- -[[rust-analyzer.runnables.extraArgs]]rust-analyzer.runnables.extraArgs (default: `[]`):: -+ --- -Additional arguments to be passed to cargo for runnables such as -tests or binaries. For example, it may be `--release`. --- -[[rust-analyzer.runnables.extraTestBinaryArgs]]rust-analyzer.runnables.extraTestBinaryArgs:: -+ --- -Default: ----- -[ - "--show-output" -] ----- -Additional arguments to be passed through Cargo to launched tests, benchmarks, or -doc-tests. - -Unless the launched target uses a -[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), -they will end up being interpreted as options to -[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). - --- -[[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`):: -+ --- -Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private -projects, or "discover" to try to automatically find it if the `rustc-dev` component -is installed. - -Any project which uses rust-analyzer with the rustcPrivate -crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. - -This option does not take effect until rust-analyzer is restarted. --- -[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: -+ --- -Additional arguments to `rustfmt`. --- -[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: -+ --- -Advanced option, fully override the command rust-analyzer uses for -formatting. This should be the equivalent of `rustfmt` here, and -not that of `cargo fmt`. The file contents will be passed on the -standard input and the formatted result will be read from the -standard output. --- -[[rust-analyzer.rustfmt.rangeFormatting.enable]]rust-analyzer.rustfmt.rangeFormatting.enable (default: `false`):: -+ --- -Enables the use of rustfmt's unstable range formatting command for the -`textDocument/rangeFormatting` request. The rustfmt option is unstable and only -available on a nightly build. --- -[[rust-analyzer.semanticHighlighting.doc.comment.inject.enable]]rust-analyzer.semanticHighlighting.doc.comment.inject.enable (default: `true`):: -+ --- -Inject additional highlighting into doc comments. - -When enabled, rust-analyzer will highlight rust source in doc comments as well as intra -doc links. --- -[[rust-analyzer.semanticHighlighting.nonStandardTokens]]rust-analyzer.semanticHighlighting.nonStandardTokens (default: `true`):: -+ --- -Whether the server is allowed to emit non-standard tokens and modifiers. --- -[[rust-analyzer.semanticHighlighting.operator.enable]]rust-analyzer.semanticHighlighting.operator.enable (default: `true`):: -+ --- -Use semantic tokens for operators. - -When disabled, rust-analyzer will emit semantic tokens only for operator tokens when -they are tagged with modifiers. --- -[[rust-analyzer.semanticHighlighting.operator.specialization.enable]]rust-analyzer.semanticHighlighting.operator.specialization.enable (default: `false`):: -+ --- -Use specialized semantic tokens for operators. - -When enabled, rust-analyzer will emit special token types for operator tokens instead -of the generic `operator` token type. --- -[[rust-analyzer.semanticHighlighting.punctuation.enable]]rust-analyzer.semanticHighlighting.punctuation.enable (default: `false`):: -+ --- -Use semantic tokens for punctuation. - -When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when -they are tagged with modifiers or have a special role. --- -[[rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang]]rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang (default: `false`):: -+ --- -When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro -calls. --- -[[rust-analyzer.semanticHighlighting.punctuation.specialization.enable]]rust-analyzer.semanticHighlighting.punctuation.specialization.enable (default: `false`):: -+ --- -Use specialized semantic tokens for punctuation. - -When enabled, rust-analyzer will emit special token types for punctuation tokens instead -of the generic `punctuation` token type. --- -[[rust-analyzer.semanticHighlighting.strings.enable]]rust-analyzer.semanticHighlighting.strings.enable (default: `true`):: -+ --- -Use semantic tokens for strings. - -In some editors (e.g. vscode) semantic tokens override other highlighting grammars. -By disabling semantic tokens for strings, other grammars can be used to highlight -their contents. --- -[[rust-analyzer.signatureInfo.detail]]rust-analyzer.signatureInfo.detail (default: `"full"`):: -+ --- -Show full signature of the callable. Only shows parameters if disabled. --- -[[rust-analyzer.signatureInfo.documentation.enable]]rust-analyzer.signatureInfo.documentation.enable (default: `true`):: -+ --- -Show documentation. --- -[[rust-analyzer.typing.triggerChars]]rust-analyzer.typing.triggerChars (default: `"=."`):: -+ --- -Specify the characters allowed to invoke special on typing triggers. -- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression -- typing `=` between two expressions adds `;` when in statement position -- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position -- typing `.` in a chain method call auto-indents -- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression -- typing `{` in a use item adds a closing `}` in the right place -- typing `>` to complete a return type `->` will insert a whitespace after it -- typing `<` in a path or type position inserts a closing `>` after the path or type. --- -[[rust-analyzer.vfs.extraIncludes]]rust-analyzer.vfs.extraIncludes (default: `[]`):: -+ --- -Additional paths to include in the VFS. Generally for code that is -generated or otherwise managed by a build system outside of Cargo, -though Cargo might be the eventual consumer. --- -[[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`):: -+ --- -Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. - -[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. -`progress_label` is used for the title in progress indicators, whereas `files_to_watch` -is used to determine which build system-specific files should be watched in order to -reload rust-analyzer. - -Below is an example of a valid configuration: -```json -"rust-analyzer.workspace.discoverConfig": { - "command": [ - "rust-project", - "develop-json" - ], - "progressLabel": "rust-analyzer", - "filesToWatch": [ - "BUCK" - ] -} -``` - -## On `DiscoverWorkspaceConfig::command` - -**Warning**: This format is provisional and subject to change. - -[`DiscoverWorkspaceConfig::command`] *must* return a JSON object -corresponding to `DiscoverProjectData::Finished`: - -```norun -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(tag = "kind")] -#[serde(rename_all = "snake_case")] -enum DiscoverProjectData { - Finished { buildfile: Utf8PathBuf, project: ProjectJsonData }, - Error { error: String, source: Option<String> }, - Progress { message: String }, -} -``` - -As JSON, `DiscoverProjectData::Finished` is: - -```json -{ - // the internally-tagged representation of the enum. - "kind": "finished", - // the file used by a non-Cargo build system to define - // a package or target. - "buildfile": "rust-analyzer/BUILD", - // the contents of a rust-project.json, elided for brevity - "project": { - "sysroot": "foo", - "crates": [] - } -} -``` - -It is encouraged, but not required, to use the other variants on -`DiscoverProjectData` to provide a more polished end-user experience. - -`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, -which will be substituted with the JSON-serialized form of the following -enum: - -```norun -#[derive(PartialEq, Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum DiscoverArgument { - Path(AbsPathBuf), - Buildfile(AbsPathBuf), -} -``` - -The JSON representation of `DiscoverArgument::Path` is: - -```json -{ - "path": "src/main.rs" -} -``` - -Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: - -``` -{ - "buildfile": "BUILD" -} -``` - -`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, -and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to -to update an existing workspace. As a reference for implementors, -buck2's `rust-project` will likely be useful: -https://github.com/facebook/buck2/tree/main/integrations/rust-project. --- -[[rust-analyzer.workspace.symbol.search.kind]]rust-analyzer.workspace.symbol.search.kind (default: `"only_types"`):: -+ --- -Workspace symbol search kind. --- -[[rust-analyzer.workspace.symbol.search.limit]]rust-analyzer.workspace.symbol.search.limit (default: `128`):: -+ --- -Limits the number of items returned from a workspace symbol search (Defaults to 128). -Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. -Other clients requires all results upfront and might require a higher limit. --- -[[rust-analyzer.workspace.symbol.search.scope]]rust-analyzer.workspace.symbol.search.scope (default: `"workspace"`):: -+ --- -Workspace symbol search scope. --- diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc deleted file mode 100644 index 4a2a6f2e368..00000000000 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ /dev/null @@ -1,1121 +0,0 @@ -= User Manual -:toc: preamble -:sectanchors: -:page-layout: post -:icons: font -:source-highlighter: rouge -:experimental: - -//// -IMPORTANT: the master copy of this document lives in the https://github.com/rust-lang/rust-analyzer repository -//// - -At its core, rust-analyzer is a *library* for semantic analysis of Rust code as it changes over time. -This manual focuses on a specific usage of the library -- running it as part of a server that implements the -https://microsoft.github.io/language-server-protocol/[Language Server Protocol] (LSP). -The LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic features like completion or goto definition by talking to an external language server process. - -[TIP] -==== -[.lead] -To improve this document, send a pull request: + -https://github.com/rust-lang/rust-analyzer/blob/master/docs/user/manual.adoc[https://github.com/rust-analyzer/.../manual.adoc] - -The manual is written in https://asciidoc.org[AsciiDoc] and includes some extra files which are generated from the source code. Run `cargo test` and `cargo xtask codegen` to create these and then `asciidoctor manual.adoc` to create an HTML copy. -==== - -If you have questions about using rust-analyzer, please ask them in the https://users.rust-lang.org/c/ide/14["`IDEs and Editors`"] topic of Rust users forum. - -== Installation - -In theory, one should be able to just install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> and have it automatically work with any editor. -We are not there yet, so some editor specific setup is required. - -Additionally, rust-analyzer needs the sources of the standard library. -If the source code is not present, rust-analyzer will attempt to install it automatically. - -To add the sources manually, run the following command: - -```bash -$ rustup component add rust-src -``` - -=== Toolchain - -Only the latest stable standard library source is officially supported for use with rust-analyzer. -If you are using an older toolchain or have an override set, rust-analyzer may fail to understand the Rust source. -You will either need to update your toolchain or use an older version of rust-analyzer that is compatible with your toolchain. - -If you are using an override in your project, you can still force rust-analyzer to use the stable toolchain via the environment variable `RUSTUP_TOOLCHAIN`. -For example, with VS Code or coc-rust-analyzer: - -[source,json] ----- -{ "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } } ----- - -=== VS Code - -This is the best supported editor at the moment. -The rust-analyzer plugin for VS Code is maintained -https://github.com/rust-lang/rust-analyzer/tree/master/editors/code[in tree]. - -You can install the latest release of the plugin from -https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer[the marketplace]. - -Note that the plugin may cause conflicts with the -https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[previous official Rust plugin]. -The latter is no longer maintained and should be uninstalled. - -The server binary is stored in the extension install directory, which starts with `rust-lang.rust-analyzer-` and is located under: - -* Linux: `~/.vscode/extensions` -* Linux (Remote, such as WSL): `~/.vscode-server/extensions` -* macOS: `~/.vscode/extensions` -* Windows: `%USERPROFILE%\.vscode\extensions` - -As an exception, on NixOS, the extension makes a copy of the server and stores it under `~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`. - -Note that we only support the two most recent versions of VS Code. - -==== Updates - -The extension will be updated automatically as new versions become available. -It will ask your permission to download the matching language server version binary if needed. - -===== Nightly - -We ship nightly releases for VS Code. -To help us out by testing the newest code, you can enable pre-release versions in the Code extension page. - -==== Manual installation - -Alternatively, download a VSIX corresponding to your platform from the -https://github.com/rust-lang/rust-analyzer/releases[releases] page. - -Install the extension with the `Extensions: Install from VSIX` command within VS Code, or from the command line via: -[source] ----- -$ code --install-extension /path/to/rust-analyzer.vsix ----- - -If you are running an unsupported platform, you can install `rust-analyzer-no-server.vsix` and compile or obtain a server binary. -Copy the server anywhere, then add the path to your settings.json, for example: -[source,json] ----- -{ "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" } ----- - -==== Building From Source - -Both the server and the Code plugin can be installed from source: - -[source] ----- -$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer -$ cargo xtask install ----- - -You'll need Cargo, nodejs (matching a supported version of VS Code) and npm for this. - -Note that installing via `xtask install` does not work for VS Code Remote, instead you'll need to install the `.vsix` manually. - -If you're not using Code, you can compile and install only the LSP server: - -[source] ----- -$ cargo xtask install --server ----- - -Make sure that `.cargo/bin` is in `$PATH` and precedes paths where `rust-analyzer` may also be installed. -Specifically, `rustup` includes a proxy called `rust-analyzer`, which can cause problems if you're planning to use a source build or even a downloaded binary. - -=== rust-analyzer Language Server Binary - -Other editors generally require the `rust-analyzer` binary to be in `$PATH`. -You can download pre-built binaries from the https://github.com/rust-lang/rust-analyzer/releases[releases] page. -You will need to uncompress and rename the binary for your platform, e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to `rust-analyzer`, make it executable, then move it into a directory in your `$PATH`. - -On Linux to install the `rust-analyzer` binary into `~/.local/bin`, these commands should work: - -[source,bash] ----- -$ mkdir -p ~/.local/bin -$ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer -$ chmod +x ~/.local/bin/rust-analyzer ----- - -Make sure that `~/.local/bin` is listed in the `$PATH` variable and use the appropriate URL if you're not on a `x86-64` system. - -You don't have to use `~/.local/bin`, any other path like `~/.cargo/bin` or `/usr/local/bin` will work just as well. - -Alternatively, you can install it from source using the command below. -You'll need the latest stable version of the Rust toolchain. - -[source,bash] ----- -$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer -$ cargo xtask install --server ----- - -If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-lang/rust-analyzer/issues/1811[this issue]. -On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help. - -==== rustup - -`rust-analyzer` is available in `rustup`: - -[source,bash] ----- -$ rustup component add rust-analyzer ----- - -==== Arch Linux - -The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository): - -- https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/[`rust-analyzer`] (built from latest tagged source) -- https://aur.archlinux.org/packages/rust-analyzer-git[`rust-analyzer-git`] (latest Git version) - -Install it with pacman, for example: - -[source,bash] ----- -$ pacman -S rust-analyzer ----- - -==== Gentoo Linux - -There are two ways to install `rust-analyzer` under Gentoo: - -- when installing `dev-lang/rust` or `dev-lang/rust-bin`, enable the `rust-analyzer` and `rust-src` USE flags -- use the `rust-analyzer` component in `rustup` (see instructions above) - -Note that in both cases, the version installed lags for a couple of months behind the official releases on GitHub. -To obtain a newer one, you can download a binary from GitHub Releases or building from source. - -==== macOS - -The `rust-analyzer` binary can be installed via https://brew.sh/[Homebrew]. - -[source,bash] ----- -$ brew install rust-analyzer ----- - -==== Windows - -It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation. -Download links can be found -https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist[here]. - -=== VS Code or VSCodium in Flatpak - -Setting up `rust-analyzer` with a Flatpak version of Code is not trivial because of the Flatpak sandbox. -While the sandbox can be disabled for some directories, `/usr/bin` will always be mounted under `/run/host/usr/bin`. -This prevents access to the system's C compiler, a system-wide installation of Rust, or any other libraries you might want to link to. -Some compilers and libraries can be acquired as Flatpak SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or `org.freedesktop.Sdk.Extension.llvm15`. - -If you use a Flatpak SDK for Rust, it must be in your `PATH`: - - * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08` - * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`) - -If you want to use Flatpak in combination with `rustup`, the following steps might help: - - - both Rust and `rustup` have to be installed using https://rustup.rs. Distro packages _will not_ work. - - you need to launch Code, open a terminal and run `echo $PATH` - - using https://flathub.org/apps/details/com.github.tchx84.Flatseal[Flatseal], you must add an environment variable called `PATH`. - Set its value to the output from above, appending `:~/.cargo/bin`, where `~` is the path to your home directory. - You must replace `~`, as it won't be expanded otherwise. - - while Flatseal is open, you must enable access to "All user files" - -A C compiler should already be available via `org.freedesktop.Sdk`. -Any other tools or libraries you will need to acquire from Flatpak. - -=== Emacs - -Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. - -To use `rust-analyzer`, you need to install and enable one of the two popular LSP client implementations for Emacs, https://github.com/joaotavora/eglot[Eglot] or https://github.com/emacs-lsp/lsp-mode[LSP Mode]. Both enable `rust-analyzer` by default in rust buffers if it is available. - -==== Eglot - -Eglot is the more minimalistic and lightweight LSP client for Emacs, integrates well with existing Emacs functionality and is built into Emacs starting from release 29. - -After installing Eglot, e.g. via `M-x package-install` (not needed from Emacs 29), you can enable it via the `M-x eglot` command or load it automatically in `rust-mode` via - -[source,emacs-lisp] ----- -(add-hook 'rust-mode-hook 'eglot-ensure) ----- - -To enable clippy, you will need to configure the initialization options to pass the `check.command` setting. - -[source,emacs-lisp] ----- -(add-to-list 'eglot-server-programs - '((rust-ts-mode rust-mode) . - ("rust-analyzer" :initializationOptions (:check (:command "clippy"))))) ----- - -For more detailed instructions and options see the https://joaotavora.github.io/eglot[Eglot manual] (also available from Emacs via `M-x info`) and the -https://github.com/joaotavora/eglot/blob/master/README.md[Eglot readme]. - -Eglot does not support the rust-analyzer extensions to the language-server protocol and does not aim to do so in the future. The https://github.com/nemethf/eglot-x#rust-analyzer-extensions[eglot-x] package adds experimental support for those LSP extensions. - -==== LSP Mode - -LSP-mode is the original LSP-client for emacs. Compared to Eglot it has a larger codebase and supports more features, like LSP protocol extensions. -With extension packages like https://github.com/emacs-lsp/lsp-mode[LSP UI] it offers a lot of visual eyecandy. -Further it integrates well with https://github.com/emacs-lsp/dap-mode[DAP mode] for support of the Debug Adapter Protocol. - -You can install LSP-mode via `M-x package-install` and then run it via the `M-x lsp` command or load it automatically in rust buffers with - -[source,emacs-lisp] ----- -(add-hook 'rust-mode-hook 'lsp-deferred) ----- - -For more information on how to set up LSP mode and its extension package see the instructions in the https://emacs-lsp.github.io/lsp-mode/page/installation[LSP mode manual]. -Also see the https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/[rust-analyzer section] for `rust-analyzer` specific options and commands, which you can optionally bind to keys. - -Note the excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm] on how to set-up Emacs for Rust development with LSP mode and several other packages. - -=== Vim/Neovim - -Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. -Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example. - -There are several LSP client implementations for Vim or Neovim: - -==== coc-rust-analyzer - -1. Install coc.nvim by following the instructions at - https://github.com/neoclide/coc.nvim[coc.nvim] - (Node.js required) -2. Run `:CocInstall coc-rust-analyzer` to install - https://github.com/fannheyward/coc-rust-analyzer[coc-rust-analyzer], - this extension implements _most_ of the features supported in the VSCode extension: - * automatically install and upgrade stable/nightly releases - * same configurations as VSCode extension, `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc. - * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc. - * inlay hints for variables and method chaining, _Neovim Only_ - -Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` are unlikely to be useful. - -==== LanguageClient-neovim - -1. Install LanguageClient-neovim by following the instructions - https://github.com/autozimu/LanguageClient-neovim[here] - * The GitHub project wiki has extra tips on configuration - -2. Configure by adding this to your Vim/Neovim config file (replacing the existing Rust-specific line if it exists): -+ -[source,vim] ----- -let g:LanguageClient_serverCommands = { -\ 'rust': ['rust-analyzer'], -\ } ----- - -==== YouCompleteMe - -Install YouCompleteMe by following the instructions - https://github.com/ycm-core/YouCompleteMe#installation[here]. - -rust-analyzer is the default in ycm, it should work out of the box. - -==== ALE - -To use the LSP server in https://github.com/dense-analysis/ale[ale]: - -[source,vim] ----- -let g:ale_linters = {'rust': ['analyzer']} ----- - -==== nvim-lsp - -Neovim 0.5 has built-in language server support. -For a quick start configuration of rust-analyzer, use https://github.com/neovim/nvim-lspconfig#rust_analyzer[neovim/nvim-lspconfig]. -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: - -[source,vim] ----- -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, - settings = { - ["rust-analyzer"] = { - imports = { - granularity = { - group = "module", - }, - prefix = "self", - }, - cargo = { - buildScripts = { - enable = true, - }, - }, - procMacro = { - enable = true - }, - } - } -}) -EOF ----- - -If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`: - -[source,vim] ----- -lspconfig.rust_analyzer.setup({ - on_attach = function(client, bufnr) - vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) - end -}) ----- - -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. - -Check out https://github.com/mrcjkb/rustaceanvim for a batteries included rust-analyzer setup for Neovim. - -==== vim-lsp - -vim-lsp is installed by following https://github.com/prabirshrestha/vim-lsp[the plugin instructions]. -It can be as simple as adding this line to your `.vimrc`: - -[source,vim] ----- -Plug 'prabirshrestha/vim-lsp' ----- - -Next you need to register the `rust-analyzer` binary. -If it is available in `$PATH`, you may want to add this to your `.vimrc`: - -[source,vim] ----- -if executable('rust-analyzer') - au User lsp_setup call lsp#register_server({ - \ 'name': 'Rust Language Server', - \ 'cmd': {server_info->['rust-analyzer']}, - \ 'whitelist': ['rust'], - \ }) -endif ----- - -There is no dedicated UI for the server configuration, so you would need to send any options as a value of the `initialization_options` field, as described in the <<configuration,Configuration>> section. -Here is an example of how to enable the proc-macro support: - -[source,vim] ----- -if executable('rust-analyzer') - au User lsp_setup call lsp#register_server({ - \ 'name': 'Rust Language Server', - \ 'cmd': {server_info->['rust-analyzer']}, - \ 'whitelist': ['rust'], - \ 'initialization_options': { - \ 'cargo': { - \ 'buildScripts': { - \ 'enable': v:true, - \ }, - \ }, - \ 'procMacro': { - \ 'enable': v:true, - \ }, - \ }, - \ }) -endif ----- - -=== Sublime Text - -==== Sublime Text 4: -* Follow the instructions in link:https://github.com/sublimelsp/LSP-rust-analyzer[LSP-rust-analyzer]. - -NOTE: Install link:https://packagecontrol.io/packages/LSP-file-watcher-chokidar[LSP-file-watcher-chokidar] to enable file watching (`workspace/didChangeWatchedFiles`). - -==== Sublime Text 3: -* Install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. -* Install the link:https://packagecontrol.io/packages/LSP[LSP package]. -* From the command palette, run `LSP: Enable Language Server Globally` and select `rust-analyzer`. - -If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the status bar, and after waiting a bit, functionalities like tooltips on hovering over variables should become available. - -If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary. - -=== GNOME Builder - -GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. -If the LSP binary is not available, GNOME Builder can install it when opening a Rust file. - - -=== Eclipse IDE - -Support for Rust development in the Eclipse IDE is provided by link:https://github.com/eclipse/corrosion[Eclipse Corrosion]. -If available in PATH or in some standard location, `rust-analyzer` is detected and powers editing of Rust files without further configuration. -If `rust-analyzer` is not detected, Corrosion will prompt you for configuration of your Rust toolchain and language server with a link to the __Window > Preferences > Rust__ preference page; from here a button allows to download and configure `rust-analyzer`, but you can also reference another installation. -You'll need to close and reopen all .rs and Cargo files, or to restart the IDE, for this change to take effect. - -=== Kate Text Editor - -Support for the language server protocol is built into Kate through the LSP plugin, which is included by default. -It is preconfigured to use rust-analyzer for Rust sources since Kate 21.12. - -To change rust-analyzer config options, start from the following example and put it into Kate's "User Server Settings" tab (located under the LSP Client settings): -[source,json] ----- -{ - "servers": { - "rust": { - "initializationOptions": { - "cachePriming": { - "enable": false - }, - "check": { - "allTargets": false - }, - "checkOnSave": false - } - } - } -} ----- -Then click on apply, and restart the LSP server for your rust project. - -=== juCi++ - -https://gitlab.com/cppit/jucipp[juCi++] has built-in support for the language server protocol, and since version 1.7.0 offers installation of both Rust and rust-analyzer when opening a Rust file. - -=== Kakoune - -https://kakoune.org/[Kakoune] supports LSP with the help of https://github.com/kak-lsp/kak-lsp[`kak-lsp`]. -Follow the https://github.com/kak-lsp/kak-lsp#installation[instructions] to install `kak-lsp`. -To configure `kak-lsp`, refer to the https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp[configuration section] which is basically about copying the https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml[configuration file] in the right place (latest versions should use `rust-analyzer` by default). - -Finally, you need to configure Kakoune to talk to `kak-lsp` (see https://github.com/kak-lsp/kak-lsp#usage[Usage section]). -A basic configuration will only get you LSP but you can also activate inlay diagnostics and auto-formatting on save. -The following might help you get all of this. - -[source,txt] ----- -eval %sh{kak-lsp --kakoune -s $kak_session} # Not needed if you load it with plug.kak. -hook global WinSetOption filetype=rust %{ - # Enable LSP - lsp-enable-window - - # Auto-formatting on save - hook window BufWritePre .* lsp-formatting-sync - - # Configure inlay hints (only on save) - hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints - hook -once -always window WinSetOption filetype=.* %{ - remove-hooks window rust-inlay-hints - } -} ----- - -=== Helix - -https://docs.helix-editor.com/[Helix] supports LSP by default. -However, it won't install `rust-analyzer` automatically. -You can follow instructions for installing <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. - -[#visual-studio] -=== [[visual-studio-2022]]Visual Studio 2022 - -There are multiple rust-analyzer extensions for Visual Studio 2022 on Windows: - -==== rust-analyzer.vs - -(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International) - -https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer[Visual Studio Marketplace] - -https://github.com/kitamstudios/rust-analyzer/[GitHub] - -Support for Rust development in the Visual Studio IDE is enabled by the link:https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer[rust-analyzer] package. Either click on the download link or install from IDE's extension manager. -For now link:https://visualstudio.microsoft.com/downloads/[Visual Studio 2022] is required. All editions are supported viz. Community, Professional & Enterprise. -The package aims to provide 0-friction installation and therefore comes loaded with most things required including rust-analyzer binary. If anything it needs is missing, appropriate errors / warnings will guide the user. E.g. cargo.exe needs to be in path and the package will tell you as much. -This package is under rapid active development. So if you encounter any issues please file it at link:https://github.com/kitamstudios/rust-analyzer/[rust-analyzer.vs]. - -==== VS_RustAnalyzer - -(License: GPL) - -https://marketplace.visualstudio.com/items?itemName=cchharris.vsrustanalyzer[Visual Studio Marketplace] - -https://github.com/cchharris/VS-RustAnalyzer[GitHub] - -==== SourceGear Rust - -(License: closed source) - -https://marketplace.visualstudio.com/items?itemName=SourceGear.SourceGearRust[Visual Studio Marketplace] - -https://github.com/sourcegear/rust-vs-extension[GitHub (docs, issues, discussions)] - -* Free (no-cost) -* Supports all editions of Visual Studio 2022 on Windows: Community, Professional, or Enterprise - -=== Lapce - -https://lapce.dev/[Lapce] has a Rust plugin which you can install directly. -Unfortunately, it downloads an old version of `rust-analyzer`, but you can set the server path under Settings. - -=== Crates - -There is a package named `ra_ap_rust_analyzer` available on https://crates.io/crates/ra_ap_rust-analyzer[crates.io], for someone who wants to use it programmatically. - -For more details, see https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/autopublish.yaml[the publish workflow]. - -=== Zed - -https://zed.dev[Zed] has native `rust-analyzer` support. -If the LSP binary is not available, Zed can install it when opening a Rust file. - -== Troubleshooting - -Start with looking at the rust-analyzer version. -Try **rust-analyzer: Show RA Version** in VS Code (using **Command Palette** feature typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the command line. -If the date is more than a week ago, it's better to update rust-analyzer version. - -The next thing to check would be panic messages in rust-analyzer's log. -Log messages are printed to stderr, in VS Code you can see them in the `Output > Rust Analyzer Language Server` tab of the panel. -To see more logs, set the `RA_LOG=info` environment variable, this can be done either by setting the environment variable manually or by using `rust-analyzer.server.extraEnv`, note that both of these approaches require the server to be restarted. - -To fully capture LSP messages between the editor and the server, run the `rust-analyzer: Toggle LSP Logs` command and check -`Output > Rust Analyzer Language Server Trace`. - -The root cause for many "`nothing works`" problems is that rust-analyzer fails to understand the project structure. -To debug that, first note the `rust-analyzer` section in the status bar. -If it has an error icon and red, that's the problem (hover will have somewhat helpful error message). -**rust-analyzer: Status** prints dependency information for the current file. -Finally, `RA_LOG=project_model=debug` enables verbose logs during project loading. - -If rust-analyzer outright crashes, try running `rust-analyzer analysis-stats /path/to/project/directory/` on the command line. -This command type checks the whole project in batch mode bypassing LSP machinery. - -When filing issues, it is useful (but not necessary) to try to minimize examples. -An ideal bug reproduction looks like this: - -```bash -$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash -$ rust-analyzer --version -rust-analyzer dd12184e4 2021-05-08 dev -$ rust-analyzer analysis-stats . -💀 💀 💀 -``` - -It is especially useful when the `repo` doesn't use external crates or the standard library. - -If you want to go as far as to modify the source code to debug the problem, be sure to take a look at the -https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev[dev docs]! - -== Configuration - -**Source:** https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs[config.rs] - -The <<installation,Installation>> section contains details on configuration for some of the editors. -In general `rust-analyzer` is configured via LSP messages, which means that it's up to the editor to decide on the exact format and location of configuration files. - -Some clients, such as <<vs-code,VS Code>> or <<coc-rust-analyzer,COC plugin in Vim>> provide `rust-analyzer` specific configuration UIs. Others may require you to know a bit more about the interaction with `rust-analyzer`. - -For the later category, it might help to know that the initial configuration is specified as a value of the `initializationOptions` field of the https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize[`InitializeParams` message, in the LSP protocol]. -The spec says that the field type is `any?`, but `rust-analyzer` is looking for a JSON object that is constructed using settings from the list below. -Name of the setting, ignoring the `rust-analyzer.` prefix, is used as a path, and value of the setting becomes the JSON property value. - -For example, a very common configuration is to enable proc-macro support, can be achieved by sending this JSON: - -[source,json] ----- -{ - "cargo": { - "buildScripts": { - "enable": true, - }, - }, - "procMacro": { - "enable": true, - } -} ----- - -Please consult your editor's documentation to learn more about how to configure https://microsoft.github.io/language-server-protocol/[LSP servers]. - -To verify which configuration is actually used by `rust-analyzer`, set `RA_LOG` environment variable to `rust_analyzer=info` and look for config-related messages. -Logs should show both the JSON that `rust-analyzer` sees as well as the updated config. - -This is the list of config options `rust-analyzer` supports: - -include::./generated_config.adoc[] - -== Non-Cargo Based Projects - -rust-analyzer does not require Cargo. -However, if you use some other build system, you'll have to describe the structure of your project for rust-analyzer in the `rust-project.json` format: - -[source,TypeScript] ----- -interface JsonProject { - /// Path to the sysroot directory. - /// - /// The sysroot is where rustc looks for the - /// crates that are built-in to rust, such as - /// std. - /// - /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root - /// - /// To see the current value of sysroot, you - /// can query rustc: - /// - /// ``` - /// $ rustc --print sysroot - /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin - /// ``` - sysroot?: string; - /// Path to the directory with *source code* of - /// sysroot crates. - /// - /// By default, this is `lib/rustlib/src/rust/library` - /// relative to the sysroot. - /// - /// It should point to the directory where std, - /// core, and friends can be found: - /// - /// https://github.com/rust-lang/rust/tree/master/library. - /// - /// If provided, rust-analyzer automatically adds - /// dependencies on sysroot crates. Conversely, - /// if you omit this path, you can specify sysroot - /// dependencies yourself and, for example, have - /// several different "sysroots" in one graph of - /// crates. - sysroot_src?: string; - /// List of groups of common cfg values, to allow - /// sharing them between crates. - /// - /// Maps from group name to its cfgs. Cfg follow - /// the same format as `Crate.cfg`. - cfg_groups?: { [key: string]: string[]; }; - /// The set of crates comprising the current - /// project. Must include all transitive - /// dependencies as well as sysroot crate (libstd, - /// libcore and such). - crates: Crate[]; - /// Configuration for CLI commands. - /// - /// These are used for running and debugging binaries - /// and tests without encoding build system-specific - /// knowledge into rust-analyzer. - /// - /// # Example - /// - /// Below is an example of a test runnable. `{label}` and `{test_id}` - /// are explained in `Runnable::args`'s documentation below. - /// - /// ```json - /// { - /// "program": "buck", - /// "args": [ - /// "test", - /// "{label}", - /// "--", - /// "{test_id}", - /// "--print-passing-details" - /// ], - /// "cwd": "/home/user/repo-root/", - /// "kind": "testOne" - /// } - /// ``` - runnables?: Runnable[]; -} - -interface Crate { - /// Optional crate name used for display purposes, - /// without affecting semantics. See the `deps` - /// key for semantically-significant crate names. - display_name?: string; - /// Path to the root module of the crate. - root_module: string; - /// Edition of the crate. - edition: '2015' | '2018' | '2021' | '2024'; - /// The version of the crate. Used for calculating - /// the correct docs.rs URL. - version?: string; - /// Dependencies - deps: Dep[]; - /// Should this crate be treated as a member of - /// current "workspace". - /// - /// By default, inferred from the `root_module` - /// (members are the crates which reside inside - /// the directory opened in the editor). - /// - /// Set this to `false` for things like standard - /// library and 3rd party crates to enable - /// performance optimizations (rust-analyzer - /// assumes that non-member crates don't change). - is_workspace_member?: boolean; - /// Optionally specify the (super)set of `.rs` - /// files comprising this crate. - /// - /// By default, rust-analyzer assumes that only - /// files under `root_module.parent` can belong - /// to a crate. `include_dirs` are included - /// recursively, unless a subdirectory is in - /// `exclude_dirs`. - /// - /// Different crates can share the same `source`. - /// - /// If two crates share an `.rs` file in common, - /// they *must* have the same `source`. - /// rust-analyzer assumes that files from one - /// source can't refer to files in another source. - source?: { - include_dirs: string[]; - exclude_dirs: string[]; - }; - /// List of cfg groups this crate inherits. - /// - /// All cfg in these groups will be concatenated to - /// `cfg`. It is impossible to replace a value from - /// the groups. - cfg_groups?: string[]; - /// The set of cfgs activated for a given crate, like - /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. - cfg: string[]; - /// Target tuple for this Crate. - /// - /// Used when running `rustc --print cfg` - /// to get target-specific cfgs. - target?: string; - /// Environment variables, used for - /// the `env!` macro - env: { [key: string]: string; }; - - /// Whether the crate is a proc-macro crate. - is_proc_macro: boolean; - /// For proc-macro crates, path to compiled - /// proc-macro (.so file). - proc_macro_dylib_path?: string; - - /// Repository, matching the URL that would be used - /// in Cargo.toml. - repository?: string; - - /// Build-specific data about this crate. - build?: BuildInfo; -} - -interface Dep { - /// Index of a crate in the `crates` array. - crate: number; - /// Name as should appear in the (implicit) - /// `extern crate name` declaration. - name: string; -} - -interface BuildInfo { - /// The name associated with this crate. - /// - /// This is determined by the build system that produced - /// the `rust-project.json` in question. For instance, if buck were used, - /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. - /// - /// Do not attempt to parse the contents of this string; it is a build system-specific - /// identifier similar to `Crate::display_name`. - label: string; - /// Path corresponding to the build system-specific file defining the crate. - build_file: string; - /// The kind of target. - /// - /// This information is used to determine what sort - /// of runnable codelens to provide, if any. - target_kind: 'bin' | 'lib' | 'test'; -} - -interface Runnable { - /// The program invoked by the runnable. - /// - /// For example, this might be `cargo`, `buck`, or `bazel`. - program: string; - /// The arguments passed to `program`. - args: string[]; - /// The current working directory of the runnable. - cwd: string; - /// Used to decide what code lens to offer. - /// - /// `testOne`: This runnable will be used when the user clicks the 'Run Test' - /// CodeLens above a test. - /// - /// The args for testOne can contain two template strings: - /// `{label}` and `{test_id}`. `{label}` will be replaced - /// with the `Build::label` and `{test_id}` will be replaced - /// with the test name. - kind: 'testOne' | string; -} ----- - -This format is provisional and subject to change. -Specifically, the `roots` setup will be different eventually. - -There are three ways to feed `rust-project.json` to rust-analyzer: - -* Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it. -* Specify `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in the settings (and make sure that your LSP client sends settings as a part of initialize request). -* Specify `"rust-analyzer.linkedProjects": [ { "roots": [...], "crates": [...] }]` inline. - -Relative paths are interpreted relative to `rust-project.json` file location or (for inline JSON) relative to `rootUri`. - -You can set the `RA_LOG` environment variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading. - -Note that calls to `cargo check` are disabled when using `rust-project.json` by default, so compilation errors and warnings will no longer be sent to your LSP client. -To enable these compilation errors you will need to specify explicitly what command rust-analyzer should run to perform the checks using the `rust-analyzer.check.overrideCommand` configuration. -As an example, the following configuration explicitly sets `cargo check` as the `check` command. - -[source,json] ----- -{ "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } ----- - -`check.overrideCommand` requires the command specified to output json error messages for rust-analyzer to consume. -The `--message-format=json` flag does this for `cargo check` so whichever command you use must also output errors in this format. -See the <<Configuration>> section for more information. - -== Security - -At the moment, rust-analyzer assumes that all code is trusted. -Here is a **non-exhaustive** list of ways to make rust-analyzer execute arbitrary code: - -* proc macros and build scripts are executed by default -* `.cargo/config` can override `rustc` with an arbitrary executable -* `rust-toolchain.toml` can override `rustc` with an arbitrary executable -* VS Code plugin reads configuration from project directory, and that can be used to override paths to various executables, like `rustfmt` or `rust-analyzer` itself. -* rust-analyzer's syntax trees library uses a lot of `unsafe` and hasn't been properly audited for memory safety. - -== Privacy - -The LSP server performs no network access in itself, but runs `cargo metadata` which will update or download the crate registry and the source code of the project dependencies. -If enabled (the default), build scripts and procedural macros can do anything. - -The Code extension does not access the network. - -Any other editor plugins are not under the control of the `rust-analyzer` developers. For any privacy concerns, you should check with their respective developers. - -For `rust-analyzer` developers, `cargo xtask release` uses the GitHub API to put together the release notes. - -== Features - -include::./generated_features.adoc[] - -== Assists (Code Actions) - -Assists, or code actions, are small local refactorings, available in a particular context. -They are usually triggered by a shortcut or by clicking a light bulb icon in the editor. -Cursor position or selection is signified by `┃` character. - -include::./generated_assists.adoc[] - -== Diagnostics - -While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis. -Some of these diagnostics don't respect `\#[allow]` or `\#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.experimental.enable` or `rust-analyzer.diagnostics.disabled` settings. - -=== Clippy - -To run `cargo clippy` instead of `cargo check`, you can set `"rust-analyzer.check.command": "clippy"`. - -include::./generated_diagnostic.adoc[] - -== Editor Features -=== VS Code - -==== Color configurations - -It is possible to change the foreground/background color and font family/size of inlay hints. -Just add this to your `settings.json`: - -[source,jsonc] ----- -{ - "editor.inlayHints.fontFamily": "Courier New", - "editor.inlayHints.fontSize": 11, - - "workbench.colorCustomizations": { - // Name of the theme you are currently using - "[Default Dark+]": { - "editorInlayHint.foreground": "#868686f0", - "editorInlayHint.background": "#3d3d3d48", - - // Overrides for specific kinds of inlay hints - "editorInlayHint.typeForeground": "#fdb6fdf0", - "editorInlayHint.parameterForeground": "#fdb6fdf0", - } - } -} ----- - -==== Semantic style customizations - -You can customize the look of different semantic elements in the source code. -For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`: - -[source,jsonc] ----- -{ - "editor.semanticTokenColorCustomizations": { - "rules": { - "*.mutable": { - "fontStyle": "", // underline is the default - }, - } - }, -} ----- - -Most themes doesn't support styling unsafe operations differently yet. You can fix this by adding overrides for the rules `operator.unsafe`, `function.unsafe`, and `method.unsafe`: - -[source,jsonc] ----- -{ - "editor.semanticTokenColorCustomizations": { - "rules": { - "operator.unsafe": "#ff6600", - "function.unsafe": "#ff6600", - "method.unsafe": "#ff6600" - } - }, -} ----- - -In addition to the top-level rules you can specify overrides for specific themes. For example, if you wanted to use a darker text color on a specific light theme, you might write: - -[source,jsonc] ----- -{ - "editor.semanticTokenColorCustomizations": { - "rules": { - "operator.unsafe": "#ff6600" - }, - "[Ayu Light]": { - "rules": { - "operator.unsafe": "#572300" - } - } - }, -} ----- - -Make sure you include the brackets around the theme name. For example, use `"[Ayu Light]"` to customize the theme Ayu Light. - -==== Special `when` clause context for keybindings. -You may use `inRustProject` context to configure keybindings for rust projects only. -For example: - -[source,json] ----- -{ - "key": "ctrl+alt+d", - "command": "rust-analyzer.openDocs", - "when": "inRustProject" -} ----- -More about `when` clause contexts https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts[here]. - -==== Setting runnable environment variables -You can use "rust-analyzer.runnables.extraEnv" setting to define runnable environment-specific substitution variables. -The simplest way for all runnables in a bunch: -```jsonc -"rust-analyzer.runnables.extraEnv": { - "RUN_SLOW_TESTS": "1" -} -``` - -Or it is possible to specify vars more granularly: -```jsonc -"rust-analyzer.runnables.extraEnv": [ - { - // "mask": null, // null mask means that this rule will be applied for all runnables - "env": { - "APP_ID": "1", - "APP_DATA": "asdf" - } - }, - { - "mask": "test_name", - "env": { - "APP_ID": "2", // overwrites only APP_ID - } - } -] -``` - -You can use any valid regular expression as a mask. -Also note that a full runnable name is something like *run bin_or_example_name*, *test some::mod::test_name* or *test-mod some::mod*, so it is possible to distinguish binaries, single tests, and test modules with this masks: `"^run"`, `"^test "` (the trailing space matters!), and `"^test-mod"` respectively. - -If needed, you can set different values for different platforms: -```jsonc -"rust-analyzer.runnables.extraEnv": [ - { - "platform": "win32", // windows only - "env": { - "APP_DATA": "windows specific data" - } - }, - { - "platform": ["linux"], - "env": { - "APP_DATA": "linux data", - } - }, - { // for all platforms - "env": { - "APP_COMMON_DATA": "xxx", - } - } -] -``` - -==== Compiler feedback from external commands - -Instead of relying on the built-in `cargo check`, you can configure Code to run a command in the background and use the `$rustc-watch` problem matcher to generate inline error markers from its output. - -To do this you need to create a new https://code.visualstudio.com/docs/editor/tasks[VS Code Task] and set `"rust-analyzer.checkOnSave": false` in preferences. - -For example, if you want to run https://crates.io/crates/cargo-watch[`cargo watch`] instead, you might add the following to `.vscode/tasks.json`: - -```json -{ - "label": "Watch", - "group": "build", - "type": "shell", - "command": "cargo watch", - "problemMatcher": "$rustc-watch", - "isBackground": true -} -``` - -==== Live Share - -VS Code Live Share has partial support for rust-analyzer. - -Live Share _requires_ the official Microsoft build of VS Code, OSS builds will not work correctly. - -The host's rust-analyzer instance will be shared with all guests joining the session. -The guests do not have to have the rust-analyzer extension installed for this to work. - -If you are joining a Live Share session and _do_ have rust-analyzer installed locally, commands from the command palette will not work correctly since they will attempt to communicate with the local server. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index f148041ac3e..0a603767705 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -501,6 +501,10 @@ }, { "pattern": "**/Cargo.lock" + }, + { + "scheme": "output", + "pattern": "extension-output-rust-lang.rust-analyzer*" } ] } @@ -2252,6 +2256,16 @@ { "title": "inlayHints", "properties": { + "rust-analyzer.inlayHints.typeHints.hideClosureParameter": { + "markdownDescription": "Whether to hide inlay parameter type hints for closures.", + "default": false, + "type": "boolean" + } + } + }, + { + "title": "inlayHints", + "properties": { "rust-analyzer.inlayHints.typeHints.hideNamedConstructor": { "markdownDescription": "Whether to hide inlay type hints for constructors.", "default": false, diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index b3aa04af7ed..eee623ecae9 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -361,10 +361,7 @@ export function syntaxTreeReveal(): Cmd { const activeEditor = vscode.window.activeTextEditor; if (activeEditor !== undefined) { - const start = activeEditor.document.positionAt(element.start); - const end = activeEditor.document.positionAt(element.end); - - const newSelection = new vscode.Selection(start, end); + const newSelection = new vscode.Selection(element.range.start, element.range.end); activeEditor.selection = newSelection; activeEditor.revealRange(newSelection); @@ -378,15 +375,12 @@ function elementToString( depth: number = 0, ): string { let result = " ".repeat(depth); - const start = element.istart ?? element.start; - const end = element.iend ?? element.end; + const offsets = element.inner?.offsets ?? element.offsets; - result += `${element.kind}@${start}..${end}`; + result += `${element.kind}@${offsets.start}..${offsets.end}`; if (element.type === "Token") { - const startPosition = activeDocument.positionAt(element.start); - const endPosition = activeDocument.positionAt(element.end); - const text = activeDocument.getText(new vscode.Range(startPosition, endPosition)); + const text = activeDocument.getText(element.range).replaceAll("\r\n", "\n"); // JSON.stringify quotes and escapes the string for us. result += ` ${JSON.stringify(text)}\n`; } else { diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 96dc4f19b82..4248305d5cc 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -384,9 +384,7 @@ export class Ctx implements RustAnalyzerExtensionApi { return; } - const start = e.textEditor.document.offsetAt(selection.start); - const end = e.textEditor.document.offsetAt(selection.end); - const result = this.syntaxTreeProvider?.getElementByRange(start, end); + const result = this.syntaxTreeProvider?.getElementByRange(selection); if (result !== undefined) { await this.syntaxTreeView?.reveal(result); } diff --git a/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts b/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts index c7e8007e838..3f7e30f13a3 100644 --- a/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts +++ b/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts @@ -37,11 +37,7 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement const editor = vscode.window.activeTextEditor; if (editor !== undefined) { - const start = editor.document.positionAt(element.start); - const end = editor.document.positionAt(element.end); - const range = new vscode.Range(start, end); - - const text = editor.document.getText(range); + const text = editor.document.getText(element.range); item.tooltip = new vscode.MarkdownString().appendCodeblock(text, "rust"); } @@ -74,14 +70,61 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement if (editor && isRustEditor(editor)) { const params = { textDocument: { uri: editor.document.uri.toString() }, range: null }; const fileText = await this.ctx.client.sendRequest(ra.viewSyntaxTree, params); - this.root = JSON.parse(fileText, (_key, value: SyntaxElement) => { + this.root = JSON.parse(fileText, (_key, value: RawElement): SyntaxElement => { + if (value.type !== "Node" && value.type !== "Token") { + // This is something other than a RawElement. + return value; + } + const [startOffset, startLine, startCol] = value.start; + const [endOffset, endLine, endCol] = value.end; + const range = new vscode.Range(startLine, startCol, endLine, endCol); + const offsets = { + start: startOffset, + end: endOffset, + }; + + let inner; + if (value.istart && value.iend) { + const [istartOffset, istartLine, istartCol] = value.istart; + const [iendOffset, iendLine, iendCol] = value.iend; + + inner = { + offsets: { + start: istartOffset, + end: iendOffset, + }, + range: new vscode.Range(istartLine, istartCol, iendLine, iendCol), + }; + } + if (value.type === "Node") { - for (const child of value.children) { - child.parent = value; + const result = { + type: value.type, + kind: value.kind, + offsets, + range, + inner, + children: value.children, + parent: undefined, + document: editor.document, + }; + + for (const child of result.children) { + child.parent = result; } - } - return value; + return result; + } else { + return { + type: value.type, + kind: value.kind, + offsets, + range, + inner, + parent: undefined, + document: editor.document, + }; + } }); } else { this.root = undefined; @@ -90,14 +133,14 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement this._onDidChangeTreeData.fire(); } - getElementByRange(start: number, end: number): SyntaxElement | undefined { + getElementByRange(target: vscode.Range): SyntaxElement | undefined { if (this.root === undefined) { return undefined; } let result: SyntaxElement = this.root; - if (this.root.start === start && this.root.end === end) { + if (this.root.range.isEqual(target)) { return result; } @@ -105,9 +148,9 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement outer: while (true) { for (const child of children) { - if (child.start <= start && child.end >= end) { + if (child.range.contains(target)) { result = child; - if (start === end && start === child.end) { + if (target.isEmpty && target.start === child.range.end) { // When the cursor is on the very end of a token, // we assume the user wants the next token instead. continue; @@ -136,31 +179,72 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement export type SyntaxNode = { type: "Node"; kind: string; - start: number; - end: number; - istart?: number; - iend?: number; + range: vscode.Range; + offsets: { + start: number; + end: number; + }; + /** This element's position within a Rust string literal, if it's inside of one. */ + inner?: { + range: vscode.Range; + offsets: { + start: number; + end: number; + }; + }; children: SyntaxElement[]; parent?: SyntaxElement; + document: vscode.TextDocument; }; type SyntaxToken = { type: "Token"; kind: string; - start: number; - end: number; - istart?: number; - iend?: number; + range: vscode.Range; + offsets: { + start: number; + end: number; + }; + /** This element's position within a Rust string literal, if it's inside of one. */ + inner?: { + range: vscode.Range; + offsets: { + start: number; + end: number; + }; + }; parent?: SyntaxElement; + document: vscode.TextDocument; }; export type SyntaxElement = SyntaxNode | SyntaxToken; +type RawNode = { + type: "Node"; + kind: string; + start: [number, number, number]; + end: [number, number, number]; + istart?: [number, number, number]; + iend?: [number, number, number]; + children: SyntaxElement[]; +}; + +type RawToken = { + type: "Token"; + kind: string; + start: [number, number, number]; + end: [number, number, number]; + istart?: [number, number, number]; + iend?: [number, number, number]; +}; + +type RawElement = RawNode | RawToken; + export class SyntaxTreeItem extends vscode.TreeItem { constructor(private readonly element: SyntaxElement) { super(element.kind); - const icon = getIcon(element.kind); - if (element.type === "Node") { + const icon = getIcon(this.element.kind); + if (this.element.type === "Node") { this.contextValue = "syntaxNode"; this.iconPath = icon ?? new vscode.ThemeIcon("list-tree"); this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; @@ -170,11 +254,9 @@ export class SyntaxTreeItem extends vscode.TreeItem { this.collapsibleState = vscode.TreeItemCollapsibleState.None; } - if (element.istart !== undefined && element.iend !== undefined) { - this.description = `${this.element.istart}..${this.element.iend}`; - } else { - this.description = `${this.element.start}..${this.element.end}`; - } + const offsets = this.element.inner?.offsets ?? this.element.offsets; + + this.description = `${offsets.start}..${offsets.end}`; } } diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs index 6f0455ee98b..bc87ada3eb5 100644 --- a/src/tools/rust-analyzer/lib/line-index/src/lib.rs +++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs @@ -235,7 +235,7 @@ fn analyze_source_file_dispatch( } } -#[cfg(target_arch = "aarch64")] +#[cfg(all(target_arch = "aarch64", target_endian = "little"))] fn analyze_source_file_dispatch( src: &str, lines: &mut Vec<TextSize>, @@ -347,7 +347,7 @@ unsafe fn analyze_source_file_sse2( } #[target_feature(enable = "neon")] -#[cfg(target_arch = "aarch64")] +#[cfg(all(target_arch = "aarch64", target_endian = "little"))] #[inline] // See https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon // @@ -362,7 +362,7 @@ unsafe fn move_mask(v: std::arch::aarch64::uint8x16_t) -> u64 { } #[target_feature(enable = "neon")] -#[cfg(target_arch = "aarch64")] +#[cfg(all(target_arch = "aarch64", target_endian = "little"))] unsafe fn analyze_source_file_neon( src: &str, lines: &mut Vec<TextSize>, @@ -441,7 +441,11 @@ unsafe fn analyze_source_file_neon( } } -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] +#[cfg(not(any( + target_arch = "x86", + target_arch = "x86_64", + all(target_arch = "aarch64", target_endian = "little") +)))] // The target (or compiler version) does not support SSE2 ... fn analyze_source_file_dispatch( src: &str, diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 2d9a927c638..f414893b6cd 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -9a1d156f38c51441ee51e5a068f1d0caf4bb0f27 +d9a4a47b8b3dc0bdff83360cea2013200d60d49c diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml index b505ee835b2..ebd8903ad8a 100644 --- a/src/tools/rust-analyzer/xtask/Cargo.toml +++ b/src/tools/rust-analyzer/xtask/Cargo.toml @@ -16,8 +16,7 @@ xflags = "0.3.0" time = { version = "0.3", default-features = false } zip = { version = "0.6", default-features = false, features = ["deflate", "time"] } stdx.workspace = true -# https://github.com/dtolnay/proc-macro2/issues/475 -proc-macro2 = "=1.0.86" +proc-macro2 = "1.0.93" quote = "1.0.20" ungrammar = "1.16.1" either.workspace = true diff --git a/src/tools/rust-analyzer/xtask/src/codegen.rs b/src/tools/rust-analyzer/xtask/src/codegen.rs index bc04b9474f2..18f49643dc7 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen.rs @@ -24,6 +24,7 @@ impl flags::Codegen { diagnostics_docs::generate(self.check); assists_doc_tests::generate(self.check); parser_inline_tests::generate(self.check); + feature_docs::generate(self.check) // diagnostics_docs::generate(self.check) doesn't generate any tests // lints::generate(self.check) Updating clones the rust repo, so don't run it unless // explicitly asked for @@ -116,13 +117,7 @@ impl fmt::Display for Location { let path = self.file.strip_prefix(project_root()).unwrap().display().to_string(); let path = path.replace('\\', "/"); let name = self.file.file_name().unwrap(); - write!( - f, - "https://github.com/rust-lang/rust-analyzer/blob/master/{}#L{}[{}]", - path, - self.line, - name.to_str().unwrap() - ) + write!(f, " [{}]({}#{}) ", name.to_str().unwrap(), path, self.line) } } @@ -162,7 +157,7 @@ fn reformat(text: String) -> String { } fn add_preamble(cg: CodegenType, mut text: String) -> String { - let preamble = format!("//! Generated by `cargo codegen {cg}`, do not edit by hand.\n\n"); + let preamble = format!("//! Generated by `cargo xtask codegen {cg}`, do not edit by hand.\n\n"); text.insert_str(0, &preamble); text } @@ -186,7 +181,7 @@ fn ensure_file_contents(cg: CodegenType, file: &Path, contents: &str, check: boo file.display(), if std::env::var("CI").is_ok() { format!( - "\n NOTE: run `cargo codegen {cg}` locally and commit the updated files\n" + "\n NOTE: run `cargo xtask codegen {cg}` locally and commit the updated files\n" ) } else { "".to_owned() diff --git a/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs b/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs index d06c9d65df3..0bb18c73cfc 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs @@ -1,4 +1,4 @@ -//! Generates `assists.md` documentation. +//! Generates `assists_generated.md` documentation. use std::{fmt, fs, path::Path}; @@ -62,7 +62,7 @@ r#####" crate::flags::CodegenType::AssistsDocTests, assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"), ); - let dst = project_root().join("docs/user/generated_assists.adoc"); + let dst = project_root().join("docs/book/src/assists_generated.md"); fs::write(dst, contents).unwrap(); } } @@ -146,7 +146,7 @@ impl fmt::Display for Assist { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let _ = writeln!( f, - "[discrete]\n=== `{}` + "### `{}` **Source:** {}", self.id, self.location, ); @@ -159,11 +159,11 @@ impl fmt::Display for Assist { " {} -.Before +#### Before ```rust {}``` -.After +#### After ```rust {}```", section.doc, diff --git a/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs b/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs index 4cb8f3f259d..cf8f97be009 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs @@ -1,4 +1,4 @@ -//! Generates `assists.md` documentation. +//! Generates `diagnostics_generated.md` documentation. use std::{fmt, fs, io, path::PathBuf}; @@ -14,7 +14,7 @@ pub(crate) fn generate(check: bool) { let contents = diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); let contents = add_preamble(crate::flags::CodegenType::DiagnosticsDocs, contents); - let dst = project_root().join("docs/user/generated_diagnostic.adoc"); + let dst = project_root().join("docs/book/src/diagnostics_generated.md"); fs::write(dst, contents).unwrap(); } } @@ -73,6 +73,6 @@ fn is_valid_diagnostic_name(diagnostic: &str) -> Result<(), String> { impl fmt::Display for Diagnostic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) + writeln!(f, "#### {}\n\nSource: {}\n\n{}\n\n", self.id, self.location, self.doc) } } diff --git a/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs b/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs index c6451d888b0..51ff13aba81 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs @@ -1,9 +1,9 @@ -//! Generates `assists.md` documentation. +//! Generates `features_generated.md` documentation. use std::{fmt, fs, io, path::PathBuf}; use crate::{ - codegen::{CommentBlock, Location}, + codegen::{add_preamble, CommentBlock, Location}, project_root, util::list_rust_files, }; @@ -11,14 +11,8 @@ use crate::{ pub(crate) fn generate(_check: bool) { let features = Feature::collect().unwrap(); let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); - let contents = format!( - " -// Generated file, do not edit by hand, see `sourcegen_feature_docs`. -{} -", - contents.trim() - ); - let dst = project_root().join("docs/user/generated_features.adoc"); + let contents = add_preamble(crate::flags::CodegenType::FeatureDocs, contents); + let dst = project_root().join("docs/book/src/features_generated.md"); fs::write(dst, contents).unwrap(); } @@ -80,6 +74,6 @@ fn is_valid_feature_name(feature: &str) -> Result<(), String> { impl fmt::Display for Feature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) + writeln!(f, "### {}\n**Source:** {}\n{}", self.id, self.location, self.doc) } } diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs index d5fe3236789..e20dda7fec3 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs @@ -397,6 +397,9 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String { if "{}[]()".contains(token) { let c = token.chars().next().unwrap(); quote! { #c } + // underscore is an identifier in the proc-macro api + } else if *token == "_" { + quote! { _ } } else { let cs = token.chars().map(|c| Punct::new(c, Spacing::Joint)); quote! { #(#cs)* } diff --git a/src/tools/rust-analyzer/xtask/src/publish/notes.rs b/src/tools/rust-analyzer/xtask/src/publish/notes.rs index c30267295bf..7245ce24311 100644 --- a/src/tools/rust-analyzer/xtask/src/publish/notes.rs +++ b/src/tools/rust-analyzer/xtask/src/publish/notes.rs @@ -549,18 +549,18 @@ impl Macro { } "pr" => { let pr = &self.target; - let url = format!("https://github.com/rust-analyzer/rust-analyzer/pull/{pr}"); + let url = format!("https://github.com/rust-lang/rust-analyzer/pull/{pr}"); format!("[`#{pr}`]({url})") } "commit" => { let hash = &self.target; let short = &hash[0..7]; - let url = format!("https://github.com/rust-analyzer/rust-analyzer/commit/{hash}"); + let url = format!("https://github.com/rust-lang/rust-analyzer/commit/{hash}"); format!("[`{short}`]({url})") } "release" => { let date = &self.target; - let url = format!("https://github.com/rust-analyzer/rust-analyzer/releases/{date}"); + let url = format!("https://github.com/rust-lang/rust-analyzer/releases/{date}"); format!("[`{date}`]({url})") } _ => bail!("macro not supported: {name}"), diff --git a/src/tools/rust-analyzer/xtask/src/release.rs b/src/tools/rust-analyzer/xtask/src/release.rs index 1a1364c7d10..8e56ce439c5 100644 --- a/src/tools/rust-analyzer/xtask/src/release.rs +++ b/src/tools/rust-analyzer/xtask/src/release.rs @@ -9,7 +9,7 @@ use directories::ProjectDirs; use stdx::JodChild; use xshell::{cmd, Shell}; -use crate::{codegen, date_iso, flags, is_release_tag, project_root}; +use crate::{date_iso, flags, is_release_tag, project_root}; impl flags::Release { pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { @@ -29,11 +29,6 @@ impl flags::Release { cmd!(sh, "git push --force").run()?; } - // Generates bits of manual.adoc. - codegen::diagnostics_docs::generate(false); - codegen::assists_doc_tests::generate(false); - codegen::feature_docs::generate(false); - let website_root = project_root().join("../rust-analyzer.github.io"); { let _dir = sh.push_dir(&website_root); @@ -54,20 +49,6 @@ impl flags::Release { .max() .unwrap_or_default(); - for adoc in [ - "manual.adoc", - "generated_assists.adoc", - "generated_config.adoc", - "generated_diagnostic.adoc", - "generated_features.adoc", - ] { - let src = project_root().join("./docs/user/").join(adoc); - let dst = website_root.join(adoc); - - let contents = sh.read_file(src)?; - sh.write_file(dst, contents)?; - } - let tags = cmd!(sh, "git tag --list").read()?; let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); diff --git a/src/tools/rust-analyzer/xtask/test_data/expected.md b/src/tools/rust-analyzer/xtask/test_data/expected.md index 19c940c67bd..301837b5c21 100644 --- a/src/tools/rust-analyzer/xtask/test_data/expected.md +++ b/src/tools/rust-analyzer/xtask/test_data/expected.md @@ -2,12 +2,12 @@ Hello! -Commit: [`0123456`](https://github.com/rust-analyzer/rust-analyzer/commit/0123456789abcdef0123456789abcdef01234567) \ -Release: [`2022-01-01`](https://github.com/rust-analyzer/rust-analyzer/releases/2022-01-01) +Commit: [`0123456`](https://github.com/rust-lang/rust-analyzer/commit/0123456789abcdef0123456789abcdef01234567) \ +Release: [`2022-01-01`](https://github.com/rust-lang/rust-analyzer/releases/2022-01-01) ## New Features -- **BREAKING** [`#1111`](https://github.com/rust-analyzer/rust-analyzer/pull/1111) shortcut <kbd>ctrl</kbd>+<kbd>r</kbd> +- **BREAKING** [`#1111`](https://github.com/rust-lang/rust-analyzer/pull/1111) shortcut <kbd>ctrl</kbd>+<kbd>r</kbd> - hyphen-prefixed list item - nested list item - `foo` -> `foofoo` @@ -65,7 +65,7 @@ Release: [`2022-01-01`](https://github.com/rust-analyzer/rust-analyzer/releases/ - list item with an inline image  -The highlight of the month is probably [`#1111`](https://github.com/rust-analyzer/rust-analyzer/pull/1111). +The highlight of the month is probably [`#1111`](https://github.com/rust-lang/rust-analyzer/pull/1111). See [online manual](https://example.com/manual) for more information. ```bash |
