From f15e026101a99487fd1debb87e6c879eb9b215c1 Mon Sep 17 00:00:00 2001 From: Raghul Nanth A <61490162+NanthR@users.noreply.github.com> Date: Mon, 12 Jun 2023 01:18:24 +0530 Subject: [PATCH 001/201] ci(metrics): Run measurement functions in parallel feat(xtask): Split metrics function --- .github/workflows/metrics.yaml | 234 +++++++++++++++++++++++++++++++-- xtask/src/flags.rs | 29 +++- xtask/src/metrics.rs | 76 ++++++----- 3 files changed, 292 insertions(+), 47 deletions(-) diff --git a/.github/workflows/metrics.yaml b/.github/workflows/metrics.yaml index 3fe2fc917a..233af42f42 100644 --- a/.github/workflows/metrics.yaml +++ b/.github/workflows/metrics.yaml @@ -11,20 +11,234 @@ env: RUSTUP_MAX_RETRIES: 10 jobs: - metrics: - if: github.repository == 'rust-lang/rust-analyzer' + setup_cargo: runs-on: ubuntu-latest + steps: + - name: Install Rust toolchain + run: | + rustup update --no-self-update stable + rustup component add rustfmt rust-src + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-${{ github.sha }} + + build_metrics: + runs-on: ubuntu-latest + needs: setup_cargo steps: - name: Checkout repository uses: actions/checkout@v3 - - name: Install Rust toolchain - run: | - rustup update --no-self-update stable - rustup component add rustfmt rust-src + - name: Restore cargo cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-${{ github.sha }} - - name: Collect metrics - run: cargo xtask metrics - env: - METRICS_TOKEN: ${{ secrets.METRICS_TOKEN }} + + - name: Collect build metrics + run: cargo xtask metrics build + + - name: Cache target + uses: actions/cache@v3 + with: + path: target/ + key: ${{ runner.os }}-target-${{ github.sha }} + + - name: Upload build metrics + uses: actions/upload-artifact@v3 + with: + name: build-${{ github.sha }} + path: target/build.json + if-no-files-found: error + + self_metrics: + runs-on: ubuntu-latest + needs: [setup_cargo, build_metrics] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Restore cargo cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-${{ github.sha }} + + - name: Restore target cache + uses: actions/cache@v3 + with: + path: target/ + key: ${{ runner.os }}-target-${{ github.sha }} + + - name: Collect build metrics + run: cargo xtask metrics self + + - name: Upload build metrics + uses: actions/upload-artifact@v3 + with: + name: self-${{ github.sha }} + path: target/self.json + if-no-files-found: error + + ripgrep_metrics: + runs-on: ubuntu-latest + needs: [setup_cargo, build_metrics] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Restore cargo cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-${{ github.sha }} + + - name: Restore target cache + uses: actions/cache@v3 + with: + path: target/ + key: ${{ runner.os }}-target-${{ github.sha }} + + - name: Collect build metrics + run: cargo xtask metrics ripgrep + + - name: Upload ripgrep metrics + uses: actions/upload-artifact@v3 + with: + name: ripgrep-${{ github.sha }} + path: target/ripgrep.json + if-no-files-found: error + + webrender_metrics: + runs-on: ubuntu-latest + needs: [setup_cargo, build_metrics] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Restore cargo cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-${{ github.sha }} + + - name: Restore target cache + uses: actions/cache@v3 + with: + path: target/ + key: ${{ runner.os }}-target-${{ github.sha }} + + - name: Collect webrender metrics + run: cargo xtask metrics webrender + + - name: Upload webrender metrics + uses: actions/upload-artifact@v3 + with: + name: webrender-${{ github.sha }} + path: target/webrender.json + if-no-files-found: error + + diesel_metrics: + runs-on: ubuntu-latest + needs: [setup_cargo, build_metrics] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Restore cargo cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-${{ github.sha }} + + - name: Restore target cache + uses: actions/cache@v3 + with: + path: target/ + key: ${{ runner.os }}-target-${{ github.sha }} + + - name: Collect build metrics + run: cargo xtask metrics diesel + + - name: Upload build metrics + uses: actions/upload-artifact@v3 + with: + name: diesel-${{ github.sha }} + path: target/diesel.json + if-no-files-found: error + + + + generate_final_metrics: + runs-on: ubuntu-latest + needs: [build_metrics, self_metrics, ripgrep_metrics, webrender_metrics, diesel_metrics] + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Download build metrics + uses: actions/download-artifact@v3 + with: + name: build-${{ github.sha }} + + - name: Download self metrics + uses: actions/download-artifact@v3 + with: + name: self-${{ github.sha }} + + - name: Download ripgrep metrics + uses: actions/download-artifact@v3 + with: + name: ripgrep-${{ github.sha }} + + - name: Download webrender metrics + uses: actions/download-artifact@v3 + with: + name: webrender-${{ github.sha }} + + - name: Download diesel metrics + uses: actions/download-artifact@v3 + with: + name: diesel-${{ github.sha }} + + - name: Combine json + run: | + git clone --depth 1 https://$METRICS_TOKEN@github.com/nanthR/metrics.git + jq -s ".[0] * .[1] * .[2] * .[3] * .[4]" build.json self.json ripgrep.json webrender.json diesel.json -c >> metrics/metrics.json + git -C metrics add . + git -C metrics -c user.name=Bot -c user.email=dummy@example.com commit --message 📈 + git -C metrics push origin master + env: + METRICS_TOKEN: ${{ secrets.METRICS_TOKEN }} diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index 2100479701..47d0ea254b 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs @@ -1,5 +1,7 @@ #![allow(unreachable_pub)] +use std::str::FromStr; + use crate::install::{ClientOpt, Malloc, ServerOpt}; xflags::xflags! { @@ -42,7 +44,7 @@ xflags::xflags! { required changelog: String } cmd metrics { - optional --dry-run + optional measurement_type: MeasurementType } /// Builds a benchmark version of rust-analyzer and puts it into `./target`. cmd bb { @@ -105,9 +107,32 @@ pub struct PublishReleaseNotes { pub dry_run: bool, } +#[derive(Debug)] +pub enum MeasurementType { + Build, + AnalyseSelf, + AnalyseRipgrep, + AnalyseWebRender, + AnalyseDiesel, +} + +impl FromStr for MeasurementType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "build" => Ok(Self::Build), + "self" => Ok(Self::AnalyseSelf), + "ripgrep" => Ok(Self::AnalyseRipgrep), + "webrender" => Ok(Self::AnalyseWebRender), + "diesel" => Ok(Self::AnalyseDiesel), + _ => Err("Invalid option".to_string()), + } + } +} + #[derive(Debug)] pub struct Metrics { - pub dry_run: bool, + pub measurement_type: Option, } #[derive(Debug)] diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs index b6f730dbf1..d51dfb183f 100644 --- a/xtask/src/metrics.rs +++ b/xtask/src/metrics.rs @@ -1,6 +1,6 @@ use std::{ collections::BTreeMap, - env, fs, + fs, io::Write as _, path::Path, time::{Instant, SystemTime, UNIX_EPOCH}, @@ -9,16 +9,13 @@ use std::{ use anyhow::{bail, format_err}; use xshell::{cmd, Shell}; -use crate::flags; +use crate::flags::{self, MeasurementType}; type Unit = String; impl flags::Metrics { pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { let mut metrics = Metrics::new(sh)?; - if !self.dry_run { - sh.remove_path("./target/release")?; - } if !Path::new("./target/rustc-perf").exists() { sh.create_dir("./target/rustc-perf")?; cmd!(sh, "git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf") @@ -32,38 +29,47 @@ impl flags::Metrics { let _env = sh.push_env("RA_METRICS", "1"); - { - // https://github.com/rust-lang/rust-analyzer/issues/9997 - let _d = sh.push_dir("target/rustc-perf/collector/benchmarks/webrender"); - cmd!(sh, "cargo update -p url --precise 1.6.1").run()?; - } - metrics.measure_build(sh)?; - metrics.measure_analysis_stats_self(sh)?; - metrics.measure_analysis_stats(sh, "ripgrep")?; - metrics.measure_analysis_stats(sh, "webrender")?; - metrics.measure_analysis_stats(sh, "diesel/diesel")?; - - if !self.dry_run { - let _d = sh.push_dir("target"); - let metrics_token = env::var("METRICS_TOKEN").unwrap(); - cmd!( - sh, - "git clone --depth 1 https://{metrics_token}@github.com/rust-analyzer/metrics.git" - ) - .run()?; - - { - let mut file = - fs::File::options().append(true).open("target/metrics/metrics.json")?; - writeln!(file, "{}", metrics.json())?; + let filename = match self.measurement_type { + Some(ms) => match ms { + MeasurementType::Build => { + metrics.measure_build(sh)?; + "build.json" + } + MeasurementType::AnalyseSelf => { + metrics.measure_analysis_stats_self(sh)?; + "self.json" + } + MeasurementType::AnalyseRipgrep => { + metrics.measure_analysis_stats(sh, "ripgrep")?; + "ripgrep.json" + } + MeasurementType::AnalyseWebRender => { + { + // https://github.com/rust-lang/rust-analyzer/issues/9997 + let _d = sh.push_dir("target/rustc-perf/collector/benchmarks/webrender"); + cmd!(sh, "cargo update -p url --precise 1.6.1").run()?; + } + metrics.measure_analysis_stats(sh, "webrender")?; + "webrender.json" + } + MeasurementType::AnalyseDiesel => { + metrics.measure_analysis_stats(sh, "diesel/diesel")?; + "diesel.json" + } + }, + None => { + metrics.measure_build(sh)?; + metrics.measure_analysis_stats_self(sh)?; + metrics.measure_analysis_stats(sh, "ripgrep")?; + metrics.measure_analysis_stats(sh, "webrender")?; + metrics.measure_analysis_stats(sh, "diesel/diesel")?; + "all.json" } + }; - let _d = sh.push_dir("metrics"); - cmd!(sh, "git add .").run()?; - cmd!(sh, "git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈") - .run()?; - cmd!(sh, "git push origin master").run()?; - } + let mut file = + fs::File::options().write(true).create(true).open(format!("target/{}", filename))?; + writeln!(file, "{}", metrics.json())?; eprintln!("{metrics:#?}"); Ok(()) } From 5d77839b25963ce9bb06d8e1de760106477e6fe1 Mon Sep 17 00:00:00 2001 From: Raghul Nanth A <61490162+NanthR@users.noreply.github.com> Date: Mon, 12 Jun 2023 23:14:04 +0530 Subject: [PATCH 002/201] style(metrics): Change spelling --- xtask/src/flags.rs | 16 ++++++++-------- xtask/src/metrics.rs | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index 47d0ea254b..7720ad69fe 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs @@ -110,10 +110,10 @@ pub struct PublishReleaseNotes { #[derive(Debug)] pub enum MeasurementType { Build, - AnalyseSelf, - AnalyseRipgrep, - AnalyseWebRender, - AnalyseDiesel, + AnalyzeSelf, + AnalyzeRipgrep, + AnalyzeWebRender, + AnalyzeDiesel, } impl FromStr for MeasurementType { @@ -121,10 +121,10 @@ impl FromStr for MeasurementType { fn from_str(s: &str) -> Result { match s { "build" => Ok(Self::Build), - "self" => Ok(Self::AnalyseSelf), - "ripgrep" => Ok(Self::AnalyseRipgrep), - "webrender" => Ok(Self::AnalyseWebRender), - "diesel" => Ok(Self::AnalyseDiesel), + "self" => Ok(Self::AnalyzeSelf), + "ripgrep" => Ok(Self::AnalyzeRipgrep), + "webrender" => Ok(Self::AnalyzeWebRender), + "diesel" => Ok(Self::AnalyzeDiesel), _ => Err("Invalid option".to_string()), } } diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs index d51dfb183f..6853742319 100644 --- a/xtask/src/metrics.rs +++ b/xtask/src/metrics.rs @@ -35,15 +35,15 @@ impl flags::Metrics { metrics.measure_build(sh)?; "build.json" } - MeasurementType::AnalyseSelf => { + MeasurementType::AnalyzeSelf => { metrics.measure_analysis_stats_self(sh)?; "self.json" } - MeasurementType::AnalyseRipgrep => { + MeasurementType::AnalyzeRipgrep => { metrics.measure_analysis_stats(sh, "ripgrep")?; "ripgrep.json" } - MeasurementType::AnalyseWebRender => { + MeasurementType::AnalyzeWebRender => { { // https://github.com/rust-lang/rust-analyzer/issues/9997 let _d = sh.push_dir("target/rustc-perf/collector/benchmarks/webrender"); @@ -52,7 +52,7 @@ impl flags::Metrics { metrics.measure_analysis_stats(sh, "webrender")?; "webrender.json" } - MeasurementType::AnalyseDiesel => { + MeasurementType::AnalyzeDiesel => { metrics.measure_analysis_stats(sh, "diesel/diesel")?; "diesel.json" } From e55a1f1916192bbf613223651e6073460b6ca1ac Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 15 Jun 2023 01:47:22 +0330 Subject: [PATCH 003/201] Map our diagnostics to rustc and clippy's ones --- Cargo.lock | 1 + crates/hir-ty/src/diagnostics.rs | 2 +- crates/hir-ty/src/diagnostics/decl_check.rs | 6 +- crates/hir/src/diagnostics.rs | 2 +- crates/hir/src/lib.rs | 10 +- crates/ide-diagnostics/Cargo.toml | 1 + .../src/handlers/break_outside_of_loop.rs | 9 +- .../src/handlers/expected_function.rs | 9 +- .../src/handlers/field_shorthand.rs | 40 +-- .../src/handlers/inactive_code.rs | 7 +- .../src/handlers/incoherent_impl.rs | 10 +- .../src/handlers/incorrect_case.rs | 112 ++++++-- .../src/handlers/invalid_derive_target.rs | 5 +- .../src/handlers/json_is_not_rust.rs | 5 +- .../src/handlers/macro_error.rs | 16 +- .../src/handlers/malformed_derive.rs | 5 +- .../src/handlers/mismatched_arg_count.rs | 4 +- .../src/handlers/missing_fields.rs | 4 +- .../src/handlers/missing_match_arms.rs | 9 +- .../src/handlers/missing_unsafe.rs | 9 +- .../src/handlers/moved_out_of_ref.rs | 9 +- .../src/handlers/mutability_errors.rs | 125 +++++---- .../src/handlers/no_such_field.rs | 9 +- .../src/handlers/private_assoc_item.rs | 21 +- .../src/handlers/private_field.rs | 9 +- .../replace_filter_map_next_with_find_map.rs | 41 ++- .../src/handlers/type_mismatch.rs | 4 +- .../src/handlers/typed_hole.rs | 5 +- .../src/handlers/undeclared_label.rs | 9 +- .../handlers/unimplemented_builtin_macro.rs | 10 +- .../src/handlers/unlinked_file.rs | 5 +- .../src/handlers/unreachable_label.rs | 9 +- .../src/handlers/unresolved_extern_crate.rs | 9 +- .../src/handlers/unresolved_field.rs | 9 +- .../src/handlers/unresolved_import.rs | 9 +- .../src/handlers/unresolved_macro_call.rs | 4 +- .../src/handlers/unresolved_method.rs | 9 +- .../src/handlers/unresolved_module.rs | 29 +- .../src/handlers/unresolved_proc_macro.rs | 4 +- .../src/handlers/useless_braces.rs | 5 +- crates/ide-diagnostics/src/lib.rs | 257 ++++++++++++++++-- crates/ide-diagnostics/src/tests.rs | 13 +- crates/rust-analyzer/src/main_loop.rs | 6 +- crates/rust-analyzer/src/to_proto.rs | 3 + 44 files changed, 628 insertions(+), 251 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 322a67383b..de7abe0f03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -748,6 +748,7 @@ dependencies = [ "hir", "ide-db", "itertools", + "once_cell", "profile", "serde_json", "sourcegen", diff --git a/crates/hir-ty/src/diagnostics.rs b/crates/hir-ty/src/diagnostics.rs index 4b147b9970..ef43ed5c46 100644 --- a/crates/hir-ty/src/diagnostics.rs +++ b/crates/hir-ty/src/diagnostics.rs @@ -5,7 +5,7 @@ mod unsafe_check; mod decl_check; pub use crate::diagnostics::{ - decl_check::{incorrect_case, IncorrectCase}, + decl_check::{incorrect_case, CaseType, IncorrectCase}, expr::{ record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic, }, diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index 1233469b94..f4c6d6dd3c 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -57,11 +57,11 @@ pub fn incorrect_case( #[derive(Debug)] pub enum CaseType { - // `some_var` + /// `some_var` LowerSnakeCase, - // `SOME_CONST` + /// `SOME_CONST` UpperSnakeCase, - // `SomeStruct` + /// `SomeStruct` UpperCamelCase, } diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index b64d81490b..80c3bcdca8 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -3,7 +3,7 @@ //! //! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. -pub use hir_ty::diagnostics::{IncoherentImpl, IncorrectCase}; +pub use hir_ty::diagnostics::{CaseType, IncoherentImpl, IncorrectCase}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 9876503d03..e9e9a20264 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -89,11 +89,11 @@ use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ attrs::{HasAttrs, Namespace}, diagnostics::{ - AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl, - IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError, - MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, - MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, - ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, + AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, + IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, + MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields, + MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, + PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut, diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml index e18624fcc2..14aa394019 100644 --- a/crates/ide-diagnostics/Cargo.toml +++ b/crates/ide-diagnostics/Cargo.toml @@ -16,6 +16,7 @@ cov-mark = "2.0.0-pre.1" either = "1.7.0" itertools = "0.10.5" serde_json = "1.0.86" +once_cell = "1.17.0" # local deps profile.workspace = true diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index 30576c71fb..4910054038 100644 --- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: break-outside-of-loop // @@ -13,10 +13,11 @@ pub(crate) fn break_outside_of_loop( let construct = if d.is_break { "break" } else { "continue" }; format!("{construct} outside of loop") }; - Diagnostic::new( - "break-outside-of-loop", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0268"), message, - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/expected_function.rs b/crates/ide-diagnostics/src/handlers/expected_function.rs index d2f27664d6..e1e5db91c5 100644 --- a/crates/ide-diagnostics/src/handlers/expected_function.rs +++ b/crates/ide-diagnostics/src/handlers/expected_function.rs @@ -1,6 +1,6 @@ use hir::HirDisplay; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: expected-function // @@ -9,10 +9,11 @@ pub(crate) fn expected_function( ctx: &DiagnosticsContext<'_>, d: &hir::ExpectedFunction, ) -> Diagnostic { - Diagnostic::new( - "expected-function", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0618"), format!("expected function, found {}", d.found.display(ctx.sema.db)), - ctx.sema.diagnostics_display_range(d.call.clone().map(|it| it.into())).range, + d.call.clone().map(|it| it.into()), ) .experimental() } diff --git a/crates/ide-diagnostics/src/handlers/field_shorthand.rs b/crates/ide-diagnostics/src/handlers/field_shorthand.rs index 2b71053625..3b69640af9 100644 --- a/crates/ide-diagnostics/src/handlers/field_shorthand.rs +++ b/crates/ide-diagnostics/src/handlers/field_shorthand.rs @@ -5,7 +5,7 @@ use ide_db::{base_db::FileId, source_change::SourceChange}; use syntax::{ast, match_ast, AstNode, SyntaxNode}; use text_edit::TextEdit; -use crate::{fix, Diagnostic, Severity}; +use crate::{fix, Diagnostic, DiagnosticCode}; pub(crate) fn field_shorthand(acc: &mut Vec, file_id: FileId, node: &SyntaxNode) { match_ast! { @@ -46,14 +46,17 @@ fn check_expr_field_shorthand( let field_range = record_field.syntax().text_range(); acc.push( - Diagnostic::new("use-field-shorthand", "Shorthand struct initialization", field_range) - .severity(Severity::WeakWarning) - .with_fixes(Some(vec![fix( - "use_expr_field_shorthand", - "Use struct shorthand initialization", - SourceChange::from_text_edit(file_id, edit), - field_range, - )])), + Diagnostic::new( + DiagnosticCode::Clippy("redundant_field_names"), + "Shorthand struct initialization", + field_range, + ) + .with_fixes(Some(vec![fix( + "use_expr_field_shorthand", + "Use struct shorthand initialization", + SourceChange::from_text_edit(file_id, edit), + field_range, + )])), ); } } @@ -87,14 +90,17 @@ fn check_pat_field_shorthand( let field_range = record_pat_field.syntax().text_range(); acc.push( - Diagnostic::new("use-field-shorthand", "Shorthand struct pattern", field_range) - .severity(Severity::WeakWarning) - .with_fixes(Some(vec![fix( - "use_pat_field_shorthand", - "Use struct field shorthand", - SourceChange::from_text_edit(file_id, edit), - field_range, - )])), + Diagnostic::new( + DiagnosticCode::Clippy("redundant_field_names"), + "Shorthand struct pattern", + field_range, + ) + .with_fixes(Some(vec![fix( + "use_pat_field_shorthand", + "Use struct field shorthand", + SourceChange::from_text_edit(file_id, edit), + field_range, + )])), ); } } diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs index f558b7256a..9eb763d3e2 100644 --- a/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -1,7 +1,7 @@ use cfg::DnfExpr; use stdx::format_to; -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: inactive-code // @@ -27,13 +27,12 @@ pub(crate) fn inactive_code( format_to!(message, ": {}", inactive); } } - + // FIXME: This shouldn't be a diagnostic let res = Diagnostic::new( - "inactive-code", + DiagnosticCode::Ra("inactive-code", Severity::WeakWarning), message, ctx.sema.diagnostics_display_range(d.node.clone()).range, ) - .severity(Severity::WeakWarning) .with_unused(true); Some(res) } diff --git a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs index 72af9ebfcb..4afb4db03b 100644 --- a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs +++ b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs @@ -1,17 +1,17 @@ use hir::InFile; -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: incoherent-impl // // This diagnostic is triggered if the targe type of an impl is from a foreign crate. pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentImpl) -> Diagnostic { - Diagnostic::new( - "incoherent-impl", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0210"), format!("cannot define inherent `impl` for foreign type"), - ctx.sema.diagnostics_display_range(InFile::new(d.file_id, d.impl_.clone().into())).range, + InFile::new(d.file_id, d.impl_.clone().into()), ) - .severity(Severity::Error) } #[cfg(test)] diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 90279e1453..92fd4f71ca 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -1,4 +1,4 @@ -use hir::{db::ExpandDatabase, InFile}; +use hir::{db::ExpandDatabase, CaseType, InFile}; use ide_db::{assists::Assist, defs::NameClass}; use syntax::AstNode; @@ -6,23 +6,29 @@ use crate::{ // references::rename::rename_with_semantics, unresolved_fix, Diagnostic, + DiagnosticCode, DiagnosticsContext, - Severity, }; // 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]. pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { - Diagnostic::new( - "incorrect-ident-case", + let code = match d.expected_case { + CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"), + CaseType::UpperSnakeCase => DiagnosticCode::RustcLint("non_upper_case_globals"), + // The name is lying. It also covers variants, traits, ... + CaseType::UpperCamelCase => DiagnosticCode::RustcLint("non_camel_case_types"), + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + code, format!( "{} `{}` should have {} name, e.g. `{}`", d.ident_type, d.ident_text, d.expected_case, d.suggested_text ), - ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range, + InFile::new(d.file, d.ident.clone().into()), ) - .severity(Severity::WeakWarning) .with_fixes(fixes(ctx, d)) } @@ -149,7 +155,7 @@ impl TestStruct { check_diagnostics( r#" fn FOO() {} -// ^^^ 💡 weak: Function `FOO` should have snake_case name, e.g. `foo` +// ^^^ 💡 warn: Function `FOO` should have snake_case name, e.g. `foo` "#, ); check_fix(r#"fn FOO$0() {}"#, r#"fn foo() {}"#); @@ -160,7 +166,7 @@ fn FOO() {} check_diagnostics( r#" fn NonSnakeCaseName() {} -// ^^^^^^^^^^^^^^^^ 💡 weak: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` +// ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` "#, ); } @@ -170,10 +176,10 @@ fn NonSnakeCaseName() {} check_diagnostics( r#" fn foo(SomeParam: u8) {} - // ^^^^^^^^^ 💡 weak: Parameter `SomeParam` should have snake_case name, e.g. `some_param` + // ^^^^^^^^^ 💡 warn: Parameter `SomeParam` should have snake_case name, e.g. `some_param` fn foo2(ok_param: &str, CAPS_PARAM: u8) {} - // ^^^^^^^^^^ 💡 weak: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` + // ^^^^^^^^^^ 💡 warn: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` "#, ); } @@ -184,9 +190,9 @@ fn foo2(ok_param: &str, CAPS_PARAM: u8) {} r#" fn foo() { let SOME_VALUE = 10; - // ^^^^^^^^^^ 💡 weak: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` + // ^^^^^^^^^^ 💡 warn: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` let AnotherValue = 20; - // ^^^^^^^^^^^^ 💡 weak: Variable `AnotherValue` should have snake_case name, e.g. `another_value` + // ^^^^^^^^^^^^ 💡 warn: Variable `AnotherValue` should have snake_case name, e.g. `another_value` } "#, ); @@ -197,10 +203,10 @@ fn foo() { check_diagnostics( r#" struct non_camel_case_name {} - // ^^^^^^^^^^^^^^^^^^^ 💡 weak: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` + // ^^^^^^^^^^^^^^^^^^^ 💡 warn: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` struct SCREAMING_CASE {} - // ^^^^^^^^^^^^^^ 💡 weak: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` + // ^^^^^^^^^^^^^^ 💡 warn: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` "#, ); } @@ -219,7 +225,7 @@ struct AABB {} check_diagnostics( r#" struct SomeStruct { SomeField: u8 } - // ^^^^^^^^^ 💡 weak: Field `SomeField` should have snake_case name, e.g. `some_field` + // ^^^^^^^^^ 💡 warn: Field `SomeField` should have snake_case name, e.g. `some_field` "#, ); } @@ -229,10 +235,10 @@ struct SomeStruct { SomeField: u8 } check_diagnostics( r#" enum some_enum { Val(u8) } - // ^^^^^^^^^ 💡 weak: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` + // ^^^^^^^^^ 💡 warn: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` enum SOME_ENUM {} - // ^^^^^^^^^ 💡 weak: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` + // ^^^^^^^^^ 💡 warn: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` "#, ); } @@ -251,7 +257,7 @@ enum AABB {} check_diagnostics( r#" enum SomeEnum { SOME_VARIANT(u8) } - // ^^^^^^^^^^^^ 💡 weak: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` + // ^^^^^^^^^^^^ 💡 warn: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` "#, ); } @@ -261,7 +267,7 @@ enum SomeEnum { SOME_VARIANT(u8) } check_diagnostics( r#" const some_weird_const: u8 = 10; - // ^^^^^^^^^^^^^^^^ 💡 weak: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` + // ^^^^^^^^^^^^^^^^ 💡 warn: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` "#, ); } @@ -271,7 +277,7 @@ const some_weird_const: u8 = 10; check_diagnostics( r#" static some_weird_const: u8 = 10; - // ^^^^^^^^^^^^^^^^ 💡 weak: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` + // ^^^^^^^^^^^^^^^^ 💡 warn: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` "#, ); } @@ -281,13 +287,13 @@ static some_weird_const: u8 = 10; check_diagnostics( r#" struct someStruct; - // ^^^^^^^^^^ 💡 weak: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` + // ^^^^^^^^^^ 💡 warn: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` impl someStruct { fn SomeFunc(&self) { - // ^^^^^^^^ 💡 weak: Function `SomeFunc` should have snake_case name, e.g. `some_func` + // ^^^^^^^^ 💡 warn: Function `SomeFunc` should have snake_case name, e.g. `some_func` let WHY_VAR_IS_CAPS = 10; - // ^^^^^^^^^^^^^^^ 💡 weak: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` + // ^^^^^^^^^^^^^^^ 💡 warn: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` } } "#, @@ -319,7 +325,7 @@ enum Option { Some, None } fn main() { match Option::None { SOME_VAR @ None => (), - // ^^^^^^^^ 💡 weak: Variable `SOME_VAR` should have snake_case name, e.g. `some_var` + // ^^^^^^^^ 💡 warn: Variable `SOME_VAR` should have snake_case name, e.g. `some_var` Some => (), } } @@ -461,7 +467,7 @@ mod CheckNonstandardStyle { #[allow(bad_style)] mod CheckBadStyle { - fn HiImABadFnName() {} + struct fooo; } mod F { @@ -483,4 +489,60 @@ pub static SomeStatic: u8 = 10; "#, ); } + + #[test] + fn deny_attributes() { + check_diagnostics( + r#" +#[deny(non_snake_case)] +fn NonSnakeCaseName(some_var: u8) -> u8 { + //^^^^^^^^^^^^^^^^ 💡 error: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` + // cov_flags generated output from elsewhere in this file + extern "C" { + #[no_mangle] + static lower_case: u8; + } + + let OtherVar = some_var + 1; + //^^^^^^^^ 💡 error: Variable `OtherVar` should have snake_case name, e.g. `other_var` + OtherVar +} + +#[deny(nonstandard_style)] +mod CheckNonstandardStyle { + fn HiImABadFnName() {} + //^^^^^^^^^^^^^^ 💡 error: Function `HiImABadFnName` should have snake_case name, e.g. `hi_im_abad_fn_name` +} + +#[deny(warnings)] +mod CheckBadStyle { + struct fooo; + //^^^^ 💡 error: Structure `fooo` should have CamelCase name, e.g. `Fooo` +} + +mod F { + #![deny(non_snake_case)] + fn CheckItWorksWithModAttr() {} + //^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Function `CheckItWorksWithModAttr` should have snake_case name, e.g. `check_it_works_with_mod_attr` +} + +#[deny(non_snake_case, non_camel_case_types)] +pub struct some_type { + //^^^^^^^^^ 💡 error: Structure `some_type` should have CamelCase name, e.g. `SomeType` + SOME_FIELD: u8, + //^^^^^^^^^^ 💡 error: Field `SOME_FIELD` should have snake_case name, e.g. `some_field` + SomeField: u16, + //^^^^^^^^^ 💡 error: Field `SomeField` should have snake_case name, e.g. `some_field` +} + +#[deny(non_upper_case_globals)] +pub const some_const: u8 = 10; + //^^^^^^^^^^ 💡 error: Constant `some_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST` + +#[deny(non_upper_case_globals)] +pub static SomeStatic: u8 = 10; + //^^^^^^^^^^ 💡 error: Static variable `SomeStatic` should have UPPER_SNAKE_CASE name, e.g. `SOME_STATIC` + "#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs index c779266bc9..1ec17952b2 100644 --- a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs +++ b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: invalid-derive-target // @@ -11,11 +11,10 @@ pub(crate) fn invalid_derive_target( let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range; Diagnostic::new( - "invalid-derive-target", + DiagnosticCode::RustcHardError("E0774"), "`derive` may only be applied to `struct`s, `enum`s and `union`s", display_range, ) - .severity(Severity::Error) } #[cfg(test)] diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 04ce1e0fee..a337e2660d 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -17,7 +17,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Diagnostic, DiagnosticsConfig, Severity}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsConfig, Severity}; #[derive(Default)] struct State { @@ -117,11 +117,10 @@ pub(crate) fn json_in_items( edit.insert(range.start(), state.result); acc.push( Diagnostic::new( - "json-is-not-rust", + DiagnosticCode::Ra("json-is-not-rust", Severity::WeakWarning), "JSON syntax is not valid as a Rust item", range, ) - .severity(Severity::WeakWarning) .with_fixes(Some(vec![{ let mut scb = SourceChangeBuilder::new(file_id); let scope = match import_scope { diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index 7547779a95..937e2f9664 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: macro-error // @@ -6,7 +6,12 @@ use crate::{Diagnostic, DiagnosticsContext}; pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic { // Use more accurate position if available. let display_range = ctx.resolve_precise_location(&d.node, d.precise_location); - Diagnostic::new("macro-error", d.message.clone(), display_range).experimental() + Diagnostic::new( + DiagnosticCode::Ra("macro-error", Severity::Error), + d.message.clone(), + display_range, + ) + .experimental() } // Diagnostic: macro-error @@ -16,7 +21,12 @@ pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefErr // Use more accurate position if available. let display_range = ctx.resolve_precise_location(&d.node.clone().map(|it| it.syntax_node_ptr()), d.name); - Diagnostic::new("macro-def-error", d.message.clone(), display_range).experimental() + Diagnostic::new( + DiagnosticCode::Ra("macro-def-error", Severity::Error), + d.message.clone(), + display_range, + ) + .experimental() } #[cfg(test)] diff --git a/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/crates/ide-diagnostics/src/handlers/malformed_derive.rs index cd48bdba07..fc57dde69f 100644 --- a/crates/ide-diagnostics/src/handlers/malformed_derive.rs +++ b/crates/ide-diagnostics/src/handlers/malformed_derive.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: malformed-derive // @@ -10,11 +10,10 @@ pub(crate) fn malformed_derive( let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range; Diagnostic::new( - "malformed-derive", + DiagnosticCode::RustcHardError("E0777"), "malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`", display_range, ) - .severity(Severity::Error) } #[cfg(test)] diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index c5db8c3741..6238c7e09e 100644 --- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -3,7 +3,7 @@ use syntax::{ AstNode, TextRange, }; -use crate::{adjusted_display_range, Diagnostic, DiagnosticsContext}; +use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: mismatched-arg-count // @@ -14,7 +14,7 @@ pub(crate) fn mismatched_arg_count( ) -> Diagnostic { let s = if d.expected == 1 { "" } else { "s" }; let message = format!("expected {} argument{s}, found {}", d.expected, d.found); - Diagnostic::new("mismatched-arg-count", message, invalid_args_range(ctx, d)) + Diagnostic::new(DiagnosticCode::RustcHardError("E0107"), message, invalid_args_range(ctx, d)) } fn invalid_args_range(ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount) -> TextRange { diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 60ccc41df0..bb0e36ff3a 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -15,7 +15,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Diagnostic, DiagnosticsContext}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: missing-fields // @@ -42,7 +42,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())), ); - Diagnostic::new("missing-fields", message, ctx.sema.diagnostics_display_range(ptr).range) + Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError("E0063"), message, ptr) .with_fixes(fixes(ctx, d)) } diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 3f13b97a44..82a9a3bd55 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: missing-match-arm // @@ -7,10 +7,11 @@ pub(crate) fn missing_match_arms( ctx: &DiagnosticsContext<'_>, d: &hir::MissingMatchArms, ) -> Diagnostic { - Diagnostic::new( - "missing-match-arm", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0004"), format!("missing match arm: {}", d.uncovered_patterns), - ctx.sema.diagnostics_display_range(d.scrutinee_expr.clone().map(Into::into)).range, + d.scrutinee_expr.clone().map(Into::into), ) } diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 2026b6fcef..70b26009ba 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -4,16 +4,17 @@ use syntax::{ast, SyntaxNode}; use syntax::{match_ast, AstNode}; use text_edit::TextEdit; -use crate::{fix, Diagnostic, DiagnosticsContext}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: missing-unsafe // // 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 { - Diagnostic::new( - "missing-unsafe", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0133"), "this operation is unsafe and requires an unsafe function or block", - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) } diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 32e321107e..3aa4aa9702 100644 --- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -1,14 +1,15 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; use hir::HirDisplay; // Diagnostic: moved-out-of-ref // // This diagnostic is triggered on moving non copy things out of references. pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOfRef) -> Diagnostic { - Diagnostic::new( - "moved-out-of-ref", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0507"), format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db)), - ctx.sema.diagnostics_display_range(d.span.clone()).range, + d.span.clone(), ) .experimental() // spans are broken, and I'm not sure how precise we can detect copy types } diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index f61460e317..a0a6513326 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -2,7 +2,7 @@ use ide_db::source_change::SourceChange; use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T}; use text_edit::TextEdit; -use crate::{fix, Diagnostic, DiagnosticsContext, Severity}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: need-mut // @@ -29,13 +29,15 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno use_range, )]) })(); - Diagnostic::new( - "need-mut", + Diagnostic::new_with_syntax_node_ptr( + ctx, + // FIXME: `E0384` is not the only error that this diagnostic handles + DiagnosticCode::RustcHardError("E0384"), format!( "cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db).display(ctx.sema.db) ), - ctx.sema.diagnostics_display_range(d.span.clone()).range, + d.span.clone(), ) .with_fixes(fixes) } @@ -68,12 +70,12 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - Diagnostic::new( - "unused-mut", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_mut"), "variable does not need to be mutable", - ctx.sema.diagnostics_display_range(ast).range, + ast, ) - .severity(Severity::WeakWarning) .experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive. .with_fixes(fixes) } @@ -93,7 +95,7 @@ mod tests { fn f(_: i32) {} fn main() { let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(x); } "#, @@ -268,7 +270,7 @@ fn main() { fn f(_: i32) {} fn main() { let mut x = (2, 7); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(x.1); } "#, @@ -302,7 +304,7 @@ fn main() { r#" fn main() { let mut x = &mut 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable *x = 5; } "#, @@ -346,7 +348,7 @@ fn main() { r#" //- minicore: copy, builtin_impls fn clone(mut i: &!) -> ! { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable *i } "#, @@ -360,7 +362,7 @@ fn main() { //- minicore: option fn main() { let mut v = &mut Some(2); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let _ = || match v { Some(k) => { *k = 5; @@ -386,7 +388,7 @@ fn main() { fn main() { match (2, 3) { (x, mut y) => { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x = 7; //^^^^^ 💡 error: cannot mutate immutable variable `x` } @@ -407,7 +409,7 @@ fn main() { fn main() { return; let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable &mut x; } "#, @@ -417,7 +419,7 @@ fn main() { fn main() { loop {} let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable &mut x; } "#, @@ -438,7 +440,7 @@ fn main(b: bool) { g(); } let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable &mut x; } "#, @@ -452,7 +454,7 @@ fn main(b: bool) { return; } let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable &mut x; } "#, @@ -466,7 +468,7 @@ fn main(b: bool) { fn f(_: i32) {} fn main() { let mut x; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x = 5; f(x); } @@ -477,7 +479,7 @@ fn main() { fn f(_: i32) {} fn main(b: bool) { let mut x; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable if b { x = 1; } else { @@ -552,15 +554,15 @@ fn f(_: i32) {} fn main() { loop { let mut x = 1; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(x); if let mut y = 2 { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(y); } match 3 { mut z => f(z), - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable } } } @@ -577,9 +579,9 @@ fn main() { loop { let c @ ( mut b, - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable mut d - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable ); a = 1; //^^^^^ 💡 error: cannot mutate immutable variable `a` @@ -597,7 +599,7 @@ fn main() { check_diagnostics( r#" fn f(mut x: i32) { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable } "#, ); @@ -640,7 +642,7 @@ fn f() { //- minicore: iterators, copy fn f(x: [(i32, u8); 10]) { for (a, mut b) in x { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` } @@ -657,9 +659,9 @@ fn f(x: [(i32, u8); 10]) { fn f(x: [(i32, u8); 10]) { let mut it = x.into_iter(); while let Some((a, mut b)) = it.next() { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable while let Some((c, mut d)) = it.next() { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` c = 2; @@ -683,7 +685,7 @@ fn f() { let x = &mut x; //^^^^^^ 💡 error: cannot mutate immutable variable `x` let mut x = x; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x[2] = 5; } "#, @@ -711,13 +713,13 @@ impl IndexMut for Foo { } fn f() { let mut x = Foo; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let y = &x[2]; let x = Foo; let y = &mut x[2]; //^💡 error: cannot mutate immutable variable `x` let mut x = &mut Foo; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let y: &mut (i32, u8) = &mut x[2]; let x = Foo; let ref mut y = x[7]; @@ -731,7 +733,7 @@ fn f() { } let mut x = Foo; let mut i = 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let y = &mut x[i]; } "#, @@ -759,7 +761,7 @@ impl DerefMut for Foo { } fn f() { let mut x = Foo; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let y = &*x; let x = Foo; let y = &mut *x; @@ -790,7 +792,7 @@ fn f() { fn f(_: i32) {} fn main() { let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable f(x); } "#, @@ -842,7 +844,7 @@ pub struct TreeLeaf { pub fn test() { let mut tree = Tree::Leaf( - //^^^^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^^^^ 💡 warn: variable does not need to be mutable TreeLeaf { depth: 0, data: 0 @@ -859,7 +861,7 @@ pub fn test() { r#" //- minicore: fn fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x(2) } fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 { @@ -867,11 +869,11 @@ fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 { //^ 💡 error: cannot mutate immutable variable `x` } fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x(2) } fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable x(2) } "#, @@ -915,14 +917,14 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { //- minicore: copy, fn fn f() { let mut x = 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let mut y = 2; y = 7; let closure = || { let mut z = 8; z = 3; let mut k = z; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable }; } "#, @@ -949,7 +951,7 @@ fn f() { fn f() { struct X; let mut x = X; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let c1 = || x; let mut x = X; let c2 = || { x = X; x }; @@ -965,12 +967,12 @@ fn f() { fn f() { let mut x = &mut 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = || { *x = 2; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = || { *x = 2; &x; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` @@ -979,12 +981,12 @@ fn f() { let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut 5; - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = move || { *x = 2; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut X(1, 2); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = || { x.0 = 2; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` @@ -1001,7 +1003,7 @@ fn f() { fn x(t: &[u8]) { match t { &[a, mut b] | &[a, _, mut b] => { - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` @@ -1055,7 +1057,7 @@ fn f() { *x = 7; //^^^^^^ 💡 error: cannot mutate immutable variable `x` let mut y = Box::new(5); - //^^^^^ 💡 weak: variable does not need to be mutable + //^^^^^ 💡 warn: variable does not need to be mutable *x = *y; //^^^^^^^ 💡 error: cannot mutate immutable variable `x` let x = Box::new(5); @@ -1080,18 +1082,39 @@ fn main() { } #[test] - fn respect_allow_unused_mut() { - // FIXME: respect + fn respect_lint_attributes_for_unused_mut() { check_diagnostics( r#" fn f(_: i32) {} fn main() { #[allow(unused_mut)] let mut x = 2; - //^^^^^ 💡 weak: variable does not need to be mutable + f(x); +} + +fn main2() { + #[deny(unused_mut)] + let mut x = 2; + //^^^^^ 💡 error: variable does not need to be mutable f(x); } "#, ); + check_diagnostics( + r#" +macro_rules! mac { + ($($x:expr),*$(,)*) => ({ + #[allow(unused_mut)] + let mut vec = 2; + vec + }); +} + +fn main2() { + let mut x = mac![]; + //^^^^^ 💡 warn: variable does not need to be mutable +} + "#, + ); } } diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index a39eceab24..a34a5824f2 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -6,16 +6,17 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Assist, Diagnostic, DiagnosticsContext}; +use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: no-such-field // // This diagnostic is triggered if created structure does not have field provided in record. pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { - Diagnostic::new( - "no-such-field", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0559"), "no such field", - ctx.sema.diagnostics_display_range(d.field.clone().map(|it| it.into())).range, + d.field.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) } diff --git a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index 4cd85a479a..c44d28e77f 100644 --- a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs @@ -1,6 +1,6 @@ use either::Either; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: private-assoc-item // @@ -16,8 +16,9 @@ pub(crate) fn private_assoc_item( .name(ctx.sema.db) .map(|name| format!("`{}` ", name.display(ctx.sema.db))) .unwrap_or_default(); - Diagnostic::new( - "private-assoc-item", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0624"), format!( "{} {}is private", match d.item { @@ -27,15 +28,13 @@ pub(crate) fn private_assoc_item( }, name, ), - ctx.sema - .diagnostics_display_range(d.expr_or_pat.clone().map(|it| match it { + d.expr_or_pat.clone().map(|it| match it { + Either::Left(it) => it.into(), + Either::Right(it) => match it { Either::Left(it) => it.into(), - Either::Right(it) => match it { - Either::Left(it) => it.into(), - Either::Right(it) => it.into(), - }, - })) - .range, + Either::Right(it) => it.into(), + }, + }), ) } diff --git a/crates/ide-diagnostics/src/handlers/private_field.rs b/crates/ide-diagnostics/src/handlers/private_field.rs index de7f51f693..553defcf99 100644 --- a/crates/ide-diagnostics/src/handlers/private_field.rs +++ b/crates/ide-diagnostics/src/handlers/private_field.rs @@ -1,18 +1,19 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: private-field // // This diagnostic is triggered if the accessed field is not visible from the current module. pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) -> Diagnostic { // FIXME: add quickfix - Diagnostic::new( - "private-field", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0616"), format!( "field `{}` of `{}` is private", d.field.name(ctx.sema.db).display(ctx.sema.db), d.field.parent_def(ctx.sema.db).name(ctx.sema.db).display(ctx.sema.db) ), - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index d3eda3c5eb..083ef3e8dc 100644 --- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -6,7 +6,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Assist, Diagnostic, DiagnosticsContext, Severity}; +use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: replace-filter-map-next-with-find-map // @@ -15,12 +15,12 @@ pub(crate) fn replace_filter_map_next_with_find_map( ctx: &DiagnosticsContext<'_>, d: &hir::ReplaceFilterMapNextWithFindMap, ) -> Diagnostic { - Diagnostic::new( - "replace-filter-map-next-with-find-map", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::Clippy("filter_map_next"), "replace filter_map(..).next() with find_map(..)", - ctx.sema.diagnostics_display_range(InFile::new(d.file, d.next_expr.clone().into())).range, + InFile::new(d.file, d.next_expr.clone().into()), ) - .severity(Severity::WeakWarning) .with_fixes(fixes(ctx, d)) } @@ -64,7 +64,7 @@ mod tests { pub(crate) fn check_diagnostics(ra_fixture: &str) { let mut config = DiagnosticsConfig::test_sample(); config.disabled.insert("inactive-code".to_string()); - config.disabled.insert("unresolved-method".to_string()); + config.disabled.insert("E0599".to_string()); check_diagnostics_with_config(config, ra_fixture) } @@ -139,4 +139,33 @@ fn foo() { "#, ) } + + #[test] + fn respect_lint_attributes_for_clippy_equivalent() { + check_diagnostics( + r#" +//- minicore: iterators + +fn foo() { + #[allow(clippy::filter_map_next)] + let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} + +#[deny(clippy::filter_map_next)] +fn foo() { + let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: replace filter_map(..).next() with find_map(..) + +fn foo() { + let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..) + +#[warn(clippy::filter_map_next)] +fn foo() { + let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warn: replace filter_map(..).next() with find_map(..) + +"#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index c28f98d833..15bd28c00d 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -7,7 +7,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext}; +use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: type-mismatch // @@ -39,7 +39,7 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) } }; let mut diag = Diagnostic::new( - "type-mismatch", + DiagnosticCode::RustcHardError("E0308"), format!( "expected {}, found {}", d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId), diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index e12bbcf682..4af6722711 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -7,7 +7,7 @@ use ide_db::{ use syntax::AstNode; use text_edit::TextEdit; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: typed-hole // @@ -26,7 +26,8 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di ) }; - Diagnostic::new("typed-hole", message, display_range.range).with_fixes(fixes) + Diagnostic::new(DiagnosticCode::RustcHardError("typed-hole"), message, display_range.range) + .with_fixes(fixes) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option> { diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs index 034e4fcfb8..7de9a9a323 100644 --- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: undeclared-label pub(crate) fn undeclared_label( @@ -6,10 +6,11 @@ pub(crate) fn undeclared_label( d: &hir::UndeclaredLabel, ) -> Diagnostic { let name = &d.name; - Diagnostic::new( - "undeclared-label", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("undeclared-label"), format!("use of undeclared label `{}`", name.display(ctx.sema.db)), - ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range, + d.node.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs b/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs index e879de75cd..bcce72a7d0 100644 --- a/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs +++ b/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: unimplemented-builtin-macro // @@ -7,10 +7,10 @@ pub(crate) fn unimplemented_builtin_macro( ctx: &DiagnosticsContext<'_>, d: &hir::UnimplementedBuiltinMacro, ) -> Diagnostic { - Diagnostic::new( - "unimplemented-builtin-macro", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::Ra("unimplemented-builtin-macro", Severity::WeakWarning), "unimplemented built-in macro".to_string(), - ctx.sema.diagnostics_display_range(d.node.clone()).range, + d.node.clone(), ) - .severity(Severity::WeakWarning) } diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 271e7ce73b..afb899f4fc 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -14,7 +14,7 @@ use syntax::{ }; use text_edit::TextEdit; -use crate::{fix, Assist, Diagnostic, DiagnosticsContext, Severity}; +use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: unlinked-file // @@ -46,8 +46,7 @@ pub(crate) fn unlinked_file( .unwrap_or(range); acc.push( - Diagnostic::new("unlinked-file", message, range) - .severity(Severity::WeakWarning) + Diagnostic::new(DiagnosticCode::Ra("unlinked-file", Severity::WeakWarning), message, range) .with_fixes(fixes), ); } diff --git a/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/crates/ide-diagnostics/src/handlers/unreachable_label.rs index 9fedadeae0..1c5d6cd090 100644 --- a/crates/ide-diagnostics/src/handlers/unreachable_label.rs +++ b/crates/ide-diagnostics/src/handlers/unreachable_label.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unreachable-label pub(crate) fn unreachable_label( @@ -6,10 +6,11 @@ pub(crate) fn unreachable_label( d: &hir::UnreachableLabel, ) -> Diagnostic { let name = &d.name; - Diagnostic::new( - "unreachable-label", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0767"), format!("use of unreachable label `{}`", name.display(ctx.sema.db)), - ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range, + d.node.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs index 74e4a69c64..f8265b6327 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-extern-crate // @@ -7,10 +7,11 @@ pub(crate) fn unresolved_extern_crate( ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedExternCrate, ) -> Diagnostic { - Diagnostic::new( - "unresolved-extern-crate", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("unresolved-extern-crate"), "unresolved extern crate", - ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, + d.decl.clone().map(|it| it.into()), ) } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 5e4efa41fd..94d49d4d48 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -8,7 +8,7 @@ use ide_db::{ use syntax::{ast, AstNode, AstPtr}; use text_edit::TextEdit; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-field // @@ -22,14 +22,15 @@ pub(crate) fn unresolved_field( } else { "" }; - Diagnostic::new( - "unresolved-field", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0559"), format!( "no field `{}` on type `{}`{method_suffix}", d.name.display(ctx.sema.db), d.receiver.display(ctx.sema.db) ), - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) .experimental() diff --git a/crates/ide-diagnostics/src/handlers/unresolved_import.rs b/crates/ide-diagnostics/src/handlers/unresolved_import.rs index e52a88459d..6b8026c034 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_import.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_import.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-import // @@ -8,10 +8,11 @@ pub(crate) fn unresolved_import( ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedImport, ) -> Diagnostic { - Diagnostic::new( - "unresolved-import", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0432"), "unresolved import", - ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, + d.decl.clone().map(|it| it.into()), ) // This currently results in false positives in the following cases: // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly) diff --git a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 3943b51ab4..33e7c2e37c 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-macro-call // @@ -12,7 +12,7 @@ pub(crate) fn unresolved_macro_call( let display_range = ctx.resolve_precise_location(&d.macro_call, d.precise_location); let bang = if d.is_bang { "!" } else { "" }; Diagnostic::new( - "unresolved-macro-call", + DiagnosticCode::RustcHardError("unresolved-macro-call"), format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db)), display_range, ) diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 8bbb837e67..ae9f6744c4 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -8,7 +8,7 @@ use ide_db::{ use syntax::{ast, AstNode, TextRange}; use text_edit::TextEdit; -use crate::{Diagnostic, DiagnosticsContext}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-method // @@ -22,14 +22,15 @@ pub(crate) fn unresolved_method( } else { "" }; - Diagnostic::new( - "unresolved-method", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0599"), format!( "no method `{}` on type `{}`{field_suffix}", d.name.display(ctx.sema.db), d.receiver.display(ctx.sema.db) ), - ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + d.expr.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) .experimental() diff --git a/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 6e3fd3b42b..be24e50c98 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -3,7 +3,7 @@ use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSyste use itertools::Itertools; use syntax::AstNode; -use crate::{fix, Diagnostic, DiagnosticsContext}; +use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unresolved-module // @@ -12,8 +12,9 @@ pub(crate) fn unresolved_module( ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule, ) -> Diagnostic { - Diagnostic::new( - "unresolved-module", + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0583"), match &*d.candidates { [] => "unresolved module".to_string(), [candidate] => format!("unresolved module, can't find module file: {candidate}"), @@ -25,7 +26,7 @@ pub(crate) fn unresolved_module( ) } }, - ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, + d.decl.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) } @@ -82,8 +83,8 @@ mod baz {} expect![[r#" [ Diagnostic { - code: DiagnosticCode( - "unresolved-module", + code: RustcHardError( + "E0583", ), message: "unresolved module, can't find module file: foo.rs, or foo/mod.rs", range: 0..8, @@ -148,6 +149,22 @@ mod baz {} }, ], ), + main_node: Some( + InFile { + file_id: FileId( + FileId( + 0, + ), + ), + value: MODULE@0..8 + MOD_KW@0..3 "mod" + WHITESPACE@3..4 " " + NAME@4..7 + IDENT@4..7 "foo" + SEMICOLON@7..8 ";" + , + }, + ), }, ] "#]], diff --git a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs index ae5cf13583..015a3d6b2c 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs @@ -1,6 +1,6 @@ use hir::db::DefDatabase; -use crate::{Diagnostic, DiagnosticsContext, Severity}; +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: unresolved-proc-macro // @@ -41,5 +41,5 @@ pub(crate) fn unresolved_proc_macro( }; let message = format!("{not_expanded_message}: {message}"); - Diagnostic::new("unresolved-proc-macro", message, display_range).severity(severity) + Diagnostic::new(DiagnosticCode::Ra("unresolved-proc-macro", severity), message, display_range) } diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs index 289ed0458c..11ce811efa 100644 --- a/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use syntax::{ast, AstNode, SyntaxNode, TextRange}; use text_edit::TextEdit; -use crate::{fix, Diagnostic, Severity}; +use crate::{fix, Diagnostic, DiagnosticCode}; // Diagnostic: unnecessary-braces // @@ -32,11 +32,10 @@ pub(crate) fn useless_braces( acc.push( Diagnostic::new( - "unnecessary-braces", + DiagnosticCode::RustcLint("unused_braces"), "Unnecessary braces in use statement".to_string(), use_range, ) - .severity(Severity::WeakWarning) .with_fixes(Some(vec![fix( "remove_braces", "Remove unnecessary braces", diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 55a4a482d3..e6b2eeeddf 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -67,24 +67,61 @@ mod handlers { #[cfg(test)] mod tests; +use std::collections::HashMap; + use hir::{diagnostics::AnyDiagnostic, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, base_db::{FileId, FileRange, SourceDatabase}, + generated::lints::{LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS}, imports::insert_use::InsertUseConfig, label::Label, source_change::SourceChange, - FxHashSet, RootDatabase, + syntax_helpers::node_ext::parse_tt_as_comma_sep_paths, + FxHashMap, FxHashSet, RootDatabase, +}; +use once_cell::sync::Lazy; +use stdx::never; +use syntax::{ + algo::find_node_at_range, + ast::{self, AstNode}, + SyntaxNode, SyntaxNodePtr, TextRange, }; -use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange}; // FIXME: Make this an enum -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct DiagnosticCode(pub &'static str); +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum DiagnosticCode { + RustcHardError(&'static str), + RustcLint(&'static str), + Clippy(&'static str), + Ra(&'static str, Severity), +} impl DiagnosticCode { - pub fn as_str(&self) -> &str { - self.0 + pub fn url(&self) -> String { + match self { + DiagnosticCode::RustcHardError(e) => { + format!("https://doc.rust-lang.org/stable/error_codes/{e}.html") + } + DiagnosticCode::RustcLint(e) => { + format!("https://doc.rust-lang.org/rustc/?search={e}") + } + DiagnosticCode::Clippy(e) => { + format!("https://rust-lang.github.io/rust-clippy/master/#/{e}") + } + DiagnosticCode::Ra(e, _) => { + format!("https://rust-analyzer.github.io/manual.html#{e}") + } + } + } + + pub fn as_str(&self) -> &'static str { + match self { + DiagnosticCode::RustcHardError(r) + | DiagnosticCode::RustcLint(r) + | DiagnosticCode::Clippy(r) + | DiagnosticCode::Ra(r, _) => r, + } } } @@ -97,29 +134,51 @@ pub struct Diagnostic { pub unused: bool, pub experimental: bool, pub fixes: Option>, + // The node that will be affected by `#[allow]` and similar attributes. + pub main_node: Option>, } impl Diagnostic { - fn new(code: &'static str, message: impl Into, range: TextRange) -> Diagnostic { + fn new(code: DiagnosticCode, message: impl Into, range: TextRange) -> Diagnostic { let message = message.into(); Diagnostic { - code: DiagnosticCode(code), + code, message, range, - severity: Severity::Error, + severity: match code { + DiagnosticCode::RustcHardError(_) => Severity::Error, + // FIXME: Rustc lints are not always warning, but the ones that are currently implemented are all warnings. + DiagnosticCode::RustcLint(_) => Severity::Warning, + // FIXME: We can make this configurable, and if the user uses `cargo clippy` on flycheck, we can + // make it normal warning. + DiagnosticCode::Clippy(_) => Severity::WeakWarning, + DiagnosticCode::Ra(_, s) => s, + }, unused: false, experimental: false, fixes: None, + main_node: None, } } + fn new_with_syntax_node_ptr( + ctx: &DiagnosticsContext<'_>, + code: DiagnosticCode, + message: impl Into, + node: InFile, + ) -> Diagnostic { + let file_id = node.file_id; + Diagnostic::new(code, message, ctx.sema.diagnostics_display_range(node.clone()).range) + .with_main_node(node.map(|x| x.to_node(&ctx.sema.parse_or_expand(file_id)))) + } + fn experimental(mut self) -> Diagnostic { self.experimental = true; self } - fn severity(mut self, severity: Severity) -> Diagnostic { - self.severity = severity; + fn with_main_node(mut self, main_node: InFile) -> Diagnostic { + self.main_node = Some(main_node); self } @@ -134,12 +193,12 @@ impl Diagnostic { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Severity { Error, - // We don't actually emit this one yet, but we should at some point. - // Warning, + Warning, WeakWarning, + Allow, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -228,11 +287,13 @@ pub fn diagnostics( let mut res = Vec::new(); // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. - res.extend( - parse.errors().iter().take(128).map(|err| { - Diagnostic::new("syntax-error", format!("Syntax Error: {err}"), err.range()) - }), - ); + res.extend(parse.errors().iter().take(128).map(|err| { + Diagnostic::new( + DiagnosticCode::RustcHardError("syntax-error"), + format!("Syntax Error: {err}"), + err.range(), + ) + })); let parse = sema.parse(file_id); @@ -271,7 +332,7 @@ pub fn diagnostics( res.extend(d.errors.iter().take(32).map(|err| { { Diagnostic::new( - "syntax-error", + DiagnosticCode::RustcHardError("syntax-error"), format!("Syntax Error in Expansion: {err}"), ctx.resolve_precise_location(&d.node.clone(), d.precise_location), ) @@ -309,14 +370,168 @@ pub fn diagnostics( res.push(d) } + let mut diagnostics_of_range = + res.iter_mut().filter_map(|x| Some((x.main_node.clone()?, x))).collect::>(); + + let mut rustc_stack: FxHashMap> = FxHashMap::default(); + let mut clippy_stack: FxHashMap> = FxHashMap::default(); + + handle_lint_attributes( + &ctx.sema, + parse.syntax(), + &mut rustc_stack, + &mut clippy_stack, + &mut diagnostics_of_range, + ); + res.retain(|d| { - !ctx.config.disabled.contains(d.code.as_str()) + d.severity != Severity::Allow + && !ctx.config.disabled.contains(d.code.as_str()) && !(ctx.config.disable_experimental && d.experimental) }); res } +// `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros + +static RUSTC_LINT_GROUPS_DICT: Lazy>> = + Lazy::new(|| build_group_dict(DEFAULT_LINT_GROUPS, &["warnings", "__RA_EVERY_LINT"], "")); + +static CLIPPY_LINT_GROUPS_DICT: Lazy>> = + Lazy::new(|| build_group_dict(CLIPPY_LINT_GROUPS, &["__RA_EVERY_LINT"], "clippy::")); + +fn build_group_dict( + lint_group: &'static [LintGroup], + all_groups: &'static [&'static str], + prefix: &'static str, +) -> HashMap<&'static str, Vec<&'static str>> { + let mut r: HashMap<&str, Vec<&str>> = HashMap::new(); + for g in lint_group { + for child in g.children { + r.entry(child.strip_prefix(prefix).unwrap()) + .or_default() + .push(g.lint.label.strip_prefix(prefix).unwrap()); + } + } + for (lint, groups) in r.iter_mut() { + groups.push(lint); + groups.extend_from_slice(all_groups); + } + r +} + +fn handle_lint_attributes( + sema: &Semantics<'_, RootDatabase>, + root: &SyntaxNode, + rustc_stack: &mut FxHashMap>, + clippy_stack: &mut FxHashMap>, + diagnostics_of_range: &mut FxHashMap, &mut Diagnostic>, +) { + let file_id = sema.hir_file_for(root); + for ev in root.preorder() { + match ev { + syntax::WalkEvent::Enter(node) => { + for attr in node.children().filter_map(ast::Attr::cast) { + parse_lint_attribute(attr, rustc_stack, clippy_stack, |stack, severity| { + stack.push(severity); + }); + } + if let Some(x) = + diagnostics_of_range.get_mut(&InFile { file_id, value: node.clone() }) + { + const EMPTY_LINTS: &[&str] = &[]; + let (names, stack) = match x.code { + DiagnosticCode::RustcLint(name) => ( + RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x), + &mut *rustc_stack, + ), + DiagnosticCode::Clippy(name) => ( + CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x), + &mut *clippy_stack, + ), + _ => continue, + }; + for &name in names { + if let Some(s) = stack.get(name).and_then(|x| x.last()) { + x.severity = *s; + } + } + } + if let Some(item) = ast::Item::cast(node.clone()) { + if let Some(me) = sema.expand_attr_macro(&item) { + for stack in [&mut *rustc_stack, &mut *clippy_stack] { + stack + .entry("__RA_EVERY_LINT".to_owned()) + .or_default() + .push(Severity::Allow); + } + handle_lint_attributes( + sema, + &me, + rustc_stack, + clippy_stack, + diagnostics_of_range, + ); + for stack in [&mut *rustc_stack, &mut *clippy_stack] { + stack.entry("__RA_EVERY_LINT".to_owned()).or_default().pop(); + } + } + } + if let Some(mc) = ast::MacroCall::cast(node) { + if let Some(me) = sema.expand(&mc) { + handle_lint_attributes( + sema, + &me, + rustc_stack, + clippy_stack, + diagnostics_of_range, + ); + } + } + } + syntax::WalkEvent::Leave(node) => { + for attr in node.children().filter_map(ast::Attr::cast) { + parse_lint_attribute(attr, rustc_stack, clippy_stack, |stack, severity| { + if stack.pop() != Some(severity) { + never!("Mismatched serevity in walking lint attributes"); + } + }); + } + } + } + } +} + +fn parse_lint_attribute( + attr: ast::Attr, + rustc_stack: &mut FxHashMap>, + clippy_stack: &mut FxHashMap>, + job: impl Fn(&mut Vec, Severity), +) { + let Some((tag, args_tt)) = attr.as_simple_call() else { + return; + }; + let serevity = match tag.as_str() { + "allow" => Severity::Allow, + "warn" => Severity::Warning, + "forbid" | "deny" => Severity::Error, + _ => return, + }; + for lint in parse_tt_as_comma_sep_paths(args_tt).into_iter().flatten() { + if let Some(lint) = lint.as_single_name_ref() { + job(rustc_stack.entry(lint.to_string()).or_default(), serevity); + } + if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) { + if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) { + if tool.to_string() == "clippy" { + job(clippy_stack.entry(name_ref.to_string()).or_default(), serevity); + } + } + } + } +} + fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist { let mut res = unresolved_fix(id, label, target); res.source_change = Some(source_change); diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index b5cd4e0d68..4ac9d0a9fb 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -114,6 +114,8 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur annotation.push_str(match d.severity { Severity::Error => "error", Severity::WeakWarning => "weak", + Severity::Warning => "warn", + Severity::Allow => "allow", }); annotation.push_str(": "); annotation.push_str(&d.message); @@ -130,14 +132,19 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur ) } } - assert_eq!(expected, actual); + if expected != actual { + let fneg = expected.iter().filter(|x| !actual.contains(x)).collect::>(); + let fpos = actual.iter().filter(|x| !expected.contains(x)).collect::>(); + + panic!("Diagnostic test failed.\nFalse negatives: {fneg:?}\nFalse positives: {fpos:?}"); + } } } #[test] fn test_disabled_diagnostics() { let mut config = DiagnosticsConfig::test_sample(); - config.disabled.insert("unresolved-module".into()); + config.disabled.insert("E0583".into()); let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); @@ -159,7 +166,7 @@ fn minicore_smoke_test() { let source = minicore.source_code(); let mut config = DiagnosticsConfig::test_sample(); // This should be ignored since we conditionaly remove code which creates single item use with braces - config.disabled.insert("unnecessary-braces".to_string()); + config.disabled.insert("unused_braces".to_string()); check_diagnostics_with_config(config, &source); } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index c70f60513b..0a20472fe7 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -831,11 +831,7 @@ impl GlobalState { d.code.as_str().to_string(), )), code_description: Some(lsp_types::CodeDescription { - href: lsp_types::Url::parse(&format!( - "https://rust-analyzer.github.io/manual.html#{}", - d.code.as_str() - )) - .unwrap(), + href: lsp_types::Url::parse(&d.code.url()).unwrap(), }), source: Some("rust-analyzer".to_string()), message: d.message, diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 648bc995ad..ba3421bf9e 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -94,7 +94,10 @@ pub(crate) fn document_highlight_kind( pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity { match severity { Severity::Error => lsp_types::DiagnosticSeverity::ERROR, + Severity::Warning => lsp_types::DiagnosticSeverity::WARNING, Severity::WeakWarning => lsp_types::DiagnosticSeverity::HINT, + // unreachable + Severity::Allow => lsp_types::DiagnosticSeverity::INFORMATION, } } From 58ac823864d55ba06f604a851c780c293e137e99 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 17 Jun 2023 10:58:52 +0200 Subject: [PATCH 004/201] Less eager parsing for module sources --- crates/hir-def/src/nameres.rs | 13 ++++++++++++- crates/hir/src/has_source.rs | 18 +++++++++++++++++- crates/ide-completion/src/completions/mod_.rs | 2 +- crates/ide-db/src/search.rs | 6 ++---- crates/ide/src/static_index.rs | 4 ++-- crates/rust-analyzer/src/cli/analysis_stats.rs | 2 +- crates/rust-analyzer/src/cli/diagnostics.rs | 4 ++-- 7 files changed, 37 insertions(+), 12 deletions(-) diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 0ab1bd8490..e7a4355d25 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -60,7 +60,7 @@ mod tests; use std::{cmp::Ord, ops::Deref}; use base_db::{CrateId, Edition, FileId, ProcMacroKind}; -use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId}; +use hir_expand::{name::Name, HirFileId, InFile, MacroCallId, MacroDefId}; use itertools::Itertools; use la_arena::Arena; use profile::Count; @@ -626,6 +626,17 @@ impl ModuleData { self.origin.definition_source(db) } + /// Same as [`definition_source`] but only returns the file id to prevent parsing the ASt. + pub fn definition_source_file_id(&self) -> HirFileId { + match self.origin { + ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { + definition.into() + } + ModuleOrigin::Inline { definition, .. } => definition.file_id, + ModuleOrigin::BlockExpr { block } => block.file_id, + } + } + /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. /// `None` for the crate root or block. pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option> { diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 9f6b5c0a9f..63d11155c6 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs @@ -1,12 +1,13 @@ //! Provides set of implementation for hir's objects that allows get back location in file. +use base_db::FileId; use either::Either; use hir_def::{ nameres::{ModuleOrigin, ModuleSource}, src::{HasChildSource, HasSource as _}, Lookup, MacroId, VariantId, }; -use hir_expand::InFile; +use hir_expand::{HirFileId, InFile}; use syntax::ast; use crate::{ @@ -32,6 +33,11 @@ impl Module { def_map[self.id.local_id].definition_source(db.upcast()) } + pub fn definition_source_file_id(self, db: &dyn HirDatabase) -> HirFileId { + let def_map = self.id.def_map(db.upcast()); + def_map[self.id.local_id].definition_source_file_id() + } + pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool { let def_map = self.id.def_map(db.upcast()); match def_map[self.id.local_id].origin { @@ -40,6 +46,16 @@ impl Module { } } + pub fn as_source_file_id(self, db: &dyn HirDatabase) -> Option { + let def_map = self.id.def_map(db.upcast()); + match def_map[self.id.local_id].origin { + ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition, .. } => { + Some(definition) + } + _ => None, + } + } + pub fn is_inline(self, db: &dyn HirDatabase) -> bool { let def_map = self.id.def_map(db.upcast()); def_map[self.id.local_id].origin.is_inline() diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs index d3e75c6da4..1e09894059 100644 --- a/crates/ide-completion/src/completions/mod_.rs +++ b/crates/ide-completion/src/completions/mod_.rs @@ -42,7 +42,7 @@ pub(crate) fn complete_mod( } let module_definition_file = - current_module.definition_source(ctx.db).file_id.original_file(ctx.db); + current_module.definition_source_file_id(ctx.db).original_file(ctx.db); let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file)); let directory_to_look_for_submodules = directory_to_look_for_submodules( current_module, diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index e8ff107bd4..16f3598183 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -149,10 +149,8 @@ impl SearchScope { let mut to_visit: Vec<_> = module.children(db).collect(); while let Some(module) = to_visit.pop() { - if let InFile { file_id, value: ModuleSource::SourceFile(_) } = - module.definition_source(db) - { - entries.insert(file_id.original_file(db), None); + if let Some(file_id) = module.as_source_file_id(db) { + entries.insert(file_id, None); } to_visit.extend(module.children(db)); } diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 3e3d9f8f85..03c7ee478a 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -187,7 +187,7 @@ impl StaticIndex<'_> { pub fn compute(analysis: &Analysis) -> StaticIndex<'_> { let db = &*analysis.db; let work = all_modules(db).into_iter().filter(|module| { - let file_id = module.definition_source(db).file_id.original_file(db); + let file_id = module.definition_source_file_id(db).original_file(db); let source_root = db.file_source_root(file_id); let source_root = db.source_root(source_root); !source_root.is_library @@ -201,7 +201,7 @@ impl StaticIndex<'_> { }; let mut visited_files = FxHashSet::default(); for module in work { - let file_id = module.definition_source(db).file_id.original_file(db); + let file_id = module.definition_source_file_id(db).original_file(db); if visited_files.contains(&file_id) { continue; } diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 01bc0d77dd..9d6c633f4c 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -105,7 +105,7 @@ impl flags::AnalysisStats { } for krate in krates { let module = krate.root_module(db); - let file_id = module.definition_source(db).file_id; + let file_id = module.definition_source_file_id(db); let file_id = file_id.original_file(db); let source_root = db.file_source_root(file_id); let source_root = db.source_root(source_root); diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 4306d72129..0b46f35074 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -37,14 +37,14 @@ impl flags::Diagnostics { let mut visited_files = FxHashSet::default(); let work = all_modules(db).into_iter().filter(|module| { - let file_id = module.definition_source(db).file_id.original_file(db); + let file_id = module.definition_source_file_id(db).original_file(db); let source_root = db.file_source_root(file_id); let source_root = db.source_root(source_root); !source_root.is_library }); for module in work { - let file_id = module.definition_source(db).file_id.original_file(db); + 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_string(); From a824b734dd1dda4337b17ffae3b8c80eaad24cec Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 17 Jun 2023 11:20:21 +0200 Subject: [PATCH 005/201] Report metric timings for file item trees and crate def map creation --- .../rust-analyzer/src/cli/analysis_stats.rs | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 9d6c633f4c..7c982fcc22 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -95,14 +95,38 @@ impl flags::AnalysisStats { eprintln!(")"); let mut analysis_sw = self.stop_watch(); - let mut num_crates = 0; - let mut visited_modules = FxHashSet::default(); - let mut visit_queue = Vec::new(); let mut krates = Crate::all(db); if self.randomize { shuffle(&mut rng, &mut krates); } + + let mut item_tree_sw = self.stop_watch(); + let mut num_item_trees = 0; + let source_roots = + krates.iter().cloned().map(|krate| db.file_source_root(krate.root_file(db))).unique(); + for source_root_id in source_roots { + let source_root = db.source_root(source_root_id); + if !source_root.is_library || self.with_deps { + for file_id in source_root.iter() { + if let Some(p) = source_root.path_for_file(&file_id) { + if let Some((_, Some("rs"))) = p.name_and_extension() { + db.file_item_tree(file_id.into()); + num_item_trees += 1; + } + } + } + } + } + eprintln!(" item trees: {num_item_trees}"); + let item_tree_time = item_tree_sw.elapsed(); + eprintln!("{:<20} {}", "Item Tree Collection:", item_tree_time); + report_metric("item tree time", item_tree_time.time.as_millis() as u64, "ms"); + + let mut crate_def_map_sw = self.stop_watch(); + let mut num_crates = 0; + let mut visited_modules = FxHashSet::default(); + let mut visit_queue = Vec::new(); for krate in krates { let module = krate.root_module(db); let file_id = module.definition_source_file_id(db); @@ -169,7 +193,9 @@ impl flags::AnalysisStats { visited_modules.len(), bodies.len() ); - eprintln!("{:<20} {}", "Item Collection:", analysis_sw.elapsed()); + let crate_def_map_time = crate_def_map_sw.elapsed(); + eprintln!("{:<20} {}", "Item Collection:", crate_def_map_time); + report_metric("crate def map time", crate_def_map_time.time.as_millis() as u64, "ms"); if self.randomize { shuffle(&mut rng, &mut bodies); From 8123a39c82f31d86cdfd1adf92cff44b8391d0b5 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Fri, 2 Jun 2023 11:51:11 +0200 Subject: [PATCH 006/201] Generate delegate trait --- .../src/handlers/generate_delegate_trait.rs | 1064 +++++++++++++++++ crates/ide-assists/src/lib.rs | 2 + crates/ide-assists/src/tests/generated.rs | 63 + 3 files changed, 1129 insertions(+) create mode 100644 crates/ide-assists/src/handlers/generate_delegate_trait.rs diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs new file mode 100644 index 0000000000..ce8f4256c0 --- /dev/null +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -0,0 +1,1064 @@ +use std::ops::Not; + +use crate::{ + assist_context::{AssistContext, Assists}, + utils::convert_param_list_to_arg_list, +}; +use either::Either; +use hir::{db::HirDatabase, HasVisibility}; +use ide_db::{ + assists::{AssistId, GroupLabel}, + path_transform::PathTransform, +}; +use syntax::{ + ast::{ + self, + edit::{self, AstNodeEdit}, + make, AssocItem, HasGenericParams, HasName, HasVisibility as astHasVisibility, Path, + }, + ted::{self, Position}, + AstNode, NodeOrToken, SyntaxKind, +}; + +// Assist: generate_delegate_trait +// +// Generate delegate trait implementation for `StructField`s. +// +// ``` +// trait SomeTrait { +// type T; +// fn fn_(arg: u32) -> u32; +// fn method_(&mut self) -> bool; +// } +// struct A; +// impl SomeTrait for A { +// type T = u32; +// +// fn fn_(arg: u32) -> u32 { +// 42 +// } +// +// fn method_(&mut self) -> bool { +// false +// } +// } +// struct B { +// a$0: A, +// } +// ``` +// -> +// ``` +// trait SomeTrait { +// type T; +// fn fn_(arg: u32) -> u32; +// fn method_(&mut self) -> bool; +// } +// struct A; +// impl SomeTrait for A { +// type T = u32; +// +// fn fn_(arg: u32) -> u32 { +// 42 +// } +// +// fn method_(&mut self) -> bool { +// false +// } +// } +// struct B { +// a: A, +// } +// +// impl SomeTrait for B { +// type T = ::T; +// +// fn fn_(arg: u32) -> u32 { +// ::fn_(arg) +// } +// +// fn method_(&mut self) -> bool { +// ::method_( &mut self.a ) +// } +// } +// ``` +pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let strukt = Struct::new(ctx.find_node_at_offset::()?)?; + + let field: Field = match ctx.find_node_at_offset::() { + Some(field) => Field::new(&ctx, Either::Left(field))?, + None => { + let field = ctx.find_node_at_offset::()?; + let field_list = ctx.find_node_at_offset::()?; + Field::new(&ctx, either::Right((field, field_list)))? + } + }; + + strukt.delegate(field, acc, ctx); + Some(()) +} + +/// A utility object that represents a struct's field. +struct Field { + name: String, + ty: ast::Type, + range: syntax::TextRange, + impls: Vec, +} + +impl Field { + pub fn new( + ctx: &AssistContext<'_>, + f: Either, + ) -> Option { + let db = ctx.sema.db; + let name: String; + let range: syntax::TextRange; + let ty: ast::Type; + + let module = ctx.sema.to_module_def(ctx.file_id())?; + + match f { + Either::Left(f) => { + name = f.name()?.to_string(); + ty = f.ty()?; + range = f.syntax().text_range(); + } + Either::Right((f, l)) => { + name = l.fields().position(|it| it == f)?.to_string(); + ty = f.ty()?; + range = f.syntax().text_range(); + } + }; + + let hir_ty = ctx.sema.resolve_type(&ty)?; + let type_impls = hir::Impl::all_for_type(db, hir_ty.clone()); + let mut impls = Vec::with_capacity(type_impls.len()); + let type_param = hir_ty.as_type_param(db); + + if let Some(tp) = type_param { + for tb in tp.trait_bounds(db) { + impls.push(Delegee::Bound(BoundCase { 0: tb })); + } + }; + + for imp in type_impls { + match imp.trait_(db) { + Some(tr) => { + if tr.is_visible_from(db, module) { + impls.push(Delegee::Impls(ImplCase { 0: tr, 1: imp })) + } + } + None => { + continue; + } + } + } + + Some(Field { name, ty, range, impls }) + } +} + +/// A field that we want to delegate can offer the enclosing struct +/// trait to implement in two ways. The first way is when the field +/// actually implements the trait and the second way is when the field +/// has a bound type parameter. We handle these cases in different ways +/// hence the enum. +enum Delegee { + Bound(BoundCase), + Impls(ImplCase), +} + +struct BoundCase(hir::Trait); +struct ImplCase(hir::Trait, hir::Impl); + +/// When we list traits we can implement for the enclosing struct +/// we use the absolute path of a trait. This trait consists of a single +/// method that produces this path. +trait Signatured { + fn signature(&self, db: &dyn HirDatabase) -> String; +} + +impl Signatured for Delegee { + fn signature(&self, db: &dyn HirDatabase) -> String { + let mut s = String::new(); + let trait_: hir::Trait; + + match self { + Delegee::Bound(b) => trait_ = b.0, + Delegee::Impls(i) => trait_ = i.0, + }; + + for m in trait_.module(db).path_to_root(db).iter().rev() { + if let Some(name) = m.name(db) { + s.push_str(&format!("{}::", name.to_smol_str())); + } else { + continue; + } + } + + s.push_str(&trait_.name(db).to_smol_str()); + s + } +} + +/// A utility struct that is used for the enclosing struct. +struct Struct { + strukt: ast::Struct, + name: ast::Name, +} + +impl Struct { + pub fn new(s: ast::Struct) -> Option { + let name = s.name()?; + Some(Struct { name, strukt: s }) + } + + pub fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) { + let db = ctx.db(); + for delegee in &field.impls { + // FIXME : We can omit already implemented impl_traits + // But we don't know what the &[hir::Type] argument should look like. + + // let trait_ = match delegee { + // Delegee::Bound(b) => b.0, + // Delegee::Impls(i) => i.1, + // }; + + // if self.hir_ty.impls_trait(db, trait_, &[]) { + // continue; + // } + let signature = delegee.signature(db); + let delegate = generate_impl(ctx, self, &field.ty, &field.name, delegee); + + acc.add_group( + &GroupLabel("Generate delegate traits...".to_owned()), + AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate), + format!("Generate delegate impl `{}` for `{}`", signature, field.name), + field.range, + |builder| { + builder.insert( + self.strukt.syntax().text_range().end(), + format!("\n\n{}", delegate.syntax()), + ); + }, + ); + } + } +} + +fn generate_impl( + ctx: &AssistContext<'_>, + strukt: &Struct, + field_ty: &ast::Type, + field_name: &String, + delegee: &Delegee, +) -> ast::Impl { + let delegate: ast::Impl; + let source: ast::Impl; + let genpar: Option; + let db = ctx.db(); + let base_path = make::path_from_text(&field_ty.to_string().as_str()); + let s_path = make::ext::ident_path(&strukt.name.to_string()); + + match delegee { + Delegee::Bound(delegee) => { + let in_file = ctx.sema.source(delegee.0.to_owned()).unwrap(); + let source: ast::Trait = in_file.value; + + delegate = make::impl_trait( + delegee.0.is_unsafe(db), + None, + None, + strukt.strukt.generic_param_list(), + None, + delegee.0.is_auto(db), + make::ty(&delegee.0.name(db).to_smol_str()), + make::ty_path(s_path), + source.where_clause(), + strukt.strukt.where_clause(), + None, + ) + .clone_for_update(); + + genpar = source.generic_param_list(); + let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); + let gen_args: String = + genpar.map_or_else(String::new, |params| params.to_generic_args().to_string()); + + // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths + let qualified_path_type = make::path_from_text(&format!( + "<{} as {}{}>", + base_path.to_string(), + delegee.0.name(db).to_smol_str(), + gen_args.to_string() + )); + + match source.assoc_item_list() { + Some(ai) => { + ai.assoc_items() + .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) + .for_each(|item| { + let assoc = + process_assoc_item(item, qualified_path_type.clone(), &field_name); + if let Some(assoc) = assoc { + delegate_assoc_items.add_item(assoc); + } + }); + } + None => {} + }; + + let target = ctx.sema.scope(strukt.strukt.syntax()).unwrap(); + let source = ctx.sema.scope_for_def(delegee.0); + + let transform = + PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); + transform.apply(&delegate.syntax()); + } + Delegee::Impls(delegee) => { + let in_file = ctx.sema.source(delegee.1.to_owned()).unwrap(); + source = in_file.value; + delegate = make::impl_trait( + delegee.0.is_unsafe(db), + source.generic_param_list(), + None, + None, + None, + delegee.0.is_auto(db), + make::ty(&delegee.0.name(db).to_smol_str()), + make::ty_path(s_path), + source.where_clause(), + strukt.strukt.where_clause(), + None, + ) + .clone_for_update(); + genpar = source.generic_param_list(); + let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); + let gen_args: String = + genpar.map_or_else(String::new, |params| params.to_generic_args().to_string()); + + // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths + let qualified_path_type = make::path_from_text(&format!( + "<{} as {}{}>", + base_path.to_string().as_str(), + delegee.0.name(db).to_smol_str(), + gen_args.to_string().as_str() + )); + + source + .get_or_create_assoc_item_list() + .assoc_items() + .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) + .for_each(|item| { + let assoc = process_assoc_item(item, qualified_path_type.clone(), &field_name); + if let Some(assoc) = assoc { + delegate_assoc_items.add_item(assoc); + } + }); + + let target = ctx.sema.scope(strukt.strukt.syntax()).unwrap(); + let source = ctx.sema.scope_for_def(delegee.0); + + let transform = + PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); + transform.apply(&delegate.syntax()); + } + } + + delegate +} + +fn process_assoc_item( + item: syntax::ast::AssocItem, + qual_path_ty: ast::Path, + base_name: &str, +) -> Option { + match item { + AssocItem::Const(c) => Some(const_assoc_item(c, qual_path_ty)), + AssocItem::Fn(f) => Some(func_assoc_item(f, qual_path_ty, base_name)), + AssocItem::MacroCall(_) => { + // FIXME : Handle MacroCall case. + // return Some(macro_assoc_item(mac, qual_path_ty)); + None + } + AssocItem::TypeAlias(ta) => Some(ty_assoc_item(ta, qual_path_ty)), + } +} + +fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> AssocItem { + let path_expr_segment = make::path_from_text(item.name().unwrap().to_string().as_str()); + + // We want rhs of the const assignment to be a qualified path + // The general case for const assigment can be found [here](`https://doc.rust-lang.org/reference/items/constant-items.html`) + // The qualified will have the following generic syntax : + // >::ConstName; + // FIXME : We can't rely on `make::path_qualified` for now but it would be nice to replace the following with it. + // make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap()); + let qualpath = qualpath(qual_path_ty, path_expr_segment); + let inner = make::item_const( + item.visibility(), + item.name().unwrap(), + item.ty().unwrap(), + make::expr_path(qualpath), + ) + .clone_for_update(); + + AssocItem::Const(inner) +} + +fn func_assoc_item(item: syntax::ast::Fn, qual_path_ty: Path, base_name: &str) -> AssocItem { + let path_expr_segment = make::path_from_text(item.name().unwrap().to_string().as_str()); + let qualpath = qualpath(qual_path_ty, path_expr_segment); + + let call = match item.param_list() { + // Methods and funcs should be handled separately. + // We ask if the func has a `self` param. + Some(l) => match l.self_param() { + Some(slf) => { + let mut self_kw = make::expr_path(make::path_from_text("self")); + self_kw = make::expr_field(self_kw, base_name); + + let tail_expr_self = match slf.kind() { + ast::SelfParamKind::Owned => self_kw, + ast::SelfParamKind::Ref => make::expr_ref(self_kw, false), + ast::SelfParamKind::MutRef => make::expr_ref(self_kw, true), + }; + + let param_count = l.params().count(); + let args = convert_param_list_to_arg_list(l).clone_for_update(); + + if param_count > 0 { + // Add SelfParam and a TOKEN::COMMA + ted::insert_all( + Position::after(args.l_paren_token().unwrap()), + vec![ + NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), + NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), + NodeOrToken::Token(make::token(SyntaxKind::COMMA)), + ], + ); + } else { + // Add SelfParam only + ted::insert( + Position::after(args.l_paren_token().unwrap()), + NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), + ); + } + + make::expr_call(make::expr_path(qualpath), args) + } + None => make::expr_call(make::expr_path(qualpath), convert_param_list_to_arg_list(l)), + }, + None => make::expr_call( + make::expr_path(qualpath), + convert_param_list_to_arg_list(make::param_list(None, Vec::new())), + ), + } + .clone_for_update(); + + let body = make::block_expr(vec![], Some(call)).clone_for_update(); + let func = make::fn_( + item.visibility(), + item.name().unwrap(), + item.generic_param_list(), + item.where_clause(), + item.param_list().unwrap(), + body, + item.ret_type(), + item.async_token().is_some(), + item.const_token().is_some(), + item.unsafe_token().is_some(), + ) + .clone_for_update(); + + AssocItem::Fn(func.indent(edit::IndentLevel(1)).clone_for_update()) +} + +fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> AssocItem { + let path_expr_segment = make::path_from_text(item.name().unwrap().to_string().as_str()); + let qualpath = qualpath(qual_path_ty, path_expr_segment); + let ty = make::ty_path(qualpath); + let ident = item.name().unwrap().to_string(); + + let alias = make::ty_alias( + ident.as_str(), + item.generic_param_list(), + None, + item.where_clause(), + Some((ty, None)), + ) + .clone_for_update(); + + AssocItem::TypeAlias(alias) +} + +fn qualpath(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path { + make::path_from_text(&format!("{}::{}", qual_path_ty.to_string(), path_expr_seg.to_string())) +} + +#[cfg(test)] +mod test { + + use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn test_tuple_struct_basic() { + check_assist( + generate_delegate_trait, + r#" +struct Base; +struct S(B$0ase); +trait Trait {} +impl Trait for Base {} +"#, + r#" +struct Base; +struct S(Base); + +impl Trait for S {} +trait Trait {} +impl Trait for Base {} +"#, + ); + } + + #[test] + fn test_struct_struct_basic() { + check_assist( + generate_delegate_trait, + r#" +struct Base; +struct S { + ba$0se : Base +} +trait Trait {} +impl Trait for Base {} +"#, + r#" +struct Base; +struct S { + base : Base +} + +impl Trait for S {} +trait Trait {} +impl Trait for Base {} +"#, + ) + } + + // Structs need to be by def populated with fields + // However user can invoke this assist while still editing + // We therefore assert its non-applicability + #[test] + fn test_yet_empty_struct() { + check_assist_not_applicable( + generate_delegate_trait, + r#" +struct Base; +struct S { + $0 +} + +impl Trait for S {} +trait Trait {} +impl Trait for Base {} +"#, + ) + } + + #[test] + fn test_yet_unspecified_field_type() { + check_assist_not_applicable( + generate_delegate_trait, + r#" +struct Base; +struct S { + ab$0c +} + +impl Trait for S {} +trait Trait {} +impl Trait for Base {} +"#, + ); + } + + #[test] + fn test_unsafe_trait() { + check_assist( + generate_delegate_trait, + r#" +struct Base; +struct S { + ba$0se : Base +} +unsafe trait Trait {} +unsafe impl Trait for Base {} +"#, + r#" +struct Base; +struct S { + base : Base +} + +unsafe impl Trait for S {} +unsafe trait Trait {} +unsafe impl Trait for Base {} +"#, + ); + } + + #[test] + fn test_unsafe_trait_with_unsafe_fn() { + check_assist( + generate_delegate_trait, + r#" +struct Base; +struct S { + ba$0se: Base, +} + +unsafe trait Trait { + unsafe fn a_func(); + unsafe fn a_method(&self); +} +unsafe impl Trait for Base { + unsafe fn a_func() {} + unsafe fn a_method(&self) {} +} +"#, + r#" +struct Base; +struct S { + base: Base, +} + +unsafe impl Trait for S { + unsafe fn a_func() { + ::a_func() + } + + unsafe fn a_method(&self) { + ::a_method( &self.base ) + } +} + +unsafe trait Trait { + unsafe fn a_func(); + unsafe fn a_method(&self); +} +unsafe impl Trait for Base { + unsafe fn a_func() {} + unsafe fn a_method(&self) {} +} +"#, + ); + } + + #[test] + fn test_struct_with_where_clause() { + check_assist( + generate_delegate_trait, + r#" +trait AnotherTrait {} +struct S +where + T: AnotherTrait, +{ + b$0 : T, +}"#, + r#" +trait AnotherTrait {} +struct S +where + T: AnotherTrait, +{ + b : T, +} + +impl AnotherTrait for S +where + T: AnotherTrait, +{}"#, + ); + } + + #[test] + fn test_complex_without_where() { + check_assist( + generate_delegate_trait, + r#" +trait Trait<'a, T, const C: usize> { + type AssocType; + const AssocConst: usize; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + field$0: Base +} + +impl<'a, T, const C: usize> Trait<'a, T, C> for Base { + type AssocType = (); + const AssocConst: usize = 0; + fn assoc_fn(p: ()) {} + fn assoc_method(&self, p: ()) {} +} +"#, + r#" +trait Trait<'a, T, const C: usize> { + type AssocType; + const AssocConst: usize; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + field: Base +} + +impl<'a, T, const C: usize> Trait<'a, T, C> for S { + type AssocType = >::AssocType; + + const AssocConst: usize = >::AssocConst; + + fn assoc_fn(p: ()) { + >::assoc_fn(p) + } + + fn assoc_method(&self, p: ()) { + >::assoc_method( &self.field , p) + } +} + +impl<'a, T, const C: usize> Trait<'a, T, C> for Base { + type AssocType = (); + const AssocConst: usize = 0; + fn assoc_fn(p: ()) {} + fn assoc_method(&self, p: ()) {} +} +"#, + ); + } + + #[test] + fn test_complex_two() { + check_assist( + generate_delegate_trait, + r" +trait AnotherTrait {} + +trait Trait<'a, T, const C: usize> { + type AssocType; + const AssocConst: usize; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + fi$0eld: Base, +} + +impl<'b, C, const D: usize> Trait<'b, C, D> for Base +where + C: AnotherTrait, +{ + type AssocType = (); + const AssocConst: usize = 0; + fn assoc_fn(p: ()) {} + fn assoc_method(&self, p: ()) {} +}", + r#" +trait AnotherTrait {} + +trait Trait<'a, T, const C: usize> { + type AssocType; + const AssocConst: usize; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + field: Base, +} + +impl<'b, C, const D: usize> Trait<'b, C, D> for S +where + C: AnotherTrait, +{ + type AssocType = >::AssocType; + + const AssocConst: usize = >::AssocConst; + + fn assoc_fn(p: ()) { + >::assoc_fn(p) + } + + fn assoc_method(&self, p: ()) { + >::assoc_method( &self.field , p) + } +} + +impl<'b, C, const D: usize> Trait<'b, C, D> for Base +where + C: AnotherTrait, +{ + type AssocType = (); + const AssocConst: usize = 0; + fn assoc_fn(p: ()) {} + fn assoc_method(&self, p: ()) {} +}"#, + ) + } + + #[test] + fn test_complex_three() { + check_assist( + generate_delegate_trait, + r#" +trait AnotherTrait {} +trait YetAnotherTrait {} + +struct StructImplsAll(); +impl AnotherTrait for StructImplsAll {} +impl YetAnotherTrait for StructImplsAll {} + +trait Trait<'a, T, const C: usize> { + type A; + const ASSOC_CONST: usize = C; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + fi$0eld: Base, +} + +impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base +where + A: AnotherTrait, +{ + type A = i32; + + const ASSOC_CONST: usize = B; + + fn assoc_fn(p: ()) {} + + fn assoc_method(&self, p: ()) {} +} +"#, + r#" +trait AnotherTrait {} +trait YetAnotherTrait {} + +struct StructImplsAll(); +impl AnotherTrait for StructImplsAll {} +impl YetAnotherTrait for StructImplsAll {} + +trait Trait<'a, T, const C: usize> { + type A; + const ASSOC_CONST: usize = C; + fn assoc_fn(p: ()); + fn assoc_method(&self, p: ()); +} + +struct Base; +struct S { + field: Base, +} + +impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for S +where + A: AnotherTrait, +{ + type A = >::A; + + const ASSOC_CONST: usize = >::ASSOC_CONST; + + fn assoc_fn(p: ()) { + >::assoc_fn(p) + } + + fn assoc_method(&self, p: ()) { + >::assoc_method( &self.field , p) + } +} + +impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base +where + A: AnotherTrait, +{ + type A = i32; + + const ASSOC_CONST: usize = B; + + fn assoc_fn(p: ()) {} + + fn assoc_method(&self, p: ()) {} +} +"#, + ) + } + + #[test] + fn test_type_bound() { + check_assist( + generate_delegate_trait, + r#" +trait AnotherTrait {} +struct S +where + T: AnotherTrait, +{ + b$0: T, +}"#, + r#" +trait AnotherTrait {} +struct S +where + T: AnotherTrait, +{ + b: T, +} + +impl AnotherTrait for S +where + T: AnotherTrait, +{}"#, + ); + } + + #[test] + fn test_docstring_example() { + check_assist( + generate_delegate_trait, + r#" +trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; +} +struct A; +impl SomeTrait for A { + type T = u32; + fn fn_(arg: u32) -> u32 { + 42 + } + fn method_(&mut self) -> bool { + false + } +} +struct B { + a$0: A, +} +"#, + r#" +trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; +} +struct A; +impl SomeTrait for A { + type T = u32; + fn fn_(arg: u32) -> u32 { + 42 + } + fn method_(&mut self) -> bool { + false + } +} +struct B { + a: A, +} + +impl SomeTrait for B { + type T = ::T; + + fn fn_(arg: u32) -> u32 { + ::fn_(arg) + } + + fn method_(&mut self) -> bool { + ::method_( &mut self.a ) + } +} +"#, + ); + } + + #[test] + fn import_from_other_mod() { + check_assist( + generate_delegate_trait, + r#" +mod some_module { + pub trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; + } + pub struct A; + impl SomeTrait for A { + type T = u32; + + fn fn_(arg: u32) -> u32 { + 42 + } + + fn method_(&mut self) -> bool { + false + } + } +} + +struct B { + a$0: some_module::A, +}"#, + r#" +mod some_module { + pub trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; + } + pub struct A; + impl SomeTrait for A { + type T = u32; + + fn fn_(arg: u32) -> u32 { + 42 + } + + fn method_(&mut self) -> bool { + false + } + } +} + +struct B { + a: some_module::A, +} + +impl some_module::SomeTrait for B { + type T = ::T; + + fn fn_(arg: u32) -> u32 { + ::fn_(arg) + } + + fn method_(&mut self) -> bool { + ::method_( &mut self.a ) + } +}"#, + ) + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 111753bf30..dc0a69971e 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -145,6 +145,7 @@ mod handlers { mod generate_constant; mod generate_default_from_enum_variant; mod generate_default_from_new; + mod generate_delegate_trait; mod generate_deref; mod generate_derive; mod generate_documentation_template; @@ -251,6 +252,7 @@ mod handlers { generate_constant::generate_constant, generate_default_from_enum_variant::generate_default_from_enum_variant, generate_default_from_new::generate_default_from_new, + generate_delegate_trait::generate_delegate_trait, generate_derive::generate_derive, generate_documentation_template::generate_documentation_template, generate_documentation_template::generate_doc_example, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index c097e07398..c6c70cc64a 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1015,6 +1015,69 @@ impl Person { ) } +#[test] +fn doctest_generate_delegate_trait() { + check_doc_test( + "generate_delegate_trait", + r#####" +trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; +} +struct A; +impl SomeTrait for A { + type T = u32; + + fn fn_(arg: u32) -> u32 { + 42 + } + + fn method_(&mut self) -> bool { + false + } +} +struct B { + a$0: A, +} +"#####, + r#####" +trait SomeTrait { + type T; + fn fn_(arg: u32) -> u32; + fn method_(&mut self) -> bool; +} +struct A; +impl SomeTrait for A { + type T = u32; + + fn fn_(arg: u32) -> u32 { + 42 + } + + fn method_(&mut self) -> bool { + false + } +} +struct B { + a: A, +} + +impl SomeTrait for B { + type T = ::T; + + fn fn_(arg: u32) -> u32 { + ::fn_(arg) + } + + fn method_(&mut self) -> bool { + ::method_( &mut self.a ) + } +} +"#####, + ) +} + #[test] fn doctest_generate_deref() { check_doc_test( From 59cc783a3be26dae2143066914d8600501229b1f Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Fri, 2 Jun 2023 12:41:05 +0200 Subject: [PATCH 007/201] Correct errors produced by CI --- crates/ide-assists/src/handlers/generate_delegate_trait.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index ce8f4256c0..19fb9fa390 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -106,7 +106,7 @@ struct Field { } impl Field { - pub fn new( + pub(crate) fn new( ctx: &AssistContext<'_>, f: Either, ) -> Option { @@ -208,12 +208,12 @@ struct Struct { } impl Struct { - pub fn new(s: ast::Struct) -> Option { + pub(crate) fn new(s: ast::Struct) -> Option { let name = s.name()?; Some(Struct { name, strukt: s }) } - pub fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) { + pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) { let db = ctx.db(); for delegee in &field.impls { // FIXME : We can omit already implemented impl_traits From 19a9de72f623c60370d0222326e4fba40ac3dc6b Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sat, 17 Jun 2023 13:17:58 +0200 Subject: [PATCH 008/201] Version 2 --- .../src/handlers/generate_delegate_trait.rs | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 19fb9fa390..4d05dbfcf5 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -137,7 +137,7 @@ impl Field { if let Some(tp) = type_param { for tb in tp.trait_bounds(db) { - impls.push(Delegee::Bound(BoundCase { 0: tb })); + impls.push(Delegee::Bound(BoundCase(tb))); } }; @@ -145,12 +145,10 @@ impl Field { match imp.trait_(db) { Some(tr) => { if tr.is_visible_from(db, module) { - impls.push(Delegee::Impls(ImplCase { 0: tr, 1: imp })) + impls.push(Delegee::Impls(ImplCase(tr, imp))) } } - None => { - continue; - } + None => (), } } @@ -171,32 +169,19 @@ enum Delegee { struct BoundCase(hir::Trait); struct ImplCase(hir::Trait, hir::Impl); -/// When we list traits we can implement for the enclosing struct -/// we use the absolute path of a trait. This trait consists of a single -/// method that produces this path. -trait Signatured { - fn signature(&self, db: &dyn HirDatabase) -> String; -} - -impl Signatured for Delegee { +impl Delegee { fn signature(&self, db: &dyn HirDatabase) -> String { let mut s = String::new(); - let trait_: hir::Trait; - match self { - Delegee::Bound(b) => trait_ = b.0, - Delegee::Impls(i) => trait_ = i.0, - }; + let (Delegee::Bound(BoundCase(it)) | Delegee::Impls(ImplCase(it, _))) = self; - for m in trait_.module(db).path_to_root(db).iter().rev() { + for m in it.module(db).path_to_root(db).iter().rev() { if let Some(name) = m.name(db) { s.push_str(&format!("{}::", name.to_smol_str())); - } else { - continue; } } - s.push_str(&trait_.name(db).to_smol_str()); + s.push_str(&it.name(db).to_smol_str()); s } } @@ -231,7 +216,7 @@ impl Struct { let delegate = generate_impl(ctx, self, &field.ty, &field.name, delegee); acc.add_group( - &GroupLabel("Generate delegate traits...".to_owned()), + &GroupLabel("Delegate trait impl for field...".to_owned()), AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate), format!("Generate delegate impl `{}` for `{}`", signature, field.name), field.range, From 4ed119766282b2e758a88ac29daf488d718f730c Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sat, 17 Jun 2023 21:27:52 +0200 Subject: [PATCH 009/201] Version 3 Remove scope_for_def calls as the definition have been removed entirely. As a result of this change the problem with false path resolutions has been solved. --- crates/ide-assists/src/handlers/generate_delegate_trait.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 4d05dbfcf5..185f47184d 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -294,7 +294,7 @@ fn generate_impl( }; let target = ctx.sema.scope(strukt.strukt.syntax()).unwrap(); - let source = ctx.sema.scope_for_def(delegee.0); + let source = ctx.sema.scope(source.syntax()).unwrap(); let transform = PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); @@ -342,7 +342,7 @@ fn generate_impl( }); let target = ctx.sema.scope(strukt.strukt.syntax()).unwrap(); - let source = ctx.sema.scope_for_def(delegee.0); + let source = ctx.sema.scope(source.syntax()).unwrap(); let transform = PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone()); From 424ef7780914c2d9ce2fc610812e965ed2d33e83 Mon Sep 17 00:00:00 2001 From: Alex Kladov Date: Sat, 17 Jun 2023 22:23:25 +0100 Subject: [PATCH 010/201] internal: remove spurious regex dependency - replace tokio's env-filter with a smaller&simpler targets filter - reshuffle logging infra a bit to make sure there's only a single place where we read environmental variables - use anyhow::Result in rust-analyzer binary --- Cargo.lock | 37 ----- crates/hir-ty/Cargo.toml | 1 - crates/hir-ty/src/tests.rs | 5 +- crates/rust-analyzer/Cargo.toml | 1 - crates/rust-analyzer/src/bin/logger.rs | 88 +++++------- crates/rust-analyzer/src/bin/main.rs | 63 ++++----- crates/rust-analyzer/src/dispatch.rs | 26 ++-- crates/rust-analyzer/src/from_proto.rs | 27 ++-- crates/rust-analyzer/src/global_state.rs | 7 +- .../src/handlers/notification.rs | 26 ++-- crates/rust-analyzer/src/handlers/request.rs | 127 +++++++++--------- crates/rust-analyzer/src/lib.rs | 13 +- crates/rust-analyzer/src/main_loop.rs | 11 +- .../rust-analyzer/tests/slow-tests/support.rs | 5 +- 14 files changed, 196 insertions(+), 241 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 50c81ca279..1fe451c2f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -967,15 +967,6 @@ dependencies = [ "url", ] -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - [[package]] name = "mbe" version = "0.0.0" @@ -1438,30 +1429,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "regex" -version = "1.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "rowan" version = "0.15.11" @@ -1953,12 +1920,8 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "matchers", - "once_cell", - "regex", "sharded-slab", "thread_local", - "tracing", "tracing-core", "tracing-log", ] diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index c8bea34507..247e6466d8 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -47,7 +47,6 @@ limit.workspace = true expect-test = "1.4.0" tracing = "0.1.35" tracing-subscriber = { version = "0.3.16", default-features = false, features = [ - "env-filter", "registry", ] } tracing-tree = "0.2.1" diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 8571412800..8e5514353d 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -30,7 +30,7 @@ use syntax::{ ast::{self, AstNode, HasName}, SyntaxNode, }; -use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; +use tracing_subscriber::{layer::SubscriberExt, Registry}; use tracing_tree::HierarchicalLayer; use triomphe::Arc; @@ -52,7 +52,8 @@ fn setup_tracing() -> Option { return None; } - let filter = EnvFilter::from_env("CHALK_DEBUG"); + let filter: tracing_subscriber::filter::Targets = + env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default(); let layer = HierarchicalLayer::default() .with_indent_lines(true) .with_ansi(false) diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 5b72d57560..dc8962064b 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -37,7 +37,6 @@ mimalloc = { version = "0.1.30", default-features = false, optional = true } lsp-server = { version = "0.7.0", path = "../../lib/lsp-server" } tracing = "0.1.35" tracing-subscriber = { version = "0.3.16", default-features = false, features = [ - "env-filter", "registry", "fmt", "tracing-log", diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs index 8caadecd85..867907dca3 100644 --- a/crates/rust-analyzer/src/bin/logger.rs +++ b/crates/rust-analyzer/src/bin/logger.rs @@ -8,10 +8,11 @@ use std::{ sync::Arc, }; -use rust_analyzer::Result; +use anyhow::Context; use tracing::{level_filters::LevelFilter, Event, Subscriber}; use tracing_log::NormalizeEvent; use tracing_subscriber::{ + filter::Targets, fmt::{ format::Writer, writer::BoxMakeWriter, FmtContext, FormatEvent, FormatFields, FormattedFields, MakeWriter, @@ -19,13 +20,14 @@ use tracing_subscriber::{ layer::SubscriberExt, registry::LookupSpan, util::SubscriberInitExt, - EnvFilter, Registry, + Registry, }; use tracing_tree::HierarchicalLayer; -pub(crate) struct Logger { - filter: EnvFilter, - file: Option, +pub(crate) struct LoggerConfig { + pub(crate) log_file: Option, + pub(crate) filter: String, + pub(crate) chalk_filter: Option, } struct MakeWriterStderr; @@ -38,62 +40,42 @@ impl<'a> MakeWriter<'a> for MakeWriterStderr { } } -impl Logger { - pub(crate) fn new(file: Option, filter: Option<&str>) -> Logger { - let filter = filter.map_or(EnvFilter::default(), EnvFilter::new); +impl LoggerConfig { + pub(crate) fn init(self) -> anyhow::Result<()> { + let mut filter: Targets = self + .filter + .parse() + .with_context(|| format!("invalid log filter: `{}`", self.filter))?; - Logger { filter, file } - } + let mut chalk_layer = None; + if let Some(chalk_filter) = self.chalk_filter { + let level: LevelFilter = + chalk_filter.parse().with_context(|| "invalid chalk log filter")?; + chalk_layer = Some( + HierarchicalLayer::default() + .with_indent_lines(true) + .with_ansi(false) + .with_indent_amount(2) + .with_writer(io::stderr), + ); + filter = filter + .with_target("chalk_solve", level) + .with_target("chalk_ir", level) + .with_target("chalk_recursive", level); + }; - pub(crate) fn install(self) -> Result<()> { - // The meaning of CHALK_DEBUG I suspected is to tell chalk crates - // (i.e. chalk-solve, chalk-ir, chalk-recursive) how to filter tracing - // logs. But now we can only have just one filter, which means we have to - // merge chalk filter to our main filter (from RA_LOG env). - // - // The acceptable syntax of CHALK_DEBUG is `target[span{field=value}]=level`. - // As the value should only affect chalk crates, we'd better manually - // specify the target. And for simplicity, CHALK_DEBUG only accept the value - // that specify level. - let chalk_level_dir = std::env::var("CHALK_DEBUG") - .map(|val| { - val.parse::().expect( - "invalid CHALK_DEBUG value, expect right log level (like debug or trace)", - ) - }) - .ok(); - - let chalk_layer = HierarchicalLayer::default() - .with_indent_lines(true) - .with_ansi(false) - .with_indent_amount(2) - .with_writer(io::stderr); - - let writer = match self.file { + let writer = match self.log_file { Some(file) => BoxMakeWriter::new(Arc::new(file)), None => BoxMakeWriter::new(io::stderr), }; let ra_fmt_layer = tracing_subscriber::fmt::layer().event_format(LoggerFormatter).with_writer(writer); - match chalk_level_dir { - Some(val) => { - Registry::default() - .with( - self.filter - .add_directive(format!("chalk_solve={val}").parse()?) - .add_directive(format!("chalk_ir={val}").parse()?) - .add_directive(format!("chalk_recursive={val}").parse()?), - ) - .with(ra_fmt_layer) - .with(chalk_layer) - .init(); - } - None => { - Registry::default().with(self.filter).with(ra_fmt_layer).init(); - } - }; - + let registry = Registry::default().with(filter).with(ra_fmt_layer); + match chalk_layer { + Some(chalk_layer) => registry.with(chalk_layer).init(), + None => registry.init(), + } Ok(()) } } diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 91911dd180..118aad585c 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -7,14 +7,11 @@ mod logger; mod rustc_wrapper; -use std::{ - env, fs, - path::{Path, PathBuf}, - process, -}; +use std::{env, fs, path::PathBuf, process}; +use anyhow::Context; use lsp_server::Connection; -use rust_analyzer::{cli::flags, config::Config, from_json, Result}; +use rust_analyzer::{cli::flags, config::Config, from_json}; use vfs::AbsPathBuf; #[cfg(all(feature = "mimalloc"))] @@ -25,7 +22,7 @@ static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -fn main() { +fn main() -> anyhow::Result<()> { if std::env::var("RA_RUSTC_WRAPPER").is_ok() { let mut args = std::env::args_os(); let _me = args.next().unwrap(); @@ -41,14 +38,7 @@ fn main() { } let flags = flags::RustAnalyzer::from_env_or_exit(); - if let Err(err) = try_main(flags) { - tracing::error!("Unexpected error: {}", err); - eprintln!("{err}"); - process::exit(101); - } -} -fn try_main(flags: flags::RustAnalyzer) -> Result<()> { #[cfg(debug_assertions)] if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() { #[allow(unused_mut)] @@ -58,14 +48,8 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { } } - let mut log_file = flags.log_file.as_deref(); + setup_logging(flags.log_file.clone())?; - let env_log_file = env::var("RA_LOG_FILE").ok(); - if let Some(env_log_file) = env_log_file.as_deref() { - log_file = Some(Path::new(env_log_file)); - } - - setup_logging(log_file)?; let verbosity = flags.verbosity(); match flags.subcommand { @@ -102,7 +86,7 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { Ok(()) } -fn setup_logging(log_file: Option<&Path>) -> Result<()> { +fn setup_logging(log_file_flag: Option) -> anyhow::Result<()> { if cfg!(windows) { // This is required so that windows finds our pdb that is placed right beside the exe. // By default it doesn't look at the folder the exe resides in, only in the current working @@ -115,23 +99,42 @@ fn setup_logging(log_file: Option<&Path>) -> Result<()> { } } } + if env::var("RUST_BACKTRACE").is_err() { env::set_var("RUST_BACKTRACE", "short"); } + let log_file = env::var("RA_LOG_FILE").ok().map(PathBuf::from).or(log_file_flag); let log_file = match log_file { Some(path) => { if let Some(parent) = path.parent() { let _ = fs::create_dir_all(parent); } - Some(fs::File::create(path)?) + Some( + fs::File::create(&path) + .with_context(|| format!("can't create log file at {}", path.display()))?, + ) } None => None, }; - let filter = env::var("RA_LOG").ok(); - // deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually useful - // information in there for debugging - logger::Logger::new(log_file, filter.as_deref().or(Some("error"))).install()?; + + logger::LoggerConfig { + log_file, + // Deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually + // useful information in there for debugging. + filter: env::var("RA_LOG").ok().unwrap_or_else(|| "error".to_string()), + // The meaning of CHALK_DEBUG I suspected is to tell chalk crates + // (i.e. chalk-solve, chalk-ir, chalk-recursive) how to filter tracing + // logs. But now we can only have just one filter, which means we have to + // merge chalk filter to our main filter (from RA_LOG env). + // + // The acceptable syntax of CHALK_DEBUG is `target[span{field=value}]=level`. + // As the value should only affect chalk crates, we'd better manually + // specify the target. And for simplicity, CHALK_DEBUG only accept the value + // that specify level. + chalk_filter: env::var("CHALK_DEBUG").ok(), + } + .init()?; profile::init(); @@ -146,8 +149,8 @@ const STACK_SIZE: usize = 1024 * 1024 * 8; fn with_extra_thread( thread_name: impl Into, thread_intent: stdx::thread::ThreadIntent, - f: impl FnOnce() -> Result<()> + Send + 'static, -) -> Result<()> { + f: impl FnOnce() -> anyhow::Result<()> + Send + 'static, +) -> anyhow::Result<()> { let handle = stdx::thread::Builder::new(thread_intent) .name(thread_name.into()) .stack_size(STACK_SIZE) @@ -158,7 +161,7 @@ fn with_extra_thread( Ok(()) } -fn run_server() -> Result<()> { +fn run_server() -> anyhow::Result<()> { tracing::info!("server version {} will start", rust_analyzer::version()); let (connection, io_threads) = Connection::stdio(); diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 4e57c6eb65..9a876f4b97 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -10,7 +10,7 @@ use crate::{ global_state::{GlobalState, GlobalStateSnapshot}, main_loop::Task, version::version, - LspError, Result, + LspError, }; /// A visitor for routing a raw JSON request to an appropriate handler function. @@ -38,7 +38,7 @@ impl<'a> RequestDispatcher<'a> { /// guarded by `catch_unwind`, so, please, don't make bugs :-) pub(crate) fn on_sync_mut( &mut self, - f: fn(&mut GlobalState, R::Params) -> Result, + f: fn(&mut GlobalState, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request, @@ -63,7 +63,7 @@ impl<'a> RequestDispatcher<'a> { /// Dispatches the request onto the current thread. pub(crate) fn on_sync( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request, @@ -92,7 +92,7 @@ impl<'a> RequestDispatcher<'a> { /// without retrying it if it panics. pub(crate) fn on_no_retry( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -128,7 +128,7 @@ impl<'a> RequestDispatcher<'a> { /// Dispatches a non-latency-sensitive request onto the thread pool. pub(crate) fn on( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -141,7 +141,7 @@ impl<'a> RequestDispatcher<'a> { /// Dispatches a latency-sensitive request onto the thread pool. pub(crate) fn on_latency_sensitive( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -156,7 +156,7 @@ impl<'a> RequestDispatcher<'a> { /// We can't run this on the main thread though as we invoke rustfmt which may take arbitrary time to complete! pub(crate) fn on_fmt_thread( &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -181,7 +181,7 @@ impl<'a> RequestDispatcher<'a> { fn on_with_thread_intent( &mut self, intent: ThreadIntent, - f: fn(GlobalStateSnapshot, R::Params) -> Result, + f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where R: lsp_types::request::Request + 'static, @@ -245,7 +245,7 @@ impl<'a> RequestDispatcher<'a> { fn thread_result_to_response( id: lsp_server::RequestId, - result: thread::Result>, + result: thread::Result>, ) -> Result where R: lsp_types::request::Request, @@ -277,7 +277,7 @@ where fn result_to_response( id: lsp_server::RequestId, - result: Result, + result: anyhow::Result, ) -> Result where R: lsp_types::request::Request, @@ -289,7 +289,7 @@ where Err(e) => match e.downcast::() { Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), Err(e) => match e.downcast::() { - Ok(cancelled) => return Err(*cancelled), + Ok(cancelled) => return Err(cancelled), Err(e) => lsp_server::Response::new_err( id, lsp_server::ErrorCode::InternalError as i32, @@ -309,8 +309,8 @@ pub(crate) struct NotificationDispatcher<'a> { impl<'a> NotificationDispatcher<'a> { pub(crate) fn on_sync_mut( &mut self, - f: fn(&mut GlobalState, N::Params) -> Result<()>, - ) -> Result<&mut Self> + f: fn(&mut GlobalState, N::Params) -> anyhow::Result<()>, + ) -> anyhow::Result<&mut Self> where N: lsp_types::notification::Notification, N::Params: DeserializeOwned + Send, diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index cd74a5500d..c247e1bb22 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs @@ -14,19 +14,21 @@ use crate::{ line_index::{LineIndex, PositionEncoding}, lsp_ext, lsp_utils::invalid_params_error, - Result, }; -pub(crate) fn abs_path(url: &lsp_types::Url) -> Result { - let path = url.to_file_path().map_err(|()| "url is not a file")?; +pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result { + let path = url.to_file_path().map_err(|()| anyhow::format_err!("url is not a file"))?; Ok(AbsPathBuf::try_from(path).unwrap()) } -pub(crate) fn vfs_path(url: &lsp_types::Url) -> Result { +pub(crate) fn vfs_path(url: &lsp_types::Url) -> anyhow::Result { abs_path(url).map(vfs::VfsPath::from) } -pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> Result { +pub(crate) fn offset( + line_index: &LineIndex, + position: lsp_types::Position, +) -> anyhow::Result { let line_col = match line_index.encoding { PositionEncoding::Utf8 => LineCol { line: position.line, col: position.character }, PositionEncoding::Wide(enc) => { @@ -42,7 +44,10 @@ pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> R Ok(text_size) } -pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Result { +pub(crate) fn text_range( + line_index: &LineIndex, + range: lsp_types::Range, +) -> anyhow::Result { let start = offset(line_index, range.start)?; let end = offset(line_index, range.end)?; match end < start { @@ -51,14 +56,14 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Res } } -pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result { +pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> anyhow::Result { snap.url_to_file_id(url) } pub(crate) fn file_position( snap: &GlobalStateSnapshot, tdpp: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let file_id = file_id(snap, &tdpp.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; let offset = offset(&line_index, tdpp.position)?; @@ -69,7 +74,7 @@ pub(crate) fn file_range( snap: &GlobalStateSnapshot, text_document_identifier: lsp_types::TextDocumentIdentifier, range: lsp_types::Range, -) -> Result { +) -> anyhow::Result { file_range_uri(snap, &text_document_identifier.uri, range) } @@ -77,7 +82,7 @@ pub(crate) fn file_range_uri( snap: &GlobalStateSnapshot, document: &lsp_types::Url, range: lsp_types::Range, -) -> Result { +) -> anyhow::Result { let file_id = file_id(snap, document)?; let line_index = snap.file_line_index(file_id)?; let range = text_range(&line_index, range)?; @@ -101,7 +106,7 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option pub(crate) fn annotation( snap: &GlobalStateSnapshot, code_lens: lsp_types::CodeLens, -) -> Result> { +) -> anyhow::Result> { let data = code_lens.data.ok_or_else(|| invalid_params_error("code lens without data".to_string()))?; let resolve = from_json::("CodeLensResolveData", &data)?; diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index d5b0e3a570..52512b2197 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -30,7 +30,6 @@ use crate::{ reload::{self, SourceRootConfig}, task_pool::TaskPool, to_proto::url_from_abs_path, - Result, }; // Enforces drop order @@ -422,7 +421,7 @@ impl Drop for GlobalState { } impl GlobalStateSnapshot { - pub(crate) fn url_to_file_id(&self, url: &Url) -> Result { + pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result { url_to_file_id(&self.vfs.read().0, url) } @@ -481,8 +480,8 @@ pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { url_from_abs_path(path) } -pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result { +pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result { let path = from_proto::vfs_path(url)?; - let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {path}"))?; + let res = vfs.file_id(&path).ok_or_else(|| anyhow::format_err!("file not found: {path}"))?; Ok(res) } diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index ae1dc23153..3f94f2720c 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -14,10 +14,10 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath}; use crate::{ config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams, - lsp_utils::apply_document_changes, mem_docs::DocumentData, reload, Result, + lsp_utils::apply_document_changes, mem_docs::DocumentData, reload, }; -pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> Result<()> { +pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> { let id: lsp_server::RequestId = match params.id { lsp_types::NumberOrString::Number(id) => id.into(), lsp_types::NumberOrString::String(id) => id.into(), @@ -29,7 +29,7 @@ pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> Re pub(crate) fn handle_work_done_progress_cancel( state: &mut GlobalState, params: WorkDoneProgressCancelParams, -) -> Result<()> { +) -> anyhow::Result<()> { if let lsp_types::NumberOrString::String(s) = ¶ms.token { if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") { if let Ok(id) = u32::from_str_radix(id, 10) { @@ -49,7 +49,7 @@ pub(crate) fn handle_work_done_progress_cancel( pub(crate) fn handle_did_open_text_document( state: &mut GlobalState, params: DidOpenTextDocumentParams, -) -> Result<()> { +) -> anyhow::Result<()> { let _p = profile::span("handle_did_open_text_document"); if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { @@ -68,7 +68,7 @@ pub(crate) fn handle_did_open_text_document( pub(crate) fn handle_did_change_text_document( state: &mut GlobalState, params: DidChangeTextDocumentParams, -) -> Result<()> { +) -> anyhow::Result<()> { let _p = profile::span("handle_did_change_text_document"); if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { @@ -100,7 +100,7 @@ pub(crate) fn handle_did_change_text_document( pub(crate) fn handle_did_close_text_document( state: &mut GlobalState, params: DidCloseTextDocumentParams, -) -> Result<()> { +) -> anyhow::Result<()> { let _p = profile::span("handle_did_close_text_document"); if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { @@ -120,7 +120,7 @@ pub(crate) fn handle_did_close_text_document( pub(crate) fn handle_did_save_text_document( state: &mut GlobalState, params: DidSaveTextDocumentParams, -) -> Result<()> { +) -> anyhow::Result<()> { if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { // Re-fetch workspaces if a workspace related file has changed if let Some(abs_path) = vfs_path.as_path() { @@ -146,7 +146,7 @@ pub(crate) fn handle_did_save_text_document( pub(crate) fn handle_did_change_configuration( state: &mut GlobalState, _params: DidChangeConfigurationParams, -) -> Result<()> { +) -> anyhow::Result<()> { // As stated in https://github.com/microsoft/language-server-protocol/issues/676, // this notification's parameters should be ignored and the actual config queried separately. state.send_request::( @@ -186,7 +186,7 @@ pub(crate) fn handle_did_change_configuration( pub(crate) fn handle_did_change_workspace_folders( state: &mut GlobalState, params: DidChangeWorkspaceFoldersParams, -) -> Result<()> { +) -> anyhow::Result<()> { let config = Arc::make_mut(&mut state.config); for workspace in params.event.removed { @@ -214,7 +214,7 @@ pub(crate) fn handle_did_change_workspace_folders( pub(crate) fn handle_did_change_watched_files( state: &mut GlobalState, params: DidChangeWatchedFilesParams, -) -> Result<()> { +) -> anyhow::Result<()> { for change in params.changes { if let Ok(path) = from_proto::abs_path(&change.uri) { state.loader.handle.invalidate(path); @@ -302,13 +302,13 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } } -pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { +pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { let _p = profile::span("handle_stop_flycheck"); state.flycheck.iter().for_each(|flycheck| flycheck.cancel()); Ok(()) } -pub(crate) fn handle_clear_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { +pub(crate) fn handle_clear_flycheck(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { let _p = profile::span("handle_clear_flycheck"); state.diagnostics.clear_check_all(); Ok(()) @@ -317,7 +317,7 @@ pub(crate) fn handle_clear_flycheck(state: &mut GlobalState, _: ()) -> Result<() pub(crate) fn handle_run_flycheck( state: &mut GlobalState, params: RunFlycheckParams, -) -> Result<()> { +) -> anyhow::Result<()> { let _p = profile::span("handle_run_flycheck"); if let Some(text_document) = params.text_document { if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) { diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index a6a72552d5..afd82fc061 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -43,10 +43,10 @@ use crate::{ FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams, }, lsp_utils::{all_edits_are_disjoint, invalid_params_error}, - to_proto, LspError, Result, + to_proto, LspError, }; -pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> { +pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { // FIXME: use `Arc::from_iter` when it becomes available state.proc_macro_clients = Arc::from(Vec::new()); state.proc_macro_changed = false; @@ -55,7 +55,7 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result< Ok(()) } -pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> Result<()> { +pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { // FIXME: use `Arc::from_iter` when it becomes available state.proc_macro_clients = Arc::from(Vec::new()); state.proc_macro_changed = false; @@ -67,7 +67,7 @@ pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> Resu pub(crate) fn handle_analyzer_status( snap: GlobalStateSnapshot, params: lsp_ext::AnalyzerStatusParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_analyzer_status"); let mut buf = String::new(); @@ -112,7 +112,7 @@ pub(crate) fn handle_analyzer_status( Ok(buf) } -pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result { +pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> anyhow::Result { let _p = profile::span("handle_memory_usage"); let mem = state.analysis_host.per_query_memory_usage(); @@ -125,7 +125,7 @@ pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result Result<()> { +pub(crate) fn handle_shuffle_crate_graph(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { state.analysis_host.shuffle_crate_graph(); Ok(()) } @@ -133,7 +133,7 @@ pub(crate) fn handle_shuffle_crate_graph(state: &mut GlobalState, _: ()) -> Resu pub(crate) fn handle_syntax_tree( snap: GlobalStateSnapshot, params: lsp_ext::SyntaxTreeParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_syntax_tree"); let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(id)?; @@ -145,7 +145,7 @@ pub(crate) fn handle_syntax_tree( pub(crate) fn handle_view_hir( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_view_hir"); let position = from_proto::file_position(&snap, params)?; let res = snap.analysis.view_hir(position)?; @@ -155,7 +155,7 @@ pub(crate) fn handle_view_hir( pub(crate) fn handle_view_mir( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_view_mir"); let position = from_proto::file_position(&snap, params)?; let res = snap.analysis.view_mir(position)?; @@ -165,7 +165,7 @@ pub(crate) fn handle_view_mir( pub(crate) fn handle_interpret_function( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_interpret_function"); let position = from_proto::file_position(&snap, params)?; let res = snap.analysis.interpret_function(position)?; @@ -175,7 +175,7 @@ pub(crate) fn handle_interpret_function( pub(crate) fn handle_view_file_text( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentIdentifier, -) -> Result { +) -> anyhow::Result { let file_id = from_proto::file_id(&snap, ¶ms.uri)?; Ok(snap.analysis.file_text(file_id)?.to_string()) } @@ -183,7 +183,7 @@ pub(crate) fn handle_view_file_text( pub(crate) fn handle_view_item_tree( snap: GlobalStateSnapshot, params: lsp_ext::ViewItemTreeParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_view_item_tree"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let res = snap.analysis.view_item_tree(file_id)?; @@ -193,16 +193,16 @@ pub(crate) fn handle_view_item_tree( pub(crate) fn handle_view_crate_graph( snap: GlobalStateSnapshot, params: ViewCrateGraphParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_view_crate_graph"); - let dot = snap.analysis.view_crate_graph(params.full)??; + let dot = snap.analysis.view_crate_graph(params.full)?.map_err(anyhow::Error::msg)?; Ok(dot) } pub(crate) fn handle_expand_macro( snap: GlobalStateSnapshot, params: lsp_ext::ExpandMacroParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_expand_macro"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; @@ -215,11 +215,11 @@ pub(crate) fn handle_expand_macro( pub(crate) fn handle_selection_range( snap: GlobalStateSnapshot, params: lsp_types::SelectionRangeParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_selection_range"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; - let res: Result> = params + let res: anyhow::Result> = params .positions .into_iter() .map(|position| { @@ -258,7 +258,7 @@ pub(crate) fn handle_selection_range( pub(crate) fn handle_matching_brace( snap: GlobalStateSnapshot, params: lsp_ext::MatchingBraceParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_matching_brace"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; @@ -281,7 +281,7 @@ pub(crate) fn handle_matching_brace( pub(crate) fn handle_join_lines( snap: GlobalStateSnapshot, params: lsp_ext::JoinLinesParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_join_lines"); let config = snap.config.join_lines(); @@ -306,7 +306,7 @@ pub(crate) fn handle_join_lines( pub(crate) fn handle_on_enter( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_on_enter"); let position = from_proto::file_position(&snap, params)?; let edit = match snap.analysis.on_enter(position)? { @@ -321,7 +321,7 @@ pub(crate) fn handle_on_enter( pub(crate) fn handle_on_type_formatting( snap: GlobalStateSnapshot, params: lsp_types::DocumentOnTypeFormattingParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_on_type_formatting"); let mut position = from_proto::file_position(&snap, params.text_document_position)?; let line_index = snap.file_line_index(position.file_id)?; @@ -361,7 +361,7 @@ pub(crate) fn handle_on_type_formatting( pub(crate) fn handle_document_symbol( snap: GlobalStateSnapshot, params: lsp_types::DocumentSymbolParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_document_symbol"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; @@ -450,7 +450,7 @@ pub(crate) fn handle_document_symbol( pub(crate) fn handle_workspace_symbol( snap: GlobalStateSnapshot, params: WorkspaceSymbolParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_workspace_symbol"); let config = snap.config.workspace_symbol(); @@ -513,7 +513,10 @@ pub(crate) fn handle_workspace_symbol( (all_symbols, libs) } - fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result> { + fn exec_query( + snap: &GlobalStateSnapshot, + query: Query, + ) -> anyhow::Result> { let mut res = Vec::new(); for nav in snap.analysis.symbol_search(query)? { let container_name = nav.container_name.as_ref().map(|v| v.to_string()); @@ -542,7 +545,7 @@ pub(crate) fn handle_workspace_symbol( pub(crate) fn handle_will_rename_files( snap: GlobalStateSnapshot, params: lsp_types::RenameFilesParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_will_rename_files"); let source_changes: Vec = params @@ -604,7 +607,7 @@ pub(crate) fn handle_will_rename_files( pub(crate) fn handle_goto_definition( snap: GlobalStateSnapshot, params: lsp_types::GotoDefinitionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_goto_definition"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let nav_info = match snap.analysis.goto_definition(position)? { @@ -619,7 +622,7 @@ pub(crate) fn handle_goto_definition( pub(crate) fn handle_goto_declaration( snap: GlobalStateSnapshot, params: lsp_types::request::GotoDeclarationParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_goto_declaration"); let position = from_proto::file_position(&snap, params.text_document_position_params.clone())?; let nav_info = match snap.analysis.goto_declaration(position)? { @@ -634,7 +637,7 @@ pub(crate) fn handle_goto_declaration( pub(crate) fn handle_goto_implementation( snap: GlobalStateSnapshot, params: lsp_types::request::GotoImplementationParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_goto_implementation"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let nav_info = match snap.analysis.goto_implementation(position)? { @@ -649,7 +652,7 @@ pub(crate) fn handle_goto_implementation( pub(crate) fn handle_goto_type_definition( snap: GlobalStateSnapshot, params: lsp_types::request::GotoTypeDefinitionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_goto_type_definition"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let nav_info = match snap.analysis.goto_type_definition(position)? { @@ -664,7 +667,7 @@ pub(crate) fn handle_goto_type_definition( pub(crate) fn handle_parent_module( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_parent_module"); if let Ok(file_path) = ¶ms.text_document.uri.to_file_path() { if file_path.file_name().unwrap_or_default() == "Cargo.toml" { @@ -731,7 +734,7 @@ pub(crate) fn handle_parent_module( pub(crate) fn handle_runnables( snap: GlobalStateSnapshot, params: lsp_ext::RunnablesParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_runnables"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; @@ -826,7 +829,7 @@ fn should_skip_for_offset(runnable: &Runnable, offset: Option) -> bool pub(crate) fn handle_related_tests( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_related_tests"); let position = from_proto::file_position(&snap, params)?; @@ -844,7 +847,7 @@ pub(crate) fn handle_related_tests( pub(crate) fn handle_completion( snap: GlobalStateSnapshot, params: lsp_types::CompletionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_completion"); let text_document_position = params.text_document_position.clone(); let position = from_proto::file_position(&snap, params.text_document_position)?; @@ -872,7 +875,7 @@ pub(crate) fn handle_completion( pub(crate) fn handle_completion_resolve( snap: GlobalStateSnapshot, mut original_completion: CompletionItem, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_completion_resolve"); if !all_edits_are_disjoint(&original_completion, &[]) { @@ -928,7 +931,7 @@ pub(crate) fn handle_completion_resolve( pub(crate) fn handle_folding_range( snap: GlobalStateSnapshot, params: FoldingRangeParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_folding_range"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let folds = snap.analysis.folding_ranges(file_id)?; @@ -945,7 +948,7 @@ pub(crate) fn handle_folding_range( pub(crate) fn handle_signature_help( snap: GlobalStateSnapshot, params: lsp_types::SignatureHelpParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_signature_help"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let help = match snap.analysis.signature_help(position)? { @@ -960,7 +963,7 @@ pub(crate) fn handle_signature_help( pub(crate) fn handle_hover( snap: GlobalStateSnapshot, params: lsp_ext::HoverParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_hover"); let range = match params.position { PositionOrRange::Position(position) => Range::new(position, position), @@ -997,7 +1000,7 @@ pub(crate) fn handle_hover( pub(crate) fn handle_prepare_rename( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_prepare_rename"); let position = from_proto::file_position(&snap, params)?; @@ -1011,7 +1014,7 @@ pub(crate) fn handle_prepare_rename( pub(crate) fn handle_rename( snap: GlobalStateSnapshot, params: RenameParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_rename"); let position = from_proto::file_position(&snap, params.text_document_position)?; @@ -1034,7 +1037,7 @@ pub(crate) fn handle_rename( pub(crate) fn handle_references( snap: GlobalStateSnapshot, params: lsp_types::ReferenceParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_references"); let position = from_proto::file_position(&snap, params.text_document_position)?; @@ -1077,7 +1080,7 @@ pub(crate) fn handle_references( pub(crate) fn handle_formatting( snap: GlobalStateSnapshot, params: lsp_types::DocumentFormattingParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_formatting"); run_rustfmt(&snap, params.text_document, None) @@ -1086,7 +1089,7 @@ pub(crate) fn handle_formatting( pub(crate) fn handle_range_formatting( snap: GlobalStateSnapshot, params: lsp_types::DocumentRangeFormattingParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_range_formatting"); run_rustfmt(&snap, params.text_document, Some(params.range)) @@ -1095,7 +1098,7 @@ pub(crate) fn handle_range_formatting( pub(crate) fn handle_code_action( snap: GlobalStateSnapshot, params: lsp_types::CodeActionParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_code_action"); if !snap.config.code_action_literals() { @@ -1158,7 +1161,7 @@ pub(crate) fn handle_code_action( pub(crate) fn handle_code_action_resolve( snap: GlobalStateSnapshot, mut code_action: lsp_ext::CodeAction, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_code_action_resolve"); let params = match code_action.data.take() { Some(it) => it, @@ -1219,7 +1222,7 @@ pub(crate) fn handle_code_action_resolve( Ok(code_action) } -fn parse_action_id(action_id: &str) -> Result<(usize, SingleResolve), String> { +fn parse_action_id(action_id: &str) -> anyhow::Result<(usize, SingleResolve), String> { let id_parts = action_id.split(':').collect::>(); match id_parts.as_slice() { [assist_id_string, assist_kind_string, index_string] => { @@ -1237,7 +1240,7 @@ fn parse_action_id(action_id: &str) -> Result<(usize, SingleResolve), String> { pub(crate) fn handle_code_lens( snap: GlobalStateSnapshot, params: lsp_types::CodeLensParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_code_lens"); let lens_config = snap.config.lens(); @@ -1280,7 +1283,7 @@ pub(crate) fn handle_code_lens( pub(crate) fn handle_code_lens_resolve( snap: GlobalStateSnapshot, code_lens: CodeLens, -) -> Result { +) -> anyhow::Result { let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { return Ok(code_lens) }; let annotation = snap.analysis.resolve_annotation(annotation)?; @@ -1301,7 +1304,7 @@ pub(crate) fn handle_code_lens_resolve( pub(crate) fn handle_document_highlight( snap: GlobalStateSnapshot, params: lsp_types::DocumentHighlightParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_document_highlight"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let line_index = snap.file_line_index(position.file_id)?; @@ -1323,7 +1326,7 @@ pub(crate) fn handle_document_highlight( pub(crate) fn handle_ssr( snap: GlobalStateSnapshot, params: lsp_ext::SsrParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_ssr"); let selections = params .selections @@ -1343,7 +1346,7 @@ pub(crate) fn handle_ssr( pub(crate) fn handle_inlay_hints( snap: GlobalStateSnapshot, params: InlayHintParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_inlay_hints"); let document_uri = ¶ms.text_document.uri; let FileRange { file_id, range } = from_proto::file_range( @@ -1365,7 +1368,7 @@ pub(crate) fn handle_inlay_hints( pub(crate) fn handle_inlay_hints_resolve( _snap: GlobalStateSnapshot, hint: InlayHint, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_inlay_hints_resolve"); Ok(hint) } @@ -1373,7 +1376,7 @@ pub(crate) fn handle_inlay_hints_resolve( pub(crate) fn handle_call_hierarchy_prepare( snap: GlobalStateSnapshot, params: CallHierarchyPrepareParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_call_hierarchy_prepare"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; @@ -1395,7 +1398,7 @@ pub(crate) fn handle_call_hierarchy_prepare( pub(crate) fn handle_call_hierarchy_incoming( snap: GlobalStateSnapshot, params: CallHierarchyIncomingCallsParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_call_hierarchy_incoming"); let item = params.item; @@ -1430,7 +1433,7 @@ pub(crate) fn handle_call_hierarchy_incoming( pub(crate) fn handle_call_hierarchy_outgoing( snap: GlobalStateSnapshot, params: CallHierarchyOutgoingCallsParams, -) -> Result>> { +) -> anyhow::Result>> { let _p = profile::span("handle_call_hierarchy_outgoing"); let item = params.item; @@ -1465,7 +1468,7 @@ pub(crate) fn handle_call_hierarchy_outgoing( pub(crate) fn handle_semantic_tokens_full( snap: GlobalStateSnapshot, params: SemanticTokensParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_semantic_tokens_full"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; @@ -1495,7 +1498,7 @@ pub(crate) fn handle_semantic_tokens_full( pub(crate) fn handle_semantic_tokens_full_delta( snap: GlobalStateSnapshot, params: SemanticTokensDeltaParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_semantic_tokens_full_delta"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; @@ -1535,7 +1538,7 @@ pub(crate) fn handle_semantic_tokens_full_delta( pub(crate) fn handle_semantic_tokens_range( snap: GlobalStateSnapshot, params: SemanticTokensRangeParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_semantic_tokens_range"); let frange = from_proto::file_range(&snap, params.text_document, params.range)?; @@ -1561,7 +1564,7 @@ pub(crate) fn handle_semantic_tokens_range( pub(crate) fn handle_open_docs( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result { +) -> anyhow::Result { let _p = profile::span("handle_open_docs"); let position = from_proto::file_position(&snap, params)?; @@ -1600,7 +1603,7 @@ pub(crate) fn handle_open_docs( pub(crate) fn handle_open_cargo_toml( snap: GlobalStateSnapshot, params: lsp_ext::OpenCargoTomlParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_open_cargo_toml"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; @@ -1618,7 +1621,7 @@ pub(crate) fn handle_open_cargo_toml( pub(crate) fn handle_move_item( snap: GlobalStateSnapshot, params: lsp_ext::MoveItemParams, -) -> Result> { +) -> anyhow::Result> { let _p = profile::span("handle_move_item"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let range = from_proto::file_range(&snap, params.text_document, params.range)?; @@ -1792,7 +1795,7 @@ fn run_rustfmt( snap: &GlobalStateSnapshot, text_document: TextDocumentIdentifier, range: Option, -) -> Result>> { +) -> anyhow::Result>> { let file_id = from_proto::file_id(snap, &text_document.uri)?; let file = snap.analysis.file_text(file_id)?; @@ -1942,7 +1945,7 @@ fn run_rustfmt( pub(crate) fn fetch_dependency_list( state: GlobalStateSnapshot, _params: FetchDependencyListParams, -) -> Result { +) -> anyhow::Result { let crates = state.analysis.fetch_crates()?; let crate_infos = crates .into_iter() diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 65de4366e9..57e26c241b 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -54,13 +54,12 @@ use serde::de::DeserializeOwned; pub use crate::{caps::server_capabilities, main_loop::main_loop, version::version}; -pub type Error = Box; -pub type Result = std::result::Result; - -pub fn from_json(what: &'static str, json: &serde_json::Value) -> Result { - let res = serde_json::from_value(json.clone()) - .map_err(|e| format!("Failed to deserialize {what}: {e}; {json}"))?; - Ok(res) +pub fn from_json( + what: &'static str, + json: &serde_json::Value, +) -> anyhow::Result { + serde_json::from_value(json.clone()) + .map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}")) } #[derive(Debug)] diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 02dd94e5fa..a6640570fc 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -23,10 +23,9 @@ use crate::{ lsp_ext, lsp_utils::{notification_is, Progress}, reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress}, - Result, }; -pub fn main_loop(config: Config, connection: Connection) -> Result<()> { +pub fn main_loop(config: Config, connection: Connection) -> anyhow::Result<()> { tracing::info!("initial config: {:#?}", config); // Windows scheduler implements priority boosts: if thread waits for an @@ -109,7 +108,7 @@ impl fmt::Debug for Event { } impl GlobalState { - fn run(mut self, inbox: Receiver) -> Result<()> { + fn run(mut self, inbox: Receiver) -> anyhow::Result<()> { self.update_status_or_notify(); if self.config.did_save_text_document_dynamic_registration() { @@ -134,7 +133,7 @@ impl GlobalState { self.handle_event(event)?; } - Err("client exited without proper shutdown sequence".into()) + anyhow::bail!("client exited without proper shutdown sequence") } fn register_did_save_capability(&mut self) { @@ -191,7 +190,7 @@ impl GlobalState { } } - fn handle_event(&mut self, event: Event) -> Result<()> { + fn handle_event(&mut self, event: Event) -> anyhow::Result<()> { let loop_start = Instant::now(); // NOTE: don't count blocking select! call as a loop-turn time let _p = profile::span("GlobalState::handle_event"); @@ -758,7 +757,7 @@ impl GlobalState { } /// Handles an incoming notification. - fn on_notification(&mut self, not: Notification) -> Result<()> { + fn on_notification(&mut self, not: Notification) -> anyhow::Result<()> { use crate::handlers::notification as handlers; use lsp_types::notification as notifs; diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index b2a8041ae9..1cae8688eb 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -78,7 +78,10 @@ impl<'a> Project<'a> { INIT.call_once(|| { tracing_subscriber::fmt() .with_test_writer() - .with_env_filter(tracing_subscriber::EnvFilter::from_env("RA_LOG")) + // FIXME: I am not smart enough to figure out how to use this with + // `tracing_subscriber::filter::Targets`. + // + // .with_env_filter(tracing_subscriber::EnvFilter::from_env("RA_LOG")) .init(); profile::init_from(crate::PROFILE); }); From 9326cf7f0cc9a98aaf16c5dff5e7a311d1d4dfb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 19 Jun 2023 09:14:04 +0300 Subject: [PATCH 011/201] Merge commit 'cd3bf9fe51676b520c546460e6d8919b8c8ff99f' into sync-from-ra --- .github/workflows/autopublish.yaml | 4 +- .github/workflows/ci.yaml | 8 +- Cargo.lock | 50 +- crates/base-db/src/fixture.rs | 6 +- crates/base-db/src/input.rs | 23 + crates/hir-def/src/body.rs | 50 +- crates/hir-def/src/body/lower.rs | 72 +- crates/hir-def/src/body/pretty.rs | 1 + crates/hir-def/src/db.rs | 23 +- crates/hir-def/src/expander.rs | 12 +- crates/hir-def/src/find_path.rs | 4 +- crates/hir-def/src/hir.rs | 20 +- crates/hir-def/src/hir/type_ref.rs | 119 +-- crates/hir-def/src/item_scope.rs | 4 - crates/hir-def/src/item_tree.rs | 6 - crates/hir-def/src/lib.rs | 301 +++++++- .../builtin_derive_macro.rs | 70 +- .../macro_expansion_tests/builtin_fn_macro.rs | 48 +- crates/hir-def/src/nameres.rs | 23 +- crates/hir-def/src/nameres/collector.rs | 102 ++- crates/hir-def/src/path.rs | 4 +- crates/hir-def/src/path/lower.rs | 4 +- crates/hir-def/src/resolver.rs | 38 +- crates/hir-expand/src/ast_id_map.rs | 7 +- crates/hir-expand/src/attrs.rs | 10 +- crates/hir-expand/src/builtin_derive_macro.rs | 154 ++-- crates/hir-expand/src/builtin_fn_macro.rs | 147 ++-- crates/hir-expand/src/db.rs | 108 ++- crates/hir-expand/src/eager.rs | 84 +- crates/hir-expand/src/lib.rs | 72 +- crates/hir-expand/src/proc_macro.rs | 11 +- crates/hir-ty/Cargo.toml | 8 +- crates/hir-ty/src/autoderef.rs | 24 +- crates/hir-ty/src/chalk_db.rs | 4 +- crates/hir-ty/src/consteval.rs | 36 +- crates/hir-ty/src/consteval/tests.rs | 11 + crates/hir-ty/src/db.rs | 1 + crates/hir-ty/src/diagnostics/unsafe_check.rs | 7 +- crates/hir-ty/src/display.rs | 43 +- crates/hir-ty/src/infer.rs | 47 +- crates/hir-ty/src/infer/closure.rs | 3 +- crates/hir-ty/src/infer/expr.rs | 5 +- crates/hir-ty/src/infer/mutability.rs | 4 +- crates/hir-ty/src/infer/path.rs | 17 +- crates/hir-ty/src/interner.rs | 2 +- crates/hir-ty/src/layout/tests.rs | 35 +- crates/hir-ty/src/lower.rs | 119 +-- crates/hir-ty/src/method_resolution.rs | 2 +- crates/hir-ty/src/mir/borrowck.rs | 16 +- crates/hir-ty/src/mir/eval.rs | 18 +- crates/hir-ty/src/mir/lower.rs | 8 +- crates/hir-ty/src/mir/pretty.rs | 3 + crates/hir-ty/src/tests.rs | 2 + crates/hir-ty/src/tests/regression.rs | 23 + crates/hir-ty/src/tests/simple.rs | 48 ++ crates/hir-ty/src/tests/traits.rs | 10 +- crates/hir-ty/src/utils.rs | 30 +- crates/hir/src/from_id.rs | 3 + crates/hir/src/lib.rs | 92 ++- crates/hir/src/semantics.rs | 18 +- crates/hir/src/source_analyzer.rs | 7 +- crates/hir/src/symbols.rs | 1 + .../src/handlers/add_missing_impl_members.rs | 363 ++++++++- .../src/handlers/extract_type_alias.rs | 49 +- .../src/handlers/generate_delegate_methods.rs | 25 +- .../src/handlers/generate_function.rs | 102 ++- .../ide-assists/src/handlers/inline_call.rs | 3 +- .../src/handlers/inline_const_as_literal.rs | 722 ++++++++++++++++++ crates/ide-assists/src/handlers/raw_string.rs | 3 +- .../replace_derive_with_manual_impl.rs | 28 +- crates/ide-assists/src/lib.rs | 2 + crates/ide-assists/src/tests/generated.rs | 21 + crates/ide-assists/src/utils.rs | 95 ++- crates/ide-completion/src/completions/dot.rs | 122 ++- .../src/completions/item_list/trait_impl.rs | 108 ++- crates/ide-db/src/apply_change.rs | 34 +- crates/ide-db/src/path_transform.rs | 169 +++- crates/ide-db/src/search.rs | 2 + .../ide-db/src/test_data/test_doc_alias.txt | 42 +- .../test_symbol_index_collection.txt | 134 ++-- crates/ide/src/hover/tests.rs | 43 ++ crates/ide/src/inlay_hints/chaining.rs | 12 +- crates/ide/src/lib.rs | 2 +- crates/ide/src/syntax_highlighting.rs | 3 + .../test_data/highlight_strings.html | 3 + crates/ide/src/syntax_highlighting/tests.rs | 3 + crates/parser/src/grammar/types.rs | 2 + .../parser/inline/ok/0017_array_type.rast | 5 +- .../test_data/parser/ok/0030_traits.rast | 5 +- .../parser/ok/0043_complex_assignment.rast | 5 +- crates/proc-macro-srv/src/server.rs | 6 +- crates/project-model/src/workspace.rs | 6 + crates/rust-analyzer/Cargo.toml | 3 +- crates/rust-analyzer/src/cli.rs | 17 +- .../rust-analyzer/src/cli/analysis_stats.rs | 328 +++++--- crates/rust-analyzer/src/cli/diagnostics.rs | 8 +- crates/rust-analyzer/src/cli/flags.rs | 20 +- .../rust-analyzer/src/cli/progress_report.rs | 41 +- crates/rust-analyzer/src/dispatch.rs | 48 +- crates/rust-analyzer/src/global_state.rs | 35 +- .../src/handlers/notification.rs | 4 +- crates/rust-analyzer/src/handlers/request.rs | 24 +- crates/rust-analyzer/src/main_loop.rs | 82 +- crates/rust-analyzer/src/reload.rs | 45 +- crates/rust-analyzer/src/to_proto.rs | 33 +- crates/rust-analyzer/tests/slow-tests/main.rs | 13 +- crates/syntax/rust.ungram | 2 +- crates/syntax/src/ast/generated/nodes.rs | 2 +- crates/syntax/src/ast/make.rs | 6 +- crates/test-utils/src/fixture.rs | 4 + crates/test-utils/src/minicore.rs | 51 +- docs/user/manual.adoc | 21 +- editors/code/.vscodeignore | 1 + lib/la-arena/src/lib.rs | 6 + 114 files changed, 3893 insertions(+), 1252 deletions(-) create mode 100644 crates/ide-assists/src/handlers/inline_const_as_literal.rs diff --git a/.github/workflows/autopublish.yaml b/.github/workflows/autopublish.yaml index 7090c94d93..15cedab127 100644 --- a/.github/workflows/autopublish.yaml +++ b/.github/workflows/autopublish.yaml @@ -28,7 +28,7 @@ jobs: - name: Publish Crates env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - PATCH: ${{ github.run_number }} + RUN_NUMBER: ${{ github.run_number }} shell: bash run: | git config --global user.email "runner@gha.local" @@ -53,4 +53,4 @@ jobs: # Remove library crates from the workspaces so we don't auto-publish them as well sed -i 's/ "lib\/\*",//' ./Cargo.toml find crates/rust-analyzer -type f -name '*.rs' -exec sed -i 's/rust_analyzer/ra_ap_rust_analyzer/g' {} + - cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$PATCH + cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$(($RUN_NUMBER + 133)) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 622da105fd..31bb7eed8d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,6 +24,7 @@ jobs: pull-requests: read outputs: typescript: ${{ steps.filter.outputs.typescript }} + proc_macros: ${{ steps.filter.outputs.proc_macros }} steps: - uses: actions/checkout@v3 - uses: dorny/paths-filter@4067d885736b84de7c414f582ac45897079b0a78 @@ -45,8 +46,8 @@ jobs: runs-on: ${{ matrix.os }} env: CC: deny_c - RUST_CHANNEL: "${{ needs.changes.outputs.proc_macros == 'true' && 'nightly' || 'stable'}}" - USE_SYSROOT_ABI: "${{ needs.changes.outputs.proc_macros == 'true' && '--features sysroot-abi' || ''}}" + RUST_CHANNEL: "${{ needs.changes.outputs.proc_macros == 'true' && 'nightly' || 'stable' }}" + USE_SYSROOT_ABI: "${{ needs.changes.outputs.proc_macros == 'true' && '--features sysroot-abi' || '' }}" strategy: fail-fast: false @@ -62,7 +63,8 @@ jobs: - name: Install Rust toolchain run: | rustup update --no-self-update ${{ env.RUST_CHANNEL }} - rustup component add rustfmt rust-src + rustup component add --toolchain ${{ env.RUST_CHANNEL }} rustfmt rust-src + rustup default ${{ env.RUST_CHANNEL }} - name: Cache Dependencies uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 diff --git a/Cargo.lock b/Cargo.lock index e36aef6a6a..50c81ca279 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,21 +177,21 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.89.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea176c50987dc4765961aa165001e8eb5a722a26308c5797a47303ea91686aab" +checksum = "c59178fded594fe78c47b841520e5a4399d00fe15fffee19b945958a878cd02d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", "synstructure", ] [[package]] name = "chalk-ir" -version = "0.89.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473b480241695428c14e8f84f1c9a47ef232450a50faf3a4041e5c9dc11e0a3b" +checksum = "8824be92876823b828d551bb792f79eb1f69c69d1948abf69fccbf84e448e57b" dependencies = [ "bitflags 1.3.2", "chalk-derive", @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.89.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6764b4fe67cac3a3758185084efbfbd39bf0352795824ba849ddd2b64cd4bb28" +checksum = "1e110d1260809c238072d1c8ef84060e39983e8ea9d4c6f74b19b0ebbf8904dc" dependencies = [ "chalk-derive", "chalk-ir", @@ -213,9 +213,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.89.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a7e6160966eceb6e7dcc2f479a2af4c477aaf5bccbc640d82515995ab1a6cc" +checksum = "12200b19abf4b0633095f7bd099f3ef609d314754b6adb358c68cc04d10589e5" dependencies = [ "chalk-derive", "chalk-ir", @@ -327,7 +327,7 @@ checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1505,7 +1505,6 @@ dependencies = [ "parking_lot 0.12.1", "parking_lot_core 0.9.6", "proc-macro-api", - "proc-macro-srv-cli", "profile", "project-model", "rayon", @@ -1578,7 +1577,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1637,7 +1636,7 @@ checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1660,7 +1659,7 @@ checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1731,14 +1730,25 @@ dependencies = [ ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "syn" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", - "syn", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", "unicode-xid", ] @@ -1811,7 +1821,7 @@ checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1913,7 +1923,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index 5b11343173..d3abc3870b 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -215,7 +215,7 @@ impl ChangeFixture { None, default_cfg, Default::default(), - Env::default(), + Env::new_for_test_fixture(), false, CrateOrigin::Local { repo: None, name: None }, default_target_data_layout @@ -259,7 +259,7 @@ impl ChangeFixture { None, Default::default(), Default::default(), - Env::default(), + Env::new_for_test_fixture(), false, CrateOrigin::Lang(LangCrateOrigin::Core), target_layout.clone(), @@ -298,7 +298,7 @@ impl ChangeFixture { None, Default::default(), Default::default(), - Env::default(), + Env::new_for_test_fixture(), true, CrateOrigin::Local { repo: None, name: None }, target_layout, diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index e8d521b42f..f2e523675b 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -151,6 +151,12 @@ pub enum CrateOrigin { Lang(LangCrateOrigin), } +impl CrateOrigin { + pub fn is_local(&self) -> bool { + matches!(self, CrateOrigin::Local { .. }) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum LangCrateOrigin { Alloc, @@ -333,6 +339,17 @@ pub struct Env { entries: FxHashMap, } +impl Env { + pub fn new_for_test_fixture() -> Self { + Env { + entries: FxHashMap::from_iter([( + String::from("__ra_is_test_fixture"), + String::from("__ra_is_test_fixture"), + )]), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Dependency { pub crate_id: CrateId, @@ -456,6 +473,12 @@ impl CrateGraph { self.arena.iter().map(|(idx, _)| idx) } + // FIXME: used for `handle_hack_cargo_workspace`, should be removed later + #[doc(hidden)] + pub fn iter_mut(&mut self) -> impl Iterator + '_ { + self.arena.iter_mut() + } + /// Returns an iterator over all transitive dependencies of the given crate, /// including the crate itself. pub fn transitive_deps(&self, of: CrateId) -> impl Iterator { diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 36626ed1a9..94dc39b117 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -37,6 +37,9 @@ pub struct Body { pub pats: Arena, pub bindings: Arena, pub labels: Arena bool { let crate_id = owner.module(db.upcast()).krate(); - let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else { + let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) + else { return false; }; let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build(); diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 82cc4f9f59..7405b51e6e 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -636,7 +636,8 @@ fn render_const_scalar( } hir_def::AdtId::EnumId(e) => { let Some((var_id, var_layout)) = - detect_variant_from_bytes(&layout, f.db, krate, b, e) else { + detect_variant_from_bytes(&layout, f.db, krate, b, e) + else { return f.write_str(""); }; let data = &f.db.enum_data(e).variants[var_id]; diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 1ac0837b5b..d53644e279 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -1232,7 +1232,9 @@ impl<'a> InferenceContext<'a> { .as_function()? .lookup(self.db.upcast()) .container - else { return None }; + else { + return None; + }; self.resolve_output_on(trait_) } diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 2a9b2debd4..06be1f80ad 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -154,7 +154,9 @@ pub fn layout_of_ty_query( ty: Ty, krate: CrateId, ) -> Result, LayoutError> { - let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; + let Some(target) = db.target_data_layout(krate) else { + return Err(LayoutError::TargetLayoutNotAvailable); + }; let cx = LayoutCx { krate, target: &target }; let dl = &*cx.current_data_layout(); let trait_env = Arc::new(TraitEnvironment::empty(krate)); diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index bd2752a711..0012b4179d 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -31,7 +31,9 @@ pub fn layout_of_adt_query( subst: Substitution, krate: CrateId, ) -> Result, LayoutError> { - let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; + let Some(target) = db.target_data_layout(krate) else { + return Err(LayoutError::TargetLayoutNotAvailable); + }; let cx = LayoutCx { krate, target: &target }; let dl = cx.current_data_layout(); let handle_variant = |def: VariantId, var: &VariantData| { diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index ab6430e8f1..39d1a0e05f 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -689,7 +689,7 @@ pub fn lookup_impl_method( fn_subst: Substitution, ) -> (FunctionId, Substitution) { let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { - return (func, fn_subst) + return (func, fn_subst); }; let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); let fn_params = fn_subst.len(Interner) - trait_params; @@ -699,8 +699,8 @@ pub fn lookup_impl_method( }; let name = &db.function_data(func).name; - let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name) - .and_then(|assoc| { + let Some((impl_fn, impl_subst)) = + lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name).and_then(|assoc| { if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) } else { diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index a5dd0182eb..0e6a63df41 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -271,7 +271,10 @@ fn ever_initialized_map( } } let Some(terminator) = &block.terminator else { - never!("Terminator should be none only in construction.\nThe body:\n{}", body.pretty_print(db)); + never!( + "Terminator should be none only in construction.\nThe body:\n{}", + body.pretty_print(db) + ); return; }; let targets = match &terminator.kind { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index ddcfc57185..51a75cbbff 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1226,8 +1226,8 @@ impl Evaluator<'_> { } Variants::Multiple { tag, tag_encoding, variants, .. } => { let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else { - not_supported!("missing target data layout"); - }; + not_supported!("missing target data layout"); + }; let size = tag.size(&*target_data_layout).bytes_usize(); let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field match tag_encoding { @@ -1344,7 +1344,9 @@ impl Evaluator<'_> { AdtId::UnionId(_) => not_supported!("unsizing unions"), AdtId::EnumId(_) => not_supported!("unsizing enums"), }; - let Some((last_field, _)) = self.db.struct_data(id).variant_data.fields().iter().rev().next() else { + let Some((last_field, _)) = + self.db.struct_data(id).variant_data.fields().iter().rev().next() + else { not_supported!("unsizing struct without field"); }; let target_last_field = self.db.field_types(id.into())[last_field] diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 23bb4a29f8..73ed52f3b3 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -327,7 +327,9 @@ impl Evaluator<'_> { } "pthread_getspecific" => { let Some(arg0) = args.get(0) else { - return Err(MirEvalError::TypeError("pthread_getspecific arg0 is not provided")); + return Err(MirEvalError::TypeError( + "pthread_getspecific arg0 is not provided", + )); }; let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]); let value = self.thread_local_storage.get_key(key)?; @@ -336,11 +338,15 @@ impl Evaluator<'_> { } "pthread_setspecific" => { let Some(arg0) = args.get(0) else { - return Err(MirEvalError::TypeError("pthread_setspecific arg0 is not provided")); + return Err(MirEvalError::TypeError( + "pthread_setspecific arg0 is not provided", + )); }; let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]); let Some(arg1) = args.get(1) else { - return Err(MirEvalError::TypeError("pthread_setspecific arg1 is not provided")); + return Err(MirEvalError::TypeError( + "pthread_setspecific arg1 is not provided", + )); }; let value = from_bytes!(u128, pad16(arg1.get(self)?, false)); self.thread_local_storage.set_key(key, value)?; @@ -390,7 +396,9 @@ impl Evaluator<'_> { "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs" | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => { let [arg] = args else { - return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64) -> f64")); + return Err(MirEvalError::TypeError( + "f64 intrinsic signature doesn't match fn (f64) -> f64", + )); }; let arg = from_bytes!(f64, arg.get(self)?); match name { @@ -416,7 +424,9 @@ impl Evaluator<'_> { } "pow" | "minnum" | "maxnum" | "copysign" => { let [arg1, arg2] = args else { - return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64, f64) -> f64")); + return Err(MirEvalError::TypeError( + "f64 intrinsic signature doesn't match fn (f64, f64) -> f64", + )); }; let arg1 = from_bytes!(f64, arg1.get(self)?); let arg2 = from_bytes!(f64, arg2.get(self)?); @@ -430,7 +440,9 @@ impl Evaluator<'_> { } "powi" => { let [arg1, arg2] = args else { - return Err(MirEvalError::TypeError("powif64 signature doesn't match fn (f64, i32) -> f64")); + return Err(MirEvalError::TypeError( + "powif64 signature doesn't match fn (f64, i32) -> f64", + )); }; let arg1 = from_bytes!(f64, arg1.get(self)?); let arg2 = from_bytes!(i32, arg2.get(self)?); @@ -438,7 +450,9 @@ impl Evaluator<'_> { } "fma" => { let [arg1, arg2, arg3] = args else { - return Err(MirEvalError::TypeError("fmaf64 signature doesn't match fn (f64, f64, f64) -> f64")); + return Err(MirEvalError::TypeError( + "fmaf64 signature doesn't match fn (f64, f64, f64) -> f64", + )); }; let arg1 = from_bytes!(f64, arg1.get(self)?); let arg2 = from_bytes!(f64, arg2.get(self)?); @@ -454,7 +468,9 @@ impl Evaluator<'_> { "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs" | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => { let [arg] = args else { - return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32) -> f32")); + return Err(MirEvalError::TypeError( + "f32 intrinsic signature doesn't match fn (f32) -> f32", + )); }; let arg = from_bytes!(f32, arg.get(self)?); match name { @@ -480,7 +496,9 @@ impl Evaluator<'_> { } "pow" | "minnum" | "maxnum" | "copysign" => { let [arg1, arg2] = args else { - return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32, f32) -> f32")); + return Err(MirEvalError::TypeError( + "f32 intrinsic signature doesn't match fn (f32, f32) -> f32", + )); }; let arg1 = from_bytes!(f32, arg1.get(self)?); let arg2 = from_bytes!(f32, arg2.get(self)?); @@ -494,7 +512,9 @@ impl Evaluator<'_> { } "powi" => { let [arg1, arg2] = args else { - return Err(MirEvalError::TypeError("powif32 signature doesn't match fn (f32, i32) -> f32")); + return Err(MirEvalError::TypeError( + "powif32 signature doesn't match fn (f32, i32) -> f32", + )); }; let arg1 = from_bytes!(f32, arg1.get(self)?); let arg2 = from_bytes!(i32, arg2.get(self)?); @@ -502,7 +522,9 @@ impl Evaluator<'_> { } "fma" => { let [arg1, arg2, arg3] = args else { - return Err(MirEvalError::TypeError("fmaf32 signature doesn't match fn (f32, f32, f32) -> f32")); + return Err(MirEvalError::TypeError( + "fmaf32 signature doesn't match fn (f32, f32, f32) -> f32", + )); }; let arg1 = from_bytes!(f32, arg1.get(self)?); let arg2 = from_bytes!(f32, arg2.get(self)?); @@ -515,14 +537,16 @@ impl Evaluator<'_> { } match name { "size_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; let size = self.size_of_sized(ty, locals, "size_of arg")?; destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) } "size_of_val" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + else { return Err(MirEvalError::TypeError("size_of_val generic arg is not provided")); }; let [arg] = args else { @@ -549,14 +573,16 @@ impl Evaluator<'_> { destination.write_from_bytes(self, &size.to_le_bytes()) } "min_align_of" | "pref_align_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + else { return Err(MirEvalError::TypeError("align_of generic arg is not provided")); }; let align = self.layout(ty)?.align.abi.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } "needs_drop" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; let result = !ty.clone().is_copy(self.db, locals.body.owner); @@ -697,10 +723,15 @@ impl Evaluator<'_> { } "copy" | "copy_nonoverlapping" => { let [src, dst, offset] = args else { - return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided")); + return Err(MirEvalError::TypeError( + "copy_nonoverlapping args are not provided", + )); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { - return Err(MirEvalError::TypeError("copy_nonoverlapping generic arg is not provided")); + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "copy_nonoverlapping generic arg is not provided", + )); }; let src = Address::from_bytes(src.get(self)?)?; let dst = Address::from_bytes(dst.get(self)?)?; @@ -715,7 +746,8 @@ impl Evaluator<'_> { let [ptr, offset] = args else { return Err(MirEvalError::TypeError("offset args are not provided")); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + else { return Err(MirEvalError::TypeError("offset generic arg is not provided")); }; let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false)); @@ -824,8 +856,11 @@ impl Evaluator<'_> { let [arg] = args else { return Err(MirEvalError::TypeError("discriminant_value arg is not provided")); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { - return Err(MirEvalError::TypeError("discriminant_value generic arg is not provided")); + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "discriminant_value generic arg is not provided", + )); }; let addr = Address::from_bytes(arg.get(self)?)?; let size = self.size_of_sized(ty, locals, "discriminant_value ptr type")?; diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs index c41e2790c1..89b04eb36b 100644 --- a/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -23,7 +23,8 @@ impl Evaluator<'_> { fn detect_simd_ty(&self, ty: &Ty) -> Result { match ty.kind(Interner) { TyKind::Adt(_, subst) => { - let Some(len) = subst.as_slice(Interner).get(1).and_then(|x| x.constant(Interner)) else { + let Some(len) = subst.as_slice(Interner).get(1).and_then(|x| x.constant(Interner)) + else { return Err(MirEvalError::TypeError("simd type without len param")); }; match try_const_usize(self.db, len) { @@ -89,7 +90,9 @@ impl Evaluator<'_> { return Err(MirEvalError::TypeError("simd_shuffle args are not provided")); }; let TyKind::Array(_, index_len) = index.ty.kind(Interner) else { - return Err(MirEvalError::TypeError("simd_shuffle index argument has non-array type")); + return Err(MirEvalError::TypeError( + "simd_shuffle index argument has non-array type", + )); }; let index_len = match try_const_usize(self.db, index_len) { Some(x) => x as usize, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 800e490e9a..41a7738357 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -310,24 +310,30 @@ impl<'ctx> MirLowerCtx<'ctx> { self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest) } Adjust::Deref(_) => { - let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? else { - return Ok(None); - }; + let Some((p, current)) = + self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? + else { + return Ok(None); + }; self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); Ok(Some(current)) } Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => { - let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { - return Ok(None); - }; + let Some((p, current)) = + self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? + else { + return Ok(None); + }; let bk = BorrowKind::from_chalk(*m); self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); Ok(Some(current)) } Adjust::Pointer(cast) => { - let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { - return Ok(None); - }; + let Some((p, current)) = + self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? + else { + return Ok(None); + }; self.push_assignment( current, place, @@ -373,45 +379,49 @@ impl<'ctx> MirLowerCtx<'ctx> { } } Err(MirLowerError::IncompleteExpr) - }, + } Expr::Path(p) => { - let pr = if let Some((assoc, subst)) = self - .infer - .assoc_resolutions_for_expr(expr_id) - { - match assoc { - hir_def::AssocItemId::ConstId(c) => { - self.lower_const(c.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; - return Ok(Some(current)) - }, - hir_def::AssocItemId::FunctionId(_) => { - // FnDefs are zero sized, no action is needed. - return Ok(Some(current)) + let pr = + if let Some((assoc, subst)) = self.infer.assoc_resolutions_for_expr(expr_id) { + match assoc { + hir_def::AssocItemId::ConstId(c) => { + self.lower_const( + c.into(), + current, + place, + subst, + expr_id.into(), + self.expr_ty_without_adjust(expr_id), + )?; + return Ok(Some(current)); + } + hir_def::AssocItemId::FunctionId(_) => { + // FnDefs are zero sized, no action is needed. + return Ok(Some(current)); + } + hir_def::AssocItemId::TypeAliasId(_) => { + // FIXME: If it is unreachable, use proper error instead of `not_supported`. + not_supported!("associated functions and types") + } } - hir_def::AssocItemId::TypeAliasId(_) => { - // FIXME: If it is unreachable, use proper error instead of `not_supported`. - not_supported!("associated functions and types") - }, - } - } else if let Some(variant) = self - .infer - .variant_resolution_for_expr(expr_id) - { - match variant { - VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), - VariantId::StructId(s) => ValueNs::StructId(s), - VariantId::UnionId(_) => implementation_error!("Union variant as path"), - } - } else { - let unresolved_name = || MirLowerError::unresolved_path(self.db, p); - let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); - resolver - .resolve_path_in_value_ns_fully(self.db.upcast(), p) - .ok_or_else(unresolved_name)? - }; + } else if let Some(variant) = self.infer.variant_resolution_for_expr(expr_id) { + match variant { + VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), + VariantId::StructId(s) => ValueNs::StructId(s), + VariantId::UnionId(_) => implementation_error!("Union variant as path"), + } + } else { + let unresolved_name = || MirLowerError::unresolved_path(self.db, p); + let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); + resolver + .resolve_path_in_value_ns_fully(self.db.upcast(), p) + .ok_or_else(unresolved_name)? + }; match pr { ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => { - let Some((temp, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, false)? else { + let Some((temp, current)) = + self.lower_expr_as_place_without_adjust(current, expr_id, false)? + else { return Ok(None); }; self.push_assignment( @@ -423,11 +433,19 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(Some(current)) } ValueNs::ConstId(const_id) => { - self.lower_const(const_id.into(), current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?; + self.lower_const( + const_id.into(), + current, + place, + Substitution::empty(Interner), + expr_id.into(), + self.expr_ty_without_adjust(expr_id), + )?; Ok(Some(current)) } ValueNs::EnumVariantId(variant_id) => { - let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id]; + let variant_data = + &self.db.enum_data(variant_id.parent).variants[variant_id.local_id]; if variant_data.variant_data.kind() == StructKind::Unit { let ty = self.infer.type_of_expr[expr_id].clone(); current = self.lower_enum_variant( @@ -478,7 +496,9 @@ impl<'ctx> MirLowerCtx<'ctx> { } } Expr::If { condition, then_branch, else_branch } => { - let Some((discr, current)) = self.lower_expr_to_some_operand(*condition, current)? else { + let Some((discr, current)) = + self.lower_expr_to_some_operand(*condition, current)? + else { return Ok(None); }; let start_of_then = self.new_basic_block(); @@ -501,15 +521,12 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into())) } Expr::Let { pat, expr } => { - let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else { + let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? + else { return Ok(None); }; - let (then_target, else_target) = self.pattern_match( - current, - None, - cond_place, - *pat, - )?; + let (then_target, else_target) = + self.pattern_match(current, None, cond_place, *pat)?; self.write_bytes_to_place( then_target, place.clone(), @@ -533,31 +550,47 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Block { id: _, statements, tail, label } => { if let Some(label) = label { - self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| { - if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? { - let end = this.current_loop_end()?; - this.set_goto(current, end, expr_id.into()); - } - Ok(()) - }) + self.lower_loop( + current, + place.clone(), + Some(*label), + expr_id.into(), + |this, begin| { + if let Some(current) = this.lower_block_to_place( + statements, + begin, + *tail, + place, + expr_id.into(), + )? { + let end = this.current_loop_end()?; + this.set_goto(current, end, expr_id.into()); + } + Ok(()) + }, + ) } else { self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) } } - Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { - let scope = this.push_drop_scope(); - if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { - current = scope.pop_and_drop(this, current); - this.set_goto(current, begin, expr_id.into()); - } else { - scope.pop_assume_dropped(this); - } - Ok(()) - }), - Expr::While { condition, body, label } => { - self.lower_loop(current, place, *label, expr_id.into(),|this, begin| { + Expr::Loop { body, label } => { + self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { let scope = this.push_drop_scope(); - let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { + if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { + current = scope.pop_and_drop(this, current); + this.set_goto(current, begin, expr_id.into()); + } else { + scope.pop_assume_dropped(this); + } + Ok(()) + }) + } + Expr::While { condition, body, label } => { + self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { + let scope = this.push_drop_scope(); + let Some((discr, to_switch)) = + this.lower_expr_to_some_operand(*condition, begin)? + else { return Ok(()); }; let fail_cond = this.new_basic_block(); @@ -583,8 +616,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }) } Expr::Call { callee, args, .. } => { - if let Some((func_id, generic_args)) = - self.infer.method_resolution(expr_id) { + if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { let ty = chalk_ir::TyKind::FnDef( CallableDefId::FunctionId(func_id).to_chalk(self.db), generic_args, @@ -604,21 +636,43 @@ impl<'ctx> MirLowerCtx<'ctx> { match &callee_ty.data(Interner).kind { chalk_ir::TyKind::FnDef(..) => { let func = Operand::from_bytes(vec![], callee_ty.clone()); - self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) + self.lower_call_and_args( + func, + args.iter().copied(), + place, + current, + self.is_uninhabited(expr_id), + expr_id.into(), + ) } chalk_ir::TyKind::Function(_) => { - let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else { + let Some((func, current)) = + self.lower_expr_to_some_operand(*callee, current)? + else { return Ok(None); }; - self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) + self.lower_call_and_args( + func, + args.iter().copied(), + place, + current, + self.is_uninhabited(expr_id), + expr_id.into(), + ) + } + TyKind::Error => { + return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)) } - TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)), _ => return Err(MirLowerError::TypeError("function call on bad type")), } } Expr::MethodCall { receiver, args, method_name, .. } => { let (func_id, generic_args) = - self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(method_name.display(self.db.upcast()).to_string()))?; + self.infer.method_resolution(expr_id).ok_or_else(|| { + MirLowerError::UnresolvedMethod( + method_name.display(self.db.upcast()).to_string(), + ) + })?; let func = Operand::from_fn(self.db, func_id, generic_args); self.lower_call_and_args( func, @@ -630,23 +684,27 @@ impl<'ctx> MirLowerCtx<'ctx> { ) } Expr::Match { expr, arms } => { - let Some((cond_place, mut current)) = self.lower_expr_as_place(current, *expr, true)? + let Some((cond_place, mut current)) = + self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; let mut end = None; for MatchArm { pat, guard, expr } in arms.iter() { - let (then, mut otherwise) = self.pattern_match( - current, - None, - cond_place.clone(), - *pat, - )?; + let (then, mut otherwise) = + self.pattern_match(current, None, cond_place.clone(), *pat)?; let then = if let &Some(guard) = guard { let next = self.new_basic_block(); let o = otherwise.get_or_insert_with(|| self.new_basic_block()); if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? { - self.set_terminator(c, TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }, expr_id.into()); + self.set_terminator( + c, + TerminatorKind::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, next, *o), + }, + expr_id.into(), + ); } next } else { @@ -672,33 +730,53 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Continue { label } => { let loop_data = match label { - Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?, - None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?, + Some(l) => { + self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)? + } + None => self + .current_loop_blocks + .as_ref() + .ok_or(MirLowerError::ContinueWithoutLoop)?, }; let begin = loop_data.begin; current = self.drop_until_scope(loop_data.drop_scope_index, current); self.set_goto(current, begin, expr_id.into()); Ok(None) - }, + } &Expr::Break { expr, label } => { if let Some(expr) = expr { let loop_data = match label { - Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?, - None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?, + Some(l) => self + .labeled_loop_blocks + .get(&l) + .ok_or(MirLowerError::UnresolvedLabel)?, + None => self + .current_loop_blocks + .as_ref() + .ok_or(MirLowerError::BreakWithoutLoop)?, }; - let Some(c) = self.lower_expr_to_place(expr, loop_data.place.clone(), current)? else { + let Some(c) = + self.lower_expr_to_place(expr, loop_data.place.clone(), current)? + else { return Ok(None); }; current = c; } let (end, drop_scope) = match label { Some(l) => { - let loop_blocks = self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?; - (loop_blocks.end.expect("We always generate end for labeled loops"), loop_blocks.drop_scope_index) - }, - None => { - (self.current_loop_end()?, self.current_loop_blocks.as_ref().unwrap().drop_scope_index) - }, + let loop_blocks = self + .labeled_loop_blocks + .get(&l) + .ok_or(MirLowerError::UnresolvedLabel)?; + ( + loop_blocks.end.expect("We always generate end for labeled loops"), + loop_blocks.drop_scope_index, + ) + } + None => ( + self.current_loop_end()?, + self.current_loop_blocks.as_ref().unwrap().drop_scope_index, + ), }; current = self.drop_until_scope(drop_scope, current); self.set_goto(current, end, expr_id.into()); @@ -706,7 +784,9 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Return { expr } => { if let Some(expr) = expr { - if let Some(c) = self.lower_expr_to_place(*expr, return_slot().into(), current)? { + if let Some(c) = + self.lower_expr_to_place(*expr, return_slot().into(), current)? + { current = c; } else { return Ok(None); @@ -725,13 +805,11 @@ impl<'ctx> MirLowerCtx<'ctx> { }; current = c; Some(p) - }, + } None => None, }; - let variant_id = self - .infer - .variant_resolution_for_expr(expr_id) - .ok_or_else(|| match path { + let variant_id = + self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()), None => MirLowerError::RecordLiteralWithoutPath, })?; @@ -746,7 +824,8 @@ impl<'ctx> MirLowerCtx<'ctx> { for RecordLitField { name, expr } in fields.iter() { let field_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? else { + let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? + else { return Ok(None); }; current = c; @@ -758,18 +837,23 @@ impl<'ctx> MirLowerCtx<'ctx> { Rvalue::Aggregate( AggregateKind::Adt(variant_id, subst), match spread_place { - Some(sp) => operands.into_iter().enumerate().map(|(i, x)| { - match x { + Some(sp) => operands + .into_iter() + .enumerate() + .map(|(i, x)| match x { Some(x) => x, None => { - let p = sp.project(ProjectionElem::Field(FieldId { - parent: variant_id, - local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)), - })); + let p = + sp.project(ProjectionElem::Field(FieldId { + parent: variant_id, + local_id: LocalFieldId::from_raw( + RawIdx::from(i as u32), + ), + })); Operand::Copy(p) - }, - } - }).collect(), + } + }) + .collect(), None => operands.into_iter().collect::>().ok_or( MirLowerError::TypeError("missing field in record literal"), )?, @@ -785,7 +869,10 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let place = place.project(PlaceElem::Field(FieldId { parent: union_id.into(), local_id })); + let place = place.project(PlaceElem::Field(FieldId { + parent: union_id.into(), + local_id, + })); self.lower_expr_to_place(*expr, place, current) } } @@ -795,9 +882,16 @@ impl<'ctx> MirLowerCtx<'ctx> { Expr::Async { .. } => not_supported!("async block"), &Expr::Const(id) => { let subst = self.placeholder_subst(); - self.lower_const(id.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; + self.lower_const( + id.into(), + current, + place, + subst, + expr_id.into(), + self.expr_ty_without_adjust(expr_id), + )?; Ok(Some(current)) - }, + } Expr::Cast { expr, type_ref: _ } => { let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); @@ -822,23 +916,37 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Box { expr } => { let ty = self.expr_ty_after_adjustments(*expr); - self.push_assignment(current, place.clone(), Rvalue::ShallowInitBoxWithAlloc(ty), expr_id.into()); - let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + self.push_assignment( + current, + place.clone(), + Rvalue::ShallowInitBoxWithAlloc(ty), + expr_id.into(), + ); + let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? + else { return Ok(None); }; let p = place.project(ProjectionElem::Deref); self.push_assignment(current, p, operand.into(), expr_id.into()); Ok(Some(current)) - }, - Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => { - let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else { + } + Expr::Field { .. } + | Expr::Index { .. } + | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => { + let Some((p, current)) = + self.lower_expr_as_place_without_adjust(current, expr_id, true)? + else { return Ok(None); }; self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); Ok(Some(current)) } - Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => { - let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + Expr::UnaryOp { + expr, + op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg), + } => { + let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? + else { return Ok(None); }; let operation = match op { @@ -853,7 +961,7 @@ impl<'ctx> MirLowerCtx<'ctx> { expr_id.into(), ); Ok(Some(current)) - }, + } Expr::BinaryOp { lhs, rhs, op } => { let op = op.ok_or(MirLowerError::IncompleteExpr)?; let is_builtin = 'b: { @@ -861,16 +969,19 @@ impl<'ctx> MirLowerCtx<'ctx> { // for binary operator, and use without adjust to simplify our conditions. let lhs_ty = self.expr_ty_without_adjust(*lhs); let rhs_ty = self.expr_ty_without_adjust(*rhs); - if matches!(op ,BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) { + if matches!(op, BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) { if lhs_ty.as_raw_ptr().is_some() && rhs_ty.as_raw_ptr().is_some() { break 'b true; } } let builtin_inequal_impls = matches!( op, - BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } + BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) + | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } ); - lhs_ty.is_scalar() && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls) + lhs_ty.is_scalar() + && rhs_ty.is_scalar() + && (lhs_ty == rhs_ty || builtin_inequal_impls) }; if !is_builtin { if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { @@ -894,32 +1005,43 @@ impl<'ctx> MirLowerCtx<'ctx> { .get(lhs) .and_then(|x| x.split_last()) .map(|x| x.1) - .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?; + .ok_or(MirLowerError::TypeError( + "adjustment of binary op was missing", + ))?; let Some((lhs_place, current)) = self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)? else { return Ok(None); }; - let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + let Some((rhs_op, current)) = + self.lower_expr_to_some_operand(*rhs, current)? + else { return Ok(None); }; - let r_value = Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place.clone()), rhs_op); + let r_value = Rvalue::CheckedBinaryOp( + op.into(), + Operand::Copy(lhs_place.clone()), + rhs_op, + ); self.push_assignment(current, lhs_place, r_value, expr_id.into()); return Ok(Some(current)); } else { let Some((lhs_place, current)) = - self.lower_expr_as_place(current, *lhs, false)? + self.lower_expr_as_place(current, *lhs, false)? else { return Ok(None); }; - let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + let Some((rhs_op, current)) = + self.lower_expr_to_some_operand(*rhs, current)? + else { return Ok(None); }; self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into()); return Ok(Some(current)); } } - let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else { + let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? + else { return Ok(None); }; if let hir_def::hir::BinaryOp::LogicOp(op) = op { @@ -928,22 +1050,31 @@ impl<'ctx> MirLowerCtx<'ctx> { syntax::ast::LogicOp::Or => 1, }; let start_of_then = self.new_basic_block(); - self.push_assignment(start_of_then, place.clone(), lhs_op.clone().into(), expr_id.into()); + self.push_assignment( + start_of_then, + place.clone(), + lhs_op.clone().into(), + expr_id.into(), + ); let end_of_then = Some(start_of_then); let start_of_else = self.new_basic_block(); - let end_of_else = - self.lower_expr_to_place(*rhs, place, start_of_else)?; + let end_of_else = self.lower_expr_to_place(*rhs, place, start_of_else)?; self.set_terminator( current, TerminatorKind::SwitchInt { discr: lhs_op, - targets: SwitchTargets::static_if(value_to_short, start_of_then, start_of_else), + targets: SwitchTargets::static_if( + value_to_short, + start_of_then, + start_of_else, + ), }, expr_id.into(), ); return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into())); } - let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? + else { return Ok(None); }; self.push_assignment( @@ -995,20 +1126,28 @@ impl<'ctx> MirLowerCtx<'ctx> { place, Rvalue::Aggregate( AggregateKind::Adt(st.into(), subst.clone()), - self.db.struct_data(st).variant_data.fields().iter().map(|x| { - let o = match x.1.name.as_str() { - Some("start") => lp.take(), - Some("end") => rp.take(), - Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())), - _ => None, - }; - o.ok_or(MirLowerError::UnresolvedField) - }).collect::>()?, + self.db + .struct_data(st) + .variant_data + .fields() + .iter() + .map(|x| { + let o = match x.1.name.as_str() { + Some("start") => lp.take(), + Some("end") => rp.take(), + Some("exhausted") => { + Some(Operand::from_bytes(vec![0], TyBuilder::bool())) + } + _ => None, + }; + o.ok_or(MirLowerError::UnresolvedField) + }) + .collect::>()?, ), expr_id.into(), ); Ok(Some(current)) - }, + } Expr::Closure { .. } => { let ty = self.expr_ty_without_adjust(expr_id); let TyKind::Closure(id, _) = ty.kind(Interner) else { @@ -1020,22 +1159,33 @@ impl<'ctx> MirLowerCtx<'ctx> { for capture in captures.iter() { let p = Place { local: self.binding_local(capture.place.local)?, - projection: capture.place.projections.clone().into_iter().map(|x| { - match x { + projection: capture + .place + .projections + .clone() + .into_iter() + .map(|x| match x { ProjectionElem::Deref => ProjectionElem::Deref, ProjectionElem::Field(x) => ProjectionElem::Field(x), - ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x), - ProjectionElem::ConstantIndex { offset, from_end } => ProjectionElem::ConstantIndex { offset, from_end }, - ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to }, + ProjectionElem::TupleOrClosureField(x) => { + ProjectionElem::TupleOrClosureField(x) + } + ProjectionElem::ConstantIndex { offset, from_end } => { + ProjectionElem::ConstantIndex { offset, from_end } + } + ProjectionElem::Subslice { from, to } => { + ProjectionElem::Subslice { from, to } + } ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x), - ProjectionElem::Index(x) => match x { }, - } - }).collect(), + ProjectionElem::Index(x) => match x {}, + }) + .collect(), }; match &capture.kind { CaptureKind::ByRef(bk) => { let placeholder_subst = self.placeholder_subst(); - let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst); + let tmp_ty = + capture.ty.clone().substitute(Interner, &placeholder_subst); let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into(); self.push_assignment( current, @@ -1044,7 +1194,7 @@ impl<'ctx> MirLowerCtx<'ctx> { capture.span, ); operands.push(Operand::Move(tmp)); - }, + } CaptureKind::ByValue => operands.push(Operand::Move(p)), } } @@ -1055,18 +1205,18 @@ impl<'ctx> MirLowerCtx<'ctx> { expr_id.into(), ); Ok(Some(current)) - }, + } Expr::Tuple { exprs, is_assignee_expr: _ } => { let Some(values) = exprs - .iter() - .map(|x| { - let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { - return Ok(None); - }; - current = c; - Ok(Some(o)) - }) - .collect::>>()? + .iter() + .map(|x| { + let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { + return Ok(None); + }; + current = c; + Ok(Some(o)) + }) + .collect::>>()? else { return Ok(None); }; @@ -1088,27 +1238,26 @@ impl<'ctx> MirLowerCtx<'ctx> { } }; let Some(values) = elements - .iter() - .map(|x| { - let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { - return Ok(None); - }; - current = c; - Ok(Some(o)) - }) - .collect::>>()? + .iter() + .map(|x| { + let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { + return Ok(None); + }; + current = c; + Ok(Some(o)) + }) + .collect::>>()? else { return Ok(None); }; - let r = Rvalue::Aggregate( - AggregateKind::Array(elem_ty), - values, - ); + let r = Rvalue::Aggregate(AggregateKind::Array(elem_ty), values); self.push_assignment(current, place, r, expr_id.into()); Ok(Some(current)) } Array::Repeat { initializer, .. } => { - let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else { + let Some((init, current)) = + self.lower_expr_to_some_operand(*initializer, current)? + else { return Ok(None); }; let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind { @@ -1122,7 +1271,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let r = Rvalue::Repeat(init, len); self.push_assignment(current, place, r, expr_id.into()); Ok(Some(current)) - }, + } }, Expr::Literal(l) => { let ty = self.expr_ty_without_adjust(expr_id); diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index d2c8d9a089..79f6da48ce 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -18,7 +18,9 @@ impl MirLowerCtx<'_> { ) -> Result> { let ty = self.expr_ty_without_adjust(expr_id); let place = self.temp(ty, prev_block, expr_id.into())?; - let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else { + let Some(current) = + self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? + else { return Ok(None); }; Ok(Some((place.into(), current))) @@ -35,7 +37,9 @@ impl MirLowerCtx<'_> { .map(|x| x.target.clone()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); let place = self.temp(ty, prev_block, expr_id.into())?; - let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else { + let Some(current) = + self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? + else { return Ok(None); }; Ok(Some((place.into(), current))) @@ -62,7 +66,8 @@ impl MirLowerCtx<'_> { expr_id, upgrade_rvalue, rest, - )? else { + )? + else { return Ok(None); }; x.0 = x.0.project(ProjectionElem::Deref); @@ -74,7 +79,8 @@ impl MirLowerCtx<'_> { expr_id, upgrade_rvalue, rest, - )? else { + )? + else { return Ok(None); }; self.lower_overloaded_deref( @@ -165,7 +171,8 @@ impl MirLowerCtx<'_> { _ => false, }; if !is_builtin { - let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else { + let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? + else { return Ok(None); }; return self.lower_overloaded_deref( @@ -192,7 +199,8 @@ impl MirLowerCtx<'_> { }, ); } - let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { + let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? + else { return Ok(None); }; r = r.project(ProjectionElem::Deref); @@ -217,12 +225,18 @@ impl MirLowerCtx<'_> { ) { let Some(index_fn) = self.infer.method_resolution(expr_id) else { - return Err(MirLowerError::UnresolvedMethod("[overloaded index]".to_string())); + return Err(MirLowerError::UnresolvedMethod( + "[overloaded index]".to_string(), + )); }; - let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + let Some((base_place, current)) = + self.lower_expr_as_place(current, *base, true)? + else { return Ok(None); }; - let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else { + let Some((index_operand, current)) = + self.lower_expr_to_some_operand(*index, current)? + else { return Ok(None); }; return self.lower_overloaded_index( @@ -249,7 +263,8 @@ impl MirLowerCtx<'_> { }; let l_index = self.temp(self.expr_ty_after_adjustments(*index), current, expr_id.into())?; - let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else { + let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? + else { return Ok(None); }; p_base = p_base.project(ProjectionElem::Index(l_index)); @@ -282,7 +297,15 @@ impl MirLowerCtx<'_> { ) .intern(Interner), ); - let Some(current) = self.lower_call(index_fn_op, Box::new([Operand::Copy(place), index_operand]), result.clone(), current, false, span)? else { + let Some(current) = self.lower_call( + index_fn_op, + Box::new([Operand::Copy(place), index_operand]), + result.clone(), + current, + false, + span, + )? + else { return Ok(None); }; result = result.project(ProjectionElem::Deref); @@ -329,7 +352,15 @@ impl MirLowerCtx<'_> { .intern(Interner), ); let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); - let Some(current) = self.lower_call(deref_fn_op, Box::new([Operand::Copy(ref_place)]), result.clone(), current, false, span)? else { + let Some(current) = self.lower_call( + deref_fn_op, + Box::new([Operand::Copy(ref_place)]), + result.clone(), + current, + false, + span, + )? + else { return Ok(None); }; result = result.project(ProjectionElem::Deref); diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 8e5514353d..d22d0d85c8 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -206,7 +206,9 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour let Some(node) = (match expr_or_pat { hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db), hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db), - }) else { continue; }; + }) else { + continue; + }; let range = node.as_ref().original_file_range(&db); let actual = format!( "expected {}, got {}", diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7d07e9cce4..9082568436 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -701,9 +701,9 @@ fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec let id = macro_id_to_def_id(db.upcast(), m.id); if let Err(e) = db.macro_def(id) { let Some(ast) = id.ast_id().left() else { - never!("MacroDefError for proc-macro: {:?}", e); - return; - }; + never!("MacroDefError for proc-macro: {:?}", e); + return; + }; emit_def_diagnostic_( db, acc, diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 0c46e2a4fe..39a3e1c448 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1494,7 +1494,11 @@ impl<'db> SemanticsImpl<'db> { } fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool { - let Some(enclosing_item) = expr.syntax().ancestors().find_map(Either::::cast) else { return false }; + let Some(enclosing_item) = + expr.syntax().ancestors().find_map(Either::::cast) + else { + return false; + }; let def = match &enclosing_item { Either::Left(ast::Item::Fn(it)) if it.unsafe_token().is_some() => return true, diff --git a/crates/ide-assists/src/handlers/desugar_doc_comment.rs b/crates/ide-assists/src/handlers/desugar_doc_comment.rs index 226a5dd9fa..ddc8a50ed4 100644 --- a/crates/ide-assists/src/handlers/desugar_doc_comment.rs +++ b/crates/ide-assists/src/handlers/desugar_doc_comment.rs @@ -27,7 +27,9 @@ use crate::{ pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let comment = ctx.find_token_at_offset::()?; // Only allow doc comments - let Some(placement) = comment.kind().doc else { return None; }; + let Some(placement) = comment.kind().doc else { + return None; + }; // Only allow comments which are alone on their line if let Some(prev) = comment.syntax().prev_token() { diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs index d6c59a9c82..4a2ab18c98 100644 --- a/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/crates/ide-assists/src/handlers/fix_visibility.rs @@ -40,12 +40,16 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) let qualifier = path.qualifier()?; let name_ref = path.segment()?.name_ref()?; let qualifier_res = ctx.sema.resolve_path(&qualifier)?; - let PathResolution::Def(ModuleDef::Module(module)) = qualifier_res else { return None; }; + let PathResolution::Def(ModuleDef::Module(module)) = qualifier_res else { + return None; + }; let (_, def) = module .scope(ctx.db(), None) .into_iter() .find(|(name, _)| name.to_smol_str() == name_ref.text().as_str())?; - let ScopeDef::ModuleDef(def) = def else { return None; }; + let ScopeDef::ModuleDef(def) = def else { + return None; + }; let current_module = ctx.sema.scope(path.syntax())?.module(); let target_module = def.module(ctx.db())?; diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index b68c766e64..1c2ec79809 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -88,7 +88,9 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let adt = ast::Adt::Struct(strukt.clone()); let name = name.display(ctx.db()).to_string(); // if `find_struct_impl` returns None, that means that a function named `name` already exists. - let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { continue; }; + let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { + continue; + }; acc.add_group( &GroupLabel("Generate delegate methods…".to_owned()), AssistId("generate_delegate_methods", AssistKind::Generate), diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index c579f6780d..8085572497 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -623,7 +623,9 @@ fn fn_generic_params( fn params_and_where_preds_in_scope( ctx: &AssistContext<'_>, ) -> (Vec, Vec) { - let Some(body) = containing_body(ctx) else { return Default::default(); }; + let Some(body) = containing_body(ctx) else { + return Default::default(); + }; let mut generic_params = Vec::new(); let mut where_clauses = Vec::new(); diff --git a/crates/ide-assists/src/handlers/move_const_to_impl.rs b/crates/ide-assists/src/handlers/move_const_to_impl.rs index b6027eac55..e1849eb71d 100644 --- a/crates/ide-assists/src/handlers/move_const_to_impl.rs +++ b/crates/ide-assists/src/handlers/move_const_to_impl.rs @@ -54,7 +54,11 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> // NOTE: We can technically provide this assist for default methods in trait definitions, but // it's somewhat complex to handle it correctly when the const's name conflicts with // supertrait's item. We may want to consider implementing it in the future. - let AssocItemContainer::Impl(impl_) = ctx.sema.to_def(&parent_fn)?.as_assoc_item(db)?.container(db) else { return None; }; + let AssocItemContainer::Impl(impl_) = + ctx.sema.to_def(&parent_fn)?.as_assoc_item(db)?.container(db) + else { + return None; + }; if impl_.trait_(db).is_some() { return None; } diff --git a/crates/ide-assists/src/handlers/unwrap_result_return_type.rs b/crates/ide-assists/src/handlers/unwrap_result_return_type.rs index 26f3c19261..f235b554e6 100644 --- a/crates/ide-assists/src/handlers/unwrap_result_return_type.rs +++ b/crates/ide-assists/src/handlers/unwrap_result_return_type.rs @@ -38,14 +38,18 @@ pub(crate) fn unwrap_result_return_type(acc: &mut Assists, ctx: &AssistContext<' }; let type_ref = &ret_type.ty()?; - let Some(hir::Adt::Enum(ret_enum)) = ctx.sema.resolve_type(type_ref)?.as_adt() else { return None; }; + let Some(hir::Adt::Enum(ret_enum)) = ctx.sema.resolve_type(type_ref)?.as_adt() else { + return None; + }; let result_enum = FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?; if ret_enum != result_enum { return None; } - let Some(ok_type) = unwrap_result_type(type_ref) else { return None; }; + let Some(ok_type) = unwrap_result_type(type_ref) else { + return None; + }; acc.add( AssistId("unwrap_result_return_type", AssistKind::RefactorRewrite), @@ -130,12 +134,16 @@ fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { // Tries to extract `T` from `Result`. fn unwrap_result_type(ty: &ast::Type) -> Option { - let ast::Type::PathType(path_ty) = ty else { return None; }; + let ast::Type::PathType(path_ty) = ty else { + return None; + }; let path = path_ty.path()?; let segment = path.first_segment()?; let generic_arg_list = segment.generic_arg_list()?; let generic_args: Vec<_> = generic_arg_list.generic_args().collect(); - let ast::GenericArg::TypeArg(ok_type) = generic_args.first()? else { return None; }; + let ast::GenericArg::TypeArg(ok_type) = generic_args.first()? else { + return None; + }; ok_type.ty() } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 7b145f3c14..3cb65b2729 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -463,7 +463,9 @@ impl CompletionContext<'_> { /// Checks whether this item should be listed in regards to stability. Returns `true` if we should. pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool { - let Some(attrs) = attrs else { return true; }; + let Some(attrs) = attrs else { + return true; + }; !attrs.is_unstable() || self.is_nightly } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index cc5221cfcc..932233ad63 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -243,10 +243,7 @@ fn analyze( let Some(name_like) = find_node_at_offset(&speculative_file, offset) else { let analysis = if let Some(original) = ast::String::cast(original_token.clone()) { - CompletionAnalysis::String { - original, - expanded: ast::String::cast(self_token.clone()), - } + CompletionAnalysis::String { original, expanded: ast::String::cast(self_token.clone()) } } else { // Fix up trailing whitespace problem // #[attr(foo = $0 diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 271e7ce73b..7e0d953281 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -119,10 +119,11 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option> { stack.pop(); 'crates: for &krate in ctx.sema.db.relevant_crates(parent_id).iter() { let crate_def_map = ctx.sema.db.crate_def_map(krate); - let Some((_, module)) = - crate_def_map.modules() - .find(|(_, module)| module.origin.file_id() == Some(parent_id) && !module.origin.is_inline()) - else { continue }; + let Some((_, module)) = crate_def_map.modules().find(|(_, module)| { + module.origin.file_id() == Some(parent_id) && !module.origin.is_inline() + }) else { + continue; + }; if stack.is_empty() { return make_fixes( diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 8112c4f725..c90ba21253 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -330,7 +330,9 @@ fn get_doc_links( base_url.and_then(|url| url.join(path).ok()) }; - let Some((target, file, frag)) = filename_and_frag_for_def(db, def) else { return Default::default(); }; + let Some((target, file, frag)) = filename_and_frag_for_def(db, def) else { + return Default::default(); + }; let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot); diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 10bee2a6ac..6d6bd315eb 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -259,7 +259,7 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, } })() else { never!("broken syntax tree?\n{:?}\n{:?}", expr, dummy_expr); - return (true, true) + return (true, true); }; // At this point diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 1c09af9abd..e2db1957ea 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -268,10 +268,12 @@ impl flags::AnalysisStats { continue; } all += 1; - let Err(e) - = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner), a.krate(db).into()) - else { - continue + let Err(e) = db.layout_of_adt( + hir_def::AdtId::from(a).into(), + Substitution::empty(Interner), + a.krate(db).into(), + ) else { + continue; }; if verbosity.is_spammy() { let full_name = full_name_of_item(db, a.module(db), a.name(db)); diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 69799a54cb..bb0c7ffa72 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1331,7 +1331,9 @@ pub(crate) fn handle_code_lens_resolve( snap: GlobalStateSnapshot, code_lens: CodeLens, ) -> anyhow::Result { - let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { return Ok(code_lens) }; + let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { + return Ok(code_lens); + }; let annotation = snap.analysis.resolve_annotation(annotation)?; let mut acc = Vec::new(); @@ -1632,9 +1634,9 @@ pub(crate) fn handle_open_docs( let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else { return if snap.config.local_docs() { Ok(ExternalDocsResponse::WithLocal(Default::default())) - } else { + } else { Ok(ExternalDocsResponse::Simple(None)) - } + }; }; let web = remote_urls.web_url.and_then(|it| Url::parse(&it).ok()); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index dd5517e697..18d9151d4a 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -334,7 +334,11 @@ impl GlobalState { let _p = profile::span("GlobalState::switch_workspaces"); tracing::info!(%cause, "will switch workspaces"); - let Some((workspaces, force_reload_crate_graph)) = self.fetch_workspaces_queue.last_op_result() else { return; }; + let Some((workspaces, force_reload_crate_graph)) = + self.fetch_workspaces_queue.last_op_result() + else { + return; + }; if let Err(_) = self.fetch_workspace_error() { if !self.workspaces.is_empty() { @@ -525,7 +529,7 @@ impl GlobalState { let mut buf = String::new(); let Some((last_op_result, _)) = self.fetch_workspaces_queue.last_op_result() else { - return Ok(()) + return Ok(()); }; if last_op_result.is_empty() { stdx::format_to!(buf, "rust-analyzer failed to discover workspace"); diff --git a/crates/sourcegen/src/lib.rs b/crates/sourcegen/src/lib.rs index c5da6ceb4d..1514c6c7d4 100644 --- a/crates/sourcegen/src/lib.rs +++ b/crates/sourcegen/src/lib.rs @@ -61,7 +61,9 @@ impl CommentBlock { let mut blocks = CommentBlock::extract_untagged(text); blocks.retain_mut(|block| { let first = block.contents.remove(0); - let Some(id) = first.strip_prefix(&tag) else { return false; }; + let Some(id) = first.strip_prefix(&tag) else { + return false; + }; if block.is_doc { panic!("Use plain (non-doc) comments with tags like {tag}:\n {first}"); From 8886d707b84e3623211b82841f7539b570479d4f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 4 Jul 2023 09:16:15 +0200 Subject: [PATCH 111/201] Stronger typing for AstId and AstIdMap --- crates/hir-def/src/data.rs | 2 +- crates/hir-def/src/data/adt.rs | 10 +-- crates/hir-def/src/item_tree.rs | 4 +- crates/hir-def/src/lib.rs | 6 +- crates/hir-def/src/lower.rs | 8 ++- crates/hir-def/src/nameres/collector.rs | 2 +- crates/hir-def/src/nameres/diagnostics.rs | 11 ++-- crates/hir-expand/src/ast_id_map.rs | 79 +++++++++++++++++------ crates/hir-expand/src/lib.rs | 28 +++++--- crates/hir/src/lib.rs | 2 +- 10 files changed, 102 insertions(+), 50 deletions(-) diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 4a9f08dca6..54fe9a2e84 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -599,7 +599,7 @@ impl<'a> AssocItemCollector<'a> { if !attrs.is_cfg_enabled(self.expander.cfg_options()) { self.diagnostics.push(DefDiagnostic::unconfigured_code( self.module_id.local_id, - InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()), + InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).erase()), attrs.cfg().unwrap(), self.expander.cfg_options().clone(), )); diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 612a5ca1a2..595b9b770b 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -330,7 +330,7 @@ impl EnumData { } else { diagnostics.push(DefDiagnostic::unconfigured_code( loc.container.local_id, - InFile::new(loc.id.file_id(), var.ast_id.upcast()), + InFile::new(loc.id.file_id(), var.ast_id.erase()), attrs.cfg().unwrap(), cfg_options.clone(), )) @@ -540,8 +540,8 @@ fn lower_fields( InFile::new( current_file_id, match field.ast_id { - FieldAstId::Record(it) => it.upcast(), - FieldAstId::Tuple(it) => it.upcast(), + FieldAstId::Record(it) => it.erase(), + FieldAstId::Tuple(it) => it.erase(), }, ), attrs.cfg().unwrap(), @@ -564,8 +564,8 @@ fn lower_fields( InFile::new( current_file_id, match field.ast_id { - FieldAstId::Record(it) => it.upcast(), - FieldAstId::Tuple(it) => it.upcast(), + FieldAstId::Record(it) => it.erase(), + FieldAstId::Tuple(it) => it.erase(), }, ), attrs.cfg().unwrap(), diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index e74b71888c..6f80bb6e07 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -46,7 +46,7 @@ use ast::{AstNode, HasName, StructKind}; use base_db::CrateId; use either::Either; use hir_expand::{ - ast_id_map::FileAstId, + ast_id_map::{AstIdNode, FileAstId}, attrs::RawAttrs, hygiene::Hygiene, name::{name, AsName, Name}, @@ -314,7 +314,7 @@ from_attrs!(ModItem(ModItem), Variant(Idx), Field(Idx), Param(Id /// Trait implemented by all item nodes in the item tree. pub trait ItemTreeNode: Clone { - type Source: AstNode + Into; + type Source: AstIdNode + Into; fn ast_id(&self) -> FileAstId; diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index e5149ab577..c0701e2576 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -64,7 +64,7 @@ use std::{ use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind}; use hir_expand::{ - ast_id_map::FileAstId, + ast_id_map::{AstIdNode, FileAstId}, attrs::{Attr, AttrId, AttrInput}, builtin_attr_macro::BuiltinAttrExpander, builtin_derive_macro::BuiltinDeriveExpander, @@ -1124,12 +1124,12 @@ impl AsMacroCall for InFile<&ast::MacroCall> { /// Helper wrapper for `AstId` with `ModPath` #[derive(Clone, Debug, Eq, PartialEq)] -struct AstIdWithPath { +struct AstIdWithPath { ast_id: AstId, path: path::ModPath, } -impl AstIdWithPath { +impl AstIdWithPath { fn new(file_id: HirFileId, ast_id: FileAstId, path: path::ModPath) -> AstIdWithPath { AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path } } diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs index af623fd0e5..e523c22917 100644 --- a/crates/hir-def/src/lower.rs +++ b/crates/hir-def/src/lower.rs @@ -1,5 +1,9 @@ //! Context for lowering paths. -use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile}; +use hir_expand::{ + ast_id_map::{AstIdMap, AstIdNode}, + hygiene::Hygiene, + AstId, HirFileId, InFile, +}; use once_cell::unsync::OnceCell; use syntax::ast; use triomphe::Arc; @@ -37,7 +41,7 @@ impl<'a> LowerCtx<'a> { Path::from_src(ast, self) } - pub(crate) fn ast_id(&self, item: &N) -> Option> { + pub(crate) fn ast_id(&self, item: &N) -> Option> { let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?; let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id)); Some(InFile::new(file_id, ast_id_map.ast_id(item))) diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index a5cdee9628..c048716d74 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -2280,7 +2280,7 @@ impl ModCollector<'_, '_> { fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) { let ast_id = item.ast_id(self.item_tree); - let ast_id = InFile::new(self.file_id(), ast_id.upcast()); + let ast_id = InFile::new(self.file_id(), ast_id.erase()); self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( self.module_id, ast_id, diff --git a/crates/hir-def/src/nameres/diagnostics.rs b/crates/hir-def/src/nameres/diagnostics.rs index 18b424255c..e82e97b628 100644 --- a/crates/hir-def/src/nameres/diagnostics.rs +++ b/crates/hir-def/src/nameres/diagnostics.rs @@ -2,12 +2,9 @@ use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; -use hir_expand::{attrs::AttrId, MacroCallKind}; +use hir_expand::{attrs::AttrId, ErasedAstId, MacroCallKind}; use la_arena::Idx; -use syntax::{ - ast::{self, AnyHasAttrs}, - SyntaxError, -}; +use syntax::{ast, SyntaxError}; use crate::{ item_tree::{self, ItemTreeId}, @@ -24,7 +21,7 @@ pub enum DefDiagnosticKind { UnresolvedImport { id: ItemTreeId, index: Idx }, - UnconfiguredCode { ast: AstId, cfg: CfgExpr, opts: CfgOptions }, + UnconfiguredCode { ast: ErasedAstId, cfg: CfgExpr, opts: CfgOptions }, UnresolvedProcMacro { ast: MacroCallKind, krate: CrateId }, @@ -81,7 +78,7 @@ impl DefDiagnostic { pub fn unconfigured_code( container: LocalModuleId, - ast: AstId, + ast: ErasedAstId, cfg: CfgExpr, opts: CfgOptions, ) -> Self { diff --git a/crates/hir-expand/src/ast_id_map.rs b/crates/hir-expand/src/ast_id_map.rs index c2b0d5985e..1906ed15ba 100644 --- a/crates/hir-expand/src/ast_id_map.rs +++ b/crates/hir-expand/src/ast_id_map.rs @@ -18,47 +18,89 @@ use rustc_hash::FxHasher; use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; /// `AstId` points to an AST node in a specific file. -pub struct FileAstId { +pub struct FileAstId { raw: ErasedFileAstId, covariant: PhantomData N>, } -impl Clone for FileAstId { +impl Clone for FileAstId { fn clone(&self) -> FileAstId { *self } } -impl Copy for FileAstId {} +impl Copy for FileAstId {} -impl PartialEq for FileAstId { +impl PartialEq for FileAstId { fn eq(&self, other: &Self) -> bool { self.raw == other.raw } } -impl Eq for FileAstId {} -impl Hash for FileAstId { +impl Eq for FileAstId {} +impl Hash for FileAstId { fn hash(&self, hasher: &mut H) { self.raw.hash(hasher); } } -impl fmt::Debug for FileAstId { +impl fmt::Debug for FileAstId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "FileAstId::<{}>({})", type_name::(), self.raw.into_raw()) } } -impl FileAstId { +impl FileAstId { // Can't make this a From implementation because of coherence - pub fn upcast(self) -> FileAstId + pub fn upcast(self) -> FileAstId where N: Into, { FileAstId { raw: self.raw, covariant: PhantomData } } + + pub fn erase(self) -> ErasedFileAstId { + self.raw + } } -type ErasedFileAstId = Idx; +pub type ErasedFileAstId = Idx; + +pub trait AstIdNode: AstNode {} +macro_rules! register_ast_id_node { + (impl AstIdNode for $($ident:ident),+ ) => { + $( + impl AstIdNode for ast::$ident {} + )+ + fn should_alloc_id(kind: syntax::SyntaxKind) -> bool { + $( + ast::$ident::can_cast(kind) + )||+ + } + }; +} +register_ast_id_node! { + impl AstIdNode for + Item, + Adt, + Enum, + Struct, + Union, + Const, + ExternBlock, + ExternCrate, + Fn, + Impl, + Macro, + MacroDef, + MacroRules, + MacroCall, + Module, + Static, + Trait, + TraitAlias, + TypeAlias, + Use, + AssocItem, BlockExpr, Variant, RecordField, TupleField, ConstArg +} /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. #[derive(Default)] @@ -92,14 +134,7 @@ impl AstIdMap { // change parent's id. This means that, say, adding a new function to a // trait does not change ids of top-level items, which helps caching. bdfs(node, |it| { - let kind = it.kind(); - if ast::Item::can_cast(kind) - || ast::BlockExpr::can_cast(kind) - || ast::Variant::can_cast(kind) - || ast::RecordField::can_cast(kind) - || ast::TupleField::can_cast(kind) - || ast::ConstArg::can_cast(kind) - { + if should_alloc_id(it.kind()) { res.alloc(&it); true } else { @@ -120,15 +155,19 @@ impl AstIdMap { res } - pub fn ast_id(&self, item: &N) -> FileAstId { + pub fn ast_id(&self, item: &N) -> FileAstId { let raw = self.erased_ast_id(item.syntax()); FileAstId { raw, covariant: PhantomData } } - pub fn get(&self, id: FileAstId) -> AstPtr { + pub fn get(&self, id: FileAstId) -> AstPtr { AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap() } + pub(crate) fn get_raw(&self, id: ErasedFileAstId) -> SyntaxNodePtr { + self.arena[id].clone() + } + fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId { let ptr = SyntaxNodePtr::new(item); let hash = hash_ptr(&ptr); diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index b0dc8e1b5c..3a534f56e4 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -37,11 +37,11 @@ use either::Either; use syntax::{ algo::{self, skip_trivia_token}, ast::{self, AstNode, HasDocComments}, - Direction, SyntaxNode, SyntaxToken, + AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, }; use crate::{ - ast_id_map::FileAstId, + ast_id_map::{AstIdNode, ErasedFileAstId, FileAstId}, attrs::AttrId, builtin_attr_macro::BuiltinAttrExpander, builtin_derive_macro::BuiltinDeriveExpander, @@ -551,9 +551,9 @@ impl MacroCallKind { }; let range = match kind { - MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(), - MacroCallKind::Derive { ast_id, .. } => ast_id.to_node(db).syntax().text_range(), - MacroCallKind::Attr { ast_id, .. } => ast_id.to_node(db).syntax().text_range(), + MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(), + MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(), + MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(), }; FileRange { range, file_id } @@ -801,13 +801,25 @@ impl ExpansionInfo { /// It is stable across reparses, and can be used as salsa key/value. pub type AstId = InFile>; -impl AstId { +impl AstId { pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N { - let root = db.parse_or_expand(self.file_id); - db.ast_id_map(self.file_id).get(self.value).to_node(&root) + self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) + } + pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> AstPtr { + db.ast_id_map(self.file_id).get(self.value) } } +pub type ErasedAstId = InFile; + +impl ErasedAstId { + pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode { + self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) + } + pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr { + db.ast_id_map(self.file_id).get_raw(self.value) + } +} /// `InFile` stores a value of `T` inside a particular file/syntax tree. /// /// Typical usages are: diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 645ea42f53..3688fd0e83 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -754,7 +754,7 @@ fn emit_def_diagnostic_( let item = ast.to_node(db.upcast()); acc.push( InactiveCode { - node: ast.with_value(AstPtr::new(&item).into()), + node: ast.with_value(SyntaxNodePtr::new(&item).into()), cfg: cfg.clone(), opts: opts.clone(), } From 7b36a7335101712df2196abdcbafa14b6934b113 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 4 Jul 2023 10:38:23 +0200 Subject: [PATCH 112/201] internal: Remove CfgExpander --- crates/hir-def/src/data/adt.rs | 120 +++++++++++++++++++++++---------- crates/hir-def/src/expander.rs | 57 +++++----------- 2 files changed, 100 insertions(+), 77 deletions(-) diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 595b9b770b..c8df3f3f96 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -18,7 +18,6 @@ use triomphe::Arc; use crate::{ builtin_type::{BuiltinInt, BuiltinUint}, db::DefDatabase, - expander::CfgExpander, item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, lang_item::LangItem, lower::LowerCtx, @@ -29,8 +28,8 @@ use crate::{ tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}, type_ref::TypeRef, visibility::RawVisibility, - EnumId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, UnionId, - VariantId, + EnumId, EnumLoc, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, + UnionId, VariantId, }; /// Note that we use `StructData` for unions as well! @@ -76,6 +75,7 @@ pub struct EnumData { pub struct EnumVariantData { pub name: Name, pub variant_data: Arc, + pub tree_id: la_arena::Idx, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -326,6 +326,7 @@ impl EnumData { variants.alloc(EnumVariantData { name: var.name.clone(), variant_data: Arc::new(var_data), + tree_id, }); } else { diagnostics.push(DefDiagnostic::unconfigured_code( @@ -368,9 +369,10 @@ impl HasChildSource for EnumId { &self, db: &dyn DefDatabase, ) -> InFile> { - let src = self.lookup(db).source(db); + let loc = &self.lookup(db); + let src = loc.source(db); let mut trace = Trace::new_for_map(); - lower_enum(db, &mut trace, &src, self.lookup(db).container); + lower_enum(db, &mut trace, &src, loc); src.with_value(trace.into_map()) } } @@ -379,31 +381,58 @@ fn lower_enum( db: &dyn DefDatabase, trace: &mut Trace, ast: &InFile, - module_id: ModuleId, + loc: &EnumLoc, ) { - let expander = CfgExpander::new(db, ast.file_id, module_id.krate); + let item_tree = loc.id.item_tree(db); + let krate = loc.container.krate; + + let item_tree_variants = item_tree[loc.id.value].variants.clone(); + + let cfg_options = &db.crate_graph()[krate].cfg_options; let variants = ast .value .variant_list() .into_iter() .flat_map(|it| it.variants()) - .filter(|var| expander.is_cfg_enabled(db, var)); - for var in variants { + .zip(item_tree_variants) + .filter(|&(_, item_tree_id)| { + item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) + }); + for (var, item_tree_id) in variants { trace.alloc( || var.clone(), || EnumVariantData { name: var.name().map_or_else(Name::missing, |it| it.as_name()), - variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)), + variant_data: Arc::new(VariantData::new( + db, + ast.with_value(var.kind()), + loc.container, + &item_tree, + item_tree_id, + )), + tree_id: item_tree_id, }, ); } } impl VariantData { - fn new(db: &dyn DefDatabase, flavor: InFile, module_id: ModuleId) -> Self { - let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate); + fn new( + db: &dyn DefDatabase, + flavor: InFile, + module_id: ModuleId, + item_tree: &ItemTree, + variant: la_arena::Idx, + ) -> Self { let mut trace = Trace::new_for_arena(); - match lower_struct(db, &mut expander, &mut trace, &flavor) { + match lower_struct( + db, + &mut trace, + &flavor, + module_id.krate, + item_tree, + &item_tree[variant].fields, + ) { StructKind::Tuple => VariantData::Tuple(trace.into_arena()), StructKind::Record => VariantData::Record(trace.into_arena()), StructKind::Unit => VariantData::Unit, @@ -435,28 +464,43 @@ impl HasChildSource for VariantId { type Value = Either; fn child_source(&self, db: &dyn DefDatabase) -> InFile> { - let (src, module_id) = match self { + let item_tree; + let (src, fields, container) = match *self { VariantId::EnumVariantId(it) => { // I don't really like the fact that we call into parent source // here, this might add to more queries then necessary. + let lookup = it.parent.lookup(db); + item_tree = lookup.id.item_tree(db); let src = it.parent.child_source(db); - (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container) + let tree_id = db.enum_data(it.parent).variants[it.local_id].tree_id; + let fields = &item_tree[tree_id].fields; + (src.map(|map| map[it.local_id].kind()), fields, lookup.container) } VariantId::StructId(it) => { - (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container) + let lookup = it.lookup(db); + item_tree = lookup.id.item_tree(db); + ( + lookup.source(db).map(|it| it.kind()), + &item_tree[lookup.id.value].fields, + lookup.container, + ) + } + VariantId::UnionId(it) => { + let lookup = it.lookup(db); + item_tree = lookup.id.item_tree(db); + ( + lookup.source(db).map(|it| { + it.record_field_list() + .map(ast::StructKind::Record) + .unwrap_or(ast::StructKind::Unit) + }), + &item_tree[lookup.id.value].fields, + lookup.container, + ) } - VariantId::UnionId(it) => ( - it.lookup(db).source(db).map(|it| { - it.record_field_list() - .map(ast::StructKind::Record) - .unwrap_or(ast::StructKind::Unit) - }), - it.lookup(db).container, - ), }; - let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); let mut trace = Trace::new_for_map(); - lower_struct(db, &mut expander, &mut trace, &src); + lower_struct(db, &mut trace, &src, container.krate, &item_tree, fields); src.with_value(trace.into_map()) } } @@ -470,16 +514,19 @@ pub enum StructKind { fn lower_struct( db: &dyn DefDatabase, - expander: &mut CfgExpander, trace: &mut Trace>, ast: &InFile, + krate: CrateId, + item_tree: &ItemTree, + fields: &Fields, ) -> StructKind { - let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id); + let ctx = LowerCtx::with_file_id(db, ast.file_id); - match &ast.value { - ast::StructKind::Tuple(fl) => { - for (i, fd) in fl.fields().enumerate() { - if !expander.is_cfg_enabled(db, &fd) { + match (&ast.value, fields) { + (ast::StructKind::Tuple(fl), Fields::Tuple(fields)) => { + let cfg_options = &db.crate_graph()[krate].cfg_options; + for ((i, fd), item_tree_id) in fl.fields().enumerate().zip(fields.clone()) { + if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) { continue; } @@ -494,9 +541,10 @@ fn lower_struct( } StructKind::Tuple } - ast::StructKind::Record(fl) => { - for fd in fl.fields() { - if !expander.is_cfg_enabled(db, &fd) { + (ast::StructKind::Record(fl), Fields::Record(fields)) => { + let cfg_options = &db.crate_graph()[krate].cfg_options; + for (fd, item_tree_id) in fl.fields().zip(fields.clone()) { + if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) { continue; } @@ -511,7 +559,7 @@ fn lower_struct( } StructKind::Record } - ast::StructKind::Unit => StructKind::Unit, + _ => StructKind::Unit, } } diff --git a/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs index a588827c8d..cc85bd893a 100644 --- a/crates/hir-def/src/expander.rs +++ b/crates/hir-def/src/expander.rs @@ -15,18 +15,11 @@ use crate::{ MacroId, ModuleId, }; -/// A subset of Expander that only deals with cfg attributes. We only need it to -/// avoid cyclic queries in crate def map during enum processing. #[derive(Debug)] -pub(crate) struct CfgExpander { +pub struct Expander { cfg_options: CfgOptions, hygiene: Hygiene, krate: CrateId, -} - -#[derive(Debug)] -pub struct Expander { - cfg_expander: CfgExpander, pub(crate) current_file_id: HirFileId, pub(crate) module: ModuleId, /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. @@ -34,41 +27,23 @@ pub struct Expander { recursion_limit: Limit, } -impl CfgExpander { - pub(crate) fn new( - db: &dyn DefDatabase, - current_file_id: HirFileId, - krate: CrateId, - ) -> CfgExpander { - let hygiene = Hygiene::new(db.upcast(), current_file_id); - let cfg_options = db.crate_graph()[krate].cfg_options.clone(); - CfgExpander { cfg_options, hygiene, krate } - } - - pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { - Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene)) - } - - pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool { - let attrs = self.parse_attrs(db, owner); - attrs.is_cfg_enabled(&self.cfg_options) - } - - pub(crate) fn hygiene(&self) -> &Hygiene { - &self.hygiene - } -} - impl Expander { pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { - let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); let recursion_limit = db.recursion_limit(module.krate); #[cfg(not(test))] let recursion_limit = Limit::new(recursion_limit as usize); // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug #[cfg(test)] let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize)); - Expander { cfg_expander, current_file_id, module, recursion_depth: 0, recursion_limit } + Expander { + current_file_id, + module, + recursion_depth: 0, + recursion_limit, + cfg_options: db.crate_graph()[module.krate].cfg_options.clone(), + hygiene: Hygiene::new(db.upcast(), current_file_id), + krate: module.krate, + } } pub fn enter_expand( @@ -120,7 +95,7 @@ impl Expander { } pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); + self.hygiene = Hygiene::new(db.upcast(), mark.file_id); self.current_file_id = mark.file_id; if self.recursion_depth == u32::MAX { // Recursion limit has been reached somewhere in the macro expansion tree. Reset the @@ -135,7 +110,7 @@ impl Expander { } pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> { - LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id) + LowerCtx::new(db, &self.hygiene, self.current_file_id) } pub(crate) fn to_source(&self, value: T) -> InFile { @@ -143,11 +118,11 @@ impl Expander { } pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { - self.cfg_expander.parse_attrs(db, owner) + Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene)) } pub(crate) fn cfg_options(&self) -> &CfgOptions { - &self.cfg_expander.cfg_options + &self.cfg_options } pub fn current_file_id(&self) -> HirFileId { @@ -155,7 +130,7 @@ impl Expander { } pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option { - let ctx = LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id); + let ctx = LowerCtx::new(db, &self.hygiene, self.current_file_id); Path::from_src(path, &ctx) } @@ -194,7 +169,7 @@ impl Expander { let parse = value.cast::()?; self.recursion_depth += 1; - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); + self.hygiene = Hygiene::new(db.upcast(), file_id); let old_file_id = std::mem::replace(&mut self.current_file_id, file_id); let mark = Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") }; From 9fd5f8c6702656f76054622294cba438a8b29fe5 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Tue, 4 Jul 2023 18:58:19 +0900 Subject: [PATCH 113/201] Support GATs in bounds for associated types --- crates/hir-ty/src/chalk_db.rs | 26 ++++++++++++++++---------- crates/hir-ty/src/tests/traits.rs | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index ff80d4cf9d..0038b0d183 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -5,7 +5,7 @@ use std::{iter, sync::Arc}; use tracing::debug; -use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds}; +use chalk_ir::{cast::Caster, fold::shift::Shift, CanonicalVarKinds}; use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; use base_db::CrateId; @@ -846,28 +846,34 @@ pub(super) fn generic_predicate_to_inline_bound( } let args_no_self = trait_ref.substitution.as_slice(Interner)[1..] .iter() - .map(|ty| ty.clone().cast(Interner)) + .cloned() + .casted(Interner) .collect(); let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self }; Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound))) } WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { - let trait_ = projection_ty.trait_(db); - if projection_ty.self_type_parameter(db) != self_ty_shifted_in { + let generics = + generics(db.upcast(), from_assoc_type_id(projection_ty.associated_ty_id).into()); + let (assoc_args, trait_args) = + projection_ty.substitution.as_slice(Interner).split_at(generics.len_self()); + let (self_ty, args_no_self) = + trait_args.split_first().expect("projection without trait self type"); + if self_ty.assert_ty_ref(Interner) != &self_ty_shifted_in { return None; } - let args_no_self = projection_ty.substitution.as_slice(Interner)[1..] - .iter() - .map(|ty| ty.clone().cast(Interner)) - .collect(); + + let args_no_self = args_no_self.iter().cloned().casted(Interner).collect(); + let parameters = assoc_args.to_vec(); + let alias_eq_bound = rust_ir::AliasEqBound { value: ty.clone(), trait_bound: rust_ir::TraitBound { - trait_id: to_chalk_trait_id(trait_), + trait_id: to_chalk_trait_id(projection_ty.trait_(db)), args_no_self, }, associated_ty_id: projection_ty.associated_ty_id, - parameters: Vec::new(), // FIXME we don't support generic associated types yet + parameters, }; Some(chalk_ir::Binders::new( binders, diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 97ae732a90..5f5cd79451 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4148,6 +4148,30 @@ where ); } +#[test] +fn gats_in_bounds_for_assoc() { + check_types( + r#" +trait Trait { + type Assoc: Another = usize>; + type Assoc2: Another = T>; +} +trait Another { + type Gat; + fn foo(&self) -> Self::Gat; + fn bar(&self) -> Self::Gat; +} + +fn test(a: T::Assoc, b: T::Assoc2) { + let v = a.foo(); + //^ usize + let v = b.bar::(); + //^ isize +} +"#, + ); +} + #[test] fn bin_op_with_scalar_fallback() { // Extra impls are significant so that chalk doesn't give us definite guidances. From 49b039f1d1591757e7fbd77243c23154691c38a9 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Tue, 4 Jul 2023 20:34:55 +0900 Subject: [PATCH 114/201] Recover from missing associated items and generic const defaults --- crates/parser/src/grammar/generic_args.rs | 19 +++++++- crates/parser/src/grammar/generic_params.rs | 3 +- ...cover_from_missing_assoc_item_binding.rast | 48 +++++++++++++++++++ ...recover_from_missing_assoc_item_binding.rs | 1 + ...22_recover_from_missing_const_default.rast | 44 +++++++++++++++++ ...0022_recover_from_missing_const_default.rs | 1 + 6 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast create mode 100644 crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs create mode 100644 crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast create mode 100644 crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs diff --git a/crates/parser/src/grammar/generic_args.rs b/crates/parser/src/grammar/generic_args.rs index e589b69934..211af98e6e 100644 --- a/crates/parser/src/grammar/generic_args.rs +++ b/crates/parser/src/grammar/generic_args.rs @@ -32,6 +32,9 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[ ]) .union(types::TYPE_FIRST); +// Despite its name, it can also be used for generic param list. +const GENERIC_ARG_RECOVERY_SET: TokenSet = TokenSet::new(&[T![>], T![,]]); + // test generic_arg // type T = S; fn generic_arg(p: &mut Parser<'_>) -> bool { @@ -55,6 +58,15 @@ fn generic_arg(p: &mut Parser<'_>) -> bool { // test assoc_type_eq // type T = StreamingIterator = &'a T>; types::type_(p); + } else if p.at_ts(GENERIC_ARG_RECOVERY_SET) { + // Although `const_arg()` recovers as expected, we want to + // handle those here to give the following message because + // we don't know whether this associated item is a type or + // const at this point. + + // test_err recover_from_missing_assoc_item_binding + // fn f() -> impl Iterator {} + p.error("missing associated item binding"); } else { // test assoc_const_eq // fn foo>() {} @@ -141,12 +153,17 @@ pub(super) fn const_arg_expr(p: &mut Parser<'_>) { expressions::literal(p); lm.complete(p, PREFIX_EXPR); } - _ => { + _ if paths::is_use_path_start(p) => { // This shouldn't be hit by `const_arg` let lm = p.start(); paths::use_path(p); lm.complete(p, PATH_EXPR); } + _ => { + // test_err recover_from_missing_const_default + // struct A; + p.err_recover("expected a generic const argument", GENERIC_ARG_RECOVERY_SET); + } } } diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs index 7fcf938bab..8ed1c84c4c 100644 --- a/crates/parser/src/grammar/generic_params.rs +++ b/crates/parser/src/grammar/generic_params.rs @@ -79,10 +79,9 @@ fn const_param(p: &mut Parser<'_>, m: Marker) { p.error("missing type for const parameter"); } - if p.at(T![=]) { + if p.eat(T![=]) { // test const_param_default_literal // struct A; - p.bump(T![=]); // test const_param_default_expression // struct A; diff --git a/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast b/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast new file mode 100644 index 0000000000..fc59db84e7 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast @@ -0,0 +1,48 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + THIN_ARROW "->" + WHITESPACE " " + IMPL_TRAIT_TYPE + IMPL_KW "impl" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Iterator" + GENERIC_ARG_LIST + L_ANGLE "<" + ASSOC_TYPE_ARG + NAME_REF + IDENT "Item" + WHITESPACE " " + EQ "=" + WHITESPACE " " + COMMA "," + WHITESPACE " " + ASSOC_TYPE_ARG + NAME_REF + IDENT "Item" + WHITESPACE " " + EQ "=" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 30: missing associated item binding +error 39: missing associated item binding diff --git a/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs b/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs new file mode 100644 index 0000000000..e484e433a0 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs @@ -0,0 +1 @@ +fn f() -> impl Iterator {} diff --git a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast new file mode 100644 index 0000000000..809ad1b8d5 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast @@ -0,0 +1,44 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "A" + GENERIC_PARAM_LIST + L_ANGLE "<" + CONST_PARAM + CONST_KW "const" + WHITESPACE " " + NAME + IDENT "N" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + WHITESPACE " " + EQ "=" + WHITESPACE " " + COMMA "," + WHITESPACE " " + CONST_PARAM + CONST_KW "const" + WHITESPACE " " + NAME + IDENT "M" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + WHITESPACE " " + EQ "=" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" +error 23: expected a generic const argument +error 40: expected a generic const argument diff --git a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs new file mode 100644 index 0000000000..5bab13da92 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs @@ -0,0 +1 @@ +struct A; From 03423116ad533e1e80d7095595429e7cb0af2af7 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Tue, 4 Jul 2023 17:13:07 +0200 Subject: [PATCH 115/201] Generate trait from impl v2 --- .../src/handlers/generate_trait_from_impl.rs | 108 +++++++++++++----- crates/syntax/src/ast/make.rs | 2 +- 2 files changed, 79 insertions(+), 31 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index ce9eeb8200..d3192ae409 100644 --- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -1,8 +1,8 @@ use crate::assist_context::{AssistContext, Assists}; -use ide_db::{assists::AssistId, SnippetCap}; +use ide_db::assists::AssistId; use syntax::{ - ast::{self, HasGenericParams, HasVisibility}, - AstNode, + ast::{self, edit::IndentLevel, make, HasGenericParams, HasVisibility}, + ted, AstNode, SyntaxKind, }; // NOTES : @@ -68,6 +68,16 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ // Get AST Node let impl_ast = ctx.find_node_at_offset::()?; + // Check if cursor is to the left of assoc item list's L_CURLY. + // if no L_CURLY then return. + let l_curly = impl_ast.assoc_item_list()?.l_curly_token()?; + + let cursor_offset = ctx.offset(); + let l_curly_offset = l_curly.text_range(); + if cursor_offset >= l_curly_offset.start() { + return None; + } + // If impl is not inherent then we don't really need to go any further. if impl_ast.for_token().is_some() { return None; @@ -80,9 +90,11 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ return None; } + let impl_name = impl_ast.self_ty()?; + acc.add( AssistId("generate_trait_from_impl", ide_db::assists::AssistKind::Generate), - "Generate trait from impl".to_owned(), + "Generate trait from impl", impl_ast.syntax().text_range(), |builder| { let trait_items = assoc_items.clone_for_update(); @@ -93,45 +105,43 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ remove_items_visibility(&item); }); - syntax::ted::replace(assoc_items.clone_for_update().syntax(), impl_items.syntax()); + ted::replace(assoc_items.clone_for_update().syntax(), impl_items.syntax()); impl_items.assoc_items().for_each(|item| { remove_items_visibility(&item); }); - let trait_ast = ast::make::trait_( + let trait_ast = make::trait_( false, - "NewTrait".to_string(), - HasGenericParams::generic_param_list(&impl_ast), - HasGenericParams::where_clause(&impl_ast), + "NewTrait", + impl_ast.generic_param_list(), + impl_ast.where_clause(), trait_items, ); // Change `impl Foo` to `impl NewTrait for Foo` - // First find the PATH_TYPE which is what Foo is. - let impl_name = impl_ast.self_ty().unwrap(); - let trait_name = if let Some(genpars) = impl_ast.generic_param_list() { - format!("NewTrait{}", genpars.to_generic_args()) + let arg_list = if let Some(genpars) = impl_ast.generic_param_list() { + genpars.to_generic_args().to_string() } else { - format!("NewTrait") + "".to_string() }; // // Then replace builder.replace( - impl_name.clone().syntax().text_range(), - format!("{} for {}", trait_name, impl_name.to_string()), + impl_name.syntax().text_range(), + format!("NewTrait{} for {}", arg_list, impl_name.to_string()), ); - builder.replace( - impl_ast.assoc_item_list().unwrap().syntax().text_range(), - impl_items.to_string(), - ); + builder.replace(assoc_items.syntax().text_range(), impl_items.to_string()); // Insert trait before TraitImpl - builder.insert_snippet( - SnippetCap::new(true).unwrap(), + builder.insert( impl_ast.syntax().text_range().start(), - format!("{}\n\n", trait_ast.to_string()), + format!( + "{}\n\n{}", + trait_ast.to_string(), + IndentLevel::from_node(impl_ast.syntax()) + ), ); }, ); @@ -144,17 +154,17 @@ fn remove_items_visibility(item: &ast::AssocItem) { match item { ast::AssocItem::Const(c) => { if let Some(vis) = c.visibility() { - syntax::ted::remove(vis.syntax()); + ted::remove(vis.syntax()); } } ast::AssocItem::Fn(f) => { if let Some(vis) = f.visibility() { - syntax::ted::remove(vis.syntax()); + ted::remove(vis.syntax()); } } ast::AssocItem::TypeAlias(t) => { if let Some(vis) = t.visibility() { - syntax::ted::remove(vis.syntax()); + ted::remove(vis.syntax()); } } _ => (), @@ -168,12 +178,12 @@ fn strip_body(item: &ast::AssocItem) { // In constrast to function bodies, we want to see no ws before a semicolon. // So let's remove them if we see any. if let Some(prev) = body.syntax().prev_sibling_or_token() { - if prev.kind() == syntax::SyntaxKind::WHITESPACE { - syntax::ted::remove(prev); + if prev.kind() == SyntaxKind::WHITESPACE { + ted::remove(prev); } } - syntax::ted::replace(body.syntax(), ast::make::tokens::semicolon()); + ted::replace(body.syntax(), ast::make::tokens::semicolon()); } } _ => (), @@ -185,6 +195,21 @@ mod tests { use super::*; use crate::tests::{check_assist, check_assist_not_applicable}; + #[test] + fn test_trigger_when_cursor_on_header() { + check_assist_not_applicable( + generate_trait_from_impl, + r#" +struct Foo(f64); + +impl Foo { $0 + fn add(&mut self, x: f64) { + self.0 += x; + } +}"#, + ); + } + #[test] fn test_assoc_item_fn() { check_assist( @@ -299,7 +324,7 @@ impl NewTrait for Foo { } #[test] - fn test_e0449_avoided() { + fn test_trait_items_should_not_have_vis() { check_assist( generate_trait_from_impl, r#" @@ -334,4 +359,27 @@ impl Emp$0tyImpl{} "#, ) } + + #[test] + fn test_not_top_level_impl() { + check_assist( + generate_trait_from_impl, + r#" +mod a { + impl S$0 { + fn foo() {} + } +}"#, + r#" +mod a { + trait NewTrait { + fn foo(); + } + + impl NewTrait for S { + fn foo() {} + } +}"#, + ) + } } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 1675d1af1d..3facd90a11 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -865,7 +865,7 @@ pub fn param_list( pub fn trait_( is_unsafe: bool, - ident: String, + ident: &str, gen_params: Option, where_clause: Option, assoc_items: ast::AssocItemList, From bb35d8fa8ef9de3c8282602b411c40b266dc3a4e Mon Sep 17 00:00:00 2001 From: Tetsuharu Ohzeki Date: Wed, 28 Jun 2023 04:15:01 +0900 Subject: [PATCH 116/201] editor/code: Set ts' --moduleResolution=node16 option https://www.typescriptlang.org/tsconfig#moduleResolution By this change, typescript compiler can read `exports` field in package.json for dependencies. https://nodejs.org/api/packages.html#exports-sugar --- editors/code/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json index 04bd456937..3f887a41b3 100644 --- a/editors/code/tsconfig.json +++ b/editors/code/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "esModuleInterop": false, "module": "commonjs", + "moduleResolution": "node16", "target": "es2021", "outDir": "out", "lib": ["es2021"], From 30cbba20de13ed37250d0610980636e43f673022 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Wed, 5 Jul 2023 13:32:12 +0200 Subject: [PATCH 117/201] Add snippet insert if client supports it --- .../src/handlers/generate_trait_from_impl.rs | 94 ++++++++++++++----- crates/ide-assists/src/tests/generated.rs | 4 +- 2 files changed, 72 insertions(+), 26 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index d3192ae409..2027309940 100644 --- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -44,7 +44,7 @@ use syntax::{ // }; // } // -// trait NewTrait { +// trait ${0:TraitName} { // // Used as an associated constant. // const CONST_ASSOC: usize = N * 4; // @@ -53,7 +53,7 @@ use syntax::{ // const_maker! {i32, 7} // } // -// impl NewTrait for Foo { +// impl ${0:TraitName} for Foo { // // Used as an associated constant. // const CONST_ASSOC: usize = N * 4; // @@ -126,23 +126,41 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ "".to_string() }; - // // Then replace - builder.replace( - impl_name.syntax().text_range(), - format!("NewTrait{} for {}", arg_list, impl_name.to_string()), - ); + if let Some(snippet_cap) = ctx.config.snippet_cap { + builder.replace_snippet( + snippet_cap, + impl_name.syntax().text_range(), + format!("${{0:TraitName}}{} for {}", arg_list, impl_name.to_string()), + ); + + // Insert trait before TraitImpl + builder.insert_snippet( + snippet_cap, + impl_ast.syntax().text_range().start(), + format!( + "{}\n\n{}", + trait_ast.to_string().replace("NewTrait", "${0:TraitName}"), + IndentLevel::from_node(impl_ast.syntax()) + ), + ); + } else { + builder.replace( + impl_name.syntax().text_range(), + format!("NewTrait{} for {}", arg_list, impl_name.to_string()), + ); + + // Insert trait before TraitImpl + builder.insert( + impl_ast.syntax().text_range().start(), + format!( + "{}\n\n{}", + trait_ast.to_string(), + IndentLevel::from_node(impl_ast.syntax()) + ), + ); + } builder.replace(assoc_items.syntax().text_range(), impl_items.to_string()); - - // Insert trait before TraitImpl - builder.insert( - impl_ast.syntax().text_range().start(), - format!( - "{}\n\n{}", - trait_ast.to_string(), - IndentLevel::from_node(impl_ast.syntax()) - ), - ); }, ); @@ -193,7 +211,7 @@ fn strip_body(item: &ast::AssocItem) { #[cfg(test)] mod tests { use super::*; - use crate::tests::{check_assist, check_assist_not_applicable}; + use crate::tests::{check_assist, check_assist_no_snippet_cap, check_assist_not_applicable}; #[test] fn test_trigger_when_cursor_on_header() { @@ -212,7 +230,7 @@ impl Foo { $0 #[test] fn test_assoc_item_fn() { - check_assist( + check_assist_no_snippet_cap( generate_trait_from_impl, r#" struct Foo(f64); @@ -239,7 +257,7 @@ impl NewTrait for Foo { #[test] fn test_assoc_item_macro() { - check_assist( + check_assist_no_snippet_cap( generate_trait_from_impl, r#" struct Foo; @@ -274,7 +292,7 @@ impl NewTrait for Foo { #[test] fn test_assoc_item_const() { - check_assist( + check_assist_no_snippet_cap( generate_trait_from_impl, r#" struct Foo; @@ -297,7 +315,7 @@ impl NewTrait for Foo { #[test] fn test_impl_with_generics() { - check_assist( + check_assist_no_snippet_cap( generate_trait_from_impl, r#" struct Foo([i32; N]); @@ -325,7 +343,7 @@ impl NewTrait for Foo { #[test] fn test_trait_items_should_not_have_vis() { - check_assist( + check_assist_no_snippet_cap( generate_trait_from_impl, r#" struct Foo; @@ -362,7 +380,7 @@ impl Emp$0tyImpl{} #[test] fn test_not_top_level_impl() { - check_assist( + check_assist_no_snippet_cap( generate_trait_from_impl, r#" mod a { @@ -382,4 +400,32 @@ mod a { }"#, ) } + + #[test] + fn test_snippet_cap_is_some() { + check_assist( + generate_trait_from_impl, + r#" +struct Foo([i32; N]); + +impl F$0oo { + // Used as an associated constant. + const CONST: usize = N * 4; +} + "#, + r#" +struct Foo([i32; N]); + +trait ${0:TraitName} { + // Used as an associated constant. + const CONST: usize = N * 4; +} + +impl ${0:TraitName} for Foo { + // Used as an associated constant. + const CONST: usize = N * 4; +} + "#, + ) + } } diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 2404fecc45..edab13c754 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1533,7 +1533,7 @@ macro_rules! const_maker { }; } -trait NewTrait { +trait ${0:TraitName} { // Used as an associated constant. const CONST_ASSOC: usize = N * 4; @@ -1542,7 +1542,7 @@ trait NewTrait { const_maker! {i32, 7} } -impl NewTrait for Foo { +impl ${0:TraitName} for Foo { // Used as an associated constant. const CONST_ASSOC: usize = N * 4; From ce0239bd6a58ce5326b7480c8bdd8552d111a46f Mon Sep 17 00:00:00 2001 From: AmrDeveloper Date: Wed, 5 Jul 2023 16:34:04 +0200 Subject: [PATCH 118/201] Disable remove unnecessary braces diagnotics for self imports --- .../src/handlers/useless_braces.rs | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs index 11ce811efa..49cdcdb00f 100644 --- a/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -1,6 +1,6 @@ use ide_db::{base_db::FileId, source_change::SourceChange}; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxNode, TextRange}; +use syntax::{ast, AstNode, SyntaxKind, SyntaxNode, TextRange}; use text_edit::TextEdit; use crate::{fix, Diagnostic, DiagnosticCode}; @@ -15,6 +15,11 @@ pub(crate) fn useless_braces( ) -> Option<()> { let use_tree_list = ast::UseTreeList::cast(node.clone())?; if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { + // If there is a `self` inside the bracketed `use`, don't show diagnostic. + if single_use_tree.syntax().first_token().unwrap().kind() == SyntaxKind::SELF_KW { + return Some(()); + } + // If there is a comment inside the bracketed `use`, // assume it is a commented out module path and don't show diagnostic. if use_tree_list.has_inner_comment() { @@ -91,6 +96,22 @@ mod a { pub mod e {} } } +"#, + ); + check_diagnostics( + r#" +use a::{self}; + +mod a { +} +"#, + ); + check_diagnostics( + r#" +use a::{self as cool_name}; + +mod a { +} "#, ); check_fix( @@ -121,16 +142,6 @@ use a::{c$0}; r#" mod a { pub mod c {} } use a::c; -"#, - ); - check_fix( - r#" -mod a {} -use a::{self$0}; -"#, - r#" -mod a {} -use a; "#, ); check_fix( From 8edb3e192aa9b7faff864e852b8b3302fcd426b1 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Wed, 5 Jul 2023 16:45:54 +0200 Subject: [PATCH 119/201] Minor changes --- crates/ide-assists/src/handlers/generate_trait_from_impl.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 2027309940..0f67380d12 100644 --- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -105,8 +105,6 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ remove_items_visibility(&item); }); - ted::replace(assoc_items.clone_for_update().syntax(), impl_items.syntax()); - impl_items.assoc_items().for_each(|item| { remove_items_visibility(&item); }); @@ -201,7 +199,7 @@ fn strip_body(item: &ast::AssocItem) { } } - ted::replace(body.syntax(), ast::make::tokens::semicolon()); + ted::replace(body.syntax(), make::tokens::semicolon()); } } _ => (), From 3fb6a3be8242cfd57d891c4b6a666e3e9e38c01d Mon Sep 17 00:00:00 2001 From: AmrDeveloper Date: Wed, 5 Jul 2023 17:38:58 +0200 Subject: [PATCH 120/201] Remove un needed extra handling for remove self with braces --- .../src/handlers/useless_braces.rs | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs index 49cdcdb00f..79dd09378c 100644 --- a/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -27,13 +27,11 @@ pub(crate) fn useless_braces( } let use_range = use_tree_list.syntax().text_range(); - let edit = remove_braces(&single_use_tree).unwrap_or_else(|| { - let to_replace = single_use_tree.syntax().text().to_string(); - let mut edit_builder = TextEdit::builder(); - edit_builder.delete(use_range); - edit_builder.insert(use_range.start(), to_replace); - edit_builder.finish() - }); + let to_replace = single_use_tree.syntax().text().to_string(); + let mut edit_builder = TextEdit::builder(); + edit_builder.delete(use_range); + edit_builder.insert(use_range.start(), to_replace); + let edit = edit_builder.finish(); acc.push( Diagnostic::new( @@ -53,16 +51,6 @@ pub(crate) fn useless_braces( Some(()) } -fn remove_braces(single_use_tree: &ast::UseTree) -> Option { - let use_tree_list_node = single_use_tree.syntax().parent()?; - if single_use_tree.path()?.segment()?.self_token().is_some() { - let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start(); - let end = use_tree_list_node.text_range().end(); - return Some(TextEdit::delete(TextRange::new(start, end))); - } - None -} - #[cfg(test)] mod tests { use crate::tests::{check_diagnostics, check_fix}; From fe65eabc819551b584e20e102d41f34b27ba2c74 Mon Sep 17 00:00:00 2001 From: AmrDeveloper Date: Wed, 5 Jul 2023 17:41:51 +0200 Subject: [PATCH 121/201] Remove un used import --- crates/ide-diagnostics/src/handlers/useless_braces.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs index 79dd09378c..d9a9c486a3 100644 --- a/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -1,6 +1,6 @@ use ide_db::{base_db::FileId, source_change::SourceChange}; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxKind, SyntaxNode, TextRange}; +use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; use text_edit::TextEdit; use crate::{fix, Diagnostic, DiagnosticCode}; From 54e897368d88650b2990aa62ba39dfee0a4f970c Mon Sep 17 00:00:00 2001 From: AmrDeveloper Date: Wed, 5 Jul 2023 19:31:37 +0200 Subject: [PATCH 122/201] Cover disable diagnostic from case with invalid syntax --- .../src/handlers/useless_braces.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs index d9a9c486a3..0aa439f797 100644 --- a/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -1,6 +1,6 @@ use ide_db::{base_db::FileId, source_change::SourceChange}; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; +use syntax::{ast, AstNode, SyntaxNode}; use text_edit::TextEdit; use crate::{fix, Diagnostic, DiagnosticCode}; @@ -16,7 +16,7 @@ pub(crate) fn useless_braces( let use_tree_list = ast::UseTreeList::cast(node.clone())?; if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { // If there is a `self` inside the bracketed `use`, don't show diagnostic. - if single_use_tree.syntax().first_token().unwrap().kind() == SyntaxKind::SELF_KW { + if single_use_tree.path()?.segment()?.self_token().is_some() { return Some(()); } @@ -53,7 +53,10 @@ pub(crate) fn useless_braces( #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_fix}; + use crate::{ + tests::{check_diagnostics, check_diagnostics_with_config, check_fix}, + DiagnosticsConfig, + }; #[test] fn test_check_unnecessary_braces_in_use_statement() { @@ -100,6 +103,16 @@ use a::{self as cool_name}; mod a { } +"#, + ); + + let mut config = DiagnosticsConfig::test_sample(); + config.disabled.insert("syntax-error".to_string()); + check_diagnostics_with_config( + config, + r#" +mod a { pub mod b {} } +use a::{b::self}; "#, ); check_fix( From 72a3883a7128b4eae6f38d6479f33aaaaae4790b Mon Sep 17 00:00:00 2001 From: Tetsuharu Ohzeki Date: Wed, 28 Jun 2023 04:03:53 +0900 Subject: [PATCH 123/201] editor/code: Enable `noUncheckedIndexedAccess` ts option https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess --- editors/code/src/ast_inspector.ts | 6 ++- editors/code/src/client.ts | 7 ++- editors/code/src/commands.ts | 14 ++++-- editors/code/src/config.ts | 54 ++++++++++++----------- editors/code/src/debug.ts | 12 +++-- editors/code/src/dependencies_provider.ts | 8 +++- editors/code/src/diagnostics.ts | 4 +- editors/code/src/nullable.ts | 19 ++++++++ editors/code/src/run.ts | 11 +++-- editors/code/src/snippets.ts | 6 ++- editors/code/src/tasks.ts | 4 +- editors/code/src/toolchain.ts | 9 +++- editors/code/src/undefinable.ts | 19 ++++++++ editors/code/tsconfig.json | 3 +- 14 files changed, 124 insertions(+), 52 deletions(-) create mode 100644 editors/code/src/nullable.ts create mode 100644 editors/code/src/undefinable.ts diff --git a/editors/code/src/ast_inspector.ts b/editors/code/src/ast_inspector.ts index 176040120f..fa963d8eb9 100644 --- a/editors/code/src/ast_inspector.ts +++ b/editors/code/src/ast_inspector.ts @@ -2,6 +2,7 @@ import * as vscode from "vscode"; import { Ctx, Disposable } from "./ctx"; import { RustEditor, isRustEditor } from "./util"; +import { unwrapUndefinable } from "./undefinable"; // FIXME: consider implementing this via the Tree View API? // https://code.visualstudio.com/api/extension-guides/tree-view @@ -164,8 +165,9 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv if (!parsedRange) return; const [begin, end] = parsedRange.slice(1).map((off) => this.positionAt(doc, +off)); - - return new vscode.Range(begin, end); + const actualBegin = unwrapUndefinable(begin); + const actualEnd = unwrapUndefinable(end); + return new vscode.Range(actualBegin, actualEnd); } // Memoize the last value, otherwise the CPU is at 100% single core diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index f721fcce76..7b151c804a 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -9,6 +9,7 @@ import { WorkspaceEdit } from "vscode"; import { Config, prepareVSCodeConfig } from "./config"; import { randomUUID } from "crypto"; import { sep as pathSeparator } from "path"; +import { unwrapUndefinable } from "./undefinable"; export interface Env { [name: string]: string; @@ -323,10 +324,12 @@ export async function createClient( } for (const [group, { index, items }] of groups) { if (items.length === 1) { - result[index] = items[0]; + const item = unwrapUndefinable(items[0]); + result[index] = item; } else { const action = new vscode.CodeAction(group); - action.kind = items[0].kind; + const item = unwrapUndefinable(items[0]); + action.kind = item.kind; action.command = { command: "rust-analyzer.applyActionGroup", title: "", diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 98ccd50dc0..3c6105e89f 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -20,6 +20,7 @@ import { startDebugSession, makeDebugConfig } from "./debug"; import { LanguageClient } from "vscode-languageclient/node"; import { LINKED_COMMANDS } from "./client"; import { DependencyId } from "./dependencies_provider"; +import { unwrapUndefinable } from "./undefinable"; export * from "./ast_inspector"; export * from "./run"; @@ -129,7 +130,8 @@ export function matchingBrace(ctx: CtxInit): Cmd { ), }); editor.selections = editor.selections.map((sel, idx) => { - const active = client.protocol2CodeConverter.asPosition(response[idx]); + const position = unwrapUndefinable(response[idx]); + const active = client.protocol2CodeConverter.asPosition(position); const anchor = sel.isEmpty ? active : sel.anchor; return new vscode.Selection(anchor, active); }); @@ -231,7 +233,7 @@ export function parentModule(ctx: CtxInit): Cmd { if (!locations) return; if (locations.length === 1) { - const loc = locations[0]; + const loc = unwrapUndefinable(locations[0]); const uri = client.protocol2CodeConverter.asUri(loc.targetUri); const range = client.protocol2CodeConverter.asRange(loc.targetRange); @@ -331,7 +333,13 @@ async function revealParentChain(document: RustDocument, ctx: CtxInit) { } while (!ctx.dependencies?.contains(documentPath)); parentChain.reverse(); for (const idx in parentChain) { - await ctx.treeView?.reveal(parentChain[idx], { select: true, expand: true }); + const treeView = ctx.treeView; + if (!treeView) { + continue; + } + + const dependency = unwrapUndefinable(parentChain[idx]); + await treeView.reveal(dependency, { select: true, expand: true }); } } diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 15a1d4e0f1..9595ba0528 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -4,6 +4,7 @@ import * as path from "path"; import * as vscode from "vscode"; import { Env } from "./client"; import { log } from "./util"; +import { expectNotUndefined, unwrapUndefinable } from "./undefinable"; export type RunnableEnvCfg = | undefined @@ -338,7 +339,7 @@ export function substituteVariablesInEnv(env: Env): Env { const depRe = new RegExp(/\${(?.+?)}/g); let match = undefined; while ((match = depRe.exec(value))) { - const depName = match.groups!.depName; + const depName = unwrapUndefinable(match.groups?.depName); deps.add(depName); // `depName` at this point can have a form of `expression` or // `prefix:expression` @@ -356,7 +357,7 @@ export function substituteVariablesInEnv(env: Env): Env { if (match) { const { prefix, body } = match.groups!; if (prefix === "env") { - const envName = body; + const envName = unwrapUndefinable(body); envWithDeps[dep] = { value: process.env[envName] ?? "", deps: [], @@ -384,13 +385,12 @@ export function substituteVariablesInEnv(env: Env): Env { do { leftToResolveSize = toResolve.size; for (const key of toResolve) { - if (envWithDeps[key].deps.every((dep) => resolved.has(dep))) { - envWithDeps[key].value = envWithDeps[key].value.replace( - /\${(?.+?)}/g, - (_wholeMatch, depName) => { - return envWithDeps[depName].value; - } - ); + const item = unwrapUndefinable(envWithDeps[key]); + if (item.deps.every((dep) => resolved.has(dep))) { + item.value = item.value.replace(/\${(?.+?)}/g, (_wholeMatch, depName) => { + const item = unwrapUndefinable(envWithDeps[depName]); + return item.value; + }); resolved.add(key); toResolve.delete(key); } @@ -399,7 +399,8 @@ export function substituteVariablesInEnv(env: Env): Env { const resolvedEnv: Env = {}; for (const key of Object.keys(env)) { - resolvedEnv[key] = envWithDeps[`env:${key}`].value; + const item = unwrapUndefinable(envWithDeps[`env:${key}`]); + resolvedEnv[key] = item.value; } return resolvedEnv; } @@ -418,20 +419,19 @@ function substituteVSCodeVariableInString(val: string): string { function computeVscodeVar(varName: string): string | null { const workspaceFolder = () => { const folders = vscode.workspace.workspaceFolders ?? []; - if (folders.length === 1) { - // TODO: support for remote workspaces? - return folders[0].uri.fsPath; - } else if (folders.length > 1) { - // could use currently opened document to detect the correct - // workspace. However, that would be determined by the document - // user has opened on Editor startup. Could lead to - // unpredictable workspace selection in practice. - // It's better to pick the first one - return folders[0].uri.fsPath; - } else { - // no workspace opened - return ""; - } + const folder = folders[0]; + // TODO: support for remote workspaces? + const fsPath: string = + folder === undefined + ? // no workspace opened + "" + : // could use currently opened document to detect the correct + // workspace. However, that would be determined by the document + // user has opened on Editor startup. Could lead to + // unpredictable workspace selection in practice. + // It's better to pick the first one + folder.uri.fsPath; + return fsPath; }; // https://code.visualstudio.com/docs/editor/variables-reference const supportedVariables: { [k: string]: () => string } = { @@ -454,7 +454,11 @@ function computeVscodeVar(varName: string): string | null { }; if (varName in supportedVariables) { - return supportedVariables[varName](); + const fn = expectNotUndefined( + supportedVariables[varName], + `${varName} should not be undefined here` + ); + return fn(); } else { // return "${" + varName + "}"; return null; diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 8fbd427039..ede90d707d 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -6,6 +6,7 @@ import * as ra from "./lsp_ext"; import { Cargo, getRustcId, getSysroot } from "./toolchain"; import { Ctx } from "./ctx"; import { prepareEnv } from "./run"; +import { unwrapUndefinable } from "./undefinable"; const debugOutput = vscode.window.createOutputChannel("Debug"); type DebugConfigProvider = ( @@ -105,12 +106,13 @@ async function getDebugConfiguration( const workspaceFolders = vscode.workspace.workspaceFolders!; const isMultiFolderWorkspace = workspaceFolders.length > 1; const firstWorkspace = workspaceFolders[0]; - const workspace = + const maybeWorkspace = !isMultiFolderWorkspace || !runnable.args.workspaceRoot ? firstWorkspace : workspaceFolders.find((w) => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) || firstWorkspace; + const workspace = unwrapUndefinable(maybeWorkspace); const wsFolder = path.normalize(workspace.uri.fsPath); const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : ""; function simplifyPath(p: string): string { @@ -130,12 +132,8 @@ async function getDebugConfiguration( sourceFileMap[`/rustc/${commitHash}/`] = rustlib; } - const debugConfig = knownEngines[debugEngine.id]( - runnable, - simplifyPath(executable), - env, - sourceFileMap - ); + const provider = unwrapUndefinable(knownEngines[debugEngine.id]); + const debugConfig = provider(runnable, simplifyPath(executable), env, sourceFileMap); if (debugConfig.type in debugOptions.engineSettings) { const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; for (var key in settingsMap) { diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts index d67345258e..51ba11ecc9 100644 --- a/editors/code/src/dependencies_provider.ts +++ b/editors/code/src/dependencies_provider.ts @@ -4,6 +4,7 @@ import * as fs from "fs"; import { CtxInit } from "./ctx"; import * as ra from "./lsp_ext"; import { FetchDependencyListResult } from "./lsp_ext"; +import { unwrapUndefinable } from "./undefinable"; export class RustDependenciesProvider implements vscode.TreeDataProvider @@ -49,7 +50,12 @@ export class RustDependenciesProvider } getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable { - if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!]; + const dependenciesMap = this.dependenciesMap; + const elementId = element.id!; + if (elementId in dependenciesMap) { + const dependency = unwrapUndefinable(dependenciesMap[elementId]); + return dependency; + } return element; } diff --git a/editors/code/src/diagnostics.ts b/editors/code/src/diagnostics.ts index 9695d8bf26..a7e0845a27 100644 --- a/editors/code/src/diagnostics.ts +++ b/editors/code/src/diagnostics.ts @@ -2,6 +2,7 @@ import * as anser from "anser"; import * as vscode from "vscode"; import { ProviderResult, Range, TextEditorDecorationType, ThemeColor, window } from "vscode"; import { Ctx } from "./ctx"; +import { unwrapUndefinable } from "./undefinable"; export const URI_SCHEME = "rust-analyzer-diagnostics-view"; @@ -195,7 +196,8 @@ export class AnsiDecorationProvider implements vscode.Disposable { // anser won't return both the RGB and the color name at the same time, // so just fake a single foreground control char with the palette number: const spans = anser.ansiToJson(`\x1b[38;5;${paletteColor}m`); - const rgb = spans[1].fg; + const span = unwrapUndefinable(spans[1]); + const rgb = span.fg; if (rgb) { return `rgb(${rgb})`; diff --git a/editors/code/src/nullable.ts b/editors/code/src/nullable.ts new file mode 100644 index 0000000000..e973e16290 --- /dev/null +++ b/editors/code/src/nullable.ts @@ -0,0 +1,19 @@ +export type NotNull = T extends null ? never : T; + +export type Nullable = T | null; + +function isNotNull(input: Nullable): input is NotNull { + return input !== null; +} + +function expectNotNull(input: Nullable, msg: string): NotNull { + if (isNotNull(input)) { + return input; + } + + throw new TypeError(msg); +} + +export function unwrapNullable(input: Nullable): NotNull { + return expectNotNull(input, `unwrapping \`null\``); +} diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index bdd8524313..c20a5487b7 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -6,6 +6,7 @@ import * as tasks from "./tasks"; import { CtxInit } from "./ctx"; import { makeDebugConfig } from "./debug"; import { Config, RunnableEnvCfg } from "./config"; +import { unwrapUndefinable } from "./undefinable"; const quickPickButtons = [ { iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." }, @@ -68,12 +69,14 @@ export async function selectRunnable( quickPick.onDidHide(() => close()), quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), quickPick.onDidTriggerButton(async (_button) => { - await makeDebugConfig(ctx, quickPick.activeItems[0].runnable); + const runnable = unwrapUndefinable(quickPick.activeItems[0]).runnable; + await makeDebugConfig(ctx, runnable); close(); }), - quickPick.onDidChangeActive((active) => { - if (showButtons && active.length > 0) { - if (active[0].label.startsWith("cargo")) { + quickPick.onDidChangeActive((activeList) => { + if (showButtons && activeList.length > 0) { + const active = unwrapUndefinable(activeList[0]); + if (active.label.startsWith("cargo")) { // save button makes no sense for `cargo test` or `cargo check` quickPick.buttons = []; } else if (quickPick.buttons.length === 0) { diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts index 299d29c27e..1ad93d280b 100644 --- a/editors/code/src/snippets.ts +++ b/editors/code/src/snippets.ts @@ -1,10 +1,11 @@ import * as vscode from "vscode"; import { assert } from "./util"; +import { unwrapUndefinable } from "./undefinable"; export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) { if (edit.entries().length === 1) { - const [uri, edits] = edit.entries()[0]; + const [uri, edits] = unwrapUndefinable(edit.entries()[0]); const editor = await editorFromUri(uri); if (editor) await applySnippetTextEdits(editor, edits); return; @@ -68,7 +69,8 @@ export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vs }); if (selections.length > 0) editor.selections = selections; if (selections.length === 1) { - editor.revealRange(selections[0], vscode.TextEditorRevealType.InCenterIfOutsideViewport); + const selection = unwrapUndefinable(selections[0]); + editor.revealRange(selection, vscode.TextEditorRevealType.InCenterIfOutsideViewport); } } diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index efb889dc79..5199508c82 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -2,6 +2,7 @@ import * as vscode from "vscode"; import * as toolchain from "./toolchain"; import { Config } from "./config"; import { log } from "./util"; +import { unwrapUndefinable } from "./undefinable"; // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and // our configuration should be compatible with it so use the same key. @@ -120,7 +121,8 @@ export async function buildCargoTask( const fullCommand = [...cargoCommand, ...args]; - exec = new vscode.ProcessExecution(fullCommand[0], fullCommand.slice(1), definition); + const processName = unwrapUndefinable(fullCommand[0]); + exec = new vscode.ProcessExecution(processName, fullCommand.slice(1), definition); } return new vscode.Task( diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 917a1d6b09..b2c0c669db 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -4,6 +4,8 @@ import * as path from "path"; import * as readline from "readline"; import * as vscode from "vscode"; import { execute, log, memoizeAsync } from "./util"; +import { unwrapNullable } from "./nullable"; +import { unwrapUndefinable } from "./undefinable"; interface CompilationArtifact { fileName: string; @@ -93,7 +95,8 @@ export class Cargo { throw new Error("Multiple compilation artifacts are not supported."); } - return artifacts[0].fileName; + const artifact = unwrapUndefinable(artifacts[0]); + return artifact.fileName; } private async runCargo( @@ -142,7 +145,9 @@ export async function getRustcId(dir: string): Promise { const data = await execute(`${rustcPath} -V -v`, { cwd: dir }); const rx = /commit-hash:\s(.*)$/m; - return rx.exec(data)![1]; + const result = unwrapNullable(rx.exec(data)); + const first = unwrapUndefinable(result[1]); + return first; } /** Mirrors `toolchain::cargo()` implementation */ diff --git a/editors/code/src/undefinable.ts b/editors/code/src/undefinable.ts new file mode 100644 index 0000000000..813bac5a12 --- /dev/null +++ b/editors/code/src/undefinable.ts @@ -0,0 +1,19 @@ +export type NotUndefined = T extends undefined ? never : T; + +export type Undefinable = T | undefined; + +function isNotUndefined(input: Undefinable): input is NotUndefined { + return input !== undefined; +} + +export function expectNotUndefined(input: Undefinable, msg: string): NotUndefined { + if (isNotUndefined(input)) { + return input; + } + + throw new TypeError(msg); +} + +export function unwrapUndefinable(input: Undefinable): NotUndefined { + return expectNotUndefined(input, `unwrapping \`undefined\``); +} diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json index 3f887a41b3..12d31f0bee 100644 --- a/editors/code/tsconfig.json +++ b/editors/code/tsconfig.json @@ -14,8 +14,7 @@ // to update typescript version without any code change. "useUnknownInCatchVariables": false, "exactOptionalPropertyTypes": false, - "noPropertyAccessFromIndexSignature": false, - "noUncheckedIndexedAccess": false + "noPropertyAccessFromIndexSignature": false }, "exclude": ["node_modules", ".vscode-test"], "include": ["src", "tests"] From f70845305f54181e1681828c7935305c66e743bb Mon Sep 17 00:00:00 2001 From: Tetsuharu Ohzeki Date: Wed, 28 Jun 2023 18:15:30 +0900 Subject: [PATCH 124/201] editor/code: Enable `noPropertyAccessFromIndexSignature` ts option --- editors/code/src/bootstrap.ts | 2 +- editors/code/src/config.ts | 4 ++-- editors/code/src/debug.ts | 5 +++-- editors/code/src/toolchain.ts | 2 +- editors/code/tests/unit/settings.test.ts | 2 +- editors/code/tsconfig.json | 3 +-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts index b38fa06a85..7b831a8a69 100644 --- a/editors/code/src/bootstrap.ts +++ b/editors/code/src/bootstrap.ts @@ -36,7 +36,7 @@ async function getServer( config: Config, state: PersistentState ): Promise { - const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath; + const explicitPath = process.env["__RA_LSP_SERVER_DEBUG"] ?? config.serverPath; if (explicitPath) { if (explicitPath.startsWith("~/")) { return os.homedir() + explicitPath.slice("~".length); diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 9595ba0528..deea958f8d 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -339,7 +339,7 @@ export function substituteVariablesInEnv(env: Env): Env { const depRe = new RegExp(/\${(?.+?)}/g); let match = undefined; while ((match = depRe.exec(value))) { - const depName = unwrapUndefinable(match.groups?.depName); + const depName = unwrapUndefinable(match.groups?.["depName"]); deps.add(depName); // `depName` at this point can have a form of `expression` or // `prefix:expression` @@ -448,7 +448,7 @@ function computeVscodeVar(varName: string): string | null { // https://github.com/microsoft/vscode/blob/08ac1bb67ca2459496b272d8f4a908757f24f56f/src/vs/workbench/api/common/extHostVariableResolverService.ts#L81 // or // https://github.com/microsoft/vscode/blob/29eb316bb9f154b7870eb5204ec7f2e7cf649bec/src/vs/server/node/remoteTerminalChannel.ts#L56 - execPath: () => process.env.VSCODE_EXEC_PATH ?? process.execPath, + execPath: () => process.env["VSCODE_EXEC_PATH"] ?? process.execPath, pathSeparator: () => path.sep, }; diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index ede90d707d..29758feafe 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -147,8 +147,9 @@ async function getDebugConfiguration( debugConfig.name = `run ${path.basename(executable)}`; } - if (debugConfig.cwd) { - debugConfig.cwd = simplifyPath(debugConfig.cwd); + const cwd = debugConfig["cwd"]; + if (cwd) { + debugConfig["cwd"] = simplifyPath(cwd); } return debugConfig; diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index b2c0c669db..014e6b66f6 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -176,7 +176,7 @@ export const getPathForExecutable = memoizeAsync( ); async function lookupInPath(exec: string): Promise { - const paths = process.env.PATH ?? ""; + const paths = process.env["PATH"] ?? ""; const candidates = paths.split(path.delimiter).flatMap((dirInPath) => { const candidate = path.join(dirInPath, exec); diff --git a/editors/code/tests/unit/settings.test.ts b/editors/code/tests/unit/settings.test.ts index 2cc1b670dc..f171f5f74f 100644 --- a/editors/code/tests/unit/settings.test.ts +++ b/editors/code/tests/unit/settings.test.ts @@ -57,7 +57,7 @@ export async function getTests(ctx: Context) { USING_VSCODE_VAR: "${workspaceFolderBasename}", }; const actualEnv = await substituteVariablesInEnv(envJson); - assert.deepStrictEqual(actualEnv.USING_VSCODE_VAR, "code"); + assert.deepStrictEqual(actualEnv["USING_VSCODE_VAR"], "code"); }); }); } diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json index 12d31f0bee..4b107a5d25 100644 --- a/editors/code/tsconfig.json +++ b/editors/code/tsconfig.json @@ -13,8 +13,7 @@ // These disables some enhancement type checking options // to update typescript version without any code change. "useUnknownInCatchVariables": false, - "exactOptionalPropertyTypes": false, - "noPropertyAccessFromIndexSignature": false + "exactOptionalPropertyTypes": false }, "exclude": ["node_modules", ".vscode-test"], "include": ["src", "tests"] From 1829d550acedeb067c8b3a7b4db6d5dd59277b14 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 6 Jul 2023 10:57:38 +0200 Subject: [PATCH 125/201] Unify getter and setter assists --- ...getter.rs => generate_getter_or_setter.rs} | 438 +++++++++++++----- .../src/handlers/generate_setter.rs | 175 ------- crates/ide-assists/src/lib.rs | 9 +- crates/ide-assists/src/tests/generated.rs | 2 +- 4 files changed, 324 insertions(+), 300 deletions(-) rename crates/ide-assists/src/handlers/{generate_getter.rs => generate_getter_or_setter.rs} (61%) delete mode 100644 crates/ide-assists/src/handlers/generate_setter.rs diff --git a/crates/ide-assists/src/handlers/generate_getter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs similarity index 61% rename from crates/ide-assists/src/handlers/generate_getter.rs rename to crates/ide-assists/src/handlers/generate_getter_or_setter.rs index dd6bbd84af..9c9478b040 100644 --- a/crates/ide-assists/src/handlers/generate_getter.rs +++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -1,4 +1,4 @@ -use ide_db::famous_defs::FamousDefs; +use ide_db::{famous_defs::FamousDefs, source_change::SourceChangeBuilder}; use stdx::{format_to, to_lower_snake_case}; use syntax::{ ast::{self, AstNode, HasName, HasVisibility}, @@ -10,6 +10,66 @@ use crate::{ AssistContext, AssistId, AssistKind, Assists, GroupLabel, }; +// Assist: generate_setter +// +// Generate a setter method. +// +// ``` +// struct Person { +// nam$0e: String, +// } +// ``` +// -> +// ``` +// struct Person { +// name: String, +// } +// +// impl Person { +// fn $0set_name(&mut self, name: String) { +// self.name = name; +// } +// } +// ``` +pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + // This if condition denotes two modes this assist can work in: + // - First is acting upon selection of record fields + // - Next is acting upon a single record field + // + // This is the only part where implementation diverges a bit, + // subsequent code is generic for both of these modes + + let (strukt, info_of_record_fields, mut fn_names) = extract_and_parse(ctx, AssistType::Set)?; + + // No record fields to do work on :( + if info_of_record_fields.len() == 0 { + return None; + } + + // Prepend set_ to fn names. + fn_names.iter_mut().for_each(|name| *name = format!("set_{}", name)); + + // Return early if we've found an existing fn + let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &fn_names)?; + + // Computing collective text range of all record fields in selected region + let target: TextRange = info_of_record_fields + .iter() + .map(|record_field_info| record_field_info.target) + .reduce(|acc, target| acc.cover(target))?; + + let setter_info = AssistInfo { impl_def, strukt, assist_type: AssistType::Set }; + + acc.add_group( + &GroupLabel("Generate getter/setter".to_owned()), + AssistId("generate_setter", AssistKind::Generate), + "Generate a setter method", + target, + |builder| build_source_change(builder, ctx, info_of_record_fields, setter_info), + ); + Some(()) +} + // Assist: generate_getter // // Generate a getter method. @@ -83,10 +143,16 @@ struct RecordFieldInfo { target: TextRange, } -struct GetterInfo { +struct AssistInfo { impl_def: Option, strukt: ast::Struct, - mutable: bool, + assist_type: AssistType, +} + +enum AssistType { + Get, + MutGet, + Set, } pub(crate) fn generate_getter_impl( @@ -94,40 +160,8 @@ pub(crate) fn generate_getter_impl( ctx: &AssistContext<'_>, mutable: bool, ) -> Option<()> { - // This if condition denotes two modes this assist can work in: - // - First is acting upon selection of record fields - // - Next is acting upon a single record field - // - // This is the only part where implementation diverges a bit, - // subsequent code is generic for both of these modes - - let (strukt, info_of_record_fields, fn_names) = if !ctx.has_empty_selection() { - // Selection Mode - let node = ctx.covering_element(); - - let node = match node { - syntax::NodeOrToken::Node(n) => n, - syntax::NodeOrToken::Token(t) => t.parent()?, - }; - - let parent_struct = node.ancestors().find_map(ast::Struct::cast)?; - - let (info_of_record_fields, field_names) = - extract_and_parse_record_fields(&parent_struct, ctx.selection_trimmed(), mutable)?; - - (parent_struct, info_of_record_fields, field_names) - } else { - // Single Record Field mode - let strukt = ctx.find_node_at_offset::()?; - let field = ctx.find_node_at_offset::()?; - - let record_field_info = parse_record_field(field, mutable)?; - - let fn_name = record_field_info.fn_name.clone(); - - (strukt, vec![record_field_info], vec![fn_name]) - }; - + let (strukt, info_of_record_fields, fn_names) = + extract_and_parse(ctx, if mutable { AssistType::MutGet } else { AssistType::Get })?; // No record fields to do work on :( if info_of_record_fields.len() == 0 { return None; @@ -147,98 +181,30 @@ pub(crate) fn generate_getter_impl( .map(|record_field_info| record_field_info.target) .reduce(|acc, target| acc.cover(target))?; - let getter_info = GetterInfo { impl_def, strukt, mutable }; + let getter_info = AssistInfo { + impl_def, + strukt, + assist_type: if mutable { AssistType::MutGet } else { AssistType::Get }, + }; acc.add_group( &GroupLabel("Generate getter/setter".to_owned()), AssistId(id, AssistKind::Generate), label, target, - |builder| { - let record_fields_count = info_of_record_fields.len(); - - let mut buf = String::with_capacity(512); - - // Check if an impl exists - if let Some(impl_def) = &getter_info.impl_def { - // Check if impl is empty - if let Some(assoc_item_list) = impl_def.assoc_item_list() { - if assoc_item_list.assoc_items().next().is_some() { - // If not empty then only insert a new line - buf.push('\n'); - } - } - } - - for (i, record_field_info) in info_of_record_fields.iter().enumerate() { - // this buf inserts a newline at the end of a getter - // automatically, if one wants to add one more newline - // for separating it from other assoc items, that needs - // to be handled separately - let mut getter_buf = - generate_getter_from_info(ctx, &getter_info, record_field_info); - - // Insert `$0` only for last getter we generate - if i == record_fields_count - 1 { - if ctx.config.snippet_cap.is_some() { - getter_buf = getter_buf.replacen("fn ", "fn $0", 1); - } - } - - // For first element we do not merge with '\n', as - // that can be inserted by impl_def check defined - // above, for other cases which are: - // - // - impl exists but it empty, here we would ideally - // not want to keep newline between impl { - // and fn () { line - // - // - next if impl itself does not exist, in this - // case we ourselves generate a new impl and that - // again ends up with the same reasoning as above - // for not keeping newline - if i == 0 { - buf = buf + &getter_buf; - } else { - buf = buf + "\n" + &getter_buf; - } - - // We don't insert a new line at the end of - // last getter as it will end up in the end - // of an impl where we would not like to keep - // getter and end of impl ( i.e. `}` ) with an - // extra line for no reason - if i < record_fields_count - 1 { - buf = buf + "\n"; - } - } - - let start_offset = getter_info - .impl_def - .as_ref() - .and_then(|impl_def| find_impl_block_end(impl_def.to_owned(), &mut buf)) - .unwrap_or_else(|| { - buf = generate_impl_text(&ast::Adt::Struct(getter_info.strukt.clone()), &buf); - getter_info.strukt.syntax().text_range().end() - }); - - match ctx.config.snippet_cap { - Some(cap) => builder.insert_snippet(cap, start_offset, buf), - None => builder.insert(start_offset, buf), - } - }, + |builder| build_source_change(builder, ctx, info_of_record_fields, getter_info), ) } fn generate_getter_from_info( ctx: &AssistContext<'_>, - info: &GetterInfo, + info: &AssistInfo, record_field_info: &RecordFieldInfo, ) -> String { let mut buf = String::with_capacity(512); let vis = info.strukt.visibility().map_or(String::new(), |v| format!("{v} ")); - let (ty, body) = if info.mutable { + let (ty, body) = if matches!(info.assist_type, AssistType::MutGet) { ( format!("&mut {}", record_field_info.field_ty), format!("&mut self.{}", record_field_info.field_name), @@ -273,7 +239,7 @@ fn generate_getter_from_info( }}", vis, record_field_info.fn_name, - info.mutable.then_some("mut ").unwrap_or_default(), + matches!(info.assist_type, AssistType::MutGet).then_some("mut ").unwrap_or_default(), ty, body, ); @@ -281,10 +247,58 @@ fn generate_getter_from_info( buf } +fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldInfo) -> String { + let mut buf = String::with_capacity(512); + let strukt = &info.strukt; + let fn_name = &record_field_info.fn_name; + let field_ty = &record_field_info.field_ty; + let vis = strukt.visibility().map_or(String::new(), |v| format!("{v} ")); + format_to!( + buf, + " {vis}fn set_{fn_name}(&mut self, {fn_name}: {field_ty}) {{ + self.{fn_name} = {fn_name}; + }}" + ); + + buf +} + +fn extract_and_parse( + ctx: &AssistContext<'_>, + assist_type: AssistType, +) -> Option<(ast::Struct, Vec, Vec)> { + // This if condition denotes two modes assists can work in: + // - First is acting upon selection of record fields + // - Next is acting upon a single record field + if !ctx.has_empty_selection() { + // Selection Mode + let node = ctx.covering_element(); + + let node = match node { + syntax::NodeOrToken::Node(n) => n, + syntax::NodeOrToken::Token(t) => t.parent()?, + }; + + let parent_struct = node.ancestors().find_map(ast::Struct::cast)?; + + let (info_of_record_fields, field_names) = + extract_and_parse_record_fields(&parent_struct, ctx.selection_trimmed(), &assist_type)?; + + return Some((parent_struct, info_of_record_fields, field_names)); + } + + // Single Record Field mode + let strukt = ctx.find_node_at_offset::()?; + let field = ctx.find_node_at_offset::()?; + let record_field_info = parse_record_field(field, &assist_type)?; + let fn_name = record_field_info.fn_name.clone(); + Some((strukt, vec![record_field_info], vec![fn_name])) +} + fn extract_and_parse_record_fields( node: &ast::Struct, selection_range: TextRange, - mutable: bool, + assist_type: &AssistType, ) -> Option<(Vec, Vec)> { let mut field_names: Vec = vec![]; let field_list = node.field_list()?; @@ -295,7 +309,7 @@ fn extract_and_parse_record_fields( .fields() .filter_map(|record_field| { if selection_range.contains_range(record_field.syntax().text_range()) { - let record_field_info = parse_record_field(record_field, mutable)?; + let record_field_info = parse_record_field(record_field, assist_type)?; field_names.push(record_field_info.fn_name.clone()); return Some(record_field_info); } @@ -316,12 +330,15 @@ fn extract_and_parse_record_fields( } } -fn parse_record_field(record_field: ast::RecordField, mutable: bool) -> Option { +fn parse_record_field( + record_field: ast::RecordField, + assist_type: &AssistType, +) -> Option { let field_name = record_field.name()?; let field_ty = record_field.ty()?; let mut fn_name = to_lower_snake_case(&field_name.to_string()); - if mutable { + if matches!(assist_type, AssistType::MutGet) { format_to!(fn_name, "_mut"); } @@ -330,8 +347,89 @@ fn parse_record_field(record_field: ast::RecordField, mutable: bool) -> Option, + info_of_record_fields: Vec, + assist_info: AssistInfo, +) { + let record_fields_count = info_of_record_fields.len(); + + let mut buf = String::with_capacity(512); + + // Check if an impl exists + if let Some(impl_def) = &assist_info.impl_def { + // Check if impl is empty + if let Some(assoc_item_list) = impl_def.assoc_item_list() { + if assoc_item_list.assoc_items().next().is_some() { + // If not empty then only insert a new line + buf.push('\n'); + } + } + } + + for (i, record_field_info) in info_of_record_fields.iter().enumerate() { + // this buf inserts a newline at the end of a getter + // automatically, if one wants to add one more newline + // for separating it from other assoc items, that needs + // to be handled separately + let mut getter_buf = match assist_info.assist_type { + AssistType::Set => generate_setter_from_info(&assist_info, record_field_info), + _ => generate_getter_from_info(ctx, &assist_info, record_field_info), + }; + + // Insert `$0` only for last getter we generate + if i == record_fields_count - 1 { + if ctx.config.snippet_cap.is_some() { + getter_buf = getter_buf.replacen("fn ", "fn $0", 1); + } + } + + // For first element we do not merge with '\n', as + // that can be inserted by impl_def check defined + // above, for other cases which are: + // + // - impl exists but it empty, here we would ideally + // not want to keep newline between impl { + // and fn () { line + // + // - next if impl itself does not exist, in this + // case we ourselves generate a new impl and that + // again ends up with the same reasoning as above + // for not keeping newline + if i == 0 { + buf = buf + &getter_buf; + } else { + buf = buf + "\n" + &getter_buf; + } + + // We don't insert a new line at the end of + // last getter as it will end up in the end + // of an impl where we would not like to keep + // getter and end of impl ( i.e. `}` ) with an + // extra line for no reason + if i < record_fields_count - 1 { + buf = buf + "\n"; + } + } + + let start_offset = assist_info + .impl_def + .as_ref() + .and_then(|impl_def| find_impl_block_end(impl_def.to_owned(), &mut buf)) + .unwrap_or_else(|| { + buf = generate_impl_text(&ast::Adt::Struct(assist_info.strukt.clone()), &buf); + assist_info.strukt.syntax().text_range().end() + }); + + match ctx.config.snippet_cap { + Some(cap) => builder.insert_snippet(cap, start_offset, buf), + None => builder.insert(start_offset, buf), + } +} + #[cfg(test)] -mod tests { +mod tests_getter { use crate::tests::{check_assist, check_assist_no_snippet_cap, check_assist_not_applicable}; use super::*; @@ -812,3 +910,105 @@ impl Context { ); } } + +#[cfg(test)] +mod tests_setter { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + fn check_not_applicable(ra_fixture: &str) { + check_assist_not_applicable(generate_setter, ra_fixture) + } + + #[test] + fn test_generate_setter_from_field() { + check_assist( + generate_setter, + r#" +struct Person { + dat$0a: T, +}"#, + r#" +struct Person { + data: T, +} + +impl Person { + fn $0set_data(&mut self, data: T) { + self.data = data; + } +}"#, + ); + } + + #[test] + fn test_generate_setter_already_implemented() { + check_not_applicable( + r#" +struct Person { + dat$0a: T, +} + +impl Person { + fn set_data(&mut self, data: T) { + self.data = data; + } +}"#, + ); + } + + #[test] + fn test_generate_setter_from_field_with_visibility_marker() { + check_assist( + generate_setter, + r#" +pub(crate) struct Person { + dat$0a: T, +}"#, + r#" +pub(crate) struct Person { + data: T, +} + +impl Person { + pub(crate) fn $0set_data(&mut self, data: T) { + self.data = data; + } +}"#, + ); + } + + #[test] + fn test_multiple_generate_setter() { + check_assist( + generate_setter, + r#" +struct Context { + data: T, + cou$0nt: usize, +} + +impl Context { + fn set_data(&mut self, data: T) { + self.data = data; + } +}"#, + r#" +struct Context { + data: T, + count: usize, +} + +impl Context { + fn set_data(&mut self, data: T) { + self.data = data; + } + + fn $0set_count(&mut self, count: usize) { + self.count = count; + } +}"#, + ); + } +} diff --git a/crates/ide-assists/src/handlers/generate_setter.rs b/crates/ide-assists/src/handlers/generate_setter.rs deleted file mode 100644 index 62f72df1c9..0000000000 --- a/crates/ide-assists/src/handlers/generate_setter.rs +++ /dev/null @@ -1,175 +0,0 @@ -use stdx::{format_to, to_lower_snake_case}; -use syntax::ast::{self, AstNode, HasName, HasVisibility}; - -use crate::{ - utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, - AssistContext, AssistId, AssistKind, Assists, GroupLabel, -}; - -// Assist: generate_setter -// -// Generate a setter method. -// -// ``` -// struct Person { -// nam$0e: String, -// } -// ``` -// -> -// ``` -// struct Person { -// name: String, -// } -// -// impl Person { -// fn set_name(&mut self, name: String) { -// self.name = name; -// } -// } -// ``` -pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let strukt = ctx.find_node_at_offset::()?; - let field = ctx.find_node_at_offset::()?; - - let field_name = field.name()?; - let field_ty = field.ty()?; - - // Return early if we've found an existing fn - let fn_name = to_lower_snake_case(&field_name.to_string()); - let impl_def = - find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &[format!("set_{fn_name}")])?; - - let target = field.syntax().text_range(); - acc.add_group( - &GroupLabel("Generate getter/setter".to_owned()), - AssistId("generate_setter", AssistKind::Generate), - "Generate a setter method", - target, - |builder| { - let mut buf = String::with_capacity(512); - - if impl_def.is_some() { - buf.push('\n'); - } - - let vis = strukt.visibility().map_or(String::new(), |v| format!("{v} ")); - format_to!( - buf, - " {vis}fn set_{fn_name}(&mut self, {fn_name}: {field_ty}) {{ - self.{fn_name} = {fn_name}; - }}" - ); - - let start_offset = impl_def - .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) - .unwrap_or_else(|| { - buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); - strukt.syntax().text_range().end() - }); - - builder.insert(start_offset, buf); - }, - ) -} - -#[cfg(test)] -mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; - - use super::*; - - fn check_not_applicable(ra_fixture: &str) { - check_assist_not_applicable(generate_setter, ra_fixture) - } - - #[test] - fn test_generate_setter_from_field() { - check_assist( - generate_setter, - r#" -struct Person { - dat$0a: T, -}"#, - r#" -struct Person { - data: T, -} - -impl Person { - fn set_data(&mut self, data: T) { - self.data = data; - } -}"#, - ); - } - - #[test] - fn test_generate_setter_already_implemented() { - check_not_applicable( - r#" -struct Person { - dat$0a: T, -} - -impl Person { - fn set_data(&mut self, data: T) { - self.data = data; - } -}"#, - ); - } - - #[test] - fn test_generate_setter_from_field_with_visibility_marker() { - check_assist( - generate_setter, - r#" -pub(crate) struct Person { - dat$0a: T, -}"#, - r#" -pub(crate) struct Person { - data: T, -} - -impl Person { - pub(crate) fn set_data(&mut self, data: T) { - self.data = data; - } -}"#, - ); - } - - #[test] - fn test_multiple_generate_setter() { - check_assist( - generate_setter, - r#" -struct Context { - data: T, - cou$0nt: usize, -} - -impl Context { - fn set_data(&mut self, data: T) { - self.data = data; - } -}"#, - r#" -struct Context { - data: T, - count: usize, -} - -impl Context { - fn set_data(&mut self, data: T) { - self.data = data; - } - - fn set_count(&mut self, count: usize) { - self.count = count; - } -}"#, - ); - } -} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index dc0a69971e..f23bb73ffc 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -154,11 +154,10 @@ mod handlers { mod generate_enum_variant; mod generate_from_impl_for_enum; mod generate_function; - mod generate_getter; + mod generate_getter_or_setter; mod generate_impl; mod generate_is_empty_from_len; mod generate_new; - mod generate_setter; mod generate_delegate_methods; mod add_return_type; mod inline_call; @@ -336,9 +335,9 @@ mod handlers { extract_function::extract_function, extract_module::extract_module, // - generate_getter::generate_getter, - generate_getter::generate_getter_mut, - generate_setter::generate_setter, + generate_getter_or_setter::generate_getter, + generate_getter_or_setter::generate_getter_mut, + generate_getter_or_setter::generate_setter, generate_delegate_methods::generate_delegate_methods, generate_deref::generate_deref, // diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index c6c70cc64a..4492b48e41 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1492,7 +1492,7 @@ struct Person { } impl Person { - fn set_name(&mut self, name: String) { + fn $0set_name(&mut self, name: String) { self.name = name; } } From 827a0530bc1ec9ab6f3b990af65819deece0b824 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Thu, 6 Jul 2023 20:35:54 +0900 Subject: [PATCH 126/201] Don't show `unresolved-field` diagnostic for missing names --- crates/hir-expand/src/name.rs | 9 +++++++++ crates/hir-ty/src/infer/expr.rs | 7 +++++++ .../ide-diagnostics/src/handlers/unresolved_field.rs | 12 +++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 01e499f2ae..0ffce333f1 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -96,6 +96,15 @@ impl Name { Name::new_inline("[missing name]") } + /// Returns true if this is a fake name for things missing in the source code. See + /// [`missing()`][Self::missing] for details. + /// + /// Use this method instead of comparing with `Self::missing()` as missing names + /// (ideally should) have a `gensym` semantics. + pub fn is_missing(&self) -> bool { + self == &Name::missing() + } + /// Generates a new name which is only equal to itself, by incrementing a counter. Due /// its implementation, it should not be used in things that salsa considers, like /// type names or field names, and it should be only used in names of local variables diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 8377eb2349..fd9522e86a 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -1449,6 +1449,13 @@ impl InferenceContext<'_> { fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none()); + + if name.is_missing() { + // Bail out early, don't even try to look up field. Also, we don't issue an unresolved + // field diagnostic because this is a syntax error rather than a semantic error. + return self.err_ty(); + } + match self.lookup_field(&receiver_ty, name) { Some((ty, field_id, adjustments, is_public)) => { self.write_expr_adj(receiver, adjustments); diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 94d49d4d48..0758706e45 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -68,7 +68,10 @@ fn method_fix( } #[cfg(test)] mod tests { - use crate::tests::check_diagnostics; + use crate::{ + tests::{check_diagnostics, check_diagnostics_with_config}, + DiagnosticsConfig, + }; #[test] fn smoke_test() { @@ -146,4 +149,11 @@ fn foo() { "#, ); } + + #[test] + fn no_diagnostic_for_missing_name() { + let mut config = DiagnosticsConfig::test_sample(); + config.disabled.insert("syntax-error".to_owned()); + check_diagnostics_with_config(config, "fn foo() { (). }"); + } } From 171ae2ee5d3256ac59412138a5566515cd785252 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 6 Jul 2023 15:41:52 +0330 Subject: [PATCH 127/201] Fix size_of_val and support min_align_of_val --- crates/hir-expand/src/name.rs | 5 + crates/hir-ty/src/consteval/tests.rs | 14 -- .../hir-ty/src/consteval/tests/intrinsics.rs | 75 ++++++++ crates/hir-ty/src/layout/adt.rs | 2 +- crates/hir-ty/src/mir/eval/shim.rs | 162 +++++++++++------- crates/hir-ty/src/mir/eval/shim/simd.rs | 50 ++++-- crates/ide/src/hover/tests.rs | 2 +- 7 files changed, 221 insertions(+), 89 deletions(-) diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 01e499f2ae..581ac40eb4 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -282,8 +282,10 @@ pub mod known { alloc, iter, ops, + fmt, future, result, + string, boxed, option, prelude, @@ -311,6 +313,7 @@ pub mod known { RangeToInclusive, RangeTo, Range, + String, Neg, Not, None, @@ -321,6 +324,7 @@ pub mod known { iter_mut, len, is_empty, + as_str, new, // Builtin macros asm, @@ -334,6 +338,7 @@ pub mod known { core_panic, env, file, + format, format_args_nl, format_args, global_asm, diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index c37c5912f2..d5feb6c7fe 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -2456,20 +2456,6 @@ fn const_trait_assoc() { ); } -#[test] -fn panic_messages() { - check_fail( - r#" - //- minicore: panic - const GOAL: u8 = { - let x: u16 = 2; - panic!("hello"); - }; - "#, - |e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())), - ); -} - #[test] fn exec_limits() { check_fail( diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index bb8fca178a..f51aceb87a 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -43,6 +43,50 @@ fn size_of_val() { "#, 12, ); + check_number( + r#" + //- minicore: coerce_unsized, transmute + use core::mem::transmute; + + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + struct X { + x: i64, + y: u8, + t: [i32], + } + + const GOAL: usize = unsafe { + let y: &X = transmute([0usize, 3]); + size_of_val(y) + }; + "#, + 24, + ); + check_number( + r#" + //- minicore: coerce_unsized, transmute + use core::mem::transmute; + + extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; + } + + struct X { + x: i32, + y: i64, + t: [u8], + } + + const GOAL: usize = unsafe { + let y: &X = transmute([0usize, 15]); + size_of_val(y) + }; + "#, + 32, + ); check_number( r#" //- minicore: coerce_unsized, fmt, builtin_impls @@ -74,6 +118,37 @@ fn size_of_val() { ); } +#[test] +fn min_align_of_val() { + check_number( + r#" + //- minicore: coerce_unsized + extern "rust-intrinsic" { + pub fn min_align_of_val(_: *const T) -> usize; + } + + struct X(i32, u8); + + const GOAL: usize = min_align_of_val(&X(1, 2)); + "#, + 4, + ); + check_number( + r#" + //- minicore: coerce_unsized + extern "rust-intrinsic" { + pub fn min_align_of_val(_: *const T) -> usize; + } + + const GOAL: usize = { + let x: &[i32] = &[1, 2, 3]; + min_align_of_val(x) + }; + "#, + 4, + ); +} + #[test] fn transmute() { check_number( diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 0012b4179d..186b8e2bc6 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -105,7 +105,7 @@ pub fn layout_of_adt_query( && variants .iter() .next() - .and_then(|x| x.last().map(|x| x.is_unsized())) + .and_then(|x| x.last().map(|x| !x.is_unsized())) .unwrap_or(true), ) .ok_or(LayoutError::SizeOverflow)? diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 73ed52f3b3..8fc93c85d8 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -3,6 +3,10 @@ use std::cmp; +use chalk_ir::TyKind; +use hir_def::resolver::HasResolver; +use hir_expand::mod_path::ModPath; + use super::*; mod simd; @@ -186,44 +190,24 @@ impl Evaluator<'_> { BeginPanic => Err(MirEvalError::Panic("".to_string())), PanicFmt => { let message = (|| { - let arguments_struct = - self.db.lang_item(self.crate_id, LangItem::FormatArguments)?.as_struct()?; - let arguments_layout = self - .layout_adt(arguments_struct.into(), Substitution::empty(Interner)) - .ok()?; - let arguments_field_pieces = - self.db.struct_data(arguments_struct).variant_data.field(&name![pieces])?; - let pieces_offset = arguments_layout - .fields - .offset(u32::from(arguments_field_pieces.into_raw()) as usize) - .bytes_usize(); - let ptr_size = self.ptr_size(); - let arg = args.next()?; - let pieces_array_addr = - Address::from_bytes(&arg[pieces_offset..pieces_offset + ptr_size]).ok()?; - let pieces_array_len = usize::from_le_bytes( - (&arg[pieces_offset + ptr_size..pieces_offset + 2 * ptr_size]) - .try_into() - .ok()?, - ); - let mut message = "".to_string(); - for i in 0..pieces_array_len { - let piece_ptr_addr = pieces_array_addr.offset(2 * i * ptr_size); - let piece_addr = - Address::from_bytes(self.read_memory(piece_ptr_addr, ptr_size).ok()?) - .ok()?; - let piece_len = usize::from_le_bytes( - self.read_memory(piece_ptr_addr.offset(ptr_size), ptr_size) - .ok()? - .try_into() - .ok()?, - ); - let piece_data = self.read_memory(piece_addr, piece_len).ok()?; - message += &std::string::String::from_utf8_lossy(piece_data); - } - Some(message) + let x = self.db.crate_def_map(self.crate_id).crate_root(); + let resolver = x.resolver(self.db.upcast()); + let Some(format_fn) = resolver.resolve_path_in_value_ns_fully( + self.db.upcast(), + &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( + hir_expand::mod_path::PathKind::Abs, + [name![std], name![fmt], name![format]].into_iter(), + )), + ) else { + not_supported!("std::fmt::format not found"); + }; + let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") }; + let message_string = self.interpret_mir(&*self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.cloned())?; + let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; + let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]); + Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned()) })() - .unwrap_or_else(|| "".to_string()); + .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}")); Err(MirEvalError::Panic(message)) } SliceLen => { @@ -544,6 +528,13 @@ impl Evaluator<'_> { let size = self.size_of_sized(ty, locals, "size_of arg")?; destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) } + "min_align_of" | "pref_align_of" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("align_of generic arg is not provided")); + }; + let align = self.layout(ty)?.align.abi.bytes(); + destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) + } "size_of_val" => { let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { @@ -552,33 +543,28 @@ impl Evaluator<'_> { let [arg] = args else { return Err(MirEvalError::TypeError("size_of_val args are not provided")); }; - let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2); - let size = match ty.kind(Interner) { - TyKind::Str => return destination.write_from_interval(self, metadata), - TyKind::Slice(inner) => { - let len = from_bytes!(usize, metadata.get(self)?); - len * self.size_of_sized(inner, locals, "slice inner type")? - } - TyKind::Dyn(_) => self.size_of_sized( - self.vtable_map.ty_of_bytes(metadata.get(self)?)?, - locals, - "dyn concrete type", - )?, - _ => self.size_of_sized( - ty, - locals, - "unsized type other than str, slice, and dyn", - )?, - }; - destination.write_from_bytes(self, &size.to_le_bytes()) + if let Some((size, _)) = self.size_align_of(ty, locals)? { + destination.write_from_bytes(self, &size.to_le_bytes()) + } else { + let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2); + let (size, _) = self.size_align_of_unsized(ty, metadata, locals)?; + destination.write_from_bytes(self, &size.to_le_bytes()) + } } - "min_align_of" | "pref_align_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) - else { - return Err(MirEvalError::TypeError("align_of generic arg is not provided")); + "min_align_of_val" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("min_align_of_val generic arg is not provided")); }; - let align = self.layout(ty)?.align.abi.bytes(); - destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) + let [arg] = args else { + return Err(MirEvalError::TypeError("min_align_of_val args are not provided")); + }; + if let Some((_, align)) = self.size_align_of(ty, locals)? { + destination.write_from_bytes(self, &align.to_le_bytes()) + } else { + let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2); + let (_, align) = self.size_align_of_unsized(ty, metadata, locals)?; + destination.write_from_bytes(self, &align.to_le_bytes()) + } } "needs_drop" => { let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) @@ -905,6 +891,58 @@ impl Evaluator<'_> { } } + fn size_align_of_unsized( + &mut self, + ty: &Ty, + metadata: Interval, + locals: &Locals<'_>, + ) -> Result<(usize, usize)> { + Ok(match ty.kind(Interner) { + TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), + TyKind::Slice(inner) => { + let len = from_bytes!(usize, metadata.get(self)?); + let (size, align) = self.size_align_of_sized(inner, locals, "slice inner type")?; + (size * len, align) + } + TyKind::Dyn(_) => self.size_align_of_sized( + self.vtable_map.ty_of_bytes(metadata.get(self)?)?, + locals, + "dyn concrete type", + )?, + TyKind::Adt(id, subst) => { + let id = id.0; + let layout = self.layout_adt(id, subst.clone())?; + let id = match id { + AdtId::StructId(s) => s, + _ => not_supported!("unsized enum or union"), + }; + let field_types = &self.db.field_types(id.into()); + let last_field_ty = + field_types.iter().rev().next().unwrap().1.clone().substitute(Interner, subst); + let sized_part_size = + layout.fields.offset(field_types.iter().count() - 1).bytes_usize(); + let sized_part_align = layout.align.abi.bytes() as usize; + let (unsized_part_size, unsized_part_align) = + self.size_align_of_unsized(&last_field_ty, metadata, locals)?; + let align = sized_part_align.max(unsized_part_align) as isize; + let size = (sized_part_size + unsized_part_size) as isize; + // Must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & -align` + let size = (size + (align - 1)) & (-align); + (size as usize, align as usize) + } + _ => not_supported!("unsized type other than str, slice, struct and dyn"), + }) + } + fn exec_atomic_intrinsic( &mut self, name: &str, diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs index 89b04eb36b..8ba6e18b9a 100644 --- a/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -1,5 +1,7 @@ //! Shim implementation for simd intrinsics +use std::cmp::Ordering; + use crate::TyKind; use super::*; @@ -22,10 +24,15 @@ macro_rules! not_supported { impl Evaluator<'_> { fn detect_simd_ty(&self, ty: &Ty) -> Result { match ty.kind(Interner) { - TyKind::Adt(_, subst) => { - let Some(len) = subst.as_slice(Interner).get(1).and_then(|x| x.constant(Interner)) - else { - return Err(MirEvalError::TypeError("simd type without len param")); + TyKind::Adt(id, subst) => { + let len = match subst.as_slice(Interner).get(1).and_then(|x| x.constant(Interner)) { + Some(len) => len, + _ => { + if let AdtId::StructId(id) = id.0 { + return Ok(self.db.struct_data(id).variant_data.fields().len()); + } + return Err(MirEvalError::TypeError("simd type with no len param")); + } }; match try_const_usize(self.db, len) { Some(x) => Ok(x as usize), @@ -63,13 +70,34 @@ impl Evaluator<'_> { .collect::>(); destination.write_from_bytes(self, &result) } - "eq" | "ne" => { + "eq" | "ne" | "lt" | "le" | "gt" | "ge" => { let [left, right] = args else { - return Err(MirEvalError::TypeError("simd_eq args are not provided")); + return Err(MirEvalError::TypeError("simd args are not provided")); }; - let result = left.get(self)? == right.get(self)?; - let result = result ^ (name == "ne"); - destination.write_from_bytes(self, &[u8::from(result)]) + let len = self.detect_simd_ty(&left.ty)?; + let size = left.interval.size / len; + let dest_size = destination.size / len; + let mut destination_bytes = vec![]; + let vector = left.get(self)?.chunks(size).zip(right.get(self)?.chunks(size)); + for (l, r) in vector { + let mut result = Ordering::Equal; + for (l, r) in l.iter().zip(r).rev() { + let x = l.cmp(r); + if x != Ordering::Equal { + result = x; + break; + } + } + let result = match result { + Ordering::Less => ["lt", "le", "ne"].contains(&name), + Ordering::Equal => ["ge", "le", "eq"].contains(&name), + Ordering::Greater => ["ge", "gt", "ne"].contains(&name), + }; + let result = if result { 255 } else { 0 }; + destination_bytes.extend(std::iter::repeat(result).take(dest_size)); + } + + destination.write_from_bytes(self, &destination_bytes) } "bitmask" => { let [op] = args else { @@ -103,9 +131,9 @@ impl Evaluator<'_> { } }; let left_len = self.detect_simd_ty(&left.ty)?; - let left_count = left.interval.size / left_len; + let left_size = left.interval.size / left_len; let vector = - left.get(self)?.chunks(left_count).chain(right.get(self)?.chunks(left_count)); + left.get(self)?.chunks(left_size).chain(right.get(self)?.chunks(left_size)); let mut result = vec![]; for index in index.get(self)?.chunks(index.interval.size / index_len) { let index = from_bytes!(u32, index) as usize; diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 2070386e98..00e21433da 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -674,7 +674,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } ``` ```rust - field_a: u8 // size = 1, align = 1, offset = 4 + field_a: u8 // size = 1, align = 1, offset = 6 ``` "#]], ); From 3a1054fc1c76a51b7195bf146f856270b32cc2e7 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 6 Jul 2023 17:33:17 +0330 Subject: [PATCH 128/201] Replace `x` with `it` --- crates/base-db/src/fixture.rs | 2 +- crates/cfg/src/lib.rs | 4 +- crates/hir-def/src/attr.rs | 8 +- crates/hir-def/src/body.rs | 4 +- crates/hir-def/src/body/lower.rs | 28 +-- crates/hir-def/src/generics.rs | 16 +- crates/hir-def/src/hir/type_ref.rs | 4 +- crates/hir-def/src/lib.rs | 84 +++---- crates/hir-def/src/path.rs | 2 +- crates/hir-def/src/pretty.rs | 4 +- crates/hir-def/src/resolver.rs | 48 ++-- crates/hir-expand/src/builtin_derive_macro.rs | 84 +++---- crates/hir-expand/src/builtin_fn_macro.rs | 10 +- crates/hir-expand/src/fixup.rs | 12 +- crates/hir-ty/src/chalk_ext.rs | 2 +- crates/hir-ty/src/consteval.rs | 8 +- .../hir-ty/src/consteval/tests/intrinsics.rs | 4 +- crates/hir-ty/src/display.rs | 33 +-- crates/hir-ty/src/infer.rs | 6 +- crates/hir-ty/src/infer/closure.rs | 50 ++-- crates/hir-ty/src/infer/expr.rs | 2 +- crates/hir-ty/src/infer/mutability.rs | 14 +- crates/hir-ty/src/infer/pat.rs | 2 +- crates/hir-ty/src/infer/unify.rs | 8 +- crates/hir-ty/src/layout.rs | 16 +- crates/hir-ty/src/layout/adt.rs | 12 +- crates/hir-ty/src/method_resolution.rs | 16 +- crates/hir-ty/src/mir/borrowck.rs | 14 +- crates/hir-ty/src/mir/eval.rs | 234 +++++++++--------- crates/hir-ty/src/mir/eval/shim.rs | 53 ++-- crates/hir-ty/src/mir/eval/shim/simd.rs | 31 +-- crates/hir-ty/src/mir/lower.rs | 123 ++++----- crates/hir-ty/src/mir/lower/as_place.rs | 18 +- crates/hir-ty/src/mir/monomorphization.rs | 22 +- crates/hir-ty/src/mir/pretty.rs | 38 +-- crates/hir/src/display.rs | 8 +- crates/hir/src/lib.rs | 44 ++-- crates/hir/src/semantics/source_to_def.rs | 7 +- crates/ide-completion/src/context/analysis.rs | 2 +- crates/ide/src/hover/render.rs | 8 +- crates/ide/src/static_index.rs | 53 ++-- crates/rust-analyzer/src/cli/lsif.rs | 34 +-- 42 files changed, 590 insertions(+), 582 deletions(-) diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index 1aa7b92645..6bfdf402c4 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -224,7 +224,7 @@ impl ChangeFixture { false, CrateOrigin::Local { repo: None, name: None }, default_target_data_layout - .map(|x| x.into()) + .map(|it| it.into()) .ok_or_else(|| "target_data_layout unset".into()), Some(toolchain), ); diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 495119d551..183b9b7d27 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -69,7 +69,7 @@ impl CfgOptions { } pub fn get_cfg_keys(&self) -> impl Iterator { - self.enabled.iter().map(|x| match x { + self.enabled.iter().map(|it| match it { CfgAtom::Flag(key) => key, CfgAtom::KeyValue { key, .. } => key, }) @@ -79,7 +79,7 @@ impl CfgOptions { &'a self, cfg_key: &'a str, ) -> impl Iterator + 'a { - self.enabled.iter().filter_map(move |x| match x { + self.enabled.iter().filter_map(move |it| match it { CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value), _ => None, }) diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index a16670b244..c29446d823 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -276,13 +276,13 @@ impl Attrs { } pub fn is_test(&self) -> bool { - self.iter().any(|x| { - x.path() + self.iter().any(|it| { + it.path() .segments() .iter() .rev() .zip(["core", "prelude", "v1", "test"].iter().rev()) - .all(|x| x.0.as_str() == Some(x.1)) + .all(|it| it.0.as_str() == Some(it.1)) }) } @@ -304,7 +304,7 @@ use std::slice::Iter as SliceIter; pub enum DocAtom { /// eg. `#[doc(hidden)]` Flag(SmolStr), - /// eg. `#[doc(alias = "x")]` + /// eg. `#[doc(alias = "it")]` /// /// Note that a key can have multiple values that are all considered "active" at the same time. /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`. diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 94dc39b117..f8d492d0e5 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -273,10 +273,10 @@ impl Body { pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool { match self.binding_owners.get(&binding) { - Some(x) => { + Some(it) => { // We assign expression ids in a way that outer closures will receive // a lower id - x.into_raw() < relative_to.into_raw() + it.into_raw() < relative_to.into_raw() } None => true, } diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index b89eb9fe62..152c02743f 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -297,11 +297,11 @@ impl ExprCollector<'_> { let (result_expr_id, prev_binding_owner) = this.initialize_binding_owner(syntax_ptr); let inner_expr = this.collect_block(e); - let x = this.db.intern_anonymous_const(ConstBlockLoc { + let it = this.db.intern_anonymous_const(ConstBlockLoc { parent: this.owner, root: inner_expr, }); - this.body.exprs[result_expr_id] = Expr::Const(x); + this.body.exprs[result_expr_id] = Expr::Const(it); this.current_binding_owner = prev_binding_owner; result_expr_id }) @@ -324,10 +324,10 @@ impl ExprCollector<'_> { ast::Expr::CallExpr(e) => { let is_rustc_box = { let attrs = e.attrs(); - attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box") + attrs.filter_map(|it| it.as_simple_atom()).any(|it| it == "rustc_box") }; if is_rustc_box { - let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next())); + let expr = self.collect_expr_opt(e.arg_list().and_then(|it| it.args().next())); self.alloc_expr(Expr::Box { expr }, syntax_ptr) } else { let callee = self.collect_expr_opt(e.expr()); @@ -781,7 +781,7 @@ impl ExprCollector<'_> { pat: self.alloc_pat_desugared(some_pat), guard: None, expr: self.with_opt_labeled_rib(label, |this| { - this.collect_expr_opt(e.loop_body().map(|x| x.into())) + this.collect_expr_opt(e.loop_body().map(|it| it.into())) }), }; let iter_name = Name::generate_new_name(); @@ -874,10 +874,10 @@ impl ExprCollector<'_> { }), guard: None, expr: { - let x = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone()); + let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone()); let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone()); let result = self.alloc_expr( - Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false }, + Expr::Call { callee, args: Box::new([it]), is_assignee_expr: false }, syntax_ptr.clone(), ); self.alloc_expr( @@ -1240,12 +1240,12 @@ impl ExprCollector<'_> { pats.push(self.collect_pat(first, binding_list)); binding_list.reject_new = true; for rest in it { - for (_, x) in binding_list.is_used.iter_mut() { - *x = false; + for (_, it) in binding_list.is_used.iter_mut() { + *it = false; } pats.push(self.collect_pat(rest, binding_list)); - for (&id, &x) in binding_list.is_used.iter() { - if !x { + for (&id, &is_used) in binding_list.is_used.iter() { + if !is_used { self.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); } @@ -1352,9 +1352,9 @@ impl ExprCollector<'_> { // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference. ast::Pat::RangePat(p) => { let mut range_part_lower = |p: Option| { - p.and_then(|x| match &x { - ast::Pat::LiteralPat(x) => { - Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0))) + p.and_then(|it| match &it { + ast::Pat::LiteralPat(it) => { + Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(it)?.0))) } ast::Pat::IdentPat(p) => { let name = diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index f19c3f028f..d7d44e4138 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -67,21 +67,21 @@ pub enum TypeOrConstParamData { impl TypeOrConstParamData { pub fn name(&self) -> Option<&Name> { match self { - TypeOrConstParamData::TypeParamData(x) => x.name.as_ref(), - TypeOrConstParamData::ConstParamData(x) => Some(&x.name), + TypeOrConstParamData::TypeParamData(it) => it.name.as_ref(), + TypeOrConstParamData::ConstParamData(it) => Some(&it.name), } } pub fn has_default(&self) -> bool { match self { - TypeOrConstParamData::TypeParamData(x) => x.default.is_some(), - TypeOrConstParamData::ConstParamData(x) => x.has_default, + TypeOrConstParamData::TypeParamData(it) => it.default.is_some(), + TypeOrConstParamData::ConstParamData(it) => it.has_default, } } pub fn type_param(&self) -> Option<&TypeParamData> { match self { - TypeOrConstParamData::TypeParamData(x) => Some(x), + TypeOrConstParamData::TypeParamData(it) => Some(it), TypeOrConstParamData::ConstParamData(_) => None, } } @@ -89,14 +89,14 @@ impl TypeOrConstParamData { pub fn const_param(&self) -> Option<&ConstParamData> { match self { TypeOrConstParamData::TypeParamData(_) => None, - TypeOrConstParamData::ConstParamData(x) => Some(x), + TypeOrConstParamData::ConstParamData(it) => Some(it), } } pub fn is_trait_self(&self) -> bool { match self { - TypeOrConstParamData::TypeParamData(x) => { - x.provenance == TypeParamProvenance::TraitSelf + TypeOrConstParamData::TypeParamData(it) => { + it.provenance == TypeParamProvenance::TraitSelf } TypeOrConstParamData::ConstParamData(_) => false, } diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index fa1f4933a2..57f023ef35 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -425,8 +425,8 @@ impl ConstRef { } match expr { ast::Expr::PathExpr(p) if is_path_ident(&p) => { - match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) { - Some(x) => Self::Path(x.as_name()), + match p.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) { + Some(it) => Self::Path(it.as_name()), None => Self::Scalar(LiteralConstRef::Unknown), } } diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index c0701e2576..1e74e2dfcb 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -406,14 +406,14 @@ impl TypeParamId { impl TypeParamId { /// Caller should check if this toc id really belongs to a type - pub fn from_unchecked(x: TypeOrConstParamId) -> Self { - Self(x) + pub fn from_unchecked(it: TypeOrConstParamId) -> Self { + Self(it) } } impl From for TypeOrConstParamId { - fn from(x: TypeParamId) -> Self { - x.0 + fn from(it: TypeParamId) -> Self { + it.0 } } @@ -432,14 +432,14 @@ impl ConstParamId { impl ConstParamId { /// Caller should check if this toc id really belongs to a const - pub fn from_unchecked(x: TypeOrConstParamId) -> Self { - Self(x) + pub fn from_unchecked(it: TypeOrConstParamId) -> Self { + Self(it) } } impl From for TypeOrConstParamId { - fn from(x: ConstParamId) -> Self { - x.0 + fn from(it: ConstParamId) -> Self { + it.0 } } @@ -562,14 +562,14 @@ pub enum TypeOwnerId { impl TypeOwnerId { fn as_generic_def_id(self) -> Option { Some(match self { - TypeOwnerId::FunctionId(x) => GenericDefId::FunctionId(x), - TypeOwnerId::ConstId(x) => GenericDefId::ConstId(x), - TypeOwnerId::AdtId(x) => GenericDefId::AdtId(x), - TypeOwnerId::TraitId(x) => GenericDefId::TraitId(x), - TypeOwnerId::TraitAliasId(x) => GenericDefId::TraitAliasId(x), - TypeOwnerId::TypeAliasId(x) => GenericDefId::TypeAliasId(x), - TypeOwnerId::ImplId(x) => GenericDefId::ImplId(x), - TypeOwnerId::EnumVariantId(x) => GenericDefId::EnumVariantId(x), + TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it), + TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it), + TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it), + TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it), + TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it), + TypeOwnerId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), + TypeOwnerId::ImplId(it) => GenericDefId::ImplId(it), + TypeOwnerId::EnumVariantId(it) => GenericDefId::EnumVariantId(it), TypeOwnerId::InTypeConstId(_) | TypeOwnerId::ModuleId(_) | TypeOwnerId::StaticId(_) => { return None } @@ -592,15 +592,15 @@ impl_from!( for TypeOwnerId ); -// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let x: Type = _; }`) +// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let it: Type = _; }`) impl From for TypeOwnerId { fn from(value: DefWithBodyId) -> Self { match value { - DefWithBodyId::FunctionId(x) => x.into(), - DefWithBodyId::StaticId(x) => x.into(), - DefWithBodyId::ConstId(x) => x.into(), - DefWithBodyId::InTypeConstId(x) => x.into(), - DefWithBodyId::VariantId(x) => x.into(), + DefWithBodyId::FunctionId(it) => it.into(), + DefWithBodyId::StaticId(it) => it.into(), + DefWithBodyId::ConstId(it) => it.into(), + DefWithBodyId::InTypeConstId(it) => it.into(), + DefWithBodyId::VariantId(it) => it.into(), } } } @@ -608,14 +608,14 @@ impl From for TypeOwnerId { impl From for TypeOwnerId { fn from(value: GenericDefId) -> Self { match value { - GenericDefId::FunctionId(x) => x.into(), - GenericDefId::AdtId(x) => x.into(), - GenericDefId::TraitId(x) => x.into(), - GenericDefId::TraitAliasId(x) => x.into(), - GenericDefId::TypeAliasId(x) => x.into(), - GenericDefId::ImplId(x) => x.into(), - GenericDefId::EnumVariantId(x) => x.into(), - GenericDefId::ConstId(x) => x.into(), + GenericDefId::FunctionId(it) => it.into(), + GenericDefId::AdtId(it) => it.into(), + GenericDefId::TraitId(it) => it.into(), + GenericDefId::TraitAliasId(it) => it.into(), + GenericDefId::TypeAliasId(it) => it.into(), + GenericDefId::ImplId(it) => it.into(), + GenericDefId::EnumVariantId(it) => it.into(), + GenericDefId::ConstId(it) => it.into(), } } } @@ -730,7 +730,7 @@ impl GeneralConstId { .const_data(const_id) .name .as_ref() - .and_then(|x| x.as_str()) + .and_then(|it| it.as_str()) .unwrap_or("_") .to_owned(), GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"), @@ -972,17 +972,17 @@ impl HasModule for MacroId { impl HasModule for TypeOwnerId { fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { match self { - TypeOwnerId::FunctionId(x) => x.lookup(db).module(db), - TypeOwnerId::StaticId(x) => x.lookup(db).module(db), - TypeOwnerId::ConstId(x) => x.lookup(db).module(db), - TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.module(db), - TypeOwnerId::AdtId(x) => x.module(db), - TypeOwnerId::TraitId(x) => x.lookup(db).container, - TypeOwnerId::TraitAliasId(x) => x.lookup(db).container, - TypeOwnerId::TypeAliasId(x) => x.lookup(db).module(db), - TypeOwnerId::ImplId(x) => x.lookup(db).container, - TypeOwnerId::EnumVariantId(x) => x.parent.lookup(db).container, - TypeOwnerId::ModuleId(x) => *x, + TypeOwnerId::FunctionId(it) => it.lookup(db).module(db), + TypeOwnerId::StaticId(it) => it.lookup(db).module(db), + TypeOwnerId::ConstId(it) => it.lookup(db).module(db), + TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db), + TypeOwnerId::AdtId(it) => it.module(db), + TypeOwnerId::TraitId(it) => it.lookup(db).container, + TypeOwnerId::TraitAliasId(it) => it.lookup(db).container, + TypeOwnerId::TypeAliasId(it) => it.lookup(db).module(db), + TypeOwnerId::ImplId(it) => it.lookup(db).container, + TypeOwnerId::EnumVariantId(it) => it.parent.lookup(db).container, + TypeOwnerId::ModuleId(it) => *it, } } } diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs index 409c9f8748..06530cc7eb 100644 --- a/crates/hir-def/src/path.rs +++ b/crates/hir-def/src/path.rs @@ -45,7 +45,7 @@ pub enum Path { /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`. generic_args: Option>]>>, }, - /// A link to a lang item. It is used in desugaring of things like `x?`. We can show these + /// A link to a lang item. It is used in desugaring of things like `it?`. We can show these /// links via a normal path since they might be private and not accessible in the usage place. LangItem(LangItemTarget), } diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs index 0aead6f37f..11d58a6ba0 100644 --- a/crates/hir-def/src/pretty.rs +++ b/crates/hir-def/src/pretty.rs @@ -12,8 +12,8 @@ use crate::{ }; pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result { - if let Path::LangItem(x) = path { - return write!(buf, "$lang_item::{x:?}"); + if let Path::LangItem(it) = path { + return write!(buf, "$lang_item::{it:?}"); } match path.type_anchor() { Some(anchor) => { diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 423bd00901..10f5702845 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -186,12 +186,12 @@ impl Resolver { Path::LangItem(l) => { return Some(( match *l { - LangItemTarget::Union(x) => TypeNs::AdtId(x.into()), - LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x), - LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()), - LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x), - LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()), - LangItemTarget::Trait(x) => TypeNs::TraitId(x), + LangItemTarget::Union(it) => TypeNs::AdtId(it.into()), + LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it), + LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()), + LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it), + LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()), + LangItemTarget::Trait(it) => TypeNs::TraitId(it), LangItemTarget::Function(_) | LangItemTarget::ImplDef(_) | LangItemTarget::Static(_) => return None, @@ -273,10 +273,10 @@ impl Resolver { Path::Normal { mod_path, .. } => mod_path, Path::LangItem(l) => { return Some(ResolveValueResult::ValueNs(match *l { - LangItemTarget::Function(x) => ValueNs::FunctionId(x), - LangItemTarget::Static(x) => ValueNs::StaticId(x), - LangItemTarget::Struct(x) => ValueNs::StructId(x), - LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x), + LangItemTarget::Function(it) => ValueNs::FunctionId(it), + LangItemTarget::Static(it) => ValueNs::StaticId(it), + LangItemTarget::Struct(it) => ValueNs::StructId(it), + LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), LangItemTarget::Union(_) | LangItemTarget::ImplDef(_) | LangItemTarget::TypeAlias(_) @@ -425,14 +425,14 @@ impl Resolver { /// The shadowing is accounted for: in /// /// ``` - /// let x = 92; + /// let it = 92; /// { - /// let x = 92; + /// let it = 92; /// $0 /// } /// ``` /// - /// there will be only one entry for `x` in the result. + /// there will be only one entry for `it` in the result. /// /// The result is ordered *roughly* from the innermost scope to the /// outermost: when the name is introduced in two namespaces in two scopes, @@ -1027,17 +1027,17 @@ impl HasResolver for ExternCrateId { impl HasResolver for TypeOwnerId { fn resolver(self, db: &dyn DefDatabase) -> Resolver { match self { - TypeOwnerId::FunctionId(x) => x.resolver(db), - TypeOwnerId::StaticId(x) => x.resolver(db), - TypeOwnerId::ConstId(x) => x.resolver(db), - TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.resolver(db), - TypeOwnerId::AdtId(x) => x.resolver(db), - TypeOwnerId::TraitId(x) => x.resolver(db), - TypeOwnerId::TraitAliasId(x) => x.resolver(db), - TypeOwnerId::TypeAliasId(x) => x.resolver(db), - TypeOwnerId::ImplId(x) => x.resolver(db), - TypeOwnerId::EnumVariantId(x) => x.resolver(db), - TypeOwnerId::ModuleId(x) => x.resolver(db), + TypeOwnerId::FunctionId(it) => it.resolver(db), + TypeOwnerId::StaticId(it) => it.resolver(db), + TypeOwnerId::ConstId(it) => it.resolver(db), + TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.resolver(db), + TypeOwnerId::AdtId(it) => it.resolver(db), + TypeOwnerId::TraitId(it) => it.resolver(db), + TypeOwnerId::TraitAliasId(it) => it.resolver(db), + TypeOwnerId::TypeAliasId(it) => it.resolver(db), + TypeOwnerId::ImplId(it) => it.resolver(db), + TypeOwnerId::EnumVariantId(it) => it.resolver(db), + TypeOwnerId::ModuleId(it) => it.resolver(db), } } } diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 3d1e272b90..78218a8345 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -72,12 +72,12 @@ enum VariantShape { } fn tuple_field_iterator(n: usize) -> impl Iterator { - (0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified())) + (0..n).map(|it| Ident::new(format!("f{it}"), tt::TokenId::unspecified())) } impl VariantShape { fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree { - self.as_pattern_map(path, |x| quote!(#x)) + self.as_pattern_map(path, |it| quote!(#it)) } fn field_names(&self) -> Vec { @@ -95,17 +95,17 @@ impl VariantShape { ) -> tt::Subtree { match self { VariantShape::Struct(fields) => { - let fields = fields.iter().map(|x| { - let mapped = field_map(x); - quote! { #x : #mapped , } + let fields = fields.iter().map(|it| { + let mapped = field_map(it); + quote! { #it : #mapped , } }); quote! { #path { ##fields } } } &VariantShape::Tuple(n) => { - let fields = tuple_field_iterator(n).map(|x| { - let mapped = field_map(&x); + let fields = tuple_field_iterator(n).map(|it| { + let mapped = field_map(&it); quote! { #mapped , } @@ -121,13 +121,13 @@ impl VariantShape { fn from(value: Option, token_map: &TokenMap) -> Result { let r = match value { None => VariantShape::Unit, - Some(FieldList::RecordFieldList(x)) => VariantShape::Struct( - x.fields() - .map(|x| x.name()) - .map(|x| name_to_token(token_map, x)) + Some(FieldList::RecordFieldList(it)) => VariantShape::Struct( + it.fields() + .map(|it| it.name()) + .map(|it| name_to_token(token_map, it)) .collect::>()?, ), - Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()), + Some(FieldList::TupleFieldList(it)) => VariantShape::Tuple(it.fields().count()), }; Ok(r) } @@ -141,7 +141,7 @@ enum AdtShape { impl AdtShape { fn as_pattern(&self, name: &tt::Ident) -> Vec { - self.as_pattern_map(name, |x| quote!(#x)) + self.as_pattern_map(name, |it| quote!(#it)) } fn field_names(&self) -> Vec> { @@ -214,8 +214,8 @@ fn parse_adt(tt: &tt::Subtree) -> Result { let default_variant = it .variant_list() .into_iter() - .flat_map(|x| x.variants()) - .position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into()))); + .flat_map(|it| it.variants()) + .position(|it| it.attrs().any(|it| it.simple_name() == Some("default".into()))); ( it.name(), it.generic_param_list(), @@ -224,11 +224,11 @@ fn parse_adt(tt: &tt::Subtree) -> Result { variants: it .variant_list() .into_iter() - .flat_map(|x| x.variants()) - .map(|x| { + .flat_map(|it| it.variants()) + .map(|it| { Ok(( - name_to_token(&token_map, x.name())?, - VariantShape::from(x.field_list(), &token_map)?, + name_to_token(&token_map, it.name())?, + VariantShape::from(it.field_list(), &token_map)?, )) }) .collect::>()?, @@ -246,16 +246,16 @@ fn parse_adt(tt: &tt::Subtree) -> Result { let name = { let this = param.name(); match this { - Some(x) => { - param_type_set.insert(x.as_name()); - mbe::syntax_node_to_token_tree(x.syntax()).0 + Some(it) => { + param_type_set.insert(it.as_name()); + mbe::syntax_node_to_token_tree(it.syntax()).0 } None => tt::Subtree::empty(), } }; let bounds = match ¶m { - ast::TypeOrConstParam::Type(x) => { - x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) + ast::TypeOrConstParam::Type(it) => { + it.type_bound_list().map(|it| mbe::syntax_node_to_token_tree(it.syntax()).0) } ast::TypeOrConstParam::Const(_) => None, }; @@ -296,7 +296,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result { let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name(); param_type_set.contains(&name).then_some(p) }) - .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) + .map(|it| mbe::syntax_node_to_token_tree(it.syntax()).0) .collect(); let name_token = name_to_token(&token_map, name)?; Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types }) @@ -373,10 +373,10 @@ fn expand_simple_derive( }) .unzip(); - where_block.extend(info.associated_types.iter().map(|x| { - let x = x.clone(); + where_block.extend(info.associated_types.iter().map(|it| { + let it = it.clone(); let bound = trait_path.clone(); - quote! { #x : #bound , } + quote! { #it : #bound , } })); let name = info.name; @@ -444,7 +444,7 @@ fn clone_expand( } let name = &adt.name; let patterns = adt.shape.as_pattern(name); - let exprs = adt.shape.as_pattern_map(name, |x| quote! { #x .clone() }); + let exprs = adt.shape.as_pattern_map(name, |it| quote! { #it .clone() }); let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| { let fat_arrow = fat_arrow(); quote! { @@ -524,10 +524,10 @@ fn debug_expand( expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| { let for_variant = |name: String, v: &VariantShape| match v { VariantShape::Struct(fields) => { - let for_fields = fields.iter().map(|x| { - let x_string = x.to_string(); + let for_fields = fields.iter().map(|it| { + let x_string = it.to_string(); quote! { - .field(#x_string, & #x) + .field(#x_string, & #it) } }); quote! { @@ -535,9 +535,9 @@ fn debug_expand( } } VariantShape::Tuple(n) => { - let for_fields = tuple_field_iterator(*n).map(|x| { + let for_fields = tuple_field_iterator(*n).map(|it| { quote! { - .field( & #x) + .field( & #it) } }); quote! { @@ -621,7 +621,7 @@ fn hash_expand( let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map( |(pat, names)| { let expr = { - let it = names.iter().map(|x| quote! { #x . hash(ra_expand_state); }); + let it = names.iter().map(|it| quote! { #it . hash(ra_expand_state); }); quote! { { ##it } } @@ -674,9 +674,9 @@ fn partial_eq_expand( quote!(true) } [first, rest @ ..] => { - let rest = rest.iter().map(|x| { - let t1 = Ident::new(format!("{}_self", x.text), x.span); - let t2 = Ident::new(format!("{}_other", x.text), x.span); + let rest = rest.iter().map(|it| { + let t1 = Ident::new(format!("{}_self", it.text), it.span); + let t2 = Ident::new(format!("{}_other", it.text), it.span); let and_and = and_and(); quote!(#and_and #t1 .eq( #t2 )) }); @@ -708,12 +708,12 @@ fn self_and_other_patterns( adt: &BasicAdtInfo, name: &tt::Ident, ) -> (Vec, Vec) { - let self_patterns = adt.shape.as_pattern_map(name, |x| { - let t = Ident::new(format!("{}_self", x.text), x.span); + let self_patterns = adt.shape.as_pattern_map(name, |it| { + let t = Ident::new(format!("{}_self", it.text), it.span); quote!(#t) }); - let other_patterns = adt.shape.as_pattern_map(name, |x| { - let t = Ident::new(format!("{}_other", x.text), x.span); + let other_patterns = adt.shape.as_pattern_map(name, |it| { + let t = Ident::new(format!("{}_other", it.text), it.span); quote!(#t) }); (self_patterns, other_patterns) diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 306bae45b4..7370f07a52 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -339,7 +339,7 @@ fn format_args_expand_general( parts.push(mem::take(&mut last_part)); let arg_tree = if argument.is_empty() { match args.next() { - Some(x) => x, + Some(it) => it, None => { err = Some(mbe::ExpandError::NoMatchingRule.into()); tt::Subtree::empty() @@ -378,11 +378,11 @@ fn format_args_expand_general( if !last_part.is_empty() { parts.push(last_part); } - let part_tts = parts.into_iter().map(|x| { + let part_tts = parts.into_iter().map(|it| { let text = if let Some(raw) = &raw_sharps { - format!("r{raw}\"{}\"{raw}", x).into() + format!("r{raw}\"{}\"{raw}", it).into() } else { - format!("\"{}\"", x).into() + format!("\"{}\"", it).into() }; let l = tt::Literal { span: tt::TokenId::unspecified(), text }; quote!(#l ,) @@ -574,7 +574,7 @@ fn concat_bytes_expand( syntax::SyntaxKind::BYTE => bytes.push(token.text().to_string()), syntax::SyntaxKind::BYTE_STRING => { let components = unquote_byte_string(lit).unwrap_or_default(); - components.into_iter().for_each(|x| bytes.push(x.to_string())); + components.into_iter().for_each(|it| bytes.push(it.to_string())); } _ => { err.get_or_insert(mbe::ExpandError::UnexpectedToken.into()); diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 00796e7c0d..af9d2d78dd 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -472,13 +472,13 @@ fn foo () {match __ra_fixup {}} check( r#" fn foo() { - match x { + match it { } } "#, expect![[r#" -fn foo () {match x {}} +fn foo () {match it {}} "#]], ) } @@ -547,11 +547,11 @@ fn foo () {a . __ra_fixup ; bar () ;} check( r#" fn foo() { - let x = a + let it = a } "#, expect![[r#" -fn foo () {let x = a ;} +fn foo () {let it = a ;} "#]], ) } @@ -561,11 +561,11 @@ fn foo () {let x = a ;} check( r#" fn foo() { - let x = a. + let it = a. } "#, expect![[r#" -fn foo () {let x = a . __ra_fixup ;} +fn foo () {let it = a . __ra_fixup ;} "#]], ) } diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 0e33b63939..c0b243ea24 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -343,7 +343,7 @@ impl TyExt for Ty { fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool { let crate_id = owner.module(db.upcast()).krate(); - let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) + let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|it| it.as_trait()) else { return false; }; diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 262341c6e9..537e28894a 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -88,7 +88,7 @@ pub(crate) fn path_to_const( ConstValue::Placeholder(to_placeholder_idx(db, p.into())) } ParamLoweringMode::Variable => match args.param_idx(p.into()) { - Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)), + Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), None => { never!( "Generic list doesn't contain this param: {:?}, {:?}, {:?}", @@ -139,11 +139,11 @@ pub fn intern_const_ref( let bytes = match value { LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. - let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16); + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) } LiteralConstRef::UInt(i) => { - let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16); + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) } LiteralConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()), @@ -171,7 +171,7 @@ pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { chalk_ir::ConstValue::InferenceVar(_) => None, chalk_ir::ConstValue::Placeholder(_) => None, chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))), + ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(&it, false))), ConstScalar::UnevaluatedConst(c, subst) => { let ec = db.const_eval(*c, subst.clone()).ok()?; try_const_usize(db, &ec) diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index f51aceb87a..b74133e24f 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -37,8 +37,8 @@ fn size_of_val() { } const GOAL: usize = { - let x: &[i32] = &[1, 2, 3]; - size_of_val(x) + let it: &[i32] = &[1, 2, 3]; + size_of_val(it) }; "#, 12, diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 7405b51e6e..b87047718d 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -481,28 +481,28 @@ fn render_const_scalar( TyKind::Scalar(s) => match s { Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }), Scalar::Char => { - let x = u128::from_le_bytes(pad16(b, false)) as u32; - let Ok(c) = char::try_from(x) else { + let it = u128::from_le_bytes(pad16(b, false)) as u32; + let Ok(c) = char::try_from(it) else { return f.write_str(""); }; write!(f, "{c:?}") } Scalar::Int(_) => { - let x = i128::from_le_bytes(pad16(b, true)); - write!(f, "{x}") + let it = i128::from_le_bytes(pad16(b, true)); + write!(f, "{it}") } Scalar::Uint(_) => { - let x = u128::from_le_bytes(pad16(b, false)); - write!(f, "{x}") + let it = u128::from_le_bytes(pad16(b, false)); + write!(f, "{it}") } Scalar::Float(fl) => match fl { chalk_ir::FloatTy::F32 => { - let x = f32::from_le_bytes(b.try_into().unwrap()); - write!(f, "{x:?}") + let it = f32::from_le_bytes(b.try_into().unwrap()); + write!(f, "{it:?}") } chalk_ir::FloatTy::F64 => { - let x = f64::from_le_bytes(b.try_into().unwrap()); - write!(f, "{x:?}") + let it = f64::from_le_bytes(b.try_into().unwrap()); + write!(f, "{it:?}") } }, }, @@ -659,8 +659,8 @@ fn render_const_scalar( } TyKind::FnDef(..) => ty.hir_fmt(f), TyKind::Function(_) | TyKind::Raw(_, _) => { - let x = u128::from_le_bytes(pad16(b, false)); - write!(f, "{:#X} as ", x)?; + let it = u128::from_le_bytes(pad16(b, false)); + write!(f, "{:#X} as ", it)?; ty.hir_fmt(f) } TyKind::Array(ty, len) => { @@ -736,7 +736,7 @@ fn render_variant_after_name( } write!(f, " }}")?; } else { - let mut it = it.map(|x| x.0); + let mut it = it.map(|it| it.0); write!(f, "(")?; if let Some(id) = it.next() { render_field(f, id)?; @@ -1278,19 +1278,20 @@ fn hir_fmt_generics( i: usize, parameters: &Substitution, ) -> bool { - if parameter.ty(Interner).map(|x| x.kind(Interner)) == Some(&TyKind::Error) + if parameter.ty(Interner).map(|it| it.kind(Interner)) + == Some(&TyKind::Error) { return true; } if let Some(ConstValue::Concrete(c)) = - parameter.constant(Interner).map(|x| &x.data(Interner).value) + parameter.constant(Interner).map(|it| &it.data(Interner).value) { if c.interned == ConstScalar::Unknown { return true; } } let default_parameter = match default_parameters.get(i) { - Some(x) => x, + Some(it) => it, None => return true, }; let actual_default = diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index d53644e279..0a617dae7d 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -290,7 +290,7 @@ impl Default for InternedStandardTypes { /// ``` /// /// Note that for a struct, the 'deep' unsizing of the struct is not recorded. -/// E.g., `struct Foo { x: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]> +/// E.g., `struct Foo { it: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]> /// The autoderef and -ref are the same as in the above example, but the type /// stored in `unsize` is `Foo<[i32]>`, we don't store any further detail about /// the underlying conversions from `[i32; 4]` to `[i32]`. @@ -1172,7 +1172,7 @@ impl<'a> InferenceContext<'a> { unresolved: Option, path: &ModPath, ) -> (Ty, Option) { - let remaining = unresolved.map(|x| path.segments()[x..].len()).filter(|x| x > &0); + let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0); match remaining { None => { let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { @@ -1324,7 +1324,7 @@ impl Expectation { /// The primary use case is where the expected type is a fat pointer, /// like `&[isize]`. For example, consider the following statement: /// - /// let x: &[isize] = &[1, 2, 3]; + /// let it: &[isize] = &[1, 2, 3]; /// /// In this case, the expected type for the `&[1, 2, 3]` expression is /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index ff64ae252b..972e5321a4 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -139,7 +139,7 @@ impl HirPlace { ) -> CaptureKind { match current_capture { CaptureKind::ByRef(BorrowKind::Mut { .. }) => { - if self.projections[len..].iter().any(|x| *x == ProjectionElem::Deref) { + if self.projections[len..].iter().any(|it| *it == ProjectionElem::Deref) { current_capture = CaptureKind::ByRef(BorrowKind::Unique); } } @@ -199,7 +199,7 @@ impl CapturedItem { .to_string(), VariantData::Tuple(fields) => fields .iter() - .position(|x| x.0 == f.local_id) + .position(|it| it.0 == f.local_id) .unwrap_or_default() .to_string(), VariantData::Unit => "[missing field]".to_string(), @@ -439,10 +439,10 @@ impl InferenceContext<'_> { } fn walk_expr(&mut self, tgt_expr: ExprId) { - if let Some(x) = self.result.expr_adjustments.get_mut(&tgt_expr) { + if let Some(it) = self.result.expr_adjustments.get_mut(&tgt_expr) { // FIXME: this take is completely unneeded, and just is here to make borrow checker // happy. Remove it if you can. - let x_taken = mem::take(x); + let x_taken = mem::take(it); self.walk_expr_with_adjust(tgt_expr, &x_taken); *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken; } else { @@ -536,7 +536,7 @@ impl InferenceContext<'_> { if let &Some(expr) = spread { self.consume_expr(expr); } - self.consume_exprs(fields.iter().map(|x| x.expr)); + self.consume_exprs(fields.iter().map(|it| it.expr)); } Expr::Field { expr, name: _ } => self.select_from_expr(*expr), Expr::UnaryOp { expr, op: UnaryOp::Deref } => { @@ -548,7 +548,7 @@ impl InferenceContext<'_> { } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) { let mutability = 'b: { if let Some(deref_trait) = - self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait()) + self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait()) { if let Some(deref_fn) = self.db.trait_data(deref_trait).method_by_name(&name![deref_mut]) @@ -615,8 +615,8 @@ impl InferenceContext<'_> { "We sort closures, so we should always have data for inner closures", ); let mut cc = mem::take(&mut self.current_captures); - cc.extend(captures.iter().filter(|x| self.is_upvar(&x.place)).map(|x| { - CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind, span: x.span } + cc.extend(captures.iter().filter(|it| self.is_upvar(&it.place)).map(|it| { + CapturedItemWithoutTy { place: it.place.clone(), kind: it.kind, span: it.span } })); self.current_captures = cc; } @@ -694,7 +694,7 @@ impl InferenceContext<'_> { }, }, } - if self.result.pat_adjustments.get(&p).map_or(false, |x| !x.is_empty()) { + if self.result.pat_adjustments.get(&p).map_or(false, |it| !it.is_empty()) { for_mut = BorrowKind::Unique; } self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); @@ -706,9 +706,9 @@ impl InferenceContext<'_> { fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { let mut ty = None; - if let Some(x) = self.result.expr_adjustments.get(&e) { - if let Some(x) = x.last() { - ty = Some(x.target.clone()); + if let Some(it) = self.result.expr_adjustments.get(&e) { + if let Some(it) = it.last() { + ty = Some(it.target.clone()); } } ty.unwrap_or_else(|| self.expr_ty(e)) @@ -727,7 +727,7 @@ impl InferenceContext<'_> { // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We // should probably let chalk know which closures are copy, but I don't know how doing it // without creating query cycles. - return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true); + return self.result.closure_info.get(id).map(|it| it.1 == FnTrait::Fn).unwrap_or(true); } self.table.resolve_completely(ty).is_copy(self.db, self.owner) } @@ -748,7 +748,7 @@ impl InferenceContext<'_> { } fn minimize_captures(&mut self) { - self.current_captures.sort_by_key(|x| x.place.projections.len()); + self.current_captures.sort_by_key(|it| it.place.projections.len()); let mut hash_map = HashMap::::new(); let result = mem::take(&mut self.current_captures); for item in result { @@ -759,7 +759,7 @@ impl InferenceContext<'_> { break Some(*k); } match it.next() { - Some(x) => lookup_place.projections.push(x.clone()), + Some(it) => lookup_place.projections.push(it.clone()), None => break None, } }; @@ -780,7 +780,7 @@ impl InferenceContext<'_> { } fn consume_with_pat(&mut self, mut place: HirPlace, pat: PatId) { - let cnt = self.result.pat_adjustments.get(&pat).map(|x| x.len()).unwrap_or_default(); + let cnt = self.result.pat_adjustments.get(&pat).map(|it| it.len()).unwrap_or_default(); place.projections = place .projections .iter() @@ -894,10 +894,10 @@ impl InferenceContext<'_> { fn closure_kind(&self) -> FnTrait { let mut r = FnTrait::Fn; - for x in &self.current_captures { + for it in &self.current_captures { r = cmp::min( r, - match &x.kind { + match &it.kind { CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => { FnTrait::FnMut } @@ -933,7 +933,7 @@ impl InferenceContext<'_> { } self.minimize_captures(); let result = mem::take(&mut self.current_captures); - let captures = result.into_iter().map(|x| x.with_ty(self)).collect::>(); + let captures = result.into_iter().map(|it| it.with_ty(self)).collect::>(); self.result.closure_info.insert(closure, (captures, closure_kind)); closure_kind } @@ -973,20 +973,20 @@ impl InferenceContext<'_> { fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec, ExprId)>)> { let mut deferred_closures = mem::take(&mut self.deferred_closures); let mut dependents_count: FxHashMap = - deferred_closures.keys().map(|x| (*x, 0)).collect(); + deferred_closures.keys().map(|it| (*it, 0)).collect(); for (_, deps) in &self.closure_dependencies { for dep in deps { *dependents_count.entry(*dep).or_default() += 1; } } let mut queue: Vec<_> = - deferred_closures.keys().copied().filter(|x| dependents_count[x] == 0).collect(); + deferred_closures.keys().copied().filter(|it| dependents_count[it] == 0).collect(); let mut result = vec![]; - while let Some(x) = queue.pop() { - if let Some(d) = deferred_closures.remove(&x) { - result.push((x, d)); + while let Some(it) = queue.pop() { + if let Some(d) = deferred_closures.remove(&it) { + result.push((it, d)); } - for dep in self.closure_dependencies.get(&x).into_iter().flat_map(|x| x.iter()) { + for dep in self.closure_dependencies.get(&it).into_iter().flat_map(|it| it.iter()) { let cnt = dependents_count.get_mut(dep).unwrap(); *cnt -= 1; if *cnt == 0 { diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index fd9522e86a..4b14345aa3 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -928,7 +928,7 @@ impl InferenceContext<'_> { if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) { if adjustments .last() - .map(|x| matches!(x.kind, Adjust::Borrow(_))) + .map(|it| matches!(it.kind, Adjust::Borrow(_))) .unwrap_or(true) { // prefer reborrow to move diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 2f307408c6..f517bc2c09 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -73,12 +73,12 @@ impl InferenceContext<'_> { self.infer_mut_expr(c, Mutability::Not); self.infer_mut_expr(body, Mutability::Not); } - Expr::MethodCall { receiver: x, method_name: _, args, generic_args: _ } - | Expr::Call { callee: x, args, is_assignee_expr: _ } => { - self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*x))); + Expr::MethodCall { receiver: it, method_name: _, args, generic_args: _ } + | Expr::Call { callee: it, args, is_assignee_expr: _ } => { + self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*it))); } Expr::Match { expr, arms } => { - let m = self.pat_iter_bound_mutability(arms.iter().map(|x| x.pat)); + let m = self.pat_iter_bound_mutability(arms.iter().map(|it| it.pat)); self.infer_mut_expr(*expr, m); for arm in arms.iter() { self.infer_mut_expr(arm.expr, Mutability::Not); @@ -96,7 +96,7 @@ impl InferenceContext<'_> { } } Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => { - self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread)) + self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) } &Expr::Index { base, index } => { if mutability == Mutability::Mut { @@ -204,8 +204,8 @@ impl InferenceContext<'_> { } /// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions - /// mutable. For example in `let (ref mut x0, ref x1) = *x;` we need to use `DerefMut` for `*x` but in - /// `let (ref x0, ref x1) = *x;` we should use `Deref`. + /// mutable. For example in `let (ref mut x0, ref x1) = *it;` we need to use `DerefMut` for `*it` but in + /// `let (ref x0, ref x1) = *it;` we should use `Deref`. fn pat_bound_mutability(&self, pat: PatId) -> Mutability { let mut r = Mutability::Not; self.body.walk_bindings_in_pat(pat, |b| { diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 415b4e1d9f..5da0ab76b8 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -306,7 +306,7 @@ impl InferenceContext<'_> { self.result .pat_adjustments .get(&pat) - .and_then(|x| x.first()) + .and_then(|it| it.first()) .unwrap_or(&self.result.type_of_pat[pat]) .clone() } diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index e322c5dbfe..385f39f537 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -91,7 +91,7 @@ pub(crate) fn unify( let mut table = InferenceTable::new(db, env); let vars = Substitution::from_iter( Interner, - tys.binders.iter(Interner).map(|x| match &x.kind { + tys.binders.iter(Interner).map(|it| match &it.kind { chalk_ir::VariableKind::Ty(_) => { GenericArgData::Ty(table.new_type_var()).intern(Interner) } @@ -686,8 +686,8 @@ impl<'a> InferenceTable<'a> { let mut arg_tys = vec![]; let arg_ty = TyBuilder::tuple(num_args) - .fill(|x| { - let arg = match x { + .fill(|it| { + let arg = match it { ParamKind::Type => self.new_type_var(), ParamKind::Const(ty) => { never!("Tuple with const parameter"); @@ -753,7 +753,7 @@ impl<'a> InferenceTable<'a> { { fold_tys_and_consts( ty, - |x, _| match x { + |it, _| match it { Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), }, diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 06be1f80ad..72e2bcc555 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -24,8 +24,8 @@ pub use self::{ }; macro_rules! user_error { - ($x: expr) => { - return Err(LayoutError::UserError(format!($x))) + ($it: expr) => { + return Err(LayoutError::UserError(format!($it))) }; } @@ -90,13 +90,13 @@ fn layout_of_simd_ty( // Supported SIMD vectors are homogeneous ADTs with at least one field: // // * #[repr(simd)] struct S(T, T, T, T); - // * #[repr(simd)] struct S { x: T, y: T, z: T, w: T } + // * #[repr(simd)] struct S { it: T, y: T, z: T, w: T } // * #[repr(simd)] struct S([T; 4]) // // where T is a primitive scalar (integer/float/pointer). let f0_ty = match fields.iter().next() { - Some(x) => x.1.clone().substitute(Interner, subst), + Some(it) => it.1.clone().substitute(Interner, subst), None => { user_error!("simd type with zero fields"); } @@ -230,7 +230,7 @@ pub fn layout_of_ty_query( .iter(Interner) .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), krate)) .collect::, _>>()?; - let fields = fields.iter().map(|x| &**x).collect::>(); + let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } @@ -348,14 +348,14 @@ pub fn layout_of_ty_query( let (captures, _) = infer.closure_info(c); let fields = captures .iter() - .map(|x| { + .map(|it| { db.layout_of_ty( - x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()), + it.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()), krate, ) }) .collect::, _>>()?; - let fields = fields.iter().map(|x| &**x).collect::>(); + let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized) .ok_or(LayoutError::Unknown)? diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 186b8e2bc6..19d5e98e73 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -72,9 +72,9 @@ pub fn layout_of_adt_query( }; let variants = variants .iter() - .map(|x| x.iter().map(|x| &**x).collect::>()) + .map(|it| it.iter().map(|it| &**it).collect::>()) .collect::>(); - let variants = variants.iter().map(|x| x.iter().collect()).collect(); + let variants = variants.iter().map(|it| it.iter().collect()).collect(); let result = if matches!(def, AdtId::UnionId(..)) { cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)? } else { @@ -105,7 +105,7 @@ pub fn layout_of_adt_query( && variants .iter() .next() - .and_then(|x| x.last().map(|x| !x.is_unsized())) + .and_then(|it| it.last().map(|it| !it.is_unsized())) .unwrap_or(true), ) .ok_or(LayoutError::SizeOverflow)? @@ -118,9 +118,9 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, let get = |name| { let attr = attrs.by_key(name).tt_values(); for tree in attr { - if let Some(x) = tree.token_trees.first() { - if let Ok(x) = x.to_string().parse() { - return Bound::Included(x); + if let Some(it) = tree.token_trees.first() { + if let Ok(it) = it.to_string().parse() { + return Bound::Included(it); } } } diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index c86d617fb5..5781ea48ee 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -559,10 +559,10 @@ impl ReceiverAdjustments { adjust.push(a); } if self.unsize_array { - ty = 'x: { + ty = 'it: { if let TyKind::Ref(m, l, inner) = ty.kind(Interner) { if let TyKind::Array(inner, _) = inner.kind(Interner) { - break 'x TyKind::Ref( + break 'it TyKind::Ref( m.clone(), l.clone(), TyKind::Slice(inner.clone()).intern(Interner), @@ -666,7 +666,7 @@ pub fn is_dyn_method( let self_ty = trait_ref.self_type_parameter(Interner); if let TyKind::Dyn(d) = self_ty.kind(Interner) { let is_my_trait_in_bounds = - d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() { + d.bounds.skip_binders().as_slice(Interner).iter().any(|it| match it.skip_binders() { // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter // what the generics are, we are sure that the method is come from the vtable. WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id, @@ -731,7 +731,7 @@ fn lookup_impl_assoc_item_for_trait_ref( let impls = db.trait_impls_in_deps(env.krate); let self_impls = match self_ty.kind(Interner) { TyKind::Adt(id, _) => { - id.0.module(db.upcast()).containing_block().map(|x| db.trait_impls_in_block(x)) + id.0.module(db.upcast()).containing_block().map(|it| db.trait_impls_in_block(it)) } _ => None, }; @@ -895,8 +895,8 @@ pub fn iterate_method_candidates_dyn( // (just as rustc does an autoderef and then autoref again). // We have to be careful about the order we're looking at candidates - // in here. Consider the case where we're resolving `x.clone()` - // where `x: &Vec<_>`. This resolves to the clone method with self + // in here. Consider the case where we're resolving `it.clone()` + // where `it: &Vec<_>`. This resolves to the clone method with self // type `Vec<_>`, *not* `&_`. I.e. we need to consider methods where // the receiver type exactly matches before cases where we have to // do autoref. But in the autoderef steps, the `&_` self type comes @@ -1480,8 +1480,8 @@ fn generic_implements_goal( .push(self_ty.value.clone()) .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) .build(); - kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|x| { - let vk = match x.data(Interner) { + kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|it| { + let vk = match it.data(Interner) { chalk_ir::GenericArgData::Ty(_) => { chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) } diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 0e6a63df41..ad98e8fa18 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -52,7 +52,7 @@ fn all_mir_bodies( let closures = body.closures.clone(); Box::new( iter::once(Ok(body)) - .chain(closures.into_iter().flat_map(|x| for_closure(db, x))), + .chain(closures.into_iter().flat_map(|it| for_closure(db, it))), ) } Err(e) => Box::new(iter::once(Err(e))), @@ -62,7 +62,7 @@ fn all_mir_bodies( Ok(body) => { let closures = body.closures.clone(); Box::new( - iter::once(Ok(body)).chain(closures.into_iter().flat_map(|x| for_closure(db, x))), + iter::once(Ok(body)).chain(closures.into_iter().flat_map(|it| for_closure(db, it))), ) } Err(e) => Box::new(iter::once(Err(e))), @@ -171,7 +171,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec } TerminatorKind::Call { func, args, .. } => { for_operand(func, terminator.span); - args.iter().for_each(|x| for_operand(x, terminator.span)); + args.iter().for_each(|it| for_operand(it, terminator.span)); } TerminatorKind::Assert { cond, .. } => { for_operand(cond, terminator.span); @@ -245,7 +245,7 @@ fn ever_initialized_map( body: &MirBody, ) -> ArenaMap> { let mut result: ArenaMap> = - body.basic_blocks.iter().map(|x| (x.0, ArenaMap::default())).collect(); + body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect(); fn dfs( db: &dyn HirDatabase, body: &MirBody, @@ -314,7 +314,7 @@ fn ever_initialized_map( result[body.start_block].insert(l, true); dfs(db, body, body.start_block, l, &mut result); } - for l in body.locals.iter().map(|x| x.0) { + for l in body.locals.iter().map(|it| it.0) { if !result[body.start_block].contains_idx(l) { result[body.start_block].insert(l, false); dfs(db, body, body.start_block, l, &mut result); @@ -328,10 +328,10 @@ fn mutability_of_locals( body: &MirBody, ) -> ArenaMap { let mut result: ArenaMap = - body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect(); + body.locals.iter().map(|it| (it.0, MutabilityReason::Not)).collect(); let mut push_mut_span = |local, span| match &mut result[local] { MutabilityReason::Mut { spans } => spans.push(span), - x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] }, + it @ MutabilityReason::Not => *it = MutabilityReason::Mut { spans: vec![span] }, }; let ever_init_maps = ever_initialized_map(db, body); for (block_id, mut ever_init_map) in ever_init_maps.into_iter() { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 51a75cbbff..299efb7145 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -48,15 +48,15 @@ mod tests; macro_rules! from_bytes { ($ty:tt, $value:expr) => { ($ty::from_le_bytes(match ($value).try_into() { - Ok(x) => x, + Ok(it) => it, Err(_) => return Err(MirEvalError::TypeError(stringify!(mismatched size in constructing $ty))), })) }; } macro_rules! not_supported { - ($x: expr) => { - return Err(MirEvalError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirEvalError::NotSupported(format!($it))) }; } @@ -68,8 +68,8 @@ pub struct VTableMap { impl VTableMap { fn id(&mut self, ty: Ty) -> usize { - if let Some(x) = self.ty_to_id.get(&ty) { - return *x; + if let Some(it) = self.ty_to_id.get(&ty) { + return *it; } let id = self.id_to_ty.len(); self.id_to_ty.push(ty.clone()); @@ -237,17 +237,17 @@ const STACK_OFFSET: usize = 1 << 30; const HEAP_OFFSET: usize = 1 << 29; impl Address { - fn from_bytes(x: &[u8]) -> Result { - Ok(Address::from_usize(from_bytes!(usize, x))) + fn from_bytes(it: &[u8]) -> Result { + Ok(Address::from_usize(from_bytes!(usize, it))) } - fn from_usize(x: usize) -> Self { - if x > STACK_OFFSET { - Stack(x - STACK_OFFSET) - } else if x > HEAP_OFFSET { - Heap(x - HEAP_OFFSET) + fn from_usize(it: usize) -> Self { + if it > STACK_OFFSET { + Stack(it - STACK_OFFSET) + } else if it > HEAP_OFFSET { + Heap(it - HEAP_OFFSET) } else { - Invalid(x) + Invalid(it) } } @@ -257,23 +257,23 @@ impl Address { fn to_usize(&self) -> usize { let as_num = match self { - Stack(x) => *x + STACK_OFFSET, - Heap(x) => *x + HEAP_OFFSET, - Invalid(x) => *x, + Stack(it) => *it + STACK_OFFSET, + Heap(it) => *it + HEAP_OFFSET, + Invalid(it) => *it, }; as_num } fn map(&self, f: impl FnOnce(usize) -> usize) -> Address { match self { - Stack(x) => Stack(f(*x)), - Heap(x) => Heap(f(*x)), - Invalid(x) => Invalid(f(*x)), + Stack(it) => Stack(f(*it)), + Heap(it) => Heap(f(*it)), + Invalid(it) => Invalid(f(*it)), } } fn offset(&self, offset: usize) -> Address { - self.map(|x| x + offset) + self.map(|it| it + offset) } } @@ -329,11 +329,11 @@ impl MirEvalError { let source_map = db.body_with_source_map(*def).1; let span: InFile = match span { MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { - Ok(s) => s.map(|x| x.into()), + Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|x| match x { + Ok(s) => s.map(|it| match it { Either::Left(e) => e.into(), Either::Right(e) => e.into(), }), @@ -445,10 +445,10 @@ struct DropFlags { impl DropFlags { fn add_place(&mut self, p: Place) { - if p.iterate_over_parents().any(|x| self.need_drop.contains(&x)) { + if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) { return; } - self.need_drop.retain(|x| !p.is_parent(x)); + self.need_drop.retain(|it| !p.is_parent(it)); self.need_drop.insert(p); } @@ -477,7 +477,7 @@ pub fn interpret_mir( ) -> (Result, String, String) { let ty = body.locals[return_slot()].ty.clone(); let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused); - let x: Result = (|| { + let it: Result = (|| { if evaluator.ptr_size() != std::mem::size_of::() { not_supported!("targets with different pointer size from host"); } @@ -491,7 +491,7 @@ pub fn interpret_mir( return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); })(); ( - x, + it, String::from_utf8_lossy(&evaluator.stdout).into_owned(), String::from_utf8_lossy(&evaluator.stderr).into_owned(), ) @@ -540,7 +540,7 @@ impl Evaluator<'_> { fn ptr_size(&self) -> usize { match self.db.target_data_layout(self.crate_id) { - Some(x) => x.pointer_size.bytes_usize(), + Some(it) => it.pointer_size.bytes_usize(), None => 8, } } @@ -582,8 +582,8 @@ impl Evaluator<'_> { } else { None }; - let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?); - addr = Address::from_usize(x); + let it = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?); + addr = Address::from_usize(it); } ProjectionElem::Index(op) => { let offset = from_bytes!( @@ -599,13 +599,13 @@ impl Evaluator<'_> { let offset = if from_end { let len = match prev_ty.kind(Interner) { TyKind::Array(_, c) => match try_const_usize(self.db, c) { - Some(x) => x as u64, + Some(it) => it as u64, None => { not_supported!("indexing array with unknown const from end") } }, TyKind::Slice(_) => match metadata { - Some(x) => from_bytes!(u64, x.get(self)?), + Some(it) => from_bytes!(u64, it.get(self)?), None => not_supported!("slice place without metadata"), }, _ => not_supported!("bad type for const index"), @@ -625,8 +625,8 @@ impl Evaluator<'_> { _ => TyKind::Error.intern(Interner), }; metadata = match metadata { - Some(x) => { - let prev_len = from_bytes!(u64, x.get(self)?); + Some(it) => { + let prev_len = from_bytes!(u64, it.get(self)?); Some(IntervalOrOwned::Owned( (prev_len - from - to).to_le_bytes().to_vec(), )) @@ -649,8 +649,8 @@ impl Evaluator<'_> { Variants::Single { .. } => &layout, Variants::Multiple { variants, .. } => { &variants[match f.parent { - hir_def::VariantId::EnumVariantId(x) => { - RustcEnumVariantIdx(x.local_id) + hir_def::VariantId::EnumVariantId(it) => { + RustcEnumVariantIdx(it.local_id) } _ => { return Err(MirEvalError::TypeError( @@ -717,8 +717,8 @@ impl Evaluator<'_> { body: &MirBody, args: impl Iterator>, ) -> Result> { - if let Some(x) = self.stack_depth_limit.checked_sub(1) { - self.stack_depth_limit = x; + if let Some(it) = self.stack_depth_limit.checked_sub(1) { + self.stack_depth_limit = it; } else { return Err(MirEvalError::StackOverflow); } @@ -730,9 +730,9 @@ impl Evaluator<'_> { let addr = body .locals .iter() - .map(|(id, x)| { + .map(|(id, it)| { let (size, align) = self.size_align_of_sized( - &x.ty, + &it.ty, &locals, "no unsized local in extending stack", )?; @@ -763,8 +763,8 @@ impl Evaluator<'_> { } loop { let current_block = &body.basic_blocks[current_block_idx]; - if let Some(x) = self.execution_limit.checked_sub(1) { - self.execution_limit = x; + if let Some(it) = self.execution_limit.checked_sub(1) { + self.execution_limit = it; } else { return Err(MirEvalError::ExecutionLimitExceeded); } @@ -801,7 +801,7 @@ impl Evaluator<'_> { let fn_ty = self.operand_ty(func, &locals)?; let args = args .iter() - .map(|x| self.operand_ty_and_eval(x, &mut locals)) + .map(|it| self.operand_ty_and_eval(it, &mut locals)) .collect::>>()?; match &fn_ty.data(Interner).kind { TyKind::Function(_) => { @@ -824,7 +824,7 @@ impl Evaluator<'_> { terminator.span, )?; } - x => not_supported!("unknown function type {x:?}"), + it => not_supported!("unknown function type {it:?}"), } locals.drop_flags.add_place(destination.clone()); current_block_idx = target.expect("broken mir, function without target"); @@ -855,7 +855,7 @@ impl Evaluator<'_> { fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result { use IntervalOrOwned::*; Ok(match r { - Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?), + Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), Rvalue::Ref(_, p) => { let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?; let mut r = addr.to_bytes(); @@ -900,9 +900,9 @@ impl Evaluator<'_> { c[0] = 1 - c[0]; } else { match op { - UnOp::Not => c.iter_mut().for_each(|x| *x = !*x), + UnOp::Not => c.iter_mut().for_each(|it| *it = !*it), UnOp::Neg => { - c.iter_mut().for_each(|x| *x = !*x); + c.iter_mut().for_each(|it| *it = !*it); for k in c.iter_mut() { let o; (*k, o) = k.overflowing_add(1); @@ -967,8 +967,8 @@ impl Evaluator<'_> { }; Owned(r.to_le_bytes().into()) } - x => not_supported!( - "invalid binop {x:?} on floating point operators" + it => not_supported!( + "invalid binop {it:?} on floating point operators" ), } } @@ -995,8 +995,8 @@ impl Evaluator<'_> { }; Owned(r.to_le_bytes().into()) } - x => not_supported!( - "invalid binop {x:?} on floating point operators" + it => not_supported!( + "invalid binop {it:?} on floating point operators" ), } } @@ -1076,12 +1076,12 @@ impl Evaluator<'_> { let result = self.compute_discriminant(ty, bytes)?; Owned(result.to_le_bytes().to_vec()) } - Rvalue::Repeat(x, len) => { + Rvalue::Repeat(it, len) => { let len = match try_const_usize(self.db, &len) { - Some(x) => x as usize, + Some(it) => it as usize, None => not_supported!("non evaluatable array len in repeat Rvalue"), }; - let val = self.eval_operand(x, locals)?.get(self)?; + let val = self.eval_operand(it, locals)?.get(self)?; let size = len * val.len(); Owned(val.iter().copied().cycle().take(size).collect()) } @@ -1097,13 +1097,13 @@ impl Evaluator<'_> { Rvalue::Aggregate(kind, values) => { let values = values .iter() - .map(|x| self.eval_operand(x, locals)) + .map(|it| self.eval_operand(it, locals)) .collect::>>()?; match kind { AggregateKind::Array(_) => { let mut r = vec![]; - for x in values { - let value = x.get(&self)?; + for it in values { + let value = it.get(&self)?; r.extend(value); } Owned(r) @@ -1114,11 +1114,12 @@ impl Evaluator<'_> { layout.size.bytes_usize(), &layout, None, - values.iter().map(|&x| x.into()), + values.iter().map(|&it| it.into()), )?) } - AggregateKind::Union(x, f) => { - let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?; + AggregateKind::Union(it, f) => { + let layout = + self.layout_adt((*it).into(), Substitution::empty(Interner))?; let offset = layout .fields .offset(u32::from(f.local_id.into_raw()) as usize) @@ -1128,14 +1129,14 @@ impl Evaluator<'_> { result[offset..offset + op.len()].copy_from_slice(op); Owned(result) } - AggregateKind::Adt(x, subst) => { + AggregateKind::Adt(it, subst) => { let (size, variant_layout, tag) = - self.layout_of_variant(*x, subst.clone(), locals)?; + self.layout_of_variant(*it, subst.clone(), locals)?; Owned(self.make_by_layout( size, &variant_layout, tag, - values.iter().map(|&x| x.into()), + values.iter().map(|&it| it.into()), )?) } AggregateKind::Closure(ty) => { @@ -1144,7 +1145,7 @@ impl Evaluator<'_> { layout.size.bytes_usize(), &layout, None, - values.iter().map(|&x| x.into()), + values.iter().map(|&it| it.into()), )?) } } @@ -1242,8 +1243,8 @@ impl Evaluator<'_> { as usize; let variant = variants .iter_enumerated() - .map(|(x, _)| x) - .filter(|x| x != untagged_variant) + .map(|(it, _)| it) + .filter(|it| it != untagged_variant) .nth(candidate_tag) .unwrap_or(*untagged_variant) .0; @@ -1264,15 +1265,15 @@ impl Evaluator<'_> { goal: impl Fn(&TyKind) -> Option, ) -> Result { let kind = ty.kind(Interner); - if let Some(x) = goal(kind) { - return Ok(x); + if let Some(it) = goal(kind) { + return Ok(it); } if let TyKind::Adt(id, subst) = kind { if let AdtId::StructId(struct_id) = id.0 { let field_types = self.db.field_types(struct_id.into()); let mut field_types = field_types.iter(); if let Some(ty) = - field_types.next().map(|x| x.1.clone().substitute(Interner, subst)) + field_types.next().map(|it| it.1.clone().substitute(Interner, subst)) { return self.coerce_unsized_look_through_fields(&ty, goal); } @@ -1287,8 +1288,8 @@ impl Evaluator<'_> { current_ty: &Ty, target_ty: &Ty, ) -> Result { - fn for_ptr(x: &TyKind) -> Option { - match x { + fn for_ptr(it: &TyKind) -> Option { + match it { TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()), _ => None, } @@ -1314,7 +1315,7 @@ impl Evaluator<'_> { None => { not_supported!("unevaluatble len of array in coerce unsized") } - Some(x) => x as usize, + Some(it) => it as usize, }; let mut r = Vec::with_capacity(16); let addr = addr.get(self)?; @@ -1369,17 +1370,17 @@ impl Evaluator<'_> { fn layout_of_variant( &mut self, - x: VariantId, + it: VariantId, subst: Substitution, locals: &Locals<'_>, ) -> Result<(usize, Arc, Option<(usize, usize, i128)>)> { - let adt = x.adt_id(); + let adt = it.adt_id(); if let DefWithBodyId::VariantId(f) = locals.body.owner { - if let VariantId::EnumVariantId(x) = x { + if let VariantId::EnumVariantId(it) = it { if AdtId::from(f.parent) == adt { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy layout - let i = self.const_eval_discriminant(x)?; + let i = self.const_eval_discriminant(it)?; return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); } } @@ -1392,8 +1393,8 @@ impl Evaluator<'_> { .db .target_data_layout(self.crate_id) .ok_or(MirEvalError::TargetDataLayoutNotAvailable)?; - let enum_variant_id = match x { - VariantId::EnumVariantId(x) => x, + let enum_variant_id = match it { + VariantId::EnumVariantId(it) => it, _ => not_supported!("multi variant layout for non-enums"), }; let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id); @@ -1407,8 +1408,8 @@ impl Evaluator<'_> { } else { discriminant = (variants .iter_enumerated() - .filter(|(x, _)| x != untagged_variant) - .position(|(x, _)| x == rustc_enum_variant_idx) + .filter(|(it, _)| it != untagged_variant) + .position(|(it, _)| it == rustc_enum_variant_idx) .unwrap() as i128) .wrapping_add(*niche_start as i128); true @@ -1451,8 +1452,8 @@ impl Evaluator<'_> { Ok(result) } - fn eval_operand(&mut self, x: &Operand, locals: &mut Locals<'_>) -> Result { - Ok(match x { + fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'_>) -> Result { + Ok(match it { Operand::Copy(p) | Operand::Move(p) => { locals.drop_flags.remove_place(p); self.eval_place(p, locals)? @@ -1528,11 +1529,11 @@ impl Evaluator<'_> { return Ok(&[]); } let (mem, pos) = match addr { - Stack(x) => (&self.stack, x), - Heap(x) => (&self.heap, x), - Invalid(x) => { + Stack(it) => (&self.stack, it), + Heap(it) => (&self.heap, it), + Invalid(it) => { return Err(MirEvalError::UndefinedBehavior(format!( - "read invalid memory address {x} with size {size}" + "read invalid memory address {it} with size {size}" ))); } }; @@ -1545,11 +1546,11 @@ impl Evaluator<'_> { return Ok(()); } let (mem, pos) = match addr { - Stack(x) => (&mut self.stack, x), - Heap(x) => (&mut self.heap, x), - Invalid(x) => { + Stack(it) => (&mut self.stack, it), + Heap(it) => (&mut self.heap, it), + Invalid(it) => { return Err(MirEvalError::UndefinedBehavior(format!( - "write invalid memory address {x} with content {r:?}" + "write invalid memory address {it} with content {r:?}" ))); } }; @@ -1587,7 +1588,7 @@ impl Evaluator<'_> { /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result { match self.size_align_of(ty, locals)? { - Some(x) => Ok(x.0), + Some(it) => Ok(it.0), None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), } } @@ -1601,7 +1602,7 @@ impl Evaluator<'_> { what: &'static str, ) -> Result<(usize, usize)> { match self.size_align_of(ty, locals)? { - Some(x) => Ok(x), + Some(it) => Ok(it), None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), } } @@ -1751,14 +1752,14 @@ impl Evaluator<'_> { match size { Some(_) => { let current = from_bytes!(usize, self.read_memory(addr, my_size)?); - if let Some(x) = patch_map.get(¤t) { - self.write_memory(addr, &x.to_le_bytes())?; + if let Some(it) = patch_map.get(¤t) { + self.write_memory(addr, &it.to_le_bytes())?; } } None => { let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?); - if let Some(x) = patch_map.get(¤t) { - self.write_memory(addr, &x.to_le_bytes())?; + if let Some(it) = patch_map.get(¤t) { + self.write_memory(addr, &it.to_le_bytes())?; } } } @@ -1848,7 +1849,7 @@ impl Evaluator<'_> { generic_args.clone(), self.trait_env.clone(), ) - .map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?; + .map_err(|it| MirEvalError::MirLowerErrorForClosure(closure, it))?; let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some() { closure_data.addr.to_bytes() @@ -1856,7 +1857,7 @@ impl Evaluator<'_> { closure_data.get(self)?.to_owned() }; let arg_bytes = iter::once(Ok(closure_data)) - .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned()))) + .chain(args.iter().map(|it| Ok(it.get(&self)?.to_owned()))) .collect::>>()?; let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| { MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner) @@ -1890,7 +1891,7 @@ impl Evaluator<'_> { size, &variant_layout, tag, - args.iter().map(|x| x.interval.into()), + args.iter().map(|it| it.interval.into()), )?; destination.write_from_bytes(self, &result)?; } @@ -1901,7 +1902,7 @@ impl Evaluator<'_> { size, &variant_layout, tag, - args.iter().map(|x| x.interval.into()), + args.iter().map(|it| it.interval.into()), )?; destination.write_from_bytes(self, &result)?; } @@ -1929,7 +1930,7 @@ impl Evaluator<'_> { return Ok(()); } let arg_bytes = - args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::>>()?; + args.iter().map(|it| Ok(it.get(&self)?.to_owned())).collect::>>()?; if let Some(self_ty_idx) = is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone()) { @@ -1945,17 +1946,16 @@ impl Evaluator<'_> { ty: ty.clone(), }; let ty = GenericArgData::Ty(ty.clone()).intern(Interner); - let generics_for_target = - Substitution::from_iter( - Interner, - generic_args.iter(Interner).enumerate().map(|(i, x)| { - if i == self_ty_idx { - &ty - } else { - x - } - }), - ); + let generics_for_target = Substitution::from_iter( + Interner, + generic_args.iter(Interner).enumerate().map(|(i, it)| { + if i == self_ty_idx { + &ty + } else { + it + } + }), + ); return self.exec_fn_with_args( def, &args_for_target, @@ -2046,7 +2046,7 @@ impl Evaluator<'_> { let arg1 = { let ty = TyKind::Tuple( args.len(), - Substitution::from_iter(Interner, args.iter().map(|x| x.ty.clone())), + Substitution::from_iter(Interner, args.iter().map(|it| it.ty.clone())), ) .intern(Interner); let layout = self.layout(&ty)?; @@ -2054,7 +2054,7 @@ impl Evaluator<'_> { layout.size.bytes_usize(), &layout, None, - args.iter().map(|x| IntervalOrOwned::Borrowed(x.interval)), + args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)), )?; // FIXME: there is some leak here let size = layout.size.bytes_usize(); @@ -2129,7 +2129,7 @@ impl Evaluator<'_> { return Ok(()); } let metadata = match metadata { - Some(x) => x.get(self)?.to_vec(), + Some(it) => it.get(self)?.to_vec(), None => vec![], }; self.run_drop_glue_deep(ty, locals, addr, &metadata, span) @@ -2233,10 +2233,10 @@ impl Evaluator<'_> { } } -pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] { - let is_negative = is_signed && x.last().unwrap_or(&0) > &127; +pub fn pad16(it: &[u8], is_signed: bool) -> [u8; 16] { + let is_negative = is_signed && it.last().unwrap_or(&0) > &127; let fill_with = if is_negative { 255 } else { 0 }; - x.iter() + it.iter() .copied() .chain(iter::repeat(fill_with)) .take(16) diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 8fc93c85d8..ab8ecdcf17 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -14,15 +14,15 @@ mod simd; macro_rules! from_bytes { ($ty:tt, $value:expr) => { ($ty::from_le_bytes(match ($value).try_into() { - Ok(x) => x, + Ok(it) => it, Err(_) => return Err(MirEvalError::TypeError("mismatched size")), })) }; } macro_rules! not_supported { - ($x: expr) => { - return Err(MirEvalError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirEvalError::NotSupported(format!($it))) }; } @@ -102,25 +102,25 @@ impl Evaluator<'_> { let alloc_fn = function_data .attrs .iter() - .filter_map(|x| x.path().as_ident()) - .filter_map(|x| x.as_str()) - .find(|x| { + .filter_map(|it| it.path().as_ident()) + .filter_map(|it| it.as_str()) + .find(|it| { [ "rustc_allocator", "rustc_deallocator", "rustc_reallocator", "rustc_allocator_zeroed", ] - .contains(x) + .contains(it) }); if let Some(alloc_fn) = alloc_fn { self.exec_alloc_fn(alloc_fn, args, destination)?; return Ok(true); } - if let Some(x) = self.detect_lang_function(def) { + if let Some(it) = self.detect_lang_function(def) { let arg_bytes = - args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::>>()?; - let result = self.exec_lang_item(x, generic_args, &arg_bytes, locals, span)?; + args.iter().map(|it| Ok(it.get(&self)?.to_owned())).collect::>>()?; + let result = self.exec_lang_item(it, generic_args, &arg_bytes, locals, span)?; destination.write_from_bytes(self, &result)?; return Ok(true); } @@ -178,7 +178,7 @@ impl Evaluator<'_> { fn exec_lang_item( &mut self, - x: LangItem, + it: LangItem, generic_args: &Substitution, args: &[Vec], locals: &Locals<'_>, @@ -186,12 +186,11 @@ impl Evaluator<'_> { ) -> Result> { use LangItem::*; let mut args = args.iter(); - match x { + match it { BeginPanic => Err(MirEvalError::Panic("".to_string())), PanicFmt => { let message = (|| { - let x = self.db.crate_def_map(self.crate_id).crate_root(); - let resolver = x.resolver(self.db.upcast()); + let resolver = self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast()); let Some(format_fn) = resolver.resolve_path_in_value_ns_fully( self.db.upcast(), &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( @@ -219,7 +218,7 @@ impl Evaluator<'_> { } DropInPlace => { let ty = - generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)).ok_or( + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)).ok_or( MirEvalError::TypeError( "generic argument of drop_in_place is not provided", ), @@ -236,7 +235,7 @@ impl Evaluator<'_> { )?; Ok(vec![]) } - x => not_supported!("Executing lang item {x:?}"), + it => not_supported!("Executing lang item {it:?}"), } } @@ -521,7 +520,7 @@ impl Evaluator<'_> { } match name { "size_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; @@ -529,14 +528,14 @@ impl Evaluator<'_> { destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) } "min_align_of" | "pref_align_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("align_of generic arg is not provided")); }; let align = self.layout(ty)?.align.abi.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } "size_of_val" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("size_of_val generic arg is not provided")); }; @@ -552,7 +551,7 @@ impl Evaluator<'_> { } } "min_align_of_val" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("min_align_of_val generic arg is not provided")); }; let [arg] = args else { @@ -567,7 +566,7 @@ impl Evaluator<'_> { } } "needs_drop" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; @@ -694,7 +693,7 @@ impl Evaluator<'_> { _ => unreachable!(), }; let is_overflow = u128overflow - || ans.to_le_bytes()[op_size..].iter().any(|&x| x != 0 && x != 255); + || ans.to_le_bytes()[op_size..].iter().any(|&it| it != 0 && it != 255); let is_overflow = vec![u8::from(is_overflow)]; let layout = self.layout(&result_ty)?; let result = self.make_by_layout( @@ -713,7 +712,7 @@ impl Evaluator<'_> { "copy_nonoverlapping args are not provided", )); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError( "copy_nonoverlapping generic arg is not provided", @@ -732,7 +731,7 @@ impl Evaluator<'_> { let [ptr, offset] = args else { return Err(MirEvalError::TypeError("offset args are not provided")); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("offset generic arg is not provided")); }; @@ -842,7 +841,7 @@ impl Evaluator<'_> { let [arg] = args else { return Err(MirEvalError::TypeError("discriminant_value arg is not provided")); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError( "discriminant_value generic arg is not provided", @@ -872,7 +871,7 @@ impl Evaluator<'_> { if let Some(target) = self.db.lang_item(self.crate_id, LangItem::FnOnce) { if let Some(def) = target .as_trait() - .and_then(|x| self.db.trait_data(x).method_by_name(&name![call_once])) + .and_then(|it| self.db.trait_data(it).method_by_name(&name![call_once])) { return self.exec_fn_trait( def, @@ -954,7 +953,7 @@ impl Evaluator<'_> { ) -> Result<()> { // We are a single threaded runtime with no UB checking and no optimization, so // we can implement these as normal functions. - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided")); }; let Some(arg0) = args.get(0) else { diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs index 8ba6e18b9a..4c2e56fe2b 100644 --- a/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -9,15 +9,15 @@ use super::*; macro_rules! from_bytes { ($ty:tt, $value:expr) => { ($ty::from_le_bytes(match ($value).try_into() { - Ok(x) => x, + Ok(it) => it, Err(_) => return Err(MirEvalError::TypeError("mismatched size")), })) }; } macro_rules! not_supported { - ($x: expr) => { - return Err(MirEvalError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirEvalError::NotSupported(format!($it))) }; } @@ -25,7 +25,8 @@ impl Evaluator<'_> { fn detect_simd_ty(&self, ty: &Ty) -> Result { match ty.kind(Interner) { TyKind::Adt(id, subst) => { - let len = match subst.as_slice(Interner).get(1).and_then(|x| x.constant(Interner)) { + let len = match subst.as_slice(Interner).get(1).and_then(|it| it.constant(Interner)) + { Some(len) => len, _ => { if let AdtId::StructId(id) = id.0 { @@ -35,7 +36,7 @@ impl Evaluator<'_> { } }; match try_const_usize(self.db, len) { - Some(x) => Ok(x as usize), + Some(it) => Ok(it as usize), None => Err(MirEvalError::TypeError("simd type with unevaluatable len param")), } } @@ -61,10 +62,10 @@ impl Evaluator<'_> { .get(self)? .iter() .zip(right.get(self)?) - .map(|(&x, &y)| match name { - "and" => x & y, - "or" => x | y, - "xor" => x ^ y, + .map(|(&it, &y)| match name { + "and" => it & y, + "or" => it | y, + "xor" => it ^ y, _ => unreachable!(), }) .collect::>(); @@ -82,9 +83,9 @@ impl Evaluator<'_> { for (l, r) in vector { let mut result = Ordering::Equal; for (l, r) in l.iter().zip(r).rev() { - let x = l.cmp(r); - if x != Ordering::Equal { - result = x; + let it = l.cmp(r); + if it != Ordering::Equal { + result = it; break; } } @@ -107,7 +108,7 @@ impl Evaluator<'_> { let op_count = op.interval.size / op_len; let mut result: u64 = 0; for (i, val) in op.get(self)?.chunks(op_count).enumerate() { - if !val.iter().all(|&x| x == 0) { + if !val.iter().all(|&it| it == 0) { result |= 1 << i; } } @@ -123,7 +124,7 @@ impl Evaluator<'_> { )); }; let index_len = match try_const_usize(self.db, index_len) { - Some(x) => x as usize, + Some(it) => it as usize, None => { return Err(MirEvalError::TypeError( "simd type with unevaluatable len param", @@ -138,7 +139,7 @@ impl Evaluator<'_> { for index in index.get(self)?.chunks(index.interval.size / index_len) { let index = from_bytes!(u32, index) as usize; let val = match vector.clone().nth(index) { - Some(x) => x, + Some(it) => it, None => { return Err(MirEvalError::TypeError( "out of bound access in simd shuffle", diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 41a7738357..d8fa8cf01a 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -146,12 +146,12 @@ impl MirLowerError { ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?, } } - MirLowerError::MissingFunctionDefinition(owner, x) => { + MirLowerError::MissingFunctionDefinition(owner, it) => { let body = db.body(*owner); writeln!( f, "Missing function definition for {}", - body.pretty_print_expr(db.upcast(), *owner, *x) + body.pretty_print_expr(db.upcast(), *owner, *it) )?; } MirLowerError::TypeMismatch(e) => { @@ -202,15 +202,15 @@ impl MirLowerError { } macro_rules! not_supported { - ($x: expr) => { - return Err(MirLowerError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirLowerError::NotSupported(format!($it))) }; } macro_rules! implementation_error { - ($x: expr) => {{ - ::stdx::never!("MIR lower implementation bug: {}", format!($x)); - return Err(MirLowerError::ImplementationError(format!($x))); + ($it: expr) => {{ + ::stdx::never!("MIR lower implementation bug: {}", format!($it)); + return Err(MirLowerError::ImplementationError(format!($it))); }}; } @@ -490,8 +490,8 @@ impl<'ctx> MirLowerCtx<'ctx> { // It's probably a unit struct or a zero sized function, so no action is needed. Ok(Some(current)) } - x => { - not_supported!("unknown name {x:?} in value name space"); + it => { + not_supported!("unknown name {it:?} in value name space"); } } } @@ -799,8 +799,8 @@ impl<'ctx> MirLowerCtx<'ctx> { Expr::Yield { .. } => not_supported!("yield"), Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => { let spread_place = match spread { - &Some(x) => { - let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else { + &Some(it) => { + let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { return Ok(None); }; current = c; @@ -840,8 +840,8 @@ impl<'ctx> MirLowerCtx<'ctx> { Some(sp) => operands .into_iter() .enumerate() - .map(|(i, x)| match x { - Some(x) => x, + .map(|(i, it)| match it { + Some(it) => it, None => { let p = sp.project(ProjectionElem::Field(FieldId { @@ -893,7 +893,7 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(Some(current)) } Expr::Cast { expr, type_ref: _ } => { - let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); }; let source_ty = self.infer[*expr].clone(); @@ -901,7 +901,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.push_assignment( current, place, - Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, x, target_ty), + Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, it, target_ty), expr_id.into(), ); Ok(Some(current)) @@ -1003,8 +1003,8 @@ impl<'ctx> MirLowerCtx<'ctx> { .infer .expr_adjustments .get(lhs) - .and_then(|x| x.split_last()) - .map(|x| x.1) + .and_then(|it| it.split_last()) + .map(|it| it.1) .ok_or(MirLowerError::TypeError( "adjustment of binary op was missing", ))?; @@ -1107,15 +1107,15 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let mut lp = None; let mut rp = None; - if let Some(x) = lhs { - let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { + if let Some(it) = lhs { + let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else { return Ok(None); }; lp = Some(o); current = c; } - if let Some(x) = rhs { - let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { + if let Some(it) = rhs { + let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else { return Ok(None); }; rp = Some(o); @@ -1131,8 +1131,8 @@ impl<'ctx> MirLowerCtx<'ctx> { .variant_data .fields() .iter() - .map(|x| { - let o = match x.1.name.as_str() { + .map(|it| { + let o = match it.1.name.as_str() { Some("start") => lp.take(), Some("end") => rp.take(), Some("exhausted") => { @@ -1164,11 +1164,11 @@ impl<'ctx> MirLowerCtx<'ctx> { .projections .clone() .into_iter() - .map(|x| match x { + .map(|it| match it { ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(x) => ProjectionElem::Field(x), - ProjectionElem::TupleOrClosureField(x) => { - ProjectionElem::TupleOrClosureField(x) + ProjectionElem::Field(it) => ProjectionElem::Field(it), + ProjectionElem::TupleOrClosureField(it) => { + ProjectionElem::TupleOrClosureField(it) } ProjectionElem::ConstantIndex { offset, from_end } => { ProjectionElem::ConstantIndex { offset, from_end } @@ -1176,8 +1176,8 @@ impl<'ctx> MirLowerCtx<'ctx> { ProjectionElem::Subslice { from, to } => { ProjectionElem::Subslice { from, to } } - ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x), - ProjectionElem::Index(x) => match x {}, + ProjectionElem::OpaqueCast(it) => ProjectionElem::OpaqueCast(it), + ProjectionElem::Index(it) => match it {}, }) .collect(), }; @@ -1209,8 +1209,8 @@ impl<'ctx> MirLowerCtx<'ctx> { Expr::Tuple { exprs, is_assignee_expr: _ } => { let Some(values) = exprs .iter() - .map(|x| { - let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { + .map(|it| { + let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else { return Ok(None); }; current = c; @@ -1239,8 +1239,8 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let Some(values) = elements .iter() - .map(|x| { - let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { + .map(|it| { + let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else { return Ok(None); }; current = c; @@ -1285,7 +1285,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn placeholder_subst(&mut self) -> Substitution { let placeholder_subst = match self.owner.as_generic_def_id() { - Some(x) => TyBuilder::placeholder_subst(self.db, x), + Some(it) => TyBuilder::placeholder_subst(self.db, it), None => Substitution::empty(Interner), }; placeholder_subst @@ -1375,8 +1375,8 @@ impl<'ctx> MirLowerCtx<'ctx> { } hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(), hir_def::hir::Literal::Bool(b) => vec![*b as u8], - hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(), - hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(), + hir_def::hir::Literal::Int(it, _) => it.to_le_bytes()[0..size].into(), + hir_def::hir::Literal::Uint(it, _) => it.to_le_bytes()[0..size].into(), hir_def::hir::Literal::Float(f, _) => match size { 8 => f.into_f64().to_le_bytes().into(), 4 => f.into_f32().to_le_bytes().into(), @@ -1526,9 +1526,9 @@ impl<'ctx> MirLowerCtx<'ctx> { fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { let mut ty = None; - if let Some(x) = self.infer.expr_adjustments.get(&e) { - if let Some(x) = x.last() { - ty = Some(x.target.clone()); + if let Some(it) = self.infer.expr_adjustments.get(&e) { + if let Some(it) = it.last() { + ty = Some(it.target.clone()); } } ty.unwrap_or_else(|| self.expr_ty_without_adjust(e)) @@ -1550,7 +1550,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { match &self.discr_temp { - Some(x) => x.clone(), + Some(it) => it.clone(), None => { let tmp: Place = self .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown) @@ -1597,7 +1597,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } fn has_adjustments(&self, expr_id: ExprId) -> bool { - !self.infer.expr_adjustments.get(&expr_id).map(|x| x.is_empty()).unwrap_or(true) + !self.infer.expr_adjustments.get(&expr_id).map(|it| it.is_empty()).unwrap_or(true) } fn merge_blocks( @@ -1627,7 +1627,7 @@ impl<'ctx> MirLowerCtx<'ctx> { ))? .end { - Some(x) => x, + Some(it) => it, None => { let s = self.new_basic_block(); self.current_loop_blocks @@ -1751,10 +1751,10 @@ impl<'ctx> MirLowerCtx<'ctx> { pick_binding: impl Fn(BindingId) -> bool, ) -> Result { let base_param_count = self.result.param_locals.len(); - self.result.param_locals.extend(params.clone().map(|(x, ty)| { + self.result.param_locals.extend(params.clone().map(|(it, ty)| { let local_id = self.result.locals.alloc(Local { ty }); self.drop_scopes.last_mut().unwrap().locals.push(local_id); - if let Pat::Bind { id, subpat: None } = self.body[x] { + if let Pat::Bind { id, subpat: None } = self.body[it] { if matches!( self.body.bindings[id].mode, BindingAnnotation::Unannotated | BindingAnnotation::Mutable @@ -1795,7 +1795,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn binding_local(&self, b: BindingId) -> Result { match self.result.binding_locals.get(b) { - Some(x) => Ok(*x), + Some(it) => Ok(*it), None => { // FIXME: It should never happens, but currently it will happen in `const_dependent_on_local` test, which // is a hir lowering problem IMO. @@ -1945,7 +1945,7 @@ pub fn mir_body_for_closure_query( implementation_error!("closure has not callable sig"); }; let current = ctx.lower_params_and_bindings( - args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())), + args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())), |_| true, )?; if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { @@ -1964,34 +1964,35 @@ pub fn mir_body_for_closure_query( FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref], }; ctx.result.walk_places(|p| { - if let Some(x) = upvar_map.get(&p.local) { - let r = x.iter().find(|x| { - if p.projection.len() < x.0.place.projections.len() { + if let Some(it) = upvar_map.get(&p.local) { + let r = it.iter().find(|it| { + if p.projection.len() < it.0.place.projections.len() { return false; } - for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) { - match (x, y) { + for (it, y) in p.projection.iter().zip(it.0.place.projections.iter()) { + match (it, y) { (ProjectionElem::Deref, ProjectionElem::Deref) => (), - (ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (), + (ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (), ( - ProjectionElem::TupleOrClosureField(x), + ProjectionElem::TupleOrClosureField(it), ProjectionElem::TupleOrClosureField(y), - ) if x == y => (), + ) if it == y => (), _ => return false, } } true }); match r { - Some(x) => { + Some(it) => { p.local = closure_local; let mut next_projs = closure_projection.clone(); - next_projs.push(PlaceElem::TupleOrClosureField(x.1)); + next_projs.push(PlaceElem::TupleOrClosureField(it.1)); let prev_projs = mem::take(&mut p.projection); - if x.0.kind != CaptureKind::ByValue { + if it.0.kind != CaptureKind::ByValue { next_projs.push(ProjectionElem::Deref); } - next_projs.extend(prev_projs.iter().cloned().skip(x.0.place.projections.len())); + next_projs + .extend(prev_projs.iter().cloned().skip(it.0.place.projections.len())); p.projection = next_projs.into(); } None => err = Some(p.clone()), @@ -2051,8 +2052,8 @@ pub fn lower_to_mir( // need to take this input explicitly. root_expr: ExprId, ) -> Result { - if let Some((_, x)) = infer.type_mismatches().next() { - return Err(MirLowerError::TypeMismatch(x.clone())); + if let Some((_, it)) = infer.type_mismatches().next() { + return Err(MirLowerError::TypeMismatch(it.clone())); } let mut ctx = MirLowerCtx::new(db, owner, body, infer); // 0 is return local @@ -2078,7 +2079,7 @@ pub fn lower_to_mir( body.params .iter() .zip(callable_sig.params().iter()) - .map(|(x, y)| (*x, y.clone())), + .map(|(it, y)| (*it, y.clone())), binding_picker, )?; } diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index 79f6da48ce..d58777e2b6 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -5,8 +5,8 @@ use hir_def::{lang_item::lang_attr, FunctionId}; use hir_expand::name; macro_rules! not_supported { - ($x: expr) => { - return Err(MirLowerError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirLowerError::NotSupported(format!($it))) }; } @@ -34,7 +34,7 @@ impl MirLowerCtx<'_> { ) -> Result> { let ty = adjustments .last() - .map(|x| x.target.clone()) + .map(|it| it.target.clone()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); let place = self.temp(ty, prev_block, expr_id.into())?; let Some(current) = @@ -61,7 +61,7 @@ impl MirLowerCtx<'_> { if let Some((last, rest)) = adjustments.split_last() { match last.kind { Adjust::Deref(None) => { - let Some(mut x) = self.lower_expr_as_place_with_adjust( + let Some(mut it) = self.lower_expr_as_place_with_adjust( current, expr_id, upgrade_rvalue, @@ -70,8 +70,8 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - x.0 = x.0.project(ProjectionElem::Deref); - Ok(Some(x)) + it.0 = it.0.project(ProjectionElem::Deref); + Ok(Some(it)) } Adjust::Deref(Some(od)) => { let Some((r, current)) = self.lower_expr_as_place_with_adjust( @@ -87,7 +87,7 @@ impl MirLowerCtx<'_> { current, r, rest.last() - .map(|x| x.target.clone()) + .map(|it| it.target.clone()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)), last.target.clone(), expr_id.into(), @@ -253,8 +253,8 @@ impl MirLowerCtx<'_> { .infer .expr_adjustments .get(base) - .and_then(|x| x.split_last()) - .map(|x| x.1) + .and_then(|it| it.split_last()) + .map(|it| it.1) .unwrap_or(&[]); let Some((mut p_base, current)) = self.lower_expr_as_place_with_adjust(current, *base, true, adjusts)? diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index ce3f7a8e51..e4574c77e2 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -29,8 +29,8 @@ use crate::{ use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind}; macro_rules! not_supported { - ($x: expr) => { - return Err(MirLowerError::NotSupported(format!($x))) + ($it: expr) => { + return Err(MirLowerError::NotSupported(format!($it))) }; } @@ -97,16 +97,16 @@ impl FallibleTypeFolder for Filler<'_> { idx: chalk_ir::PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> std::result::Result, Self::Error> { - let x = from_placeholder_idx(self.db, idx); - let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { + let it = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { not_supported!("missing idx in generics"); }; Ok(self .subst .as_slice(Interner) .get(idx) - .and_then(|x| x.constant(Interner)) - .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))? + .and_then(|it| it.constant(Interner)) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))? .clone()) } @@ -115,16 +115,16 @@ impl FallibleTypeFolder for Filler<'_> { idx: chalk_ir::PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> std::result::Result { - let x = from_placeholder_idx(self.db, idx); - let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { + let it = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { not_supported!("missing idx in generics"); }; Ok(self .subst .as_slice(Interner) .get(idx) - .and_then(|x| x.ty(Interner)) - .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))? + .and_then(|it| it.ty(Interner)) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))? .clone()) } @@ -180,7 +180,7 @@ impl Filler<'_> { MirLowerError::GenericArgNotProvided( self.generics .as_ref() - .and_then(|x| x.iter().nth(b.index)) + .and_then(|it| it.iter().nth(b.index)) .unwrap() .0, self.subst.clone(), diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index ac23e77bd2..781ffaecad 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -135,7 +135,7 @@ impl<'a> MirPrettyCtx<'a> { fn for_closure(&mut self, closure: ClosureId) { let body = match self.db.mir_body_for_closure(closure) { - Ok(x) => x, + Ok(it) => it, Err(e) => { wln!(self, "// error in {closure:?}: {e:?}"); return; @@ -145,7 +145,7 @@ impl<'a> MirPrettyCtx<'a> { let indent = mem::take(&mut self.indent); let mut ctx = MirPrettyCtx { body: &body, - local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(), + local_to_binding: body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(), result, indent, ..*self @@ -167,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> { } fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self { - let local_to_binding = body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(); + let local_to_binding = body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(); MirPrettyCtx { body, db, @@ -315,17 +315,17 @@ impl<'a> MirPrettyCtx<'a> { } } } - ProjectionElem::TupleOrClosureField(x) => { + ProjectionElem::TupleOrClosureField(it) => { f(this, local, head); - w!(this, ".{}", x); + w!(this, ".{}", it); } ProjectionElem::Index(l) => { f(this, local, head); w!(this, "[{}]", this.local_name(*l).display(this.db)); } - x => { + it => { f(this, local, head); - w!(this, ".{:?}", x); + w!(this, ".{:?}", it); } } } @@ -356,14 +356,14 @@ impl<'a> MirPrettyCtx<'a> { } self.place(p); } - Rvalue::Aggregate(AggregateKind::Tuple(_), x) => { + Rvalue::Aggregate(AggregateKind::Tuple(_), it) => { w!(self, "("); - self.operand_list(x); + self.operand_list(it); w!(self, ")"); } - Rvalue::Aggregate(AggregateKind::Array(_), x) => { + Rvalue::Aggregate(AggregateKind::Array(_), it) => { w!(self, "["); - self.operand_list(x); + self.operand_list(it); w!(self, "]"); } Rvalue::Repeat(op, len) => { @@ -371,19 +371,19 @@ impl<'a> MirPrettyCtx<'a> { self.operand(op); w!(self, "; {}]", len.display(self.db)); } - Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => { + Rvalue::Aggregate(AggregateKind::Adt(_, _), it) => { w!(self, "Adt("); - self.operand_list(x); + self.operand_list(it); w!(self, ")"); } - Rvalue::Aggregate(AggregateKind::Closure(_), x) => { + Rvalue::Aggregate(AggregateKind::Closure(_), it) => { w!(self, "Closure("); - self.operand_list(x); + self.operand_list(it); w!(self, ")"); } - Rvalue::Aggregate(AggregateKind::Union(_, _), x) => { + Rvalue::Aggregate(AggregateKind::Union(_, _), it) => { w!(self, "Union("); - self.operand_list(x); + self.operand_list(it); w!(self, ")"); } Rvalue::Len(p) => { @@ -428,8 +428,8 @@ impl<'a> MirPrettyCtx<'a> { } } - fn operand_list(&mut self, x: &[Operand]) { - let mut it = x.iter(); + fn operand_list(&mut self, it: &[Operand]) { + let mut it = it.iter(); if let Some(first) = it.next() { self.operand(first); for op in it { diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 9a2090ab79..4de9c872ad 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -251,8 +251,8 @@ impl HirDisplay for GenericParam { impl HirDisplay for TypeOrConstParam { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self.split(f.db) { - either::Either::Left(x) => x.hir_fmt(f), - either::Either::Right(x) => x.hir_fmt(f), + either::Either::Left(it) => it.hir_fmt(f), + either::Either::Right(it) => it.hir_fmt(f), } } } @@ -303,11 +303,11 @@ fn write_generic_params( ) -> Result<(), HirDisplayError> { let params = f.db.generic_params(def); if params.lifetimes.is_empty() - && params.type_or_consts.iter().all(|x| x.1.const_param().is_none()) + && params.type_or_consts.iter().all(|it| it.1.const_param().is_none()) && params .type_or_consts .iter() - .filter_map(|x| x.1.type_param()) + .filter_map(|it| it.1.type_param()) .all(|param| !matches!(param.provenance, TypeParamProvenance::TypeParamList)) { return Ok(()); diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 3688fd0e83..4eaf99b0b8 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1235,7 +1235,7 @@ impl Adt { pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { let subst = db.generic_defaults(self.into()); subst.iter().any(|ty| match ty.skip_binders().data(Interner) { - GenericArgData::Ty(x) => x.is_unknown(), + GenericArgData::Ty(it) => it.is_unknown(), _ => false, }) } @@ -1636,11 +1636,11 @@ impl DefWithBody { for moof in &borrowck_result.moved_out_of_ref { let span: InFile = match moof.span { mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) { - Ok(s) => s.map(|x| x.into()), + Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) { - Ok(s) => s.map(|x| match x { + Ok(s) => s.map(|it| match it { Either::Left(e) => e.into(), Either::Right(e) => e.into(), }), @@ -1679,11 +1679,11 @@ impl DefWithBody { for span in spans { let span: InFile = match span { mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { - Ok(s) => s.map(|x| x.into()), + Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|x| match x { + Ok(s) => s.map(|it| match it { Either::Left(e) => e.into(), Either::Right(e) => e.into(), }), @@ -1696,7 +1696,7 @@ impl DefWithBody { } (mir::MutabilityReason::Not, true) => { if !infer.mutated_bindings_in_closure.contains(&binding_id) { - let should_ignore = matches!(body[binding_id].name.as_str(), Some(x) if x.starts_with("_")); + let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with("_")); if !should_ignore { acc.push(UnusedMut { local }.into()) } @@ -2294,7 +2294,7 @@ impl TypeAlias { pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { let subst = db.generic_defaults(self.id.into()); subst.iter().any(|ty| match ty.skip_binders().data(Interner) { - GenericArgData::Ty(x) => x.is_unknown(), + GenericArgData::Ty(it) => it.is_unknown(), _ => false, }) } @@ -2684,8 +2684,8 @@ impl GenericDef { let ty_params = generics.type_or_consts.iter().map(|(local_id, _)| { let toc = TypeOrConstParam { id: TypeOrConstParamId { parent: self.into(), local_id } }; match toc.split(db) { - Either::Left(x) => GenericParam::ConstParam(x), - Either::Right(x) => GenericParam::TypeParam(x), + Either::Left(it) => GenericParam::ConstParam(it), + Either::Right(it) => GenericParam::TypeParam(it), } }); self.lifetime_params(db) @@ -2733,14 +2733,14 @@ pub struct LocalSource { impl LocalSource { pub fn as_ident_pat(&self) -> Option<&ast::IdentPat> { match &self.source.value { - Either::Left(x) => Some(x), + Either::Left(it) => Some(it), Either::Right(_) => None, } } pub fn into_ident_pat(self) -> Option { match self.source.value { - Either::Left(x) => Some(x), + Either::Left(it) => Some(it), Either::Right(_) => None, } } @@ -2762,7 +2762,7 @@ impl LocalSource { } pub fn syntax_ptr(self) -> InFile { - self.source.map(|x| SyntaxNodePtr::new(x.syntax())) + self.source.map(|it| SyntaxNodePtr::new(it.syntax())) } } @@ -2821,13 +2821,13 @@ impl Local { Type::new(db, def, ty) } - /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = x;` + /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;` pub fn sources(self, db: &dyn HirDatabase) -> Vec { let (body, source_map) = db.body_with_source_map(self.parent); self.sources_(db, &body, &source_map).collect() } - /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;` + /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = it;` pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource { let (body, source_map) = db.body_with_source_map(self.parent); let src = self.sources_(db, &body, &source_map).next().unwrap(); @@ -3081,7 +3081,9 @@ impl TypeParam { let subst = TyBuilder::placeholder_subst(db, self.id.parent()); let ty = ty.substitute(Interner, &subst); match ty.data(Interner) { - GenericArgData::Ty(x) => Some(Type::new_with_resolver_inner(db, &resolver, x.clone())), + GenericArgData::Ty(it) => { + Some(Type::new_with_resolver_inner(db, &resolver, it.clone())) + } _ => None, } } @@ -3120,7 +3122,7 @@ impl ConstParam { pub fn name(self, db: &dyn HirDatabase) -> Name { let params = db.generic_params(self.id.parent()); match params.type_or_consts[self.id.local_id()].name() { - Some(x) => x.clone(), + Some(it) => it.clone(), None => { never!(); Name::missing() @@ -3177,8 +3179,8 @@ impl TypeOrConstParam { pub fn ty(self, db: &dyn HirDatabase) -> Type { match self.split(db) { - Either::Left(x) => x.ty(db), - Either::Right(x) => x.ty(db), + Either::Left(it) => it.ty(db), + Either::Right(it) => it.ty(db), } } } @@ -3676,9 +3678,9 @@ impl Type { }; let parent_subst = TyBuilder::subst_for_def(db, trait_id, None) .push(self.ty.clone()) - .fill(|x| { + .fill(|it| { // FIXME: this code is not covered in tests. - match x { + match it { ParamKind::Type => { GenericArgData::Ty(args.next().unwrap().ty.clone()).intern(Interner) } @@ -3845,7 +3847,7 @@ impl Type { pub fn as_array(&self, db: &dyn HirDatabase) -> Option<(Type, usize)> { if let TyKind::Array(ty, len) = &self.ty.kind(Interner) { - try_const_usize(db, len).map(|x| (self.derived(ty.clone()), x as usize)) + try_const_usize(db, len).map(|it| (self.derived(ty.clone()), it as usize)) } else { None } diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index c50ffa4f8b..b971ca6238 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -298,7 +298,7 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn type_param_to_def(&mut self, src: InFile) -> Option { let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into(); let dyn_map = self.cache_for(container, src.file_id); - dyn_map[keys::TYPE_PARAM].get(&src.value).copied().map(|x| TypeParamId::from_unchecked(x)) + dyn_map[keys::TYPE_PARAM].get(&src.value).copied().map(|it| TypeParamId::from_unchecked(it)) } pub(super) fn lifetime_param_to_def( @@ -316,7 +316,10 @@ impl SourceToDefCtx<'_, '_> { ) -> Option { let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into(); let dyn_map = self.cache_for(container, src.file_id); - dyn_map[keys::CONST_PARAM].get(&src.value).copied().map(|x| ConstParamId::from_unchecked(x)) + dyn_map[keys::CONST_PARAM] + .get(&src.value) + .copied() + .map(|it| ConstParamId::from_unchecked(it)) } pub(super) fn generic_param_to_def( diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 932233ad63..3ea5065903 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -733,7 +733,7 @@ fn classify_name_ref( return None; } let parent = match ast::Fn::cast(parent.parent()?) { - Some(x) => x.param_list(), + Some(it) => it.param_list(), None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(), }; diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 1362146413..ef33386a7e 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -422,10 +422,10 @@ pub(super) fn definition( |&it| { if !it.parent_enum(db).is_data_carrying(db) { match it.eval(db) { - Ok(x) => { - Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }) + Ok(it) => { + Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") }) } - Err(_) => it.value(db).map(|x| format!("{x:?}")), + Err(_) => it.value(db).map(|it| format!("{it:?}")), } } else { None @@ -437,7 +437,7 @@ pub(super) fn definition( Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.render_eval(db); match body { - Ok(x) => Some(x), + Ok(it) => Some(it), Err(_) => { let source = it.source(db)?; let mut body = source.value.body()?.syntax().clone(); diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 03c7ee478a..7101e8ed20 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -73,7 +73,7 @@ impl TokenStore { } pub fn iter(self) -> impl Iterator { - self.0.into_iter().enumerate().map(|(i, x)| (TokenId(i), x)) + self.0.into_iter().enumerate().map(|(id, data)| (TokenId(id), data)) } } @@ -132,9 +132,9 @@ impl StaticIndex<'_> { // hovers let sema = hir::Semantics::new(self.db); let tokens_or_nodes = sema.parse(file_id).syntax().clone(); - let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|x| match x { + let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|it| match it { syntax::NodeOrToken::Node(_) => None, - syntax::NodeOrToken::Token(x) => Some(x), + syntax::NodeOrToken::Token(it) => Some(it), }); let hover_config = HoverConfig { links_in_hover: true, @@ -154,28 +154,29 @@ impl StaticIndex<'_> { let range = token.text_range(); let node = token.parent().unwrap(); let def = match get_definition(&sema, token.clone()) { - Some(x) => x, + Some(it) => it, None => continue, }; - let id = if let Some(x) = self.def_map.get(&def) { - *x + let id = if let Some(it) = self.def_map.get(&def) { + *it } else { - let x = self.tokens.insert(TokenStaticData { + let it = self.tokens.insert(TokenStaticData { hover: hover_for_definition(&sema, file_id, def, &node, &hover_config), - definition: def - .try_to_nav(self.db) - .map(|x| FileRange { file_id: x.file_id, range: x.focus_or_full_range() }), + definition: def.try_to_nav(self.db).map(|it| FileRange { + file_id: it.file_id, + range: it.focus_or_full_range(), + }), references: vec![], moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), }); - self.def_map.insert(def, x); - x + self.def_map.insert(def, it); + it }; let token = self.tokens.get_mut(id).unwrap(); token.references.push(ReferenceData { range: FileRange { range, file_id }, is_definition: match def.try_to_nav(self.db) { - Some(x) => x.file_id == file_id && x.focus_or_full_range() == range, + Some(it) => it.file_id == file_id && it.focus_or_full_range() == range, None => false, }, }); @@ -216,8 +217,8 @@ impl StaticIndex<'_> { fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option { for token in sema.descend_into_macros(token) { let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); - if let Some(&[x]) = def.as_deref() { - return Some(x); + if let Some(&[it]) = def.as_deref() { + return Some(it); } } None @@ -233,14 +234,14 @@ mod tests { fn check_all_ranges(ra_fixture: &str) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); let s = StaticIndex::compute(&analysis); - let mut range_set: HashSet<_> = ranges.iter().map(|x| x.0).collect(); + let mut range_set: HashSet<_> = ranges.iter().map(|it| it.0).collect(); for f in s.files { for (range, _) in f.tokens { - let x = FileRange { file_id: f.file_id, range }; - if !range_set.contains(&x) { - panic!("additional range {x:?}"); + let it = FileRange { file_id: f.file_id, range }; + if !range_set.contains(&it) { + panic!("additional range {it:?}"); } - range_set.remove(&x); + range_set.remove(&it); } } if !range_set.is_empty() { @@ -251,17 +252,17 @@ mod tests { fn check_definitions(ra_fixture: &str) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); let s = StaticIndex::compute(&analysis); - let mut range_set: HashSet<_> = ranges.iter().map(|x| x.0).collect(); + let mut range_set: HashSet<_> = ranges.iter().map(|it| it.0).collect(); for (_, t) in s.tokens.iter() { - if let Some(x) = t.definition { - if x.range.start() == TextSize::from(0) { + if let Some(t) = t.definition { + if t.range.start() == TextSize::from(0) { // ignore definitions that are whole of file continue; } - if !range_set.contains(&x) { - panic!("additional definition {x:?}"); + if !range_set.contains(&t) { + panic!("additional definition {t:?}"); } - range_set.remove(&x); + range_set.remove(&t); } } if !range_set.is_empty() { diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index bb5016349a..42d180114e 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -48,8 +48,8 @@ struct LsifManager<'a> { struct Id(i32); impl From for lsp_types::NumberOrString { - fn from(Id(x): Id) -> Self { - lsp_types::NumberOrString::Number(x) + fn from(Id(it): Id) -> Self { + lsp_types::NumberOrString::Number(it) } } @@ -88,8 +88,8 @@ impl LsifManager<'_> { } fn get_token_id(&mut self, id: TokenId) -> Id { - if let Some(x) = self.token_map.get(&id) { - return *x; + if let Some(it) = self.token_map.get(&id) { + return *it; } let result_set_id = self.add_vertex(lsif::Vertex::ResultSet(lsif::ResultSet { key: None })); self.token_map.insert(id, result_set_id); @@ -97,8 +97,8 @@ impl LsifManager<'_> { } fn get_package_id(&mut self, package_information: PackageInformation) -> Id { - if let Some(x) = self.package_map.get(&package_information) { - return *x; + if let Some(it) = self.package_map.get(&package_information) { + return *it; } let pi = package_information.clone(); let result_set_id = @@ -119,8 +119,8 @@ impl LsifManager<'_> { } fn get_range_id(&mut self, id: FileRange) -> Id { - if let Some(x) = self.range_map.get(&id) { - return *x; + if let Some(it) = self.range_map.get(&id) { + return *it; } let file_id = id.file_id; let doc_id = self.get_file_id(file_id); @@ -142,8 +142,8 @@ impl LsifManager<'_> { } fn get_file_id(&mut self, id: FileId) -> Id { - if let Some(x) = self.file_map.get(&id) { - return *x; + if let Some(it) = self.file_map.get(&id) { + return *it; } let path = self.vfs.file_path(id); let path = path.as_path().unwrap(); @@ -216,18 +216,18 @@ impl LsifManager<'_> { })); let mut edges = token.references.iter().fold( HashMap::<_, Vec>::new(), - |mut edges, x| { + |mut edges, it| { let entry = - edges.entry((x.range.file_id, x.is_definition)).or_insert_with(Vec::new); - entry.push((*self.range_map.get(&x.range).unwrap()).into()); + edges.entry((it.range.file_id, it.is_definition)).or_insert_with(Vec::new); + entry.push((*self.range_map.get(&it.range).unwrap()).into()); edges }, ); - for x in token.references { - if let Some(vertices) = edges.remove(&(x.range.file_id, x.is_definition)) { + for it in token.references { + if let Some(vertices) = edges.remove(&(it.range.file_id, it.is_definition)) { self.add_edge(lsif::Edge::Item(lsif::Item { - document: (*self.file_map.get(&x.range.file_id).unwrap()).into(), - property: Some(if x.is_definition { + document: (*self.file_map.get(&it.range.file_id).unwrap()).into(), + property: Some(if it.is_definition { lsif::ItemKind::Definitions } else { lsif::ItemKind::References From d2693aabd6bc32683808b21e15a7ab20c62a8fa5 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 6 Jul 2023 17:20:01 +0200 Subject: [PATCH 129/201] assist : add enum to glob_import_expand --- .../src/handlers/expand_glob_import.rs | 162 ++++++++++++++++-- 1 file changed, 149 insertions(+), 13 deletions(-) diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs index 5c435dd9c2..3aff5c9144 100644 --- a/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{AssocItem, HasVisibility, Module, ModuleDef, Name, PathResolution, ScopeDef}; +use hir::{AssocItem, Enum, HasVisibility, Module, ModuleDef, Name, PathResolution, ScopeDef}; use ide_db::{ defs::{Definition, NameRefClass}, search::SearchScope, @@ -45,7 +45,8 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> let use_tree = star.parent().and_then(ast::UseTree::cast)?; let (parent, mod_path) = find_parent_and_path(&star)?; let target_module = match ctx.sema.resolve_path(&mod_path)? { - PathResolution::Def(ModuleDef::Module(it)) => it, + PathResolution::Def(ModuleDef::Module(it)) => Expandable::Module(it), + PathResolution::Def(ModuleDef::Adt(hir::Adt::Enum(e))) => Expandable::Enum(e), _ => return None, }; @@ -90,6 +91,11 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> ) } +enum Expandable { + Module(Module), + Enum(Enum), +} + fn find_parent_and_path( star: &SyntaxToken, ) -> Option<(Either, ast::Path)> { @@ -168,23 +174,59 @@ impl Refs { } } -fn find_refs_in_mod(ctx: &AssistContext<'_>, module: Module, visible_from: Module) -> Option { - if !is_mod_visible_from(ctx, module, visible_from) { +fn find_refs_in_mod( + ctx: &AssistContext<'_>, + expandable: Expandable, + visible_from: Module, +) -> Option { + if !is_expandable_visible_from(ctx, &expandable, visible_from) { return None; } - let module_scope = module.scope(ctx.db(), Some(visible_from)); - let refs = module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect(); - Some(Refs(refs)) + match expandable { + Expandable::Module(module) => { + let module_scope = module.scope(ctx.db(), Some(visible_from)); + let refs = + module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect(); + Some(Refs(refs)) + } + Expandable::Enum(enm) => Some(Refs( + enm.variants(ctx.db()) + .into_iter() + .map(|v| Ref { visible_name: v.name(ctx.db()), def: Definition::Variant(v) }) + .collect(), + )), + } } -fn is_mod_visible_from(ctx: &AssistContext<'_>, module: Module, from: Module) -> bool { - match module.parent(ctx.db()) { - Some(parent) => { - module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) - && is_mod_visible_from(ctx, parent, from) +fn is_expandable_visible_from( + ctx: &AssistContext<'_>, + expandable: &Expandable, + from: Module, +) -> bool { + fn is_mod_visible_from(ctx: &AssistContext<'_>, module: Module, from: Module) -> bool { + match module.parent(ctx.db()) { + Some(parent) => { + module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) + && is_mod_visible_from(ctx, parent, from) + } + None => true, + } + } + + match expandable { + Expandable::Module(module) => match module.parent(ctx.db()) { + Some(parent) => { + module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) + && is_mod_visible_from(ctx, parent, from) + } + None => true, + }, + Expandable::Enum(enm) => { + let module = enm.module(ctx.db()); + enm.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) + && is_mod_visible_from(ctx, module, from) } - None => true, } } @@ -897,4 +939,98 @@ struct Baz { ", ); } + + #[test] + fn test_support_for_enums() { + check_assist( + expand_glob_import, + r#" +mod foo { + pub enum Foo { + Bar, + Baz, + } +} + +use foo::Foo; +use foo::Foo::*$0; + +struct Strukt { + bar: Foo, +} + +fn main() { + let s: Strukt = Strukt { bar: Bar }; +}"#, + r#" +mod foo { + pub enum Foo { + Bar, + Baz, + } +} + +use foo::Foo; +use foo::Foo::Bar; + +struct Strukt { + bar: Foo, +} + +fn main() { + let s: Strukt = Strukt { bar: Bar }; +}"#, + ) + } + + #[test] + fn test_expanding_multiple_variants_at_once() { + check_assist( + expand_glob_import, + r#" +mod foo { + pub enum Foo { + Bar, + Baz, + } +} + +mod abc { + use super::foo; + use super::foo::Foo::*$0; + + struct Strukt { + baz: foo::Foo, + bar: foo::Foo, + } + + fn trying_calling() { + let s: Strukt = Strukt { bar: Bar , baz : Baz }; + } + +}"#, + r#" +mod foo { + pub enum Foo { + Bar, + Baz, + } +} + +mod abc { + use super::foo; + use super::foo::Foo::{Bar, Baz}; + + struct Strukt { + baz: foo::Foo, + bar: foo::Foo, + } + + fn trying_calling() { + let s: Strukt = Strukt { bar: Bar , baz : Baz }; + } + +}"#, + ) + } } From ebaf8c8135dfc0d655fe26fe38b5282df39fff26 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Thu, 6 Jul 2023 23:25:02 -0400 Subject: [PATCH 130/201] fix: Indent after pressing enter on a blank line --- editors/code/src/config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index da7c74c28b..5c63823321 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -99,7 +99,8 @@ export class Config { let onEnterRules: vscode.OnEnterRule[] = [ { // Carry indentation from the previous line - beforeText: /^\s*$/, + // if it's only whitespace + beforeText: /^\s+$/, action: { indentAction: vscode.IndentAction.None }, }, { From 4a444e768c05c288725143fd2ec3a7e0abb79324 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 7 Jul 2023 15:07:29 +0330 Subject: [PATCH 131/201] Implement recursion in mir interpreter without recursion --- crates/hir-def/src/db.rs | 5 +- crates/hir-def/src/lang_item.rs | 8 +- crates/hir-ty/src/chalk_db.rs | 4 +- crates/hir-ty/src/consteval.rs | 8 +- crates/hir-ty/src/consteval/tests.rs | 4 +- crates/hir-ty/src/db.rs | 8 + crates/hir-ty/src/lower.rs | 4 +- crates/hir-ty/src/method_resolution.rs | 2 +- crates/hir-ty/src/mir/eval.rs | 617 +++++++++++++++--------- crates/hir-ty/src/mir/eval/shim.rs | 24 +- crates/hir-ty/src/mir/eval/shim/simd.rs | 2 +- crates/hir-ty/src/mir/eval/tests.rs | 2 +- crates/hir-ty/src/mir/lower/as_place.rs | 4 +- crates/hir/src/lib.rs | 2 +- crates/hir/src/source_analyzer.rs | 2 +- 15 files changed, 432 insertions(+), 264 deletions(-) diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 33c3a59db2..82e6dfb30c 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -18,7 +18,7 @@ use crate::{ generics::GenericParams, import_map::ImportMap, item_tree::{AttrOwner, ItemTree}, - lang_item::{LangItem, LangItemTarget, LangItems}, + lang_item::{self, LangItem, LangItemTarget, LangItems}, nameres::{diagnostics::DefDiagnostic, DefMap}, visibility::{self, Visibility}, AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId, @@ -204,6 +204,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Attrs; + #[salsa::invoke(lang_item::lang_attr_query)] + fn lang_attr(&self, def: AttrDefId) -> Option; + #[salsa::transparent] #[salsa::invoke(AttrsWithOwner::attrs_with_owner)] fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner; diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 0e9ac58fba..627479bb7c 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -180,15 +180,15 @@ impl LangItems { T: Into + Copy, { let _p = profile::span("collect_lang_item"); - if let Some(lang_item) = lang_attr(db, item) { + if let Some(lang_item) = db.lang_attr(item.into()) { self.items.entry(lang_item).or_insert_with(|| constructor(item)); } } } -pub fn lang_attr(db: &dyn DefDatabase, item: impl Into + Copy) -> Option { - let attrs = db.attrs(item.into()); - attrs.by_key("lang").string_value().cloned().and_then(|it| LangItem::from_str(&it)) +pub(crate) fn lang_attr_query(db: &dyn DefDatabase, item: AttrDefId) -> Option { + let attrs = db.attrs(item); + attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(&it)) } pub enum GenericRequirement { diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index 0038b0d183..50eabc867b 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -11,7 +11,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; use base_db::CrateId; use hir_def::{ hir::Movability, - lang_item::{lang_attr, LangItem, LangItemTarget}, + lang_item::{LangItem, LangItemTarget}, AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, }; use hir_expand::name::name; @@ -565,7 +565,7 @@ pub(crate) fn trait_datum_query( let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect(); let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; - let well_known = lang_attr(db.upcast(), trait_).and_then(well_known_trait_from_lang_item); + let well_known = db.lang_attr(trait_.into()).and_then(well_known_trait_from_lang_item); let trait_datum = TraitDatum { id: trait_id, binders: make_binders(db, &generic_params, trait_datum_bound), diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 537e28894a..4de90d40a7 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -228,7 +228,7 @@ pub(crate) fn const_eval_query( } GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?, }; - let c = interpret_mir(db, &body, false).0?; + let c = interpret_mir(db, body, false).0?; Ok(c) } @@ -241,7 +241,7 @@ pub(crate) fn const_eval_static_query( Substitution::empty(Interner), db.trait_environment_for_body(def.into()), )?; - let c = interpret_mir(db, &body, false).0?; + let c = interpret_mir(db, body, false).0?; Ok(c) } @@ -268,7 +268,7 @@ pub(crate) fn const_eval_discriminant_variant( Substitution::empty(Interner), db.trait_environment_for_body(def), )?; - let c = interpret_mir(db, &mir_body, false).0?; + let c = interpret_mir(db, mir_body, false).0?; let c = try_const_usize(db, &c).unwrap() as i128; Ok(c) } @@ -293,7 +293,7 @@ pub(crate) fn eval_to_const( } let infer = ctx.clone().resolve_all(); if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) { - if let Ok(result) = interpret_mir(db, &mir_body, true).0 { + if let Ok(result) = interpret_mir(db, Arc::new(mir_body), true).0 { return result; } } diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index d5feb6c7fe..35ecb89824 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -16,7 +16,7 @@ mod intrinsics; fn simplify(e: ConstEvalError) -> ConstEvalError { match e { - ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => { + ConstEvalError::MirEvalError(MirEvalError::InFunction(e, _)) => { simplify(ConstEvalError::MirEvalError(*e)) } _ => e, @@ -2471,7 +2471,7 @@ fn exec_limits() { } const GOAL: i32 = f(0); "#, - |e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow), + |e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded), ); // Reasonable code should still work check_number( diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 9dd810f844..14b719ea41 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -110,6 +110,14 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option>; + #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] + fn lookup_impl_method( + &self, + env: Arc, + func: FunctionId, + fn_subst: Substitution, + ) -> (FunctionId, Substitution); + #[salsa::invoke(crate::lower::callable_item_sig)] fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index e21f77a2d7..2837f400bc 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -23,7 +23,7 @@ use hir_def::{ generics::{ TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, - lang_item::{lang_attr, LangItem}, + lang_item::LangItem, nameres::MacroSubNs, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, resolver::{HasResolver, Resolver, TypeNs}, @@ -1012,7 +1012,7 @@ impl<'a> TyLoweringContext<'a> { // (So ideally, we'd only ignore `~const Drop` here) // - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until // the builtin impls are supported by Chalk, we ignore them here. - if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) { + if let Some(lang) = self.db.lang_attr(tr.hir_trait_id().into()) { if matches!(lang, LangItem::Drop | LangItem::Destruct) { return false; } diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 5781ea48ee..5e1040bc6a 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -682,7 +682,7 @@ pub fn is_dyn_method( /// Looks up the impl method that actually runs for the trait method `func`. /// /// Returns `func` if it's not a method defined in a trait or the lookup failed. -pub fn lookup_impl_method( +pub(crate) fn lookup_impl_method_query( db: &dyn HirDatabase, env: Arc, func: FunctionId, diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 299efb7145..c76d066232 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1,6 +1,6 @@ //! This module provides a MIR interpreter, which is used in const eval. -use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range}; +use std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt::Write, iter, mem, ops::Range}; use base_db::{CrateId, FileId}; use chalk_ir::Mutability; @@ -8,7 +8,7 @@ use either::Either; use hir_def::{ builtin_type::BuiltinType, data::adt::{StructFlags, VariantData}, - lang_item::{lang_attr, LangItem}, + lang_item::LangItem, layout::{TagEncoding, Variants}, AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, VariantId, @@ -28,7 +28,7 @@ use crate::{ infer::PointerCast, layout::{Layout, LayoutError, RustcEnumVariantIdx}, mapping::from_chalk, - method_resolution::{is_dyn_method, lookup_impl_method}, + method_resolution::is_dyn_method, name, static_lifetime, traits::FnTrait, utils::{detect_variant_from_bytes, ClosureSubst}, @@ -37,8 +37,8 @@ use crate::{ }; use super::{ - return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, Operand, - Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, + return_slot, AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, + MirSpan, Operand, Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, }; mod shim; @@ -114,11 +114,20 @@ impl TlsData { } } +struct StackFrame { + body: Arc, + locals: Locals, + destination: Option, + prev_stack_ptr: usize, + span: (MirSpan, DefWithBodyId), +} + pub struct Evaluator<'a> { db: &'a dyn HirDatabase, trait_env: Arc, stack: Vec, heap: Vec, + code_stack: Vec, /// Stores the global location of the statics. We const evaluate every static first time we need it /// and see it's missing, then we add it to this to reuse. static_locations: FxHashMap, @@ -129,6 +138,7 @@ pub struct Evaluator<'a> { thread_local_storage: TlsData, stdout: Vec, stderr: Vec, + layout_cache: RefCell>>, crate_id: CrateId, // FIXME: This is a workaround, see the comment on `interpret_mir` assert_placeholder_ty_is_unused: bool, @@ -192,7 +202,7 @@ impl IntervalAndTy { addr: Address, ty: Ty, evaluator: &Evaluator<'_>, - locals: &Locals<'_>, + locals: &Locals, ) -> Result { let size = evaluator.size_of_sized(&ty, locals, "type of interval")?; Ok(IntervalAndTy { interval: Interval { addr, size }, ty }) @@ -292,7 +302,7 @@ pub enum MirEvalError { TypeIsUnsized(Ty, &'static str), NotSupported(String), InvalidConst(Const), - InFunction(Either, Box, MirSpan, DefWithBodyId), + InFunction(Box, Vec<(Either, MirSpan, DefWithBodyId)>), ExecutionLimitExceeded, StackOverflow, TargetDataLayoutNotAvailable, @@ -310,40 +320,42 @@ impl MirEvalError { ) -> std::result::Result<(), std::fmt::Error> { writeln!(f, "Mir eval error:")?; let mut err = self; - while let MirEvalError::InFunction(func, e, span, def) = err { + while let MirEvalError::InFunction(e, stack) = err { err = e; - match func { - Either::Left(func) => { - let function_name = db.function_data(*func); - writeln!( - f, - "In function {} ({:?})", - function_name.name.display(db.upcast()), - func - )?; - } - Either::Right(clos) => { - writeln!(f, "In {:?}", clos)?; + for (func, span, def) in stack.iter().take(30).rev() { + match func { + Either::Left(func) => { + let function_name = db.function_data(*func); + writeln!( + f, + "In function {} ({:?})", + function_name.name.display(db.upcast()), + func + )?; + } + Either::Right(clos) => { + writeln!(f, "In {:?}", clos)?; + } } + let source_map = db.body_with_source_map(*def).1; + let span: InFile = match span { + MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { + Ok(s) => s.map(|it| it.into()), + Err(_) => continue, + }, + MirSpan::PatId(p) => match source_map.pat_syntax(*p) { + Ok(s) => s.map(|it| match it { + Either::Left(e) => e.into(), + Either::Right(e) => e.into(), + }), + Err(_) => continue, + }, + MirSpan::Unknown => continue, + }; + let file_id = span.file_id.original_file(db.upcast()); + let text_range = span.value.text_range(); + writeln!(f, "{}", span_formatter(file_id, text_range))?; } - let source_map = db.body_with_source_map(*def).1; - let span: InFile = match span { - MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { - Ok(s) => s.map(|it| it.into()), - Err(_) => continue, - }, - MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|it| match it { - Either::Left(e) => e.into(), - Either::Right(e) => e.into(), - }), - Err(_) => continue, - }, - MirSpan::Unknown => continue, - }; - let file_id = span.file_id.original_file(db.upcast()); - let text_range = span.value.text_range(); - writeln!(f, "{}", span_formatter(file_id, text_range))?; } match err { MirEvalError::InFunction(..) => unreachable!(), @@ -423,13 +435,7 @@ impl std::fmt::Debug for MirEvalError { let data = &arg0.data(Interner); f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish() } - Self::InFunction(func, e, span, _) => { - let mut e = &**e; - let mut stack = vec![(*func, *span)]; - while let Self::InFunction(f, next_e, span, _) = e { - e = &next_e; - stack.push((*f, *span)); - } + Self::InFunction(e, stack) => { f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish() } } @@ -459,15 +465,15 @@ impl DropFlags { } #[derive(Debug)] -struct Locals<'a> { - ptr: &'a ArenaMap, - body: &'a MirBody, +struct Locals { + ptr: ArenaMap, + body: Arc, drop_flags: DropFlags, } pub fn interpret_mir( db: &dyn HirDatabase, - body: &MirBody, + body: Arc, // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now // they share their body with their parent, so in MIR lowering we have locals of the parent body, which // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has @@ -476,16 +482,16 @@ pub fn interpret_mir( assert_placeholder_ty_is_unused: bool, ) -> (Result, String, String) { let ty = body.locals[return_slot()].ty.clone(); - let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused); + let mut evaluator = Evaluator::new(db, &body, assert_placeholder_ty_is_unused); let it: Result = (|| { if evaluator.ptr_size() != std::mem::size_of::() { not_supported!("targets with different pointer size from host"); } - let bytes = evaluator.interpret_mir(&body, None.into_iter())?; + let bytes = evaluator.interpret_mir(body.clone(), None.into_iter())?; let mut memory_map = evaluator.create_memory_map( &bytes, &ty, - &Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() }, + &Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() }, )?; memory_map.vtable = evaluator.vtable_map.clone(); return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); @@ -508,6 +514,7 @@ impl Evaluator<'_> { Evaluator { stack: vec![0], heap: vec![0], + code_stack: vec![], vtable_map: VTableMap::default(), thread_local_storage: TlsData::default(), static_locations: HashMap::default(), @@ -519,14 +526,15 @@ impl Evaluator<'_> { assert_placeholder_ty_is_unused, stack_depth_limit: 100, execution_limit: 1000_000, + layout_cache: RefCell::new(HashMap::default()), } } - fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result
{ + fn place_addr(&self, p: &Place, locals: &Locals) -> Result
{ Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0) } - fn place_interval(&self, p: &Place, locals: &Locals<'_>) -> Result { + fn place_interval(&self, p: &Place, locals: &Locals) -> Result { let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?; Ok(Interval { addr: place_addr_and_ty.0, @@ -548,7 +556,7 @@ impl Evaluator<'_> { fn place_addr_and_ty_and_metadata<'a>( &'a self, p: &Place, - locals: &'a Locals<'a>, + locals: &'a Locals, ) -> Result<(Address, Ty, Option)> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty = locals.body.locals[p.local].ty.clone(); @@ -675,9 +683,15 @@ impl Evaluator<'_> { } fn layout(&self, ty: &Ty) -> Result> { - self.db + if let Some(x) = self.layout_cache.borrow().get(ty) { + return Ok(x.clone()); + } + let r = self + .db .layout_of_ty(ty.clone(), self.crate_id) - .map_err(|e| MirEvalError::LayoutError(e, ty.clone())) + .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))?; + self.layout_cache.borrow_mut().insert(ty.clone(), r.clone()); + Ok(r) } fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result> { @@ -686,11 +700,11 @@ impl Evaluator<'_> { }) } - fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result { + fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1) } - fn operand_ty(&self, o: &Operand, locals: &Locals<'_>) -> Result { + fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result { Ok(match o { Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?, Operand::Constant(c) => c.data(Interner).ty.clone(), @@ -701,11 +715,7 @@ impl Evaluator<'_> { }) } - fn operand_ty_and_eval( - &mut self, - o: &Operand, - locals: &mut Locals<'_>, - ) -> Result { + fn operand_ty_and_eval(&mut self, o: &Operand, locals: &mut Locals) -> Result { Ok(IntervalAndTy { interval: self.eval_operand(o, locals)?, ty: self.operand_ty(o, locals)?, @@ -714,8 +724,8 @@ impl Evaluator<'_> { fn interpret_mir( &mut self, - body: &MirBody, - args: impl Iterator>, + body: Arc, + args: impl Iterator, ) -> Result> { if let Some(it) = self.stack_depth_limit.checked_sub(1) { self.stack_depth_limit = it; @@ -723,14 +733,192 @@ impl Evaluator<'_> { return Err(MirEvalError::StackOverflow); } let mut current_block_idx = body.start_block; + let (mut locals, prev_stack_ptr) = self.create_locals_for_body(body.clone(), None)?; + self.fill_locals_for_body(&body, &mut locals, args)?; + let prev_code_stack = mem::take(&mut self.code_stack); + let span = (MirSpan::Unknown, body.owner); + self.code_stack.push(StackFrame { body, locals, destination: None, prev_stack_ptr, span }); + 'stack: loop { + let Some(mut my_stack_frame) = self.code_stack.pop() else { + not_supported!("missing stack frame"); + }; + let e = (|| { + let mut locals = &mut my_stack_frame.locals; + let body = &*my_stack_frame.body; + loop { + let current_block = &body.basic_blocks[current_block_idx]; + if let Some(it) = self.execution_limit.checked_sub(1) { + self.execution_limit = it; + } else { + return Err(MirEvalError::ExecutionLimitExceeded); + } + for statement in ¤t_block.statements { + match &statement.kind { + StatementKind::Assign(l, r) => { + let addr = self.place_addr(l, &locals)?; + let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?; + self.write_memory(addr, &result)?; + locals.drop_flags.add_place(l.clone()); + } + StatementKind::Deinit(_) => not_supported!("de-init statement"), + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop => (), + } + } + let Some(terminator) = current_block.terminator.as_ref() else { + not_supported!("block without terminator"); + }; + match &terminator.kind { + TerminatorKind::Goto { target } => { + current_block_idx = *target; + } + TerminatorKind::Call { + func, + args, + destination, + target, + cleanup: _, + from_hir_call: _, + } => { + let destination_interval = self.place_interval(destination, &locals)?; + let fn_ty = self.operand_ty(func, &locals)?; + let args = args + .iter() + .map(|it| self.operand_ty_and_eval(it, &mut locals)) + .collect::>>()?; + let stack_frame = match &fn_ty.data(Interner).kind { + TyKind::Function(_) => { + let bytes = self.eval_operand(func, &mut locals)?; + self.exec_fn_pointer( + bytes, + destination_interval, + &args, + &locals, + *target, + terminator.span, + )? + } + TyKind::FnDef(def, generic_args) => self.exec_fn_def( + *def, + generic_args, + destination_interval, + &args, + &locals, + *target, + terminator.span, + )?, + it => not_supported!("unknown function type {it:?}"), + }; + locals.drop_flags.add_place(destination.clone()); + if let Some(stack_frame) = stack_frame { + self.code_stack.push(my_stack_frame); + current_block_idx = stack_frame.body.start_block; + self.code_stack.push(stack_frame); + return Ok(None); + } else { + current_block_idx = + target.ok_or(MirEvalError::UndefinedBehavior( + "Diverging function returned".to_owned(), + ))?; + } + } + TerminatorKind::SwitchInt { discr, targets } => { + let val = u128::from_le_bytes(pad16( + self.eval_operand(discr, &mut locals)?.get(&self)?, + false, + )); + current_block_idx = targets.target_for_value(val); + } + TerminatorKind::Return => { + break; + } + TerminatorKind::Unreachable => { + return Err(MirEvalError::UndefinedBehavior( + "unreachable executed".to_owned(), + )); + } + TerminatorKind::Drop { place, target, unwind: _ } => { + self.drop_place(place, &mut locals, terminator.span)?; + current_block_idx = *target; + } + _ => not_supported!("unknown terminator"), + } + } + Ok(Some(my_stack_frame)) + })(); + let my_stack_frame = match e { + Ok(None) => continue 'stack, + Ok(Some(x)) => x, + Err(e) => { + let my_code_stack = mem::replace(&mut self.code_stack, prev_code_stack); + let mut error_stack = vec![]; + for frame in my_code_stack.into_iter().rev() { + if let DefWithBodyId::FunctionId(f) = frame.body.owner { + error_stack.push((Either::Left(f), frame.span.0, frame.span.1)); + } + } + return Err(MirEvalError::InFunction(Box::new(e), error_stack)); + } + }; + match my_stack_frame.destination { + None => { + self.code_stack = prev_code_stack; + self.stack_depth_limit += 1; + return Ok(my_stack_frame.locals.ptr[return_slot()].get(self)?.to_vec()); + } + Some(bb) => { + // We don't support const promotion, so we can't truncate the stack yet. + let _ = my_stack_frame.prev_stack_ptr; + // self.stack.truncate(my_stack_frame.prev_stack_ptr); + current_block_idx = bb; + } + } + } + } + + fn fill_locals_for_body( + &mut self, + body: &MirBody, + locals: &mut Locals, + args: impl Iterator, + ) -> Result<()> { + let mut remain_args = body.param_locals.len(); + for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) { + locals.drop_flags.add_place(l.into()); + match value { + IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?, + IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?, + } + if remain_args == 0 { + return Err(MirEvalError::TypeError("more arguments provided")); + } + remain_args -= 1; + } + if remain_args > 0 { + return Err(MirEvalError::TypeError("not enough arguments provided")); + } + Ok(()) + } + + fn create_locals_for_body( + &mut self, + body: Arc, + destination: Option, + ) -> Result<(Locals, usize)> { let mut locals = - Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() }; + Locals { ptr: ArenaMap::new(), body: body.clone(), drop_flags: DropFlags::default() }; let (locals_ptr, stack_size) = { let mut stack_ptr = self.stack.len(); let addr = body .locals .iter() .map(|(id, it)| { + if id == return_slot() { + if let Some(destination) = destination { + return Ok((id, destination)); + } + } let (size, align) = self.size_align_of_sized( &it.ty, &locals, @@ -747,112 +935,13 @@ impl Evaluator<'_> { let stack_size = stack_ptr - self.stack.len(); (addr, stack_size) }; - locals.ptr = &locals_ptr; + locals.ptr = locals_ptr; + let prev_stack_pointer = self.stack.len(); self.stack.extend(iter::repeat(0).take(stack_size)); - let mut remain_args = body.param_locals.len(); - for ((l, interval), value) in locals_ptr.iter().skip(1).zip(args) { - locals.drop_flags.add_place(l.into()); - interval.write_from_bytes(self, &value)?; - if remain_args == 0 { - return Err(MirEvalError::TypeError("more arguments provided")); - } - remain_args -= 1; - } - if remain_args > 0 { - return Err(MirEvalError::TypeError("not enough arguments provided")); - } - loop { - let current_block = &body.basic_blocks[current_block_idx]; - if let Some(it) = self.execution_limit.checked_sub(1) { - self.execution_limit = it; - } else { - return Err(MirEvalError::ExecutionLimitExceeded); - } - for statement in ¤t_block.statements { - match &statement.kind { - StatementKind::Assign(l, r) => { - let addr = self.place_addr(l, &locals)?; - let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?; - self.write_memory(addr, &result)?; - locals.drop_flags.add_place(l.clone()); - } - StatementKind::Deinit(_) => not_supported!("de-init statement"), - StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Nop => (), - } - } - let Some(terminator) = current_block.terminator.as_ref() else { - not_supported!("block without terminator"); - }; - match &terminator.kind { - TerminatorKind::Goto { target } => { - current_block_idx = *target; - } - TerminatorKind::Call { - func, - args, - destination, - target, - cleanup: _, - from_hir_call: _, - } => { - let destination_interval = self.place_interval(destination, &locals)?; - let fn_ty = self.operand_ty(func, &locals)?; - let args = args - .iter() - .map(|it| self.operand_ty_and_eval(it, &mut locals)) - .collect::>>()?; - match &fn_ty.data(Interner).kind { - TyKind::Function(_) => { - let bytes = self.eval_operand(func, &mut locals)?; - self.exec_fn_pointer( - bytes, - destination_interval, - &args, - &locals, - terminator.span, - )?; - } - TyKind::FnDef(def, generic_args) => { - self.exec_fn_def( - *def, - generic_args, - destination_interval, - &args, - &locals, - terminator.span, - )?; - } - it => not_supported!("unknown function type {it:?}"), - } - locals.drop_flags.add_place(destination.clone()); - current_block_idx = target.expect("broken mir, function without target"); - } - TerminatorKind::SwitchInt { discr, targets } => { - let val = u128::from_le_bytes(pad16( - self.eval_operand(discr, &mut locals)?.get(&self)?, - false, - )); - current_block_idx = targets.target_for_value(val); - } - TerminatorKind::Return => { - self.stack_depth_limit += 1; - return Ok(locals.ptr[return_slot()].get(self)?.to_vec()); - } - TerminatorKind::Unreachable => { - return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned())); - } - TerminatorKind::Drop { place, target, unwind: _ } => { - self.drop_place(place, &mut locals, terminator.span)?; - current_block_idx = *target; - } - _ => not_supported!("unknown terminator"), - } - } + Ok((locals, prev_stack_pointer)) } - fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result { + fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result { use IntervalOrOwned::*; Ok(match r { Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), @@ -1372,7 +1461,7 @@ impl Evaluator<'_> { &mut self, it: VariantId, subst: Substitution, - locals: &Locals<'_>, + locals: &Locals, ) -> Result<(usize, Arc, Option<(usize, usize, i128)>)> { let adt = it.adt_id(); if let DefWithBodyId::VariantId(f) = locals.body.owner { @@ -1452,7 +1541,7 @@ impl Evaluator<'_> { Ok(result) } - fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'_>) -> Result { + fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result { Ok(match it { Operand::Copy(p) | Operand::Move(p) => { locals.drop_flags.remove_place(p); @@ -1482,7 +1571,7 @@ impl Evaluator<'_> { &mut self, c: &chalk_ir::ConcreteConst, ty: &Ty, - locals: &Locals<'_>, + locals: &Locals, konst: &chalk_ir::Const, ) -> Result { Ok(match &c.interned { @@ -1516,7 +1605,7 @@ impl Evaluator<'_> { }) } - fn eval_place(&mut self, p: &Place, locals: &Locals<'_>) -> Result { + fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result { let addr = self.place_addr(p, locals)?; Ok(Interval::new( addr, @@ -1562,7 +1651,7 @@ impl Evaluator<'_> { Ok(()) } - fn size_align_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result> { + fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result> { if let DefWithBodyId::VariantId(f) = locals.body.owner { if let Some((adt, _)) = ty.as_adt() { if AdtId::from(f.parent) == adt { @@ -1586,7 +1675,7 @@ impl Evaluator<'_> { /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` - fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result { + fn size_of_sized(&self, ty: &Ty, locals: &Locals, what: &'static str) -> Result { match self.size_align_of(ty, locals)? { Some(it) => Ok(it.0), None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), @@ -1598,7 +1687,7 @@ impl Evaluator<'_> { fn size_align_of_sized( &self, ty: &Ty, - locals: &Locals<'_>, + locals: &Locals, what: &'static str, ) -> Result<(usize, usize)> { match self.size_align_of(ty, locals)? { @@ -1621,7 +1710,7 @@ impl Evaluator<'_> { let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else { return None; }; - let l = lang_attr(self.db.upcast(), parent)?; + let l = self.db.lang_attr(parent.into())?; match l { FnOnce => Some(FnTrait::FnOnce), FnMut => Some(FnTrait::FnMut), @@ -1630,12 +1719,12 @@ impl Evaluator<'_> { } } - fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result { + fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals) -> Result { fn rec( this: &Evaluator<'_>, bytes: &[u8], ty: &Ty, - locals: &Locals<'_>, + locals: &Locals, mm: &mut MemoryMap, ) -> Result<()> { match ty.kind(Interner) { @@ -1741,7 +1830,7 @@ impl Evaluator<'_> { old_vtable: &VTableMap, addr: Address, ty: &Ty, - locals: &Locals<'_>, + locals: &Locals, ) -> Result<()> { // FIXME: support indirect references let layout = self.layout(ty)?; @@ -1815,21 +1904,21 @@ impl Evaluator<'_> { bytes: Interval, destination: Interval, args: &[IntervalAndTy], - locals: &Locals<'_>, + locals: &Locals, + target_bb: Option, span: MirSpan, - ) -> Result<()> { + ) -> Result> { let id = from_bytes!(usize, bytes.get(self)?); let next_ty = self.vtable_map.ty(id)?.clone(); match &next_ty.data(Interner).kind { TyKind::FnDef(def, generic_args) => { - self.exec_fn_def(*def, generic_args, destination, args, &locals, span)?; + self.exec_fn_def(*def, generic_args, destination, args, &locals, target_bb, span) } TyKind::Closure(id, subst) => { - self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span)?; + self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span) } - _ => return Err(MirEvalError::TypeError("function pointer to non function")), + _ => Err(MirEvalError::TypeError("function pointer to non function")), } - Ok(()) } fn exec_closure( @@ -1839,9 +1928,9 @@ impl Evaluator<'_> { generic_args: &Substitution, destination: Interval, args: &[IntervalAndTy], - locals: &Locals<'_>, + locals: &Locals, span: MirSpan, - ) -> Result<()> { + ) -> Result> { let mir_body = self .db .monomorphized_mir_body_for_closure( @@ -1859,10 +1948,16 @@ impl Evaluator<'_> { let arg_bytes = iter::once(Ok(closure_data)) .chain(args.iter().map(|it| Ok(it.get(&self)?.to_owned()))) .collect::>>()?; - let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| { - MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner) - })?; - destination.write_from_bytes(self, &bytes) + let bytes = self + .interpret_mir(mir_body, arg_bytes.into_iter().map(IntervalOrOwned::Owned)) + .map_err(|e| { + MirEvalError::InFunction( + Box::new(e), + vec![(Either::Right(closure), span, locals.body.owner)], + ) + })?; + destination.write_from_bytes(self, &bytes)?; + Ok(None) } fn exec_fn_def( @@ -1871,18 +1966,34 @@ impl Evaluator<'_> { generic_args: &Substitution, destination: Interval, args: &[IntervalAndTy], - locals: &Locals<'_>, + locals: &Locals, + target_bb: Option, span: MirSpan, - ) -> Result<()> { + ) -> Result> { let def: CallableDefId = from_chalk(self.db, def); let generic_args = generic_args.clone(); match def { CallableDefId::FunctionId(def) => { if let Some(_) = self.detect_fn_trait(def) { - self.exec_fn_trait(def, args, generic_args, locals, destination, span)?; - return Ok(()); + return self.exec_fn_trait( + def, + args, + generic_args, + locals, + destination, + target_bb, + span, + ); } - self.exec_fn_with_args(def, args, generic_args, locals, destination, span)?; + self.exec_fn_with_args( + def, + args, + generic_args, + locals, + destination, + target_bb, + span, + ) } CallableDefId::StructId(id) => { let (size, variant_layout, tag) = @@ -1894,6 +2005,7 @@ impl Evaluator<'_> { args.iter().map(|it| it.interval.into()), )?; destination.write_from_bytes(self, &result)?; + Ok(None) } CallableDefId::EnumVariantId(id) => { let (size, variant_layout, tag) = @@ -1905,9 +2017,9 @@ impl Evaluator<'_> { args.iter().map(|it| it.interval.into()), )?; destination.write_from_bytes(self, &result)?; + Ok(None) } } - Ok(()) } fn exec_fn_with_args( @@ -1915,10 +2027,11 @@ impl Evaluator<'_> { def: FunctionId, args: &[IntervalAndTy], generic_args: Substitution, - locals: &Locals<'_>, + locals: &Locals, destination: Interval, + target_bb: Option, span: MirSpan, - ) -> Result<()> { + ) -> Result> { if self.detect_and_exec_special_function( def, args, @@ -1927,10 +2040,9 @@ impl Evaluator<'_> { destination, span, )? { - return Ok(()); + return Ok(None); } - let arg_bytes = - args.iter().map(|it| Ok(it.get(&self)?.to_owned())).collect::>>()?; + let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)); if let Some(self_ty_idx) = is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone()) { @@ -1938,8 +2050,10 @@ impl Evaluator<'_> { // `&T`, `&mut T`, `Box`, `Rc`, `Arc`, and `Pin

` where `P` is one of possible recievers, // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on // the type. + let first_arg = arg_bytes.clone().next().unwrap(); + let first_arg = first_arg.get(self)?; let ty = - self.vtable_map.ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?; + self.vtable_map.ty_of_bytes(&first_arg[self.ptr_size()..self.ptr_size() * 2])?; let mut args_for_target = args.to_vec(); args_for_target[0] = IntervalAndTy { interval: args_for_target[0].interval.slice(0..self.ptr_size()), @@ -1962,40 +2076,65 @@ impl Evaluator<'_> { generics_for_target, locals, destination, + target_bb, span, ); } let (imp, generic_args) = - lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args); - self.exec_looked_up_function(generic_args, locals, imp, arg_bytes, span, destination) + self.db.lookup_impl_method(self.trait_env.clone(), def, generic_args); + self.exec_looked_up_function( + generic_args, + locals, + imp, + arg_bytes, + span, + destination, + target_bb, + ) } fn exec_looked_up_function( &mut self, generic_args: Substitution, - locals: &Locals<'_>, + locals: &Locals, imp: FunctionId, - arg_bytes: Vec>, + arg_bytes: impl Iterator, span: MirSpan, destination: Interval, - ) -> Result<()> { + target_bb: Option, + ) -> Result> { let def = imp.into(); let mir_body = self .db .monomorphized_mir_body(def, generic_args, self.trait_env.clone()) .map_err(|e| { MirEvalError::InFunction( - Either::Left(imp), Box::new(MirEvalError::MirLowerError(imp, e)), - span, - locals.body.owner, + vec![(Either::Left(imp), span, locals.body.owner)], ) })?; - let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| { - MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner) - })?; - destination.write_from_bytes(self, &result)?; - Ok(()) + Ok(if let Some(target_bb) = target_bb { + let (mut locals, prev_stack_ptr) = + self.create_locals_for_body(mir_body.clone(), Some(destination))?; + self.fill_locals_for_body(&mir_body, &mut locals, arg_bytes.into_iter())?; + let span = (span, locals.body.owner); + Some(StackFrame { + body: mir_body, + locals, + destination: Some(target_bb), + prev_stack_ptr, + span, + }) + } else { + let result = self.interpret_mir(mir_body, arg_bytes).map_err(|e| { + MirEvalError::InFunction( + Box::new(e), + vec![(Either::Left(imp), span, locals.body.owner)], + ) + })?; + destination.write_from_bytes(self, &result)?; + None + }) } fn exec_fn_trait( @@ -2003,10 +2142,11 @@ impl Evaluator<'_> { def: FunctionId, args: &[IntervalAndTy], generic_args: Substitution, - locals: &Locals<'_>, + locals: &Locals, destination: Interval, + target_bb: Option, span: MirSpan, - ) -> Result<()> { + ) -> Result> { let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?; let mut func_ty = func.ty.clone(); let mut func_data = func.interval; @@ -2023,13 +2163,28 @@ impl Evaluator<'_> { } match &func_ty.data(Interner).kind { TyKind::FnDef(def, subst) => { - self.exec_fn_def(*def, subst, destination, &args[1..], locals, span)?; + return self.exec_fn_def( + *def, + subst, + destination, + &args[1..], + locals, + target_bb, + span, + ); } TyKind::Function(_) => { - self.exec_fn_pointer(func_data, destination, &args[1..], locals, span)?; + return self.exec_fn_pointer( + func_data, + destination, + &args[1..], + locals, + target_bb, + span, + ); } TyKind::Closure(closure, subst) => { - self.exec_closure( + return self.exec_closure( *closure, func_data, &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()), @@ -2037,7 +2192,7 @@ impl Evaluator<'_> { &args[1..], locals, span, - )?; + ); } _ => { // try to execute the manual impl of `FnTrait` for structs (nightly feature used in std) @@ -2068,14 +2223,14 @@ impl Evaluator<'_> { generic_args, locals, destination, + target_bb, span, ); } } - Ok(()) } - fn eval_static(&mut self, st: StaticId, locals: &Locals<'_>) -> Result

{ + fn eval_static(&mut self, st: StaticId, locals: &Locals) -> Result
{ if let Some(o) = self.static_locations.get(&st) { return Ok(*o); }; @@ -2123,7 +2278,7 @@ impl Evaluator<'_> { } } - fn drop_place(&mut self, place: &Place, locals: &mut Locals<'_>, span: MirSpan) -> Result<()> { + fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; if !locals.drop_flags.remove_place(place) { return Ok(()); @@ -2138,7 +2293,7 @@ impl Evaluator<'_> { fn run_drop_glue_deep( &mut self, ty: Ty, - locals: &Locals<'_>, + locals: &Locals, addr: Address, _metadata: &[u8], span: MirSpan, @@ -2151,8 +2306,7 @@ impl Evaluator<'_> { // we can ignore drop in them. return Ok(()); }; - let (impl_drop_candidate, subst) = lookup_impl_method( - self.db, + let (impl_drop_candidate, subst) = self.db.lookup_impl_method( self.trait_env.clone(), drop_fn, Substitution::from1(Interner, ty.clone()), @@ -2162,9 +2316,10 @@ impl Evaluator<'_> { subst, locals, impl_drop_candidate, - vec![addr.to_bytes()], + [IntervalOrOwned::Owned(addr.to_bytes())].into_iter(), span, Interval { addr: Address::Invalid(0), size: 0 }, + None, )?; } match ty.kind(Interner) { diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index ab8ecdcf17..6cdf9f03c9 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -32,7 +32,7 @@ impl Evaluator<'_> { def: FunctionId, args: &[IntervalAndTy], generic_args: &Substitution, - locals: &Locals<'_>, + locals: &Locals, destination: Interval, span: MirSpan, ) -> Result { @@ -168,7 +168,7 @@ impl Evaluator<'_> { fn detect_lang_function(&self, def: FunctionId) -> Option { use LangItem::*; - let candidate = lang_attr(self.db.upcast(), def)?; + let candidate = self.db.lang_attr(def.into())?; // We want to execute these functions with special logic if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) { return Some(candidate); @@ -181,7 +181,7 @@ impl Evaluator<'_> { it: LangItem, generic_args: &Substitution, args: &[Vec], - locals: &Locals<'_>, + locals: &Locals, span: MirSpan, ) -> Result> { use LangItem::*; @@ -201,7 +201,7 @@ impl Evaluator<'_> { not_supported!("std::fmt::format not found"); }; let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") }; - let message_string = self.interpret_mir(&*self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.cloned())?; + let message_string = self.interpret_mir(self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.map(|x| IntervalOrOwned::Owned(x.clone())))?; let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]); Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned()) @@ -245,7 +245,7 @@ impl Evaluator<'_> { args: &[IntervalAndTy], _generic_args: &Substitution, destination: Interval, - locals: &Locals<'_>, + locals: &Locals, _span: MirSpan, ) -> Result<()> { match as_str { @@ -353,7 +353,7 @@ impl Evaluator<'_> { args: &[IntervalAndTy], generic_args: &Substitution, destination: Interval, - locals: &Locals<'_>, + locals: &Locals, span: MirSpan, ) -> Result<()> { if let Some(name) = name.strip_prefix("simd_") { @@ -368,7 +368,7 @@ impl Evaluator<'_> { args: &[IntervalAndTy], generic_args: &Substitution, destination: Interval, - locals: &Locals<'_>, + locals: &Locals, span: MirSpan, ) -> Result<()> { if let Some(name) = name.strip_prefix("atomic_") { @@ -873,15 +873,17 @@ impl Evaluator<'_> { .as_trait() .and_then(|it| self.db.trait_data(it).method_by_name(&name![call_once])) { - return self.exec_fn_trait( + self.exec_fn_trait( def, &args, // FIXME: wrong for manual impls of `FnOnce` Substitution::empty(Interner), locals, destination, + None, span, - ); + )?; + return Ok(()); } } not_supported!("FnOnce was not available for executing const_eval_select"); @@ -894,7 +896,7 @@ impl Evaluator<'_> { &mut self, ty: &Ty, metadata: Interval, - locals: &Locals<'_>, + locals: &Locals, ) -> Result<(usize, usize)> { Ok(match ty.kind(Interner) { TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), @@ -948,7 +950,7 @@ impl Evaluator<'_> { args: &[IntervalAndTy], generic_args: &Substitution, destination: Interval, - locals: &Locals<'_>, + locals: &Locals, _span: MirSpan, ) -> Result<()> { // We are a single threaded runtime with no UB checking and no optimization, so diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs index 4c2e56fe2b..64fd130182 100644 --- a/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -50,7 +50,7 @@ impl Evaluator<'_> { args: &[IntervalAndTy], _generic_args: &Substitution, destination: Interval, - _locals: &Locals<'_>, + _locals: &Locals, _span: MirSpan, ) -> Result<()> { match name { diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index ca4268b8fb..1129eaa549 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -30,7 +30,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr db.trait_environment(func_id.into()), ) .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; - let (result, stdout, stderr) = interpret_mir(db, &body, false); + let (result, stdout, stderr) = interpret_mir(db, body, false); result?; Ok((stdout, stderr)) } diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index d58777e2b6..213f151ab6 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -1,7 +1,7 @@ //! MIR lowering for places use super::*; -use hir_def::{lang_item::lang_attr, FunctionId}; +use hir_def::FunctionId; use hir_expand::name; macro_rules! not_supported { @@ -162,7 +162,7 @@ impl MirLowerCtx<'_> { let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) { TyKind::Ref(..) | TyKind::Raw(..) => true, TyKind::Adt(id, _) => { - if let Some(lang_item) = lang_attr(self.db.upcast(), id.0) { + if let Some(lang_item) = self.db.lang_attr(id.0.into()) { lang_item == LangItem::OwnedBox } else { false diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 4eaf99b0b8..337cfbfb62 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1986,7 +1986,7 @@ impl Function { return r; } }; - let (result, stdout, stderr) = interpret_mir(db, &body, false); + let (result, stdout, stderr) = interpret_mir(db, body, false); let mut text = match result { Ok(_) => "pass".to_string(), Err(e) => { diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index ecb1b306a6..3499daf114 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -832,7 +832,7 @@ impl SourceAnalyzer { None => return func, }; let env = db.trait_environment_for_body(owner); - method_resolution::lookup_impl_method(db, env, func, substs).0 + db.lookup_impl_method(env, func, substs).0 } fn resolve_impl_const_or_trait_def( From f0ba0dbe8aee408322f6e675549b446847c6442b Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sat, 8 Jul 2023 01:07:38 +0330 Subject: [PATCH 132/201] Use debug impl in rendering const eval result --- crates/hir-ty/src/display.rs | 22 ------- crates/hir-ty/src/mir.rs | 4 +- crates/hir-ty/src/mir/eval.rs | 115 ++++++++++++++++++++++++---------- crates/hir/src/lib.rs | 23 ++++++- 4 files changed, 106 insertions(+), 58 deletions(-) diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index b87047718d..8cffdef289 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -446,28 +446,6 @@ impl HirDisplay for Const { } } -pub struct HexifiedConst(pub Const); - -impl HirDisplay for HexifiedConst { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let data = &self.0.data(Interner); - if let TyKind::Scalar(s) = data.ty.kind(Interner) { - if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) { - if let ConstValue::Concrete(c) = &data.value { - if let ConstScalar::Bytes(b, m) = &c.interned { - let value = u128::from_le_bytes(pad16(b, false)); - if value >= 10 { - render_const_scalar(f, &b, m, &data.ty)?; - return write!(f, " ({:#X})", value); - } - } - } - } - } - self.0.hir_fmt(f) - } -} - fn render_const_scalar( f: &mut HirFormatter<'_>, b: &[u8], diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 2345bab0bb..5e92b350a3 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -22,7 +22,9 @@ mod pretty; mod monomorphization; pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason}; -pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap}; +pub use eval::{ + interpret_mir, pad16, render_const_using_debug_impl, Evaluator, MirEvalError, VTableMap, +}; pub use lower::{ lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError, }; diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index c76d066232..010ebcbd06 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -10,10 +10,11 @@ use hir_def::{ data::adt::{StructFlags, VariantData}, lang_item::LangItem, layout::{TagEncoding, Variants}, - AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, - VariantId, + resolver::{HasResolver, TypeNs, ValueNs}, + AdtId, ConstId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, + StaticId, VariantId, }; -use hir_expand::InFile; +use hir_expand::{mod_path::ModPath, InFile}; use intern::Interned; use la_arena::ArenaMap; use rustc_hash::{FxHashMap, FxHashSet}; @@ -482,7 +483,7 @@ pub fn interpret_mir( assert_placeholder_ty_is_unused: bool, ) -> (Result, String, String) { let ty = body.locals[return_slot()].ty.clone(); - let mut evaluator = Evaluator::new(db, &body, assert_placeholder_ty_is_unused); + let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused); let it: Result = (|| { if evaluator.ptr_size() != std::mem::size_of::() { not_supported!("targets with different pointer size from host"); @@ -506,11 +507,11 @@ pub fn interpret_mir( impl Evaluator<'_> { pub fn new<'a>( db: &'a dyn HirDatabase, - body: &MirBody, + owner: DefWithBodyId, assert_placeholder_ty_is_unused: bool, ) -> Evaluator<'a> { - let crate_id = body.owner.module(db.upcast()).krate(); - let trait_env = db.trait_environment_for_body(body.owner); + let crate_id = owner.module(db.upcast()).krate(); + let trait_env = db.trait_environment_for_body(owner); Evaluator { stack: vec![0], heap: vec![0], @@ -1551,29 +1552,15 @@ impl Evaluator<'_> { let addr = self.eval_static(*st, locals)?; Interval::new(addr, self.ptr_size()) } - Operand::Constant(konst) => { - let data = &konst.data(Interner); - match &data.value { - chalk_ir::ConstValue::BoundVar(_) => not_supported!("bound var constant"), - chalk_ir::ConstValue::InferenceVar(_) => { - not_supported!("inference var constant") - } - chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"), - chalk_ir::ConstValue::Concrete(c) => { - self.allocate_const_in_heap(c, &data.ty, locals, konst)? - } - } - } + Operand::Constant(konst) => self.allocate_const_in_heap(locals, konst)?, }) } - fn allocate_const_in_heap( - &mut self, - c: &chalk_ir::ConcreteConst, - ty: &Ty, - locals: &Locals, - konst: &chalk_ir::Const, - ) -> Result { + fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result { + let ty = &konst.data(Interner).ty; + let chalk_ir::ConstValue::Concrete(c) = &konst.data(Interner).value else { + not_supported!("evaluating non concrete constant"); + }; Ok(match &c.interned { ConstScalar::Bytes(v, memory_map) => { let mut v: Cow<'_, [u8]> = Cow::Borrowed(v); @@ -2242,12 +2229,7 @@ impl Evaluator<'_> { Box::new(e), ) })?; - let data = &konst.data(Interner); - if let chalk_ir::ConstValue::Concrete(c) = &data.value { - self.allocate_const_in_heap(&c, &data.ty, locals, &konst)? - } else { - not_supported!("unevaluatable static"); - } + self.allocate_const_in_heap(locals, &konst)? } else { let ty = &self.db.infer(st.into())[self.db.body(st.into()).body_expr]; let Some((size, align)) = self.size_align_of(&ty, locals)? else { @@ -2388,6 +2370,73 @@ impl Evaluator<'_> { } } +pub fn render_const_using_debug_impl( + db: &dyn HirDatabase, + owner: ConstId, + c: &Const, +) -> Result { + let mut evaluator = Evaluator::new(db, owner.into(), false); + let locals = &Locals { + ptr: ArenaMap::new(), + body: db + .mir_body(owner.into()) + .map_err(|_| MirEvalError::NotSupported("unreachable".to_string()))?, + drop_flags: DropFlags::default(), + }; + let data = evaluator.allocate_const_in_heap(locals, c)?; + let resolver = owner.resolver(db.upcast()); + let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully( + db.upcast(), + &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( + hir_expand::mod_path::PathKind::Abs, + [name![core], name![fmt], name![Debug]].into_iter(), + )), + ) else { + not_supported!("core::fmt::Debug not found"); + }; + let Some(debug_fmt_fn) = db.trait_data(debug_trait).method_by_name(&name![fmt]) else { + not_supported!("core::fmt::Debug::fmt not found"); + }; + // a1 = &[""] + let a1 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size()); + // a2 = &[::core::fmt::ArgumentV1::new(&(THE_CONST), ::core::fmt::Debug::fmt)] + // FIXME: we should call the said function, but since its name is going to break in the next rustc version + // and its ABI doesn't break yet, we put it in memory manually. + let a2 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size()); + evaluator.write_memory(a2, &data.addr.to_bytes())?; + let debug_fmt_fn_ptr = evaluator.vtable_map.id(TyKind::FnDef( + db.intern_callable_def(debug_fmt_fn.into()).into(), + Substitution::from1(Interner, c.data(Interner).ty.clone()), + ) + .intern(Interner)); + evaluator.write_memory(a2.offset(evaluator.ptr_size()), &debug_fmt_fn_ptr.to_le_bytes())?; + // 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.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(ModPath::from_segments( + hir_expand::mod_path::PathKind::Abs, + [name![std], name![fmt], name![format]].into_iter(), + )), + ) else { + not_supported!("std::fmt::format not found"); + }; + let message_string = evaluator.interpret_mir( + db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, + [IntervalOrOwned::Borrowed(Interval { addr: a3, size: evaluator.ptr_size() * 6 })] + .into_iter(), + )?; + let addr = + Address::from_bytes(&message_string[evaluator.ptr_size()..2 * evaluator.ptr_size()])?; + let size = from_bytes!(usize, message_string[2 * evaluator.ptr_size()..]); + Ok(std::string::String::from_utf8_lossy(evaluator.read_memory(addr, size)?).into_owned()) +} + pub fn pad16(it: &[u8], is_signed: bool) -> [u8; 16] { let is_negative = is_signed && it.last().unwrap_or(&0) > &127; let fill_with = if is_negative { 255 } else { 0 }; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 337cfbfb62..44f901fb3a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -62,7 +62,6 @@ use hir_ty::{ all_super_traits, autoderef, consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, - display::HexifiedConst, layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, @@ -2156,7 +2155,27 @@ impl Const { pub fn render_eval(self, db: &dyn HirDatabase) -> Result { let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?; - let r = format!("{}", HexifiedConst(c).display(db)); + let data = &c.data(Interner); + if let TyKind::Scalar(s) = data.ty.kind(Interner) { + if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) { + if let hir_ty::ConstValue::Concrete(c) = &data.value { + if let hir_ty::ConstScalar::Bytes(b, _) = &c.interned { + let value = u128::from_le_bytes(mir::pad16(b, false)); + let value_signed = + i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_)))); + if value >= 10 { + return Ok(format!("{} ({:#X})", value_signed, value)); + } else { + return Ok(format!("{}", value_signed)); + } + } + } + } + } + if let Ok(s) = mir::render_const_using_debug_impl(db, self.id, &c) { + return Ok(s); + } + let r = format!("{}", c.display(db)); return Ok(r); } } From 4de7cbe04c1e256984901bd40ac3c8739a9f3f78 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sat, 24 Jun 2023 20:31:44 -0400 Subject: [PATCH 133/201] internal: add `add_tabstop_{before,after}_token` --- crates/ide-db/src/source_change.rs | 63 +++++++++++++++++++----------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs index 061fb0f05c..fad0ca51a0 100644 --- a/crates/ide-db/src/source_change.rs +++ b/crates/ide-db/src/source_change.rs @@ -9,7 +9,10 @@ use crate::SnippetCap; use base_db::{AnchoredPathBuf, FileId}; use nohash_hasher::IntMap; use stdx::never; -use syntax::{algo, ast, ted, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; +use syntax::{ + algo, ast, ted, AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, + TextSize, +}; use text_edit::{TextEdit, TextEditBuilder}; #[derive(Default, Debug, Clone)] @@ -237,19 +240,31 @@ impl SourceChangeBuilder { /// Adds a tabstop snippet to place the cursor before `node` pub fn add_tabstop_before(&mut self, _cap: SnippetCap, node: impl AstNode) { assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::Before(node.syntax().clone())); + self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into())); } /// Adds a tabstop snippet to place the cursor after `node` pub fn add_tabstop_after(&mut self, _cap: SnippetCap, node: impl AstNode) { assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::After(node.syntax().clone())); + self.add_snippet(PlaceSnippet::After(node.syntax().clone().into())); + } + + /// Adds a tabstop snippet to place the cursor before `token` + pub fn add_tabstop_before_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { + assert!(token.parent().is_some()); + self.add_snippet(PlaceSnippet::Before(token.clone().into())); + } + + /// Adds a tabstop snippet to place the cursor after `token` + pub fn add_tabstop_after_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { + assert!(token.parent().is_some()); + self.add_snippet(PlaceSnippet::After(token.clone().into())); } /// Adds a snippet to move the cursor selected over `node` pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) { assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::Over(node.syntax().clone())) + self.add_snippet(PlaceSnippet::Over(node.syntax().clone().into())) } fn add_snippet(&mut self, snippet: PlaceSnippet) { @@ -282,38 +297,40 @@ impl From for SourceChange { } enum PlaceSnippet { - /// Place a tabstop before a node - Before(SyntaxNode), - /// Place a tabstop before a node - After(SyntaxNode), - /// Place a placeholder snippet in place of the node - Over(SyntaxNode), + /// Place a tabstop before an element + Before(SyntaxElement), + /// Place a tabstop before an element + After(SyntaxElement), + /// Place a placeholder snippet in place of the element + Over(SyntaxElement), } impl PlaceSnippet { - /// Places the snippet before or over a node with the given tab stop index + /// Places the snippet before or over an element with the given tab stop index fn place(self, order: usize) { - // ensure the target node is still attached + // ensure the target element is still attached match &self { - PlaceSnippet::Before(node) | PlaceSnippet::After(node) | PlaceSnippet::Over(node) => { - // node should still be in the tree, but if it isn't + PlaceSnippet::Before(element) + | PlaceSnippet::After(element) + | PlaceSnippet::Over(element) => { + // element should still be in the tree, but if it isn't // then it's okay to just ignore this place - if stdx::never!(node.parent().is_none()) { + if stdx::never!(element.parent().is_none()) { return; } } } match self { - PlaceSnippet::Before(node) => { - ted::insert_raw(ted::Position::before(&node), Self::make_tab_stop(order)); + PlaceSnippet::Before(element) => { + ted::insert_raw(ted::Position::before(&element), Self::make_tab_stop(order)); } - PlaceSnippet::After(node) => { - ted::insert_raw(ted::Position::after(&node), Self::make_tab_stop(order)); + PlaceSnippet::After(element) => { + ted::insert_raw(ted::Position::after(&element), Self::make_tab_stop(order)); } - PlaceSnippet::Over(node) => { - let position = ted::Position::before(&node); - node.detach(); + PlaceSnippet::Over(element) => { + let position = ted::Position::before(&element); + element.detach(); let snippet = ast::SourceFile::parse(&format!("${{{order}:_}}")) .syntax_node() @@ -321,7 +338,7 @@ impl PlaceSnippet { let placeholder = snippet.descendants().find_map(ast::UnderscoreExpr::cast).unwrap(); - ted::replace(placeholder.syntax(), node); + ted::replace(placeholder.syntax(), element); ted::insert_raw(position, snippet); } From c0172333c2973add4c4a3575646733d31333df06 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sat, 24 Jun 2023 21:20:13 -0400 Subject: [PATCH 134/201] add attr-related `make` functions --- crates/syntax/src/ast/make.rs | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 3facd90a11..4c6db0ef06 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -10,6 +10,8 @@ //! `parse(format!())` we use internally is an implementation detail -- long //! term, it will be replaced with direct tree manipulation. use itertools::Itertools; +use parser::T; +use rowan::NodeOrToken; use stdx::{format_to, never}; use crate::{ast, utils::is_raw_identifier, AstNode, SourceFile, SyntaxKind, SyntaxToken}; @@ -1030,6 +1032,41 @@ pub fn struct_( ast_from_text(&format!("{visibility}struct {strukt_name}{type_params}{field_list}{semicolon}",)) } +pub fn attr_outer(meta: ast::Meta) -> ast::Attr { + ast_from_text(&format!("#[{meta}]")) +} + +pub fn attr_inner(meta: ast::Meta) -> ast::Attr { + ast_from_text(&format!("#![{meta}]")) +} + +pub fn meta_expr(path: ast::Path, expr: ast::Expr) -> ast::Meta { + ast_from_text(&format!("#[{path} = {expr}]")) +} + +pub fn meta_token_tree(path: ast::Path, tt: ast::TokenTree) -> ast::Meta { + ast_from_text(&format!("#[{path}{tt}]")) +} + +pub fn meta_path(path: ast::Path) -> ast::Meta { + ast_from_text(&format!("#[{path}]")) +} + +pub fn token_tree( + delimiter: SyntaxKind, + tt: Vec>, +) -> ast::TokenTree { + let (l_delimiter, r_delimiter) = match delimiter { + T!['('] => ('(', ')'), + T!['['] => ('[', ']'), + T!['{'] => ('{', '}'), + _ => panic!("invalid delimiter `{delimiter:?}`"), + }; + let tt = tt.into_iter().join(""); + + ast_from_text(&format!("tt!{l_delimiter}{tt}{r_delimiter}")) +} + #[track_caller] fn ast_from_text(text: &str) -> N { let parse = SourceFile::parse(text); From 419f641d49beaf7db70aeea385dbfadc6283e906 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sat, 24 Jun 2023 21:20:57 -0400 Subject: [PATCH 135/201] add `AttrsOwnerEdit::add_attr` --- crates/syntax/src/ast/edit_in_place.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 5919609d91..1f5ed206b4 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -213,6 +213,28 @@ pub trait AttrsOwnerEdit: ast::HasAttrs { } } } + + fn add_attr(&self, attr: ast::Attr) { + add_attr(self.syntax(), attr); + + fn add_attr(node: &SyntaxNode, attr: ast::Attr) { + let indent = IndentLevel::from_node(node); + attr.reindent_to(indent); + + let after_attrs_and_comments = node + .children_with_tokens() + .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) + .map_or(Position::first_child_of(node), |it| Position::before(it)); + + ted::insert_all( + after_attrs_and_comments, + vec![ + attr.syntax().clone().into(), + make::tokens::whitespace(&format!("\n{indent}")).into(), + ], + ) + } + } } impl AttrsOwnerEdit for T {} From 25088fca9d367acc8f124e3236beaae933a7364d Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sat, 24 Jun 2023 21:24:24 -0400 Subject: [PATCH 136/201] Migrate `generate_derive` to mutable ast --- .../src/handlers/generate_derive.rs | 74 ++++++++----------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs index 78ac2eb30e..c8388e4237 100644 --- a/crates/ide-assists/src/handlers/generate_derive.rs +++ b/crates/ide-assists/src/handlers/generate_derive.rs @@ -1,7 +1,6 @@ use syntax::{ - ast::{self, edit::IndentLevel, AstNode, HasAttrs}, - SyntaxKind::{COMMENT, WHITESPACE}, - TextSize, + ast::{self, edit_in_place::AttrsOwnerEdit, make, AstNode, HasAttrs}, + T, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -27,48 +26,37 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let cap = ctx.config.snippet_cap?; let nominal = ctx.find_node_at_offset::()?; - let node_start = derive_insertion_offset(&nominal)?; let target = nominal.syntax().text_range(); - acc.add( - AssistId("generate_derive", AssistKind::Generate), - "Add `#[derive]`", - target, - |builder| { - let derive_attr = nominal - .attrs() - .filter_map(|x| x.as_simple_call()) - .filter(|(name, _arg)| name == "derive") - .map(|(_name, arg)| arg) - .next(); - match derive_attr { - None => { - let indent_level = IndentLevel::from_node(nominal.syntax()); - builder.insert_snippet( - cap, - node_start, - format!("#[derive($0)]\n{indent_level}"), - ); - } - Some(tt) => { - // Just move the cursor. - builder.insert_snippet( - cap, - tt.syntax().text_range().end() - TextSize::of(')'), - "$0", - ) - } - }; - }, - ) -} + acc.add(AssistId("generate_derive", AssistKind::Generate), "Add `#[derive]`", target, |edit| { + let derive_attr = nominal + .attrs() + .filter_map(|x| x.as_simple_call()) + .filter(|(name, _arg)| name == "derive") + .map(|(_name, arg)| arg) + .next(); + match derive_attr { + None => { + let derive = make::attr_outer(make::meta_token_tree( + make::ext::ident_path("derive"), + make::token_tree(T!['('], vec![]).clone_for_update(), + )) + .clone_for_update(); -// Insert `derive` after doc comments. -fn derive_insertion_offset(nominal: &ast::Adt) -> Option { - let non_ws_child = nominal - .syntax() - .children_with_tokens() - .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; - Some(non_ws_child.text_range().start()) + let nominal = edit.make_mut(nominal); + nominal.add_attr(derive.clone()); + + edit.add_tabstop_before_token( + cap, + derive.meta().unwrap().token_tree().unwrap().r_paren_token().unwrap(), + ); + } + Some(tt) => { + // Just move the cursor. + let tt = edit.make_mut(tt); + edit.add_tabstop_before_token(cap, tt.r_paren_token().unwrap()); + } + }; + }) } #[cfg(test)] From f8b6b4cc0b7d8e47dddf640c408dc3df02f5c88f Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sat, 24 Jun 2023 22:20:36 -0400 Subject: [PATCH 137/201] Migrate `wrap_return_type_in_result` to mutable ast --- .../handlers/wrap_return_type_in_result.rs | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index b6c489eb62..24c3387457 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -6,7 +6,7 @@ use ide_db::{ }; use syntax::{ ast::{self, make, Expr}, - match_ast, AstNode, + match_ast, ted, AstNode, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -52,8 +52,8 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite), "Wrap return type in Result", type_ref.syntax().text_range(), - |builder| { - let body = ast::Expr::BlockExpr(body); + |edit| { + let body = edit.make_mut(ast::Expr::BlockExpr(body)); let mut exprs_to_wrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); @@ -70,17 +70,24 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< let ok_wrapped = make::expr_call( make::expr_path(make::ext::ident_path("Ok")), make::arg_list(iter::once(ret_expr_arg.clone())), - ); - builder.replace_ast(ret_expr_arg, ok_wrapped); + ) + .clone_for_update(); + ted::replace(ret_expr_arg.syntax(), ok_wrapped.syntax()); } - match ctx.config.snippet_cap { - Some(cap) => { - let snippet = format!("Result<{type_ref}, ${{0:_}}>"); - builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet) - } - None => builder - .replace(type_ref.syntax().text_range(), format!("Result<{type_ref}, _>")), + let new_result_ty = + make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); + let old_result_ty = edit.make_mut(type_ref.clone()); + + ted::replace(old_result_ty.syntax(), new_result_ty.syntax()); + + if let Some(cap) = ctx.config.snippet_cap { + let generic_args = new_result_ty + .syntax() + .descendants() + .find_map(ast::GenericArgList::cast) + .unwrap(); + edit.add_placeholder_snippet(cap, generic_args.generic_args().last().unwrap()); } }, ) From 58e2053327799313de2a5ccb4c50c57612993899 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 25 Jun 2023 00:06:30 -0400 Subject: [PATCH 138/201] Migrate `generate_delegate_methods` to mutable ast --- .../src/handlers/generate_delegate_methods.rs | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 1c2ec79809..bd21b7669a 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -1,13 +1,18 @@ use std::collections::HashSet; use hir::{self, HasCrate, HasSource, HasVisibility}; -use syntax::ast::{self, make, AstNode, HasGenericParams, HasName, HasVisibility as _}; +use syntax::{ + ast::{ + self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasGenericParams, HasName, + HasVisibility as _, + }, + ted, +}; use crate::{ - utils::{convert_param_list_to_arg_list, find_struct_impl, render_snippet, Cursor}, + utils::{convert_param_list_to_arg_list, find_struct_impl}, AssistContext, AssistId, AssistKind, Assists, GroupLabel, }; -use syntax::ast::edit::AstNodeEdit; // Assist: generate_delegate_methods // @@ -96,7 +101,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' AssistId("generate_delegate_methods", AssistKind::Generate), format!("Generate delegate for `{field_name}.{name}()`",), target, - |builder| { + |edit| { // Create the function let method_source = match method.source(ctx.db()) { Some(source) => source.value, @@ -135,32 +140,25 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' is_const, is_unsafe, ) - .indent(ast::edit::IndentLevel(1)) .clone_for_update(); - - let cursor = Cursor::Before(f.syntax()); + f.reindent_to(IndentLevel(1)); // Create or update an impl block, attach the function to it, // then insert into our code. match impl_def { Some(impl_def) => { // Remember where in our source our `impl` block lives. - let impl_def = impl_def.clone_for_update(); - let old_range = impl_def.syntax().text_range(); + let impl_def = edit.make_mut(impl_def); - // Attach the function to the impl block + // Attach the function to the impl block. let assoc_items = impl_def.get_or_create_assoc_item_list(); assoc_items.add_item(f.clone().into()); // Update the impl block. - match ctx.config.snippet_cap { - Some(cap) => { - let snippet = render_snippet(cap, impl_def.syntax(), cursor); - builder.replace_snippet(cap, old_range, snippet); - } - None => { - builder.replace(old_range, impl_def.syntax().to_string()); - } + ted::replace(impl_def.syntax(), impl_def.syntax()); + + if let Some(cap) = ctx.config.snippet_cap { + edit.add_tabstop_before(cap, f); } } None => { @@ -178,22 +176,22 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' None, ) .clone_for_update(); + let assoc_items = impl_def.get_or_create_assoc_item_list(); assoc_items.add_item(f.clone().into()); // Insert the impl block. - match ctx.config.snippet_cap { - Some(cap) => { - let offset = strukt.syntax().text_range().end(); - let snippet = render_snippet(cap, impl_def.syntax(), cursor); - let snippet = format!("\n\n{snippet}"); - builder.insert_snippet(cap, offset, snippet); - } - None => { - let offset = strukt.syntax().text_range().end(); - let snippet = format!("\n\n{}", impl_def.syntax()); - builder.insert(offset, snippet); - } + let strukt = edit.make_mut(strukt.clone()); + ted::insert_all( + ted::Position::after(strukt.syntax()), + vec![ + make::tokens::blank_line().into(), + impl_def.syntax().clone().into(), + ], + ); + + if let Some(cap) = ctx.config.snippet_cap { + edit.add_tabstop_before(cap, f) } } } From 09a3bd58995079111dd59c0bd677521cceb28581 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Thu, 6 Jul 2023 21:43:34 -0400 Subject: [PATCH 139/201] Match indentation of generated delegate method --- .../src/handlers/generate_delegate_methods.rs | 96 ++++++++++++++++++- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index bd21b7669a..f0b9b01672 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -3,8 +3,7 @@ use std::collections::HashSet; use hir::{self, HasCrate, HasSource, HasVisibility}; use syntax::{ ast::{ - self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasGenericParams, HasName, - HasVisibility as _, + self, edit_in_place::Indent, make, AstNode, HasGenericParams, HasName, HasVisibility as _, }, ted, }; @@ -141,7 +140,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' is_unsafe, ) .clone_for_update(); - f.reindent_to(IndentLevel(1)); // Create or update an impl block, attach the function to it, // then insert into our code. @@ -150,6 +148,9 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' // Remember where in our source our `impl` block lives. let impl_def = edit.make_mut(impl_def); + // Fixup function indentation. + f.reindent_to(impl_def.indent_level() + 1); + // Attach the function to the impl block. let assoc_items = impl_def.get_or_create_assoc_item_list(); assoc_items.add_item(f.clone().into()); @@ -177,15 +178,22 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' ) .clone_for_update(); + // Fixup function indentation. + f.reindent_to(impl_def.indent_level() + 1); + let assoc_items = impl_def.get_or_create_assoc_item_list(); assoc_items.add_item(f.clone().into()); + // Fixup impl_def indentation + let indent = strukt.indent_level(); + impl_def.reindent_to(indent); + // Insert the impl block. let strukt = edit.make_mut(strukt.clone()); ted::insert_all( ted::Position::after(strukt.syntax()), vec![ - make::tokens::blank_line().into(), + make::tokens::whitespace(&format!("\n\n{indent}")).into(), impl_def.syntax().clone().into(), ], ); @@ -242,6 +250,45 @@ impl Person { ); } + #[test] + fn test_generate_delegate_create_impl_block_match_indent() { + check_assist( + generate_delegate_methods, + r#" +mod indent { + struct Age(u8); + impl Age { + fn age(&self) -> u8 { + self.0 + } + } + + struct Person { + ag$0e: Age, + } +}"#, + r#" +mod indent { + struct Age(u8); + impl Age { + fn age(&self) -> u8 { + self.0 + } + } + + struct Person { + age: Age, + } + + impl Person { + $0fn age(&self) -> u8 { + self.age.age() + } + } +}"#, + ); + } + #[test] fn test_generate_delegate_update_impl_block() { check_assist( @@ -279,6 +326,47 @@ impl Person { ); } + #[test] + fn test_generate_delegate_update_impl_block_match_indent() { + check_assist( + generate_delegate_methods, + r#" +mod indent { + struct Age(u8); + impl Age { + fn age(&self) -> u8 { + self.0 + } + } + + struct Person { + ag$0e: Age, + } + + impl Person {} +}"#, + r#" +mod indent { + struct Age(u8); + impl Age { + fn age(&self) -> u8 { + self.0 + } + } + + struct Person { + age: Age, + } + + impl Person { + $0fn age(&self) -> u8 { + self.age.age() + } + } +}"#, + ); + } + #[test] fn test_generate_delegate_tuple_struct() { check_assist( From 2eb506462ef2baa7e6e63c0ef1ae7af1a998c750 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Thu, 6 Jul 2023 22:31:28 -0400 Subject: [PATCH 140/201] refactor: simplify `generate_delegate_method` Can actually split out adding the functions from getting the impl to update or create thanks to being able to refer to the impl ast node. FIXME Context: Unfortunately we can't adjust the indentation of the newly added function inside of `ast::AssocItemList::add_item` since for some reason the `todo!()` placeholder generated by `add_missing_impl_members` and `replace_derive_with_manual_impl` gets indented weirdly. --- .../src/handlers/generate_delegate_methods.rs | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index f0b9b01672..31fc69562c 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -141,29 +141,10 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' ) .clone_for_update(); - // Create or update an impl block, attach the function to it, - // then insert into our code. - match impl_def { - Some(impl_def) => { - // Remember where in our source our `impl` block lives. - let impl_def = edit.make_mut(impl_def); - - // Fixup function indentation. - f.reindent_to(impl_def.indent_level() + 1); - - // Attach the function to the impl block. - let assoc_items = impl_def.get_or_create_assoc_item_list(); - assoc_items.add_item(f.clone().into()); - - // Update the impl block. - ted::replace(impl_def.syntax(), impl_def.syntax()); - - if let Some(cap) = ctx.config.snippet_cap { - edit.add_tabstop_before(cap, f); - } - } + // Get the impl to update, or create one if we need to. + let impl_def = match impl_def { + Some(impl_def) => edit.make_mut(impl_def), None => { - // Attach the function to the impl block let name = &strukt_name.to_string(); let params = strukt.generic_param_list(); let ty_params = params.clone(); @@ -178,12 +159,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' ) .clone_for_update(); - // Fixup function indentation. - f.reindent_to(impl_def.indent_level() + 1); - - let assoc_items = impl_def.get_or_create_assoc_item_list(); - assoc_items.add_item(f.clone().into()); - // Fixup impl_def indentation let indent = strukt.indent_level(); impl_def.reindent_to(indent); @@ -198,10 +173,19 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' ], ); - if let Some(cap) = ctx.config.snippet_cap { - edit.add_tabstop_before(cap, f) - } + impl_def } + }; + + // Fixup function indentation. + // FIXME: Should really be handled by `AssocItemList::add_item` + f.reindent_to(impl_def.indent_level() + 1); + + let assoc_items = impl_def.get_or_create_assoc_item_list(); + assoc_items.add_item(f.clone().into()); + + if let Some(cap) = ctx.config.snippet_cap { + edit.add_tabstop_before(cap, f) } }, )?; From cfa15d49aaff173a055617a347eda6f6caa9e915 Mon Sep 17 00:00:00 2001 From: Adenine Date: Sun, 18 Jun 2023 20:31:46 -0400 Subject: [PATCH 141/201] implement first pass of memory layout viewer --- crates/ide/src/lib.rs | 6 + crates/ide/src/view_memory_layout.rs | 170 +++++++++++ crates/rust-analyzer/src/handlers/request.rs | 28 ++ crates/rust-analyzer/src/lsp_ext.rs | 34 +++ crates/rust-analyzer/src/main_loop.rs | 1 + editors/code/package.json | 9 + editors/code/src/commands.ts | 283 +++++++++++++++++++ editors/code/src/lsp_ext.ts | 27 +- editors/code/src/main.ts | 9 +- 9 files changed, 561 insertions(+), 6 deletions(-) create mode 100644 crates/ide/src/view_memory_layout.rs diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index cb2a1140ba..a9a8f6903b 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -60,6 +60,7 @@ mod interpret_function; mod view_item_tree; mod shuffle_crate_graph; mod fetch_crates; +mod view_memory_layout; use std::ffi::OsStr; @@ -74,6 +75,7 @@ use ide_db::{ }; use syntax::SourceFile; use triomphe::Arc; +use view_memory_layout::{view_memory_layout, RecursiveMemoryLayout}; use crate::navigation_target::{ToNav, TryToNav}; @@ -724,6 +726,10 @@ impl Analysis { self.with_db(|db| move_item::move_item(db, range, direction)) } + pub fn get_recursive_memory_layout(&self, position: FilePosition) -> Cancellable> { + self.with_db(|db| view_memory_layout(db, position)) + } + /// Performs an operation on the database that may be canceled. /// /// rust-analyzer needs to be able to answer semantic questions about the diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs new file mode 100644 index 0000000000..3b1b4968e7 --- /dev/null +++ b/crates/ide/src/view_memory_layout.rs @@ -0,0 +1,170 @@ +use hir::{Field, HirDisplay, Layout, Semantics, Type}; +use ide_db::{ + defs::{Definition, IdentClass}, + helpers::pick_best_token, + RootDatabase, +}; +use syntax::{AstNode, SyntaxKind, SyntaxToken}; + +use crate::FilePosition; + +pub struct MemoryLayoutNode { + pub item_name: String, + pub typename: String, + pub size: u64, + pub alignment: u64, + pub offset: u64, + pub parent_idx: i64, + pub children_start: i64, + pub children_len: u64, +} + +pub struct RecursiveMemoryLayout { + pub nodes: Vec, +} + +fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option { + for token in sema.descend_into_macros(token) { + let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); + if let Some(&[x]) = def.as_deref() { + return Some(x); + } + } + None +} + +pub(crate) fn view_memory_layout( + db: &RootDatabase, + position: FilePosition, +) -> Option { + let sema = Semantics::new(db); + let file = sema.parse(position.file_id); + let token = + pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { + SyntaxKind::IDENT => 3, + _ => 0, + })?; + + let def = get_definition(&sema, token)?; + + let ty = match def { + Definition::Adt(it) => it.ty(db), + Definition::TypeAlias(it) => it.ty(db), + Definition::BuiltinType(it) => it.ty(db), + Definition::SelfType(it) => it.self_ty(db), + Definition::Local(it) => it.ty(db), + _ => return None, + }; + + enum FieldOrTupleIdx { + Field(Field), + TupleIdx(usize), + } + + impl FieldOrTupleIdx { + fn name(&self, db: &RootDatabase) -> String { + match *self { + FieldOrTupleIdx::Field(f) => f + .name(db) + .as_str() + .map(|s| s.to_owned()) + .unwrap_or_else(|| format!("{:#?}", f.name(db).as_tuple_index().unwrap())), + FieldOrTupleIdx::TupleIdx(i) => format!(".{i}").to_owned(), + } + } + + fn index(&self) -> usize { + match *self { + FieldOrTupleIdx::Field(f) => f.index(), + FieldOrTupleIdx::TupleIdx(i) => i, + } + } + } + + fn read_layout( + nodes: &mut Vec, + db: &RootDatabase, + ty: &Type, + layout: &Layout, + parent_idx: usize, + ) { + let mut fields = ty + .fields(db) + .into_iter() + .map(|(f, ty)| (FieldOrTupleIdx::Field(f), ty)) + .chain( + ty.tuple_fields(db) + .into_iter() + .enumerate() + .map(|(i, ty)| (FieldOrTupleIdx::TupleIdx(i), ty)), + ) + .collect::>(); + + fields.sort_by_key(|(f, _)| layout.field_offset(f.index()).unwrap_or(u64::MAX)); + + if fields.len() == 0 { + return; + } + + let children_start = nodes.len(); + nodes[parent_idx].children_start = children_start as i64; + nodes[parent_idx].children_len = fields.len() as u64; + + for (field, child_ty) in fields.iter() { + if let Ok(child_layout) = child_ty.layout(db) { + nodes.push(MemoryLayoutNode { + item_name: field.name(db), + typename: child_ty.display(db).to_string(), + size: child_layout.size(), + alignment: child_layout.align(), + offset: layout.field_offset(field.index()).unwrap_or(0), + parent_idx: parent_idx as i64, + children_start: -1, + children_len: 0, + }); + } else { + nodes.push(MemoryLayoutNode { + item_name: field.name(db) + + format!("(no layout data: {:?})", child_ty.layout(db).unwrap_err()) + .as_ref(), + typename: child_ty.display(db).to_string(), + size: 0, + offset: 0, + alignment: 0, + parent_idx: parent_idx as i64, + children_start: -1, + children_len: 0, + }); + } + } + + for (i, (_, child_ty)) in fields.iter().enumerate() { + if let Ok(child_layout) = child_ty.layout(db) { + read_layout(nodes, db, &child_ty, &child_layout, children_start + i); + } + } + } + + ty.layout(db).map(|layout| { + let item_name = match def { + Definition::Local(l) => l.name(db).as_str().unwrap().to_owned(), + _ => "[ROOT]".to_owned(), + }; + + let typename = ty.display(db).to_string(); + + let mut nodes = vec![MemoryLayoutNode { + item_name, + typename: typename.clone(), + size: layout.size(), + offset: 0, + alignment: layout.align(), + parent_idx: -1, + children_start: -1, + children_len: 0, + }]; + read_layout(&mut nodes, db, &ty, &layout, 0); + + RecursiveMemoryLayout { nodes } + }) +} diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index bb0c7ffa72..2b901a6019 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1689,6 +1689,34 @@ pub(crate) fn handle_move_item( } } +pub(crate) fn handle_view_recursive_memory_layout( + snap: GlobalStateSnapshot, + params: lsp_ext::ViewRecursiveMemoryLayoutParams, +) -> Result> { + let _p = profile::span("view_recursive_memory_layout"); + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.file_line_index(file_id)?; + let offset = from_proto::offset(&line_index, params.position)?; + + let res = snap.analysis.get_recursive_memory_layout(FilePosition { file_id, offset })?; + Ok(res.map(|it| lsp_ext::RecursiveMemoryLayout { + nodes: it + .nodes + .iter() + .map(|n| lsp_ext::MemoryLayoutNode { + item_name: n.item_name.clone(), + typename: n.typename.clone(), + size: n.size, + offset: n.offset, + alignment: n.alignment, + parent_idx: n.parent_idx, + children_start: n.children_start, + children_len: n.children_len, + }) + .collect(), + })) +} + fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::CommandLink { lsp_ext::CommandLink { tooltip: Some(tooltip), command } } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 4d67c8b305..39b8e84028 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -182,6 +182,40 @@ pub struct ExpandedMacro { pub expansion: String, } +pub enum ViewRecursiveMemoryLayout {} + +impl Request for ViewRecursiveMemoryLayout { + type Params = ViewRecursiveMemoryLayoutParams; + type Result = Option; + const METHOD: &'static str = "rust-analyzer/viewRecursiveMemoryLayout"; +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ViewRecursiveMemoryLayoutParams { + pub text_document: TextDocumentIdentifier, + pub position: Position, +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RecursiveMemoryLayout { + pub nodes: Vec, +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct MemoryLayoutNode { + pub item_name: String, + pub typename: String, + pub size: u64, + pub offset: u64, + pub alignment: u64, + pub parent_idx: i64, + pub children_start: i64, + pub children_len: u64, +} + pub enum CancelFlycheck {} impl Notification for CancelFlycheck { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 9eecf4957a..74036710fa 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -753,6 +753,7 @@ impl GlobalState { ) .on::(handlers::handle_will_rename_files) .on::(handlers::handle_ssr) + .on::(handlers::handle_view_recursive_memory_layout) .finish(); } diff --git a/editors/code/package.json b/editors/code/package.json index 1a1d76c699..cd8b40e355 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -284,6 +284,11 @@ "command": "rust-analyzer.revealDependency", "title": "Reveal File", "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.viewMemoryLayout", + "title": "View Memory Layout", + "category": "rust-analyzer" } ], "keybindings": [ @@ -2067,6 +2072,10 @@ { "command": "rust-analyzer.openCargoToml", "when": "inRustProject" + }, + { + "command": "rust-analyzer.viewMemoryLayout", + "when": "inRustProject" } ], "editor/context": [ diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 3c6105e89f..c0d68881c2 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -1129,3 +1129,286 @@ export function linkToCommand(_: Ctx): Cmd { } }; } + +export function viewMemoryLayout(ctx: CtxInit): Cmd { + return async () => { + + const editor = vscode.window.activeTextEditor; + if (!editor) return ""; + const client = ctx.client; + + const position = editor.selection.active; + const expanded = await client.sendRequest(ra.viewRecursiveMemoryLayout, { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier( + editor.document + ), + position, + }); + + // if (expanded == null) return "Not available"; + + + const document = vscode.window.createWebviewPanel( + "memory_layout", + "[Memory Layout]", + vscode.ViewColumn.Two, + { enableScripts: true, }); + + document.webview.html = ` + + + + + + Document + + + +
+ + +` + + ctx.pushExtCleanup(document); + }; +} diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index b72804e510..4244098ae1 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -70,7 +70,7 @@ export const viewItemTree = new lc.RequestType export type AnalyzerStatusParams = { textDocument?: lc.TextDocumentIdentifier }; -export interface FetchDependencyListParams {} +export interface FetchDependencyListParams { } export interface FetchDependencyListResult { crates: { @@ -86,7 +86,7 @@ export const fetchDependencyList = new lc.RequestType< void >("rust-analyzer/fetchDependencyList"); -export interface FetchDependencyGraphParams {} +export interface FetchDependencyGraphParams { } export interface FetchDependencyGraphResult { crates: { @@ -150,6 +150,9 @@ export const serverStatus = new lc.NotificationType( "experimental/serverStatus" ); export const ssr = new lc.RequestType("experimental/ssr"); +export const viewRecursiveMemoryLayout = new lc.RequestType( + "rust-analyzer/viewRecursiveMemoryLayout" +); export type JoinLinesParams = { textDocument: lc.TextDocumentIdentifier; @@ -197,3 +200,23 @@ export type SsrParams = { position: lc.Position; selections: readonly lc.Range[]; }; + +export type ViewRecursiveMemoryLayoutParams = { + textDocument: lc.TextDocumentIdentifier; + position: lc.Position; +}; +export type RecursiveMemoryLayoutNode = { + item_name: string; + typename: string; + size: number; + alignment: number; + offset: number; + parent_idx: number; + children_start: number; + children_len: number; +}; +export type RecursiveMemoryLayout = { + name: string; + expansion: string; + nodes: RecursiveMemoryLayoutNode[]; +}; \ No newline at end of file diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index be9bc9d363..492275968f 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -24,11 +24,11 @@ export async function activate( vscode.window .showWarningMessage( `You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` + - "plugins enabled. These are known to conflict and cause various functions of " + - "both plugins to not work correctly. You should disable one of them.", + "plugins enabled. These are known to conflict and cause various functions of " + + "both plugins to not work correctly. You should disable one of them.", "Got it" ) - .then(() => {}, console.error); + .then(() => { }, console.error); } const ctx = new Ctx(context, createCommands(), fetchWorkspace()); @@ -144,7 +144,7 @@ function createCommands(): Record { health: "stopped", }); }, - disabled: (_) => async () => {}, + disabled: (_) => async () => { }, }, analyzerStatus: { enabled: commands.analyzerStatus }, @@ -179,6 +179,7 @@ function createCommands(): Record { runFlycheck: { enabled: commands.runFlycheck }, ssr: { enabled: commands.ssr }, serverVersion: { enabled: commands.serverVersion }, + viewMemoryLayout: { enabled: commands.viewMemoryLayout }, // Internal commands which are invoked by the server. applyActionGroup: { enabled: commands.applyActionGroup }, applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand }, From de5e3cf7459b955aa729bcbc6150655edf23e790 Mon Sep 17 00:00:00 2001 From: Adenine Date: Mon, 19 Jun 2023 00:24:42 -0400 Subject: [PATCH 142/201] fix incorrect committed rust and run prettier --- crates/ide/src/view_memory_layout.rs | 40 +++++++++++++++------------- editors/code/src/commands.ts | 11 +++----- editors/code/src/lsp_ext.ts | 14 +++++----- editors/code/src/main.ts | 8 +++--- 4 files changed, 37 insertions(+), 36 deletions(-) diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs index 3b1b4968e7..9ed0d3f224 100644 --- a/crates/ide/src/view_memory_layout.rs +++ b/crates/ide/src/view_memory_layout.rs @@ -145,26 +145,28 @@ pub(crate) fn view_memory_layout( } } - ty.layout(db).map(|layout| { - let item_name = match def { - Definition::Local(l) => l.name(db).as_str().unwrap().to_owned(), - _ => "[ROOT]".to_owned(), - }; + ty.layout(db) + .map(|layout| { + let item_name = match def { + Definition::Local(l) => l.name(db).as_str().unwrap().to_owned(), + _ => "[ROOT]".to_owned(), + }; - let typename = ty.display(db).to_string(); + let typename = ty.display(db).to_string(); - let mut nodes = vec![MemoryLayoutNode { - item_name, - typename: typename.clone(), - size: layout.size(), - offset: 0, - alignment: layout.align(), - parent_idx: -1, - children_start: -1, - children_len: 0, - }]; - read_layout(&mut nodes, db, &ty, &layout, 0); + let mut nodes = vec![MemoryLayoutNode { + item_name, + typename: typename.clone(), + size: layout.size(), + offset: 0, + alignment: layout.align(), + parent_idx: -1, + children_start: -1, + children_len: 0, + }]; + read_layout(&mut nodes, db, &ty, &layout, 0); - RecursiveMemoryLayout { nodes } - }) + RecursiveMemoryLayout { nodes } + }) + .ok() } diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index c0d68881c2..1e74158b82 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -1132,27 +1132,24 @@ export function linkToCommand(_: Ctx): Cmd { export function viewMemoryLayout(ctx: CtxInit): Cmd { return async () => { - const editor = vscode.window.activeTextEditor; if (!editor) return ""; const client = ctx.client; const position = editor.selection.active; const expanded = await client.sendRequest(ra.viewRecursiveMemoryLayout, { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier( - editor.document - ), + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), position, }); // if (expanded == null) return "Not available"; - const document = vscode.window.createWebviewPanel( "memory_layout", "[Memory Layout]", vscode.ViewColumn.Two, - { enableScripts: true, }); + { enableScripts: true } + ); document.webview.html = ` @@ -1407,7 +1404,7 @@ locate() })() -` +`; ctx.pushExtCleanup(document); }; diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 4244098ae1..8a0c55a942 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -70,7 +70,7 @@ export const viewItemTree = new lc.RequestType export type AnalyzerStatusParams = { textDocument?: lc.TextDocumentIdentifier }; -export interface FetchDependencyListParams { } +export interface FetchDependencyListParams {} export interface FetchDependencyListResult { crates: { @@ -86,7 +86,7 @@ export const fetchDependencyList = new lc.RequestType< void >("rust-analyzer/fetchDependencyList"); -export interface FetchDependencyGraphParams { } +export interface FetchDependencyGraphParams {} export interface FetchDependencyGraphResult { crates: { @@ -150,9 +150,11 @@ export const serverStatus = new lc.NotificationType( "experimental/serverStatus" ); export const ssr = new lc.RequestType("experimental/ssr"); -export const viewRecursiveMemoryLayout = new lc.RequestType( - "rust-analyzer/viewRecursiveMemoryLayout" -); +export const viewRecursiveMemoryLayout = new lc.RequestType< + ViewRecursiveMemoryLayoutParams, + RecursiveMemoryLayout | null, + void +>("rust-analyzer/viewRecursiveMemoryLayout"); export type JoinLinesParams = { textDocument: lc.TextDocumentIdentifier; @@ -219,4 +221,4 @@ export type RecursiveMemoryLayout = { name: string; expansion: string; nodes: RecursiveMemoryLayoutNode[]; -}; \ No newline at end of file +}; diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 492275968f..64aea03394 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -24,11 +24,11 @@ export async function activate( vscode.window .showWarningMessage( `You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` + - "plugins enabled. These are known to conflict and cause various functions of " + - "both plugins to not work correctly. You should disable one of them.", + "plugins enabled. These are known to conflict and cause various functions of " + + "both plugins to not work correctly. You should disable one of them.", "Got it" ) - .then(() => { }, console.error); + .then(() => {}, console.error); } const ctx = new Ctx(context, createCommands(), fetchWorkspace()); @@ -144,7 +144,7 @@ function createCommands(): Record { health: "stopped", }); }, - disabled: (_) => async () => { }, + disabled: (_) => async () => {}, }, analyzerStatus: { enabled: commands.analyzerStatus }, From c43cfefdd868c92f60d936e8958c0980428a0679 Mon Sep 17 00:00:00 2001 From: Adenine Date: Mon, 19 Jun 2023 00:27:53 -0400 Subject: [PATCH 143/201] fix typescript return path warning --- editors/code/src/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 1e74158b82..052e391824 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -1133,7 +1133,7 @@ export function linkToCommand(_: Ctx): Cmd { export function viewMemoryLayout(ctx: CtxInit): Cmd { return async () => { const editor = vscode.window.activeTextEditor; - if (!editor) return ""; + if (!editor) return; const client = ctx.client; const position = editor.selection.active; From 4d5c66986ece98fb0015cbca5ee0de1f9870c800 Mon Sep 17 00:00:00 2001 From: Adenine Date: Fri, 7 Jul 2023 23:03:55 -0400 Subject: [PATCH 144/201] cleanup + docs + tests --- crates/ide-db/src/helpers.rs | 18 +- crates/ide/src/lib.rs | 5 +- crates/ide/src/static_index.rs | 17 +- crates/ide/src/view_memory_layout.rs | 281 +++++++++++++++++++++++---- docs/dev/lsp-extensions.md | 55 +++++- editors/code/src/commands.ts | 2 - editors/code/src/lsp_ext.ts | 2 - 7 files changed, 320 insertions(+), 60 deletions(-) diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index eba9d8afc4..1eb8f00020 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs @@ -9,7 +9,10 @@ use syntax::{ AstToken, SyntaxKind, SyntaxToken, TokenAtOffset, }; -use crate::{defs::Definition, generated, RootDatabase}; +use crate::{ + defs::{Definition, IdentClass}, + generated, RootDatabase, +}; pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option { match item { @@ -109,3 +112,16 @@ pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool { let source_root_id = db.file_source_root(root_file); !db.source_root(source_root_id).is_library } + +pub fn get_definition( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> Option { + for token in sema.descend_into_macros(token) { + let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); + if let Some(&[x]) = def.as_deref() { + return Some(x); + } + } + None +} diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index a9a8f6903b..0ad4c6c47e 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -726,7 +726,10 @@ impl Analysis { self.with_db(|db| move_item::move_item(db, range, direction)) } - pub fn get_recursive_memory_layout(&self, position: FilePosition) -> Cancellable> { + pub fn get_recursive_memory_layout( + &self, + position: FilePosition, + ) -> Cancellable> { self.with_db(|db| view_memory_layout(db, position)) } diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 7101e8ed20..59e8300dcd 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -3,13 +3,14 @@ use std::collections::HashMap; -use hir::{db::HirDatabase, Crate, Module, Semantics}; +use hir::{db::HirDatabase, Crate, Module}; +use ide_db::helpers::get_definition; use ide_db::{ base_db::{FileId, FileRange, SourceDatabaseExt}, - defs::{Definition, IdentClass}, + defs::Definition, FxHashSet, RootDatabase, }; -use syntax::{AstNode, SyntaxKind::*, SyntaxToken, TextRange, T}; +use syntax::{AstNode, SyntaxKind::*, TextRange, T}; use crate::{ hover::hover_for_definition, @@ -214,16 +215,6 @@ impl StaticIndex<'_> { } } -fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option { - for token in sema.descend_into_macros(token) { - let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); - if let Some(&[it]) = def.as_deref() { - return Some(it); - } - } - None -} - #[cfg(test)] mod tests { use crate::{fixture, StaticIndex}; diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs index 9ed0d3f224..ae8dd504e3 100644 --- a/crates/ide/src/view_memory_layout.rs +++ b/crates/ide/src/view_memory_layout.rs @@ -1,10 +1,10 @@ use hir::{Field, HirDisplay, Layout, Semantics, Type}; use ide_db::{ - defs::{Definition, IdentClass}, - helpers::pick_best_token, + defs::Definition, + helpers::{get_definition, pick_best_token}, RootDatabase, }; -use syntax::{AstNode, SyntaxKind, SyntaxToken}; +use syntax::{AstNode, SyntaxKind}; use crate::FilePosition; @@ -23,16 +23,40 @@ pub struct RecursiveMemoryLayout { pub nodes: Vec, } -fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option { - for token in sema.descend_into_macros(token) { - let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); - if let Some(&[x]) = def.as_deref() { - return Some(x); - } - } - None +enum FieldOrTupleIdx { + Field(Field), + TupleIdx(usize), } +impl FieldOrTupleIdx { + fn name(&self, db: &RootDatabase) -> String { + match *self { + FieldOrTupleIdx::Field(f) => f + .name(db) + .as_str() + .map(|s| s.to_owned()) + .unwrap_or_else(|| format!(".{}", f.name(db).as_tuple_index().unwrap())), + FieldOrTupleIdx::TupleIdx(i) => format!(".{i}").to_owned(), + } + } + + fn index(&self) -> usize { + match *self { + FieldOrTupleIdx::Field(f) => f.index(), + FieldOrTupleIdx::TupleIdx(i) => i, + } + } +} + +// Feature: View Memory Layout +// +// Displays the recursive memory layout of a datatype. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **rust-analyzer: View Memory Layout** +// |=== pub(crate) fn view_memory_layout( db: &RootDatabase, position: FilePosition, @@ -53,34 +77,12 @@ pub(crate) fn view_memory_layout( Definition::BuiltinType(it) => it.ty(db), Definition::SelfType(it) => it.self_ty(db), Definition::Local(it) => it.ty(db), + Definition::Field(it) => it.ty(db), + Definition::Const(it) => it.ty(db), + Definition::Static(it) => it.ty(db), _ => return None, }; - enum FieldOrTupleIdx { - Field(Field), - TupleIdx(usize), - } - - impl FieldOrTupleIdx { - fn name(&self, db: &RootDatabase) -> String { - match *self { - FieldOrTupleIdx::Field(f) => f - .name(db) - .as_str() - .map(|s| s.to_owned()) - .unwrap_or_else(|| format!("{:#?}", f.name(db).as_tuple_index().unwrap())), - FieldOrTupleIdx::TupleIdx(i) => format!(".{i}").to_owned(), - } - } - - fn index(&self) -> usize { - match *self { - FieldOrTupleIdx::Field(f) => f.index(), - FieldOrTupleIdx::TupleIdx(i) => i, - } - } - } - fn read_layout( nodes: &mut Vec, db: &RootDatabase, @@ -100,12 +102,12 @@ pub(crate) fn view_memory_layout( ) .collect::>(); - fields.sort_by_key(|(f, _)| layout.field_offset(f.index()).unwrap_or(u64::MAX)); - if fields.len() == 0 { return; } + fields.sort_by_key(|(f, _)| layout.field_offset(f.index()).unwrap()); + let children_start = nodes.len(); nodes[parent_idx].children_start = children_start as i64; nodes[parent_idx].children_len = fields.len() as u64; @@ -148,8 +150,21 @@ pub(crate) fn view_memory_layout( ty.layout(db) .map(|layout| { let item_name = match def { - Definition::Local(l) => l.name(db).as_str().unwrap().to_owned(), - _ => "[ROOT]".to_owned(), + // def is a datatype + Definition::Adt(_) + | Definition::TypeAlias(_) + | Definition::BuiltinType(_) + | Definition::SelfType(_) => "[ROOT]".to_owned(), + + // def is an item + def => def + .name(db) + .map(|n| { + n.as_str() + .map(|s| s.to_owned()) + .unwrap_or_else(|| format!(".{}", n.as_tuple_index().unwrap())) + }) + .unwrap_or("[ROOT]".to_owned()), }; let typename = ty.display(db).to_string(); @@ -170,3 +185,189 @@ pub(crate) fn view_memory_layout( }) .ok() } + +#[cfg(test)] +mod tests { + use super::*; + + use crate::fixture; + + fn make_memory_layout(ra_fixture: &str) -> Option { + let (analysis, position, _) = fixture::annotations(ra_fixture); + + view_memory_layout(&analysis.db, position) + } + + fn check_item_info(node: &MemoryLayoutNode, item_name: &str, check_typename: bool) { + assert_eq!(node.item_name, item_name); + assert_eq!(node.size, core::mem::size_of::() as u64); + assert_eq!(node.alignment, core::mem::align_of::() as u64); + if check_typename { + assert_eq!(node.typename, std::any::type_name::()); + } + } + + #[test] + fn view_memory_layout_none() { + assert!(make_memory_layout(r#"$0"#).is_none()); + assert!(make_memory_layout(r#"stru$0ct Blah {}"#).is_none()); + } + + #[test] + fn view_memory_layout_primitive() { + let ml = make_memory_layout( + r#" +fn main() { + let foo$0 = 109; // default i32 +} +"#, + ) + .unwrap(); + + assert_eq!(ml.nodes.len(), 1); + assert_eq!(ml.nodes[0].parent_idx, -1); + assert_eq!(ml.nodes[0].children_start, -1); + check_item_info::(&ml.nodes[0], "foo", true); + assert_eq!(ml.nodes[0].offset, 0); + } + + #[test] + fn view_memory_layout_constant() { + let ml = make_memory_layout( + r#" +const BLAH$0: bool = 0; +"#, + ) + .unwrap(); + + assert_eq!(ml.nodes.len(), 1); + assert_eq!(ml.nodes[0].parent_idx, -1); + assert_eq!(ml.nodes[0].children_start, -1); + check_item_info::(&ml.nodes[0], "BLAH", true); + assert_eq!(ml.nodes[0].offset, 0); + } + + #[test] + fn view_memory_layout_static() { + let ml = make_memory_layout( + r#" +static BLAH$0: bool = 0; +"#, + ) + .unwrap(); + + assert_eq!(ml.nodes.len(), 1); + assert_eq!(ml.nodes[0].parent_idx, -1); + assert_eq!(ml.nodes[0].children_start, -1); + check_item_info::(&ml.nodes[0], "BLAH", true); + assert_eq!(ml.nodes[0].offset, 0); + } + + #[test] + fn view_memory_layout_tuple() { + let ml = make_memory_layout( + r#" +fn main() { + let x$0 = (101.0, 111u8, 119i64); +} + "#, + ) + .unwrap(); + + assert_eq!(ml.nodes.len(), 4); + assert_eq!(ml.nodes[0].children_start, 1); + assert_eq!(ml.nodes[0].children_len, 3); + check_item_info::<(f64, u8, i64)>(&ml.nodes[0], "x", true); + } + + #[test] + fn view_memory_layout_struct() { + let ml = make_memory_layout( + r#" +#[repr(C)] +struct Blah$0 { + a: u32, + b: (i32, u8), + c: i8, +} +"#, + ) + .unwrap(); + + #[repr(C)] // repr C makes this testable, rustc doesn't enforce a layout otherwise ;-; + struct Blah { + a: u32, + b: (i32, u8), + c: i8, + } + + assert_eq!(ml.nodes.len(), 6); + check_item_info::(&ml.nodes[0], "[ROOT]", false); + assert_eq!(ml.nodes[0].offset, 0); + + check_item_info::(&ml.nodes[1], "a", true); + assert_eq!(ml.nodes[1].offset, 0); + + check_item_info::<(i32, u8)>(&ml.nodes[2], "b", true); + assert_eq!(ml.nodes[2].offset, 4); + + check_item_info::(&ml.nodes[3], "c", true); + assert_eq!(ml.nodes[3].offset, 12); + } + + #[test] + fn view_memory_layout_member() { + let ml = make_memory_layout( + r#" +struct Oof { + a$0: bool +} +"#, + ) + .unwrap(); + + assert_eq!(ml.nodes.len(), 1); + assert_eq!(ml.nodes[0].parent_idx, -1); + assert_eq!(ml.nodes[0].children_start, -1); + check_item_info::(&ml.nodes[0], "a", true); + // NOTE: this should not give the memory layout relative to the parent structure, but the type referred to by the member variable alone. + assert_eq!(ml.nodes[0].offset, 0); + } + + #[test] + fn view_memory_layout_alias() { + let ml_a = make_memory_layout( + r#" +struct X { + a: u32, + b: i8, + c: (f32, f32), +} + +type Foo$0 = X; + "#, + ) + .unwrap(); + let ml_b = make_memory_layout( + r#" +struct X$0 { + a: u32, + b: i8, + c: (f32, f32), +} + "#, + ) + .unwrap(); + + ml_a.nodes.iter().zip(ml_b.nodes.iter()).for_each(|(a, b)| { + assert_eq!(a.item_name, b.item_name); + assert_eq!(a.typename, b.typename); + assert_eq!(a.size, b.size); + assert_eq!(a.alignment, b.alignment); + assert_eq!(a.offset, b.offset); + assert_eq!(a.parent_idx, b.parent_idx); + assert_eq!(a.children_start, b.children_start); + assert_eq!(a.children_len, b.children_len); + }) + } +} diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index bc58aa7220..8655e95467 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@