From df27dfa0eae8a14d69e5006334bb06d84ba050b7 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 22 Mar 2024 16:50:48 +0200 Subject: [PATCH 001/178] Optimize integer pow by removing exit branch The branch at the end of the `pow` implementations is redundant with multiplication code already present in the loop. By rotating the exit check, this branch can be largely removed, improving code size and instruction cache coherence. --- library/core/src/num/int_macros.rs | 61 ++++++++++++--------------- library/core/src/num/uint_macros.rs | 64 ++++++++++++----------------- 2 files changed, 52 insertions(+), 73 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index d40e02352a1d0..6ed0eb07e48d8 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1495,18 +1495,17 @@ macro_rules! int_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = try_opt!(acc.checked_mul(base)); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return Some(acc); + } } exp /= 2; base = try_opt!(base.checked_mul(base)); } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.checked_mul(base) } /// Strict exponentiation. Computes `self.pow(exp)`, panicking if @@ -1546,18 +1545,17 @@ macro_rules! int_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = acc.strict_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } exp /= 2; base = base.strict_mul(base); } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.strict_mul(base) } /// Returns the square root of the number, rounded down. @@ -2181,19 +2179,17 @@ macro_rules! int_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = acc.wrapping_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } exp /= 2; base = base.wrapping_mul(base); } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) } /// Calculates `self` + `rhs` @@ -2687,9 +2683,14 @@ macro_rules! int_impl { // Scratch space for storing results of overflowing_mul. let mut r; - while exp > 1 { + loop { if (exp & 1) == 1 { r = acc.overflowing_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + r.1 |= overflown; + return r; + } acc = r.0; overflown |= r.1; } @@ -2698,14 +2699,6 @@ macro_rules! int_impl { base = r.0; overflown |= r.1; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; - r } /// Raises self to the power of `exp`, using exponentiation by squaring. @@ -2732,19 +2725,17 @@ macro_rules! int_impl { let mut base = self; let mut acc = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = acc * base; + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } exp /= 2; base = base * base; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base } /// Returns the square root of the number, rounded down. diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index ad72c29758bd7..b272a9d901bf4 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1534,20 +1534,17 @@ macro_rules! uint_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = try_opt!(acc.checked_mul(base)); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return Some(acc); + } } exp /= 2; base = try_opt!(base.checked_mul(base)); } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - - acc.checked_mul(base) } /// Strict exponentiation. Computes `self.pow(exp)`, panicking if @@ -1587,18 +1584,17 @@ macro_rules! uint_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = acc.strict_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } exp /= 2; base = base.strict_mul(base); } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.strict_mul(base) } /// Saturating integer addition. Computes `self + rhs`, saturating at @@ -2059,19 +2055,17 @@ macro_rules! uint_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = acc.wrapping_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } exp /= 2; base = base.wrapping_mul(base); } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) } /// Calculates `self` + `rhs` @@ -2516,9 +2510,14 @@ macro_rules! uint_impl { // Scratch space for storing results of overflowing_mul. let mut r; - while exp > 1 { + loop { if (exp & 1) == 1 { r = acc.overflowing_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + r.1 |= overflown; + return r; + } acc = r.0; overflown |= r.1; } @@ -2527,15 +2526,6 @@ macro_rules! uint_impl { base = r.0; overflown |= r.1; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; - - r } /// Raises self to the power of `exp`, using exponentiation by squaring. @@ -2560,19 +2550,17 @@ macro_rules! uint_impl { let mut base = self; let mut acc = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = acc * base; + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } exp /= 2; base = base * base; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base } /// Returns the square root of the number, rounded down. From 1faa1018c7ddd2f505904b0f71255afa5ae1bbe7 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 12 Jul 2024 00:54:26 +0300 Subject: [PATCH 002/178] Explicitly unroll integer pow for small exponents The newly optimized loop has introduced a regression in the case when pow is called with a small constant exponent. LLVM is no longer able to unroll the loop and the generated code is larger and slower than what's expected in tests. Match and handle small exponent values separately by branching out to an explicit multiplication sequence for that exponent. Powers larger than 6 need more than three multiplications, so these cases are less likely to benefit from this optimization, also such constant exponents are less likely to be used in practice. For uses with a non-constant exponent, this might also provide a performance benefit if the exponent is small and does not vary between successive calls, so the same match arm tends to be taken as a predicted branch. --- library/core/src/num/int_macros.rs | 62 ++++++++++++++++++++++++++--- library/core/src/num/uint_macros.rs | 62 ++++++++++++++++++++++++++--- 2 files changed, 112 insertions(+), 12 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 6ed0eb07e48d8..d1bb5a6ef478a 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2173,10 +2173,35 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn wrapping_pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } let mut base = self; + + // Unroll multiplications for small exponent values. + // This gives the optimizer a way to efficiently inline call sites + // for the most common use cases with constant exponents. + // Currently, LLVM is unable to unroll the loop below. + match exp { + 0 => return 1, + 1 => return base, + 2 => return base.wrapping_mul(base), + 3 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(base); + } + 4 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(squared); + } + 5 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(squared).wrapping_mul(base); + } + 6 => { + let cubed = base.wrapping_mul(base).wrapping_mul(base); + return cubed.wrapping_mul(cubed); + } + _ => {} + } + let mut acc: Self = 1; loop { @@ -2719,10 +2744,35 @@ macro_rules! int_impl { #[inline] #[rustc_inherit_overflow_checks] pub const fn pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } let mut base = self; + + // Unroll multiplications for small exponent values. + // This gives the optimizer a way to efficiently inline call sites + // for the most common use cases with constant exponents. + // Currently, LLVM is unable to unroll the loop below. + match exp { + 0 => return 1, + 1 => return base, + 2 => return base * base, + 3 => { + let squared = base * base; + return squared * base; + } + 4 => { + let squared = base * base; + return squared * squared; + } + 5 => { + let squared = base * base; + return squared * squared * base; + } + 6 => { + let cubed = base * base * base; + return cubed * cubed; + } + _ => {} + } + let mut acc = 1; loop { diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index b272a9d901bf4..6e5d37f8163ea 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2049,10 +2049,35 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn wrapping_pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } let mut base = self; + + // Unroll multiplications for small exponent values. + // This gives the optimizer a way to efficiently inline call sites + // for the most common use cases with constant exponents. + // Currently, LLVM is unable to unroll the loop below. + match exp { + 0 => return 1, + 1 => return base, + 2 => return base.wrapping_mul(base), + 3 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(base); + } + 4 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(squared); + } + 5 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(squared).wrapping_mul(base); + } + 6 => { + let cubed = base.wrapping_mul(base).wrapping_mul(base); + return cubed.wrapping_mul(cubed); + } + _ => {} + } + let mut acc: Self = 1; loop { @@ -2544,10 +2569,35 @@ macro_rules! uint_impl { #[inline] #[rustc_inherit_overflow_checks] pub const fn pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } let mut base = self; + + // Unroll multiplications for small exponent values. + // This gives the optimizer a way to efficiently inline call sites + // for the most common use cases with constant exponents. + // Currently, LLVM is unable to unroll the loop below. + match exp { + 0 => return 1, + 1 => return base, + 2 => return base * base, + 3 => { + let squared = base * base; + return squared * base; + } + 4 => { + let squared = base * base; + return squared * squared; + } + 5 => { + let squared = base * base; + return squared * squared * base; + } + 6 => { + let cubed = base * base * base; + return cubed * cubed; + } + _ => {} + } + let mut acc = 1; loop { From 2f235343529c39bdab47704ec9620d6784eeeb6d Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 12 Jul 2024 22:54:08 +0300 Subject: [PATCH 003/178] Use is_val_statically_known to optimize pow In the dynamic exponent case, it's preferred to not increase code size, so use solely the loop-based implementation there. This shows about 4% penalty in the variable exponent benchmarks on x86_64. --- library/core/src/lib.rs | 1 + library/core/src/num/int_macros.rs | 108 ++++++++++++++++------------ library/core/src/num/uint_macros.rs | 108 ++++++++++++++++------------ 3 files changed, 125 insertions(+), 92 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0f82f01e57a71..ede95e3b2ca9b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -170,6 +170,7 @@ #![feature(internal_impls_macro)] #![feature(ip)] #![feature(is_ascii_octdigit)] +#![feature(is_val_statically_known)] #![feature(isqrt)] #![feature(link_cfg)] #![feature(offset_of_enum)] diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index d1bb5a6ef478a..be0e6a2a03b70 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2172,35 +2172,43 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] + #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn wrapping_pow(self, mut exp: u32) -> Self { let mut base = self; - // Unroll multiplications for small exponent values. - // This gives the optimizer a way to efficiently inline call sites - // for the most common use cases with constant exponents. - // Currently, LLVM is unable to unroll the loop below. - match exp { - 0 => return 1, - 1 => return base, - 2 => return base.wrapping_mul(base), - 3 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(base); - } - 4 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(squared); - } - 5 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(squared).wrapping_mul(base); + if intrinsics::is_val_statically_known(exp) { + // Unroll multiplications for small exponent values. + // This gives the optimizer a way to efficiently inline call sites + // for the most common use cases with constant exponents. + // Currently, LLVM is unable to unroll the loop below. + match exp { + 0 => return 1, + 1 => return base, + 2 => return base.wrapping_mul(base), + 3 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(base); + } + 4 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(squared); + } + 5 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(squared).wrapping_mul(base); + } + 6 => { + let cubed = base.wrapping_mul(base).wrapping_mul(base); + return cubed.wrapping_mul(cubed); + } + _ => {} } - 6 => { - let cubed = base.wrapping_mul(base).wrapping_mul(base); - return cubed.wrapping_mul(cubed); + } else { + if exp == 0 { + return 1; } - _ => {} } + debug_assert!(exp != 0); let mut acc: Self = 1; @@ -2743,35 +2751,43 @@ macro_rules! int_impl { without modifying the original"] #[inline] #[rustc_inherit_overflow_checks] + #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn pow(self, mut exp: u32) -> Self { let mut base = self; - // Unroll multiplications for small exponent values. - // This gives the optimizer a way to efficiently inline call sites - // for the most common use cases with constant exponents. - // Currently, LLVM is unable to unroll the loop below. - match exp { - 0 => return 1, - 1 => return base, - 2 => return base * base, - 3 => { - let squared = base * base; - return squared * base; - } - 4 => { - let squared = base * base; - return squared * squared; - } - 5 => { - let squared = base * base; - return squared * squared * base; + if intrinsics::is_val_statically_known(exp) { + // Unroll multiplications for small exponent values. + // This gives the optimizer a way to efficiently inline call sites + // for the most common use cases with constant exponents. + // Currently, LLVM is unable to unroll the loop below. + match exp { + 0 => return 1, + 1 => return base, + 2 => return base * base, + 3 => { + let squared = base * base; + return squared * base; + } + 4 => { + let squared = base * base; + return squared * squared; + } + 5 => { + let squared = base * base; + return squared * squared * base; + } + 6 => { + let cubed = base * base * base; + return cubed * cubed; + } + _ => {} } - 6 => { - let cubed = base * base * base; - return cubed * cubed; + } else { + if exp == 0 { + return 1; } - _ => {} } + debug_assert!(exp != 0); let mut acc = 1; diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 6e5d37f8163ea..24352593fca3d 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2048,35 +2048,43 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] + #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn wrapping_pow(self, mut exp: u32) -> Self { let mut base = self; - // Unroll multiplications for small exponent values. - // This gives the optimizer a way to efficiently inline call sites - // for the most common use cases with constant exponents. - // Currently, LLVM is unable to unroll the loop below. - match exp { - 0 => return 1, - 1 => return base, - 2 => return base.wrapping_mul(base), - 3 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(base); - } - 4 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(squared); - } - 5 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(squared).wrapping_mul(base); + if intrinsics::is_val_statically_known(exp) { + // Unroll multiplications for small exponent values. + // This gives the optimizer a way to efficiently inline call sites + // for the most common use cases with constant exponents. + // Currently, LLVM is unable to unroll the loop below. + match exp { + 0 => return 1, + 1 => return base, + 2 => return base.wrapping_mul(base), + 3 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(base); + } + 4 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(squared); + } + 5 => { + let squared = base.wrapping_mul(base); + return squared.wrapping_mul(squared).wrapping_mul(base); + } + 6 => { + let cubed = base.wrapping_mul(base).wrapping_mul(base); + return cubed.wrapping_mul(cubed); + } + _ => {} } - 6 => { - let cubed = base.wrapping_mul(base).wrapping_mul(base); - return cubed.wrapping_mul(cubed); + } else { + if exp == 0 { + return 1; } - _ => {} } + debug_assert!(exp != 0); let mut acc: Self = 1; @@ -2568,35 +2576,43 @@ macro_rules! uint_impl { without modifying the original"] #[inline] #[rustc_inherit_overflow_checks] + #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn pow(self, mut exp: u32) -> Self { let mut base = self; - // Unroll multiplications for small exponent values. - // This gives the optimizer a way to efficiently inline call sites - // for the most common use cases with constant exponents. - // Currently, LLVM is unable to unroll the loop below. - match exp { - 0 => return 1, - 1 => return base, - 2 => return base * base, - 3 => { - let squared = base * base; - return squared * base; - } - 4 => { - let squared = base * base; - return squared * squared; - } - 5 => { - let squared = base * base; - return squared * squared * base; + if intrinsics::is_val_statically_known(exp) { + // Unroll multiplications for small exponent values. + // This gives the optimizer a way to efficiently inline call sites + // for the most common use cases with constant exponents. + // Currently, LLVM is unable to unroll the loop below. + match exp { + 0 => return 1, + 1 => return base, + 2 => return base * base, + 3 => { + let squared = base * base; + return squared * base; + } + 4 => { + let squared = base * base; + return squared * squared; + } + 5 => { + let squared = base * base; + return squared * squared * base; + } + 6 => { + let cubed = base * base * base; + return cubed * cubed; + } + _ => {} } - 6 => { - let cubed = base * base * base; - return cubed * cubed; + } else { + if exp == 0 { + return 1; } - _ => {} } + debug_assert!(exp != 0); let mut acc = 1; From a22aeb2c75e66c61e4a6416c169bff15234f59f1 Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 26 Jul 2024 15:33:18 +1000 Subject: [PATCH 004/178] Determine toolchain RA on age Selects a rust-toolchain declared RA based on its date. The earliest (oldest) RA wins and becomes the one that the workspace uses as a whole. In terms of precedence: nightly > stable-with-version > stable With stable-with-version, we invoke the RA with a `--version` arg and attempt to extract a date. Given the same date as a nightly, the nightly RA will win. --- .../editors/code/src/bootstrap.ts | 123 +++++++++++++++--- .../editors/code/tests/unit/bootstrap.test.ts | 89 +++++++++++++ 2 files changed, 196 insertions(+), 16 deletions(-) create mode 100644 src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index 527edf19eb45f..ebcafd6f72e6f 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -4,6 +4,7 @@ import type { Config } from "./config"; import { type Env, log } from "./util"; import type { PersistentState } from "./persistent_state"; import { exec, spawnSync } from "child_process"; +import { TextDecoder } from "node:util"; export async function bootstrap( context: vscode.ExtensionContext, @@ -50,26 +51,35 @@ async function getServer( } return explicitPath; } - if (packageJson.releaseTag === null) return "rust-analyzer"; - if (vscode.workspace.workspaceFolders?.length === 1) { - // otherwise check if there is a toolchain override for the current vscode workspace - // and if the toolchain of this override has a rust-analyzer component - // if so, use the rust-analyzer component - const toolchainTomlExists = await fileExists( - vscode.Uri.joinPath(vscode.workspace.workspaceFolders[0]!.uri, "rust-toolchain.toml"), - ); - if (toolchainTomlExists) { - const res = spawnSync("rustup", ["which", "rust-analyzer"], { - encoding: "utf8", - env: { ...process.env }, - cwd: vscode.workspace.workspaceFolders[0]!.uri.fsPath, - }); - if (!res.error && res.status === 0) { - return res.stdout.trim(); + let toolchainServerPath = undefined; + if (vscode.workspace.workspaceFolders) { + for (const workspaceFolder of vscode.workspace.workspaceFolders) { + // otherwise check if there is a toolchain override for the current vscode workspace + // and if the toolchain of this override has a rust-analyzer component + // if so, use the rust-analyzer component + const toolchainUri = vscode.Uri.joinPath(workspaceFolder.uri, "rust-toolchain.toml"); + if (await hasToolchainFileWithRaDeclared(toolchainUri)) { + const res = spawnSync("rustup", ["which", "rust-analyzer"], { + encoding: "utf8", + env: { ...process.env }, + cwd: workspaceFolder.uri.fsPath, + }); + if (!res.error && res.status === 0) { + toolchainServerPath = earliestToolchainPath( + toolchainServerPath, + res.stdout.trim(), + raVersionResolver, + ); + } } } } + if (toolchainServerPath) { + return toolchainServerPath; + } + + if (packageJson.releaseTag === null) return "rust-analyzer"; // finally, use the bundled one const ext = process.platform === "win32" ? ".exe" : ""; @@ -102,6 +112,69 @@ async function getServer( return undefined; } +// Given a path to a rust-analyzer executable, resolve its version and return it. +function raVersionResolver(path: string): string | undefined { + const res = spawnSync(path, ["--version"], { + encoding: "utf8", + }); + if (!res.error && res.status === 0) { + return res.stdout; + } else { + return undefined; + } +} + +// Given a path to two rust-analyzer executables, return the earliest one by date. +function earliestToolchainPath( + path0: string | undefined, + path1: string, + raVersionResolver: (path: string) => string | undefined, +): string { + if (path0) { + if (orderFromPath(path0, raVersionResolver) < orderFromPath(path1, raVersionResolver)) { + return path0; + } else { + return path1; + } + } else { + return path1; + } +} + +// Further to extracting a date for comparison, determine the order of a toolchain as follows: +// Highest - nightly +// Medium - versioned +// Lowest - stable +// Example paths: +// nightly - /Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer +// versioned - /Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer +// stable - /Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer +function orderFromPath( + path: string, + raVersionResolver: (path: string) => string | undefined, +): string { + const capture = path.match(/^.*\/toolchains\/(.*)\/bin\/rust-analyzer$/); + + if (capture?.length === 2) { + const toolchain = capture[1]!; + if (toolchain.startsWith("stable-")) { + return "1"; + } else { + // It is a semver, so we must resolve Rust Analyzer's version. + const raVersion = raVersionResolver(path); + const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/); + if (raDate?.length === 2) { + const precedence = toolchain.startsWith("nightly-") ? "/0" : "/1"; + return "0-" + raDate[1] + precedence; + } else { + return "2"; + } + } + } else { + return "2"; + } +} + async function fileExists(uri: vscode.Uri) { return await vscode.workspace.fs.stat(uri).then( () => true, @@ -109,6 +182,19 @@ async function fileExists(uri: vscode.Uri) { ); } +async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise { + try { + const toolchainFileContents = new TextDecoder().decode( + await vscode.workspace.fs.readFile(uri), + ); + return ( + toolchainFileContents.match(/components\s*=\s*\[.*\"rust-analyzer\".*\]/g)?.length === 1 + ); + } catch (e) { + return false; + } +} + export function isValidExecutable(path: string, extraEnv: Env): boolean { log.debug("Checking availability of a binary at", path); @@ -205,3 +291,8 @@ async function patchelf(dest: vscode.Uri): Promise { }, ); } + +export const _private = { + earliestToolchainPath, + orderFromPath, +}; diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts new file mode 100644 index 0000000000000..78eb8f08cea20 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts @@ -0,0 +1,89 @@ +import * as assert from "assert"; +import { _private } from "../../src/bootstrap"; +import type { Context } from "."; + +export async function getTests(ctx: Context) { + await ctx.suite("Bootstrap/Select toolchain RA", (suite) => { + suite.addTest("Order of nightly RA", async () => { + assert.deepStrictEqual( + _private.orderFromPath( + "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", + function (path: string) { + assert.deepStrictEqual( + path, + "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", + ); + return "rust-analyzer 1.67.0-nightly (b7bc90fe 2022-11-21)"; + }, + ), + "0-2022-11-21/0", + ); + }); + + suite.addTest("Order of versioned RA", async () => { + assert.deepStrictEqual( + _private.orderFromPath( + "/Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer", + function (path: string) { + assert.deepStrictEqual( + path, + "/Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer", + ); + return "rust-analyzer 1.72.1 (d5c2e9c3 2023-09-13)"; + }, + ), + "0-2023-09-13/1", + ); + }); + + suite.addTest("Order of versioned RA when unable to obtain version date", async () => { + assert.deepStrictEqual( + _private.orderFromPath( + "/Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer", + function () { + return "rust-analyzer 1.72.1"; + }, + ), + "2", + ); + }); + + suite.addTest("Order of stable RA", async () => { + assert.deepStrictEqual( + _private.orderFromPath( + "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer", + function () { + assert.fail("Shouldn't get here."); + }, + ), + "1", + ); + }); + + suite.addTest("Order with invalid path to RA", async () => { + assert.deepStrictEqual( + _private.orderFromPath("some-weird-path", function () { + assert.fail("Shouldn't get here."); + }), + "2", + ); + }); + + suite.addTest("Earliest RA between nightly and stable", async () => { + assert.deepStrictEqual( + _private.earliestToolchainPath( + "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer", + "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", + function (path: string) { + assert.deepStrictEqual( + path, + "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", + ); + return "rust-analyzer 1.67.0-nightly (b7bc90fe 2022-11-21)"; + }, + ), + "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", + ); + }); + }); +} From 786ef8393c78819d09d519939dd103b89b000a44 Mon Sep 17 00:00:00 2001 From: huntc Date: Tue, 30 Jul 2024 16:26:56 +1000 Subject: [PATCH 005/178] Checks date for any RA --- .../editors/code/src/bootstrap.ts | 19 ++++++--------- .../editors/code/tests/unit/bootstrap.test.ts | 23 ++++++++++++------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index ebcafd6f72e6f..521d0ba4c1d02 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -154,21 +154,16 @@ function orderFromPath( raVersionResolver: (path: string) => string | undefined, ): string { const capture = path.match(/^.*\/toolchains\/(.*)\/bin\/rust-analyzer$/); - if (capture?.length === 2) { const toolchain = capture[1]!; - if (toolchain.startsWith("stable-")) { - return "1"; + // It is a semver, so we must resolve Rust Analyzer's version. + const raVersion = raVersionResolver(path); + const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/); + if (raDate?.length === 2) { + const precedence = toolchain.startsWith("nightly-") ? "/0" : "/1"; + return "0-" + raDate[1] + precedence; } else { - // It is a semver, so we must resolve Rust Analyzer's version. - const raVersion = raVersionResolver(path); - const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/); - if (raDate?.length === 2) { - const precedence = toolchain.startsWith("nightly-") ? "/0" : "/1"; - return "0-" + raDate[1] + precedence; - } else { - return "2"; - } + return "2"; } } else { return "2"; diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts index 78eb8f08cea20..6e17d73adc476 100644 --- a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts +++ b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts @@ -52,11 +52,15 @@ export async function getTests(ctx: Context) { assert.deepStrictEqual( _private.orderFromPath( "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer", - function () { - assert.fail("Shouldn't get here."); + function (path: string) { + assert.deepStrictEqual( + path, + "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer", + ); + return "rust-analyzer 1.79.0 (129f3b99 2024-06-10)"; }, ), - "1", + "0-2024-06-10/1", ); }); @@ -75,11 +79,14 @@ export async function getTests(ctx: Context) { "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer", "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", function (path: string) { - assert.deepStrictEqual( - path, - "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", - ); - return "rust-analyzer 1.67.0-nightly (b7bc90fe 2022-11-21)"; + if ( + path === + "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer" + ) { + return "rust-analyzer 1.67.0-nightly (b7bc90fe 2022-11-21)"; + } else { + return "rust-analyzer 1.79.0 (129f3b99 2024-06-10)"; + } }, ), "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", From 6b4c0e03f84fdb10be959cdbd10bdd1f628a51bb Mon Sep 17 00:00:00 2001 From: huntc Date: Tue, 30 Jul 2024 16:38:47 +1000 Subject: [PATCH 006/178] Further simplifications --- .../editors/code/src/bootstrap.ts | 18 ++++++------------ .../editors/code/tests/unit/bootstrap.test.ts | 8 ++++---- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index 521d0ba4c1d02..df49b7b700786 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -153,18 +153,12 @@ function orderFromPath( path: string, raVersionResolver: (path: string) => string | undefined, ): string { - const capture = path.match(/^.*\/toolchains\/(.*)\/bin\/rust-analyzer$/); - if (capture?.length === 2) { - const toolchain = capture[1]!; - // It is a semver, so we must resolve Rust Analyzer's version. - const raVersion = raVersionResolver(path); - const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/); - if (raDate?.length === 2) { - const precedence = toolchain.startsWith("nightly-") ? "/0" : "/1"; - return "0-" + raDate[1] + precedence; - } else { - return "2"; - } + // It is a semver, so we must resolve Rust Analyzer's version. + const raVersion = raVersionResolver(path); + const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/); + if (raDate?.length === 2) { + const precedence = path.includes("nightly-") ? "0" : "1"; + return precedence + "-" + raDate[1]; } else { return "2"; } diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts index 6e17d73adc476..884e73e454a3a 100644 --- a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts +++ b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts @@ -16,7 +16,7 @@ export async function getTests(ctx: Context) { return "rust-analyzer 1.67.0-nightly (b7bc90fe 2022-11-21)"; }, ), - "0-2022-11-21/0", + "0-2022-11-21", ); }); @@ -32,7 +32,7 @@ export async function getTests(ctx: Context) { return "rust-analyzer 1.72.1 (d5c2e9c3 2023-09-13)"; }, ), - "0-2023-09-13/1", + "1-2023-09-13", ); }); @@ -60,14 +60,14 @@ export async function getTests(ctx: Context) { return "rust-analyzer 1.79.0 (129f3b99 2024-06-10)"; }, ), - "0-2024-06-10/1", + "1-2024-06-10", ); }); suite.addTest("Order with invalid path to RA", async () => { assert.deepStrictEqual( _private.orderFromPath("some-weird-path", function () { - assert.fail("Shouldn't get here."); + return undefined; }), "2", ); From 440b8befaafba57d5c7db854a14ef5d7c7bebd08 Mon Sep 17 00:00:00 2001 From: huntc Date: Tue, 30 Jul 2024 19:24:15 +1000 Subject: [PATCH 007/178] Outdated comment removed --- .../rust-analyzer/editors/code/src/bootstrap.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index df49b7b700786..7bc3b3c36a246 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -15,7 +15,7 @@ export async function bootstrap( if (!path) { throw new Error( "rust-analyzer Language Server is not available. " + - "Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation).", + "Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation).", ); } @@ -103,11 +103,11 @@ async function getServer( await vscode.window.showErrorMessage( "Unfortunately we don't ship binaries for your platform yet. " + - "You need to manually clone the rust-analyzer repository and " + - "run `cargo xtask install --server` to build the language server from sources. " + - "If you feel that your platform should be supported, please create an issue " + - "about that [here](https://github.com/rust-lang/rust-analyzer/issues) and we " + - "will consider it.", + "You need to manually clone the rust-analyzer repository and " + + "run `cargo xtask install --server` to build the language server from sources. " + + "If you feel that your platform should be supported, please create an issue " + + "about that [here](https://github.com/rust-lang/rust-analyzer/issues) and we " + + "will consider it.", ); return undefined; } @@ -153,7 +153,6 @@ function orderFromPath( path: string, raVersionResolver: (path: string) => string | undefined, ): string { - // It is a semver, so we must resolve Rust Analyzer's version. const raVersion = raVersionResolver(path); const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/); if (raDate?.length === 2) { From af4a59b98520412f1e17ae04390593ad260fa678 Mon Sep 17 00:00:00 2001 From: huntc Date: Tue, 30 Jul 2024 19:25:03 +1000 Subject: [PATCH 008/178] Formatting --- .../rust-analyzer/editors/code/src/bootstrap.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index 7bc3b3c36a246..9850dcc9355f7 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -15,7 +15,7 @@ export async function bootstrap( if (!path) { throw new Error( "rust-analyzer Language Server is not available. " + - "Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation).", + "Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation).", ); } @@ -103,11 +103,11 @@ async function getServer( await vscode.window.showErrorMessage( "Unfortunately we don't ship binaries for your platform yet. " + - "You need to manually clone the rust-analyzer repository and " + - "run `cargo xtask install --server` to build the language server from sources. " + - "If you feel that your platform should be supported, please create an issue " + - "about that [here](https://github.com/rust-lang/rust-analyzer/issues) and we " + - "will consider it.", + "You need to manually clone the rust-analyzer repository and " + + "run `cargo xtask install --server` to build the language server from sources. " + + "If you feel that your platform should be supported, please create an issue " + + "about that [here](https://github.com/rust-lang/rust-analyzer/issues) and we " + + "will consider it.", ); return undefined; } From 8ca9034d1f01e7e9e4d7d7c347a8632aae73c58a Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 31 Jul 2024 21:24:56 +1000 Subject: [PATCH 009/178] Corrected precedence position --- src/tools/rust-analyzer/editors/code/src/bootstrap.ts | 2 +- .../rust-analyzer/editors/code/tests/unit/bootstrap.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index 9850dcc9355f7..2bcd9ca0a408e 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -157,7 +157,7 @@ function orderFromPath( const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/); if (raDate?.length === 2) { const precedence = path.includes("nightly-") ? "0" : "1"; - return precedence + "-" + raDate[1]; + return "0-" + raDate[1] + "/" + precedence; } else { return "2"; } diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts index 884e73e454a3a..8aeb72180a02b 100644 --- a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts +++ b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts @@ -16,7 +16,7 @@ export async function getTests(ctx: Context) { return "rust-analyzer 1.67.0-nightly (b7bc90fe 2022-11-21)"; }, ), - "0-2022-11-21", + "0-2022-11-21/0", ); }); @@ -32,7 +32,7 @@ export async function getTests(ctx: Context) { return "rust-analyzer 1.72.1 (d5c2e9c3 2023-09-13)"; }, ), - "1-2023-09-13", + "0-2023-09-13/1", ); }); @@ -60,7 +60,7 @@ export async function getTests(ctx: Context) { return "rust-analyzer 1.79.0 (129f3b99 2024-06-10)"; }, ), - "1-2024-06-10", + "0-2024-06-10/1", ); }); From 44f5392f5a7f6ab4a6e8c42a436ef7cda54929d0 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 1 Aug 2024 21:24:28 +0900 Subject: [PATCH 010/178] fix: Insert a tail `Ok(())` for expr block instead of wrapping with `Ok` --- .../src/handlers/type_mismatch.rs | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 4e52d28051bd1..6f5c68d4b5c86 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -2,8 +2,12 @@ use either::Either; use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type}; use ide_db::{famous_defs::FamousDefs, source_change::SourceChange}; use syntax::{ - ast::{self, BlockExpr, ExprStmt}, - AstNode, AstPtr, + ast::{ + self, + edit::{AstNodeEdit, IndentLevel}, + BlockExpr, Expr, ExprStmt, + }, + AstNode, AstPtr, TextSize, }; use text_edit::TextEdit; @@ -119,6 +123,38 @@ fn add_missing_ok_or_some( return None; } + if d.actual.is_unit() { + if let Expr::BlockExpr(block) = &expr { + if block.tail_expr().is_none() { + let mut builder = TextEdit::builder(); + let block_indent = block.indent_level(); + + if block.statements().count() == 0 { + // Empty block + let indent = block_indent + 1; + builder.insert( + block.syntax().text_range().start() + TextSize::from(1), + format!("\n{indent}{variant_name}(())\n{block_indent}"), + ); + } else { + let indent = IndentLevel::from(1); + builder.insert( + block.syntax().text_range().end() - TextSize::from(1), + format!("{indent}{variant_name}(())\n{block_indent}"), + ); + } + + let source_change = SourceChange::from_text_edit( + expr_ptr.file_id.original_file(ctx.sema.db), + builder.finish(), + ); + let name = format!("Insert {variant_name}(()) as the tail of this block"); + acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range)); + } + return Some(()); + } + } + let mut builder = TextEdit::builder(); builder.insert(expr.syntax().text_range().start(), format!("{variant_name}(")); builder.insert(expr.syntax().text_range().end(), ")".to_owned()); @@ -533,6 +569,36 @@ fn div(x: i32, y: i32) -> MyResult { ); } + #[test] + fn test_wrapped_unit_as_block_tail_expr() { + check_fix( + r#" +//- minicore: result +fn foo() -> Result<(), ()> { + foo(); +}$0 + "#, + r#" +fn foo() -> Result<(), ()> { + foo(); + Ok(()) +} + "#, + ); + + check_fix( + r#" +//- minicore: result +fn foo() -> Result<(), ()> {}$0 + "#, + r#" +fn foo() -> Result<(), ()> { + Ok(()) +} + "#, + ); + } + #[test] fn test_in_const_and_static() { check_fix( From cce9da3befb9226b85c66a9aa5dd88ef9c747d1c Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Thu, 1 Aug 2024 22:06:15 +0800 Subject: [PATCH 011/178] feat: support inlay hint for more expr with label --- .../ide/src/inlay_hints/closing_brace.rs | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index ea96c9504e55c..318fe1c93967e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -7,7 +7,7 @@ use hir::{HirDisplay, Semantics}; use ide_db::{FileRange, RootDatabase}; use span::EditionedFileId; use syntax::{ - ast::{self, AstNode, HasName}, + ast::{self, AstNode, HasLoopBody, HasName}, match_ast, SyntaxKind, SyntaxNode, T, }; @@ -57,9 +57,30 @@ pub(super) fn hints( // the actual number of lines in this case should be the line count of the parent BlockExpr, // which the `min_lines` config cares about node = node.parent()?; - let block = label.syntax().parent().and_then(ast::BlockExpr::cast)?; - closing_token = block.stmt_list()?.r_curly_token()?; + + let parent = label.syntax().parent()?; + let block; + match_ast! { + match parent { + ast::BlockExpr(block_expr) => { + block = block_expr.stmt_list()?; + }, + ast::LoopExpr(loop_expr) => { + block = loop_expr.loop_body()?.stmt_list()?; + }, + ast::WhileExpr(while_expr) => { + block = while_expr.loop_body()?.stmt_list()?; + }, + ast::ForExpr(for_expr) => { + block = for_expr.loop_body()?.stmt_list()?; + }, + _ => return None, + } + } + closing_token = block.r_curly_token()?; + let lifetime = label.lifetime().map_or_else(String::new, |it| it.to_string()); + (lifetime, Some(label.syntax().text_range())) } else if let Some(block) = ast::BlockExpr::cast(node.clone()) { closing_token = block.stmt_list()?.r_curly_token()?; From 5ac2ad4eafb18c6e4351af9c22aff5081a161cd9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 2 Aug 2024 11:08:19 +0200 Subject: [PATCH 012/178] internal: Remove AbsPathBuf::TryFrom impl that checks too many things at once --- src/tools/rust-analyzer/Cargo.lock | 1 + .../rust-analyzer/crates/paths/src/lib.rs | 16 +---- .../crates/project-model/src/build_scripts.rs | 14 ++-- .../crates/project-model/src/lib.rs | 7 +- .../crates/rust-analyzer/src/bin/main.rs | 3 + .../rust-analyzer/src/cli/rustc_tests.rs | 3 +- .../crates/rust-analyzer/src/config.rs | 71 +++++-------------- .../src/handlers/notification.rs | 3 + .../rust-analyzer/src/handlers/request.rs | 9 ++- .../src/integrated_benchmarks.rs | 30 ++++++-- .../rust-analyzer/src/lsp/from_proto.rs | 3 +- .../rust-analyzer/crates/syntax/src/tests.rs | 4 +- .../crates/test-utils/Cargo.toml | 1 + .../crates/test-utils/src/lib.rs | 6 +- .../crates/vfs-notify/src/lib.rs | 14 +++- 15 files changed, 90 insertions(+), 95 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index b98a1195d8bb7..e4f6ad28c1ed3 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1987,6 +1987,7 @@ name = "test-utils" version = "0.0.0" dependencies = [ "dissimilar", + "paths", "profile", "rustc-hash", "stdx", diff --git a/src/tools/rust-analyzer/crates/paths/src/lib.rs b/src/tools/rust-analyzer/crates/paths/src/lib.rs index 885f071889eca..4d7af96cd98d7 100644 --- a/src/tools/rust-analyzer/crates/paths/src/lib.rs +++ b/src/tools/rust-analyzer/crates/paths/src/lib.rs @@ -1,4 +1,4 @@ -//! Thin wrappers around `std::path`/`camino::path`, distinguishing between absolute and +//! Thin wrappers around [`camino::path`], distinguishing between absolute and //! relative paths. use std::{ @@ -8,9 +8,9 @@ use std::{ path::{Path, PathBuf}, }; -pub use camino::*; +pub use camino::{Utf8Component, Utf8Components, Utf8Path, Utf8PathBuf, Utf8Prefix}; -/// Wrapper around an absolute [`Utf8PathBuf`]. +/// A [`Utf8PathBuf`] that is guaranteed to be absolute. #[derive(Debug, Clone, Ord, PartialOrd, Eq, Hash)] pub struct AbsPathBuf(Utf8PathBuf); @@ -73,16 +73,6 @@ impl TryFrom for AbsPathBuf { } } -impl TryFrom for AbsPathBuf { - type Error = PathBuf; - fn try_from(path_buf: PathBuf) -> Result { - if !path_buf.is_absolute() { - return Err(path_buf); - } - Ok(AbsPathBuf(Utf8PathBuf::from_path_buf(path_buf)?)) - } -} - impl TryFrom<&str> for AbsPathBuf { type Error = Utf8PathBuf; fn try_from(path: &str) -> Result { diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index 839d8e569fe3f..e434bb7c8c82f 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -6,17 +6,12 @@ //! This module implements this second part. We use "build script" terminology //! here, but it covers procedural macros as well. -use std::{ - cell::RefCell, - io, mem, - path::{self, PathBuf}, - process::Command, -}; +use std::{cell::RefCell, io, mem, path, process::Command}; use cargo_metadata::{camino::Utf8Path, Message}; use itertools::Itertools; use la_arena::ArenaMap; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize; use toolchain::Tool; @@ -423,7 +418,7 @@ impl WorkspaceBuildScripts { utf8_stdout(cmd) })()?; - let target_libdir = AbsPathBuf::try_from(PathBuf::from(target_libdir)) + let target_libdir = AbsPathBuf::try_from(Utf8PathBuf::from(target_libdir)) .map_err(|_| anyhow::format_err!("target-libdir was not an absolute path"))?; tracing::info!("Loading rustc proc-macro paths from {target_libdir}"); @@ -435,7 +430,8 @@ impl WorkspaceBuildScripts { let extension = path.extension()?; if extension == std::env::consts::DLL_EXTENSION { let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned(); - let path = AbsPathBuf::try_from(path).ok()?; + let path = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).ok()?) + .ok()?; return Some((name, path)); } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index 92bf6a08f870d..6510ced66ac27 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -37,7 +37,7 @@ use std::{ }; use anyhow::{bail, format_err, Context}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashSet; pub use crate::{ @@ -132,8 +132,11 @@ impl ProjectManifest { .filter_map(Result::ok) .map(|it| it.path().join("Cargo.toml")) .filter(|it| it.exists()) + .map(Utf8PathBuf::from_path_buf) + .filter_map(Result::ok) .map(AbsPathBuf::try_from) - .filter_map(|it| it.ok()?.try_into().ok()) + .filter_map(Result::ok) + .filter_map(|it| it.try_into().ok()) .collect() } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index 6a980a153c991..42953d3b833c2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -14,6 +14,7 @@ use std::{env, fs, path::PathBuf, process::ExitCode, sync::Arc}; use anyhow::Context; use lsp_server::Connection; +use paths::Utf8PathBuf; use rust_analyzer::{ cli::flags, config::{Config, ConfigChange, ConfigErrors}, @@ -189,6 +190,7 @@ fn run_server() -> anyhow::Result<()> { let root_path = match root_uri .and_then(|it| it.to_file_path().ok()) .map(patch_path_prefix) + .and_then(|it| Utf8PathBuf::from_path_buf(it).ok()) .and_then(|it| AbsPathBuf::try_from(it).ok()) { Some(it) => it, @@ -218,6 +220,7 @@ fn run_server() -> anyhow::Result<()> { .into_iter() .filter_map(|it| it.uri.to_file_path().ok()) .map(patch_path_prefix) + .filter_map(|it| Utf8PathBuf::from_path_buf(it).ok()) .filter_map(|it| AbsPathBuf::try_from(it).ok()) .collect::>() }) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 31565878d845c..fddc790e6989d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -8,6 +8,7 @@ use std::{cell::RefCell, fs::read_to_string, panic::AssertUnwindSafe, path::Path use hir::{ChangeWithProcMacros, Crate}; use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig}; use itertools::Either; +use paths::Utf8PathBuf; use profile::StopWatch; use project_model::target_data_layout::RustcDataLayoutConfig; use project_model::{ @@ -64,7 +65,7 @@ impl Tester { fn new() -> Result { let mut path = std::env::temp_dir(); path.push("ra-rustc-test.rs"); - let tmp_file = AbsPathBuf::try_from(path).unwrap(); + let tmp_file = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).unwrap()).unwrap(); std::fs::write(&tmp_file, "")?; let cargo_config = CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index b9b8cfdfc9eab..3ebe12eb1fbdf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -3450,7 +3450,7 @@ mod tests { let s = remove_ws(&schema); if !p.contains(&s) { package_json.replace_range(start..end, &schema); - ensure_file_contents(&package_json_path, &package_json) + ensure_file_contents(package_json_path.as_std_path(), &package_json) } } @@ -3458,7 +3458,7 @@ mod tests { fn generate_config_documentation() { let docs_path = project_root().join("docs/user/generated_config.adoc"); let expected = FullConfigInput::manual(); - ensure_file_contents(&docs_path, &expected); + ensure_file_contents(docs_path.as_std_path(), &expected); } fn remove_ws(text: &str) -> String { @@ -3467,13 +3467,8 @@ mod tests { #[test] fn proc_macro_srv_null() { - let mut config = Config::new( - AbsPathBuf::try_from(project_root()).unwrap(), - Default::default(), - vec![], - None, - None, - ); + let mut config = + Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None); let mut change = ConfigChange::default(); change.change_client_config(serde_json::json!({ @@ -3487,32 +3482,22 @@ mod tests { #[test] fn proc_macro_srv_abs() { - let mut config = Config::new( - AbsPathBuf::try_from(project_root()).unwrap(), - Default::default(), - vec![], - None, - None, - ); + let mut config = + Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None); let mut change = ConfigChange::default(); change.change_client_config(serde_json::json!({ "procMacro" : { - "server": project_root().display().to_string(), + "server": project_root().to_string(), }})); (config, _, _) = config.apply_change(change); - assert_eq!(config.proc_macro_srv(), Some(AbsPathBuf::try_from(project_root()).unwrap())); + assert_eq!(config.proc_macro_srv(), Some(AbsPathBuf::assert(project_root()))); } #[test] fn proc_macro_srv_rel() { - let mut config = Config::new( - AbsPathBuf::try_from(project_root()).unwrap(), - Default::default(), - vec![], - None, - None, - ); + let mut config = + Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None); let mut change = ConfigChange::default(); @@ -3531,13 +3516,8 @@ mod tests { #[test] fn cargo_target_dir_unset() { - let mut config = Config::new( - AbsPathBuf::try_from(project_root()).unwrap(), - Default::default(), - vec![], - None, - None, - ); + let mut config = + Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None); let mut change = ConfigChange::default(); @@ -3554,13 +3534,8 @@ mod tests { #[test] fn cargo_target_dir_subdir() { - let mut config = Config::new( - AbsPathBuf::try_from(project_root()).unwrap(), - Default::default(), - vec![], - None, - None, - ); + let mut config = + Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None); let mut change = ConfigChange::default(); change.change_client_config(serde_json::json!({ @@ -3577,13 +3552,8 @@ mod tests { #[test] fn cargo_target_dir_relative_dir() { - let mut config = Config::new( - AbsPathBuf::try_from(project_root()).unwrap(), - Default::default(), - vec![], - None, - None, - ); + let mut config = + Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None); let mut change = ConfigChange::default(); change.change_client_config(serde_json::json!({ @@ -3603,13 +3573,8 @@ mod tests { #[test] fn toml_unknown_key() { - let config = Config::new( - AbsPathBuf::try_from(project_root()).unwrap(), - Default::default(), - vec![], - None, - None, - ); + let config = + Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None); let mut change = ConfigChange::default(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 4b14dcfc3721b..a2f9229047ec8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -9,6 +9,7 @@ use lsp_types::{ DidChangeWatchedFilesParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, WorkDoneProgressCancelParams, }; +use paths::Utf8PathBuf; use triomphe::Arc; use vfs::{AbsPathBuf, ChangeKind, VfsPath}; @@ -240,6 +241,7 @@ pub(crate) fn handle_did_change_workspace_folders( for workspace in params.event.removed { let Ok(path) = workspace.uri.to_file_path() else { continue }; + let Ok(path) = Utf8PathBuf::from_path_buf(path) else { continue }; let Ok(path) = AbsPathBuf::try_from(path) else { continue }; config.remove_workspace(&path); } @@ -249,6 +251,7 @@ pub(crate) fn handle_did_change_workspace_folders( .added .into_iter() .filter_map(|it| it.uri.to_file_path().ok()) + .filter_map(|it| Utf8PathBuf::from_path_buf(it).ok()) .filter_map(|it| AbsPathBuf::try_from(it).ok()); config.add_workspaces(added); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index eca139d79ae6a..fc3a02ab1843d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -781,9 +781,12 @@ pub(crate) fn 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" { // search workspaces for parent packages or fallback to workspace root - let abs_path_buf = match AbsPathBuf::try_from(file_path.to_path_buf()).ok() { - Some(abs_path_buf) => abs_path_buf, - None => return Ok(None), + let abs_path_buf = match Utf8PathBuf::from_path_buf(file_path.to_path_buf()) + .ok() + .map(AbsPathBuf::try_from) + { + Some(Ok(abs_path_buf)) => abs_path_buf, + _ => return Ok(None), }; let manifest_path = match ManifestPath::try_from(abs_path_buf).ok() { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index ff8eb6c8612e0..f6543a82e57ab 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -46,13 +46,19 @@ fn integrated_highlighting_benchmark() { let (db, vfs, _proc_macro) = { let _it = stdx::timeit("workspace loading"); - load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap() + load_workspace_at( + workspace_to_load.as_std_path(), + &cargo_config, + &load_cargo_config, + &|_| {}, + ) + .unwrap() }; let mut host = AnalysisHost::with_database(db); let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); + let path = VfsPath::from(AbsPathBuf::assert(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; @@ -106,13 +112,19 @@ fn integrated_completion_benchmark() { let (db, vfs, _proc_macro) = { let _it = stdx::timeit("workspace loading"); - load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap() + load_workspace_at( + workspace_to_load.as_std_path(), + &cargo_config, + &load_cargo_config, + &|_| {}, + ) + .unwrap() }; let mut host = AnalysisHost::with_database(db); let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); + let path = VfsPath::from(AbsPathBuf::assert(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; @@ -274,13 +286,19 @@ fn integrated_diagnostics_benchmark() { let (db, vfs, _proc_macro) = { let _it = stdx::timeit("workspace loading"); - load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap() + load_workspace_at( + workspace_to_load.as_std_path(), + &cargo_config, + &load_cargo_config, + &|_| {}, + ) + .unwrap() }; let mut host = AnalysisHost::with_database(db); let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); + let path = VfsPath::from(AbsPathBuf::assert(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs index aea424298f8c8..1f4544887f062 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs @@ -2,6 +2,7 @@ use anyhow::format_err; use ide::{Annotation, AnnotationKind, AssistKind, LineCol}; use ide_db::{line_index::WideLineCol, FileId, FilePosition, FileRange}; +use paths::Utf8PathBuf; use syntax::{TextRange, TextSize}; use vfs::AbsPathBuf; @@ -13,7 +14,7 @@ use crate::{ 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()) + Ok(AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).unwrap()).unwrap()) } pub(crate) fn vfs_path(url: &lsp_types::Url) -> anyhow::Result { diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests.rs b/src/tools/rust-analyzer/crates/syntax/src/tests.rs index f0d58efc01ed8..b50489c6f0f2a 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests.rs @@ -79,7 +79,7 @@ fn self_hosting_parsing() { let crates_dir = project_root().join("crates"); let mut files = Vec::new(); - let mut work = vec![crates_dir.to_path_buf()]; + let mut work = vec![crates_dir.into_std_path_buf()]; while let Some(dir) = work.pop() { for entry in dir.read_dir().unwrap() { let entry = entry.unwrap(); @@ -127,7 +127,7 @@ fn self_hosting_parsing() { } fn test_data_dir() -> PathBuf { - project_root().join("crates/syntax/test_data") + project_root().into_std_path_buf().join("crates/syntax/test_data") } fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) { diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml index 2ff1fad6c29d9..44628a6c11c0d 100644 --- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml @@ -18,6 +18,7 @@ text-size.workspace = true tracing.workspace = true rustc-hash.workspace = true +paths.workspace = true stdx.workspace = true profile.workspace = true diff --git a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs index 088817b8357ce..36be9937d3fea 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs @@ -18,6 +18,7 @@ use std::{ path::{Path, PathBuf}, }; +use paths::Utf8PathBuf; use profile::StopWatch; use stdx::is_ci; use text_size::{TextRange, TextSize}; @@ -402,9 +403,10 @@ pub fn skip_slow_tests() -> bool { } /// Returns the path to the root directory of `rust-analyzer` project. -pub fn project_root() -> PathBuf { +pub fn project_root() -> Utf8PathBuf { let dir = env!("CARGO_MANIFEST_DIR"); - PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() + Utf8PathBuf::from_path_buf(PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()) + .unwrap() } pub fn format_diff(chunks: Vec>) -> String { diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 7e0f9af7af89f..a87b75e68f94f 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -14,7 +14,7 @@ use std::{ use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use vfs::loader; use walkdir::WalkDir; @@ -145,7 +145,12 @@ impl NotifyActor { let files = event .paths .into_iter() - .map(|path| AbsPathBuf::try_from(path).unwrap()) + .filter_map(|path| { + Some( + AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).ok()?) + .expect("path is absolute"), + ) + }) .filter_map(|path| { let meta = fs::metadata(&path).ok()?; if meta.file_type().is_dir() @@ -220,7 +225,10 @@ impl NotifyActor { let depth = entry.depth(); let is_dir = entry.file_type().is_dir(); let is_file = entry.file_type().is_file(); - let abs_path = AbsPathBuf::try_from(entry.into_path()).ok()?; + let abs_path = AbsPathBuf::try_from( + Utf8PathBuf::from_path_buf(entry.into_path()).ok()?, + ) + .ok()?; if depth < 2 && is_dir { self.send(make_message(abs_path.clone())); } From 07e6f9d918d3de3e95af8557084db15af17db0ed Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 2 Aug 2024 12:57:15 +0200 Subject: [PATCH 013/178] internal: Load VFS config changes in parallel --- src/tools/rust-analyzer/Cargo.lock | 1 + .../crates/load-cargo/src/lib.rs | 10 +- .../crates/rust-analyzer/src/main_loop.rs | 11 +- .../rust-analyzer/tests/slow-tests/main.rs | 2 +- .../crates/vfs-notify/Cargo.toml | 3 +- .../crates/vfs-notify/src/lib.rs | 172 +++++++++++------- .../rust-analyzer/crates/vfs/src/loader.rs | 11 +- 7 files changed, 129 insertions(+), 81 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e4f6ad28c1ed3..925afdcc98474 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2332,6 +2332,7 @@ dependencies = [ "crossbeam-channel", "notify", "paths", + "rayon", "stdx", "tracing", "vfs", diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 8737f2246be59..a1aee43815983 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -19,7 +19,11 @@ use project_model::{ CargoConfig, ManifestPath, PackageRoot, ProjectManifest, ProjectWorkspace, ProjectWorkspaceKind, }; use span::Span; -use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath}; +use vfs::{ + file_set::FileSetConfig, + loader::{Handle, LoadingProgress}, + AbsPath, AbsPathBuf, VfsPath, +}; pub struct LoadCargoConfig { pub load_out_dirs_from_check: bool, @@ -409,8 +413,8 @@ fn load_crate_graph( // wait until Vfs has loaded all roots for task in receiver { match task { - vfs::loader::Message::Progress { n_done, n_total, .. } => { - if n_done == Some(n_total) { + vfs::loader::Message::Progress { n_done, .. } => { + if n_done == LoadingProgress::Finished { break; } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 9c820749ece6d..9db81f2295d2e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -15,7 +15,7 @@ use lsp_server::{Connection, Notification, Request}; use lsp_types::{notification::Notification as _, TextDocumentIdentifier}; use stdx::thread::ThreadIntent; use tracing::{error, span, Level}; -use vfs::{AbsPathBuf, FileId}; +use vfs::{loader::LoadingProgress, AbsPathBuf, FileId}; use crate::{ config::Config, @@ -772,12 +772,11 @@ impl GlobalState { let _p = tracing::info_span!("GlobalState::handle_vfs_mgs/progress").entered(); always!(config_version <= self.vfs_config_version); - let state = match n_done { - None => Progress::Begin, - Some(done) if done == n_total => Progress::End, - Some(_) => Progress::Report, + let (n_done, state) = match n_done { + LoadingProgress::Started => (0, Progress::Begin), + LoadingProgress::Progress(n_done) => (n_done.min(n_total), Progress::Report), + LoadingProgress::Finished => (n_total, Progress::End), }; - let n_done = n_done.unwrap_or_default(); self.vfs_progress_config_version = config_version; self.vfs_progress_n_total = n_total; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index b1ef48377178b..a2825464f0980 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -909,7 +909,7 @@ version = \"0.0.0\" fn out_dirs_check_impl(root_contains_symlink: bool) { if skip_slow_tests() { - // return; + return; } let mut server = Project::with_fixture( diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index a6d5027c3a60a..3602bac4dd156 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -16,10 +16,11 @@ tracing.workspace = true walkdir = "2.3.2" crossbeam-channel = "0.5.5" notify = "6.1.1" +rayon = "1.10.0" stdx.workspace = true vfs.workspace = true paths.workspace = true [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index a87b75e68f94f..0972e4234e812 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -10,12 +10,14 @@ use std::{ fs, path::{Component, Path}, + sync::atomic::AtomicUsize, }; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; -use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher}; +use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; -use vfs::loader; +use rayon::iter::{IndexedParallelIterator as _, IntoParallelIterator as _, ParallelIterator}; +use vfs::loader::{self, LoadingProgress}; use walkdir::WalkDir; #[derive(Debug)] @@ -104,35 +106,61 @@ impl NotifyActor { let config_version = config.version; let n_total = config.load.len(); - self.send(loader::Message::Progress { + self.watched_entries.clear(); + + let send = |msg| (self.sender)(msg); + send(loader::Message::Progress { n_total, - n_done: None, + n_done: LoadingProgress::Started, config_version, dir: None, }); - self.watched_entries.clear(); - - for (i, entry) in config.load.into_iter().enumerate() { - let watch = config.watch.contains(&i); - if watch { - self.watched_entries.push(entry.clone()); + let (entry_tx, entry_rx) = unbounded(); + let (watch_tx, watch_rx) = unbounded(); + let processed = AtomicUsize::new(0); + config.load.into_par_iter().enumerate().for_each(move |(i, entry)| { + let do_watch = config.watch.contains(&i); + if do_watch { + _ = entry_tx.send(entry.clone()); } - let files = - self.load_entry(entry, watch, |file| loader::Message::Progress { - n_total, - n_done: Some(i), - dir: Some(file), - config_version, - }); - self.send(loader::Message::Loaded { files }); - self.send(loader::Message::Progress { + let files = Self::load_entry( + |f| _ = watch_tx.send(f.to_owned()), + entry, + do_watch, + |file| { + send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Progress( + processed.load(std::sync::atomic::Ordering::Relaxed), + ), + dir: Some(file), + config_version, + }) + }, + ); + send(loader::Message::Loaded { files }); + send(loader::Message::Progress { n_total, - n_done: Some(i + 1), + n_done: LoadingProgress::Progress( + processed.fetch_add(1, std::sync::atomic::Ordering::AcqRel) + 1, + ), config_version, dir: None, }); + }); + for path in watch_rx { + self.watch(&path); + } + for entry in entry_rx { + self.watched_entries.push(entry); } + self.send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Finished, + config_version, + dir: None, + }); } Message::Invalidate(path) => { let contents = read(path.as_path()); @@ -142,60 +170,67 @@ impl NotifyActor { }, Event::NotifyEvent(event) => { if let Some(event) = log_notify_error(event) { - let files = event - .paths - .into_iter() - .filter_map(|path| { - Some( - AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).ok()?) + if let EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) = + event.kind + { + let files = event + .paths + .into_iter() + .filter_map(|path| { + Some( + AbsPathBuf::try_from( + Utf8PathBuf::from_path_buf(path).ok()?, + ) .expect("path is absolute"), - ) - }) - .filter_map(|path| { - let meta = fs::metadata(&path).ok()?; - if meta.file_type().is_dir() - && self + ) + }) + .filter_map(|path| { + let meta = fs::metadata(&path).ok()?; + if meta.file_type().is_dir() + && self + .watched_entries + .iter() + .any(|entry| entry.contains_dir(&path)) + { + self.watch(path.as_ref()); + return None; + } + + if !meta.file_type().is_file() { + return None; + } + if !self .watched_entries .iter() - .any(|entry| entry.contains_dir(&path)) - { - self.watch(path); - return None; - } - - if !meta.file_type().is_file() { - return None; - } - if !self - .watched_entries - .iter() - .any(|entry| entry.contains_file(&path)) - { - return None; - } - - let contents = read(&path); - Some((path, contents)) - }) - .collect(); - self.send(loader::Message::Changed { files }); + .any(|entry| entry.contains_file(&path)) + { + return None; + } + + let contents = read(&path); + Some((path, contents)) + }) + .collect(); + self.send(loader::Message::Changed { files }); + } } } } } } + fn load_entry( - &mut self, + mut watch: impl FnMut(&Path), entry: loader::Entry, - watch: bool, - make_message: impl Fn(AbsPathBuf) -> loader::Message, + do_watch: bool, + send_message: impl Fn(AbsPathBuf), ) -> Vec<(AbsPathBuf, Option>)> { match entry { loader::Entry::Files(files) => files .into_iter() .map(|file| { - if watch { - self.watch(file.clone()); + if do_watch { + watch(file.as_ref()); } let contents = read(file.as_path()); (file, contents) @@ -205,7 +240,7 @@ impl NotifyActor { let mut res = Vec::new(); for root in &dirs.include { - self.send(make_message(root.clone())); + send_message(root.clone()); let walkdir = WalkDir::new(root).follow_links(true).into_iter().filter_entry(|entry| { if !entry.file_type().is_dir() { @@ -213,7 +248,7 @@ impl NotifyActor { } let path = entry.path(); - if path_is_parent_symlink(path) { + if path_might_be_cyclic(path) { return false; } @@ -230,10 +265,10 @@ impl NotifyActor { ) .ok()?; if depth < 2 && is_dir { - self.send(make_message(abs_path.clone())); + send_message(abs_path.clone()); } - if is_dir && watch { - self.watch(abs_path.clone()); + if is_dir && do_watch { + watch(abs_path.as_ref()); } if !is_file { return None; @@ -255,12 +290,13 @@ impl NotifyActor { } } - fn watch(&mut self, path: AbsPathBuf) { + fn watch(&mut self, path: &Path) { if let Some((watcher, _)) = &mut self.watcher { - log_notify_error(watcher.watch(path.as_ref(), RecursiveMode::NonRecursive)); + log_notify_error(watcher.watch(path, RecursiveMode::NonRecursive)); } } - fn send(&mut self, msg: loader::Message) { + + fn send(&self, msg: loader::Message) { (self.sender)(msg); } } @@ -279,7 +315,7 @@ fn log_notify_error(res: notify::Result) -> Option { /// heuristic is not sufficient to catch all symlink cycles (it's /// possible to construct cycle using two or more symlinks), but it /// catches common cases. -fn path_is_parent_symlink(path: &Path) -> bool { +fn path_might_be_cyclic(path: &Path) -> bool { let Ok(destination) = std::fs::read_link(path) else { return false; }; diff --git a/src/tools/rust-analyzer/crates/vfs/src/loader.rs b/src/tools/rust-analyzer/crates/vfs/src/loader.rs index 3af91b1af815b..30c08a9ff2b9c 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/loader.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/loader.rs @@ -43,6 +43,13 @@ pub struct Config { pub watch: Vec, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum LoadingProgress { + Started, + Progress(usize), + Finished, +} + /// Message about an action taken by a [`Handle`]. pub enum Message { /// Indicate a gradual progress. @@ -52,7 +59,7 @@ pub enum Message { /// The total files to be loaded. n_total: usize, /// The files that have been loaded successfully. - n_done: Option, + n_done: LoadingProgress, /// The dir being loaded, `None` if its for a file. dir: Option, /// The [`Config`] version. @@ -65,7 +72,7 @@ pub enum Message { } /// Type that will receive [`Messages`](Message) from a [`Handle`]. -pub type Sender = Box; +pub type Sender = Box; /// Interface for reading and watching files. pub trait Handle: fmt::Debug { From 2d8a4949e92afa3f779e044152b6f3f5142e05bd Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 2 Aug 2024 17:09:25 +0200 Subject: [PATCH 014/178] Wait with change processing until the vfs is done --- .../crates/rust-analyzer/src/global_state.rs | 10 +++---- .../crates/rust-analyzer/src/main_loop.rs | 21 +++++++++------ .../crates/rust-analyzer/src/reload.rs | 26 +++++++++---------- .../crates/vfs-notify/src/lib.rs | 1 + src/tools/rust-analyzer/crates/vfs/src/lib.rs | 6 ++--- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index f1dde104fce4a..e435af6c807e6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -33,7 +33,7 @@ use crate::{ lsp_ext, main_loop::Task, mem_docs::MemDocs, - op_queue::OpQueue, + op_queue::{Cause, OpQueue}, reload, target_spec::{CargoTargetSpec, ProjectJsonTargetSpec, TargetSpec}, task_pool::{TaskPool, TaskQueue}, @@ -108,8 +108,8 @@ pub(crate) struct GlobalState { pub(crate) vfs: Arc)>>, pub(crate) vfs_config_version: u32, pub(crate) vfs_progress_config_version: u32, - pub(crate) vfs_progress_n_total: usize, - pub(crate) vfs_progress_n_done: usize, + pub(crate) vfs_done: bool, + pub(crate) wants_to_switch: Option, /// `workspaces` field stores the data we actually use, while the `OpQueue` /// stores the result of the last fetch. @@ -252,8 +252,8 @@ impl GlobalState { vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))), vfs_config_version: 0, vfs_progress_config_version: 0, - vfs_progress_n_total: 0, - vfs_progress_n_done: 0, + vfs_done: true, + wants_to_switch: None, workspaces: Arc::from(Vec::new()), crate_graph_file_dependencies: FxHashSet::default(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 9db81f2295d2e..23ae28239692f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -375,9 +375,14 @@ impl GlobalState { } } let event_handling_duration = loop_start.elapsed(); - - let state_changed = self.process_changes(); - let memdocs_added_or_removed = self.mem_docs.take_changes(); + let (state_changed, memdocs_added_or_removed) = if self.vfs_done { + if let Some(cause) = self.wants_to_switch.take() { + self.switch_workspaces(cause); + } + (self.process_changes(), self.mem_docs.take_changes()) + } else { + (false, false) + }; if self.is_quiescent() { let became_quiescent = !was_quiescent; @@ -672,7 +677,7 @@ impl GlobalState { if let Err(e) = self.fetch_workspace_error() { error!("FetchWorkspaceError:\n{e}"); } - self.switch_workspaces("fetched workspace".to_owned()); + self.wants_to_switch = Some("fetched workspace".to_owned()); (Progress::End, None) } }; @@ -718,8 +723,9 @@ impl GlobalState { error!("FetchBuildDataError:\n{e}"); } - self.switch_workspaces("fetched build data".to_owned()); - + if self.wants_to_switch.is_none() { + self.wants_to_switch = Some("fetched build data".to_owned()); + } (Some(Progress::End), None) } }; @@ -779,8 +785,7 @@ impl GlobalState { }; self.vfs_progress_config_version = config_version; - self.vfs_progress_n_total = n_total; - self.vfs_progress_n_done = n_done; + self.vfs_done = state == Progress::End; let mut message = format!("{n_done}/{n_total}"); if let Some(dir) = dir { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 5c95ccd4b826f..39301f4288429 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -62,13 +62,13 @@ pub(crate) enum ProcMacroProgress { impl GlobalState { pub(crate) fn is_quiescent(&self) -> bool { - !(self.last_reported_status.is_none() - || self.fetch_workspaces_queue.op_in_progress() - || self.fetch_build_data_queue.op_in_progress() - || self.fetch_proc_macros_queue.op_in_progress() - || self.discover_workspace_queue.op_in_progress() - || self.vfs_progress_config_version < self.vfs_config_version - || self.vfs_progress_n_done < self.vfs_progress_n_total) + self.vfs_done + && self.last_reported_status.is_some() + && !self.fetch_workspaces_queue.op_in_progress() + && !self.fetch_build_data_queue.op_in_progress() + && !self.fetch_proc_macros_queue.op_in_progress() + && !self.discover_workspace_queue.op_in_progress() + && self.vfs_progress_config_version >= self.vfs_config_version } pub(crate) fn update_configuration(&mut self, config: Config) { @@ -102,15 +102,13 @@ impl GlobalState { } pub(crate) fn current_status(&self) -> lsp_ext::ServerStatusParams { - let mut status = lsp_ext::ServerStatusParams { - health: lsp_ext::Health::Ok, - quiescent: self.is_quiescent(), - message: None, - }; + let quiescent = self.is_quiescent(); + let mut status = + lsp_ext::ServerStatusParams { health: lsp_ext::Health::Ok, quiescent, message: None }; let mut message = String::new(); if !self.config.cargo_autoreload(None) - && self.is_quiescent() + && quiescent && self.fetch_workspaces_queue.op_requested() && self.config.discover_workspace_config().is_none() { @@ -242,7 +240,7 @@ impl GlobalState { let discover_command = self.config.discover_workspace_config().cloned(); let is_quiescent = !(self.discover_workspace_queue.op_in_progress() || self.vfs_progress_config_version < self.vfs_config_version - || self.vfs_progress_n_done < self.vfs_progress_n_total); + || !self.vfs_done); move |sender| { let progress = { diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 0972e4234e812..7328cd9ed6018 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -61,6 +61,7 @@ type NotifyEvent = notify::Result; struct NotifyActor { sender: loader::Sender, + // FIXME: Consider hashset watched_entries: Vec, // Drop order is significant. watcher: Option<(RecommendedWatcher, Receiver)>, diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs index 77f890fd7e0de..bc40e03c5a246 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs @@ -201,8 +201,8 @@ impl Vfs { pub fn set_file_contents(&mut self, path: VfsPath, contents: Option>) -> bool { let _p = span!(Level::INFO, "Vfs::set_file_contents").entered(); let file_id = self.alloc_file_id(path); - let state = self.get(file_id); - let change_kind = match (state, contents) { + let state: FileState = self.get(file_id); + let change = match (state, contents) { (FileState::Deleted, None) => return false, (FileState::Deleted, Some(v)) => { let hash = hash_once::(&*v); @@ -225,7 +225,7 @@ impl Vfs { }; }; - let changed_file = ChangedFile { file_id, change: change_kind }; + let changed_file = ChangedFile { file_id, change }; match self.changes.entry(file_id) { // two changes to the same file in one cycle, merge them appropriately Entry::Occupied(mut o) => { From a95ff26fdcb065dbd03bda0e9a69423338bb335f Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sun, 4 Aug 2024 19:19:15 +0800 Subject: [PATCH 015/178] test: add test case for inlay hint support for expr with label --- .../crates/ide/src/inlay_hints/closing_brace.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 318fe1c93967e..9b7e708848b8f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -240,6 +240,19 @@ fn test() { //^ 'do_a } //^ 'end + + 'a: loop { + 'b: for i in 0..5 { + 'c: while true { + + + } + //^ 'c + } + //^ 'b + } + //^ 'a + } //^ fn test "#, From fbe7233c0a17d23b0035112259a7b37cda2d419b Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 5 Aug 2024 00:06:29 +0900 Subject: [PATCH 016/178] fix: Insert a generic arg for `impl Trait` when lowering generic args --- .../crates/hir-def/src/path/lower.rs | 5 +++++ .../crates/hir-ty/src/tests/regression.rs | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs index 7c39773aa6874..70918a9358e8e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs @@ -194,6 +194,11 @@ pub(super) fn lower_generic_args( match generic_arg { ast::GenericArg::TypeArg(type_arg) => { let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.ty()); + type_ref.walk(&mut |tr| { + if let TypeRef::ImplTrait(bounds) = tr { + lower_ctx.update_impl_traits_bounds(bounds.clone()); + } + }); args.push(GenericArg::Type(type_ref)); } ast::GenericArg::AssocTypeArg(assoc_type_arg) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index ac2dfea1010d4..281983861207b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2122,3 +2122,22 @@ fn test() { "#, ) } + +#[test] +fn issue_17191() { + check_types( + r#" +trait A { + type Item; +} + +trait B {} + +fn foo>() {} + +fn test() { + let f = foo; + //^ fn foo<{unknown}>() +}"#, + ); +} From b6b4e1a9c5bfa009f46a9ab9239df4fcffe271eb Mon Sep 17 00:00:00 2001 From: Joel Daniel Rico Date: Mon, 5 Aug 2024 00:29:28 -0700 Subject: [PATCH 017/178] add msvc note to manual --- src/tools/rust-analyzer/docs/user/manual.adoc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc index a177720908748..703ec66921c4c 100644 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ b/src/tools/rust-analyzer/docs/user/manual.adoc @@ -203,6 +203,12 @@ The `rust-analyzer` binary can be installed via https://brew.sh/[Homebrew]. $ brew install rust-analyzer ---- +==== Windows + +It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation. +Download links can be found +https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist[here]. + === VS Code or VSCodium in Flatpak Setting up `rust-analyzer` with a Flatpak version of Code is not trivial because of the Flatpak sandbox. From ef7d2c5d6534464ea2bd3e2b72471bbcff4a698a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 Aug 2024 12:18:19 +0200 Subject: [PATCH 018/178] feat: Load sysroot library via cargo metadata --- .../src/handlers/bind_unused_param.rs | 2 +- .../crates/load-cargo/src/lib.rs | 4 +- .../rust-analyzer/crates/paths/src/lib.rs | 4 + .../crates/project-model/src/build_scripts.rs | 2 +- .../project-model/src/cargo_workspace.rs | 6 +- .../crates/project-model/src/manifest_path.rs | 12 + .../crates/project-model/src/sysroot.rs | 236 ++++++++---------- .../crates/project-model/src/tests.rs | 13 +- .../crates/project-model/src/workspace.rs | 51 ++-- .../rust-analyzer/src/cli/analysis_stats.rs | 1 - .../crates/rust-analyzer/src/cli/flags.rs | 4 - .../rust-analyzer/src/cli/rustc_tests.rs | 2 +- .../crates/rust-analyzer/src/config.rs | 9 - .../crates/rust-analyzer/tests/crate_graph.rs | 2 +- .../docs/user/generated_config.adoc | 10 - .../rust-analyzer/editors/code/package.json | 10 - 16 files changed, 156 insertions(+), 212 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs index 839ffa2614b05..8f053f4df949f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -43,7 +43,7 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O acc.add( AssistId("bind_unused_param", AssistKind::QuickFix), - &format!("Bind as `let _ = {ident_pat};`"), + format!("Bind as `let _ = {ident_pat};`"), param.syntax().text_range(), |builder| { let line_index = ctx.db().line_index(ctx.file_id().into()); diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 8737f2246be59..a77e9569701a7 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -16,7 +16,7 @@ use ide_db::{ use itertools::Itertools; use proc_macro_api::{MacroDylib, ProcMacroServer}; use project_model::{ - CargoConfig, ManifestPath, PackageRoot, ProjectManifest, ProjectWorkspace, ProjectWorkspaceKind, + CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace, ProjectWorkspaceKind, }; use span::Span; use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath}; @@ -247,7 +247,7 @@ impl ProjectFolders { let mut file_set_roots: Vec = vec![]; let mut entries = vec![]; - if let Some(manifest) = ws.manifest().map(ManifestPath::as_ref) { + if let Some(manifest) = ws.manifest().map(|it| it.to_path_buf()) { file_set_roots.push(VfsPath::from(manifest.to_owned())); entries.push(manifest.to_owned()); } diff --git a/src/tools/rust-analyzer/crates/paths/src/lib.rs b/src/tools/rust-analyzer/crates/paths/src/lib.rs index 885f071889eca..51c7959094c12 100644 --- a/src/tools/rust-analyzer/crates/paths/src/lib.rs +++ b/src/tools/rust-analyzer/crates/paths/src/lib.rs @@ -151,6 +151,10 @@ impl AbsPathBuf { pub fn push>(&mut self, suffix: P) { self.0.push(suffix) } + + pub fn join(&self, path: impl AsRef) -> Self { + Self(self.0.join(path)) + } } impl fmt::Display for AbsPathBuf { diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index 839d8e569fe3f..65b2f54b48967 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -78,7 +78,7 @@ impl WorkspaceBuildScripts { cmd.args(&config.extra_args); cmd.arg("--manifest-path"); - cmd.arg(manifest_path.as_ref()); + cmd.arg(manifest_path); if let Some(target_dir) = &config.target_dir { cmd.arg("--target-dir").arg(target_dir); diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 632ba1cacf213..38eeedec6217f 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -85,8 +85,6 @@ pub struct CargoConfig { pub target: Option, /// Sysroot loading behavior pub sysroot: Option, - /// Whether to invoke `cargo metadata` on the sysroot crate. - pub sysroot_query_metadata: bool, pub sysroot_src: Option, /// rustc private crate source pub rustc_source: Option, @@ -259,6 +257,7 @@ impl CargoWorkspace { current_dir: &AbsPath, config: &CargoConfig, sysroot: &Sysroot, + locked: bool, progress: &dyn Fn(String), ) -> anyhow::Result { let targets = find_list_of_build_targets(config, cargo_toml, sysroot); @@ -312,6 +311,9 @@ impl CargoWorkspace { // opt into it themselves. other_options.push("-Zscript".to_owned()); } + if locked { + other_options.push("--locked".to_owned()); + } meta.other_options(other_options); // FIXME: Fetching metadata is a slow process, as it might require diff --git a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs index 2331c0c36c3ad..a8be5dff7b690 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs @@ -64,6 +64,18 @@ impl AsRef for ManifestPath { } } +impl AsRef for ManifestPath { + fn as_ref(&self) -> &std::path::Path { + self.file.as_ref() + } +} + +impl AsRef for ManifestPath { + fn as_ref(&self) -> &std::ffi::OsStr { + self.file.as_ref() + } +} + impl Borrow for ManifestPath { fn borrow(&self) -> &AbsPath { self.file.borrow() diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index 1eeec4cedeba1..bcd51930f0dad 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -123,32 +123,27 @@ impl Sysroot { // FIXME: Expose a builder api as loading the sysroot got way too modular and complicated. impl Sysroot { /// Attempts to discover the toolchain's sysroot from the given `dir`. - pub fn discover( - dir: &AbsPath, - extra_env: &FxHashMap, - metadata: bool, - ) -> Sysroot { + pub fn discover(dir: &AbsPath, extra_env: &FxHashMap) -> Sysroot { let sysroot_dir = discover_sysroot_dir(dir, extra_env); let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| { discover_sysroot_src_dir_or_add_component(sysroot_dir, dir, extra_env) }); - Sysroot::load_core_check(Some(sysroot_dir), sysroot_src_dir, metadata) + Sysroot::load_core_check(Some(sysroot_dir), sysroot_src_dir) } pub fn discover_with_src_override( current_dir: &AbsPath, extra_env: &FxHashMap, sysroot_src_dir: AbsPathBuf, - metadata: bool, ) -> Sysroot { let sysroot_dir = discover_sysroot_dir(current_dir, extra_env); - Sysroot::load_core_check(Some(sysroot_dir), Some(Ok(sysroot_src_dir)), metadata) + Sysroot::load_core_check(Some(sysroot_dir), Some(Ok(sysroot_src_dir))) } - pub fn discover_sysroot_src_dir(sysroot_dir: AbsPathBuf, metadata: bool) -> Sysroot { + pub fn discover_sysroot_src_dir(sysroot_dir: AbsPathBuf) -> Sysroot { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir) .ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}")); - Sysroot::load_core_check(Some(Ok(sysroot_dir)), Some(sysroot_src_dir), metadata) + Sysroot::load_core_check(Some(Ok(sysroot_dir)), Some(sysroot_src_dir)) } pub fn discover_rustc_src(&self) -> Option { @@ -191,20 +186,15 @@ impl Sysroot { }) } - pub fn load( - sysroot_dir: Option, - sysroot_src_dir: Option, - metadata: bool, - ) -> Sysroot { - Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok), metadata) + pub fn load(sysroot_dir: Option, sysroot_src_dir: Option) -> Sysroot { + Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok)) } fn load_core_check( sysroot_dir: Option>, sysroot_src_dir: Option>, - metadata: bool, ) -> Sysroot { - let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir, metadata); + let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir); if sysroot.error.is_none() { if let Some(src_root) = &sysroot.src_root { let has_core = match &sysroot.mode { @@ -230,7 +220,6 @@ impl Sysroot { fn load_( sysroot_dir: Option>, sysroot_src_dir: Option>, - metadata: bool, ) -> Sysroot { let sysroot_dir = match sysroot_dir { Some(Ok(sysroot_dir)) => Some(sysroot_dir), @@ -263,119 +252,16 @@ impl Sysroot { } } }; - if metadata { - let sysroot: Option<_> = (|| { - let sysroot_cargo_toml = ManifestPath::try_from( - AbsPathBuf::try_from(&*format!("{sysroot_src_dir}/sysroot/Cargo.toml")).ok()?, - ) - .ok()?; - let current_dir = - AbsPathBuf::try_from(&*format!("{sysroot_src_dir}/sysroot")).ok()?; - - let mut cargo_config = CargoConfig::default(); - // the sysroot uses `public-dependency`, so we make cargo think it's a nightly - cargo_config.extra_env.insert( - "__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS".to_owned(), - "nightly".to_owned(), - ); - - let res = CargoWorkspace::fetch_metadata( - &sysroot_cargo_toml, - ¤t_dir, - &cargo_config, - &Sysroot::empty(), - &|_| (), - ) - .map_err(|e| { - tracing::error!( - "failed to load sysroot `{sysroot_src_dir}/sysroot/Cargo.toml`: {}", - e - ); - e - }); - if let Err(e) = - std::fs::remove_file(format!("{sysroot_src_dir}/sysroot/Cargo.lock")) - { - tracing::error!( - "failed to remove sysroot `{sysroot_src_dir}/sysroot/Cargo.lock`: {}", - e - ) - } - let mut res = res.ok()?; - - // Patch out `rustc-std-workspace-*` crates to point to the real crates. - // This is done prior to `CrateGraph` construction to avoid having duplicate `std` targets. - - let mut fake_core = None; - let mut fake_alloc = None; - let mut fake_std = None; - let mut real_core = None; - let mut real_alloc = None; - let mut real_std = None; - res.packages.iter().enumerate().for_each(|(idx, package)| { - match package.name.strip_prefix("rustc-std-workspace-") { - Some("core") => fake_core = Some((idx, package.id.clone())), - Some("alloc") => fake_alloc = Some((idx, package.id.clone())), - Some("std") => fake_std = Some((idx, package.id.clone())), - Some(_) => { - tracing::warn!("unknown rustc-std-workspace-* crate: {}", package.name) - } - None => match &*package.name { - "core" => real_core = Some(package.id.clone()), - "alloc" => real_alloc = Some(package.id.clone()), - "std" => real_std = Some(package.id.clone()), - _ => (), - }, - } - }); - - let patches = - [fake_core.zip(real_core), fake_alloc.zip(real_alloc), fake_std.zip(real_std)] - .into_iter() - .flatten(); - - let resolve = res.resolve.as_mut().expect("metadata executed with deps"); - let mut remove_nodes = vec![]; - for (idx, node) in resolve.nodes.iter_mut().enumerate() { - // Replace them in the dependency list - node.deps.iter_mut().for_each(|dep| { - if let Some((_, real)) = - patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg) - { - dep.pkg = real; - } - }); - if patches.clone().any(|((_, fake), _)| fake == node.id) { - remove_nodes.push(idx); - } - } - // Remove the fake ones from the resolve data - remove_nodes.into_iter().rev().for_each(|r| { - resolve.nodes.remove(r); - }); - // Remove the fake ones from the packages - patches.map(|((r, _), _)| r).sorted().rev().for_each(|r| { - res.packages.remove(r); - }); - - res.workspace_members = res - .packages - .iter() - .filter(|&package| RELEVANT_SYSROOT_CRATES.contains(&&*package.name)) - .map(|package| package.id.clone()) - .collect(); - let cargo_workspace = CargoWorkspace::new(res, sysroot_cargo_toml); - Some(Sysroot { - root: sysroot_dir.clone(), - src_root: Some(sysroot_src_dir.clone()), - mode: SysrootMode::Workspace(cargo_workspace), - error: None, - }) - })(); - if let Some(sysroot) = sysroot { + let library_manifest = ManifestPath::try_from(sysroot_src_dir.join("Cargo.toml")).unwrap(); + if fs::metadata(&library_manifest).is_ok() { + if let Some(sysroot) = + Self::load_library_via_cargo(library_manifest, &sysroot_dir, &sysroot_src_dir) + { return sysroot; } } + tracing::debug!("Stitching sysroot library: {sysroot_src_dir}"); + let mut stitched = Stitched { crates: Arena::default() }; for path in SYSROOT_CRATES.trim().lines() { @@ -384,7 +270,7 @@ impl Sysroot { .into_iter() .map(|it| sysroot_src_dir.join(it)) .filter_map(|it| ManifestPath::try_from(it).ok()) - .find(|it| fs::metadata(it.as_ref()).is_ok()); + .find(|it| fs::metadata(it).is_ok()); if let Some(root) = root { stitched.crates.alloc(SysrootCrateData { @@ -425,6 +311,92 @@ impl Sysroot { error: None, } } + + fn load_library_via_cargo( + library_manifest: ManifestPath, + sysroot_dir: &Option, + sysroot_src_dir: &AbsPathBuf, + ) -> Option { + tracing::debug!("Loading library metadata: {library_manifest}"); + let mut cargo_config = CargoConfig::default(); + // the sysroot uses `public-dependency`, so we make cargo think it's a nightly + cargo_config.extra_env.insert( + "__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS".to_owned(), + "nightly".to_owned(), + ); + + let mut res = match CargoWorkspace::fetch_metadata( + &library_manifest, + sysroot_src_dir, + &cargo_config, + &Sysroot::empty(), + // Make sure we never attempt to write to the sysroot + true, + &|_| (), + ) { + Ok(it) => it, + Err(e) => { + tracing::error!("`cargo metadata` failed on `{library_manifest}` : {e}"); + return None; + } + }; + + // Patch out `rustc-std-workspace-*` crates to point to the real crates. + // This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing. + let patches = { + let mut fake_core = None; + let mut fake_alloc = None; + let mut fake_std = None; + let mut real_core = None; + let mut real_alloc = None; + let mut real_std = None; + res.packages.iter().enumerate().for_each(|(idx, package)| { + match package.name.strip_prefix("rustc-std-workspace-") { + Some("core") => fake_core = Some((idx, package.id.clone())), + Some("alloc") => fake_alloc = Some((idx, package.id.clone())), + Some("std") => fake_std = Some((idx, package.id.clone())), + Some(_) => { + tracing::warn!("unknown rustc-std-workspace-* crate: {}", package.name) + } + None => match &*package.name { + "core" => real_core = Some(package.id.clone()), + "alloc" => real_alloc = Some(package.id.clone()), + "std" => real_std = Some(package.id.clone()), + _ => (), + }, + } + }); + + [fake_core.zip(real_core), fake_alloc.zip(real_alloc), fake_std.zip(real_std)] + .into_iter() + .flatten() + }; + + let resolve = res.resolve.as_mut().expect("metadata executed with deps"); + resolve.nodes.retain_mut(|node| { + // Replace `rustc-std-workspace` crate with the actual one in the dependency list + node.deps.iter_mut().for_each(|dep| { + let real_pkg = patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg); + if let Some((_, real)) = real_pkg { + dep.pkg = real; + } + }); + // Remove this node if it's a fake one + !patches.clone().any(|((_, fake), _)| fake == node.id) + }); + // Remove the fake ones from the package list + patches.map(|((idx, _), _)| idx).sorted().rev().for_each(|idx| { + res.packages.remove(idx); + }); + + let cargo_workspace = CargoWorkspace::new(res, library_manifest); + Some(Sysroot { + root: sysroot_dir.clone(), + src_root: Some(sysroot_src_dir.clone()), + mode: SysrootMode::Workspace(cargo_workspace), + error: None, + }) + } } fn discover_sysroot_dir( @@ -485,7 +457,7 @@ fn get_rustc_src(sysroot_path: &AbsPath) -> Option { let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml"); let rustc_src = ManifestPath::try_from(rustc_src).ok()?; tracing::debug!("checking for rustc source code: {rustc_src}"); - if fs::metadata(rustc_src.as_ref()).is_ok() { + if fs::metadata(&rustc_src).is_ok() { Some(rustc_src) } else { None @@ -531,5 +503,3 @@ test"; const PROC_MACRO_DEPS: &str = " std core"; - -const RELEVANT_SYSROOT_CRATES: &[&str] = &["core", "alloc", "std", "test", "proc_macro"]; diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index 8f5457bf99af9..e3bc81e1963d9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -12,8 +12,8 @@ use span::FileId; use triomphe::Arc; use crate::{ - workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides, ManifestPath, ProjectJson, - ProjectJsonData, ProjectWorkspace, Sysroot, WorkspaceBuildScripts, + sysroot::SysrootMode, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides, + ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, WorkspaceBuildScripts, }; fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) { @@ -146,7 +146,7 @@ fn get_fake_sysroot() -> Sysroot { // fake sysroot, so we give them both the same path: let sysroot_dir = AbsPathBuf::assert(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); - Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), false) + Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir)) } fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { @@ -274,10 +274,9 @@ fn crate_graph_dedup() { } #[test] +// FIXME Remove the ignore +#[ignore = "requires nightly until the sysroot ships a cargo workspace for library on stable"] fn smoke_test_real_sysroot_cargo() { - if std::env::var("SYSROOT_CARGO_METADATA").is_err() { - return; - } let file_map = &mut FxHashMap::::default(); let meta: Metadata = get_test_json_file("hello-world-metadata.json"); let manifest_path = @@ -286,8 +285,8 @@ fn smoke_test_real_sysroot_cargo() { let sysroot = Sysroot::discover( AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))), &Default::default(), - true, ); + assert!(matches!(sysroot.mode(), SysrootMode::Workspace(_))); let project_workspace = ProjectWorkspace { kind: ProjectWorkspaceKind::Cargo { diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 31d1c77fd07c4..c90543d0eadfa 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -194,7 +194,7 @@ impl ProjectWorkspace { ) -> anyhow::Result { let res = match manifest { ProjectManifest::ProjectJson(project_json) => { - let file = fs::read_to_string(project_json.as_ref()) + let file = fs::read_to_string(project_json) .with_context(|| format!("Failed to read json file {project_json}"))?; let data = serde_json::from_str(&file) .with_context(|| format!("Failed to deserialize json file {project_json}"))?; @@ -213,28 +213,22 @@ impl ProjectWorkspace { } ProjectManifest::CargoToml(cargo_toml) => { let sysroot = match (&config.sysroot, &config.sysroot_src) { - (Some(RustLibSource::Discover), None) => Sysroot::discover( - cargo_toml.parent(), - &config.extra_env, - config.sysroot_query_metadata, - ), + (Some(RustLibSource::Discover), None) => { + Sysroot::discover(cargo_toml.parent(), &config.extra_env) + } (Some(RustLibSource::Discover), Some(sysroot_src)) => { Sysroot::discover_with_src_override( cargo_toml.parent(), &config.extra_env, sysroot_src.clone(), - config.sysroot_query_metadata, ) } - (Some(RustLibSource::Path(path)), None) => Sysroot::discover_sysroot_src_dir( - path.clone(), - config.sysroot_query_metadata, - ), - (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load( - Some(sysroot.clone()), - Some(sysroot_src.clone()), - config.sysroot_query_metadata, - ), + (Some(RustLibSource::Path(path)), None) => { + Sysroot::discover_sysroot_src_dir(path.clone()) + } + (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => { + Sysroot::load(Some(sysroot.clone()), Some(sysroot_src.clone())) + } (None, _) => Sysroot::empty(), }; tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot"); @@ -260,6 +254,7 @@ impl ProjectWorkspace { ..config.clone() }, &sysroot, + false, progress, ) { Ok(meta) => { @@ -312,7 +307,8 @@ impl ProjectWorkspace { cargo_toml.parent(), config, &sysroot, - progress, + false, + progress, ) .with_context(|| { format!( @@ -350,8 +346,7 @@ impl ProjectWorkspace { extra_env: &FxHashMap, cfg_overrides: &CfgOverrides, ) -> ProjectWorkspace { - let sysroot = - Sysroot::load(project_json.sysroot.clone(), project_json.sysroot_src.clone(), false); + let sysroot = Sysroot::load(project_json.sysroot.clone(), project_json.sysroot_src.clone()); let cfg_config = RustcCfgConfig::Rustc(&sysroot); let data_layout_config = RustcDataLayoutConfig::Rustc(&sysroot); let toolchain = match get_toolchain_version( @@ -386,12 +381,8 @@ impl ProjectWorkspace { ) -> anyhow::Result { let dir = detached_file.parent(); let sysroot = match &config.sysroot { - Some(RustLibSource::Path(path)) => { - Sysroot::discover_sysroot_src_dir(path.clone(), config.sysroot_query_metadata) - } - Some(RustLibSource::Discover) => { - Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata) - } + Some(RustLibSource::Path(path)) => Sysroot::discover_sysroot_src_dir(path.clone()), + Some(RustLibSource::Discover) => Sysroot::discover(dir, &config.extra_env), None => Sysroot::empty(), }; @@ -412,14 +403,14 @@ impl ProjectWorkspace { ); let cargo_script = - CargoWorkspace::fetch_metadata(detached_file, dir, config, &sysroot, &|_| ()).ok().map( - |ws| { + CargoWorkspace::fetch_metadata(detached_file, dir, config, &sysroot, false, &|_| ()) + .ok() + .map(|ws| { ( CargoWorkspace::new(ws, detached_file.clone()), WorkspaceBuildScripts::default(), ) - }, - ); + }); let cargo_config_extra_env = cargo_config_env(detached_file, &config.extra_env, &sysroot); Ok(ProjectWorkspace { @@ -651,7 +642,7 @@ impl ProjectWorkspace { ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => { iter::once(PackageRoot { is_local: true, - include: vec![file.as_ref().to_owned()], + include: vec![file.to_path_buf()], exclude: Vec::new(), }) .chain(cargo_script.iter().flat_map(|(cargo, build_scripts)| { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 380105d2c2141..41b59ab0d0f75 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -64,7 +64,6 @@ impl flags::AnalysisStats { true => None, false => Some(RustLibSource::Discover), }, - sysroot_query_metadata: self.query_sysroot_metadata, ..Default::default() }; let no_progress = &|_| (); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index b3b8ab9a40499..2a3e74c680b92 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -71,9 +71,6 @@ xflags::xflags! { optional --with-deps /// Don't load sysroot crates (`std`, `core` & friends). optional --no-sysroot - /// Run cargo metadata on the sysroot to analyze its third-party dependencies. - /// Requires --no-sysroot to not be set. - optional --query-sysroot-metadata /// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis. optional --disable-build-scripts @@ -214,7 +211,6 @@ pub struct AnalysisStats { pub only: Option, pub with_deps: bool, pub no_sysroot: bool, - pub query_sysroot_metadata: bool, pub disable_build_scripts: bool, pub disable_proc_macros: bool, pub skip_lowering: bool, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 31565878d845c..2ae80f5b8c5c9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -69,7 +69,7 @@ impl Tester { let cargo_config = CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; - let sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env, false); + let sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env); let data_layout = target_data_layout::get( RustcDataLayoutConfig::Rustc(&sysroot), None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index b9b8cfdfc9eab..5eb53ddc59de8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -143,13 +143,6 @@ config_data! { /// /// This option does not take effect until rust-analyzer is restarted. cargo_sysroot: Option = Some("discover".to_owned()), - /// Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze - /// third-party dependencies of the standard libraries. - /// - /// This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer - /// will attempt to clean up afterwards, but nevertheless requires the location to be - /// writable to. - cargo_sysrootQueryMetadata: bool = false, /// Relative path to the sysroot library sources. If left unset, this will default to /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. /// @@ -1839,7 +1832,6 @@ impl Config { }); let sysroot_src = self.cargo_sysrootSrc(None).as_ref().map(|sysroot| self.root_path.join(sysroot)); - let sysroot_query_metadata = self.cargo_sysrootQueryMetadata(None); CargoConfig { all_targets: *self.cargo_allTargets(None), @@ -1852,7 +1844,6 @@ impl Config { }, target: self.cargo_target(None).clone(), sysroot, - sysroot_query_metadata: *sysroot_query_metadata, sysroot_src, rustc_source, cfg_overrides: project_model::CfgOverrides { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs index 66481d3d7f5e1..b8a82fd6a72ee 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs @@ -69,7 +69,7 @@ fn get_fake_sysroot() -> Sysroot { // fake sysroot, so we give them both the same path: let sysroot_dir = AbsPathBuf::assert_utf8(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); - Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), false) + Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir)) } #[test] diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index ac95767ea5b79..2be338dd4d1d9 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -144,16 +144,6 @@ Unsetting this disables sysroot loading. This option does not take effect until rust-analyzer is restarted. -- -[[rust-analyzer.cargo.sysrootQueryMetadata]]rust-analyzer.cargo.sysrootQueryMetadata (default: `false`):: -+ --- -Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze -third-party dependencies of the standard libraries. - -This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer -will attempt to clean up afterwards, but nevertheless requires the location to be -writable to. --- [[rust-analyzer.cargo.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`):: + -- diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 4b594129a3662..abb40ac35ede3 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -826,16 +826,6 @@ } } }, - { - "title": "cargo", - "properties": { - "rust-analyzer.cargo.sysrootQueryMetadata": { - "markdownDescription": "Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze\nthird-party dependencies of the standard libraries.\n\nThis will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer\nwill attempt to clean up afterwards, but nevertheless requires the location to be\nwritable to.", - "default": false, - "type": "boolean" - } - } - }, { "title": "cargo", "properties": { From 2e7db9c69ffeb58cb512e84a88ab5dced33abcf2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 3 Aug 2024 18:00:36 +0200 Subject: [PATCH 019/178] Simplify FileDelegate --- .../crates/base-db/src/change.rs | 4 +- .../rust-analyzer/crates/base-db/src/lib.rs | 50 +++++++++---------- .../hir-def/src/nameres/tests/incremental.rs | 2 +- .../crates/hir-def/src/test_db.rs | 5 +- .../crates/hir-expand/src/change.rs | 4 +- .../crates/hir-expand/src/lib.rs | 7 ++- .../crates/hir-ty/src/test_db.rs | 5 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 2 +- .../crates/hir-ty/src/tests/incremental.rs | 2 +- .../crates/ide-assists/src/tests.rs | 2 +- .../ide-completion/src/completions/mod_.rs | 2 +- .../crates/ide-completion/src/tests.rs | 2 +- .../crates/ide-db/src/helpers.rs | 2 +- .../rust-analyzer/crates/ide-db/src/lib.rs | 5 +- .../crates/ide-db/src/prime_caches.rs | 2 +- .../rust-analyzer/crates/ide-db/src/search.rs | 2 +- .../crates/ide-db/src/symbol_index.rs | 4 +- .../src/handlers/unlinked_file.rs | 4 +- .../crates/ide-diagnostics/src/tests.rs | 2 +- .../rust-analyzer/crates/ide-ssr/src/lib.rs | 6 +-- .../crates/ide-ssr/src/search.rs | 2 +- .../rust-analyzer/crates/ide-ssr/src/tests.rs | 2 +- .../crates/ide/src/goto_definition.rs | 2 +- .../crates/ide/src/hover/tests.rs | 2 +- .../crates/ide/src/interpret_function.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 4 +- .../crates/ide/src/static_index.rs | 2 +- .../crates/ide/src/view_crate_graph.rs | 2 +- .../crates/project-model/src/workspace.rs | 11 ++-- .../rust-analyzer/src/cli/analysis_stats.rs | 2 +- .../rust-analyzer/src/cli/diagnostics.rs | 2 +- .../crates/rust-analyzer/src/cli/run_tests.rs | 2 +- .../crates/rust-analyzer/src/cli/ssr.rs | 5 +- .../crates/rust-analyzer/src/global_state.rs | 2 +- .../crates/rust-analyzer/src/main_loop.rs | 2 +- .../crates/test-fixture/src/lib.rs | 6 +-- 36 files changed, 78 insertions(+), 86 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs index 0fd54e1211cd9..a9d91d64ceb6d 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/change.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs @@ -7,7 +7,7 @@ use salsa::Durability; use triomphe::Arc; use vfs::FileId; -use crate::{CrateGraph, SourceDatabaseExt, SourceDatabaseExt2, SourceRoot, SourceRootId}; +use crate::{CrateGraph, SourceDatabaseFileInputExt, SourceRoot, SourceRootDatabase, SourceRootId}; /// Encapsulate a bunch of raw `.set` calls on the database. #[derive(Default)] @@ -50,7 +50,7 @@ impl FileChange { self.crate_graph = Some(graph); } - pub fn apply(self, db: &mut dyn SourceDatabaseExt) { + pub fn apply(self, db: &mut dyn SourceRootDatabase) { let _p = tracing::info_span!("FileChange::apply").entered(); if let Some(roots) = self.roots { for (idx, root) in roots.into_iter().enumerate() { diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index f319f98537b60..35b51f22c5619 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -47,8 +47,6 @@ pub const DEFAULT_PARSE_LRU_CAP: u16 = 128; pub const DEFAULT_BORROWCK_LRU_CAP: u16 = 2024; pub trait FileLoader { - /// Text of the file. - fn file_text(&self, file_id: FileId) -> Arc; fn resolve_path(&self, path: AnchoredPath<'_>) -> Option; /// Crates whose root's source root is the same as the source root of `file_id` fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>; @@ -58,6 +56,13 @@ pub trait FileLoader { /// model. Everything else in rust-analyzer is derived from these queries. #[salsa::query_group(SourceDatabaseStorage)] pub trait SourceDatabase: FileLoader + std::fmt::Debug { + #[salsa::input] + fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>; + + /// Text of the file. + #[salsa::lru] + fn file_text(&self, file_id: FileId) -> Arc; + /// Parses the file into the syntax tree. #[salsa::lru] fn parse(&self, file_id: EditionedFileId) -> Parse; @@ -99,16 +104,18 @@ fn parse_errors(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Option Arc { + let bytes = db.compressed_file_text(file_id); + let bytes = + lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail"); + let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8"); + Arc::from(text) +} + /// We don't want to give HIR knowledge of source roots, hence we extract these /// methods into a separate DB. -#[salsa::query_group(SourceDatabaseExtStorage)] -pub trait SourceDatabaseExt: SourceDatabase { - #[salsa::input] - fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>; - - #[salsa::lru] - fn file_text(&self, file_id: FileId) -> Arc; - +#[salsa::query_group(SourceRootDatabaseStorage)] +pub trait SourceRootDatabase: SourceDatabase { /// Path to a file, relative to the root of its source root. /// Source root of the file. #[salsa::input] @@ -121,15 +128,7 @@ pub trait SourceDatabaseExt: SourceDatabase { fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>; } -fn file_text(db: &dyn SourceDatabaseExt, file_id: FileId) -> Arc { - let bytes = db.compressed_file_text(file_id); - let bytes = - lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail"); - let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8"); - Arc::from(text) -} - -pub trait SourceDatabaseExt2 { +pub trait SourceDatabaseFileInputExt { fn set_file_text(&mut self, file_id: FileId, text: &str) { self.set_file_text_with_durability(file_id, text, Durability::LOW); } @@ -142,7 +141,7 @@ pub trait SourceDatabaseExt2 { ); } -impl SourceDatabaseExt2 for Db { +impl SourceDatabaseFileInputExt for Db { fn set_file_text_with_durability( &mut self, file_id: FileId, @@ -159,7 +158,7 @@ impl SourceDatabaseExt2 for Db { } } -fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[CrateId]> { +fn source_root_crates(db: &dyn SourceRootDatabase, id: SourceRootId) -> Arc<[CrateId]> { let graph = db.crate_graph(); let mut crates = graph .iter() @@ -173,13 +172,12 @@ fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[Crat crates.into_iter().collect() } -/// Silly workaround for cyclic deps between the traits +// FIXME: Would be nice to get rid of this somehow +/// Silly workaround for cyclic deps due to the SourceRootDatabase and SourceDatabase split +/// regarding FileLoader pub struct FileLoaderDelegate(pub T); -impl FileLoader for FileLoaderDelegate<&'_ T> { - fn file_text(&self, file_id: FileId) -> Arc { - SourceDatabaseExt::file_text(self.0, file_id) - } +impl FileLoader for FileLoaderDelegate<&'_ T> { fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { // FIXME: this *somehow* should be platform agnostic... let source_root = self.0.file_source_root(path.anchor); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index e82af318501ac..d319831867c0e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -1,4 +1,4 @@ -use base_db::{SourceDatabase, SourceDatabaseExt2 as _}; +use base_db::{SourceDatabase, SourceDatabaseFileInputExt as _}; use test_fixture::WithFixture; use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId}; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index f44472eae5b0f..df9dec69d464e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -19,7 +19,7 @@ use crate::{ }; #[salsa::database( - base_db::SourceDatabaseExtStorage, + base_db::SourceRootDatabaseStorage, base_db::SourceDatabaseStorage, hir_expand::db::ExpandDatabaseStorage, crate::db::InternDatabaseStorage, @@ -69,9 +69,6 @@ impl fmt::Debug for TestDB { impl panic::RefUnwindSafe for TestDB {} impl FileLoader for TestDB { - fn file_text(&self, file_id: FileId) -> Arc { - FileLoaderDelegate(self).file_text(file_id) - } fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { FileLoaderDelegate(self).resolve_path(path) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs index 1a3dd0e7ddbd6..8b3f69db0277c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs @@ -1,7 +1,7 @@ //! Defines a unit of change that can applied to the database to get the next //! state. Changes are transactional. use base_db::{ - salsa::Durability, CrateGraph, CrateId, FileChange, SourceDatabaseExt, SourceRoot, + salsa::Durability, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootDatabase, TargetLayoutLoadResult, Version, }; use la_arena::RawIdx; @@ -23,7 +23,7 @@ impl ChangeWithProcMacros { Self::default() } - pub fn apply(self, db: &mut (impl ExpandDatabase + SourceDatabaseExt)) { + pub fn apply(self, db: &mut (impl ExpandDatabase + SourceRootDatabase)) { self.source_change.apply(db); if let Some(proc_macros) = self.proc_macros { db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 18da77d6caa9b..2bea9026265a6 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -176,7 +176,12 @@ impl ExpandErrorKind { &ExpandErrorKind::MissingProcMacroExpander(def_crate) => { match db.proc_macros().get_error_for_crate(def_crate) { Some((e, hard_err)) => (e.to_owned(), hard_err), - None => ("missing expander".to_owned(), true), + None => ( + format!( + "internal error: proc-macro map is missing error entry for crate {def_crate:?}" + ), + true, + ), } } ExpandErrorKind::MacroDefinition => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index 108ae198d50ee..0efb9c52fb647 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -15,7 +15,7 @@ use test_utils::extract_annotations; use triomphe::Arc; #[salsa::database( - base_db::SourceDatabaseExtStorage, + base_db::SourceRootDatabaseStorage, base_db::SourceDatabaseStorage, hir_expand::db::ExpandDatabaseStorage, hir_def::db::InternDatabaseStorage, @@ -75,9 +75,6 @@ impl salsa::ParallelDatabase for TestDB { impl panic::RefUnwindSafe for TestDB {} impl FileLoader for TestDB { - fn file_text(&self, file_id: FileId) -> Arc { - FileLoaderDelegate(self).file_text(file_id) - } fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { FileLoaderDelegate(self).resolve_path(path) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index e67124d57a2cb..19619008e3d57 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -12,7 +12,7 @@ mod traits; use std::env; -use base_db::SourceDatabaseExt2 as _; +use base_db::SourceDatabaseFileInputExt as _; use expect_test::Expect; use hir_def::{ body::{Body, BodySourceMap, SyntheticSyntax}, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index e9c62d3416954..0a24eeb1fe82d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -1,4 +1,4 @@ -use base_db::SourceDatabaseExt2 as _; +use base_db::SourceDatabaseFileInputExt as _; use test_fixture::WithFixture; use crate::{db::HirDatabase, test_db::TestDB}; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index e42be636d71de..6469957fe16b5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -3,7 +3,7 @@ mod generated; use expect_test::expect; use hir::{FileRange, Semantics}; use ide_db::{ - base_db::SourceDatabaseExt, + base_db::{SourceDatabase, SourceRootDatabase}, imports::insert_use::{ImportGranularity, InsertUseConfig}, source_change::FileSystemEdit, EditionedFileId, RootDatabase, SnippetCap, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs index 713968c1caf97..d9a10893bf52b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs @@ -4,7 +4,7 @@ use std::iter; use hir::{HirFileIdExt, Module}; use ide_db::{ - base_db::{SourceDatabaseExt, VfsPath}, + base_db::{SourceRootDatabase, VfsPath}, FxHashSet, RootDatabase, SymbolKind, }; use stdx::IsNoneOr; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index f6274cf537605..415f2afeebbea 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -23,10 +23,10 @@ mod type_pos; mod use_tree; mod visibility; +use base_db::SourceDatabase; use expect_test::Expect; use hir::PrefixKind; use ide_db::{ - base_db::FileLoader, imports::insert_use::{ImportGranularity, InsertUseConfig}, FilePosition, RootDatabase, SnippetCap, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs index f6a781907db46..e6638dde5d403 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs @@ -2,7 +2,7 @@ use std::collections::VecDeque; -use base_db::SourceDatabaseExt; +use base_db::SourceRootDatabase; use hir::{Crate, DescendPreference, ItemInNs, ModuleDef, Name, Semantics}; use span::FileId; use syntax::{ diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 4c52ba39dec41..cb1cf793d4291 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -74,7 +74,7 @@ pub type FilePosition = FilePositionWrapper; pub type FileRange = FileRangeWrapper; #[salsa::database( - base_db::SourceDatabaseExtStorage, + base_db::SourceRootDatabaseStorage, base_db::SourceDatabaseStorage, hir::db::ExpandDatabaseStorage, hir::db::DefDatabaseStorage, @@ -125,9 +125,6 @@ impl Upcast for RootDatabase { } impl FileLoader for RootDatabase { - fn file_text(&self, file_id: FileId) -> Arc { - FileLoaderDelegate(self).file_text(file_id) - } fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { FileLoaderDelegate(self).resolve_path(path) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 62104fb7dce66..bb121f4a80ae2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -11,7 +11,7 @@ use hir::db::DefDatabase; use crate::{ base_db::{ salsa::{Database, ParallelDatabase, Snapshot}, - Cancelled, CrateId, SourceDatabase, SourceDatabaseExt, + Cancelled, CrateId, SourceDatabase, SourceRootDatabase, }, FxIndexMap, RootDatabase, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 05b32e2a85487..b6d17ce67edf1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -6,7 +6,7 @@ use std::mem; -use base_db::{salsa::Database, SourceDatabase, SourceDatabaseExt}; +use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase}; use hir::{ sym, AsAssocItem, DefWithBody, DescendPreference, FileRange, HasAttrs, HasSource, HirFileIdExt, InFile, InRealFile, ModuleSource, PathResolution, Semantics, Visibility, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index c70aed4c43bb8..209b1477bace4 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -29,7 +29,7 @@ use std::{ use base_db::{ salsa::{self, ParallelDatabase}, - SourceDatabaseExt, SourceRootId, Upcast, + SourceRootDatabase, SourceRootId, Upcast, }; use fst::{raw::IndexedValue, Automaton, Streamer}; use hir::{ @@ -100,7 +100,7 @@ impl Query { } #[salsa::query_group(SymbolsDatabaseStorage)] -pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast { +pub trait SymbolsDatabase: HirDatabase + SourceRootDatabase + Upcast { /// The symbol index for a given module. These modules should only be in source roots that /// are inside local_roots. fn module_symbols(&self, module: Module) -> Arc; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 1b71a3a3e69b3..b229ba8084db2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -4,7 +4,7 @@ use std::iter; use hir::{db::DefDatabase, DefMap, InFile, ModuleSource}; use ide_db::{ - base_db::{FileLoader, SourceDatabaseExt}, + base_db::{FileLoader, SourceDatabase, SourceRootDatabase}, source_change::SourceChange, FileId, FileRange, LineIndexDatabase, }; @@ -47,7 +47,7 @@ pub(crate) fn unlinked_file( // // Only show this diagnostic on the first three characters of // the file, to avoid overwhelming the user during startup. - range = FileLoader::file_text(ctx.sema.db, file_id) + range = SourceDatabase::file_text(ctx.sema.db, file_id) .char_indices() .take(3) .last() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index e56fca1e50082..e943503522f9f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -1,7 +1,7 @@ #![allow(clippy::print_stderr)] use ide_db::{ - assists::AssistResolveStrategy, base_db::SourceDatabaseExt, LineIndexDatabase, RootDatabase, + assists::AssistResolveStrategy, base_db::SourceDatabase, LineIndexDatabase, RootDatabase, }; use itertools::Itertools; use stdx::trim_indent; diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs index e62ef60433649..54236ea8bc4e9 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs @@ -84,7 +84,7 @@ pub use crate::{errors::SsrError, from_comment::ssr_from_comment, matching::Matc use crate::{errors::bail, matching::MatchFailureReason}; use hir::{FileRange, Semantics}; -use ide_db::{EditionedFileId, FileId, FxHashMap, RootDatabase}; +use ide_db::{base_db::SourceDatabase, EditionedFileId, FileId, FxHashMap, RootDatabase}; use resolving::ResolvedRule; use syntax::{ast, AstNode, SyntaxNode, TextRange}; use text_edit::TextEdit; @@ -141,7 +141,7 @@ impl<'db> MatchFinder<'db> { /// Constructs an instance using the start of the first file in `db` as the lookup context. pub fn at_first_file(db: &'db ide_db::RootDatabase) -> Result, SsrError> { - use ide_db::base_db::SourceDatabaseExt; + use ide_db::base_db::SourceRootDatabase; use ide_db::symbol_index::SymbolsDatabase; if let Some(first_file_id) = db.local_roots().iter().next().and_then(|root| db.source_root(*root).iter().next()) @@ -172,7 +172,6 @@ impl<'db> MatchFinder<'db> { /// Finds matches for all added rules and returns edits for all found matches. pub fn edits(&self) -> FxHashMap { - use ide_db::base_db::SourceDatabaseExt; let mut matches_by_file = FxHashMap::default(); for m in self.matches().matches { matches_by_file @@ -228,7 +227,6 @@ impl<'db> MatchFinder<'db> { file_id: EditionedFileId, snippet: &str, ) -> Vec { - use ide_db::base_db::SourceDatabaseExt; let file = self.sema.parse(file_id); let mut res = Vec::new(); let file_text = self.sema.db.file_text(file_id.into()); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs index 832386685d72c..241de10b44dc3 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs @@ -156,7 +156,7 @@ impl MatchFinder<'_> { fn search_files_do(&self, mut callback: impl FnMut(FileId)) { if self.restrict_ranges.is_empty() { // Unrestricted search. - use ide_db::base_db::SourceDatabaseExt; + use ide_db::base_db::SourceRootDatabase; use ide_db::symbol_index::SymbolsDatabase; for &root in self.sema.db.local_roots().iter() { let sr = self.sema.db.source_root(root); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index 4477a268b2912..42930889d757a 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -1,7 +1,7 @@ use expect_test::{expect, Expect}; use hir::{FilePosition, FileRange}; use ide_db::{ - base_db::{salsa::Durability, SourceDatabaseExt}, + base_db::{salsa::Durability, SourceDatabase}, EditionedFileId, FxHashSet, }; use test_utils::RangeOrOffset; diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index d0701a45b1019..8a8bc07945f1a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -10,7 +10,7 @@ use hir::{ Semantics, }; use ide_db::{ - base_db::{AnchoredPath, FileLoader}, + base_db::{AnchoredPath, FileLoader, SourceDatabase}, defs::{Definition, IdentClass}, helpers::pick_best_token, RootDatabase, SymbolKind, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 3257305184e91..ecfcf82e00c3d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1,5 +1,5 @@ use expect_test::{expect, Expect}; -use ide_db::{base_db::FileLoader, FileRange}; +use ide_db::{base_db::SourceDatabase, FileRange}; use syntax::TextRange; use crate::{ diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs index aeb3c8c1ee5ac..ff1317d135c9d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs +++ b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs @@ -1,5 +1,5 @@ use hir::Semantics; -use ide_db::{base_db::SourceDatabaseExt, FilePosition, LineIndexDatabase, RootDatabase}; +use ide_db::{base_db::SourceRootDatabase, FilePosition, LineIndexDatabase, RootDatabase}; use std::{fmt::Write, time::Instant}; use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange}; diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 8cb81a9cc452c..4624f6bfcf1a6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -65,7 +65,7 @@ use hir::{sym, ChangeWithProcMacros}; use ide_db::{ base_db::{ salsa::{self, ParallelDatabase}, - CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, SourceDatabaseExt, VfsPath, + CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, SourceRootDatabase, VfsPath, }, prime_caches, symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase, }; @@ -286,7 +286,7 @@ impl Analysis { /// Gets the text of the source file. pub fn file_text(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| SourceDatabaseExt::file_text(db, file_id)) + self.with_db(|db| SourceDatabase::file_text(db, file_id)) } /// Gets the syntax tree of the file. diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index cd9b7ae2f6209..f688adff28d9f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -3,7 +3,7 @@ use hir::{db::HirDatabase, Crate, HirFileIdExt, Module, Semantics}; use ide_db::{ - base_db::SourceDatabaseExt, defs::Definition, documentation::Documentation, + base_db::SourceRootDatabase, defs::Definition, documentation::Documentation, famous_defs::FamousDefs, helpers::get_definition, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, }; diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs index 727012112ebd4..9ff099f479e7b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs @@ -1,6 +1,6 @@ use dot::{Id, LabelText}; use ide_db::{ - base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt}, + base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceRootDatabase}, FxHashSet, RootDatabase, }; use triomphe::Arc; diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index c90543d0eadfa..9156c8da33a95 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -1349,12 +1349,13 @@ fn add_target_crate_root( ); if let TargetKind::Lib { is_proc_macro: true } = kind { let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) { - Some(it) => it.cloned().map(|path| Ok((cargo_name.to_owned(), path))), - None => Some(Err("proc-macro crate is missing its build data".to_owned())), + Some(it) => match it { + Some(path) => Ok((cargo_name.to_owned(), path.clone())), + None => Err("proc-macro crate build data is missing dylib path".to_owned()), + }, + None => Err("proc-macro crate is missing its build data".to_owned()), }; - if let Some(proc_macro) = proc_macro { - proc_macros.insert(crate_id, proc_macro); - } + proc_macros.insert(crate_id, proc_macro); } crate_id diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 41b59ab0d0f75..cf5508bb7f1f5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -23,7 +23,7 @@ use ide::{ use ide_db::{ base_db::{ salsa::{self, debug::DebugQueryTable, ParallelDatabase}, - SourceDatabase, SourceDatabaseExt, + SourceDatabase, SourceRootDatabase, }, EditionedFileId, LineIndexDatabase, SnippetCap, }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index 4ddeb4ab1b0c3..5ec657a227aee 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -6,7 +6,7 @@ use rustc_hash::FxHashSet; use hir::{db::HirDatabase, Crate, HirFileIdExt, Module}; use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity}; -use ide_db::{base_db::SourceDatabaseExt, LineIndexDatabase}; +use ide_db::{base_db::SourceRootDatabase, LineIndexDatabase}; use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; use crate::cli::flags; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs index 10cb2d5ad6e3d..157ef43dd0a27 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs @@ -2,7 +2,7 @@ use hir::{Crate, Module}; use hir_ty::db::HirDatabase; -use ide_db::{base_db::SourceDatabaseExt, LineIndexDatabase}; +use ide_db::{base_db::SourceRootDatabase, LineIndexDatabase}; use profile::StopWatch; use project_model::{CargoConfig, RustLibSource}; use syntax::TextRange; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs index 7f24fa2835e93..3caa487988744 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs @@ -1,7 +1,7 @@ //! Applies structured search replace rules from the command line. use anyhow::Context; -use ide_db::EditionedFileId; +use ide_db::{base_db::SourceDatabase, EditionedFileId}; use ide_ssr::MatchFinder; use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; use project_model::{CargoConfig, RustLibSource}; @@ -10,7 +10,6 @@ use crate::cli::flags; impl flags::Ssr { pub fn run(self) -> anyhow::Result<()> { - use ide_db::base_db::SourceDatabaseExt; let cargo_config = CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; let load_cargo_config = LoadCargoConfig { @@ -46,7 +45,7 @@ impl flags::Search { /// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful /// for much else. pub fn run(self) -> anyhow::Result<()> { - use ide_db::base_db::SourceDatabaseExt; + use ide_db::base_db::SourceRootDatabase; use ide_db::symbol_index::SymbolsDatabase; let cargo_config = CargoConfig::default(); let load_cargo_config = LoadCargoConfig { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index f1dde104fce4a..2ed56a7fd6c4a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -9,7 +9,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::{project_json, FlycheckHandle}; use hir::ChangeWithProcMacros; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; -use ide_db::base_db::{CrateId, ProcMacroPaths, SourceDatabaseExt}; +use ide_db::base_db::{CrateId, ProcMacroPaths, SourceDatabase, SourceRootDatabase}; use itertools::Itertools; use load_cargo::SourceRootConfig; use lsp_types::{SemanticTokens, Url}; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 9c820749ece6d..f8989b8479b09 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -10,7 +10,7 @@ use std::{ use always_assert::always; use crossbeam_channel::{select, Receiver}; use flycheck::project_json; -use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath}; +use ide_db::base_db::{SourceDatabase, SourceRootDatabase, VfsPath}; use lsp_server::{Connection, Notification, Request}; use lsp_types::{notification::Notification as _, TextDocumentIdentifier}; use stdx::thread::ThreadIntent; diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index e1f40f5da0116..0c90127a5f454 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -3,7 +3,7 @@ use std::{iter, mem, str::FromStr, sync}; use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileChange, - FileSet, LangCrateOrigin, SourceDatabaseExt, SourceRoot, Version, VfsPath, + FileSet, LangCrateOrigin, SourceRootDatabase, SourceRoot, Version, VfsPath, }; use cfg::CfgOptions; use hir_expand::{ @@ -26,7 +26,7 @@ use tt::{Leaf, Subtree, TokenTree}; pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0); -pub trait WithFixture: Default + ExpandDatabase + SourceDatabaseExt + 'static { +pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static { #[track_caller] fn with_single_file(ra_fixture: &str) -> (Self, EditionedFileId) { let fixture = ChangeFixture::parse(ra_fixture); @@ -101,7 +101,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabaseExt + 'static { } } -impl WithFixture for DB {} +impl WithFixture for DB {} pub struct ChangeFixture { pub file_position: Option<(EditionedFileId, RangeOrOffset)>, From 90803b16c19d10e106225f72a05b7d57885e04d7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 Aug 2024 10:43:01 +0200 Subject: [PATCH 020/178] Split out syntax-bridge into a separate crate --- src/tools/rust-analyzer/Cargo.lock | 24 +++- src/tools/rust-analyzer/Cargo.toml | 1 + src/tools/rust-analyzer/crates/cfg/Cargo.toml | 2 +- .../rust-analyzer/crates/cfg/src/tests.rs | 5 +- .../rust-analyzer/crates/hir-def/Cargo.toml | 2 +- .../rust-analyzer/crates/hir-def/src/attr.rs | 2 +- .../hir-def/src/macro_expansion_tests/mbe.rs | 1 - .../hir-def/src/macro_expansion_tests/mod.rs | 4 +- .../crates/hir-expand/Cargo.toml | 1 + .../crates/hir-expand/src/attrs.rs | 6 +- .../hir-expand/src/builtin/derive_macro.rs | 16 +-- .../crates/hir-expand/src/builtin/fn_macro.rs | 38 ++++--- .../rust-analyzer/crates/hir-expand/src/db.rs | 25 +++-- .../crates/hir-expand/src/declarative.rs | 8 +- .../crates/hir-expand/src/eager.rs | 4 +- .../crates/hir-expand/src/fixup.rs | 15 +-- .../crates/hir-expand/src/span_map.rs | 4 +- src/tools/rust-analyzer/crates/mbe/Cargo.toml | 1 + .../rust-analyzer/crates/mbe/src/benchmark.rs | 6 +- src/tools/rust-analyzer/crates/mbe/src/lib.rs | 17 +-- .../crates/proc-macro-srv/Cargo.toml | 9 +- .../src/server_impl/token_stream.rs | 9 +- .../crates/proc-macro-srv/src/tests/utils.rs | 5 +- .../crates/rust-analyzer/Cargo.toml | 2 +- .../crates/rust-analyzer/src/target_spec.rs | 5 +- .../rust-analyzer/crates/span/src/hygiene.rs | 4 + .../crates/syntax-bridge/Cargo.toml | 33 ++++++ .../src/lib.rs} | 53 +-------- .../crates/syntax-bridge/src/tests.rs | 104 ++++++++++++++++++ .../src/to_parser_input.rs | 2 +- 30 files changed, 268 insertions(+), 140 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml rename src/tools/rust-analyzer/crates/{mbe/src/syntax_bridge.rs => syntax-bridge/src/lib.rs} (96%) create mode 100644 src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs rename src/tools/rust-analyzer/crates/{mbe => syntax-bridge}/src/to_parser_input.rs (98%) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index b98a1195d8bb7..99acda7c0ba6f 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -148,10 +148,10 @@ dependencies = [ "derive_arbitrary", "expect-test", "intern", - "mbe", "oorandom", "rustc-hash", "syntax", + "syntax-bridge", "tt", ] @@ -554,6 +554,7 @@ dependencies = [ "span", "stdx", "syntax", + "syntax-bridge", "test-fixture", "test-utils", "tracing", @@ -582,6 +583,7 @@ dependencies = [ "span", "stdx", "syntax", + "syntax-bridge", "tracing", "triomphe", "tt", @@ -1056,6 +1058,7 @@ dependencies = [ "span", "stdx", "syntax", + "syntax-bridge", "test-utils", "tracing", "tt", @@ -1350,7 +1353,6 @@ dependencies = [ "expect-test", "intern", "libloading", - "mbe", "memmap2", "object 0.33.0", "paths", @@ -1360,6 +1362,7 @@ dependencies = [ "snap", "span", "stdx", + "syntax-bridge", "tt", ] @@ -1665,7 +1668,6 @@ dependencies = [ "load-cargo", "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", - "mbe", "memchr", "mimalloc", "nohash-hasher", @@ -1685,6 +1687,7 @@ dependencies = [ "serde_json", "stdx", "syntax", + "syntax-bridge", "test-fixture", "test-utils", "tikv-jemallocator", @@ -1967,6 +1970,21 @@ dependencies = [ "triomphe", ] +[[package]] +name = "syntax-bridge" +version = "0.0.0" +dependencies = [ + "intern", + "parser", + "rustc-hash", + "span", + "stdx", + "syntax", + "test-utils", + "tracing", + "tt", +] + [[package]] name = "test-fixture" version = "0.0.0" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index c2f601a91bc7a..d56d941dbff98 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -77,6 +77,7 @@ salsa = { path = "./crates/salsa", version = "0.0.0" } span = { path = "./crates/span", version = "0.0.0" } stdx = { path = "./crates/stdx", version = "0.0.0" } syntax = { path = "./crates/syntax", version = "0.0.0" } +syntax-bridge = { path = "./crates/syntax-bridge", version = "0.0.0" } text-edit = { path = "./crates/text-edit", version = "0.0.0" } toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index faf93f62c6afb..0076b674768e8 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -28,7 +28,7 @@ arbitrary = "1.3.2" derive_arbitrary = "1.3.2" # local deps -mbe.workspace = true +syntax-bridge.workspace = true syntax.workspace = true [lints] diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs index 597023a792bb7..6d87d83ad9300 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs @@ -1,8 +1,11 @@ use arbitrary::{Arbitrary, Unstructured}; use expect_test::{expect, Expect}; use intern::Symbol; -use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; use syntax::{ast, AstNode, Edition}; +use syntax_bridge::{ + dummy_test_span_utils::{DummyTestSpanMap, DUMMY}, + syntax_node_to_token_tree, DocCommentDesugarMode, +}; use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 8ac2d00313778..7c58dd221de6b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -52,7 +52,7 @@ expect-test.workspace = true # local deps test-utils.workspace = true test-fixture.workspace = true - +syntax-bridge.workspace = true [features] in-rust-tree = ["hir-expand/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index ba88495e14d30..198dc93f6b141 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -657,9 +657,9 @@ mod tests { use triomphe::Arc; use hir_expand::span_map::{RealSpanMap, SpanMap}; - use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode}; use span::FileId; use syntax::{ast, AstNode, TextRange}; + use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode}; use crate::attr::{DocAtom, DocExpr}; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index 64b37d2d06508..fc1460870ceaf 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1201,7 +1201,6 @@ macro_rules! m { #[test] fn test_meta_doc_comments() { - cov_mark::check!(test_meta_doc_comments); check( r#" macro_rules! m { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index d34f0afc3ef8f..b430e2cefb35d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -317,9 +317,9 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { _: Span, _: Span, ) -> Result { - let (parse, _) = ::mbe::token_tree_to_syntax_node( + let (parse, _) = syntax_bridge::token_tree_to_syntax_node( subtree, - ::mbe::TopEntryPoint::MacroItems, + syntax_bridge::TopEntryPoint::MacroItems, span::Edition::CURRENT, ); if parse.errors().is_empty() { diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index ca05618aecd90..ff4b468f6c9c4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -33,6 +33,7 @@ mbe.workspace = true limit.workspace = true span.workspace = true parser.workspace = true +syntax-bridge.workspace = true [dev-dependencies] expect-test = "1.4.0" diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 777e415418662..79cfeb4cf1844 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -6,14 +6,12 @@ use cfg::CfgExpr; use either::Either; use intern::{sym, Interned, Symbol}; -use mbe::{ - desugar_doc_comment_text, syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode, - Punct, -}; +use mbe::{DelimiterKind, Punct}; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; use syntax::unescape; use syntax::{ast, match_ast, AstNode, AstToken, SyntaxNode}; +use syntax_bridge::{desugar_doc_comment_text, syntax_node_to_token_tree, DocCommentDesugarMode}; use triomphe::ThinArc; use crate::name::Name; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index f560d3bfd1db3..7d3e8deaf08ee 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -2,10 +2,10 @@ use intern::sym; use itertools::izip; -use mbe::DocCommentDesugarMode; use rustc_hash::FxHashSet; use span::{MacroCallId, Span}; use stdx::never; +use syntax_bridge::DocCommentDesugarMode; use tracing::debug; use crate::{ @@ -209,9 +209,9 @@ struct BasicAdtInfo { } fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result { - let (parsed, tm) = &mbe::token_tree_to_syntax_node( + let (parsed, tm) = &syntax_bridge::token_tree_to_syntax_node( tt, - mbe::TopEntryPoint::MacroItems, + syntax_bridge::TopEntryPoint::MacroItems, parser::Edition::CURRENT_FIXME, ); let macro_items = ast::MacroItems::cast(parsed.syntax_node()) @@ -268,7 +268,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result { param_type_set.insert(it.as_name()); - mbe::syntax_node_to_token_tree( + syntax_bridge::syntax_node_to_token_tree( it.syntax(), tm, call_site, @@ -282,7 +282,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result it.type_bound_list().map(|it| { - mbe::syntax_node_to_token_tree( + syntax_bridge::syntax_node_to_token_tree( it.syntax(), tm, call_site, @@ -295,7 +295,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result Result Result ExpandResult { let call_site_span = span_with_call_site_ctxt(db, span, id); - let args = parse_exprs_with_sep(tt, ',', call_site_span, Edition::CURRENT_FIXME); + + let mut iter = ::tt::iter::TtIter::new(tt); + + let cond = expect_fragment( + &mut iter, + parser::PrefixEntryPoint::Expr, + db.crate_graph()[id.lookup(db).krate].edition, + tt::DelimSpan { open: tt.delimiter.open, close: tt.delimiter.close }, + ); + _ = iter.expect_char(','); + let rest = iter.as_slice(); + let dollar_crate = dollar_crate(span); - let expanded = match &*args { - [cond, panic_args @ ..] => { - let comma = tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(call_site_span), - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { - char: ',', - spacing: tt::Spacing::Alone, - span: call_site_span, - }))]), - }; - let cond = cond.clone(); - let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma); + let expanded = match cond.value { + Some(cond) => { + let panic_args = rest.iter().cloned(); let mac = if use_panic_2021(db, span) { quote! {call_site_span => #dollar_crate::panic::panic_2021!(##panic_args) } } else { @@ -253,10 +256,13 @@ fn assert_expand( } }} } - [] => quote! {call_site_span =>{}}, + None => quote! {call_site_span =>{}}, }; - ExpandResult::ok(expanded) + match cond.err { + Some(err) => ExpandResult::new(expanded, err.into()), + None => ExpandResult::ok(expanded), + } } fn file_expand( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 01a35660a904b..584f9631e3445 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -3,10 +3,11 @@ use base_db::{salsa, CrateId, SourceDatabase}; use either::Either; use limit::Limit; -use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, MatchedArmIndex}; +use mbe::MatchedArmIndex; use rustc_hash::FxHashSet; use span::{AstIdMap, EditionedFileId, Span, SyntaxContextData, SyntaxContextId}; use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T}; +use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode}; use triomphe::Arc; use crate::{ @@ -165,7 +166,7 @@ pub fn expand_speculative( // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match loc.kind { MacroCallKind::FnLike { .. } => ( - mbe::syntax_node_to_token_tree( + syntax_bridge::syntax_node_to_token_tree( speculative_args, span_map, span, @@ -178,7 +179,7 @@ pub fn expand_speculative( SyntaxFixupUndoInfo::NONE, ), MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => ( - mbe::syntax_node_to_token_tree( + syntax_bridge::syntax_node_to_token_tree( speculative_args, span_map, span, @@ -213,7 +214,7 @@ pub fn expand_speculative( fixups.remove.extend(censor_cfg); ( - mbe::syntax_node_to_token_tree_modified( + syntax_bridge::syntax_node_to_token_tree_modified( speculative_args, span_map, fixups.append, @@ -459,7 +460,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { return dummy_tt(kind); } - let mut tt = mbe::syntax_node_to_token_tree( + let mut tt = syntax_bridge::syntax_node_to_token_tree( tt.syntax(), map.as_ref(), span, @@ -515,7 +516,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { fixups.remove.extend(censor_cfg); ( - mbe::syntax_node_to_token_tree_modified( + syntax_bridge::syntax_node_to_token_tree_modified( syntax, map, fixups.append, @@ -720,13 +721,13 @@ fn token_tree_to_syntax_node( edition: parser::Edition, ) -> (Parse, ExpansionSpanMap) { let entry_point = match expand_to { - ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts, - ExpandTo::Items => mbe::TopEntryPoint::MacroItems, - ExpandTo::Pattern => mbe::TopEntryPoint::Pattern, - ExpandTo::Type => mbe::TopEntryPoint::Type, - ExpandTo::Expr => mbe::TopEntryPoint::Expr, + ExpandTo::Statements => syntax_bridge::TopEntryPoint::MacroStmts, + ExpandTo::Items => syntax_bridge::TopEntryPoint::MacroItems, + ExpandTo::Pattern => syntax_bridge::TopEntryPoint::Pattern, + ExpandTo::Type => syntax_bridge::TopEntryPoint::Type, + ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr, }; - mbe::token_tree_to_syntax_node(tt, entry_point, edition) + syntax_bridge::token_tree_to_syntax_node(tt, entry_point, edition) } fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 48851af3fd160..b1a6eed2fbc71 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -2,10 +2,10 @@ use base_db::CrateId; use intern::sym; -use mbe::DocCommentDesugarMode; use span::{Edition, MacroCallId, Span, SyntaxContextId}; use stdx::TupleExt; use syntax::{ast, AstNode}; +use syntax_bridge::DocCommentDesugarMode; use triomphe::Arc; use crate::{ @@ -112,7 +112,7 @@ impl DeclarativeMacroExpander { ast::Macro::MacroRules(macro_rules) => ( match macro_rules.token_tree() { Some(arg) => { - let tt = mbe::syntax_node_to_token_tree( + let tt = syntax_bridge::syntax_node_to_token_tree( arg.syntax(), map.as_ref(), map.span_for_range( @@ -135,14 +135,14 @@ impl DeclarativeMacroExpander { let span = map.span_for_range(macro_def.macro_token().unwrap().text_range()); let args = macro_def.args().map(|args| { - mbe::syntax_node_to_token_tree( + syntax_bridge::syntax_node_to_token_tree( args.syntax(), map.as_ref(), span, DocCommentDesugarMode::Mbe, ) }); - let body = mbe::syntax_node_to_token_tree( + let body = syntax_bridge::syntax_node_to_token_tree( body.syntax(), map.as_ref(), span, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 5385b44532b05..3528b2dde73e7 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -19,9 +19,9 @@ //! //! See the full discussion : use base_db::CrateId; -use mbe::DocCommentDesugarMode; use span::SyntaxContextId; use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent}; +use syntax_bridge::DocCommentDesugarMode; use triomphe::Arc; use crate::{ @@ -82,7 +82,7 @@ pub fn expand_eager_macro_input( return ExpandResult { value: None, err }; }; - let mut subtree = mbe::syntax_node_to_token_tree( + let mut subtree = syntax_bridge::syntax_node_to_token_tree( &expanded_eager_input, arg_map, span, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 71579d2f87f4b..b6d5828da9641 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -2,7 +2,6 @@ //! fix up syntax errors in the code we're passing to them. use intern::sym; -use mbe::DocCommentDesugarMode; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; use span::{ @@ -14,6 +13,7 @@ use syntax::{ ast::{self, AstNode, HasLoopBody}, match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize, }; +use syntax_bridge::DocCommentDesugarMode; use triomphe::Arc; use tt::Spacing; @@ -76,7 +76,8 @@ pub(crate) fn fixup_syntax( if can_handle_error(&node) && has_error_to_handle(&node) { remove.insert(node.clone().into()); // the node contains an error node, we have to completely replace it by something valid - let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site, mode); + let original_tree = + syntax_bridge::syntax_node_to_token_tree(&node, span_map, call_site, mode); let idx = original.len() as u32; original.push(original_tree); let span = span_map.span_for_range(node_range); @@ -434,9 +435,9 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { #[cfg(test)] mod tests { use expect_test::{expect, Expect}; - use mbe::DocCommentDesugarMode; use span::{Edition, EditionedFileId, FileId}; use syntax::TextRange; + use syntax_bridge::DocCommentDesugarMode; use triomphe::Arc; use crate::{ @@ -483,7 +484,7 @@ mod tests { span_map.span_for_range(TextRange::empty(0.into())), DocCommentDesugarMode::Mbe, ); - let mut tt = mbe::syntax_node_to_token_tree_modified( + let mut tt = syntax_bridge::syntax_node_to_token_tree_modified( &parsed.syntax_node(), span_map.as_ref(), fixups.append, @@ -498,9 +499,9 @@ mod tests { expect.assert_eq(&actual); // the fixed-up tree should be syntactically valid - let (parse, _) = mbe::token_tree_to_syntax_node( + let (parse, _) = syntax_bridge::token_tree_to_syntax_node( &tt, - ::mbe::TopEntryPoint::MacroItems, + syntax_bridge::TopEntryPoint::MacroItems, parser::Edition::CURRENT, ); assert!( @@ -513,7 +514,7 @@ mod tests { // the fixed-up + reversed version should be equivalent to the original input // modulo token IDs and `Punct`s' spacing. - let original_as_tt = mbe::syntax_node_to_token_tree( + let original_as_tt = syntax_bridge::syntax_node_to_token_tree( &parsed.syntax_node(), span_map.as_ref(), span_map.span_for_range(TextRange::empty(0.into())), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs index 3be88ee9daef5..740c27b89cea1 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs @@ -28,13 +28,13 @@ pub enum SpanMapRef<'a> { RealSpanMap(&'a RealSpanMap), } -impl mbe::SpanMapper for SpanMap { +impl syntax_bridge::SpanMapper for SpanMap { fn span_for(&self, range: TextRange) -> Span { self.span_for_range(range) } } -impl mbe::SpanMapper for SpanMapRef<'_> { +impl syntax_bridge::SpanMapper for SpanMapRef<'_> { fn span_for(&self, range: TextRange) -> Span { self.span_for_range(range) } diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index 57834623e84fa..e441c0ec3d742 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -25,6 +25,7 @@ tt.workspace = true stdx.workspace = true span.workspace = true intern.workspace = true +syntax-bridge.workspace = true [dev-dependencies] test-utils.workspace = true diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index b6db4d2e76c40..43604eb232dc9 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -7,11 +7,15 @@ use syntax::{ ast::{self, HasName}, AstNode, }; +use syntax_bridge::{ + dummy_test_span_utils::{DummyTestSpanMap, DUMMY}, + syntax_node_to_token_tree, DocCommentDesugarMode, +}; use test_utils::{bench, bench_fixture, skip_slow_tests}; use crate::{ parser::{MetaVarKind, Op, RepeatKind, Separator}, - syntax_node_to_token_tree, DeclarativeMacro, DocCommentDesugarMode, DummyTestSpanMap, DUMMY, + DeclarativeMacro, }; #[test] diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 568490d57345f..88785537c7d20 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -8,13 +8,12 @@ mod expander; mod parser; -mod syntax_bridge; -mod to_parser_input; #[cfg(test)] mod benchmark; use span::{Edition, Span, SyntaxContextId}; +use syntax_bridge::to_parser_input; use tt::iter::TtIter; use tt::DelimSpan; @@ -23,18 +22,8 @@ use std::sync::Arc; use crate::parser::{MetaTemplate, MetaVarKind, Op}; -// FIXME: we probably should re-think `token_tree_to_syntax_node` interfaces -pub use ::parser::TopEntryPoint; pub use tt::{Delimiter, DelimiterKind, Punct}; -pub use crate::syntax_bridge::{ - desugar_doc_comment_text, parse_exprs_with_sep, parse_to_token_tree, - parse_to_token_tree_static_span, syntax_node_to_token_tree, syntax_node_to_token_tree_modified, - token_tree_to_syntax_node, DocCommentDesugarMode, SpanMapper, -}; - -pub use crate::syntax_bridge::dummy_test_span_utils::*; - #[derive(Debug, PartialEq, Eq, Clone)] pub enum ParseError { UnexpectedToken(Box), @@ -361,7 +350,7 @@ impl From> for ValueResult { } } -fn expect_fragment( +pub fn expect_fragment( tt_iter: &mut TtIter<'_, Span>, entry_point: ::parser::PrefixEntryPoint, edition: ::parser::Edition, @@ -369,7 +358,7 @@ fn expect_fragment( ) -> ExpandResult>> { use ::parser; let buffer = tt::buffer::TokenBuffer::from_tokens(tt_iter.as_slice()); - let parser_input = to_parser_input::to_parser_input(edition, &buffer); + let parser_input = to_parser_input(edition, &buffer); let tree_traversal = entry_point.parse(&parser_input, edition); let mut cursor = buffer.begin(); let mut error = false; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 673b5bd78a8e9..b3221443bdad4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -19,23 +19,24 @@ snap.workspace = true stdx.workspace = true tt.workspace = true -mbe.workspace = true +syntax-bridge.workspace = true paths.workspace = true base-db.workspace = true span.workspace = true proc-macro-api.workspace = true -ra-ap-rustc_lexer.workspace = true intern.workspace = true +ra-ap-rustc_lexer.workspace = true + [dev-dependencies] -expect-test = "1.4.0" +expect-test.workspace = true # used as proc macro test targets proc-macro-test.path = "./proc-macro-test" [features] sysroot-abi = [] -in-rust-tree = ["mbe/in-rust-tree", "tt/in-rust-tree","sysroot-abi"] +in-rust-tree = ["syntax-bridge/in-rust-tree", "tt/in-rust-tree", "sysroot-abi"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs index cdf93fa4251d5..4d8d496418bf8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs @@ -126,9 +126,12 @@ pub(super) mod token_stream { /// change these errors into `LexError`s later. impl TokenStream { pub(crate) fn from_str(src: &str, call_site: S) -> Result, String> { - let subtree = - mbe::parse_to_token_tree_static_span(span::Edition::CURRENT_FIXME, call_site, src) - .ok_or("lexing error")?; + let subtree = syntax_bridge::parse_to_token_tree_static_span( + span::Edition::CURRENT_FIXME, + call_site, + src, + ) + .ok_or("lexing error")?; Ok(TokenStream::with_subtree(subtree)) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 70eff51cadea8..d63e04ae0765c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -9,7 +9,8 @@ use crate::{dylib, proc_macro_test_dylib_path, EnvSnapshot, ProcMacroSrv}; fn parse_string(call_site: TokenId, src: &str) -> crate::server_impl::TokenStream { crate::server_impl::TokenStream::with_subtree( - mbe::parse_to_token_tree_static_span(span::Edition::CURRENT, call_site, src).unwrap(), + syntax_bridge::parse_to_token_tree_static_span(span::Edition::CURRENT, call_site, src) + .unwrap(), ) } @@ -19,7 +20,7 @@ fn parse_string_spanned( src: &str, ) -> crate::server_impl::TokenStream { crate::server_impl::TokenStream::with_subtree( - mbe::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src).unwrap(), + syntax_bridge::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src).unwrap(), ) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index bc1b13a649700..c07f9ee506a1f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -82,7 +82,7 @@ xshell.workspace = true test-utils.workspace = true test-fixture.workspace = true -mbe.workspace = true +syntax-bridge.workspace = true [features] jemalloc = ["jemallocator", "profile/jemalloc"] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index 67e1bad5281d0..965fd415e9967 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -263,11 +263,14 @@ mod tests { use super::*; use ide::Edition; - use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; use syntax::{ ast::{self, AstNode}, SmolStr, }; + use syntax_bridge::{ + dummy_test_span_utils::{DummyTestSpanMap, DUMMY}, + syntax_node_to_token_tree, DocCommentDesugarMode, + }; fn check(cfg: &str, expected_features: &[&str]) { let cfg_expr = { diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index e8c558355c965..874480c59fbf3 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -79,6 +79,10 @@ impl SyntaxContextId { #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct SyntaxContextData { /// Invariant: Only [`SyntaxContextId::ROOT`] has a [`None`] outer expansion. + // FIXME: The None case needs to encode the context crate id. We can encode that as the MSB of + // MacroCallId is reserved anyways so we can do bit tagging here just fine. + // The bigger issue is that that will cause interning to now create completely separate chains + // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent. pub outer_expn: Option, pub outer_transparency: Transparency, pub parent: SyntaxContextId, diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml new file mode 100644 index 0000000000000..b4f59ae216d7c --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "syntax-bridge" +version = "0.0.0" +description = "TBD" + +authors.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[lib] +doctest = false + +[dependencies] +rustc-hash.workspace = true +tracing.workspace = true + +# local deps +syntax.workspace = true +parser.workspace = true +tt.workspace = true +stdx.workspace = true +span.workspace = true +intern.workspace = true + +[dev-dependencies] +test-utils.workspace = true + +[features] +in-rust-tree = ["parser/in-rust-tree", "tt/in-rust-tree", "syntax/in-rust-tree"] + +[lints] +workspace = true diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs similarity index 96% rename from src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs rename to src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs index a29efdd4ef77e..b0afd245c52e3 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs @@ -14,11 +14,13 @@ use syntax::{ }; use tt::{ buffer::{Cursor, TokenBuffer}, - iter::TtIter, token_to_literal, }; -use crate::to_parser_input::to_parser_input; +mod to_parser_input; +pub use to_parser_input::to_parser_input; +// FIXME: we probably should re-think `token_tree_to_syntax_node` interfaces +pub use ::parser::TopEntryPoint; #[cfg(test)] mod tests; @@ -43,7 +45,7 @@ impl> SpanMapper for &SM { } /// Dummy things for testing where spans don't matter. -pub(crate) mod dummy_test_span_utils { +pub mod dummy_test_span_utils { use span::{Span, SyntaxContextId}; @@ -211,50 +213,6 @@ where Some(convert_tokens(&mut conv)) } -/// Split token tree with separate expr: $($e:expr)SEP* -pub fn parse_exprs_with_sep( - tt: &tt::Subtree, - sep: char, - span: span::Span, - edition: Edition, -) -> Vec> { - if tt.token_trees.is_empty() { - return Vec::new(); - } - - let mut iter = TtIter::new(tt); - let mut res = Vec::new(); - - while iter.peek_n(0).is_some() { - let expanded = crate::expect_fragment( - &mut iter, - parser::PrefixEntryPoint::Expr, - edition, - tt::DelimSpan { open: tt.delimiter.open, close: tt.delimiter.close }, - ); - - res.push(match expanded.value { - None => break, - Some(tt) => tt.subtree_or_wrap(tt::DelimSpan { open: span, close: span }), - }); - - let mut fork = iter.clone(); - if fork.expect_char(sep).is_err() { - break; - } - iter = fork; - } - - if iter.peek_n(0).is_some() { - res.push(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: iter.cloned().collect(), - }); - } - - res -} - fn convert_tokens(conv: &mut C) -> tt::Subtree where C: TokenConverter, @@ -479,7 +437,6 @@ fn convert_doc_comment( span: S, mode: DocCommentDesugarMode, ) -> Option>> { - cov_mark::hit!(test_meta_doc_comments); let comment = ast::Comment::cast(token.clone())?; let doc = comment.kind().doc?; diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs new file mode 100644 index 0000000000000..7b8e3f2b49c20 --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs @@ -0,0 +1,104 @@ +use rustc_hash::FxHashMap; +use span::Span; +use syntax::{ast, AstNode}; +use test_utils::extract_annotations; +use tt::{ + buffer::{TokenBuffer, TokenTreeRef}, + Leaf, Punct, Spacing, +}; + +use crate::{ + dummy_test_span_utils::{DummyTestSpanMap, DUMMY}, + syntax_node_to_token_tree, DocCommentDesugarMode, +}; + +fn check_punct_spacing(fixture: &str) { + let source_file = ast::SourceFile::parse(fixture, span::Edition::CURRENT).ok().unwrap(); + let subtree = syntax_node_to_token_tree( + source_file.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::Mbe, + ); + let mut annotations: FxHashMap<_, _> = extract_annotations(fixture) + .into_iter() + .map(|(range, annotation)| { + let spacing = match annotation.as_str() { + "Alone" => Spacing::Alone, + "Joint" => Spacing::Joint, + a => panic!("unknown annotation: {a}"), + }; + (range, spacing) + }) + .collect(); + + let buf = TokenBuffer::from_subtree(&subtree); + let mut cursor = buf.begin(); + while !cursor.eof() { + while let Some(token_tree) = cursor.token_tree() { + if let TokenTreeRef::Leaf( + Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }), + _, + ) = token_tree + { + if let Some(expected) = annotations.remove(range) { + assert_eq!(expected, *spacing); + } + } + cursor = cursor.bump_subtree(); + } + cursor = cursor.bump(); + } + + assert!(annotations.is_empty(), "unchecked annotations: {annotations:?}"); +} + +#[test] +fn punct_spacing() { + check_punct_spacing( + r#" +fn main() { + 0+0; + //^ Alone + 0+(0); + //^ Alone + 0<=0; + //^ Joint + // ^ Alone + 0<=(0); + // ^ Alone + a=0; + //^ Alone + a=(0); + //^ Alone + a+=0; + //^ Joint + // ^ Alone + a+=(0); + // ^ Alone + a&&b; + //^ Joint + // ^ Alone + a&&(b); + // ^ Alone + foo::bar; + // ^ Joint + // ^ Alone + use foo::{bar,baz,}; + // ^ Alone + // ^ Alone + // ^ Alone + struct Struct<'a> {}; + // ^ Joint + // ^ Joint + Struct::<0>; + // ^ Alone + Struct::<{0}>; + // ^ Alone + ;; + //^ Joint + // ^ Alone +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs similarity index 98% rename from src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs rename to src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs index c35b28527a09b..2c54899268834 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs @@ -8,7 +8,7 @@ use syntax::{SyntaxKind, SyntaxKind::*, T}; use tt::buffer::TokenBuffer; -pub(crate) fn to_parser_input( +pub fn to_parser_input( edition: Edition, buffer: &TokenBuffer<'_, S>, ) -> parser::Input { From cdee65f5ea9bfb20d2a0c8d44f805dd42c845938 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 3 Aug 2024 19:16:56 +0200 Subject: [PATCH 021/178] Newtype ErasedFileAstId --- src/tools/rust-analyzer/Cargo.lock | 3 +- .../rust-analyzer/crates/base-db/src/lib.rs | 2 +- .../rust-analyzer/crates/paths/Cargo.toml | 7 +--- .../crates/proc-macro-api/Cargo.toml | 5 +-- .../crates/proc-macro-api/src/msg.rs | 6 +-- .../crates/proc-macro-api/src/msg/flat.rs | 8 ++-- .../src/server_impl/rust_analyzer_span.rs | 4 +- .../crates/proc-macro-srv/src/tests/utils.rs | 4 +- .../rust-analyzer/tests/slow-tests/main.rs | 2 - .../rust-analyzer/crates/span/src/ast_id.rs | 39 +++++++++++++++---- .../rust-analyzer/crates/span/src/lib.rs | 7 ++-- .../rust-analyzer/crates/span/src/map.rs | 2 +- .../crates/test-fixture/src/lib.rs | 2 +- 13 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e4f6ad28c1ed3..b89bab1a6b94b 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1268,6 +1268,7 @@ name = "paths" version = "0.0.0" dependencies = [ "camino", + "serde", ] [[package]] @@ -1330,14 +1331,12 @@ dependencies = [ "base-db", "indexmap", "intern", - "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", "rustc-hash", "serde", "serde_json", "span", "stdx", - "text-size", "tracing", "tt", ] diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 35b51f22c5619..20ef45d0b3729 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -1,5 +1,5 @@ //! base_db defines basic database traits. The concrete DB is defined by ide. - +// FIXME: Rename this crate, base db is non descriptive mod change; mod input; diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index 59a4ad9a25587..b51892c2655c6 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -13,13 +13,10 @@ doctest = false [dependencies] camino.workspace = true -# Adding this dep sadly puts a lot of rust-analyzer crates after the -# serde-derive crate. Even though we don't activate the derive feature here, -# someone else in the crate graph certainly does! -# serde.workspace = true +serde = { workspace = true, optional = true } [features] -serde1 = ["camino/serde1"] +serde1 = ["camino/serde1", "dep:serde"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 345fb9f8ae973..3e6f80878bdc2 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -22,12 +22,11 @@ indexmap.workspace = true paths = { workspace = true, features = ["serde1"] } tt.workspace = true stdx.workspace = true -text-size.workspace = true -span.workspace = true # Ideally this crate would not depend on salsa things, but we need span information here which wraps # InternIds for the syntax context +span.workspace = true +# only here due to the `Env` newtype :/ base-db.workspace = true -la-arena.workspace = true intern.workspace = true [lints] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs index 6a99b5ed1cc84..883528558d95d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs @@ -158,9 +158,7 @@ type ProtocolWrite = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) #[cfg(test)] mod tests { use intern::{sym, Symbol}; - use la_arena::RawIdx; - use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId}; - use text_size::{TextRange, TextSize}; + use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize}; use tt::{Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, Subtree, TokenTree}; use super::*; @@ -171,7 +169,7 @@ mod tests { span::FileId::from_raw(0xe4e4e), span::Edition::CURRENT, ), - ast_id: ErasedFileAstId::from_raw(RawIdx::from(0)), + ast_id: ErasedFileAstId::from_raw(0), }; let token_trees = Box::new([ diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs index a8661f59b2887..88256e98b5887 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs @@ -38,11 +38,9 @@ use std::collections::VecDeque; use intern::Symbol; -use la_arena::RawIdx; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; -use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId}; -use text_size::TextRange; +use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange}; use crate::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA}; @@ -54,7 +52,7 @@ pub fn serialize_span_data_index_map(map: &SpanDataIndexMap) -> Vec { .flat_map(|span| { [ span.anchor.file_id.as_u32(), - span.anchor.ast_id.into_raw().into_u32(), + span.anchor.ast_id.into_raw(), span.range.start().into(), span.range.end().into(), span.ctx.into_u32(), @@ -71,7 +69,7 @@ pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap { Span { anchor: SpanAnchor { file_id: EditionedFileId::from_raw(file_id), - ast_id: ErasedFileAstId::from_raw(RawIdx::from_u32(ast_id)), + ast_id: ErasedFileAstId::from_raw(ast_id), }, range: TextRange::new(start.into(), end.into()), ctx: SyntaxContextId::from_u32(e), diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 8b9eb3beb6e88..552d99f51ba12 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -479,7 +479,7 @@ mod tests { range: TextRange::empty(TextSize::new(0)), anchor: span::SpanAnchor { file_id: EditionedFileId::current_edition(FileId::from_raw(0)), - ast_id: span::ErasedFileAstId::from_raw(0.into()), + ast_id: span::ErasedFileAstId::from_raw(0), }, ctx: SyntaxContextId::ROOT, }; @@ -515,7 +515,7 @@ mod tests { range: TextRange::empty(TextSize::new(0)), anchor: span::SpanAnchor { file_id: EditionedFileId::current_edition(FileId::from_raw(0)), - ast_id: span::ErasedFileAstId::from_raw(0.into()), + ast_id: span::ErasedFileAstId::from_raw(0), }, ctx: SyntaxContextId::ROOT, }; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 70eff51cadea8..ded985877cf42 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -69,7 +69,7 @@ fn assert_expand_impl( range: TextRange::new(0.into(), 150.into()), anchor: SpanAnchor { file_id: EditionedFileId::current_edition(FileId::from_raw(41)), - ast_id: ErasedFileAstId::from_raw(From::from(1)), + ast_id: ErasedFileAstId::from_raw(1), }, ctx: SyntaxContextId::ROOT, }; @@ -77,7 +77,7 @@ fn assert_expand_impl( range: TextRange::new(0.into(), 100.into()), anchor: SpanAnchor { file_id: EditionedFileId::current_edition(FileId::from_raw(42)), - ast_id: ErasedFileAstId::from_raw(From::from(2)), + ast_id: ErasedFileAstId::from_raw(2), }, ctx: SyntaxContextId::ROOT, }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index b1ef48377178b..c5b9fee6b1616 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -1084,7 +1084,6 @@ fn resolve_proc_macro() { let sysroot = project_model::Sysroot::discover( &AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()), &Default::default(), - false, ); let proc_macro_server_path = sysroot.discover_proc_macro_srv().unwrap(); @@ -1125,7 +1124,6 @@ edition = "2021" proc-macro = true //- /bar/src/lib.rs -extern crate proc_macro; use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; macro_rules! t { ($n:literal) => { diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs index b61baa224465f..0ebd72e1514c6 100644 --- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs +++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs @@ -18,7 +18,28 @@ use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; /// See crates\hir-expand\src\ast_id_map.rs /// This is a type erased FileAstId. -pub type ErasedFileAstId = la_arena::Idx; +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ErasedFileAstId(u32); + +impl ErasedFileAstId { + pub const fn into_raw(self) -> u32 { + self.0 + } + pub const fn from_raw(u32: u32) -> Self { + Self(u32) + } +} + +impl fmt::Display for ErasedFileAstId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::Debug for ErasedFileAstId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} /// `AstId` points to an AST node in a specific file. pub struct FileAstId { @@ -47,7 +68,7 @@ impl Hash 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()) + write!(f, "FileAstId::<{}>({})", type_name::(), self.raw) } } @@ -176,7 +197,10 @@ impl AstIdMap { let ptr = ptr.syntax_node_ptr(); let hash = hash_ptr(&ptr); match self.map.raw_entry().from_hash(hash, |&idx| self.arena[idx] == ptr) { - Some((&raw, &())) => FileAstId { raw, covariant: PhantomData }, + Some((&raw, &())) => FileAstId { + raw: ErasedFileAstId(raw.into_raw().into_u32()), + covariant: PhantomData, + }, None => panic!( "Can't find {:?} in AstIdMap:\n{:?}", ptr, @@ -186,18 +210,19 @@ impl AstIdMap { } pub fn get(&self, id: FileAstId) -> AstPtr { - AstPtr::try_from_raw(self.arena[id.raw]).unwrap() + AstPtr::try_from_raw(self.arena[Idx::from_raw(RawIdx::from_u32(id.raw.into_raw()))]) + .unwrap() } pub fn get_erased(&self, id: ErasedFileAstId) -> SyntaxNodePtr { - self.arena[id] + self.arena[Idx::from_raw(RawIdx::from_u32(id.into_raw()))] } fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId { let ptr = SyntaxNodePtr::new(item); let hash = hash_ptr(&ptr); match self.map.raw_entry().from_hash(hash, |&idx| self.arena[idx] == ptr) { - Some((&idx, &())) => idx, + Some((&idx, &())) => ErasedFileAstId(idx.into_raw().into_u32()), None => panic!( "Can't find {:?} in AstIdMap:\n{:?}", item, @@ -207,7 +232,7 @@ impl AstIdMap { } fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { - self.arena.alloc(SyntaxNodePtr::new(item)) + ErasedFileAstId(self.arena.alloc(SyntaxNodePtr::new(item)).into_raw().into_u32()) } } diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index b4e21d64f81e3..61e4c98128a57 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -21,15 +21,14 @@ pub use vfs::FileId; /// The root ast id always points to the encompassing file, using this in spans is discouraged as /// any range relative to it will be effectively absolute, ruining the entire point of anchored /// relative text ranges. -pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId = - la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(0)); +pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(0); /// FileId used as the span for syntax node fixups. Any Span containing this file id is to be /// considered fake. pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId = - // we pick the second to last for this in case we every consider making this a NonMaxU32, this + // we pick the second to last for this in case we ever consider making this a NonMaxU32, this // is required to be stable for the proc-macro-server - la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(!0 - 1)); + ErasedFileAstId::from_raw(!0 - 1); pub type Span = SpanData; diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs index 6269f4c30c7d2..c539754979d6a 100644 --- a/src/tools/rust-analyzer/crates/span/src/map.rs +++ b/src/tools/rust-analyzer/crates/span/src/map.rs @@ -119,7 +119,7 @@ impl fmt::Display for RealSpanMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "RealSpanMap({:?}):", self.file_id)?; for span in self.pairs.iter() { - writeln!(f, "{}: {}", u32::from(span.0), span.1.into_raw().into_u32())?; + writeln!(f, "{}: {}", u32::from(span.0), span.1.into_raw())?; } Ok(()) } diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 0c90127a5f454..03e85a898ab0c 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -3,7 +3,7 @@ use std::{iter, mem, str::FromStr, sync}; use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileChange, - FileSet, LangCrateOrigin, SourceRootDatabase, SourceRoot, Version, VfsPath, + FileSet, LangCrateOrigin, SourceRoot, SourceRootDatabase, Version, VfsPath, }; use cfg::CfgOptions; use hir_expand::{ From f12aca95e4badcb079c2615b55f1c85e845b22e7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 Aug 2024 13:58:01 +0200 Subject: [PATCH 022/178] Simplify --- .../crates/ide/src/inlay_hints/closing_brace.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 9b7e708848b8f..8f2777f3928d1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -65,15 +65,9 @@ pub(super) fn hints( ast::BlockExpr(block_expr) => { block = block_expr.stmt_list()?; }, - ast::LoopExpr(loop_expr) => { + ast::AnyHasLoopBody(loop_expr) => { block = loop_expr.loop_body()?.stmt_list()?; }, - ast::WhileExpr(while_expr) => { - block = while_expr.loop_body()?.stmt_list()?; - }, - ast::ForExpr(for_expr) => { - block = for_expr.loop_body()?.stmt_list()?; - }, _ => return None, } } From 3e809f8f1f80d6d7e02fb43ab2da720c7dd34873 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 5 Aug 2024 03:07:24 +0900 Subject: [PATCH 023/178] feat: Implement diagnostic for `await` outside of `async` --- .../rust-analyzer/crates/hir-def/src/body.rs | 1 + .../crates/hir-def/src/body/lower.rs | 90 +++++++++++++--- .../crates/hir/src/diagnostics.rs | 7 ++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 3 + .../src/handlers/await_outside_of_async.rs | 101 ++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 6 files changed, 187 insertions(+), 17 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index d3c134f326604..58812479ddf58 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -118,6 +118,7 @@ pub enum BodyDiagnostic { MacroError { node: InFile>, err: ExpandError }, UnresolvedMacroCall { node: InFile>, path: ModPath }, UnreachableLabel { node: InFile>, name: Name }, + AwaitOutsideOfAsync { node: InFile>, location: String }, UndeclaredLabel { node: InFile>, name: Name }, } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 9e30aff8fe9b5..abf7895829206 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -72,6 +72,7 @@ pub(super) fn lower( is_lowering_coroutine: false, label_ribs: Vec::new(), current_binding_owner: None, + awaitable_context: None, } .collect(params, body, is_async_fn) } @@ -100,6 +101,8 @@ struct ExprCollector<'a> { // resolution label_ribs: Vec, current_binding_owner: Option, + + awaitable_context: Option, } #[derive(Clone, Debug)] @@ -135,6 +138,11 @@ impl RibKind { } } +enum Awaitable { + Yes, + No(&'static str), +} + #[derive(Debug, Default)] struct BindingList { map: FxHashMap, @@ -180,6 +188,18 @@ impl ExprCollector<'_> { body: Option, is_async_fn: bool, ) -> (Body, BodySourceMap) { + self.awaitable_context.replace(if is_async_fn { + Awaitable::Yes + } else { + match self.owner { + DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"), + DefWithBodyId::StaticId(..) => Awaitable::No("static"), + DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => { + Awaitable::No("constant") + } + DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), + } + }); if let Some((param_list, mut attr_enabled)) = param_list { let mut params = vec![]; if let Some(self_param) = @@ -280,31 +300,40 @@ impl ExprCollector<'_> { } Some(ast::BlockModifier::Async(_)) => { self.with_label_rib(RibKind::Closure, |this| { - this.collect_block_(e, |id, statements, tail| Expr::Async { - id, - statements, - tail, + this.with_awaitable_block(Awaitable::Yes, |this| { + this.collect_block_(e, |id, statements, tail| Expr::Async { + id, + statements, + tail, + }) }) }) } Some(ast::BlockModifier::Const(_)) => { self.with_label_rib(RibKind::Constant, |this| { - let (result_expr_id, prev_binding_owner) = - this.initialize_binding_owner(syntax_ptr); - let inner_expr = this.collect_block(e); - let it = this.db.intern_anonymous_const(ConstBlockLoc { - parent: this.owner, - root: inner_expr, - }); - this.body.exprs[result_expr_id] = Expr::Const(it); - this.current_binding_owner = prev_binding_owner; - result_expr_id + this.with_awaitable_block(Awaitable::No("constant block"), |this| { + let (result_expr_id, prev_binding_owner) = + this.initialize_binding_owner(syntax_ptr); + let inner_expr = this.collect_block(e); + let it = this.db.intern_anonymous_const(ConstBlockLoc { + parent: this.owner, + root: inner_expr, + }); + this.body.exprs[result_expr_id] = Expr::Const(it); + this.current_binding_owner = prev_binding_owner; + result_expr_id + }) }) } // FIXME - Some(ast::BlockModifier::AsyncGen(_)) | Some(ast::BlockModifier::Gen(_)) | None => { - self.collect_block(e) + Some(ast::BlockModifier::AsyncGen(_)) => { + self.with_awaitable_block(Awaitable::Yes, |this| this.collect_block(e)) } + Some(ast::BlockModifier::Gen(_)) => self + .with_awaitable_block(Awaitable::No("non-async gen block"), |this| { + this.collect_block(e) + }), + None => self.collect_block(e), }, ast::Expr::LoopExpr(e) => { let label = e.label().map(|label| self.collect_label(label)); @@ -469,6 +498,12 @@ impl ExprCollector<'_> { } ast::Expr::AwaitExpr(e) => { let expr = self.collect_expr_opt(e.expr()); + if let Awaitable::No(location) = self.is_lowering_awaitable_block() { + self.source_map.diagnostics.push(BodyDiagnostic::AwaitOutsideOfAsync { + node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)), + location: location.to_string(), + }); + } self.alloc_expr(Expr::Await { expr }, syntax_ptr) } ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e), @@ -527,7 +562,13 @@ impl ExprCollector<'_> { let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine); let prev_try_block_label = this.current_try_block_label.take(); - let body = this.collect_expr_opt(e.body()); + let awaitable = if e.async_token().is_some() { + Awaitable::Yes + } else { + Awaitable::No("non-async closure") + }; + let body = + this.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body())); let closure_kind = if this.is_lowering_coroutine { let movability = if e.static_token().is_some() { @@ -2082,6 +2123,21 @@ impl ExprCollector<'_> { fn alloc_label_desugared(&mut self, label: Label) -> LabelId { self.body.labels.alloc(label) } + + fn is_lowering_awaitable_block(&self) -> &Awaitable { + self.awaitable_context.as_ref().unwrap_or(&Awaitable::No("unknown")) + } + + fn with_awaitable_block( + &mut self, + awaitable: Awaitable, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + let orig = self.awaitable_context.replace(awaitable); + let res = f(self); + self.awaitable_context = orig; + res + } } fn comma_follows_token(t: Option) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 4bb8c140a1f20..ffb972475f8f7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -48,6 +48,7 @@ macro_rules! diagnostics { // ] diagnostics![ + AwaitOutsideOfAsync, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, @@ -135,6 +136,12 @@ pub struct UnreachableLabel { pub name: Name, } +#[derive(Debug)] +pub struct AwaitOutsideOfAsync { + pub node: InFile>, + pub location: String, +} + #[derive(Debug, Clone, Eq, PartialEq)] pub struct UndeclaredLabel { pub node: InFile>, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 266ef2a55c520..1c5e4ce4b533c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1828,6 +1828,9 @@ impl DefWithBody { is_bang: true, } .into(), + BodyDiagnostic::AwaitOutsideOfAsync { node, location } => { + AwaitOutsideOfAsync { node: *node, location: location.clone() }.into() + } BodyDiagnostic::UnreachableLabel { node, name } => { UnreachableLabel { node: *node, name: name.clone() }.into() } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs new file mode 100644 index 0000000000000..92b6e748ca5ed --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs @@ -0,0 +1,101 @@ +use crate::{adjusted_display_range, Diagnostic, DiagnosticsContext}; + +// Diagnostic: await-outside-of-async +// +// This diagnostic is triggered if the `await` keyword is used outside of an async function or block +pub(crate) fn await_outside_of_async( + ctx: &DiagnosticsContext<'_>, + d: &hir::AwaitOutsideOfAsync, +) -> Diagnostic { + let display_range = + adjusted_display_range(ctx, d.node, &|node| Some(node.await_token()?.text_range())); + Diagnostic::new( + crate::DiagnosticCode::RustcHardError("E0728"), + format!("`await` is used inside {}, which is not an `async` context", d.location), + display_range, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn await_inside_non_async_fn() { + check_diagnostics( + r#" +async fn foo() {} + +fn bar() { + foo().await; + //^^^^^ error: `await` is used inside non-async function, which is not an `async` context +} +"#, + ); + } + + #[test] + fn await_inside_async_fn() { + check_diagnostics( + r#" +async fn foo() {} + +async fn bar() { + foo().await; +} +"#, + ); + } + + #[test] + fn await_inside_closure() { + check_diagnostics( + r#" +async fn foo() {} + +async fn bar() { + let _a = || { foo().await }; + //^^^^^ error: `await` is used inside non-async closure, which is not an `async` context +} +"#, + ); + } + + #[test] + fn await_inside_async_block() { + check_diagnostics( + r#" +async fn foo() {} + +fn bar() { + let _a = async { foo().await }; +} +"#, + ); + } + + #[test] + fn await_in_complex_context() { + check_diagnostics( + r#" +async fn foo() {} + +fn bar() { + async fn baz() { + let a = foo().await; + } + + let x = || { + let y = async { + baz().await; + let z = || { + baz().await; + //^^^^^ error: `await` is used inside non-async closure, which is not an `async` context + }; + }; + }; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 263ab74755999..7d9589dab22f9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -24,6 +24,7 @@ //! don't yet have a great pattern for how to do them properly. mod handlers { + pub(crate) mod await_outside_of_async; pub(crate) mod break_outside_of_loop; pub(crate) mod expected_function; pub(crate) mod inactive_code; @@ -348,6 +349,7 @@ pub fn diagnostics( for diag in diags { let d = match diag { + AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { Some(it) => it, From 92a07b8b9c6b8d79f9e5f9f6644fdf16d250e154 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 Aug 2024 14:59:27 +0200 Subject: [PATCH 024/178] minor: Fix metrics not running --- src/tools/rust-analyzer/xtask/src/metrics.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/metrics.rs b/src/tools/rust-analyzer/xtask/src/metrics.rs index 21001c28da60c..6555f225415f6 100644 --- a/src/tools/rust-analyzer/xtask/src/metrics.rs +++ b/src/tools/rust-analyzer/xtask/src/metrics.rs @@ -117,11 +117,7 @@ impl Metrics { ) -> anyhow::Result<()> { assert!(Path::new(path).exists(), "unable to find bench in {path}"); eprintln!("\nMeasuring analysis-stats/{name}"); - let output = cmd!( - sh, - "./target/release/rust-analyzer -q analysis-stats {path} --query-sysroot-metadata" - ) - .read()?; + let output = cmd!(sh, "./target/release/rust-analyzer -q analysis-stats {path}").read()?; for (metric, value, unit) in parse_metrics(&output) { self.report(&format!("analysis-stats/{name}/{metric}"), value, unit.into()); } From 80c8786408b0d1da972e1493300108f753bb922a Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sat, 3 Aug 2024 03:14:03 +0900 Subject: [PATCH 025/178] perf: Segregate syntax and semantic diagnostics --- .../crates/ide-diagnostics/src/lib.rs | 78 ++++++++-- .../crates/ide-diagnostics/src/tests.rs | 146 +++++++++--------- src/tools/rust-analyzer/crates/ide/src/lib.rs | 27 +++- .../rust-analyzer/src/cli/analysis_stats.rs | 2 +- .../rust-analyzer/src/cli/diagnostics.rs | 2 +- .../rust-analyzer/src/cli/rustc_tests.rs | 2 +- .../crates/rust-analyzer/src/diagnostics.rs | 95 +++++++----- .../src/integrated_benchmarks.rs | 4 +- .../crates/rust-analyzer/src/main_loop.rs | 45 ++++-- 9 files changed, 255 insertions(+), 146 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 263ab74755999..5db0c9a91ae46 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -96,6 +96,7 @@ use syntax::{ #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum DiagnosticCode { RustcHardError(&'static str), + SyntaxError, RustcLint(&'static str), Clippy(&'static str), Ra(&'static str, Severity), @@ -107,6 +108,9 @@ impl DiagnosticCode { DiagnosticCode::RustcHardError(e) => { format!("https://doc.rust-lang.org/stable/error_codes/{e}.html") } + DiagnosticCode::SyntaxError => { + String::from("https://doc.rust-lang.org/stable/reference/") + } DiagnosticCode::RustcLint(e) => { format!("https://doc.rust-lang.org/rustc/?search={e}") } @@ -125,6 +129,7 @@ impl DiagnosticCode { | DiagnosticCode::RustcLint(r) | DiagnosticCode::Clippy(r) | DiagnosticCode::Ra(r, _) => r, + DiagnosticCode::SyntaxError => "syntax-error", } } } @@ -154,7 +159,7 @@ impl Diagnostic { message, range: range.into(), severity: match code { - DiagnosticCode::RustcHardError(_) => Severity::Error, + DiagnosticCode::RustcHardError(_) | DiagnosticCode::SyntaxError => 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 @@ -297,31 +302,54 @@ impl DiagnosticsContext<'_> { } } -/// Request diagnostics for the given [`FileId`]. The produced diagnostics may point to other files +/// Request parser level diagnostics for the given [`FileId`]. +pub fn syntax_diagnostics( + db: &RootDatabase, + config: &DiagnosticsConfig, + file_id: FileId, +) -> Vec { + let _p = tracing::info_span!("syntax_diagnostics").entered(); + + if config.disabled.contains("syntax-error") { + return Vec::new(); + } + + let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + + // [#3434] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. + db.parse_errors(file_id) + .as_deref() + .into_iter() + .flatten() + .take(128) + .map(|err| { + Diagnostic::new( + DiagnosticCode::SyntaxError, + format!("Syntax Error: {err}"), + FileRange { file_id: file_id.into(), range: err.range() }, + ) + }) + .collect() +} + +/// Request semantic diagnostics for the given [`FileId`]. The produced diagnostics may point to other files /// due to macros. -pub fn diagnostics( +pub fn semantic_diagnostics( db: &RootDatabase, config: &DiagnosticsConfig, resolve: &AssistResolveStrategy, file_id: FileId, ) -> Vec { - let _p = tracing::info_span!("diagnostics").entered(); + let _p = tracing::info_span!("semantic_diagnostics").entered(); let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); 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(db.parse_errors(file_id).as_deref().into_iter().flatten().take(128).map(|err| { - Diagnostic::new( - DiagnosticCode::RustcHardError("syntax-error"), - format!("Syntax Error: {err}"), - FileRange { file_id: file_id.into(), range: err.range() }, - ) - })); - let parse_errors = res.len(); - let parse = sema.parse(file_id); // FIXME: This iterates the entire file which is a rather expensive operation. @@ -341,8 +369,11 @@ pub fn diagnostics( match module { // A bunch of parse errors in a file indicate some bigger structural parse changes in the // file, so we skip semantic diagnostics so we can show these faster. - Some(m) if parse_errors < 16 => m.diagnostics(db, &mut diags, config.style_lints), - Some(_) => (), + Some(m) => { + if !db.parse_errors(file_id).as_deref().is_some_and(|es| es.len() >= 16) { + m.diagnostics(db, &mut diags, config.style_lints); + } + } None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id.file_id()), } @@ -363,7 +394,7 @@ pub fn diagnostics( res.extend(d.errors.iter().take(16).map(|err| { { Diagnostic::new( - DiagnosticCode::RustcHardError("syntax-error"), + DiagnosticCode::SyntaxError, format!("Syntax Error in Expansion: {err}"), ctx.resolve_precise_location(&d.node.clone(), d.precise_location), ) @@ -464,6 +495,19 @@ pub fn diagnostics( res } +/// Request both syntax and semantic diagnostics for the given [`FileId`]. +pub fn full_diagnostics( + db: &RootDatabase, + config: &DiagnosticsConfig, + resolve: &AssistResolveStrategy, + file_id: FileId, +) -> Vec { + let mut res = syntax_diagnostics(db, config, file_id); + let sema = semantic_diagnostics(db, config, resolve, file_id); + res.extend(sema); + res +} + // `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros static RUSTC_LINT_GROUPS_DICT: Lazy>> = diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index e56fca1e50082..bd0f29c2570d6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -59,10 +59,14 @@ fn check_nth_fix_with_config( let after = trim_indent(ra_fixture_after); let (db, file_position) = RootDatabase::with_position(ra_fixture_before); - let diagnostic = - super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_position.file_id.into()) - .pop() - .expect("no diagnostics"); + let diagnostic = super::full_diagnostics( + &db, + &config, + &AssistResolveStrategy::All, + file_position.file_id.into(), + ) + .pop() + .expect("no diagnostics"); let fix = &diagnostic .fixes .unwrap_or_else(|| panic!("{:?} diagnostic misses fixes", diagnostic.code))[nth]; @@ -102,37 +106,39 @@ pub(crate) fn check_has_fix(ra_fixture_before: &str, ra_fixture_after: &str) { let (db, file_position) = RootDatabase::with_position(ra_fixture_before); let mut conf = DiagnosticsConfig::test_sample(); conf.expr_fill_default = ExprFillDefaultMode::Default; - let fix = - super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into()) - .into_iter() - .find(|d| { - d.fixes - .as_ref() - .and_then(|fixes| { - fixes.iter().find(|fix| { - if !fix.target.contains_inclusive(file_position.offset) { - return false; - } - let actual = { - let source_change = fix.source_change.as_ref().unwrap(); - let file_id = - *source_change.source_file_edits.keys().next().unwrap(); - let mut actual = db.file_text(file_id).to_string(); + let fix = super::full_diagnostics( + &db, + &conf, + &AssistResolveStrategy::All, + file_position.file_id.into(), + ) + .into_iter() + .find(|d| { + d.fixes + .as_ref() + .and_then(|fixes| { + fixes.iter().find(|fix| { + if !fix.target.contains_inclusive(file_position.offset) { + return false; + } + let actual = { + let source_change = fix.source_change.as_ref().unwrap(); + let file_id = *source_change.source_file_edits.keys().next().unwrap(); + let mut actual = db.file_text(file_id).to_string(); - for (edit, snippet_edit) in source_change.source_file_edits.values() - { - edit.apply(&mut actual); - if let Some(snippet_edit) = snippet_edit { - snippet_edit.apply(&mut actual); - } - } - actual - }; - after == actual - }) - }) - .is_some() - }); + for (edit, snippet_edit) in source_change.source_file_edits.values() { + edit.apply(&mut actual); + if let Some(snippet_edit) = snippet_edit { + snippet_edit.apply(&mut actual); + } + } + actual + }; + after == actual + }) + }) + .is_some() + }); assert!(fix.is_some(), "no diagnostic with desired fix"); } @@ -144,38 +150,40 @@ pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &s let mut conf = DiagnosticsConfig::test_sample(); conf.expr_fill_default = ExprFillDefaultMode::Default; let mut n_fixes = 0; - let fix = - super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into()) - .into_iter() - .find(|d| { - d.fixes - .as_ref() - .and_then(|fixes| { - n_fixes += fixes.len(); - fixes.iter().find(|fix| { - if !fix.target.contains_inclusive(file_position.offset) { - return false; - } - let actual = { - let source_change = fix.source_change.as_ref().unwrap(); - let file_id = - *source_change.source_file_edits.keys().next().unwrap(); - let mut actual = db.file_text(file_id).to_string(); + let fix = super::full_diagnostics( + &db, + &conf, + &AssistResolveStrategy::All, + file_position.file_id.into(), + ) + .into_iter() + .find(|d| { + d.fixes + .as_ref() + .and_then(|fixes| { + n_fixes += fixes.len(); + fixes.iter().find(|fix| { + if !fix.target.contains_inclusive(file_position.offset) { + return false; + } + let actual = { + let source_change = fix.source_change.as_ref().unwrap(); + let file_id = *source_change.source_file_edits.keys().next().unwrap(); + let mut actual = db.file_text(file_id).to_string(); - for (edit, snippet_edit) in source_change.source_file_edits.values() - { - edit.apply(&mut actual); - if let Some(snippet_edit) = snippet_edit { - snippet_edit.apply(&mut actual); - } - } - actual - }; - after == actual - }) - }) - .is_some() - }); + for (edit, snippet_edit) in source_change.source_file_edits.values() { + edit.apply(&mut actual); + if let Some(snippet_edit) = snippet_edit { + snippet_edit.apply(&mut actual); + } + } + actual + }; + after == actual + }) + }) + .is_some() + }); assert!(fix.is_some(), "no diagnostic with desired fix"); assert!(n_fixes == 1, "Too many fixes suggested"); } @@ -183,7 +191,7 @@ pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &s /// Checks that there's a diagnostic *without* fix at `$0`. pub(crate) fn check_no_fix(ra_fixture: &str) { let (db, file_position) = RootDatabase::with_position(ra_fixture); - let diagnostic = super::diagnostics( + let diagnostic = super::full_diagnostics( &db, &DiagnosticsConfig::test_sample(), &AssistResolveStrategy::All, @@ -215,7 +223,7 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur .iter() .copied() .flat_map(|file_id| { - super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id.into()) + super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id.into()) .into_iter() .map(|d| { let mut annotation = String::new(); @@ -277,10 +285,10 @@ fn test_disabled_diagnostics() { let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); let file_id = file_id.into(); - let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); + let diagnostics = super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); assert!(diagnostics.is_empty()); - let diagnostics = super::diagnostics( + let diagnostics = super::full_diagnostics( &db, &DiagnosticsConfig::test_sample(), &AssistResolveStrategy::All, diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 8cb81a9cc452c..9f25fd6e4af2f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -672,14 +672,33 @@ impl Analysis { .unwrap_or_default()) } - /// Computes the set of diagnostics for the given file. - pub fn diagnostics( + /// Computes the set of parser level diagnostics for the given file. + pub fn syntax_diagnostics( + &self, + config: &DiagnosticsConfig, + file_id: FileId, + ) -> Cancellable> { + self.with_db(|db| ide_diagnostics::syntax_diagnostics(db, config, file_id)) + } + + /// Computes the set of semantic diagnostics for the given file. + pub fn semantic_diagnostics( + &self, + config: &DiagnosticsConfig, + resolve: AssistResolveStrategy, + file_id: FileId, + ) -> Cancellable> { + self.with_db(|db| ide_diagnostics::semantic_diagnostics(db, config, &resolve, file_id)) + } + + /// Computes the set of both syntax and semantic diagnostics for the given file. + pub fn full_diagnostics( &self, config: &DiagnosticsConfig, resolve: AssistResolveStrategy, file_id: FileId, ) -> Cancellable> { - self.with_db(|db| ide_diagnostics::diagnostics(db, config, &resolve, file_id)) + self.with_db(|db| ide_diagnostics::full_diagnostics(db, config, &resolve, file_id)) } /// Convenience function to return assists + quick fixes for diagnostics @@ -697,7 +716,7 @@ impl Analysis { self.with_db(|db| { let diagnostic_assists = if diagnostics_config.enabled && include_fixes { - ide_diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id) + ide_diagnostics::full_diagnostics(db, diagnostics_config, &resolve, frange.file_id) .into_iter() .flat_map(|it| it.fixes.unwrap_or_default()) .filter(|it| it.target.intersect(frange.range).is_some()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 380105d2c2141..fa08b8a5aba3e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -977,7 +977,7 @@ impl flags::AnalysisStats { let mut sw = self.stop_watch(); for &file_id in &file_ids { - _ = analysis.diagnostics( + _ = analysis.full_diagnostics( &DiagnosticsConfig { enabled: true, proc_macros_enabled: true, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index 4ddeb4ab1b0c3..c03a298c2bc85 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -63,7 +63,7 @@ impl flags::Diagnostics { _vfs.file_path(file_id.into()) ); for diagnostic in analysis - .diagnostics( + .full_diagnostics( &DiagnosticsConfig::test_sample(), AssistResolveStrategy::None, file_id.into(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index fddc790e6989d..3932db00bbab9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -155,7 +155,7 @@ impl Tester { let root_file = self.root_file; move || { let res = std::panic::catch_unwind(move || { - analysis.diagnostics( + analysis.full_diagnostics( diagnostic_config, ide::AssistResolveStrategy::None, root_file, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index b23e7b7e98c69..b99a8de2fc2ef 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -11,7 +11,7 @@ use rustc_hash::FxHashSet; use stdx::iter_eq_by; use triomphe::Arc; -use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext}; +use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext, main_loop::DiagnosticsTaskKind}; pub(crate) type CheckFixes = Arc>>>; @@ -28,7 +28,8 @@ pub(crate) type DiagnosticsGeneration = usize; #[derive(Debug, Default, Clone)] pub(crate) struct DiagnosticCollection { // FIXME: should be IntMap> - pub(crate) native: IntMap)>, + pub(crate) native_syntax: IntMap)>, + pub(crate) native_semantic: IntMap)>, // FIXME: should be Vec pub(crate) check: IntMap>>, pub(crate) check_fixes: CheckFixes, @@ -64,7 +65,8 @@ impl DiagnosticCollection { } pub(crate) fn clear_native_for(&mut self, file_id: FileId) { - self.native.remove(&file_id); + self.native_syntax.remove(&file_id); + self.native_semantic.remove(&file_id); self.changes.insert(file_id); } @@ -88,43 +90,51 @@ impl DiagnosticCollection { self.changes.insert(file_id); } - pub(crate) fn set_native_diagnostics( - &mut self, - generation: DiagnosticsGeneration, - file_id: FileId, - mut diagnostics: Vec, - ) { - diagnostics.sort_by_key(|it| (it.range.start, it.range.end)); - if let Some((old_gen, existing_diagnostics)) = self.native.get_mut(&file_id) { - if existing_diagnostics.len() == diagnostics.len() - && iter_eq_by(&diagnostics, &*existing_diagnostics, |new, existing| { - are_diagnostics_equal(new, existing) - }) - { - // don't signal an update if the diagnostics are the same - return; + pub(crate) fn set_native_diagnostics(&mut self, kind: DiagnosticsTaskKind) { + let (generation, diagnostics, target) = match kind { + DiagnosticsTaskKind::Syntax(generation, diagnostics) => { + (generation, diagnostics, &mut self.native_syntax) + } + DiagnosticsTaskKind::Semantic(generation, diagnostics) => { + (generation, diagnostics, &mut self.native_semantic) } - if *old_gen < generation || generation == 0 { - self.native.insert(file_id, (generation, diagnostics)); + }; + + for (file_id, mut diagnostics) in diagnostics { + diagnostics.sort_by_key(|it| (it.range.start, it.range.end)); + + if let Some((old_gen, existing_diagnostics)) = target.get_mut(&file_id) { + if existing_diagnostics.len() == diagnostics.len() + && iter_eq_by(&diagnostics, &*existing_diagnostics, |new, existing| { + are_diagnostics_equal(new, existing) + }) + { + // don't signal an update if the diagnostics are the same + return; + } + if *old_gen < generation || generation == 0 { + target.insert(file_id, (generation, diagnostics)); + } else { + existing_diagnostics.extend(diagnostics); + // FIXME: Doing the merge step of a merge sort here would be a bit more performant + // but eh + existing_diagnostics.sort_by_key(|it| (it.range.start, it.range.end)) + } } else { - existing_diagnostics.extend(diagnostics); - // FIXME: Doing the merge step of a merge sort here would be a bit more performant - // but eh - existing_diagnostics.sort_by_key(|it| (it.range.start, it.range.end)) + target.insert(file_id, (generation, diagnostics)); } - } else { - self.native.insert(file_id, (generation, diagnostics)); + self.changes.insert(file_id); } - self.changes.insert(file_id); } pub(crate) fn diagnostics_for( &self, file_id: FileId, ) -> impl Iterator { - let native = self.native.get(&file_id).into_iter().flat_map(|(_, d)| d); + let native_syntax = self.native_syntax.get(&file_id).into_iter().flat_map(|(_, d)| d); + let native_semantic = self.native_semantic.get(&file_id).into_iter().flat_map(|(_, d)| d); let check = self.check.values().filter_map(move |it| it.get(&file_id)).flatten(); - native.chain(check) + native_syntax.chain(native_semantic).chain(check) } pub(crate) fn take_changes(&mut self) -> Option> { @@ -147,10 +157,16 @@ fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagno && left.message == right.message } +pub(crate) enum NativeDiagnosticsFetchKind { + Syntax, + Semantic, +} + pub(crate) fn fetch_native_diagnostics( - snapshot: GlobalStateSnapshot, + snapshot: &GlobalStateSnapshot, subscriptions: std::sync::Arc<[FileId]>, slice: std::ops::Range, + kind: NativeDiagnosticsFetchKind, ) -> Vec<(FileId, Vec)> { let _p = tracing::info_span!("fetch_native_diagnostics").entered(); let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned()); @@ -180,14 +196,17 @@ pub(crate) fn fetch_native_diagnostics( let line_index = snapshot.file_line_index(file_id).ok()?; let source_root = snapshot.analysis.source_root_id(file_id).ok()?; - let diagnostics = snapshot - .analysis - .diagnostics( - &snapshot.config.diagnostics(Some(source_root)), - ide::AssistResolveStrategy::None, - file_id, - ) - .ok()? + let config = &snapshot.config.diagnostics(Some(source_root)); + let diagnostics = match kind { + NativeDiagnosticsFetchKind::Syntax => { + snapshot.analysis.syntax_diagnostics(config, file_id).ok()? + } + NativeDiagnosticsFetchKind::Semantic => snapshot + .analysis + .semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id) + .ok()?, + }; + let diagnostics = diagnostics .into_iter() .filter_map(|d| { if d.range.file_id == file_id { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index f6543a82e57ab..28f4b809d6c82 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -325,7 +325,7 @@ fn integrated_diagnostics_benchmark() { term_search_borrowck: true, }; host.analysis() - .diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id) + .full_diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id) .unwrap(); let _g = crate::tracing::hprof::init("*"); @@ -343,7 +343,7 @@ fn integrated_diagnostics_benchmark() { let _p = tracing::info_span!("diagnostics").entered(); let _span = profile::cpu_span(); host.analysis() - .diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id) + .full_diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id) .unwrap(); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 9c820749ece6d..b0c12c8b78501 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -19,7 +19,7 @@ use vfs::{AbsPathBuf, FileId}; use crate::{ config::Config, - diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration}, + diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind}, dispatch::{NotificationDispatcher, RequestDispatcher}, global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState}, hack_recover_crate_name, @@ -86,12 +86,18 @@ pub(crate) enum QueuedTask { CheckProcMacroSources(Vec), } +#[derive(Debug)] +pub(crate) enum DiagnosticsTaskKind { + Syntax(DiagnosticsGeneration, Vec<(FileId, Vec)>), + Semantic(DiagnosticsGeneration, Vec<(FileId, Vec)>), +} + #[derive(Debug)] pub(crate) enum Task { Response(lsp_server::Response), DiscoverLinkedProjects(DiscoverProjectParam), Retry(lsp_server::Request), - Diagnostics(DiagnosticsGeneration, Vec<(FileId, Vec)>), + Diagnostics(DiagnosticsTaskKind), DiscoverTest(lsp_ext::DiscoverTestResults), PrimeCaches(PrimeCachesProgress), FetchWorkspace(ProjectWorkspaceProgress), @@ -549,14 +555,29 @@ impl GlobalState { } // Diagnostics are triggered by the user typing // so we run them on a latency sensitive thread. - self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, { - let snapshot = self.snapshot(); + let snapshot = self.snapshot(); + self.task_pool.handle.spawn_with_sender(ThreadIntent::LatencySensitive, { let subscriptions = subscriptions.clone(); - move || { - Task::Diagnostics( - generation, - fetch_native_diagnostics(snapshot, subscriptions, slice), - ) + move |sender| { + let diags = fetch_native_diagnostics( + &snapshot, + subscriptions.clone(), + slice.clone(), + NativeDiagnosticsFetchKind::Syntax, + ); + sender + .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags))) + .unwrap(); + + let diags = fetch_native_diagnostics( + &snapshot, + subscriptions, + slice, + NativeDiagnosticsFetchKind::Semantic, + ); + sender + .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic(generation, diags))) + .unwrap(); } }); start = end; @@ -644,10 +665,8 @@ impl GlobalState { // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work. Task::Retry(req) if !self.is_completed(&req) => self.on_request(req), Task::Retry(_) => (), - Task::Diagnostics(generation, diagnostics_per_file) => { - for (file_id, diagnostics) in diagnostics_per_file { - self.diagnostics.set_native_diagnostics(generation, file_id, diagnostics) - } + Task::Diagnostics(kind) => { + self.diagnostics.set_native_diagnostics(kind); } Task::PrimeCaches(progress) => match progress { PrimeCachesProgress::Begin => prime_caches_progress.push(progress), From 314f30167f9b59b0d0b7949307fb810e79481c21 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 Aug 2024 15:56:23 +0200 Subject: [PATCH 026/178] Slightly optimize watch list in vfs --- src/tools/rust-analyzer/Cargo.lock | 1 + .../crates/vfs-notify/Cargo.toml | 1 + .../crates/vfs-notify/src/lib.rs | 40 +++++++++++++------ 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 925afdcc98474..65318b1947cff 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2333,6 +2333,7 @@ dependencies = [ "notify", "paths", "rayon", + "rustc-hash", "stdx", "tracing", "vfs", diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index 3602bac4dd156..2e4a452bf807a 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -21,6 +21,7 @@ rayon = "1.10.0" stdx.workspace = true vfs.workspace = true paths.workspace = true +rustc-hash.workspace = true [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 7328cd9ed6018..d0d3a844465d3 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -17,6 +17,7 @@ use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rayon::iter::{IndexedParallelIterator as _, IntoParallelIterator as _, ParallelIterator}; +use rustc_hash::FxHashSet; use vfs::loader::{self, LoadingProgress}; use walkdir::WalkDir; @@ -61,8 +62,8 @@ type NotifyEvent = notify::Result; struct NotifyActor { sender: loader::Sender, - // FIXME: Consider hashset - watched_entries: Vec, + watched_file_entries: FxHashSet, + watched_dir_entries: Vec, // Drop order is significant. watcher: Option<(RecommendedWatcher, Receiver)>, } @@ -75,7 +76,12 @@ enum Event { impl NotifyActor { fn new(sender: loader::Sender) -> NotifyActor { - NotifyActor { sender, watched_entries: Vec::new(), watcher: None } + NotifyActor { + sender, + watched_dir_entries: Vec::new(), + watched_file_entries: FxHashSet::default(), + watcher: None, + } } fn next_event(&self, receiver: &Receiver) -> Option { @@ -107,7 +113,8 @@ impl NotifyActor { let config_version = config.version; let n_total = config.load.len(); - self.watched_entries.clear(); + self.watched_dir_entries.clear(); + self.watched_file_entries.clear(); let send = |msg| (self.sender)(msg); send(loader::Message::Progress { @@ -154,7 +161,14 @@ impl NotifyActor { self.watch(&path); } for entry in entry_rx { - self.watched_entries.push(entry); + match entry { + loader::Entry::Files(files) => { + self.watched_file_entries.extend(files) + } + loader::Entry::Directories(dir) => { + self.watched_dir_entries.push(dir) + } + } } self.send(loader::Message::Progress { n_total, @@ -185,13 +199,13 @@ impl NotifyActor { .expect("path is absolute"), ) }) - .filter_map(|path| { + .filter_map(|path| -> Option<(AbsPathBuf, Option>)> { let meta = fs::metadata(&path).ok()?; if meta.file_type().is_dir() && self - .watched_entries + .watched_dir_entries .iter() - .any(|entry| entry.contains_dir(&path)) + .any(|dir| dir.contains_dir(&path)) { self.watch(path.as_ref()); return None; @@ -200,10 +214,12 @@ impl NotifyActor { if !meta.file_type().is_file() { return None; } - if !self - .watched_entries - .iter() - .any(|entry| entry.contains_file(&path)) + + if !(self.watched_file_entries.contains(&path) + || self + .watched_dir_entries + .iter() + .any(|dir| dir.contains_file(&path))) { return None; } From d95a4b8d2bed8e2229c4815a2c6be6b625485fc2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 2 Aug 2024 15:39:05 +0200 Subject: [PATCH 027/178] Reorganize debug.ts --- .../rust-analyzer/editors/code/src/debug.ts | 268 ++++++++++++------ .../rust-analyzer/editors/code/src/run.ts | 16 +- .../code/tests/unit/runnable_env.test.ts | 2 +- 3 files changed, 187 insertions(+), 99 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts index d9622b4a0d20f..3aae0f9ce6eb9 100644 --- a/src/tools/rust-analyzer/editors/code/src/debug.ts +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -7,21 +7,15 @@ import { Cargo } from "./toolchain"; import type { Ctx } from "./ctx"; import { prepareEnv } from "./run"; import { execute, isCargoRunnableArgs, unwrapUndefinable } from "./util"; +import type { Config } from "./config"; const debugOutput = vscode.window.createOutputChannel("Debug"); -type DebugConfigProvider = ( - runnable: ra.Runnable, - runnableArgs: ra.CargoRunnableArgs, - executable: string, - env: Record, - sourceFileMap?: Record, -) => vscode.DebugConfiguration; export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise { const scope = ctx.activeRustEditor?.document.uri; if (!scope) return; - const debugConfig = await getDebugConfiguration(ctx, runnable); + const debugConfig = await getDebugConfiguration(ctx.config, runnable, false); if (!debugConfig) return; const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); @@ -57,7 +51,7 @@ export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promis message = " (from launch.json)"; debugOutput.clear(); } else { - debugConfig = await getDebugConfiguration(ctx, runnable); + debugConfig = await getDebugConfiguration(ctx.config, runnable); } if (!debugConfig) return false; @@ -74,35 +68,35 @@ function createCommandLink(extensionId: string): string { } async function getDebugConfiguration( - ctx: Ctx, + config: Config, runnable: ra.Runnable, + inheritEnv: boolean = true, ): Promise { if (!isCargoRunnableArgs(runnable.args)) { return; } const runnableArgs: ra.CargoRunnableArgs = runnable.args; - const editor = ctx.activeRustEditor; - if (!editor) return; + const debugOptions = config.debug; - const knownEngines: Record = { - "vadimcn.vscode-lldb": getCodeLldbDebugConfig, - "ms-vscode.cpptools": getCCppDebugConfig, - "webfreak.debug": getNativeDebugConfig, - }; - const debugOptions = ctx.config.debug; + let provider: null | KnownEnginesType = null; - let debugEngine = null; if (debugOptions.engine === "auto") { - for (var engineId in knownEngines) { - debugEngine = vscode.extensions.getExtension(engineId); - if (debugEngine) break; + for (const engineId in knownEngines) { + const debugEngine = vscode.extensions.getExtension(engineId); + if (debugEngine) { + provider = knownEngines[engineId as keyof typeof knownEngines]; + break; + } } } else if (debugOptions.engine) { - debugEngine = vscode.extensions.getExtension(debugOptions.engine); + const debugEngine = vscode.extensions.getExtension(debugOptions.engine); + if (debugEngine && Object.keys(knownEngines).includes(debugOptions.engine)) { + provider = knownEngines[debugOptions.engine as keyof typeof knownEngines]; + } } - if (!debugEngine) { + if (!provider) { const commandCCpp: string = createCommandLink("ms-vscode.cpptools"); const commandCodeLLDB: string = createCommandLink("vadimcn.vscode-lldb"); const commandNativeDebug: string = createCommandLink("webfreak.debug"); @@ -116,7 +110,7 @@ async function getDebugConfiguration( } debugOutput.clear(); - if (ctx.config.debug.openDebugPane) { + if (config.debug.openDebugPane) { debugOutput.show(true); } // folder exists or RA is not active. @@ -131,37 +125,36 @@ async function getDebugConfiguration( firstWorkspace; const workspace = unwrapUndefinable(maybeWorkspace); - const wsFolder = path.normalize(workspace.uri.fsPath); + let wsFolder = path.normalize(workspace.uri.fsPath); + if (os.platform() === "win32") { + // in windows, the drive letter can vary in casing for VSCode, so we gotta normalize that first + wsFolder = wsFolder.replace(/^[a-z]:\\/, (c) => c.toUpperCase()); + } + const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : ""; function simplifyPath(p: string): string { + // in windows, the drive letter can vary in casing for VSCode, so we gotta normalize that first + if (os.platform() === "win32") { + p = p.replace(/^[a-z]:\\/, (c) => c.toUpperCase()); + } // see https://github.com/rust-lang/rust-analyzer/pull/5513#issuecomment-663458818 for why this is needed return path.normalize(p).replace(wsFolder, `\${workspaceFolder${workspaceQualifier}}`); } - const env = prepareEnv(runnable.label, runnableArgs, ctx.config.runnablesExtraEnv); + const env = prepareEnv(inheritEnv, runnable.label, runnableArgs, config.runnablesExtraEnv); const executable = await getDebugExecutable(runnableArgs, env); let sourceFileMap = debugOptions.sourceFileMap; if (sourceFileMap === "auto") { sourceFileMap = {}; - const sysroot = env["RUSTC_TOOLCHAIN"]; - if (sysroot) { - // let's try to use the default toolchain - const data = await execute(`rustc -V -v`, { cwd: wsFolder, env }); - const rx = /commit-hash:\s(.*)$/m; - - const commitHash = rx.exec(data)?.[1]; - if (commitHash) { - const rustlib = path.normalize(sysroot + "/lib/rustlib/src/rust"); - sourceFileMap[`/rustc/${commitHash}/`] = rustlib; - } - } + await discoverSourceFileMap(sourceFileMap, env, wsFolder); } - const provider = unwrapUndefinable(knownEngines[debugEngine.id]); - const debugConfig = provider( + const debugConfig = getDebugConfig( + provider, + simplifyPath, runnable, runnableArgs, - simplifyPath(executable), + executable, env, sourceFileMap, ); @@ -186,6 +179,92 @@ async function getDebugConfiguration( return debugConfig; } +async function discoverSourceFileMap( + sourceFileMap: Record, + env: Record, + cwd: string, +) { + const sysroot = env["RUSTC_TOOLCHAIN"]; + if (sysroot) { + // let's try to use the default toolchain + const data = await execute(`rustc -V -v`, { cwd, env }); + const rx = /commit-hash:\s(.*)$/m; + + const commitHash = rx.exec(data)?.[1]; + if (commitHash) { + const rustlib = path.normalize(sysroot + "/lib/rustlib/src/rust"); + sourceFileMap[`/rustc/${commitHash}/`] = rustlib; + } + } +} + +type PropertyFetcher = ( + input: Input, +) => [Key, Config[Key]]; + +type DebugConfigProvider> = { + executableProperty: keyof DebugConfig; + environmentProperty: PropertyFetcher, keyof DebugConfig>; + runnableArgsProperty: PropertyFetcher; + sourceFileMapProperty?: keyof DebugConfig; + type: Type; + additional?: Record; +}; + +type KnownEnginesType = (typeof knownEngines)[keyof typeof knownEngines]; +const knownEngines: { + "vadimcn.vscode-lldb": DebugConfigProvider<"lldb", CodeLldbDebugConfig>; + "ms-vscode.cpptools": DebugConfigProvider<"cppvsdbg" | "cppdbg", CCppDebugConfig>; + "webfreak.debug": DebugConfigProvider<"gdb", NativeDebugConfig>; +} = { + "vadimcn.vscode-lldb": { + type: "lldb", + executableProperty: "program", + environmentProperty: (env) => ["env", env], + runnableArgsProperty: (runnableArgs: ra.CargoRunnableArgs) => [ + "args", + runnableArgs.executableArgs, + ], + sourceFileMapProperty: "sourceMap", + additional: { + sourceLanguages: ["rust"], + }, + }, + "ms-vscode.cpptools": { + type: os.platform() === "win32" ? "cppvsdbg" : "cppdbg", + executableProperty: "program", + environmentProperty: (env) => [ + "environment", + Object.entries(env).map((entry) => ({ + name: entry[0], + value: entry[1], + })), + ], + runnableArgsProperty: (runnableArgs: ra.CargoRunnableArgs) => [ + "args", + runnableArgs.executableArgs, + ], + sourceFileMapProperty: "sourceFileMap", + additional: { + osx: { + MIMode: "lldb", + }, + }, + }, + "webfreak.debug": { + type: "gdb", + executableProperty: "target", + runnableArgsProperty: (runnableArgs: ra.CargoRunnableArgs) => [ + "arguments", + quote(runnableArgs.executableArgs), + ], + environmentProperty: (env) => ["env", env], + additional: { + valuesFormatting: "prettyPrinters", + }, + }, +}; + async function getDebugExecutable( runnableArgs: ra.CargoRunnableArgs, env: Record, @@ -197,71 +276,74 @@ async function getDebugExecutable( return executable; } -function getCCppDebugConfig( - runnable: ra.Runnable, - runnableArgs: ra.CargoRunnableArgs, - executable: string, - env: Record, - sourceFileMap?: Record, -): vscode.DebugConfiguration { - return { - type: os.platform() === "win32" ? "cppvsdbg" : "cppdbg", - request: "launch", - name: runnable.label, - program: executable, - args: runnableArgs.executableArgs, - cwd: runnable.args.cwd || runnableArgs.workspaceRoot || ".", - sourceFileMap, - environment: Object.entries(env).map((entry) => ({ - name: entry[0], - value: entry[1], - })), - // See https://github.com/rust-lang/rust-analyzer/issues/16901#issuecomment-2024486941 - osx: { - MIMode: "lldb", - }, - }; -} +type BaseDebugConfig = { + type: type; + request: "launch"; + name: string; + cwd: string; +}; -function getCodeLldbDebugConfig( +function getDebugConfig( + provider: KnownEnginesType, + simplifyPath: (p: string) => string, runnable: ra.Runnable, runnableArgs: ra.CargoRunnableArgs, executable: string, env: Record, sourceFileMap?: Record, ): vscode.DebugConfiguration { + const { + environmentProperty, + executableProperty, + runnableArgsProperty, + type, + additional, + sourceFileMapProperty, + } = provider; + const [envProperty, envValue] = environmentProperty(env); + const [argsProperty, argsValue] = runnableArgsProperty(runnableArgs); return { - type: "lldb", + type, request: "launch", name: runnable.label, - program: executable, - args: runnableArgs.executableArgs, - cwd: runnable.args.cwd || runnableArgs.workspaceRoot || ".", - sourceMap: sourceFileMap, - sourceLanguages: ["rust"], - env, + cwd: simplifyPath(runnable.args.cwd || runnableArgs.workspaceRoot || "."), + [executableProperty]: simplifyPath(executable), + [envProperty]: envValue, + [argsProperty]: argsValue, + ...(sourceFileMapProperty ? { [sourceFileMapProperty]: sourceFileMap } : {}), + ...additional, }; } -function getNativeDebugConfig( - runnable: ra.Runnable, - runnableArgs: ra.CargoRunnableArgs, - executable: string, - env: Record, - _sourceFileMap?: Record, -): vscode.DebugConfiguration { - return { - type: "gdb", - request: "launch", - name: runnable.label, - target: executable, - // See https://github.com/WebFreak001/code-debug/issues/359 - arguments: quote(runnableArgs.executableArgs), - cwd: runnable.args.cwd || runnableArgs.workspaceRoot || ".", - env, - valuesFormatting: "prettyPrinters", +type CCppDebugConfig = { + program: string; + args: string[]; + sourceFileMap: Record | undefined; + environment: { + name: string; + value: string; + }[]; + // See https://github.com/rust-lang/rust-analyzer/issues/16901#issuecomment-2024486941 + osx: { + MIMode: "lldb"; }; -} +} & BaseDebugConfig<"cppvsdbg" | "cppdbg">; + +type CodeLldbDebugConfig = { + program: string; + args: string[]; + sourceMap: Record | undefined; + sourceLanguages: ["rust"]; + env: Record; +} & BaseDebugConfig<"lldb">; + +type NativeDebugConfig = { + target: string; + // See https://github.com/WebFreak001/code-debug/issues/359 + arguments: string; + env: Record; + valuesFormatting: "prettyPrinters"; +} & BaseDebugConfig<"gdb">; // Based on https://github.com/ljharb/shell-quote/blob/main/quote.js function quote(xs: string[]) { diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts index 7179eb3744771..dd0da6b62c824 100644 --- a/src/tools/rust-analyzer/editors/code/src/run.ts +++ b/src/tools/rust-analyzer/editors/code/src/run.ts @@ -65,9 +65,14 @@ export class RunnableQuickPick implements vscode.QuickPickItem { } } -export function prepareBaseEnv(base?: Record): Record { +export function prepareBaseEnv( + inheritEnv: boolean, + base?: Record, +): Record { const env: Record = { RUST_BACKTRACE: "short" }; - Object.assign(env, process.env); + if (inheritEnv) { + Object.assign(env, process.env); + } if (base) { Object.assign(env, base); } @@ -75,11 +80,12 @@ export function prepareBaseEnv(base?: Record): Record { - const env = prepareBaseEnv(runnableArgs.environment); + const env = prepareBaseEnv(inheritEnv, runnableArgs.environment); const platform = process.platform; const checkPlatform = (it: RunnableEnvCfgItem) => { @@ -134,7 +140,7 @@ export async function createTaskFromRunnable( }; options = { cwd: runnableArgs.workspaceRoot || ".", - env: prepareEnv(runnable.label, runnableArgs, config.runnablesExtraEnv), + env: prepareEnv(true, runnable.label, runnableArgs, config.runnablesExtraEnv), }; } else { const runnableArgs = runnable.args; @@ -145,7 +151,7 @@ export async function createTaskFromRunnable( }; options = { cwd: runnableArgs.cwd, - env: prepareBaseEnv(), + env: prepareBaseEnv(true), }; } diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts index 81850e03f1cdd..f0a62a3cce292 100644 --- a/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts +++ b/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts @@ -19,7 +19,7 @@ function makeRunnable(label: string): ra.Runnable { function fakePrepareEnv(runnableName: string, config?: RunnableEnvCfg): Record { const runnable = makeRunnable(runnableName); const runnableArgs = runnable.args as ra.CargoRunnableArgs; - return prepareEnv(runnable.label, runnableArgs, config); + return prepareEnv(false, runnable.label, runnableArgs, config); } export async function getTests(ctx: Context) { From 3e23c4541b33649754e08c86f12bc4b3563885ff Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 Aug 2024 16:15:28 +0200 Subject: [PATCH 028/178] Surpress type mismatches in calls with mismatched arg counts --- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 7 ++++--- .../src/handlers/mismatched_arg_count.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 3d762b174ace0..f5eb37f427876 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1759,13 +1759,14 @@ impl InferenceContext<'_> { skip_indices: &[u32], is_varargs: bool, ) { - if args.len() != param_tys.len() + skip_indices.len() && !is_varargs { + let arg_count_mismatch = args.len() != param_tys.len() + skip_indices.len() && !is_varargs; + if arg_count_mismatch { self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount { call_expr: expr, expected: param_tys.len() + skip_indices.len(), found: args.len(), }); - } + }; // Quoting https://github.com/rust-lang/rust/blob/6ef275e6c3cb1384ec78128eceeb4963ff788dca/src/librustc_typeck/check/mod.rs#L3325 -- // We do this in a pretty awful way: first we type-check any arguments @@ -1819,7 +1820,7 @@ impl InferenceContext<'_> { // The function signature may contain some unknown types, so we need to insert // type vars here to avoid type mismatch false positive. let coercion_target = self.insert_type_vars(coercion_target); - if self.coerce(Some(arg), &ty, &coercion_target).is_err() { + if self.coerce(Some(arg), &ty, &coercion_target).is_err() && !arg_count_mismatch { self.result.type_mismatches.insert( arg.into(), TypeMismatch { expected: coercion_target, actual: ty.clone() }, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 56ec45c898409..7126617cdeed3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -472,4 +472,18 @@ fn f( "#, ) } + + #[test] + fn no_type_mismatches_when_arg_count_mismatch() { + check_diagnostics( + r#" +fn foo((): (), (): ()) { + foo(1, 2, 3); + // ^^ error: expected 2 arguments, found 3 + foo(1); + // ^ error: expected 2 arguments, found 1 +} +"#, + ); + } } From ad4e8155baa257c0be477772e8e28a142f98720f Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 6 Aug 2024 04:24:40 +0900 Subject: [PATCH 029/178] fix: Panic in path transform with default type parameters --- .../src/handlers/add_missing_impl_members.rs | 25 ++++++++++++++++++ .../crates/ide-db/src/path_transform.rs | 26 +++++++++++++++---- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index ebfbb83bb9183..4cd15f1c755e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2280,4 +2280,29 @@ impl b::LocalTrait for B { "#, ) } + + #[test] + fn impl_with_type_param_with_former_param_as_default() { + check_assist( + add_missing_impl_members, + r#" +pub trait Test<'a, T, U = T> { + fn test(item: &'a T) -> U; +} +impl<'a> Test<'a, i32> for bool { + $0 +} +"#, + r#" +pub trait Test<'a, T, U = T> { + fn test(item: &'a T) -> U; +} +impl<'a> Test<'a, i32> for bool { + fn test(item: &'a i32) -> i32 { + ${0:todo!()} + } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 84a388a460b82..0afa9163e31d2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -7,7 +7,7 @@ use itertools::Itertools; use rustc_hash::FxHashMap; use syntax::{ ast::{self, make, AstNode, HasGenericArgs}, - ted, SyntaxNode, + ted, NodeOrToken, SyntaxNode, }; #[derive(Default)] @@ -328,10 +328,26 @@ impl Ctx<'_> { let qualified = make::path_from_segments(std::iter::once(segment), false); ted::replace(path.syntax(), qualified.clone_for_update().syntax()); } else if let Some(path_ty) = ast::PathType::cast(parent) { - ted::replace( - path_ty.syntax(), - subst.clone_subtree().clone_for_update().syntax(), - ); + let old = path_ty.syntax(); + + if old.parent().is_some() { + ted::replace(old, subst.clone_subtree().clone_for_update().syntax()); + } else { + // Some `path_ty` has no parent, especially ones made for default value + // of type parameters. + // In this case, `ted` cannot replace `path_ty` with `subst` directly. + // So, just replace its children as long as the `subst` is the same type. + let new = subst.clone_subtree().clone_for_update(); + if !matches!(new, ast::Type::PathType(..)) { + return None; + } + let start = path_ty.syntax().first_child().map(NodeOrToken::Node)?; + let end = path_ty.syntax().last_child().map(NodeOrToken::Node)?; + ted::replace_all( + start..=end, + new.syntax().children().map(NodeOrToken::Node).collect::>(), + ); + } } else { ted::replace( path.syntax(), From 88ea306270e6f4fcb21f48329779d7c47aa1794e Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Tue, 30 Jul 2024 14:10:08 +0200 Subject: [PATCH 030/178] Replace `"TBD"` with more helpful desciptions in published crates' `[package.description]` fields --- src/tools/rust-analyzer/crates/base-db/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/cfg/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/flycheck/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir-def/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir-expand/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir-ty/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-assists/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-completion/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-db/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/intern/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/limit/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/load-cargo/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/mbe/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/parser/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/paths/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/profile/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/project-model/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/span/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/stdx/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/syntax/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/test-utils/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/text-edit/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/toolchain/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/tt/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/vfs/Cargo.toml | 2 +- 33 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index 1b1ee034cac2f..488ddbfd3bf2c 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "base-db" version = "0.0.0" -description = "TBD" +description = "Basic database traits. The concrete DB is defined by `ra_ap_ide`." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index 0076b674768e8..7d01b4a62622f 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cfg" version = "0.0.0" -description = "TBD" +description = "Conditional compiling options, `cfg` attribute parser and evaluator." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml index bb3a94c8da678..b886c131caff1 100644 --- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml +++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "flycheck" version = "0.0.0" -description = "TBD" +description = "Functionality needed to run `cargo` commands in a background thread." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 7c58dd221de6b..9903f929a173b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hir-def" version = "0.0.0" -description = "TBD" +description = "Everything between macro expansion and type inference for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index ff4b468f6c9c4..25f77df7653d8 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hir-expand" version = "0.0.0" -description = "TBD" +description = "Macro expansion for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index b6c33683ff6d9..8f61ef016c406 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hir-ty" version = "0.0.0" -description = "TBD" +description = "The type system for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index edf26a07a7442..b8ed4a719e40f 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hir" version = "0.0.0" -description = "TBD" +description = "A high-level object oriented access to Rust code." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml index b1e7609afef41..422b030c27d28 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide-assists" version = "0.0.0" -description = "TBD" +description = "Code assists for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml index 6a4c70d460f2c..041f937058c90 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide-completion" version = "0.0.0" -description = "TBD" +description = "Utilities for generating completions of user input." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 9a6826a5c4263..18e77bf7ccc0f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide-db" version = "0.0.0" -description = "TBD" +description = "Core data-structure representing IDE state." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index edd05009332e5..ad561b6b803df 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide-diagnostics" version = "0.0.0" -description = "TBD" +description = "Diagnostics rendering and fixits." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml index 57b1f9465ad3e..78d1ca6ec871d 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "ide-ssr" version = "0.0.0" -description = "Structural search and replace of Rust code" repository = "https://github.com/rust-lang/rust-analyzer" +description = "Structural search and replace of Rust code." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index 9d8400ba3adc5..6d0813e4d5d05 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide" version = "0.0.0" -description = "TBD" +description = "IDE-centric APIs for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index c08ecb5c307b1..eda5e3de16ff1 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "intern" version = "0.0.0" -description = "TBD" +description = "Global `Arc`-based object interning infrastructure." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/limit/Cargo.toml b/src/tools/rust-analyzer/crates/limit/Cargo.toml index c1a768833b99e..27fe4329094dc 100644 --- a/src/tools/rust-analyzer/crates/limit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/limit/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "limit" version = "0.0.0" -description = "TBD" +description = "A struct to enforce limits." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index 64ed93bbb1640..5188138183e75 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "load-cargo" version = "0.0.0" -description = "TBD" +description = "Loads a Cargo project into a static instance of analysis." rust-version.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index e441c0ec3d742..ccf4c7670a356 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "mbe" version = "0.0.0" -description = "TBD" +description = "Handling of `macro_rules` macros for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml index 54b57c201be90..050eb0252f3c7 100644 --- a/src/tools/rust-analyzer/crates/parser/Cargo.toml +++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "parser" version = "0.0.0" -description = "TBD" +description = "The Rust parser for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index b51892c2655c6..938f1db5b567d 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "paths" version = "0.0.0" -description = "TBD" +description = "Path wrappers for absolute and relative paths." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 3e6f80878bdc2..d8c1e0afa44ac 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "proc-macro-api" version = "0.0.0" -description = "TBD" +description = "Client-side proc-macros for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index a559ba0175565..5d56eb695699a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "proc-macro-srv-cli" version = "0.0.0" -description = "TBD" +description = "A standalone binary for `proc-macro-srv`." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index b3221443bdad4..028593db9c59b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "proc-macro-srv" version = "0.0.0" -description = "TBD" +description = "Proc-macro server for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 5989dc6c96291..1fdf53c9611d4 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "profile" version = "0.0.0" -description = "TBD" +description = "A collection of tools for profiling rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 8b34bd3fad1a1..368dd94ec341b 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "project-model" version = "0.0.0" -description = "TBD" +description = "A representation for a Cargo project for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/span/Cargo.toml b/src/tools/rust-analyzer/crates/span/Cargo.toml index 9f85f0107cc18..3ccf00872e0ca 100644 --- a/src/tools/rust-analyzer/crates/span/Cargo.toml +++ b/src/tools/rust-analyzer/crates/span/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "span" version = "0.0.0" -description = "TBD" +description = "File and span related types for rust-analyzer." rust-version.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index 99824df1f69d1..a9ab64374204a 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "stdx" version = "0.0.0" -description = "TBD" +description = "Missing batteries for standard libraries for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index b371ec6ebd532..757ae31ee847b 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "syntax" version = "0.0.0" -description = "Comment and whitespace preserving parser for the Rust language" repository = "https://github.com/rust-lang/rust-analyzer" +description = "Comment and whitespace preserving parser for the Rust language" authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml index 44628a6c11c0d..d40722ffe4aa3 100644 --- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "test-utils" version = "0.0.0" -description = "TBD" +description = "Assorted testing utilities for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml index f745674794c99..1b9008ddc6404 100644 --- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "text-edit" version = "0.0.0" -description = "TBD" +description = "Representation of a `TextEdit` for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml index c85efd432b00d..451dcadac5f34 100644 --- a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml +++ b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "toolchain" version = "0.0.0" -description = "TBD" +description = "Discovery of `cargo` & `rustc` executables for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index cea1519c2dd46..280d052f8e8ab 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tt" version = "0.0.0" -description = "TBD" +description = "A `TokenTree` data structure for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index 2e4a452bf807a..c724a5db7b65d 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "vfs-notify" version = "0.0.0" -description = "TBD" +description = "Implementation of `loader::Handle` for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml index 84f2110ebad15..b1a3bf5edb48e 100644 --- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "vfs" version = "0.0.0" -description = "TBD" +description = "A virtual file system for rust-analyzer." authors.workspace = true edition.workspace = true From 163534182de2275d0316c2f6b36c5aad1155eaad Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Tue, 30 Jul 2024 14:11:16 +0200 Subject: [PATCH 031/178] Add repository URL for published crates' missing `[package.repository]` fields --- src/tools/rust-analyzer/crates/base-db/Cargo.toml | 1 + src/tools/rust-analyzer/crates/cfg/Cargo.toml | 1 + src/tools/rust-analyzer/crates/flycheck/Cargo.toml | 1 + src/tools/rust-analyzer/crates/hir-def/Cargo.toml | 1 + src/tools/rust-analyzer/crates/hir-expand/Cargo.toml | 1 + src/tools/rust-analyzer/crates/hir-ty/Cargo.toml | 1 + src/tools/rust-analyzer/crates/hir/Cargo.toml | 1 + src/tools/rust-analyzer/crates/ide-assists/Cargo.toml | 1 + src/tools/rust-analyzer/crates/ide-completion/Cargo.toml | 1 + src/tools/rust-analyzer/crates/ide-db/Cargo.toml | 1 + src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml | 1 + src/tools/rust-analyzer/crates/ide/Cargo.toml | 1 + src/tools/rust-analyzer/crates/intern/Cargo.toml | 1 + src/tools/rust-analyzer/crates/limit/Cargo.toml | 1 + src/tools/rust-analyzer/crates/load-cargo/Cargo.toml | 1 + src/tools/rust-analyzer/crates/mbe/Cargo.toml | 1 + src/tools/rust-analyzer/crates/parser/Cargo.toml | 1 + src/tools/rust-analyzer/crates/paths/Cargo.toml | 1 + src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml | 1 + src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml | 1 + src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml | 1 + src/tools/rust-analyzer/crates/profile/Cargo.toml | 1 + src/tools/rust-analyzer/crates/project-model/Cargo.toml | 1 + src/tools/rust-analyzer/crates/span/Cargo.toml | 1 + src/tools/rust-analyzer/crates/stdx/Cargo.toml | 1 + src/tools/rust-analyzer/crates/test-utils/Cargo.toml | 1 + src/tools/rust-analyzer/crates/text-edit/Cargo.toml | 1 + src/tools/rust-analyzer/crates/toolchain/Cargo.toml | 1 + src/tools/rust-analyzer/crates/tt/Cargo.toml | 1 + src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml | 1 + src/tools/rust-analyzer/crates/vfs/Cargo.toml | 1 + 31 files changed, 31 insertions(+) diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index 488ddbfd3bf2c..b949de8dc33d4 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "base-db" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Basic database traits. The concrete DB is defined by `ra_ap_ide`." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index 7d01b4a62622f..cfdd80de9e07a 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "cfg" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Conditional compiling options, `cfg` attribute parser and evaluator." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml index b886c131caff1..affa9a295086e 100644 --- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml +++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "flycheck" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Functionality needed to run `cargo` commands in a background thread." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 9903f929a173b..9ad34fa4a8439 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "hir-def" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Everything between macro expansion and type inference for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index 25f77df7653d8..1fabbda7f3eff 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "hir-expand" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Macro expansion for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 8f61ef016c406..0b39f71a3c8c3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "hir-ty" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "The type system for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index b8ed4a719e40f..68e7be7ff3763 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "hir" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "A high-level object oriented access to Rust code." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml index 422b030c27d28..a236a01928cf3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "ide-assists" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Code assists for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml index 041f937058c90..bc1d35f715d06 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "ide-completion" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Utilities for generating completions of user input." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 18e77bf7ccc0f..7eeb36cebb377 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "ide-db" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Core data-structure representing IDE state." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index ad561b6b803df..2c7dd9c62d41f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "ide-diagnostics" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Diagnostics rendering and fixits." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index 6d0813e4d5d05..016ed1997d5c6 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "ide" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "IDE-centric APIs for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index eda5e3de16ff1..3152a0281c514 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "intern" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Global `Arc`-based object interning infrastructure." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/limit/Cargo.toml b/src/tools/rust-analyzer/crates/limit/Cargo.toml index 27fe4329094dc..aafde2b74a123 100644 --- a/src/tools/rust-analyzer/crates/limit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/limit/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "limit" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "A struct to enforce limits." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index 5188138183e75..e9be7f43aa125 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "load-cargo" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Loads a Cargo project into a static instance of analysis." rust-version.workspace = true diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index ccf4c7670a356..f1623ca1fc0de 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "mbe" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Handling of `macro_rules` macros for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml index 050eb0252f3c7..d3e763affc68d 100644 --- a/src/tools/rust-analyzer/crates/parser/Cargo.toml +++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "parser" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "The Rust parser for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index 938f1db5b567d..0d50d1598ff5d 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "paths" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Path wrappers for absolute and relative paths." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index d8c1e0afa44ac..39585ed644ba4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "proc-macro-api" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Client-side proc-macros for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index 5d56eb695699a..f9f203209685b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "proc-macro-srv-cli" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "A standalone binary for `proc-macro-srv`." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 028593db9c59b..8c760bf8490b1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "proc-macro-srv" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Proc-macro server for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 1fdf53c9611d4..b1a4c6f41052a 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "profile" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "A collection of tools for profiling rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 368dd94ec341b..46cd7a1e3c2f2 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "project-model" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "A representation for a Cargo project for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/span/Cargo.toml b/src/tools/rust-analyzer/crates/span/Cargo.toml index 3ccf00872e0ca..d07286032016b 100644 --- a/src/tools/rust-analyzer/crates/span/Cargo.toml +++ b/src/tools/rust-analyzer/crates/span/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "span" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "File and span related types for rust-analyzer." rust-version.workspace = true diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index a9ab64374204a..7b6c54eff00e1 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "stdx" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Missing batteries for standard libraries for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml index d40722ffe4aa3..2d64893434261 100644 --- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "test-utils" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Assorted testing utilities for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml index 1b9008ddc6404..d8d72dcde3a8f 100644 --- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "text-edit" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Representation of a `TextEdit` for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml index 451dcadac5f34..a5eb66e53a390 100644 --- a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml +++ b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "toolchain" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Discovery of `cargo` & `rustc` executables for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 280d052f8e8ab..092faeeef20bc 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "tt" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "A `TokenTree` data structure for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index c724a5db7b65d..3b22952e2b294 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "vfs-notify" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "Implementation of `loader::Handle` for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml index b1a3bf5edb48e..6653863d92d85 100644 --- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "vfs" version = "0.0.0" +repository = "https://github.com/rust-lang/rust-analyzer" description = "A virtual file system for rust-analyzer." authors.workspace = true From a22691afd9e47377cc0a22116de9bef54db3eda8 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Tue, 30 Jul 2024 14:11:45 +0200 Subject: [PATCH 032/178] Fix obsolete repository URL in `[package.repository]` of `rust-analyzer` crate --- src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index c07f9ee506a1f..3983e52cd10c9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -2,7 +2,7 @@ name = "rust-analyzer" version = "0.0.0" homepage = "https://rust-analyzer.github.io/" -repository = "https://github.com/rust-analyzer/rust-analyzer" +repository = "https://github.com/rust-lang/rust-analyzer" description = "A language server for the Rust programming language" documentation = "https://rust-analyzer.github.io/manual.html" autobins = false From cc67602501c7d057bb9db6c25020841992fc5eab Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Tue, 30 Jul 2024 14:27:22 +0200 Subject: [PATCH 033/178] Fix spelling of "object-oriented" --- src/tools/rust-analyzer/crates/hir/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index 68e7be7ff3763..aeb28ae0b9941 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -2,7 +2,7 @@ name = "hir" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "A high-level object oriented access to Rust code." +description = "A high-level object-oriented access to Rust code." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 1c5e4ce4b533c..67fbe3b789c49 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1,4 +1,4 @@ -//! HIR (previously known as descriptors) provides a high-level object oriented +//! HIR (previously known as descriptors) provides a high-level object-oriented //! access to Rust code. //! //! The principal difference between HIR and syntax trees is that HIR is bound From 54e68e6adfb5bae1d8b887621dee284005ab61f6 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Tue, 30 Jul 2024 14:28:13 +0200 Subject: [PATCH 034/178] Fix spelling of "data structure" --- src/tools/rust-analyzer/crates/ide-db/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-db/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 7eeb36cebb377..1d395f1c2d578 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -2,7 +2,7 @@ name = "ide-db" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Core data-structure representing IDE state." +description = "Core data structure representing IDE state." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index cb1cf793d4291..3cf29987fa14c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -1,4 +1,4 @@ -//! This crate defines the core datastructure representing IDE state -- `RootDatabase`. +//! This crate defines the core data structure representing IDE state -- `RootDatabase`. //! //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. From ccc4177911198d1492908477af459b7adcf5972a Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Sat, 3 Aug 2024 17:40:12 +0200 Subject: [PATCH 035/178] Replace "`ra_ap_ide`" with "`ide` (aka `ra_ap_ide`)" --- src/tools/rust-analyzer/crates/base-db/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index b949de8dc33d4..8e3f38590cdc3 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -2,7 +2,7 @@ name = "base-db" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Basic database traits. The concrete DB is defined by `ra_ap_ide`." +description = "Basic database traits. The concrete DB is defined by `ide` (aka `ra_ap_ide`)." authors.workspace = true edition.workspace = true From 08d3d44c7d5f29912e7d8b3de8cfa5cd623f3b95 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Sat, 3 Aug 2024 17:48:46 +0200 Subject: [PATCH 036/178] Apply Veykril's change suggestions --- src/tools/rust-analyzer/crates/hir-def/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/syntax/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 9ad34fa4a8439..3a5a7c3e70e11 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -2,7 +2,7 @@ name = "hir-def" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Everything between macro expansion and type inference for rust-analyzer." +description = "RPC Api for the `proc-macro-srv` crate of rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 39585ed644ba4..8f74c7c6e4d43 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -2,7 +2,7 @@ name = "proc-macro-api" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Client-side proc-macros for rust-analyzer." +description = "RPC Api for the `proc-macro-srv` crate of rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index f9f203209685b..12f4d9d963aac 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -2,7 +2,7 @@ name = "proc-macro-srv-cli" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "A standalone binary for `proc-macro-srv`." +description = "A standalone binary for the `proc-macro-srv` crate of rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 757ae31ee847b..ed99bc794f8ab 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -2,7 +2,7 @@ name = "syntax" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Comment and whitespace preserving parser for the Rust language" +description = "Concrete syntax tree definitions for rust-analyzer." authors.workspace = true edition.workspace = true From e15ffa5629a65278904f65b4c442a2aeeba59859 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Sat, 3 Aug 2024 17:51:31 +0200 Subject: [PATCH 037/178] Unify package descriptions by adding references to "rust-analyzer" With the lack of a README on the individually published library crates and the somewhat cryptic `ra_ap_` prefix it is hard to figure out where those crates belong to, so mentioning "rust-analyzer" feels like auseful hint there. --- src/tools/rust-analyzer/crates/base-db/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/cfg/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/flycheck/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-completion/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-db/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/intern/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/limit/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/load-cargo/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/paths/Cargo.toml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index 8e3f38590cdc3..f5f51650fa832 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -2,7 +2,7 @@ name = "base-db" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Basic database traits. The concrete DB is defined by `ide` (aka `ra_ap_ide`)." +description = "Basic database traits for rust-analyzer. The concrete DB is defined by `ide` (aka `ra_ap_ide`)." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index cfdd80de9e07a..6a8a638d9ec68 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -2,7 +2,7 @@ name = "cfg" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Conditional compiling options, `cfg` attribute parser and evaluator." +description = "Conditional compiling options, `cfg` attribute parser and evaluator for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml index affa9a295086e..d3b3ffc11f031 100644 --- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml +++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml @@ -2,7 +2,7 @@ name = "flycheck" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Functionality needed to run `cargo` commands in a background thread." +description = "Functionality needed for rust-analyzer to run `cargo` commands in a background thread." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index aeb28ae0b9941..9689ca94a51ac 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -2,7 +2,7 @@ name = "hir" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "A high-level object-oriented access to Rust code." +description = "A high-level object-oriented access to Rust code for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml index bc1d35f715d06..a0cf6ec4f898c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml @@ -2,7 +2,7 @@ name = "ide-completion" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Utilities for generating completions of user input." +description = "Utilities for generating completions of user input for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 1d395f1c2d578..275542ab4d2c2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -2,7 +2,7 @@ name = "ide-db" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Core data structure representing IDE state." +description = "Core data structure representing IDE state for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index 2c7dd9c62d41f..1a3e14c7c7494 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -2,7 +2,7 @@ name = "ide-diagnostics" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Diagnostics rendering and fixits." +description = "Diagnostics rendering and fixits for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml index 78d1ca6ec871d..9c6f69a8eaa67 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml @@ -2,7 +2,7 @@ name = "ide-ssr" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Structural search and replace of Rust code." +description = "Structural search and replace of Rust code for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index 3152a0281c514..bd9032e05127a 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -2,7 +2,7 @@ name = "intern" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Global `Arc`-based object interning infrastructure." +description = "Global `Arc`-based object interning infrastructure for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/limit/Cargo.toml b/src/tools/rust-analyzer/crates/limit/Cargo.toml index aafde2b74a123..634c3b3513903 100644 --- a/src/tools/rust-analyzer/crates/limit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/limit/Cargo.toml @@ -2,7 +2,7 @@ name = "limit" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "A struct to enforce limits." +description = "A struct to enforce limits for rust-analyzer." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index e9be7f43aa125..fde42ef871af0 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -2,7 +2,7 @@ name = "load-cargo" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Loads a Cargo project into a static instance of analysis." +description = "Loads a Cargo project into a static instance of rust-analyzer for analysis." rust-version.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index 0d50d1598ff5d..c6a5e08bcd2a2 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -2,7 +2,7 @@ name = "paths" version = "0.0.0" repository = "https://github.com/rust-lang/rust-analyzer" -description = "Path wrappers for absolute and relative paths." +description = "Path wrappers for absolute and relative paths rust-analyzer." authors.workspace = true edition.workspace = true From 584f9543ea976ea4380bc542d5325f14940c4be8 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Tue, 6 Aug 2024 00:23:22 +0200 Subject: [PATCH 038/178] =?UTF-8?q?Replace=20`[package.repository]=20=3D?= =?UTF-8?q?=20"=E2=80=A6"`=20of=20published=20crates=20with=20`[package.re?= =?UTF-8?q?pository.workspace]=20=3D=20true`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/rust-analyzer/Cargo.toml | 1 + src/tools/rust-analyzer/crates/base-db/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/cfg/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/flycheck/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir-def/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir-expand/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir-ty/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-assists/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-completion/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-db/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/ide/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/intern/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/limit/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/load-cargo/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/mbe/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/parser/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/paths/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/profile/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/project-model/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/span/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/stdx/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/syntax/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/test-utils/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/text-edit/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/toolchain/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/tt/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/vfs/Cargo.toml | 2 +- 35 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index d56d941dbff98..4e8348406984b 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -8,6 +8,7 @@ rust-version = "1.78" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] +repository = "https://github.com/rust-lang/rust-analyzer" [profile.dev] debug = 1 diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index f5f51650fa832..b17b08a720c99 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "base-db" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Basic database traits for rust-analyzer. The concrete DB is defined by `ide` (aka `ra_ap_ide`)." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index 6a8a638d9ec68..29b7ad6f8fe2a 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cfg" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Conditional compiling options, `cfg` attribute parser and evaluator for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml index d3b3ffc11f031..a7f6e296d3b0d 100644 --- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml +++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "flycheck" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Functionality needed for rust-analyzer to run `cargo` commands in a background thread." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 3a5a7c3e70e11..5b9d227e3152c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hir-def" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "RPC Api for the `proc-macro-srv` crate of rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index 1fabbda7f3eff..03a9d54d2e652 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hir-expand" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Macro expansion for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 0b39f71a3c8c3..b079b5675bd9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hir-ty" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "The type system for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index 9689ca94a51ac..324fa1c6a8571 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hir" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "A high-level object-oriented access to Rust code for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml index a236a01928cf3..df52562b6a102 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide-assists" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Code assists for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml index a0cf6ec4f898c..035b2fc0db0c4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide-completion" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Utilities for generating completions of user input for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 275542ab4d2c2..6714a99f80eb4 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide-db" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Core data structure representing IDE state for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index 1a3e14c7c7494..9c3a279a945dd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide-diagnostics" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Diagnostics rendering and fixits for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml index 9c6f69a8eaa67..fad62fa3b96b8 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide-ssr" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Structural search and replace of Rust code for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index 016ed1997d5c6..d976d604f1aa9 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ide" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "IDE-centric APIs for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index bd9032e05127a..5e7ee54c6af77 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "intern" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Global `Arc`-based object interning infrastructure for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/limit/Cargo.toml b/src/tools/rust-analyzer/crates/limit/Cargo.toml index 634c3b3513903..30666f5219af3 100644 --- a/src/tools/rust-analyzer/crates/limit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/limit/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "limit" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "A struct to enforce limits for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index fde42ef871af0..23fd50a05644c 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "load-cargo" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Loads a Cargo project into a static instance of rust-analyzer for analysis." rust-version.workspace = true diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index f1623ca1fc0de..756d42ef573fc 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "mbe" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Handling of `macro_rules` macros for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml index d3e763affc68d..d5255665b46a1 100644 --- a/src/tools/rust-analyzer/crates/parser/Cargo.toml +++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "parser" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "The Rust parser for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index c6a5e08bcd2a2..d4b0a54ed6446 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "paths" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Path wrappers for absolute and relative paths rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 8f74c7c6e4d43..84b877f026b77 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "proc-macro-api" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "RPC Api for the `proc-macro-srv` crate of rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index 12f4d9d963aac..1c394513c459d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "proc-macro-srv-cli" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "A standalone binary for the `proc-macro-srv` crate of rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 8c760bf8490b1..e8d9677c92863 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "proc-macro-srv" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Proc-macro server for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index b1a4c6f41052a..2e3413f339b63 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "profile" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "A collection of tools for profiling rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 46cd7a1e3c2f2..68e0e1ba55430 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "project-model" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "A representation for a Cargo project for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 3983e52cd10c9..6aa2789c5eb7d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -2,7 +2,7 @@ name = "rust-analyzer" version = "0.0.0" homepage = "https://rust-analyzer.github.io/" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "A language server for the Rust programming language" documentation = "https://rust-analyzer.github.io/manual.html" autobins = false diff --git a/src/tools/rust-analyzer/crates/span/Cargo.toml b/src/tools/rust-analyzer/crates/span/Cargo.toml index d07286032016b..3381dac0b4255 100644 --- a/src/tools/rust-analyzer/crates/span/Cargo.toml +++ b/src/tools/rust-analyzer/crates/span/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "span" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "File and span related types for rust-analyzer." rust-version.workspace = true diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index 7b6c54eff00e1..77c9f3cb1486c 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "stdx" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Missing batteries for standard libraries for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index ed99bc794f8ab..994c21469ff45 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "syntax" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Concrete syntax tree definitions for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml index 2d64893434261..b1457722a922e 100644 --- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "test-utils" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Assorted testing utilities for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml index d8d72dcde3a8f..dc6b3d31a09f4 100644 --- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "text-edit" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Representation of a `TextEdit` for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml index a5eb66e53a390..87e8efb20fc6f 100644 --- a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml +++ b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "toolchain" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Discovery of `cargo` & `rustc` executables for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 092faeeef20bc..82e7c24668fe6 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tt" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "A `TokenTree` data structure for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index 3b22952e2b294..d2f7079915ffc 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "vfs-notify" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "Implementation of `loader::Handle` for rust-analyzer." authors.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml index 6653863d92d85..c6f34e2af082c 100644 --- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "vfs" version = "0.0.0" -repository = "https://github.com/rust-lang/rust-analyzer" +repository.workspace = true description = "A virtual file system for rust-analyzer." authors.workspace = true From 6c1d83b725e1c8374e70612fc112235001885846 Mon Sep 17 00:00:00 2001 From: Nicolas Guichard Date: Fri, 2 Aug 2024 19:55:37 +0200 Subject: [PATCH 039/178] Include vendored crates in StaticIndex StaticIndex::compute filters out modules from libraries. This makes an exceptions for vendored libraries, ie libraries actually defined inside the workspace being indexed. This aims to solve https://bugzilla.mozilla.org/show_bug.cgi?id=1846041 In general StaticIndex is meant for code browsers, which likely want to index all visible source files. --- .../crates/ide/src/static_index.rs | 46 +++++++++++++++---- .../crates/rust-analyzer/src/cli/lsif.rs | 3 +- .../crates/rust-analyzer/src/cli/scip.rs | 29 ++++++------ 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index f688adff28d9f..eaccee08e8c01 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -3,9 +3,12 @@ use hir::{db::HirDatabase, Crate, HirFileIdExt, Module, Semantics}; use ide_db::{ - base_db::SourceRootDatabase, defs::Definition, documentation::Documentation, - famous_defs::FamousDefs, helpers::get_definition, FileId, FileRange, FxHashMap, FxHashSet, - RootDatabase, + base_db::{SourceRootDatabase, VfsPath}, + defs::Definition, + documentation::Documentation, + famous_defs::FamousDefs, + helpers::get_definition, + FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, }; use syntax::{AstNode, SyntaxKind::*, SyntaxNode, TextRange, T}; @@ -227,13 +230,16 @@ impl StaticIndex<'_> { self.files.push(result); } - pub fn compute(analysis: &Analysis) -> StaticIndex<'_> { + pub fn compute<'a>(analysis: &'a Analysis, workspace_root: &VfsPath) -> StaticIndex<'a> { let db = &*analysis.db; let work = all_modules(db).into_iter().filter(|module| { let file_id = module.definition_source_file_id(db).original_file(db); let source_root = db.file_source_root(file_id.into()); let source_root = db.source_root(source_root); - !source_root.is_library + let is_vendored = source_root + .path_for_file(&file_id.into()) + .is_some_and(|module_path| module_path.starts_with(workspace_root)); + !source_root.is_library || is_vendored }); let mut this = StaticIndex { files: vec![], @@ -259,12 +265,13 @@ impl StaticIndex<'_> { #[cfg(test)] mod tests { use crate::{fixture, StaticIndex}; - use ide_db::{FileRange, FxHashSet}; + use ide_db::{base_db::VfsPath, FileRange, FxHashSet}; use syntax::TextSize; fn check_all_ranges(ra_fixture: &str) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); - let s = StaticIndex::compute(&analysis); + let s = + StaticIndex::compute(&analysis, &VfsPath::new_virtual_path("/workspace".to_owned())); let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect(); for f in s.files { for (range, _) in f.tokens { @@ -283,7 +290,8 @@ mod tests { #[track_caller] fn check_definitions(ra_fixture: &str) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); - let s = StaticIndex::compute(&analysis); + let s = + StaticIndex::compute(&analysis, &VfsPath::new_virtual_path("/workspace".to_owned())); let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect(); for (_, t) in s.tokens.iter() { if let Some(t) = t.definition { @@ -326,7 +334,7 @@ enum E { X(Foo) } fn multi_crate() { check_definitions( r#" -//- /main.rs crate:main deps:foo +//- /workspace/main.rs crate:main deps:foo use foo::func; @@ -335,7 +343,7 @@ fn main() { //^^^^ func(); } -//- /foo/lib.rs crate:foo +//- /workspace/foo/lib.rs crate:foo pub func() { @@ -344,6 +352,24 @@ pub func() { ); } + #[test] + fn vendored_crate() { + check_all_ranges( + r#" +//- /workspace/main.rs crate:main deps:external,vendored +struct Main(i32); + //^^^^ ^^^ + +//- /external/lib.rs new_source_root:library crate:external@0.1.0,https://a.b/foo.git library +struct ExternalLibrary(i32); + +//- /workspace/vendored/lib.rs new_source_root:library crate:vendored@0.1.0,https://a.b/bar.git library +struct VendoredLibrary(i32); + //^^^^^^^^^^^^^^^ ^^^ +"#, + ); + } + #[test] fn derives() { check_all_ranges( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index 3ff9be7102fbc..e67ddb9d060c8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -287,6 +287,7 @@ impl flags::Lsif { let manifest = ProjectManifest::discover_single(&path)?; let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; + let root = workspace.workspace_root().to_owned(); let (db, vfs, _proc_macro) = load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?; @@ -294,7 +295,7 @@ impl flags::Lsif { let db = host.raw_database(); let analysis = host.analysis(); - let si = StaticIndex::compute(&analysis); + let si = StaticIndex::compute(&analysis, &root.into()); let mut lsif = LsifManager::new(&analysis, db, &vfs); lsif.add_vertex(lsif::Vertex::MetaData(lsif::MetaData { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index ee134b6c507e8..2fc0ef8d4daac 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -63,7 +63,7 @@ impl flags::Scip { let db = host.raw_database(); let analysis = host.analysis(); - let si = StaticIndex::compute(&analysis); + let si = StaticIndex::compute(&analysis, &root.clone().into()); let metadata = scip_types::Metadata { version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(), @@ -334,6 +334,7 @@ mod test { use ide::{FilePosition, TextSize}; use scip::symbol::format_symbol; use test_fixture::ChangeFixture; + use vfs::VfsPath; fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) { let mut host = AnalysisHost::default(); @@ -351,7 +352,8 @@ mod test { let (host, position) = position(ra_fixture); let analysis = host.analysis(); - let si = StaticIndex::compute(&analysis); + let si = + StaticIndex::compute(&analysis, &VfsPath::new_virtual_path("/workspace".to_owned())); let FilePosition { file_id, offset } = position; @@ -384,7 +386,7 @@ mod test { fn basic() { check_symbol( r#" -//- /lib.rs crate:main deps:foo +//- /workspace/lib.rs crate:main deps:foo use foo::example_mod::func; fn main() { func$0(); @@ -485,7 +487,7 @@ pub mod module { fn symbol_for_field() { check_symbol( r#" - //- /lib.rs crate:main deps:foo + //- /workspace/lib.rs crate:main deps:foo use foo::St; fn main() { let x = St { a$0: 2 }; @@ -503,7 +505,7 @@ pub mod module { fn symbol_for_param() { check_symbol( r#" -//- /lib.rs crate:main deps:foo +//- /workspace/lib.rs crate:main deps:foo use foo::example_mod::func; fn main() { func(42); @@ -521,7 +523,7 @@ pub mod example_mod { fn symbol_for_closure_param() { check_symbol( r#" -//- /lib.rs crate:main deps:foo +//- /workspace/lib.rs crate:main deps:foo use foo::example_mod::func; fn main() { func(); @@ -541,7 +543,7 @@ pub mod example_mod { fn local_symbol_for_local() { check_symbol( r#" - //- /lib.rs crate:main deps:foo + //- /workspace/lib.rs crate:main deps:foo use foo::module::func; fn main() { func(); @@ -561,13 +563,13 @@ pub mod example_mod { fn global_symbol_for_pub_struct() { check_symbol( r#" - //- /lib.rs crate:main + //- /workspace/lib.rs crate:main mod foo; fn main() { let _bar = foo::Bar { i: 0 }; } - //- /foo.rs + //- /workspace/foo.rs pub struct Bar$0 { pub i: i32, } @@ -580,13 +582,13 @@ pub mod example_mod { fn global_symbol_for_pub_struct_reference() { check_symbol( r#" - //- /lib.rs crate:main + //- /workspace/lib.rs crate:main mod foo; fn main() { let _bar = foo::Bar$0 { i: 0 }; } - //- /foo.rs + //- /workspace/foo.rs pub struct Bar { pub i: i32, } @@ -599,7 +601,7 @@ pub mod example_mod { fn symbol_for_for_type_alias() { check_symbol( r#" - //- /lib.rs crate:main + //- /workspace/lib.rs crate:main pub type MyTypeAlias$0 = u8; "#, "rust-analyzer cargo main . MyTypeAlias#", @@ -615,7 +617,8 @@ pub mod example_mod { host.raw_database_mut().apply_change(change_fixture.change); let analysis = host.analysis(); - let si = StaticIndex::compute(&analysis); + let si = + StaticIndex::compute(&analysis, &VfsPath::new_virtual_path("/workspace".to_owned())); let file = si.files.first().unwrap(); let (_, token_id) = file.tokens.first().unwrap(); From ac7c46651533765c4809f1bad6f5177f865624ef Mon Sep 17 00:00:00 2001 From: roife Date: Tue, 6 Aug 2024 21:52:43 +0800 Subject: [PATCH 040/178] fix: tyck for non-ADT types when searching refs for `Self` kw --- .../rust-analyzer/crates/ide-db/src/search.rs | 9 ++++++- .../crates/ide/src/references.rs | 25 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index b6d17ce67edf1..9e01a6d44083c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -663,9 +663,16 @@ impl<'a> FindUsages<'a> { name_ref: &ast::NameRef, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { + // See https://github.com/rust-lang/rust-analyzer/pull/15864/files/e0276dc5ddc38c65240edb408522bb869f15afb4#r1389848845 + let ty_eq = |ty: hir::Type| match (ty.as_adt(), self_ty.as_adt()) { + (Some(ty), Some(self_ty)) => ty == self_ty, + (None, None) => ty == *self_ty, + _ => false, + }; + match NameRefClass::classify(self.sema, name_ref) { Some(NameRefClass::Definition(Definition::SelfType(impl_))) - if impl_.self_ty(self.sema.db).as_adt() == self_ty.as_adt() => + if ty_eq(impl_.self_ty(self.sema.db)) => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 46c2d47ee827f..64b82b31c7417 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -60,7 +60,6 @@ pub(crate) fn find_all_refs( move |def: Definition| { let mut usages = def.usages(sema).set_scope(search_scope.as_ref()).include_self_refs().all(); - if literal_search { retain_adt_literal_usages(&mut usages, def, sema); } @@ -817,6 +816,30 @@ impl S { ) } + #[test] + fn test_self_inside_not_adt_impl() { + check( + r#" +pub trait TestTrait { + type Assoc; + fn stuff() -> Self; +} +impl TestTrait for () { + type Assoc$0 = u8; + fn stuff() -> Self { + let me: Self = (); + me + } +} +"#, + expect![[r#" + Assoc TypeAlias FileId(0) 92..108 97..102 + + FileId(0) 31..36 + "#]], + ) + } + #[test] fn test_find_all_refs_two_modules() { check( From 1b587a6e76200fdd8364ef910246efa11c973e7b Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 6 Aug 2024 14:33:14 -0700 Subject: [PATCH 041/178] alloc: add ToString specialization for `&&str` Fixes #128690 --- library/alloc/src/string.rs | 43 ++++++++++++++++---- tests/codegen/issues/str-to-string-128690.rs | 36 ++++++++++++++++ 2 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 tests/codegen/issues/str-to-string-128690.rs diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 124230812df56..d943901e9ecf9 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2643,14 +2643,41 @@ impl ToString for i8 { } } -#[doc(hidden)] -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "str_to_string_specialization", since = "1.9.0")] -impl ToString for str { - #[inline] - fn to_string(&self) -> String { - String::from(self) - } +// Generic/generated code can sometimes have multiple, nested references +// for strings, including `&&&str`s that would never be written +// by hand. This macro generates twelve layers of nested `&`-impl +// for primitive strings. +macro_rules! to_string_str { + {type ; x $($x:ident)*} => { + &to_string_str! { type ; $($x)* } + }; + {type ;} => { str }; + {impl ; x $($x:ident)*} => { + to_string_str! { $($x)* } + }; + {impl ;} => { }; + {$self:expr ; x $($x:ident)*} => { + *(to_string_str! { $self ; $($x)* }) + }; + {$self:expr ;} => { $self }; + {$($x:ident)*} => { + #[doc(hidden)] + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "str_to_string_specialization", since = "1.9.0")] + impl ToString for to_string_str!(type ; $($x)*) { + #[inline] + fn to_string(&self) -> String { + String::from(to_string_str!(self ; $($x)*)) + } + } + to_string_str! { impl ; $($x)* } + }; +} + +to_string_str! { + x x x x + x x x x + x x x x } #[doc(hidden)] diff --git a/tests/codegen/issues/str-to-string-128690.rs b/tests/codegen/issues/str-to-string-128690.rs new file mode 100644 index 0000000000000..8b416306ba66e --- /dev/null +++ b/tests/codegen/issues/str-to-string-128690.rs @@ -0,0 +1,36 @@ +//@ compile-flags: -C opt-level=3 -Z merge-functions=disabled +#![crate_type = "lib"] + +//! Make sure str::to_string is specialized not to use fmt machinery. + +// CHECK-LABEL: define {{(dso_local )?}}void @one_ref +#[no_mangle] +pub fn one_ref(input: &str) -> String { + // CHECK-NOT: {{(call|invoke).*}}fmt + input.to_string() +} + +// CHECK-LABEL: define {{(dso_local )?}}void @two_ref +#[no_mangle] +pub fn two_ref(input: &&str) -> String { + // CHECK-NOT: {{(call|invoke).*}}fmt + input.to_string() +} + +// CHECK-LABEL: define {{(dso_local )?}}void @thirteen_ref +#[no_mangle] +pub fn thirteen_ref(input: &&&&&&&&&&&&&str) -> String { + // CHECK-NOT: {{(call|invoke).*}}fmt + input.to_string() +} + +// This is a known performance cliff because of the macro-generated +// specialized impl. If this test suddenly starts failing, +// consider removing the `to_string_str!` macro in `alloc/str/string.rs`. +// +// CHECK-LABEL: define {{(dso_local )?}}void @fourteen_ref +#[no_mangle] +pub fn fourteen_ref(input: &&&&&&&&&&&&&&str) -> String { + // CHECK: {{(call|invoke).*}}fmt + input.to_string() +} From 20c833c632d76ee78284441226f12b919318bc4b Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 6 Aug 2024 18:18:15 -0700 Subject: [PATCH 042/178] diagnostics: `Box` suggestion with multiple matching impl The two altered expectation messages both seem like improvements: - `coerce-expect-unsized-ascribed.stderr` says you can go `Box -> Box`, which you can. - `upcast_soundness_bug.stderr` used to say that you could go `Box> -> Box`, which you can't, because the type parameters are missing in the destination and the only ones that work aren't what's needed. --- .../error_reporting/infer/note_and_explain.rs | 24 +++++++------------ .../coerce-expect-unsized-ascribed.stderr | 4 ++++ tests/ui/traits/upcast_soundness_bug.stderr | 1 - 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 864510bb65047..0c140f93d94b2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -313,11 +313,9 @@ impl Trait for X { (ty::Dynamic(t, _, ty::DynKind::Dyn), _) if let Some(def_id) = t.principal_def_id() => { - let mut impl_def_ids = vec![]; - tcx.for_each_relevant_impl(def_id, values.found, |did| { - impl_def_ids.push(did) - }); - if let [_] = &impl_def_ids[..] { + let has_non_blanket_impl = + tcx.non_blanket_impls_for_ty(def_id, values.found).next().is_some(); + if has_non_blanket_impl { let trait_name = tcx.item_name(def_id); diag.help(format!( "`{}` implements `{trait_name}` so you could box the found value \ @@ -330,11 +328,9 @@ impl Trait for X { (_, ty::Dynamic(t, _, ty::DynKind::Dyn)) if let Some(def_id) = t.principal_def_id() => { - let mut impl_def_ids = vec![]; - tcx.for_each_relevant_impl(def_id, values.expected, |did| { - impl_def_ids.push(did) - }); - if let [_] = &impl_def_ids[..] { + let has_non_blanket_impl = + tcx.non_blanket_impls_for_ty(def_id, values.expected).next().is_some(); + if has_non_blanket_impl { let trait_name = tcx.item_name(def_id); diag.help(format!( "`{}` implements `{trait_name}` so you could change the expected \ @@ -346,11 +342,9 @@ impl Trait for X { (ty::Dynamic(t, _, ty::DynKind::DynStar), _) if let Some(def_id) = t.principal_def_id() => { - let mut impl_def_ids = vec![]; - tcx.for_each_relevant_impl(def_id, values.found, |did| { - impl_def_ids.push(did) - }); - if let [_] = &impl_def_ids[..] { + let has_non_blanket_impl = + tcx.non_blanket_impls_for_ty(def_id, values.found).next().is_some(); + if has_non_blanket_impl { let trait_name = tcx.item_name(def_id); diag.help(format!( "`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \ diff --git a/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr b/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr index 646044ae41abc..0c220a13876b8 100644 --- a/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr +++ b/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr @@ -42,6 +42,7 @@ LL | let _ = type_ascribe!(Box::new( if true { false } else { true }), Box` found struct `Box` + = help: `bool` implements `Debug` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:16:27 @@ -51,6 +52,7 @@ LL | let _ = type_ascribe!(Box::new( match true { true => 'a', false => 'b' | = note: expected struct `Box` found struct `Box` + = help: `char` implements `Debug` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:18:27 @@ -96,6 +98,7 @@ LL | let _ = type_ascribe!(&if true { false } else { true }, &dyn Debug); | = note: expected reference `&dyn Debug` found reference `&bool` + = help: `bool` implements `Debug` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:24:27 @@ -105,6 +108,7 @@ LL | let _ = type_ascribe!(&match true { true => 'a', false => 'b' }, &dyn D | = note: expected reference `&dyn Debug` found reference `&char` + = help: `char` implements `Debug` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:26:27 diff --git a/tests/ui/traits/upcast_soundness_bug.stderr b/tests/ui/traits/upcast_soundness_bug.stderr index 5864abcdb41f5..31f77b52b5fbf 100644 --- a/tests/ui/traits/upcast_soundness_bug.stderr +++ b/tests/ui/traits/upcast_soundness_bug.stderr @@ -6,7 +6,6 @@ LL | let p = p as *const dyn Trait; // <- this is bad! | = note: expected trait object `dyn Trait` found trait object `dyn Trait` - = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error: aborting due to 1 previous error From 7af01f7c90e683bbf24203452acac548914d6125 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Wed, 7 Aug 2024 03:27:03 +0200 Subject: [PATCH 043/178] Allow rust-project.json to be hidden --- src/tools/rust-analyzer/crates/project-model/src/lib.rs | 6 ++++++ .../crates/rust-analyzer/tests/slow-tests/main.rs | 2 +- src/tools/rust-analyzer/editors/code/package.json | 8 +++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index 6510ced66ac27..32280d5c763cb 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -68,6 +68,9 @@ impl ProjectManifest { if path.file_name().unwrap_or_default() == "rust-project.json" { return Ok(ProjectManifest::ProjectJson(path)); } + if path.file_name().unwrap_or_default() == ".rust-project.json" { + return Ok(ProjectManifest::ProjectJson(path)); + } if path.file_name().unwrap_or_default() == "Cargo.toml" { return Ok(ProjectManifest::CargoToml(path)); } @@ -94,6 +97,9 @@ impl ProjectManifest { if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { return Ok(vec![ProjectManifest::ProjectJson(project_json)]); } + if let Some(project_json) = find_in_parent_dirs(path, ".rust-project.json") { + return Ok(vec![ProjectManifest::ProjectJson(project_json)]); + } return find_cargo_toml(path) .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 9d292c0411e4f..54cd27f4b3bc3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -750,7 +750,7 @@ fn test_missing_module_code_action_in_json_project() { let code = format!( r#" -//- /rust-project.json +//- /.rust-project.json {project} //- /src/lib.rs diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index abb40ac35ede3..bf9c4a366d422 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -71,7 +71,9 @@ "workspaceContains:Cargo.toml", "workspaceContains:*/Cargo.toml", "workspaceContains:rust-project.json", - "workspaceContains:*/rust-project.json" + "workspaceContains:*/rust-project.json", + "workspaceContains:.rust-project.json", + "workspaceContains:*/.rust-project.json" ], "main": "./out/main", "contributes": { @@ -3211,6 +3213,10 @@ { "fileMatch": "rust-project.json", "url": "https://json.schemastore.org/rust-project.json" + }, + { + "fileMatch": ".rust-project.json", + "url": "https://json.schemastore.org/rust-project.json" } ], "walkthroughs": [ From dbf2c126a30508fabb75eb03c8147efccd3ba11d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Aug 2024 14:27:59 +0200 Subject: [PATCH 044/178] Remove unnecessary CfgFlag definition in project-model --- .../rust-analyzer/crates/base-db/src/input.rs | 17 + src/tools/rust-analyzer/crates/cfg/src/lib.rs | 8 + ...build_scripts.rs => build_dependencies.rs} | 379 +++++++++--------- .../crates/project-model/src/cfg.rs | 100 ----- .../crates/project-model/src/lib.rs | 45 ++- .../crates/project-model/src/project_json.rs | 35 +- .../crates/project-model/src/rustc_cfg.rs | 15 +- .../crates/project-model/src/workspace.rs | 30 +- 8 files changed, 310 insertions(+), 319 deletions(-) rename src/tools/rust-analyzer/crates/project-model/src/{build_scripts.rs => build_dependencies.rs} (91%) delete mode 100644 src/tools/rust-analyzer/crates/project-model/src/cfg.rs diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 460581f4a6c06..3616fa9fd8684 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -690,6 +690,14 @@ impl Env { pub fn extend_from_other(&mut self, other: &Env) { self.entries.extend(other.entries.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); } + + pub fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + pub fn insert(&mut self, k: impl Into, v: impl Into) -> Option { + self.entries.insert(k.into(), v.into()) + } } impl From for Vec<(String, String)> { @@ -700,6 +708,15 @@ impl From for Vec<(String, String)> { } } +impl<'a> IntoIterator for &'a Env { + type Item = (&'a String, &'a String); + type IntoIter = std::collections::hash_map::Iter<'a, String, String>; + + fn into_iter(self) -> Self::IntoIter { + self.entries.iter() + } +} + #[derive(Debug)] pub struct CyclicDependenciesError { path: Vec<(CrateId, Option)>, diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 6d46dfb99949b..e9daaf7de3c3b 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -108,6 +108,14 @@ impl<'a> IntoIterator for &'a CfgOptions { } } +impl FromIterator for CfgOptions { + fn from_iter>(iter: T) -> Self { + let mut options = CfgOptions::default(); + options.extend(iter); + options + } +} + #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CfgDiff { // Invariants: No duplicates, no atom that's both in `enable` and `disable`. diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs similarity index 91% rename from src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs rename to src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 40e66e00b68f5..e7a4b8f39f7a5 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -1,14 +1,16 @@ -//! Workspace information we get from cargo consists of two pieces. The first is -//! the output of `cargo metadata`. The second is the output of running -//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc -//! macro. +//! Logic to invoke `cargo` for building build-dependencies (build scripts and proc-macros) as well as +//! executing the build scripts to fetch required dependency information (`OUT_DIR` env var, extra +//! cfg flags, etc). //! -//! This module implements this second part. We use "build script" terminology -//! here, but it covers procedural macros as well. +//! In essence this just invokes `cargo` with the appropriate output format which we consume, +//! but if enabled we will also use `RUSTC_WRAPPER` to only compile the build scripts and +//! proc-macros and skip everything else. -use std::{cell::RefCell, io, mem, path, process::Command}; +use std::{cell::RefCell, io, mem, process::Command}; +use base_db::Env; use cargo_metadata::{camino::Utf8Path, Message}; +use cfg::CfgAtom; use itertools::Itertools; use la_arena::ArenaMap; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; @@ -17,7 +19,7 @@ use serde::Deserialize; use toolchain::Tool; use crate::{ - cfg::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, + utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, InvocationStrategy, ManifestPath, Package, Sysroot, TargetKind, }; @@ -32,12 +34,12 @@ pub struct WorkspaceBuildScripts { #[derive(Debug, Clone, Default, PartialEq, Eq)] pub(crate) struct BuildScriptOutput { /// List of config flags defined by this package's build script. - pub(crate) cfgs: Vec, + pub(crate) cfgs: Vec, /// List of cargo-related environment variables with their value. /// /// If the package has a build script which defines environment variables, /// they can also be found here. - pub(crate) envs: Vec<(String, String)>, + pub(crate) envs: Env, /// Directory where a build script might place its output. pub(crate) out_dir: Option, /// Path to the proc-macro library file if this package exposes proc-macros. @@ -45,7 +47,7 @@ pub(crate) struct BuildScriptOutput { } impl BuildScriptOutput { - fn is_unchanged(&self) -> bool { + fn is_empty(&self) -> bool { self.cfgs.is_empty() && self.envs.is_empty() && self.out_dir.is_none() @@ -54,85 +56,6 @@ impl BuildScriptOutput { } impl WorkspaceBuildScripts { - fn build_command( - config: &CargoConfig, - allowed_features: &FxHashSet, - manifest_path: &ManifestPath, - sysroot: &Sysroot, - ) -> io::Result { - let mut cmd = match config.run_build_script_command.as_deref() { - Some([program, args @ ..]) => { - let mut cmd = Command::new(program); - cmd.args(args); - cmd - } - _ => { - let mut cmd = sysroot.tool(Tool::Cargo); - - cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); - cmd.args(&config.extra_args); - - cmd.arg("--manifest-path"); - cmd.arg(manifest_path); - - if let Some(target_dir) = &config.target_dir { - cmd.arg("--target-dir").arg(target_dir); - } - - // --all-targets includes tests, benches and examples in addition to the - // default lib and bins. This is an independent concept from the --target - // flag below. - if config.all_targets { - cmd.arg("--all-targets"); - } - - if let Some(target) = &config.target { - cmd.args(["--target", target]); - } - - match &config.features { - CargoFeatures::All => { - cmd.arg("--all-features"); - } - CargoFeatures::Selected { features, no_default_features } => { - if *no_default_features { - cmd.arg("--no-default-features"); - } - if !features.is_empty() { - cmd.arg("--features"); - cmd.arg( - features - .iter() - .filter(|&feat| allowed_features.contains(feat)) - .join(","), - ); - } - } - } - - if manifest_path.is_rust_manifest() { - cmd.arg("-Zscript"); - } - - cmd.arg("--keep-going"); - - cmd - } - }; - - cmd.envs(&config.extra_env); - if config.wrap_rustc_in_build_scripts { - // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use - // that to compile only proc macros and build scripts during the initial - // `cargo check`. - let myself = std::env::current_exe()?; - cmd.env("RUSTC_WRAPPER", myself); - cmd.env("RA_RUSTC_WRAPPER", "1"); - } - - Ok(cmd) - } - /// Runs the build scripts for the given workspace pub(crate) fn run_for_workspace( config: &CargoConfig, @@ -141,17 +64,19 @@ impl WorkspaceBuildScripts { sysroot: &Sysroot, ) -> io::Result { let current_dir = match &config.invocation_location { - InvocationLocation::Root(root) if config.run_build_script_command.is_some() => { - root.as_path() - } + InvocationLocation::Root(root) if config.run_build_script_command.is_some() => root, _ => workspace.workspace_root(), - } - .as_ref(); + }; let allowed_features = workspace.workspace_features(); - let cmd = - Self::build_command(config, &allowed_features, workspace.manifest_path(), sysroot)?; - Self::run_per_ws(cmd, workspace, current_dir, progress) + let cmd = Self::build_command( + config, + &allowed_features, + workspace.manifest_path(), + current_dir, + sysroot, + )?; + Self::run_per_ws(cmd, workspace, progress) } /// Runs the build scripts by invoking the configured command *once*. @@ -178,6 +103,7 @@ impl WorkspaceBuildScripts { &Default::default(), // This is not gonna be used anyways, so just construct a dummy here &ManifestPath::try_from(workspace_root.clone()).unwrap(), + current_dir, &Sysroot::empty(), )?; // NB: Cargo.toml could have been modified between `cargo metadata` and @@ -206,7 +132,6 @@ impl WorkspaceBuildScripts { let errors = Self::run_command( cmd, - current_dir.as_path().as_ref(), |package, cb| { if let Some(&(package, workspace)) = by_id.get(package) { cb(&workspaces[workspace][package].name, &mut res[workspace].outputs[package]); @@ -225,7 +150,7 @@ impl WorkspaceBuildScripts { for (idx, workspace) in workspaces.iter().enumerate() { for package in workspace.packages() { let package_build_data = &mut res[idx].outputs[package]; - if !package_build_data.is_unchanged() { + if !package_build_data.is_empty() { tracing::info!( "{}: {package_build_data:?}", workspace[package].manifest.parent(), @@ -238,10 +163,100 @@ impl WorkspaceBuildScripts { Ok(res) } + pub fn error(&self) -> Option<&str> { + self.error.as_deref() + } + + pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> { + self.outputs.get(idx) + } + + /// Assembles build script outputs for the rustc crates via `--print target-libdir`. + pub(crate) fn rustc_crates( + rustc: &CargoWorkspace, + current_dir: &AbsPath, + extra_env: &FxHashMap, + sysroot: &Sysroot, + ) -> Self { + let mut bs = WorkspaceBuildScripts::default(); + for p in rustc.packages() { + bs.outputs.insert(p, BuildScriptOutput::default()); + } + let res = (|| { + let target_libdir = (|| { + let mut cargo_config = sysroot.tool(Tool::Cargo); + cargo_config.envs(extra_env); + cargo_config + .current_dir(current_dir) + .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"]) + .env("RUSTC_BOOTSTRAP", "1"); + if let Ok(it) = utf8_stdout(cargo_config) { + return Ok(it); + } + let mut cmd = sysroot.tool(Tool::Rustc); + cmd.envs(extra_env); + cmd.args(["--print", "target-libdir"]); + utf8_stdout(cmd) + })()?; + + let target_libdir = AbsPathBuf::try_from(Utf8PathBuf::from(target_libdir)) + .map_err(|_| anyhow::format_err!("target-libdir was not an absolute path"))?; + tracing::info!("Loading rustc proc-macro paths from {target_libdir}"); + + let proc_macro_dylibs: Vec<(String, AbsPathBuf)> = std::fs::read_dir(target_libdir)? + .filter_map(|entry| { + let dir_entry = entry.ok()?; + if dir_entry.file_type().ok()?.is_file() { + let path = dir_entry.path(); + let extension = path.extension()?; + if extension == std::env::consts::DLL_EXTENSION { + let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned(); + let path = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).ok()?) + .ok()?; + return Some((name, path)); + } + } + None + }) + .collect(); + for p in rustc.packages() { + let package = &rustc[p]; + if package + .targets + .iter() + .any(|&it| matches!(rustc[it].kind, TargetKind::Lib { is_proc_macro: true })) + { + if let Some((_, path)) = proc_macro_dylibs + .iter() + .find(|(name, _)| *name.trim_start_matches("lib") == package.name) + { + bs.outputs[p].proc_macro_dylib_path = Some(path.clone()); + } + } + } + + if tracing::enabled!(tracing::Level::INFO) { + for package in rustc.packages() { + let package_build_data = &bs.outputs[package]; + if !package_build_data.is_empty() { + tracing::info!( + "{}: {package_build_data:?}", + rustc[package].manifest.parent(), + ); + } + } + } + Ok(()) + })(); + if let Err::<_, anyhow::Error>(e) = res { + bs.error = Some(e.to_string()); + } + bs + } + fn run_per_ws( cmd: Command, workspace: &CargoWorkspace, - current_dir: &path::Path, progress: &dyn Fn(String), ) -> io::Result { let mut res = WorkspaceBuildScripts::default(); @@ -257,7 +272,6 @@ impl WorkspaceBuildScripts { res.error = Self::run_command( cmd, - current_dir, |package, cb| { if let Some(&package) = by_id.get(package) { cb(&workspace[package].name, &mut outputs[package]); @@ -269,7 +283,7 @@ impl WorkspaceBuildScripts { if tracing::enabled!(tracing::Level::INFO) { for package in workspace.packages() { let package_build_data = &outputs[package]; - if !package_build_data.is_unchanged() { + if !package_build_data.is_empty() { tracing::info!( "{}: {package_build_data:?}", workspace[package].manifest.parent(), @@ -282,8 +296,7 @@ impl WorkspaceBuildScripts { } fn run_command( - mut cmd: Command, - current_dir: &path::Path, + cmd: Command, // ideally this would be something like: // with_output_for: impl FnMut(&str, dyn FnOnce(&mut BuildScriptOutput)), // but owned trait objects aren't a thing @@ -297,8 +310,7 @@ impl WorkspaceBuildScripts { e.push('\n'); }; - tracing::info!("Running build scripts in {}: {:?}", current_dir.display(), cmd); - cmd.current_dir(current_dir); + tracing::info!("Running build scripts: {:?}", cmd); let output = stdx::process::spawn_with_streaming_output( cmd, &mut |line| { @@ -316,7 +328,7 @@ impl WorkspaceBuildScripts { let cfgs = { let mut acc = Vec::new(); for cfg in &message.cfgs { - match cfg.parse::() { + match crate::parse_cfg(cfg) { Ok(it) => acc.push(it), Err(err) => { push_err(&format!( @@ -328,16 +340,14 @@ impl WorkspaceBuildScripts { } acc }; - if !message.env.is_empty() { - data.envs = mem::take(&mut message.env); - } + data.envs.extend(message.env.drain(..)); // cargo_metadata crate returns default (empty) path for // older cargos, which is not absolute, so work around that. let out_dir = mem::take(&mut message.out_dir); if !out_dir.as_str().is_empty() { let out_dir = AbsPathBuf::assert(out_dir); // inject_cargo_env(package, package_build_data); - data.envs.push(("OUT_DIR".to_owned(), out_dir.as_str().to_owned())); + data.envs.insert("OUT_DIR", out_dir.as_str()); data.out_dir = Some(out_dir); data.cfgs = cfgs; } @@ -349,7 +359,7 @@ impl WorkspaceBuildScripts { if message.target.kind.iter().any(|k| k == "proc-macro") { // Skip rmeta file if let Some(filename) = - message.filenames.iter().find(|name| is_dylib(name)) + message.filenames.iter().find(|file| is_dylib(file)) { let filename = AbsPath::assert(filename); data.proc_macro_dylib_path = Some(filename.to_owned()); @@ -383,94 +393,85 @@ impl WorkspaceBuildScripts { Ok(errors) } - pub fn error(&self) -> Option<&str> { - self.error.as_deref() - } - - pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> { - self.outputs.get(idx) - } - - pub(crate) fn rustc_crates( - rustc: &CargoWorkspace, + fn build_command( + config: &CargoConfig, + allowed_features: &FxHashSet, + manifest_path: &ManifestPath, current_dir: &AbsPath, - extra_env: &FxHashMap, sysroot: &Sysroot, - ) -> Self { - let mut bs = WorkspaceBuildScripts::default(); - for p in rustc.packages() { - bs.outputs.insert(p, BuildScriptOutput::default()); - } - let res = (|| { - let target_libdir = (|| { - let mut cargo_config = sysroot.tool(Tool::Cargo); - cargo_config.envs(extra_env); - cargo_config - .current_dir(current_dir) - .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Ok(it) = utf8_stdout(cargo_config) { - return Ok(it); + ) -> io::Result { + let mut cmd = match config.run_build_script_command.as_deref() { + Some([program, args @ ..]) => { + let mut cmd = Command::new(program); + cmd.args(args); + cmd + } + _ => { + let mut cmd = sysroot.tool(Tool::Cargo); + + cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); + cmd.args(&config.extra_args); + + cmd.arg("--manifest-path"); + cmd.arg(manifest_path); + + if let Some(target_dir) = &config.target_dir { + cmd.arg("--target-dir").arg(target_dir); } - let mut cmd = sysroot.tool(Tool::Rustc); - cmd.envs(extra_env); - cmd.args(["--print", "target-libdir"]); - utf8_stdout(cmd) - })()?; - let target_libdir = AbsPathBuf::try_from(Utf8PathBuf::from(target_libdir)) - .map_err(|_| anyhow::format_err!("target-libdir was not an absolute path"))?; - tracing::info!("Loading rustc proc-macro paths from {target_libdir}"); + // --all-targets includes tests, benches and examples in addition to the + // default lib and bins. This is an independent concept from the --target + // flag below. + if config.all_targets { + cmd.arg("--all-targets"); + } - let proc_macro_dylibs: Vec<(String, AbsPathBuf)> = std::fs::read_dir(target_libdir)? - .filter_map(|entry| { - let dir_entry = entry.ok()?; - if dir_entry.file_type().ok()?.is_file() { - let path = dir_entry.path(); - let extension = path.extension()?; - if extension == std::env::consts::DLL_EXTENSION { - let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned(); - let path = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).ok()?) - .ok()?; - return Some((name, path)); - } + if let Some(target) = &config.target { + cmd.args(["--target", target]); + } + + match &config.features { + CargoFeatures::All => { + cmd.arg("--all-features"); } - None - }) - .collect(); - for p in rustc.packages() { - let package = &rustc[p]; - if package - .targets - .iter() - .any(|&it| matches!(rustc[it].kind, TargetKind::Lib { is_proc_macro: true })) - { - if let Some((_, path)) = proc_macro_dylibs - .iter() - .find(|(name, _)| *name.trim_start_matches("lib") == package.name) - { - bs.outputs[p].proc_macro_dylib_path = Some(path.clone()); + CargoFeatures::Selected { features, no_default_features } => { + if *no_default_features { + cmd.arg("--no-default-features"); + } + if !features.is_empty() { + cmd.arg("--features"); + cmd.arg( + features + .iter() + .filter(|&feat| allowed_features.contains(feat)) + .join(","), + ); + } } } - } - if tracing::enabled!(tracing::Level::INFO) { - for package in rustc.packages() { - let package_build_data = &bs.outputs[package]; - if !package_build_data.is_unchanged() { - tracing::info!( - "{}: {package_build_data:?}", - rustc[package].manifest.parent(), - ); - } + if manifest_path.is_rust_manifest() { + cmd.arg("-Zscript"); } + + cmd.arg("--keep-going"); + + cmd } - Ok(()) - })(); - if let Err::<_, anyhow::Error>(e) = res { - bs.error = Some(e.to_string()); + }; + + cmd.current_dir(current_dir); + cmd.envs(&config.extra_env); + if config.wrap_rustc_in_build_scripts { + // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use + // that to compile only proc macros and build scripts during the initial + // `cargo check`. + let myself = std::env::current_exe()?; + cmd.env("RUSTC_WRAPPER", myself); + cmd.env("RA_RUSTC_WRAPPER", "1"); } - bs + + Ok(cmd) } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/cfg.rs deleted file mode 100644 index e921e3de722d1..0000000000000 --- a/src/tools/rust-analyzer/crates/project-model/src/cfg.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! Parsing of CfgFlags as command line arguments, as in -//! -//! rustc main.rs --cfg foo --cfg 'feature="bar"' -use std::{fmt, str::FromStr}; - -use cfg::{CfgDiff, CfgOptions}; -use intern::Symbol; -use rustc_hash::FxHashMap; -use serde::Serialize; - -#[derive(Clone, Eq, PartialEq, Debug, Serialize)] -pub enum CfgFlag { - Atom(String), - KeyValue { key: String, value: String }, -} - -impl FromStr for CfgFlag { - type Err = String; - fn from_str(s: &str) -> Result { - let res = match s.split_once('=') { - Some((key, value)) => { - if !(value.starts_with('"') && value.ends_with('"')) { - return Err(format!("Invalid cfg ({s:?}), value should be in quotes")); - } - let key = key.to_owned(); - let value = value[1..value.len() - 1].to_string(); - CfgFlag::KeyValue { key, value } - } - None => CfgFlag::Atom(s.into()), - }; - Ok(res) - } -} - -impl<'de> serde::Deserialize<'de> for CfgFlag { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom) - } -} - -impl Extend for CfgOptions { - fn extend>(&mut self, iter: T) { - for cfg_flag in iter { - match cfg_flag { - CfgFlag::Atom(it) => self.insert_atom(Symbol::intern(&it)), - CfgFlag::KeyValue { key, value } => { - self.insert_key_value(Symbol::intern(&key), Symbol::intern(&value)) - } - } - } - } -} - -impl FromIterator for CfgOptions { - fn from_iter>(iter: T) -> Self { - let mut this = CfgOptions::default(); - this.extend(iter); - this - } -} - -impl fmt::Display for CfgFlag { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - CfgFlag::Atom(atom) => f.write_str(atom), - CfgFlag::KeyValue { key, value } => { - f.write_str(key)?; - f.write_str("=")?; - f.write_str(value) - } - } - } -} - -/// A set of cfg-overrides per crate. -#[derive(Default, Debug, Clone, Eq, PartialEq)] -pub struct CfgOverrides { - /// A global set of overrides matching all crates. - pub global: CfgDiff, - /// A set of overrides matching specific crates. - pub selective: FxHashMap, -} - -impl CfgOverrides { - pub fn len(&self) -> usize { - self.global.len() + self.selective.values().map(|it| it.len()).sum::() - } - - pub fn apply(&self, cfg_options: &mut CfgOptions, name: &str) { - if !self.global.is_empty() { - cfg_options.apply_diff(self.global.clone()); - }; - if let Some(diff) = self.selective.get(name) { - cfg_options.apply_diff(diff.clone()); - }; - } -} diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index 32280d5c763cb..4fa70508bbd4d 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -15,9 +15,8 @@ //! procedural macros). //! * Lowering of concrete model to a [`base_db::CrateGraph`] -mod build_scripts; +mod build_dependencies; mod cargo_workspace; -mod cfg; mod env; mod manifest_path; pub mod project_json; @@ -41,12 +40,11 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashSet; pub use crate::{ - build_scripts::WorkspaceBuildScripts, + build_dependencies::WorkspaceBuildScripts, cargo_workspace::{ CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency, RustLibSource, Target, TargetData, TargetKind, }, - cfg::CfgOverrides, manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, sysroot::Sysroot, @@ -201,3 +199,42 @@ pub enum InvocationLocation { #[default] Workspace, } + +/// A set of cfg-overrides per crate. +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct CfgOverrides { + /// A global set of overrides matching all crates. + pub global: cfg::CfgDiff, + /// A set of overrides matching specific crates. + pub selective: rustc_hash::FxHashMap, +} + +impl CfgOverrides { + pub fn len(&self) -> usize { + self.global.len() + self.selective.values().map(|it| it.len()).sum::() + } + + pub fn apply(&self, cfg_options: &mut cfg::CfgOptions, name: &str) { + if !self.global.is_empty() { + cfg_options.apply_diff(self.global.clone()); + }; + if let Some(diff) = self.selective.get(name) { + cfg_options.apply_diff(diff.clone()); + }; + } +} + +fn parse_cfg(s: &str) -> Result { + let res = match s.split_once('=') { + Some((key, value)) => { + if !(value.starts_with('"') && value.ends_with('"')) { + return Err(format!("Invalid cfg ({s:?}), value should be in quotes")); + } + let key = intern::Symbol::intern(key); + let value = intern::Symbol::intern(&value[1..value.len() - 1]); + cfg::CfgAtom::KeyValue { key, value } + } + None => cfg::CfgAtom::Flag(intern::Symbol::intern(s)), + }; + Ok(res) +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index cf0a6ad402508..1fb9cec8e2661 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -50,12 +50,13 @@ //! rust-project.json over time via configuration request!) use base_db::{CrateDisplayName, CrateName}; +use cfg::CfgAtom; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::{de, Deserialize, Serialize}; use span::Edition; -use crate::{cfg::CfgFlag, ManifestPath, TargetKind}; +use crate::{ManifestPath, TargetKind}; /// Roots and crates that compose this Rust project. #[derive(Clone, Debug, Eq, PartialEq)] @@ -82,7 +83,7 @@ pub struct Crate { pub(crate) edition: Edition, pub(crate) version: Option, pub(crate) deps: Vec, - pub(crate) cfg: Vec, + pub(crate) cfg: Vec, pub(crate) target: Option, pub(crate) env: FxHashMap, pub(crate) proc_macro_dylib_path: Option, @@ -319,7 +320,8 @@ struct CrateData { version: Option, deps: Vec, #[serde(default)] - cfg: Vec, + #[serde(with = "cfg_")] + cfg: Vec, target: Option, #[serde(default)] env: FxHashMap, @@ -334,6 +336,33 @@ struct CrateData { build: Option, } +mod cfg_ { + use cfg::CfgAtom; + use serde::{Deserialize, Serialize}; + + pub(super) fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + let cfg: Vec = Vec::deserialize(deserializer)?; + cfg.into_iter().map(|it| crate::parse_cfg(&it).map_err(serde::de::Error::custom)).collect() + } + pub(super) fn serialize(cfg: &[CfgAtom], serializer: S) -> Result + where + S: serde::Serializer, + { + cfg.iter() + .map(|cfg| match cfg { + CfgAtom::Flag(flag) => flag.as_str().to_owned(), + CfgAtom::KeyValue { key, value } => { + format!("{}=\"{}\"", key.as_str(), value.as_str()) + } + }) + .collect::>() + .serialize(serializer) + } +} + #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] #[serde(rename = "edition")] enum EditionData { diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs index 599897f84a082..aa73ff891004b 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs @@ -1,10 +1,12 @@ //! Runs `rustc --print cfg` to get built-in cfg flags. use anyhow::Context; +use cfg::CfgAtom; +use intern::Symbol; use rustc_hash::FxHashMap; use toolchain::Tool; -use crate::{cfg::CfgFlag, utf8_stdout, ManifestPath, Sysroot}; +use crate::{utf8_stdout, ManifestPath, Sysroot}; /// Determines how `rustc --print cfg` is discovered and invoked. pub(crate) enum RustcCfgConfig<'a> { @@ -20,15 +22,15 @@ pub(crate) fn get( target: Option<&str>, extra_env: &FxHashMap, config: RustcCfgConfig<'_>, -) -> Vec { +) -> Vec { let _p = tracing::info_span!("rustc_cfg::get").entered(); - let mut res = Vec::with_capacity(6 * 2 + 1); + let mut res: Vec<_> = Vec::with_capacity(6 * 2 + 1); // Some nightly-only cfgs, which are required for stdlib - res.push(CfgFlag::Atom("target_thread_local".into())); + res.push(CfgAtom::Flag(Symbol::intern("target_thread_local"))); for ty in ["8", "16", "32", "64", "cas", "ptr"] { for key in ["target_has_atomic", "target_has_atomic_load_store"] { - res.push(CfgFlag::KeyValue { key: key.to_owned(), value: ty.into() }); + res.push(CfgAtom::KeyValue { key: Symbol::intern(key), value: Symbol::intern(ty) }); } } @@ -42,8 +44,7 @@ pub(crate) fn get( } }; - let rustc_cfgs = - rustc_cfgs.lines().map(|it| it.parse::()).collect::, _>>(); + let rustc_cfgs = rustc_cfgs.lines().map(crate::parse_cfg).collect::, _>>(); match rustc_cfgs { Ok(rustc_cfgs) => { diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 9156c8da33a95..5620dfade2d22 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -20,16 +20,15 @@ use tracing::instrument; use triomphe::Arc; use crate::{ - build_scripts::BuildScriptOutput, + build_dependencies::BuildScriptOutput, cargo_workspace::{DepKind, PackageData, RustLibSource}, - cfg::{CfgFlag, CfgOverrides}, env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, project_json::{Crate, CrateArrayIdx}, rustc_cfg::{self, RustcCfgConfig}, sysroot::{SysrootCrate, SysrootMode}, target_data_layout::{self, RustcDataLayoutConfig}, - utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package, - ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, + utf8_stdout, CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, + Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, }; use tracing::{debug, error, info}; @@ -55,7 +54,7 @@ pub struct ProjectWorkspace { /// `rustc --print cfg`. // FIXME: make this a per-crate map, as, eg, build.rs might have a // different target. - pub rustc_cfg: Vec, + pub rustc_cfg: Vec, /// The toolchain version used by this workspace. pub toolchain: Option, /// The target data layout queried for workspace. @@ -842,7 +841,7 @@ impl ProjectWorkspace { #[instrument(skip_all)] fn project_json_to_crate_graph( - rustc_cfg: Vec, + rustc_cfg: Vec, load: FileLoader<'_>, project: &ProjectJson, sysroot: &Sysroot, @@ -854,8 +853,8 @@ fn project_json_to_crate_graph( let (public_deps, libproc_macro) = sysroot_to_crate_graph(crate_graph, sysroot, rustc_cfg.clone(), load); - let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned()); - let mut cfg_cache: FxHashMap<&str, Vec> = FxHashMap::default(); + let r_a_cfg_flag = CfgAtom::Flag(sym::rust_analyzer.clone()); + let mut cfg_cache: FxHashMap<&str, Vec> = FxHashMap::default(); let idx_to_crate_id: FxHashMap = project .crates() @@ -962,7 +961,7 @@ fn cargo_to_crate_graph( rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>, cargo: &CargoWorkspace, sysroot: &Sysroot, - rustc_cfg: Vec, + rustc_cfg: Vec, override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, ) -> (CrateGraph, ProcMacroPaths) { @@ -1145,7 +1144,7 @@ fn cargo_to_crate_graph( } fn detached_file_to_crate_graph( - rustc_cfg: Vec, + rustc_cfg: Vec, load: FileLoader<'_>, detached_file: &ManifestPath, sysroot: &Sysroot, @@ -1308,11 +1307,10 @@ fn add_target_crate_root( None } else { let mut potential_cfg_options = cfg_options.clone(); - potential_cfg_options.extend( - pkg.features - .iter() - .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }), - ); + potential_cfg_options.extend(pkg.features.iter().map(|feat| CfgAtom::KeyValue { + key: sym::feature.clone(), + value: Symbol::intern(feat.0), + })); Some(potential_cfg_options) }; let cfg_options = { @@ -1378,7 +1376,7 @@ impl SysrootPublicDeps { fn sysroot_to_crate_graph( crate_graph: &mut CrateGraph, sysroot: &Sysroot, - rustc_cfg: Vec, + rustc_cfg: Vec, load: FileLoader<'_>, ) -> (SysrootPublicDeps, Option) { let _p = tracing::info_span!("sysroot_to_crate_graph").entered(); From f1f048c9c9c7fe88de91f9759adc284f4bf6b1cf Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Aug 2024 14:36:22 +0200 Subject: [PATCH 045/178] Fix cargo config get env parsing --- .../rust-analyzer/crates/project-model/src/env.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index 049acc290bbfe..ac7246acc592f 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -90,10 +90,13 @@ fn parse_output_cargo_config_env(stdout: String) -> FxHashMap { stdout .lines() .filter_map(|l| l.strip_prefix("env.")) - .filter_map(|l| { - l.split_once(" = ") - // cargo used to report it with this, keep it for a couple releases around - .or_else(|| l.split_once(".value = ")) + .filter_map(|l| l.split_once(" = ")) + .filter_map(|(k, v)| { + if k.contains('.') { + k.strip_suffix(".value").zip(Some(v)) + } else { + Some((k, v)) + } }) .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned())) .collect() From c4809d0d6ec11b63f6e17beb41b7d69dbbc5ad3d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Aug 2024 15:07:09 +0200 Subject: [PATCH 046/178] Fix unconfigured diagnostic being attached to the wrong file for modules --- .../crates/hir-def/src/nameres/collector.rs | 2 +- .../src/handlers/inactive_code.rs | 26 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 483bffc4b290b..d970dbac1c2b4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -2003,7 +2003,7 @@ impl ModCollector<'_, '_> { Err(cfg) => { self.emit_unconfigured_diagnostic( self.tree_id, - AttrOwner::TopLevel, + AttrOwner::ModItem(module_id.into()), &cfg, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs index acff811696103..1f8f805a1e296 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -42,7 +42,10 @@ mod tests { use crate::{tests::check_diagnostics_with_config, DiagnosticsConfig}; pub(crate) fn check(ra_fixture: &str) { - let config = DiagnosticsConfig::test_sample(); + let config = DiagnosticsConfig { + disabled: std::iter::once("unlinked-file".to_owned()).collect(), + ..DiagnosticsConfig::test_sample() + }; check_diagnostics_with_config(config, ra_fixture) } @@ -168,6 +171,27 @@ union FooBar { #[cfg(a)] baz: u32, //^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled } +"#, + ); + } + + #[test] + fn modules() { + check( + r#" +//- /main.rs + #[cfg(outline)] mod outline; +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: outline is disabled + + mod outline_inner; +//^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: outline_inner is disabled + + #[cfg(inline)] mod inline {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: inline is disabled + +//- /outline_inner.rs +#![cfg(outline_inner)] +//- /outline.rs "#, ); } From d366706cc3519f646fc65bcd006c2c4f1cbb250e Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Wed, 7 Aug 2024 23:33:39 +0900 Subject: [PATCH 047/178] Fix native diagnostics not working --- src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index b99a8de2fc2ef..034c49c3d5c69 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -110,7 +110,7 @@ impl DiagnosticCollection { }) { // don't signal an update if the diagnostics are the same - return; + continue; } if *old_gen < generation || generation == 0 { target.insert(file_id, (generation, diagnostics)); From e7086a35b56d75eb29c746c877794d270e8eaac5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Aug 2024 16:35:42 +0200 Subject: [PATCH 048/178] Slightly quieter fatal panics --- .../crates/rust-analyzer/src/main_loop.rs | 30 +++++++++++-------- .../crates/vfs-notify/src/lib.rs | 5 +++- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 42eb6d2e79c29..75635d1c28843 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -179,7 +179,10 @@ impl GlobalState { } } - while let Some(event) = self.next_event(&inbox) { + while let Ok(event) = self.next_event(&inbox) { + let Some(event) = event else { + anyhow::bail!("client exited without proper shutdown sequence"); + }; if matches!( &event, Event::Lsp(lsp_server::Message::Notification(Notification { method, .. })) @@ -190,7 +193,7 @@ impl GlobalState { self.handle_event(event)?; } - anyhow::bail!("client exited without proper shutdown sequence") + Err(anyhow::anyhow!("A receiver has been dropped, something panicked!")) } fn register_did_save_capability(&mut self, additional_patterns: impl Iterator) { @@ -237,37 +240,40 @@ impl GlobalState { ); } - fn next_event(&self, inbox: &Receiver) -> Option { + fn next_event( + &self, + inbox: &Receiver, + ) -> Result, crossbeam_channel::RecvError> { select! { recv(inbox) -> msg => - msg.ok().map(Event::Lsp), + return Ok(msg.ok().map(Event::Lsp)), recv(self.task_pool.receiver) -> task => - Some(Event::Task(task.unwrap())), + task.map(Event::Task), recv(self.deferred_task_queue.receiver) -> task => - Some(Event::QueuedTask(task.unwrap())), + task.map(Event::QueuedTask), recv(self.fmt_pool.receiver) -> task => - Some(Event::Task(task.unwrap())), + task.map(Event::Task), recv(self.loader.receiver) -> task => - Some(Event::Vfs(task.unwrap())), + task.map(Event::Vfs), recv(self.flycheck_receiver) -> task => - Some(Event::Flycheck(task.unwrap())), + task.map(Event::Flycheck), recv(self.test_run_receiver) -> task => - Some(Event::TestResult(task.unwrap())), + task.map(Event::TestResult), recv(self.discover_receiver) -> task => - Some(Event::DiscoverProject(task.unwrap())), + task.map(Event::DiscoverProject), } + .map(Some) } 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 = tracing::info_span!("GlobalState::handle_event", event = %event).entered(); let event_dbg_msg = format!("{event:?}"); diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index d0d3a844465d3..57e83ac0a89b2 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -103,7 +103,10 @@ impl NotifyActor { let (watcher_sender, watcher_receiver) = unbounded(); let watcher = log_notify_error(RecommendedWatcher::new( move |event| { - watcher_sender.send(event).unwrap(); + // we don't care about the error. If sending fails that usually + // means we were dropped, so unwrapping will just add to the + // panic noise. + _ = watcher_sender.send(event); }, Config::default(), )); From 3e4632d99363615d585bae18511c08d3cfc49fad Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Aug 2024 16:43:51 +0200 Subject: [PATCH 049/178] Offload diagnostics serialization to the task pool --- .../crates/rust-analyzer/src/global_state.rs | 43 +++++++++++++++++++ .../crates/rust-analyzer/src/main_loop.rs | 37 +++------------- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 12146c8ce0a86..9d064ed12260a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -560,6 +560,49 @@ impl GlobalState { fn send(&self, message: lsp_server::Message) { self.sender.send(message).unwrap() } + + pub(crate) fn publish_diagnostics( + &mut self, + uri: Url, + version: Option, + mut diagnostics: Vec, + ) { + // We put this on a separate thread to avoid blocking the main thread with serialization work + self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, { + let sender = self.sender.clone(); + move |_| { + // VSCode assumes diagnostic messages to be non-empty strings, so we need to patch + // empty diagnostics. Neither the docs of VSCode nor the LSP spec say whether + // diagnostic messages are actually allowed to be empty or not and patching this + // in the VSCode client does not work as the assertion happens in the protocol + // conversion. So this hack is here to stay, and will be considered a hack + // until the LSP decides to state that empty messages are allowed. + + // See https://github.com/rust-lang/rust-analyzer/issues/11404 + // See https://github.com/rust-lang/rust-analyzer/issues/13130 + let patch_empty = |message: &mut String| { + if message.is_empty() { + " ".clone_into(message); + } + }; + + for d in &mut diagnostics { + patch_empty(&mut d.message); + if let Some(dri) = &mut d.related_information { + for dri in dri { + patch_empty(&mut dri.message); + } + } + } + + let not = lsp_server::Notification::new( + ::METHOD.to_owned(), + lsp_types::PublishDiagnosticsParams { uri, diagnostics, version }, + ); + _ = sender.send(not.into()); + } + }); + } } impl Drop for GlobalState { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 75635d1c28843..54f718479f055 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -440,40 +440,13 @@ impl GlobalState { if let Some(diagnostic_changes) = self.diagnostics.take_changes() { for file_id in diagnostic_changes { let uri = file_id_to_url(&self.vfs.read().0, file_id); - let mut diagnostics = - self.diagnostics.diagnostics_for(file_id).cloned().collect::>(); - - // VSCode assumes diagnostic messages to be non-empty strings, so we need to patch - // empty diagnostics. Neither the docs of VSCode nor the LSP spec say whether - // diagnostic messages are actually allowed to be empty or not and patching this - // in the VSCode client does not work as the assertion happens in the protocol - // conversion. So this hack is here to stay, and will be considered a hack - // until the LSP decides to state that empty messages are allowed. - - // See https://github.com/rust-lang/rust-analyzer/issues/11404 - // See https://github.com/rust-lang/rust-analyzer/issues/13130 - let patch_empty = |message: &mut String| { - if message.is_empty() { - " ".clone_into(message); - } - }; - - for d in &mut diagnostics { - patch_empty(&mut d.message); - if let Some(dri) = &mut d.related_information { - for dri in dri { - patch_empty(&mut dri.message); - } - } - } - let version = from_proto::vfs_path(&uri) - .map(|path| self.mem_docs.get(&path).map(|it| it.version)) - .unwrap_or_default(); + .ok() + .and_then(|path| self.mem_docs.get(&path).map(|it| it.version)); - self.send_notification::( - lsp_types::PublishDiagnosticsParams { uri, diagnostics, version }, - ); + let diagnostics = + self.diagnostics.diagnostics_for(file_id).cloned().collect::>(); + self.publish_diagnostics(uri, version, diagnostics); } } From 3312f5d65244b4ccb035be7b4c61541afc211914 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 7 Aug 2024 09:06:49 -0700 Subject: [PATCH 050/178] alloc: make `to_string_str!` a bit less complex --- library/alloc/src/string.rs | 57 +++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index d943901e9ecf9..e628be1546f76 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2647,37 +2647,50 @@ impl ToString for i8 { // for strings, including `&&&str`s that would never be written // by hand. This macro generates twelve layers of nested `&`-impl // for primitive strings. -macro_rules! to_string_str { - {type ; x $($x:ident)*} => { - &to_string_str! { type ; $($x)* } - }; - {type ;} => { str }; - {impl ; x $($x:ident)*} => { - to_string_str! { $($x)* } +#[cfg(not(no_global_oom_handling))] +macro_rules! to_string_str_wrap_in_ref { + {x $($x:ident)*} => { + &to_string_str_wrap_in_ref! { $($x)* } }; - {impl ;} => { }; + {} => { str }; +} +#[cfg(not(no_global_oom_handling))] +macro_rules! to_string_expr_wrap_in_deref { {$self:expr ; x $($x:ident)*} => { - *(to_string_str! { $self ; $($x)* }) + *(to_string_expr_wrap_in_deref! { $self ; $($x)* }) }; {$self:expr ;} => { $self }; - {$($x:ident)*} => { - #[doc(hidden)] - #[cfg(not(no_global_oom_handling))] - #[stable(feature = "str_to_string_specialization", since = "1.9.0")] - impl ToString for to_string_str!(type ; $($x)*) { - #[inline] - fn to_string(&self) -> String { - String::from(to_string_str!(self ; $($x)*)) +} +#[cfg(not(no_global_oom_handling))] +macro_rules! to_string_str { + {$($($x:ident)*),+} => { + $( + #[doc(hidden)] + #[stable(feature = "str_to_string_specialization", since = "1.9.0")] + impl ToString for to_string_str_wrap_in_ref!($($x)*) { + #[inline] + fn to_string(&self) -> String { + String::from(to_string_expr_wrap_in_deref!(self ; $($x)*)) + } } - } - to_string_str! { impl ; $($x)* } + )+ }; } +#[cfg(not(no_global_oom_handling))] to_string_str! { - x x x x - x x x x - x x x x + x x x x x x x x x x x x, + x x x x x x x x x x x, + x x x x x x x x x x, + x x x x x x x x x, + x x x x x x x x, + x x x x x x x, + x x x x x x, + x x x x x, + x x x x, + x x x, + x x, + x, } #[doc(hidden)] From 1555fd9a39c7c80c4e2ec9a44ebc4227b5487932 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Wed, 7 Aug 2024 19:57:59 +0200 Subject: [PATCH 051/178] Make 'syntax-bridge' crate inherit `[package.repository]` from workspace --- src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml index b4f59ae216d7c..de4aec13e2451 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml @@ -2,6 +2,7 @@ name = "syntax-bridge" version = "0.0.0" description = "TBD" +repository.workspace = true authors.workspace = true edition.workspace = true From d8bb3c80f5e81d39730692883b6e7b47c321a78f Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Wed, 7 Aug 2024 19:54:00 +0200 Subject: [PATCH 052/178] Add missing `[package.description]` for 'syntax-bridge' crate --- src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml index de4aec13e2451..e995ff3b55b44 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "syntax-bridge" version = "0.0.0" -description = "TBD" repository.workspace = true +description = "Conversions between syntax nodes and token trees for rust-analyzer." authors.workspace = true edition.workspace = true From 1114de0c37b2c54bcb3367064925b46b8fdda530 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 8 Aug 2024 12:52:55 +0200 Subject: [PATCH 053/178] Move flycheck crate into rust-analyzer main crate --- src/tools/rust-analyzer/Cargo.lock | 20 +------- src/tools/rust-analyzer/Cargo.toml | 1 - .../rust-analyzer/crates/flycheck/Cargo.toml | 31 ------------ .../crates/rust-analyzer/Cargo.toml | 3 +- .../crates/rust-analyzer/src/config.rs | 16 +++--- .../rust-analyzer/src/diagnostics/to_proto.rs | 13 ++--- .../lib.rs => rust-analyzer/src/flycheck.rs} | 49 +++++++++---------- .../src/flycheck}/command.rs | 0 .../src/flycheck}/project_json.rs | 16 +++--- .../src/flycheck}/test_runner.rs | 12 ++--- .../crates/rust-analyzer/src/global_state.rs | 2 +- .../rust-analyzer/src/handlers/request.rs | 8 +-- .../crates/rust-analyzer/src/lib.rs | 1 + .../crates/rust-analyzer/src/main_loop.rs | 2 +- .../crates/rust-analyzer/src/reload.rs | 10 ++-- 15 files changed, 71 insertions(+), 113 deletions(-) delete mode 100644 src/tools/rust-analyzer/crates/flycheck/Cargo.toml rename src/tools/rust-analyzer/crates/{flycheck/src/lib.rs => rust-analyzer/src/flycheck.rs} (94%) rename src/tools/rust-analyzer/crates/{flycheck/src => rust-analyzer/src/flycheck}/command.rs (100%) rename src/tools/rust-analyzer/crates/{flycheck/src => rust-analyzer/src/flycheck}/project_json.rs (91%) rename src/tools/rust-analyzer/crates/{flycheck/src => rust-analyzer/src/flycheck}/test_runner.rs (93%) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 8fee2b64fd3ce..b6bf516af1542 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -423,23 +423,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "flycheck" -version = "0.0.0" -dependencies = [ - "cargo_metadata", - "crossbeam-channel", - "paths", - "process-wrap", - "project-model", - "rustc-hash", - "serde", - "serde_json", - "stdx", - "toolchain", - "tracing", -] - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1650,12 +1633,12 @@ version = "0.0.0" dependencies = [ "always-assert", "anyhow", + "cargo_metadata", "cfg", "crossbeam-channel", "dirs", "dissimilar", "expect-test", - "flycheck", "hir", "hir-def", "hir-ty", @@ -1676,6 +1659,7 @@ dependencies = [ "parser", "paths", "proc-macro-api", + "process-wrap", "profile", "project-model", "rayon", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 4e8348406984b..b2cf3451467fb 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -52,7 +52,6 @@ debug = 2 # local crates base-db = { path = "./crates/base-db", version = "0.0.0" } cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] } -flycheck = { path = "./crates/flycheck", version = "0.0.0" } hir = { path = "./crates/hir", version = "0.0.0" } hir-def = { path = "./crates/hir-def", version = "0.0.0" } hir-expand = { path = "./crates/hir-expand", version = "0.0.0" } diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml deleted file mode 100644 index a7f6e296d3b0d..0000000000000 --- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "flycheck" -version = "0.0.0" -repository.workspace = true -description = "Functionality needed for rust-analyzer to run `cargo` commands in a background thread." - -authors.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true - -[lib] -doctest = false - -[dependencies] -cargo_metadata.workspace = true -crossbeam-channel.workspace = true -tracing.workspace = true -rustc-hash.workspace = true -serde_json.workspace = true -serde.workspace = true -process-wrap.workspace = true - -# local deps -paths.workspace = true -stdx.workspace = true -toolchain.workspace = true -project-model.workspace = true - -[lints] -workspace = true \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 6aa2789c5eb7d..f0878b25dd30c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -47,9 +47,10 @@ always-assert = "0.2.0" walkdir = "2.3.2" semver.workspace = true memchr = "2.7.1" +cargo_metadata.workspace = true +process-wrap.workspace = true cfg.workspace = true -flycheck.workspace = true hir-def.workspace = true hir-ty.workspace = true hir.workspace = true diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 54573344245ed..486046c47c7bb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -7,7 +7,6 @@ use std::{fmt, iter, ops::Not, sync::OnceLock}; use cfg::{CfgAtom, CfgDiff}; use dirs::config_dir; -use flycheck::{CargoOptions, FlycheckConfig}; use hir::Symbol; use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, @@ -37,6 +36,7 @@ use vfs::{AbsPath, AbsPathBuf, VfsPath}; use crate::{ capabilities::ClientCapabilities, diagnostics::DiagnosticsMapConfig, + flycheck::{CargoOptions, FlycheckConfig}, lsp_ext::{WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope}, }; @@ -1899,7 +1899,7 @@ impl Config { *self.check_workspace(None) } - pub fn cargo_test_options(&self) -> CargoOptions { + pub(crate) fn cargo_test_options(&self) -> CargoOptions { CargoOptions { target_triples: self.cargo_target(None).clone().into_iter().collect(), all_targets: false, @@ -1915,7 +1915,7 @@ impl Config { } } - pub fn flycheck(&self) -> FlycheckConfig { + pub(crate) fn flycheck(&self) -> FlycheckConfig { match &self.check_overrideCommand(None) { Some(args) if !args.is_empty() => { let mut args = args.clone(); @@ -1925,16 +1925,18 @@ impl Config { args, extra_env: self.check_extra_env(), invocation_strategy: match self.check_invocationStrategy(None) { - InvocationStrategy::Once => flycheck::InvocationStrategy::Once, + InvocationStrategy::Once => crate::flycheck::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => { - flycheck::InvocationStrategy::PerWorkspace + crate::flycheck::InvocationStrategy::PerWorkspace } }, invocation_location: match self.check_invocationLocation(None) { InvocationLocation::Root => { - flycheck::InvocationLocation::Root(self.root_path.clone()) + crate::flycheck::InvocationLocation::Root(self.root_path.clone()) + } + InvocationLocation::Workspace => { + crate::flycheck::InvocationLocation::Workspace } - InvocationLocation::Workspace => flycheck::InvocationLocation::Workspace, }, } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs index defa464f2ba1e..208a70bc02ad9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -1,7 +1,7 @@ //! This module provides the functionality needed to convert diagnostics from //! `cargo check` json format to the LSP diagnostic format. -use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan}; +use crate::flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan}; use itertools::Itertools; use rustc_hash::FxHashMap; use stdx::format_to; @@ -17,8 +17,8 @@ use super::{DiagnosticsMapConfig, Fix}; /// Determines the LSP severity from a diagnostic fn diagnostic_severity( config: &DiagnosticsMapConfig, - level: flycheck::DiagnosticLevel, - code: Option, + level: crate::flycheck::DiagnosticLevel, + code: Option, ) -> Option { let res = match level { DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::ERROR, @@ -181,7 +181,7 @@ enum MappedRustChildDiagnostic { fn map_rust_child_diagnostic( config: &DiagnosticsMapConfig, workspace_root: &AbsPath, - rd: &flycheck::Diagnostic, + rd: &crate::flycheck::Diagnostic, snap: &GlobalStateSnapshot, ) -> MappedRustChildDiagnostic { let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); @@ -284,7 +284,7 @@ pub(crate) struct MappedRustDiagnostic { /// If the diagnostic has no primary span this will return `None` pub(crate) fn map_rust_diagnostic_to_lsp( config: &DiagnosticsMapConfig, - rd: &flycheck::Diagnostic, + rd: &crate::flycheck::Diagnostic, workspace_root: &AbsPath, snap: &GlobalStateSnapshot, ) -> Vec { @@ -537,7 +537,8 @@ mod tests { } fn check_with_config(config: DiagnosticsMapConfig, diagnostics_json: &str, expect: ExpectFile) { - let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap(); + let diagnostic: crate::flycheck::Diagnostic = + serde_json::from_str(diagnostics_json).unwrap(); let workspace_root: &AbsPath = Utf8Path::new("/test/").try_into().unwrap(); let (sender, _) = crossbeam_channel::unbounded(); let state = GlobalState::new( diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs similarity index 94% rename from src/tools/rust-analyzer/crates/flycheck/src/lib.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 3dd2a91d8fd96..a713f9ea8c5bd 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -13,43 +13,42 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::Deserialize; -pub use cargo_metadata::diagnostic::{ +pub(crate) use cargo_metadata::diagnostic::{ Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, - DiagnosticSpanMacroExpansion, }; use toolchain::Tool; mod command; -pub mod project_json; +pub(crate) mod project_json; mod test_runner; use command::{CommandHandle, ParseFromLine}; -pub use test_runner::{CargoTestHandle, CargoTestMessage, TestState, TestTarget}; +pub(crate) use test_runner::{CargoTestHandle, CargoTestMessage, TestState, TestTarget}; #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum InvocationStrategy { +pub(crate) enum InvocationStrategy { Once, #[default] PerWorkspace, } #[derive(Clone, Debug, Default, PartialEq, Eq)] -pub enum InvocationLocation { +pub(crate) enum InvocationLocation { Root(AbsPathBuf), #[default] Workspace, } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct CargoOptions { - pub target_triples: Vec, - pub all_targets: bool, - pub no_default_features: bool, - pub all_features: bool, - pub features: Vec, - pub extra_args: Vec, - pub extra_env: FxHashMap, - pub target_dir: Option, +pub(crate) struct CargoOptions { + pub(crate) target_triples: Vec, + pub(crate) all_targets: bool, + pub(crate) no_default_features: bool, + pub(crate) all_features: bool, + pub(crate) features: Vec, + pub(crate) extra_args: Vec, + pub(crate) extra_env: FxHashMap, + pub(crate) target_dir: Option, } impl CargoOptions { @@ -79,7 +78,7 @@ impl CargoOptions { } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum FlycheckConfig { +pub(crate) enum FlycheckConfig { CargoCommand { command: String, options: CargoOptions, @@ -110,7 +109,7 @@ impl fmt::Display for FlycheckConfig { /// diagnostics based on the output. /// The spawned thread is shut down when this struct is dropped. #[derive(Debug)] -pub struct FlycheckHandle { +pub(crate) struct FlycheckHandle { // XXX: drop order is significant sender: Sender, _thread: stdx::thread::JoinHandle, @@ -118,7 +117,7 @@ pub struct FlycheckHandle { } impl FlycheckHandle { - pub fn spawn( + pub(crate) fn spawn( id: usize, sender: Box, config: FlycheckConfig, @@ -137,28 +136,28 @@ impl FlycheckHandle { } /// Schedule a re-start of the cargo check worker to do a workspace wide check. - pub fn restart_workspace(&self, saved_file: Option) { + pub(crate) fn restart_workspace(&self, saved_file: Option) { self.sender.send(StateChange::Restart { package: None, saved_file }).unwrap(); } /// Schedule a re-start of the cargo check worker to do a package wide check. - pub fn restart_for_package(&self, package: String) { + pub(crate) fn restart_for_package(&self, package: String) { self.sender .send(StateChange::Restart { package: Some(package), saved_file: None }) .unwrap(); } /// Stop this cargo check worker. - pub fn cancel(&self) { + pub(crate) fn cancel(&self) { self.sender.send(StateChange::Cancel).unwrap(); } - pub fn id(&self) -> usize { + pub(crate) fn id(&self) -> usize { self.id } } -pub enum Message { +pub(crate) enum Message { /// Request adding a diagnostic with fixes included to a file AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic }, @@ -193,7 +192,7 @@ impl fmt::Debug for Message { } #[derive(Debug)] -pub enum Progress { +pub(crate) enum Progress { DidStart, DidCheckCrate(String), DidFinish(io::Result<()>), @@ -241,7 +240,7 @@ enum FlycheckStatus { Finished, } -pub const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; +pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; impl FlycheckActor { fn new( diff --git a/src/tools/rust-analyzer/crates/flycheck/src/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/command.rs similarity index 100% rename from src/tools/rust-analyzer/crates/flycheck/src/command.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/command.rs diff --git a/src/tools/rust-analyzer/crates/flycheck/src/project_json.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/project_json.rs similarity index 91% rename from src/tools/rust-analyzer/crates/flycheck/src/project_json.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/project_json.rs index b6e4495bc6d6e..58619d4754352 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/project_json.rs @@ -7,21 +7,21 @@ use project_model::ProjectJsonData; use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::command::{CommandHandle, ParseFromLine}; +use crate::flycheck::{CommandHandle, ParseFromLine}; -pub const ARG_PLACEHOLDER: &str = "{arg}"; +pub(crate) const ARG_PLACEHOLDER: &str = "{arg}"; /// A command wrapper for getting a `rust-project.json`. /// /// This is analogous to `cargo-metadata`, but for non-Cargo build systems. -pub struct Discover { +pub(crate) struct Discover { command: Vec, sender: Sender, } #[derive(PartialEq, Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] -pub enum DiscoverArgument { +pub(crate) enum DiscoverArgument { Path(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf), Buildfile(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf), } @@ -36,12 +36,12 @@ where impl Discover { /// Create a new [Discover]. - pub fn new(sender: Sender, command: Vec) -> Self { + pub(crate) fn new(sender: Sender, command: Vec) -> Self { Self { sender, command } } /// Spawn the command inside [Discover] and report progress, if any. - pub fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result { + pub(crate) fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result { let command = &self.command[0]; let args = &self.command[1..]; @@ -65,7 +65,7 @@ impl Discover { /// A handle to a spawned [Discover]. #[derive(Debug)] -pub struct DiscoverHandle { +pub(crate) struct DiscoverHandle { _handle: CommandHandle, } @@ -81,7 +81,7 @@ enum DiscoverProjectData { } #[derive(Debug, PartialEq, Clone)] -pub enum DiscoverProjectMessage { +pub(crate) enum DiscoverProjectMessage { Finished { project: ProjectJsonData, buildfile: AbsPathBuf }, Error { error: String, source: Option }, Progress { message: String }, diff --git a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/test_runner.rs similarity index 93% rename from src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/test_runner.rs index 74ebca34103aa..35419571fe553 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/test_runner.rs @@ -8,14 +8,14 @@ use paths::AbsPath; use serde::Deserialize; use toolchain::Tool; -use crate::{ +use crate::flycheck::{ command::{CommandHandle, ParseFromLine}, CargoOptions, }; #[derive(Debug, Deserialize)] #[serde(tag = "event", rename_all = "camelCase")] -pub enum TestState { +pub(crate) enum TestState { Started, Ok, Ignored, @@ -24,7 +24,7 @@ pub enum TestState { #[derive(Debug, Deserialize)] #[serde(tag = "type", rename_all = "camelCase")] -pub enum CargoTestMessage { +pub(crate) enum CargoTestMessage { Test { name: String, #[serde(flatten)] @@ -54,7 +54,7 @@ impl ParseFromLine for CargoTestMessage { } #[derive(Debug)] -pub struct CargoTestHandle { +pub(crate) struct CargoTestHandle { _handle: CommandHandle, } @@ -64,13 +64,13 @@ pub struct CargoTestHandle { // cargo test --package my-package --no-fail-fast -- module::func -Z unstable-options --format=json #[derive(Debug)] -pub enum TestTarget { +pub(crate) enum TestTarget { Workspace, Package(String), } impl CargoTestHandle { - pub fn new( + pub(crate) fn new( path: Option<&str>, options: CargoOptions, root: &AbsPath, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 9d064ed12260a..df7d75fe1ae20 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -5,8 +5,8 @@ use std::{ops::Not as _, time::Instant}; +use crate::flycheck::{self, project_json, FlycheckHandle}; use crossbeam_channel::{unbounded, Receiver, Sender}; -use flycheck::{project_json, FlycheckHandle}; use hir::ChangeWithProcMacros; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; use ide_db::base_db::{CrateId, ProcMacroPaths, SourceDatabase, SourceRootDatabase}; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index fc3a02ab1843d..5f92f045398d3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -246,15 +246,15 @@ pub(crate) fn handle_run_test( if let ProjectWorkspaceKind::Cargo { cargo, .. } = &ws.kind { let test_target = if let Some(namespace_root) = namespace_root { if let Some(package_name) = find_package_name(namespace_root, cargo) { - flycheck::TestTarget::Package(package_name) + crate::flycheck::TestTarget::Package(package_name) } else { - flycheck::TestTarget::Workspace + crate::flycheck::TestTarget::Workspace } } else { - flycheck::TestTarget::Workspace + crate::flycheck::TestTarget::Workspace }; - let handle = flycheck::CargoTestHandle::new( + let handle = crate::flycheck::CargoTestHandle::new( test_path, state.config.cargo_test_options(), cargo.workspace_root(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 174979ededed8..98c3d79dae206 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -15,6 +15,7 @@ mod capabilities; mod diagnostics; mod diff; mod dispatch; +mod flycheck; mod hack_recover_crate_name; mod line_index; mod main_loop; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 54f718479f055..14c1ba0c5601f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -9,7 +9,6 @@ use std::{ use always_assert::always; use crossbeam_channel::{select, Receiver}; -use flycheck::project_json; use ide_db::base_db::{SourceDatabase, SourceRootDatabase, VfsPath}; use lsp_server::{Connection, Notification, Request}; use lsp_types::{notification::Notification as _, TextDocumentIdentifier}; @@ -21,6 +20,7 @@ use crate::{ config::Config, diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind}, dispatch::{NotificationDispatcher, RequestDispatcher}, + flycheck::{self, project_json}, global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState}, hack_recover_crate_name, lsp::{ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 39301f4288429..e432f5d5cff73 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -15,7 +15,6 @@ // FIXME: This is a mess that needs some untangling work use std::{iter, mem}; -use flycheck::{FlycheckConfig, FlycheckHandle}; use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros, ProcMacrosBuilder}; use ide_db::{ base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, Version}, @@ -32,6 +31,7 @@ use vfs::{AbsPath, AbsPathBuf, ChangeKind}; use crate::{ config::{Config, FilesWatcher, LinkedProject}, + flycheck::{FlycheckConfig, FlycheckHandle}, global_state::{FetchWorkspaceRequest, GlobalState}, lsp_ext, main_loop::{DiscoverProjectParam, Task}, @@ -749,12 +749,14 @@ impl GlobalState { let config = self.config.flycheck(); let sender = self.flycheck_sender.clone(); let invocation_strategy = match config { - FlycheckConfig::CargoCommand { .. } => flycheck::InvocationStrategy::PerWorkspace, + FlycheckConfig::CargoCommand { .. } => { + crate::flycheck::InvocationStrategy::PerWorkspace + } FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy, }; self.flycheck = match invocation_strategy { - flycheck::InvocationStrategy::Once => vec![FlycheckHandle::spawn( + crate::flycheck::InvocationStrategy::Once => vec![FlycheckHandle::spawn( 0, Box::new(move |msg| sender.send(msg).unwrap()), config, @@ -762,7 +764,7 @@ impl GlobalState { self.config.root_path().clone(), None, )], - flycheck::InvocationStrategy::PerWorkspace => { + crate::flycheck::InvocationStrategy::PerWorkspace => { self.workspaces .iter() .enumerate() From 8ff6b2f16091306022465238294c809997764d9e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 8 Aug 2024 13:06:16 +0200 Subject: [PATCH 054/178] Split up flycheck module --- .../crates/project-model/src/project_json.rs | 247 +++++++++--------- .../src/{flycheck => }/command.rs | 4 +- .../{flycheck/project_json.rs => discover.rs} | 13 +- .../crates/rust-analyzer/src/flycheck.rs | 42 ++- .../crates/rust-analyzer/src/global_state.rs | 20 +- .../rust-analyzer/src/handlers/request.rs | 9 +- .../crates/rust-analyzer/src/lib.rs | 3 + .../crates/rust-analyzer/src/main_loop.rs | 57 ++-- .../src/{flycheck => }/test_runner.rs | 4 +- 9 files changed, 195 insertions(+), 204 deletions(-) rename src/tools/rust-analyzer/crates/rust-analyzer/src/{flycheck => }/command.rs (98%) rename src/tools/rust-analyzer/crates/rust-analyzer/src/{flycheck/project_json.rs => discover.rs} (92%) rename src/tools/rust-analyzer/crates/rust-analyzer/src/{flycheck => }/test_runner.rs (98%) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 1fb9cec8e2661..7dea0c3839841 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -74,106 +74,6 @@ pub struct ProjectJson { runnables: Vec, } -/// A crate points to the root module of a crate and lists the dependencies of the crate. This is -/// useful in creating the crate graph. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Crate { - pub(crate) display_name: Option, - pub root_module: AbsPathBuf, - pub(crate) edition: Edition, - pub(crate) version: Option, - pub(crate) deps: Vec, - pub(crate) cfg: Vec, - pub(crate) target: Option, - pub(crate) env: FxHashMap, - pub(crate) proc_macro_dylib_path: Option, - pub(crate) is_workspace_member: bool, - pub(crate) include: Vec, - pub(crate) exclude: Vec, - pub(crate) is_proc_macro: bool, - pub(crate) repository: Option, - pub build: Option, -} - -/// Additional, build-specific data about a crate. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Build { - /// The name associated with this crate. - /// - /// This is determined by the build system that produced - /// the `rust-project.json` in question. For instance, if buck were used, - /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. - /// - /// Do not attempt to parse the contents of this string; it is a build system-specific - /// identifier similar to [`Crate::display_name`]. - pub label: String, - /// Path corresponding to the build system-specific file defining the crate. - /// - /// It is roughly analogous to [`ManifestPath`], but it should *not* be used with - /// [`crate::ProjectManifest::from_manifest_file`], as the build file may not be - /// be in the `rust-project.json`. - pub build_file: Utf8PathBuf, - /// The kind of target. - /// - /// Examples (non-exhaustively) include [`TargetKind::Bin`], [`TargetKind::Lib`], - /// and [`TargetKind::Test`]. This information is used to determine what sort - /// of runnable codelens to provide, if any. - pub target_kind: TargetKind, -} - -/// A template-like structure for describing runnables. -/// -/// These are used for running and debugging binaries and tests without encoding -/// build system-specific knowledge into rust-analyzer. -/// -/// # Example -/// -/// Below is an example of a test runnable. `{label}` and `{test_id}` -/// are explained in [`Runnable::args`]'s documentation. -/// -/// ```json -/// { -/// "program": "buck", -/// "args": [ -/// "test", -/// "{label}", -/// "--", -/// "{test_id}", -/// "--print-passing-details" -/// ], -/// "cwd": "/home/user/repo-root/", -/// "kind": "testOne" -/// } -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Runnable { - /// The program invoked by the runnable. - /// - /// For example, this might be `cargo`, `buck`, or `bazel`. - pub program: String, - /// The arguments passed to [`Runnable::program`]. - /// - /// The args can contain two template strings: `{label}` and `{test_id}`. - /// rust-analyzer will find and replace `{label}` with [`Build::label`] and - /// `{test_id}` with the test name. - pub args: Vec, - /// The current working directory of the runnable. - pub cwd: Utf8PathBuf, - pub kind: RunnableKind, -} - -/// The kind of runnable. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum RunnableKind { - Check, - - /// Can run a binary. - Run, - - /// Run a single test. - TestOne, -} - impl ProjectJson { /// Create a new ProjectJson instance. /// @@ -302,6 +202,106 @@ impl ProjectJson { } } +/// A crate points to the root module of a crate and lists the dependencies of the crate. This is +/// useful in creating the crate graph. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Crate { + pub(crate) display_name: Option, + pub root_module: AbsPathBuf, + pub(crate) edition: Edition, + pub(crate) version: Option, + pub(crate) deps: Vec, + pub(crate) cfg: Vec, + pub(crate) target: Option, + pub(crate) env: FxHashMap, + pub(crate) proc_macro_dylib_path: Option, + pub(crate) is_workspace_member: bool, + pub(crate) include: Vec, + pub(crate) exclude: Vec, + pub(crate) is_proc_macro: bool, + pub(crate) repository: Option, + pub build: Option, +} + +/// Additional, build-specific data about a crate. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Build { + /// The name associated with this crate. + /// + /// This is determined by the build system that produced + /// the `rust-project.json` in question. For instance, if buck were used, + /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. + /// + /// Do not attempt to parse the contents of this string; it is a build system-specific + /// identifier similar to [`Crate::display_name`]. + pub label: String, + /// Path corresponding to the build system-specific file defining the crate. + /// + /// It is roughly analogous to [`ManifestPath`], but it should *not* be used with + /// [`crate::ProjectManifest::from_manifest_file`], as the build file may not be + /// be in the `rust-project.json`. + pub build_file: Utf8PathBuf, + /// The kind of target. + /// + /// Examples (non-exhaustively) include [`TargetKind::Bin`], [`TargetKind::Lib`], + /// and [`TargetKind::Test`]. This information is used to determine what sort + /// of runnable codelens to provide, if any. + pub target_kind: TargetKind, +} + +/// A template-like structure for describing runnables. +/// +/// These are used for running and debugging binaries and tests without encoding +/// build system-specific knowledge into rust-analyzer. +/// +/// # Example +/// +/// Below is an example of a test runnable. `{label}` and `{test_id}` +/// are explained in [`Runnable::args`]'s documentation. +/// +/// ```json +/// { +/// "program": "buck", +/// "args": [ +/// "test", +/// "{label}", +/// "--", +/// "{test_id}", +/// "--print-passing-details" +/// ], +/// "cwd": "/home/user/repo-root/", +/// "kind": "testOne" +/// } +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Runnable { + /// The program invoked by the runnable. + /// + /// For example, this might be `cargo`, `buck`, or `bazel`. + pub program: String, + /// The arguments passed to [`Runnable::program`]. + /// + /// The args can contain two template strings: `{label}` and `{test_id}`. + /// rust-analyzer will find and replace `{label}` with [`Build::label`] and + /// `{test_id}` with the test name. + pub args: Vec, + /// The current working directory of the runnable. + pub cwd: Utf8PathBuf, + pub kind: RunnableKind, +} + +/// The kind of runnable. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RunnableKind { + Check, + + /// Can run a binary. + Run, + + /// Run a single test. + TestOne, +} + #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub struct ProjectJsonData { sysroot: Option, @@ -407,6 +407,29 @@ pub enum TargetKindData { Lib, Test, } +/// Identifies a crate by position in the crates array. +/// +/// This will differ from `CrateId` when multiple `ProjectJson` +/// workspaces are loaded. +#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[serde(transparent)] +pub struct CrateArrayIdx(pub usize); + +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub(crate) struct Dep { + /// Identifies a crate by position in the crates array. + #[serde(rename = "crate")] + pub(crate) krate: CrateArrayIdx, + #[serde(serialize_with = "serialize_crate_name")] + #[serde(deserialize_with = "deserialize_crate_name")] + pub(crate) name: CrateName, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +struct CrateSource { + include_dirs: Vec, + exclude_dirs: Vec, +} impl From for TargetKind { fn from(data: TargetKindData) -> Self { @@ -445,30 +468,6 @@ impl From for RunnableKind { } } -/// Identifies a crate by position in the crates array. -/// -/// This will differ from `CrateId` when multiple `ProjectJson` -/// workspaces are loaded. -#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)] -#[serde(transparent)] -pub struct CrateArrayIdx(pub usize); - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub(crate) struct Dep { - /// Identifies a crate by position in the crates array. - #[serde(rename = "crate")] - pub(crate) krate: CrateArrayIdx, - #[serde(serialize_with = "serialize_crate_name")] - #[serde(deserialize_with = "deserialize_crate_name")] - pub(crate) name: CrateName, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -struct CrateSource { - include_dirs: Vec, - exclude_dirs: Vec, -} - fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result where D: de::Deserializer<'de>, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs similarity index 98% rename from src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/command.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs index 38c7c81f57a15..f1009eb46602f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/command.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs @@ -1,5 +1,5 @@ -//! Utilities for running a cargo command like `cargo check` or `cargo test` in a separate thread and -//! parse its stdout/stderr. +//! Utilities for running a cargo command like `cargo check` or `cargo test` in a separate thread +//! and parse its stdout/stderr. use std::{ ffi::OsString, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/project_json.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs similarity index 92% rename from src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/project_json.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 58619d4754352..7e9162eee6e58 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/project_json.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -1,4 +1,5 @@ -//! A `cargo-metadata`-equivalent for non-Cargo build systems. +//! Infrastructure for lazy project discovery. Currently only support rust-project.json discovery +//! via a custom discover command. use std::{io, process::Command}; use crossbeam_channel::Sender; @@ -7,14 +8,14 @@ use project_model::ProjectJsonData; use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::flycheck::{CommandHandle, ParseFromLine}; +use crate::command::{CommandHandle, ParseFromLine}; pub(crate) const ARG_PLACEHOLDER: &str = "{arg}"; /// A command wrapper for getting a `rust-project.json`. /// -/// This is analogous to `cargo-metadata`, but for non-Cargo build systems. -pub(crate) struct Discover { +/// This is analogous to discovering a cargo project + running `cargo-metadata` on it, but for non-Cargo build systems. +pub(crate) struct DiscoverCommand { command: Vec, sender: Sender, } @@ -34,8 +35,8 @@ where se.serialize_str(path.as_str()) } -impl Discover { - /// Create a new [Discover]. +impl DiscoverCommand { + /// Create a new [DiscoverCommand]. pub(crate) fn new(sender: Sender, command: Vec) -> Self { Self { sender, command } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index a713f9ea8c5bd..c2b943d1d6f41 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -1,11 +1,6 @@ -//! Flycheck provides the functionality needed to run `cargo check` or -//! another compatible command (f.x. clippy) in a background thread and provide +//! Flycheck provides the functionality needed to run `cargo check` to provide //! LSP diagnostics based on the output of the command. -// FIXME: This crate now handles running `cargo test` needed in the test explorer in -// addition to `cargo check`. Either split it into 3 crates (one for test, one for check -// and one common utilities) or change its name and docs to reflect the current state. - use std::{fmt, io, process::Command, time::Duration}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; @@ -18,12 +13,7 @@ pub(crate) use cargo_metadata::diagnostic::{ }; use toolchain::Tool; -mod command; -pub(crate) mod project_json; -mod test_runner; - -use command::{CommandHandle, ParseFromLine}; -pub(crate) use test_runner::{CargoTestHandle, CargoTestMessage, TestState, TestTarget}; +use crate::command::{CommandHandle, ParseFromLine}; #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub(crate) enum InvocationStrategy { @@ -52,7 +42,7 @@ pub(crate) struct CargoOptions { } impl CargoOptions { - fn apply_on_command(&self, cmd: &mut Command) { + pub(crate) fn apply_on_command(&self, cmd: &mut Command) { for target in &self.target_triples { cmd.args(["--target", target.as_str()]); } @@ -119,7 +109,7 @@ pub(crate) struct FlycheckHandle { impl FlycheckHandle { pub(crate) fn spawn( id: usize, - sender: Box, + sender: Box, config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, @@ -157,7 +147,7 @@ impl FlycheckHandle { } } -pub(crate) enum Message { +pub(crate) enum FlycheckMessage { /// Request adding a diagnostic with fixes included to a file AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic }, @@ -172,19 +162,19 @@ pub(crate) enum Message { }, } -impl fmt::Debug for Message { +impl fmt::Debug for FlycheckMessage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Message::AddDiagnostic { id, workspace_root, diagnostic } => f + FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => f .debug_struct("AddDiagnostic") .field("id", id) .field("workspace_root", workspace_root) .field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code)) .finish(), - Message::ClearDiagnostics { id } => { + FlycheckMessage::ClearDiagnostics { id } => { f.debug_struct("ClearDiagnostics").field("id", id).finish() } - Message::Progress { id, progress } => { + FlycheckMessage::Progress { id, progress } => { f.debug_struct("Progress").field("id", id).field("progress", progress).finish() } } @@ -209,7 +199,7 @@ enum StateChange { struct FlycheckActor { /// The workspace id of this flycheck instance. id: usize, - sender: Box, + sender: Box, config: FlycheckConfig, manifest_path: Option, /// Either the workspace root of the workspace we are flychecking, @@ -245,7 +235,7 @@ pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; impl FlycheckActor { fn new( id: usize, - sender: Box, + sender: Box, config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, @@ -266,7 +256,7 @@ impl FlycheckActor { } fn report_progress(&self, progress: Progress) { - self.send(Message::Progress { id: self.id, progress }); + self.send(FlycheckMessage::Progress { id: self.id, progress }); } fn next_event(&self, inbox: &Receiver) -> Option { @@ -339,7 +329,7 @@ impl FlycheckActor { ); } if self.status == FlycheckStatus::Started { - self.send(Message::ClearDiagnostics { id: self.id }); + self.send(FlycheckMessage::ClearDiagnostics { id: self.id }); } self.report_progress(Progress::DidFinish(res)); self.status = FlycheckStatus::Finished; @@ -361,9 +351,9 @@ impl FlycheckActor { "diagnostic received" ); if self.status == FlycheckStatus::Started { - self.send(Message::ClearDiagnostics { id: self.id }); + self.send(FlycheckMessage::ClearDiagnostics { id: self.id }); } - self.send(Message::AddDiagnostic { + self.send(FlycheckMessage::AddDiagnostic { id: self.id, workspace_root: self.root.clone(), diagnostic: msg, @@ -488,7 +478,7 @@ impl FlycheckActor { Some(cmd) } - fn send(&self, check_task: Message) { + fn send(&self, check_task: FlycheckMessage) { (self.sender)(check_task); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index df7d75fe1ae20..df809c0723553 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -5,7 +5,6 @@ use std::{ops::Not as _, time::Instant}; -use crate::flycheck::{self, project_json, FlycheckHandle}; use crossbeam_channel::{unbounded, Receiver, Sender}; use hir::ChangeWithProcMacros; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; @@ -28,6 +27,8 @@ use vfs::{AbsPathBuf, AnchoredPathBuf, ChangeKind, Vfs, VfsPath}; use crate::{ config::{Config, ConfigChange, ConfigErrors, RatomlFileKind}, diagnostics::{CheckFixes, DiagnosticCollection}, + discover, + flycheck::{FlycheckHandle, FlycheckMessage}, line_index::{LineEndings, LineIndex}, lsp::{from_proto, to_proto::url_from_abs_path}, lsp_ext, @@ -37,6 +38,7 @@ use crate::{ reload, target_spec::{CargoTargetSpec, ProjectJsonTargetSpec, TargetSpec}, task_pool::{TaskPool, TaskQueue}, + test_runner::{CargoTestHandle, CargoTestMessage}, }; pub(crate) struct FetchWorkspaceRequest { @@ -88,20 +90,20 @@ pub(crate) struct GlobalState { // Flycheck pub(crate) flycheck: Arc<[FlycheckHandle]>, - pub(crate) flycheck_sender: Sender, - pub(crate) flycheck_receiver: Receiver, + pub(crate) flycheck_sender: Sender, + pub(crate) flycheck_receiver: Receiver, pub(crate) last_flycheck_error: Option, // Test explorer - pub(crate) test_run_session: Option>, - pub(crate) test_run_sender: Sender, - pub(crate) test_run_receiver: Receiver, + pub(crate) test_run_session: Option>, + pub(crate) test_run_sender: Sender, + pub(crate) test_run_receiver: Receiver, pub(crate) test_run_remaining_jobs: usize, // Project loading - pub(crate) discover_handle: Option, - pub(crate) discover_sender: Sender, - pub(crate) discover_receiver: Receiver, + pub(crate) discover_handle: Option, + pub(crate) discover_sender: Sender, + pub(crate) discover_receiver: Receiver, // VFS pub(crate) loader: Handle, Receiver>, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 5f92f045398d3..a77d31167a750 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -51,6 +51,7 @@ use crate::{ FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams, }, target_spec::{CargoTargetSpec, TargetSpec}, + test_runner::{CargoTestHandle, TestTarget}, }; pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { @@ -246,15 +247,15 @@ pub(crate) fn handle_run_test( if let ProjectWorkspaceKind::Cargo { cargo, .. } = &ws.kind { let test_target = if let Some(namespace_root) = namespace_root { if let Some(package_name) = find_package_name(namespace_root, cargo) { - crate::flycheck::TestTarget::Package(package_name) + TestTarget::Package(package_name) } else { - crate::flycheck::TestTarget::Workspace + TestTarget::Workspace } } else { - crate::flycheck::TestTarget::Workspace + TestTarget::Workspace }; - let handle = crate::flycheck::CargoTestHandle::new( + let handle = CargoTestHandle::new( test_path, state.config.cargo_test_options(), cargo.workspace_root(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 98c3d79dae206..56eb420770e9b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -12,8 +12,10 @@ pub mod cli; mod capabilities; +mod command; mod diagnostics; mod diff; +mod discover; mod dispatch; mod flycheck; mod hack_recover_crate_name; @@ -24,6 +26,7 @@ mod op_queue; mod reload; mod target_spec; mod task_pool; +mod test_runner; mod version; mod handlers { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 14c1ba0c5601f..85e7d81fce3d7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -19,8 +19,9 @@ use vfs::{loader::LoadingProgress, AbsPathBuf, FileId}; use crate::{ config::Config, diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind}, + discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage}, dispatch::{NotificationDispatcher, RequestDispatcher}, - flycheck::{self, project_json}, + flycheck::{self, FlycheckMessage}, global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState}, hack_recover_crate_name, lsp::{ @@ -29,6 +30,7 @@ use crate::{ }, lsp_ext, reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress}, + test_runner::{CargoTestMessage, TestState}, }; pub fn main_loop(config: Config, connection: Connection) -> anyhow::Result<()> { @@ -61,9 +63,9 @@ enum Event { Task(Task), QueuedTask(QueuedTask), Vfs(vfs::loader::Message), - Flycheck(flycheck::Message), - TestResult(flycheck::CargoTestMessage), - DiscoverProject(project_json::DiscoverProjectMessage), + Flycheck(FlycheckMessage), + TestResult(CargoTestMessage), + DiscoverProject(DiscoverProjectMessage), } impl fmt::Display for Event { @@ -689,8 +691,7 @@ impl GlobalState { // `self.report_progress` is called later let title = &cfg.progress_label.clone(); let command = cfg.command.clone(); - let discover = - project_json::Discover::new(self.discover_sender.clone(), command); + let discover = DiscoverCommand::new(self.discover_sender.clone(), command); self.report_progress(title, Progress::Begin, None, None, None); self.discover_workspace_queue @@ -698,12 +699,8 @@ impl GlobalState { let _ = self.discover_workspace_queue.should_start_op(); let arg = match arg { - DiscoverProjectParam::Buildfile(it) => { - project_json::DiscoverArgument::Buildfile(it) - } - DiscoverProjectParam::Path(it) => { - project_json::DiscoverArgument::Path(it) - } + DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it), + DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), }; let handle = discover.spawn(arg).unwrap(); @@ -852,14 +849,14 @@ impl GlobalState { } } - fn handle_discover_msg(&mut self, message: project_json::DiscoverProjectMessage) { + fn handle_discover_msg(&mut self, message: DiscoverProjectMessage) { let title = self .config .discover_workspace_config() .map(|cfg| cfg.progress_label.clone()) .expect("No title could be found; this is a bug"); match message { - project_json::DiscoverProjectMessage::Finished { project, buildfile } => { + DiscoverProjectMessage::Finished { project, buildfile } => { self.report_progress(&title, Progress::End, None, None, None); self.discover_workspace_queue.op_completed(()); @@ -867,10 +864,10 @@ impl GlobalState { config.add_linked_projects(project, buildfile); self.update_configuration(config); } - project_json::DiscoverProjectMessage::Progress { message } => { + DiscoverProjectMessage::Progress { message } => { self.report_progress(&title, Progress::Report, Some(message), None, None) } - project_json::DiscoverProjectMessage::Error { error, source } => { + DiscoverProjectMessage::Error { error, source } => { let message = format!("Project discovery failed: {error}"); self.discover_workspace_queue.op_completed(()); self.show_and_log_error(message.clone(), source); @@ -879,16 +876,14 @@ impl GlobalState { } } - fn handle_cargo_test_msg(&mut self, message: flycheck::CargoTestMessage) { + fn handle_cargo_test_msg(&mut self, message: CargoTestMessage) { match message { - flycheck::CargoTestMessage::Test { name, state } => { + CargoTestMessage::Test { name, state } => { let state = match state { - flycheck::TestState::Started => lsp_ext::TestState::Started, - flycheck::TestState::Ignored => lsp_ext::TestState::Skipped, - flycheck::TestState::Ok => lsp_ext::TestState::Passed, - flycheck::TestState::Failed { stdout } => { - lsp_ext::TestState::Failed { message: stdout } - } + TestState::Started => lsp_ext::TestState::Started, + TestState::Ignored => lsp_ext::TestState::Skipped, + TestState::Ok => lsp_ext::TestState::Passed, + TestState::Failed { stdout } => lsp_ext::TestState::Failed { message: stdout }, }; let Some(test_id) = hack_recover_crate_name::lookup_name(name) else { return; @@ -897,23 +892,23 @@ impl GlobalState { lsp_ext::ChangeTestStateParams { test_id, state }, ); } - flycheck::CargoTestMessage::Suite => (), - flycheck::CargoTestMessage::Finished => { + CargoTestMessage::Suite => (), + CargoTestMessage::Finished => { self.test_run_remaining_jobs = self.test_run_remaining_jobs.saturating_sub(1); if self.test_run_remaining_jobs == 0 { self.send_notification::(()); self.test_run_session = None; } } - flycheck::CargoTestMessage::Custom { text } => { + CargoTestMessage::Custom { text } => { self.send_notification::(text); } } } - fn handle_flycheck_msg(&mut self, message: flycheck::Message) { + fn handle_flycheck_msg(&mut self, message: FlycheckMessage) { match message { - flycheck::Message::AddDiagnostic { id, workspace_root, diagnostic } => { + FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => { let snap = self.snapshot(); let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( &self.config.diagnostics_map(), @@ -939,9 +934,9 @@ impl GlobalState { } } - flycheck::Message::ClearDiagnostics { id } => self.diagnostics.clear_check(id), + FlycheckMessage::ClearDiagnostics { id } => self.diagnostics.clear_check(id), - flycheck::Message::Progress { id, progress } => { + FlycheckMessage::Progress { id, progress } => { let (state, message) = match progress { flycheck::Progress::DidStart => (Progress::Begin, None), flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs similarity index 98% rename from src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/test_runner.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 35419571fe553..293cff474337d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -8,9 +8,9 @@ use paths::AbsPath; use serde::Deserialize; use toolchain::Tool; -use crate::flycheck::{ +use crate::{ command::{CommandHandle, ParseFromLine}, - CargoOptions, + flycheck::CargoOptions, }; #[derive(Debug, Deserialize)] From ce846da6d6a13cbfc36bb5d4e93675a62e4115ec Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 8 Aug 2024 22:03:31 +0900 Subject: [PATCH 055/178] fix: Panic while rendering function with impl trait arg --- .../crates/hir-ty/src/display.rs | 8 +++---- .../crates/ide/src/hover/tests.rs | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index a433ecfd778b9..47ea2f5347c5f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1022,16 +1022,16 @@ impl HirDisplay for Ty { // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? if parameters.len() - impl_ > 0 { // `parameters` are in the order of fn's params (including impl traits), fn's lifetimes + let without_impl = self_param as usize + type_ + const_ + lifetime; // parent's params (those from enclosing impl or trait, if any). - let (fn_params, other) = - parameters.split_at(self_param as usize + type_ + const_ + lifetime); - let (_impl, parent_params) = other.split_at(impl_); + let (fn_params, parent_params) = parameters.split_at(without_impl + impl_); debug_assert_eq!(parent_params.len(), parent_len); let parent_params = generic_args_sans_defaults(f, Some(generic_def_id), parent_params); let fn_params = - generic_args_sans_defaults(f, Some(generic_def_id), fn_params); + &generic_args_sans_defaults(f, Some(generic_def_id), fn_params) + [0..without_impl]; write!(f, "<")?; hir_fmt_generic_arguments(f, parent_params, None)?; diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index ecfcf82e00c3d..516e32ef91725 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -8579,3 +8579,26 @@ fn main(a$0: T) {} "#]], ); } + +#[test] +fn hover_fn_with_impl_trait_arg() { + check( + r#" +trait Foo {} +impl Foo for bool {} +fn bar(_: impl Foo) {} +fn test() { + let f = bar::<3>; + f$0(true); +} +"#, + expect![[r#" + *f* + + ```rust + // size = 0, align = 1 + let f: fn bar<3>(bool) + ``` + "#]], + ); +} From e6d426e723dcd6fcbe50bb561e7d38564f758477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Cruz?= Date: Thu, 8 Aug 2024 17:12:20 +0100 Subject: [PATCH 056/178] Reuse recursion limit as expansion limit A configurable recursion limit was introduced by looking at the recursion_limit crate attribute. Instead of relying on a global constant we will reuse this value for expansion limit as well. --- .../crates/hir-def/src/nameres/collector.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index d970dbac1c2b4..debc5a44326ef 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -56,7 +56,6 @@ use crate::{ }; static GLOB_RECURSION_LIMIT: Limit = Limit::new(100); -static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128); static FIXED_POINT_LIMIT: Limit = Limit::new(8192); pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap { @@ -1440,7 +1439,14 @@ impl DefCollector<'_> { depth: usize, container: ItemContainerId, ) { - if EXPANSION_DEPTH_LIMIT.check(depth).is_err() { + let recursion_limit = self.def_map.recursion_limit() as usize; + let recursion_limit = Limit::new(if cfg!(test) { + // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug + std::cmp::min(32, recursion_limit) + } else { + recursion_limit + }); + if recursion_limit.check(depth).is_err() { cov_mark::hit!(macro_expansion_overflow); tracing::warn!("macro expansion is too deep"); return; From 077403a0538e45bdba438ddca1ea630ae234c09b Mon Sep 17 00:00:00 2001 From: winstxnhdw Date: Fri, 9 Aug 2024 03:44:51 +0800 Subject: [PATCH 057/178] minor: log error when sysroot can't be discovered --- .../rust-analyzer/crates/project-model/src/sysroot.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index bcd51930f0dad..419fac3f41f75 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -443,13 +443,13 @@ fn discover_sysroot_src_dir_or_add_component( get_rust_src(sysroot_path) }) .ok_or_else(|| { - format_err!( - "\ + let error = "\ can't load standard library from sysroot {sysroot_path} (discovered via `rustc --print sysroot`) -try installing the Rust source the same way you installed rustc", - ) +try installing the Rust source the same way you installed rustc"; + tracing::error!(error); + format_err!(error) }) } From 3fc5469a8d340bd6872e8067ea449af90b79e142 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 9 Aug 2024 14:03:16 +1000 Subject: [PATCH 058/178] Remove `Binder::split`. It's unused. --- compiler/rustc_type_ir/src/binder.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index c1f6fb36324ed..84ff5cebba1ec 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -247,21 +247,6 @@ impl Binder { // `self.value` is equivalent to `self.skip_binder()` if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) } } - - /// Splits the contents into two things that share the same binder - /// level as the original, returning two distinct binders. - /// - /// `f` should consider bound regions at depth 1 to be free, and - /// anything it produces with bound regions at depth 1 will be - /// bound in the resulting return values. - pub fn split(self, f: F) -> (Binder, Binder) - where - F: FnOnce(T) -> (U, V), - { - let Binder { value, bound_vars } = self; - let (u, v) = f(value); - (Binder { value: u, bound_vars }, Binder { value: v, bound_vars }) - } } impl Binder> { From 8640998869b28adab4b8faeaee169744804d3e68 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 9 Aug 2024 10:01:59 +1000 Subject: [PATCH 059/178] Split `split_inputs_and_output` in two. I think it's a little clearer and nicer that way. --- compiler/rustc_middle/src/ty/sty.rs | 9 ++++++--- compiler/rustc_type_ir/src/inherent.rs | 4 +++- compiler/rustc_type_ir/src/ty_kind.rs | 11 ++++------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 8c97de1c59b26..fe2ea2e5da56c 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1955,9 +1955,12 @@ impl<'tcx> Ty<'tcx> { } impl<'tcx> rustc_type_ir::inherent::Tys> for &'tcx ty::List> { - fn split_inputs_and_output(self) -> (&'tcx [Ty<'tcx>], Ty<'tcx>) { - let (output, inputs) = self.split_last().unwrap(); - (inputs, *output) + fn inputs(self) -> &'tcx [Ty<'tcx>] { + self.split_last().unwrap().1 + } + + fn output(self) -> Ty<'tcx> { + *self.split_last().unwrap().0 } } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 263ba676427c5..6ca2fed038065 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -203,7 +203,9 @@ pub trait Ty>: pub trait Tys>: Copy + Debug + Hash + Eq + SliceLike + TypeFoldable + Default { - fn split_inputs_and_output(self) -> (I::FnInputTys, I::Ty); + fn inputs(self) -> I::FnInputTys; + + fn output(self) -> I::Ty; } pub trait Abi>: Copy + Debug + Hash + Eq + Relate { diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 7e48f1b20a868..828f90212c48e 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -868,16 +868,12 @@ pub struct FnSig { } impl FnSig { - pub fn split_inputs_and_output(self) -> (I::FnInputTys, I::Ty) { - self.inputs_and_output.split_inputs_and_output() - } - pub fn inputs(self) -> I::FnInputTys { - self.split_inputs_and_output().0 + self.inputs_and_output.inputs() } pub fn output(self) -> I::Ty { - self.split_inputs_and_output().1 + self.inputs_and_output.output() } pub fn is_fn_trait_compatible(self) -> bool { @@ -935,7 +931,7 @@ impl fmt::Debug for FnSig { } write!(f, "fn(")?; - let (inputs, output) = sig.split_inputs_and_output(); + let inputs = sig.inputs(); for (i, ty) in inputs.iter().enumerate() { if i > 0 { write!(f, ", ")?; @@ -951,6 +947,7 @@ impl fmt::Debug for FnSig { } write!(f, ")")?; + let output = sig.output(); match output.kind() { Tuple(list) if list.is_empty() => Ok(()), _ => write!(f, " -> {:?}", sig.output()), From c4717cc9d15d30e08f03f51845c950175979bcc4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 8 Aug 2024 17:18:20 +1000 Subject: [PATCH 060/178] Shrink `TyKind::FnPtr`. By splitting the `FnSig` within `TyKind::FnPtr` into `FnSigTys` and `FnHeader`, which can be packed more efficiently. This reduces the size of the hot `TyKind` type from 32 bytes to 24 bytes on 64-bit platforms. This reduces peak memory usage by a few percent on some benchmarks. It also reduces cache misses and page faults similarly, though this doesn't translate to clear cycles or wall-time improvements on CI. --- .../src/diagnostics/conflict_errors.rs | 2 +- compiler/rustc_borrowck/src/lib.rs | 4 +- compiler/rustc_borrowck/src/type_check/mod.rs | 4 +- .../rustc_codegen_cranelift/src/common.rs | 2 +- .../src/value_and_place.rs | 2 +- compiler/rustc_codegen_gcc/src/type_of.rs | 5 +- .../src/debuginfo/metadata.rs | 2 +- .../src/debuginfo/type_names.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- .../src/check_consts/check.rs | 4 +- .../src/const_eval/valtrees.rs | 4 +- .../rustc_const_eval/src/interpret/cast.rs | 4 +- .../src/interpret/intrinsics.rs | 2 +- .../rustc_const_eval/src/interpret/stack.rs | 2 +- .../rustc_const_eval/src/interpret/step.rs | 2 +- .../src/interpret/validity.rs | 2 +- .../rustc_const_eval/src/util/type_name.rs | 2 +- .../src/check/intrinsicck.rs | 2 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../src/coherence/inherent_impls.rs | 2 +- .../src/variance/constraints.rs | 4 +- compiler/rustc_hir_typeck/src/callee.rs | 4 +- compiler/rustc_hir_typeck/src/closure.rs | 4 +- compiler/rustc_hir_typeck/src/coercion.rs | 17 ++--- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 2 +- .../src/fn_ctxt/suggestions.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 2 +- .../src/infer/canonical/canonicalizer.rs | 2 +- compiler/rustc_lint/src/types.rs | 9 ++- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_middle/src/ty/error.rs | 4 +- compiler/rustc_middle/src/ty/flags.rs | 6 +- compiler/rustc_middle/src/ty/layout.rs | 5 +- compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_middle/src/ty/print/mod.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 6 +- .../rustc_middle/src/ty/structural_impls.rs | 4 +- compiler/rustc_middle/src/ty/sty.rs | 19 ++--- compiler/rustc_middle/src/ty/util.rs | 12 +-- compiler/rustc_middle/src/ty/walk.rs | 9 ++- .../src/move_paths/builder.rs | 4 +- .../src/abort_unwinding_calls.rs | 2 +- .../src/ffi_unwind_calls.rs | 2 +- compiler/rustc_mir_transform/src/shim.rs | 2 +- .../src/canonicalizer.rs | 2 +- .../rustc_next_trait_solver/src/coherence.rs | 2 +- .../src/solve/assembly/mod.rs | 4 +- .../src/solve/assembly/structural_traits.rs | 9 ++- .../src/solve/trait_goals.rs | 2 +- compiler/rustc_passes/src/abi_test.rs | 17 +++-- compiler/rustc_pattern_analysis/src/rustc.rs | 2 +- .../src/cfi/typeid/itanium_cxx_abi/encode.rs | 9 ++- .../rustc_smir/src/rustc_internal/internal.rs | 5 +- .../rustc_smir/src/rustc_smir/convert/ty.rs | 4 +- compiler/rustc_symbol_mangling/src/v0.rs | 3 +- .../src/error_reporting/infer/mod.rs | 12 +-- .../error_reporting/infer/note_and_explain.rs | 6 +- .../src/error_reporting/infer/suggest.rs | 13 ++-- .../traits/fulfillment_errors.rs | 12 +-- .../src/error_reporting/traits/suggestions.rs | 20 ++--- .../src/traits/project.rs | 2 +- .../src/traits/query/dropck_outlives.rs | 4 +- .../src/traits/select/candidate_assembly.rs | 16 ++-- .../src/traits/select/confirmation.rs | 2 +- .../src/traits/select/mod.rs | 6 +- .../rustc_trait_selection/src/traits/wf.rs | 2 +- compiler/rustc_ty_utils/src/instance.rs | 2 +- compiler/rustc_ty_utils/src/layout.rs | 2 +- compiler/rustc_type_ir/src/binder.rs | 1 + compiler/rustc_type_ir/src/fast_reject.rs | 21 +++--- compiler/rustc_type_ir/src/inherent.rs | 6 +- compiler/rustc_type_ir/src/outlives.rs | 2 +- compiler/rustc_type_ir/src/relate.rs | 4 +- compiler/rustc_type_ir/src/ty_kind.rs | 73 ++++++++++++++++++- compiler/rustc_type_ir/src/ty_kind/closure.rs | 24 +++--- src/librustdoc/clean/mod.rs | 2 +- .../passes/collect_intra_doc_links.rs | 2 +- .../src/casts/fn_to_numeric_cast.rs | 2 +- .../src/casts/fn_to_numeric_cast_any.rs | 2 +- .../fn_to_numeric_cast_with_truncation.rs | 2 +- .../src/default_numeric_fallback.rs | 2 +- .../clippy/clippy_lints/src/dereference.rs | 2 +- .../clippy/clippy_lints/src/eta_reduction.rs | 2 +- .../clippy_lints/src/methods/map_flatten.rs | 2 +- .../src/mixed_read_write_in_expression.rs | 2 +- .../src/multiple_unsafe_ops_per_block.rs | 2 +- .../clippy/clippy_lints/src/mut_reference.rs | 2 +- src/tools/clippy/clippy_utils/src/ty.rs | 4 +- src/tools/clippy/clippy_utils/src/visitors.rs | 2 +- 89 files changed, 298 insertions(+), 201 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7a925705806fc..bb5b73931ce11 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3989,7 +3989,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { } else { let ty = self.infcx.tcx.type_of(self.mir_def_id()).instantiate_identity(); match ty.kind() { - ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig( + ty::FnDef(_, _) | ty::FnPtr(..) => self.annotate_fn_sig( self.mir_def_id(), self.infcx.tcx.fn_sig(self.mir_def_id()).instantiate_identity(), ), diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 9c2a0036befa8..30dd45d847c9b 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1644,7 +1644,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { | ty::Pat(_, _) | ty::Slice(_) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) @@ -1689,7 +1689,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::CoroutineWitness(..) | ty::Never diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index b13773ffe1460..f9af133f0e57e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1364,7 +1364,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { debug!("func_ty.kind: {:?}", func_ty.kind()); let sig = match func_ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) => func_ty.fn_sig(tcx), + ty::FnDef(..) | ty::FnPtr(..) => func_ty.fn_sig(tcx), _ => { span_mirbug!(self, term, "call to non-function {:?}", func_ty); return; @@ -2420,7 +2420,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_left = left.ty(body, tcx); match ty_left.kind() { // Types with regions are comparable if they have a common super-type. - ty::RawPtr(_, _) | ty::FnPtr(_) => { + ty::RawPtr(_, _) | ty::FnPtr(..) => { let ty_right = right.ty(body, tcx); let common_ty = self.infcx.next_ty_var(body.source_info(location).span); self.sub_types( diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 0931713993691..cbede8bc5790e 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -69,7 +69,7 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option types::F64, FloatTy::F128 => unimplemented!("f16_f128"), }, - ty::FnPtr(_) => pointer_ty(tcx), + ty::FnPtr(..) => pointer_ty(tcx), ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => { if has_ptr_meta(tcx, *pointee_ty) { return None; diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 1aa28daeafc7e..c2aa1f206488b 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -872,7 +872,7 @@ pub(crate) fn assert_assignable<'tcx>( (ty::Ref(_, a, _), ty::RawPtr(b, _)) | (ty::RawPtr(a, _), ty::Ref(_, b, _)) => { assert_assignable(fx, *a, *b, limit - 1); } - (ty::FnPtr(_), ty::FnPtr(_)) => { + (ty::FnPtr(..), ty::FnPtr(..)) => { let from_sig = fx.tcx.normalize_erasing_late_bound_regions( ParamEnv::reveal_all(), from_ty.fn_sig(fx.tcx), diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index d85ed4f12ffab..b7b1be5369c42 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -213,9 +213,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { // NOTE: we cannot remove this match like in the LLVM codegen because the call // to fn_ptr_backend_type handle the on-stack attribute. // TODO(antoyo): find a less hackish way to hande the on-stack attribute. - ty::FnPtr(sig) => { - cx.fn_ptr_backend_type(cx.fn_abi_of_fn_ptr(sig, ty::List::empty())) - } + ty::FnPtr(sig_tys, hdr) => cx + .fn_ptr_backend_type(cx.fn_abi_of_fn_ptr(sig_tys.with(hdr), ty::List::empty())), _ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO), }; cx.scalar_types.borrow_mut().insert(self.ty, ty); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index ad63858861261..87bea22d8ddaf 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -456,7 +456,7 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D { build_pointer_or_reference_di_node(cx, t, t.boxed_ty(), unique_type_id) } - ty::FnDef(..) | ty::FnPtr(_) => build_subroutine_type_di_node(cx, unique_type_id), + ty::FnDef(..) | ty::FnPtr(..) => build_subroutine_type_di_node(cx, unique_type_id), ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id), ty::CoroutineClosure(..) => build_closure_env_di_node(cx, unique_type_id), ty::Coroutine(..) => enums::build_coroutine_di_node(cx, unique_type_id), diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 2755803892751..359043a6d78ec 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -331,7 +331,7 @@ fn push_debuginfo_type_name<'tcx>( output.push(')'); } } - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { // We've encountered a weird 'recursive type' // Currently, the only way to generate such a type // is by using 'impl trait': diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index bc3076528da24..5f5ddd6f4f74d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -846,7 +846,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ), None, ), - ty::FnPtr(_) => (None, Some(callee.immediate())), + ty::FnPtr(..) => (None, Some(callee.immediate())), _ => bug!("{} is not callable", callee.layout.ty), }; diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 3ded81b90ffc1..69850a35c612b 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -170,7 +170,7 @@ struct LocalReturnTyVisitor<'ck, 'mir, 'tcx> { impl<'ck, 'mir, 'tcx> TypeVisitor> for LocalReturnTyVisitor<'ck, 'mir, 'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) { match t.kind() { - ty::FnPtr(_) => {} + ty::FnPtr(..) => {} ty::Ref(_, _, hir::Mutability::Mut) => { self.checker.check_op(ops::mut_ref::MutRef(self.kind)); t.super_visit_with(self) @@ -725,7 +725,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { let (mut callee, mut fn_args) = match *fn_ty.kind() { ty::FnDef(def_id, fn_args) => (def_id, fn_args), - ty::FnPtr(_) => { + ty::FnPtr(..) => { self.check_op(ops::FnCallIndirect); return; } diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 8227c04594883..53693f9a6a60e 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -132,7 +132,7 @@ fn const_to_valtree_inner<'tcx>( // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to // agree with runtime equality tests. - ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType(ty)), + ty::FnPtr(..) => Err(ValTreeCreationError::NonSupportedType(ty)), ty::Ref(_, _, _) => { let derefd_place = ecx.deref_pointer(place)?; @@ -353,7 +353,7 @@ pub fn valtree_to_const_value<'tcx>( | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Str | ty::Slice(_) | ty::Dynamic(..) => bug!("no ValTree should have been created for type {:?}", ty.kind()), diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index b2f07de0ac4e2..6a6e7250f334f 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -97,7 +97,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => { let src = self.read_immediate(src)?; match cast_ty.kind() { - ty::FnPtr(_) => { + ty::FnPtr(..) => { // No change to value self.write_immediate(*src, dest)?; } @@ -230,7 +230,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { src: &ImmTy<'tcx, M::Provenance>, cast_to: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { - assert_matches!(src.layout.ty.kind(), ty::RawPtr(_, _) | ty::FnPtr(_)); + assert_matches!(src.layout.ty.kind(), ty::RawPtr(_, _) | ty::FnPtr(..)); assert!(cast_to.ty.is_integral()); let scalar = src.to_scalar(); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 9210ec4e16fd0..d6d528ef36164 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -79,7 +79,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 50dbced6a2a6a..0f6bf5c03364c 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -483,7 +483,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Bool | ty::Float(_) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::RawPtr(..) | ty::Char | ty::Ref(..) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 2527eca344620..aaee6f6d247f8 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -424,7 +424,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() { - ty::FnPtr(_sig) => { + ty::FnPtr(..) => { let fn_ptr = self.read_pointer(&func)?; let fn_val = self.get_ptr_fn(fn_ptr)?; (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 460f5448634b5..47e86e0702b0a 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -616,7 +616,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.check_safe_pointer(value, PointerKind::Ref(*mutbl))?; Ok(true) } - ty::FnPtr(_sig) => { + ty::FnPtr(..) => { let value = self.read_scalar(value, ExpectedKind::FnPtr)?; // If we check references recursively, also check that this points to a function. diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 3aa3b3b74e053..36c7bed5c1194 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -35,7 +35,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Never | ty::Tuple(_) | ty::Dynamic(_, _, _) => self.pretty_print_type(ty), diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 847a1e6470679..ff008536b4b8e 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -66,7 +66,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), ty::Float(FloatTy::F128) => Some(InlineAsmType::F128), - ty::FnPtr(_) => Some(asm_ty_isize), + ty::FnPtr(..) => Some(asm_ty_isize), ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), ty::Adt(adt, args) if adt.repr().simd() => { let fields = &adt.non_enum_variant().fields; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c878095ba0d70..196bb4548a72d 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -951,7 +951,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), } else { let mut diag = match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => return Ok(()), - ty::FnPtr(_) => tcx.dcx().struct_span_err( + ty::FnPtr(..) => tcx.dcx().struct_span_err( hir_ty.span, "using function pointers as const generic parameters is forbidden", ), diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index e2d3ff558cf7d..89acb778d6c29 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -172,7 +172,7 @@ impl<'tcx> InherentCollect<'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::Never - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Tuple(..) => self.check_primitive_impl(id, self_ty), ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) | ty::Param(_) => { Err(self.tcx.dcx().emit_err(errors::InherentNominal { span: item_span })) diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 92baa41e07f90..ce9e73bf24503 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -317,8 +317,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraint(current, data.index, variance); } - ty::FnPtr(sig) => { - self.add_constraints_from_sig(current, sig, variance); + ty::FnPtr(sig_tys, hdr) => { + self.add_constraints_from_sig(current, sig_tys.with(hdr), variance); } ty::Error(_) => { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 07f64ead6f636..44cb08e44eb81 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -137,7 +137,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the callee is a bare function or a closure, then we're all set. match *adjusted_ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { let adjustments = self.adjust_steps(autoderef); self.apply_adjustments(callee_expr, adjustments); return Some(CallStep::Builtin(adjusted_ty)); @@ -467,7 +467,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (fn_sig, Some(def_id)) } // FIXME(effects): these arms should error because we can't enforce them - ty::FnPtr(sig) => (sig, None), + ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None), _ => { for arg in arg_exprs { self.check_expr(arg); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index a7953acc95c80..589a9c5a71914 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -336,9 +336,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .map(|obl| (obl.predicate, obl.cause.span)), ), - ty::FnPtr(sig) => match closure_kind { + ty::FnPtr(sig_tys, hdr) => match closure_kind { hir::ClosureKind::Closure => { - let expected_sig = ExpectedSig { cause_span: None, sig }; + let expected_sig = ExpectedSig { cause_span: None, sig: sig_tys.with(hdr) }; (Some(expected_sig), Some(ty::ClosureKind::Fn)) } hir::ClosureKind::Coroutine(_) | hir::ClosureKind::CoroutineClosure(_) => { diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index fcd3798eb48e6..0c83d50ad121c 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -225,10 +225,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // items to drop the unsafe qualifier. self.coerce_from_fn_item(a, b) } - ty::FnPtr(a_f) => { + ty::FnPtr(a_sig_tys, a_hdr) => { // We permit coercion of fn pointers to drop the // unsafe qualifier. - self.coerce_from_fn_pointer(a, a_f, b) + self.coerce_from_fn_pointer(a, a_sig_tys.with(a_hdr), b) } ty::Closure(closure_def_id_a, args_a) => { // Non-capturing closures are coercible to @@ -788,9 +788,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.commit_if_ok(|snapshot| { let outer_universe = self.infcx.universe(); - let result = if let ty::FnPtr(fn_ty_b) = b.kind() - && let (hir::Safety::Safe, hir::Safety::Unsafe) = - (fn_ty_a.safety(), fn_ty_b.safety()) + let result = if let ty::FnPtr(_, hdr_b) = b.kind() + && let (hir::Safety::Safe, hir::Safety::Unsafe) = (fn_ty_a.safety(), hdr_b.safety) { let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a); self.unify_and(unsafe_a, b, to_unsafe) @@ -842,7 +841,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); match b.kind() { - ty::FnPtr(b_sig) => { + ty::FnPtr(_, b_hdr) => { let a_sig = a.fn_sig(self.tcx); if let ty::FnDef(def_id, _) = *a.kind() { // Intrinsics are not coercible to function pointers @@ -852,7 +851,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). - if b_sig.safety() == hir::Safety::Safe + if b_hdr.safety == hir::Safety::Safe && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() { return Err(TypeError::TargetFeatureCast(def_id)); @@ -910,7 +909,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // // All we care here is if any variable is being captured and not the exact paths, // so we check `upvars_mentioned` for root variables being captured. - ty::FnPtr(fn_ty) + ty::FnPtr(_, hdr) if self .tcx .upvars_mentioned(closure_def_id_a.expect_local()) @@ -923,7 +922,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // or // `unsafe fn(arg0,arg1,...) -> _` let closure_sig = args_a.as_closure().sig(); - let safety = fn_ty.safety(); + let safety = hdr.safety; let pointer_ty = Ty::new_fn_ptr(self.tcx, self.tcx.signature_unclosure(closure_sig, safety)); debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index cef003e0a43de..0c727d285a871 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1527,7 +1527,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Int(_) | ty::Uint(_) => Some(ty), ty::Char => Some(tcx.types.u8), ty::RawPtr(..) => Some(tcx.types.usize), - ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), + ty::FnDef(..) | ty::FnPtr(..) => Some(tcx.types.usize), _ => None, }); opt_ty.unwrap_or_else(|| self.next_int_var()) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 6b4edcd95d993..703273968c5bb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -604,7 +604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { + if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { // Report upto four upvars being captured to reduce the amount error messages // reported back to the user. diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 94133357a3eb5..b8af839094f0d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -50,7 +50,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Not all of these (e.g., unsafe fns) implement `FnOnce`, // so we look for these beforehand. // FIXME(async_closures): These don't impl `FnOnce` by default. - ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true, + ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(..) => true, // If it's not a simple function, look for things which implement `FnOnce`. _ => { let Some(fn_once) = tcx.lang_items().fn_once_trait() else { diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 3bcb92d8029a2..db5139172b022 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -438,7 +438,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(..) | ty::Never | ty::Tuple(..) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 5b17c0d718a44..77fa40fea3956 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1025,7 +1025,7 @@ fn ty_is_known_nonnull<'tcx>( let ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty); match ty.kind() { - ty::FnPtr(_) => true, + ty::FnPtr(..) => true, ty::Ref(..) => true, ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true, ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => { @@ -1476,7 +1476,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty), - ty::FnPtr(sig) => { + ty::FnPtr(sig_tys, hdr) => { + let sig = sig_tys.with(hdr); if self.is_internal_abi(sig.abi()) { return FfiUnsafe { ty, @@ -1712,8 +1713,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { type Result = ControlFlow>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if let ty::FnPtr(sig) = ty.kind() - && !self.visitor.is_internal_abi(sig.abi()) + if let ty::FnPtr(_, hdr) = ty.kind() + && !self.visitor.is_internal_abi(hdr.abi) { self.tys.push(ty); } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8f8fd09c9e4d9..53380bfd7218a 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -427,7 +427,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::Closure(..) | ty::CoroutineClosure(..) diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 2f9bdb16bb0ab..d974a86a3036f 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -130,7 +130,7 @@ impl<'tcx> Ty<'tcx> { DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(), _ => "fn item".into(), }, - ty::FnPtr(_) => "fn pointer".into(), + ty::FnPtr(..) => "fn pointer".into(), ty::Dynamic(inner, ..) if let Some(principal) = inner.principal() => { format!("`dyn {}`", tcx.def_path_str(principal.def_id())).into() } @@ -194,7 +194,7 @@ impl<'tcx> Ty<'tcx> { DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(), _ => "fn item".into(), }, - ty::FnPtr(_) => "fn pointer".into(), + ty::FnPtr(..) => "fn pointer".into(), ty::Dynamic(..) => "trait object".into(), ty::Closure(..) | ty::CoroutineClosure(..) => "closure".into(), ty::Coroutine(def_id, ..) => { diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index c3430b5840692..1e38e30628aa9 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -250,9 +250,9 @@ impl FlagComputation { self.add_args(args); } - &ty::FnPtr(fn_sig) => self.bound_computation(fn_sig, |computation, fn_sig| { - computation.add_tys(fn_sig.inputs()); - computation.add_ty(fn_sig.output()); + &ty::FnPtr(sig_tys, _) => self.bound_computation(sig_tys, |computation, sig_tys| { + computation.add_tys(sig_tys.inputs()); + computation.add_ty(sig_tys.output()); }), } } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d7d27975f60a9..55511d0833ce0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -801,7 +801,7 @@ where | ty::Int(_) | ty::Uint(_) | ty::Float(_) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Never | ty::FnDef(..) | ty::CoroutineWitness(..) @@ -986,7 +986,8 @@ where safe: None, }) } - ty::FnPtr(fn_sig) if offset.bytes() == 0 => { + ty::FnPtr(sig_tys, hdr) if offset.bytes() == 0 => { + let fn_sig = sig_tys.with(hdr); tcx.layout_of(param_env.and(Ty::new_fn_ptr(tcx, fn_sig))).ok().map(|layout| { PointeeInfo { size: layout.size, align: layout.align.abi, safe: None } }) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9736428e6f7c7..2e6b961834143 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2149,6 +2149,6 @@ mod size_asserts { use super::*; // tidy-alphabetical-start static_assert_size!(PredicateKind<'_>, 32); - static_assert_size!(WithCachedTypeInfo>, 56); + static_assert_size!(WithCachedTypeInfo>, 48); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 6cce79dfdc1cf..cc7467467603a 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -290,7 +290,7 @@ fn characteristic_def_id_of_type_cached<'a>( | ty::Int(_) | ty::Uint(_) | ty::Str - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Alias(..) | ty::Placeholder(..) | ty::Param(_) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 29d72183dd3ff..2dd0caf4f87ec 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -696,7 +696,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(print(sig), " {{", print_value_path(def_id, args), "}}"); } } - ty::FnPtr(ref bare_fn) => p!(print(bare_fn)), + ty::FnPtr(ref sig_tys, hdr) => p!(print(sig_tys.with(hdr))), ty::Infer(infer_ty) => { if self.should_print_verbose() { p!(write("{:?}", ty.kind())); @@ -1678,7 +1678,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } } - ty::FnPtr(_) => { + ty::FnPtr(..) => { // FIXME: We should probably have a helper method to share code with the "Byte strings" // printing above (which also has to handle pointers to all sorts of things). if let Some(GlobalAlloc::Function { instance, .. }) = @@ -1741,7 +1741,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(write("{:?}", char::try_from(int).unwrap())) } // Pointer types - ty::Ref(..) | ty::RawPtr(_, _) | ty::FnPtr(_) => { + ty::Ref(..) | ty::RawPtr(_, _) | ty::FnPtr(..) => { let data = int.to_bits(self.tcx().data_layout.pointer_size); self.typed_value( |this| { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 8fb44a5f0b1b1..80b33c2cda929 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -374,7 +374,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ), ty::Tuple(ts) => ty::Tuple(ts.try_fold_with(folder)?), ty::FnDef(def_id, args) => ty::FnDef(def_id, args.try_fold_with(folder)?), - ty::FnPtr(f) => ty::FnPtr(f.try_fold_with(folder)?), + ty::FnPtr(sig_tys, hdr) => ty::FnPtr(sig_tys.try_fold_with(folder)?, hdr), ty::Ref(r, ty, mutbl) => { ty::Ref(r.try_fold_with(folder)?, ty.try_fold_with(folder)?, mutbl) } @@ -424,7 +424,7 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { } ty::Tuple(ts) => ts.visit_with(visitor), ty::FnDef(_, args) => args.visit_with(visitor), - ty::FnPtr(ref f) => f.visit_with(visitor), + ty::FnPtr(ref sig_tys, _) => sig_tys.visit_with(visitor), ty::Ref(r, ty, _) => { try_visit!(r.visit_with(visitor)); ty.visit_with(visitor) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index fe2ea2e5da56c..609d929e4a1fc 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -658,7 +658,8 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_fn_ptr(tcx: TyCtxt<'tcx>, fty: PolyFnSig<'tcx>) -> Ty<'tcx> { - Ty::new(tcx, FnPtr(fty)) + let (sig_tys, hdr) = fty.split(); + Ty::new(tcx, FnPtr(sig_tys, hdr)) } #[inline] @@ -1182,7 +1183,7 @@ impl<'tcx> Ty<'tcx> { | Float(_) | Uint(_) | FnDef(..) - | FnPtr(_) + | FnPtr(..) | RawPtr(_, _) | Infer(IntVar(_) | FloatVar(_)) ) @@ -1333,7 +1334,7 @@ impl<'tcx> Ty<'tcx> { pub fn fn_sig(self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> { match self.kind() { FnDef(def_id, args) => tcx.fn_sig(*def_id).instantiate(tcx, args), - FnPtr(f) => *f, + FnPtr(sig_tys, hdr) => sig_tys.with(*hdr), Error(_) => { // ignore errors (#54954) Binder::dummy(ty::FnSig { @@ -1352,12 +1353,12 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_fn(self) -> bool { - matches!(self.kind(), FnDef(..) | FnPtr(_)) + matches!(self.kind(), FnDef(..) | FnPtr(..)) } #[inline] pub fn is_fn_ptr(self) -> bool { - matches!(self.kind(), FnPtr(_)) + matches!(self.kind(), FnPtr(..)) } #[inline] @@ -1599,7 +1600,7 @@ impl<'tcx> Ty<'tcx> { | ty::Bool | ty::Float(_) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::RawPtr(..) | ty::Char | ty::Ref(..) @@ -1791,7 +1792,7 @@ impl<'tcx> Ty<'tcx> { | ty::Bool | ty::Float(_) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::RawPtr(..) | ty::Char | ty::Ref(..) @@ -1941,7 +1942,7 @@ impl<'tcx> Ty<'tcx> { | RawPtr(_, _) | Ref(_, _, _) | FnDef(_, _) - | FnPtr(_) + | FnPtr(..) | Dynamic(_, _, _) | Closure(_, _) | CoroutineClosure(_, _) @@ -1972,6 +1973,6 @@ mod size_asserts { use super::*; // tidy-alphabetical-start static_assert_size!(ty::RegionKind<'_>, 24); - static_assert_size!(ty::TyKind<'_>, 32); + static_assert_size!(ty::TyKind<'_>, 24); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 3cf8531bb62d5..0343c762313de 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1282,7 +1282,7 @@ impl<'tcx> Ty<'tcx> { | ty::RawPtr(_, _) | ty::FnDef(..) | ty::Error(_) - | ty::FnPtr(_) => true, + | ty::FnPtr(..) => true, ty::Tuple(fields) => fields.iter().all(Self::is_trivially_freeze), ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_freeze(), ty::Adt(..) @@ -1322,7 +1322,7 @@ impl<'tcx> Ty<'tcx> { | ty::RawPtr(_, _) | ty::FnDef(..) | ty::Error(_) - | ty::FnPtr(_) => true, + | ty::FnPtr(..) => true, ty::Tuple(fields) => fields.iter().all(Self::is_trivially_unpin), ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_unpin(), ty::Adt(..) @@ -1361,7 +1361,7 @@ impl<'tcx> Ty<'tcx> { | ty::Ref(..) | ty::RawPtr(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Infer(ty::FreshIntTy(_)) | ty::Infer(ty::FreshFloatTy(_)) => AsyncDropGlueMorphology::Noop, @@ -1544,7 +1544,7 @@ impl<'tcx> Ty<'tcx> { ty::Pat(..) | ty::Ref(..) | ty::Array(..) | ty::Slice(_) | ty::Tuple(..) => true, // Raw pointers use bitwise comparison. - ty::RawPtr(_, _) | ty::FnPtr(_) => true, + ty::RawPtr(_, _) | ty::FnPtr(..) => true, // Floating point numbers are not `Eq`. ty::Float(_) => false, @@ -1675,7 +1675,7 @@ pub fn needs_drop_components_with_async<'tcx>( | ty::Float(_) | ty::Never | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Char | ty::RawPtr(_, _) | ty::Ref(..) @@ -1742,7 +1742,7 @@ pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool { | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Never | ty::Foreign(_) => true, diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 2dd7a96f19268..0a328352e2e97 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -189,9 +189,12 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) stack.extend(args.iter().rev()); } ty::Tuple(ts) => stack.extend(ts.iter().rev().map(GenericArg::from)), - ty::FnPtr(sig) => { - stack.push(sig.skip_binder().output().into()); - stack.extend(sig.skip_binder().inputs().iter().copied().rev().map(|ty| ty.into())); + ty::FnPtr(sig_tys, hdr) => { + let fn_sig = sig_tys.with(hdr); + stack.push(fn_sig.skip_binder().output().into()); + stack.extend( + fn_sig.skip_binder().inputs().iter().copied().rev().map(|ty| ty.into()), + ); } }, GenericArgKind::Lifetime(_) => {} diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index c26a72e454382..86091379f5a92 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -158,7 +158,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | ty::Pat(_, _) | ty::Slice(_) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -201,7 +201,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::CoroutineWitness(..) | ty::Never diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index f52a4524d784b..edb6bc4fbea2f 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -64,7 +64,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { let ty = func.ty(body, tcx); let sig = ty.fn_sig(tcx); let fn_def_id = match ty.kind() { - ty::FnPtr(_) => None, + ty::FnPtr(..) => None, &ty::FnDef(def_id, _) => Some(def_id), _ => span_bug!(span, "invalid callee of type {:?}", ty), }; diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index 4132e604f20e0..9a2cc057232f3 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -57,7 +57,7 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { }; let fn_def_id = match ty.kind() { - ty::FnPtr(_) => None, + ty::FnPtr(..) => None, &ty::FnDef(def_id, _) => { // Rust calls cannot themselves create foreign unwinds (even if they use a non-Rust ABI). // So the leak of the foreign unwind into Rust can only be elsewhere, not here. diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index f41f3ef656c51..49d55166ec5bc 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -437,7 +437,7 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) - let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0))); match self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) => builder.copy_shim(), + ty::FnDef(..) | ty::FnPtr(..) => builder.copy_shim(), ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()), ty::CoroutineClosure(_, args) => { builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys()) diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 82488088e30e6..394518daa4250 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -360,7 +360,7 @@ impl, I: Interner> TypeFolder for Canonicaliz | ty::Ref(_, _, _) | ty::Pat(_, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::Closure(..) | ty::CoroutineClosure(..) diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index f22ea41c51222..2461ef0c0df57 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -334,7 +334,7 @@ where | ty::Str | ty::FnDef(..) | ty::Pat(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Array(..) | ty::Slice(..) | ty::RawPtr(..) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 84921e87fb3f4..bb05eb4c25697 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -533,7 +533,7 @@ where | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(..) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -620,7 +620,7 @@ where | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Alias(..) | ty::Closure(..) | ty::CoroutineClosure(..) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 00837f7cdd87d..f32531821ec47 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -31,7 +31,7 @@ where | ty::Bool | ty::Float(_) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Error(_) | ty::Never | ty::Char => Ok(vec![]), @@ -117,7 +117,7 @@ where | ty::Bool | ty::Float(_) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::RawPtr(..) | ty::Char | ty::Ref(..) @@ -178,7 +178,7 @@ where { match ty.kind() { // impl Copy/Clone for FnDef, FnPtr - ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(vec![]), // Implementations are provided in core ty::Uint(_) @@ -269,7 +269,8 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable { + ty::FnPtr(sig_tys, hdr) => { + let sig = sig_tys.with(hdr); if sig.is_fn_trait_compatible() { Ok(Some( sig.map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())), diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index b1dba712f797a..1314b7eb6ffc6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1145,7 +1145,7 @@ where | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 839b96fb3de84..d0cc123c41a8a 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -127,14 +127,17 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut for meta_item in meta_items { match meta_item.name_or_empty() { sym::debug => { - let ty::FnPtr(sig) = ty.kind() else { + let ty::FnPtr(sig_tys, hdr) = ty.kind() else { span_bug!( meta_item.span(), "`#[rustc_abi(debug)]` on a type alias requires function pointer type" ); }; let abi = unwrap_fn_abi( - tcx.fn_abi_of_fn_ptr(param_env.and((*sig, /* extra_args */ ty::List::empty()))), + tcx.fn_abi_of_fn_ptr( + param_env + .and((sig_tys.with(*hdr), /* extra_args */ ty::List::empty())), + ), tcx, item_def_id, ); @@ -155,7 +158,7 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut "`#[rustc_abi(assert_eq)]` on a type alias requires pair type" ); }; - let ty::FnPtr(sig1) = field1.kind() else { + let ty::FnPtr(sig_tys1, hdr1) = field1.kind() else { span_bug!( meta_item.span(), "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types" @@ -163,12 +166,13 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut }; let abi1 = unwrap_fn_abi( tcx.fn_abi_of_fn_ptr( - param_env.and((*sig1, /* extra_args */ ty::List::empty())), + param_env + .and((sig_tys1.with(*hdr1), /* extra_args */ ty::List::empty())), ), tcx, item_def_id, ); - let ty::FnPtr(sig2) = field2.kind() else { + let ty::FnPtr(sig_tys2, hdr2) = field2.kind() else { span_bug!( meta_item.span(), "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types" @@ -176,7 +180,8 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut }; let abi2 = unwrap_fn_abi( tcx.fn_abi_of_fn_ptr( - param_env.and((*sig2, /* extra_args */ ty::List::empty())), + param_env + .and((sig_tys2.with(*hdr2), /* extra_args */ ty::List::empty())), ), tcx, item_def_id, diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 6290aeb252312..cc978b1208317 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -414,7 +414,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | ty::Foreign(_) | ty::RawPtr(_, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Pat(_, _) | ty::Dynamic(_, _, _) | ty::Closure(..) diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 61de338eab1c0..a4e4f50e8f2d4 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -607,10 +607,15 @@ pub fn encode_ty<'tcx>( typeid.push_str(&s); } - ty::FnPtr(fn_sig) => { + ty::FnPtr(sig_tys, hdr) => { // PFE let mut s = String::from("P"); - s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::empty())); + s.push_str(&encode_fnsig( + tcx, + &sig_tys.with(*hdr).skip_binder(), + dict, + TypeIdOptions::empty(), + )); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); typeid.push_str(&s); } diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index a4577461094bb..f52cb010a872f 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -131,7 +131,10 @@ impl RustcInternal for RigidTy { RigidTy::FnDef(def, args) => { rustc_ty::TyKind::FnDef(def.0.internal(tables, tcx), args.internal(tables, tcx)) } - RigidTy::FnPtr(sig) => rustc_ty::TyKind::FnPtr(sig.internal(tables, tcx)), + RigidTy::FnPtr(sig) => { + let (sig_tys, hdr) = sig.internal(tables, tcx).split(); + rustc_ty::TyKind::FnPtr(sig_tys, hdr) + } RigidTy::Closure(def, args) => { rustc_ty::TyKind::Closure(def.0.internal(tables, tcx), args.internal(tables, tcx)) } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index ef2eb7d52eaef..332fe22d86955 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -352,7 +352,9 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::FnDef(def_id, generic_args) => { TyKind::RigidTy(RigidTy::FnDef(tables.fn_def(*def_id), generic_args.stable(tables))) } - ty::FnPtr(poly_fn_sig) => TyKind::RigidTy(RigidTy::FnPtr(poly_fn_sig.stable(tables))), + ty::FnPtr(sig_tys, hdr) => { + TyKind::RigidTy(RigidTy::FnPtr(sig_tys.with(*hdr).stable(tables))) + } ty::Dynamic(existential_predicates, region, dyn_kind) => { TyKind::RigidTy(RigidTy::Dynamic( existential_predicates diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index c2451c08d1158..3a606f244e37a 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -427,7 +427,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { self.print_def_path(def_id, &[])?; } - ty::FnPtr(sig) => { + ty::FnPtr(sig_tys, hdr) => { + let sig = sig_tys.with(hdr); self.push("F"); self.in_binder(&sig, |cx, sig| { if sig.safety == hir::Safety::Unsafe { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 8ccb2a8483ae3..5193333be8e90 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1087,9 +1087,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values } - (ty::FnDef(did1, args1), ty::FnPtr(sig2)) => { + (ty::FnDef(did1, args1), ty::FnPtr(sig_tys2, hdr2)) => { let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); - let mut values = self.cmp_fn_sig(&sig1, sig2); + let mut values = self.cmp_fn_sig(&sig1, &sig_tys2.with(*hdr2)); values.0.push_highlighted(format!( " {{{}}}", self.tcx.def_path_str_with_args(*did1, args1) @@ -1097,16 +1097,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values } - (ty::FnPtr(sig1), ty::FnDef(did2, args2)) => { + (ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => { let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2); - let mut values = self.cmp_fn_sig(sig1, &sig2); + let mut values = self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig2); values .1 .push_normal(format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2))); values } - (ty::FnPtr(sig1), ty::FnPtr(sig2)) => self.cmp_fn_sig(sig1, sig2), + (ty::FnPtr(sig_tys1, hdr1), ty::FnPtr(sig_tys2, hdr2)) => { + self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig_tys2.with(*hdr2)) + } _ => { let mut strs = (DiagStyledString::new(), DiagStyledString::new()); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 864510bb65047..e77c738acce1a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -441,9 +441,9 @@ impl Trait for X { } } } - (ty::FnPtr(sig), ty::FnDef(def_id, _)) - | (ty::FnDef(def_id, _), ty::FnPtr(sig)) => { - if tcx.fn_sig(def_id).skip_binder().safety() < sig.safety() { + (ty::FnPtr(_, hdr), ty::FnDef(def_id, _)) + | (ty::FnDef(def_id, _), ty::FnPtr(_, hdr)) => { + if tcx.fn_sig(def_id).skip_binder().safety() < hdr.safety { diag.note( "unsafe functions cannot be coerced into safe function pointers", ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index ee159aa0b77a6..35f68a56d2ddc 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -383,8 +383,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return; } match (&expected_inner.kind(), &found_inner.kind()) { - (ty::FnPtr(sig), ty::FnDef(did, args)) => { - let expected_sig = &(self.normalize_fn_sig)(*sig); + (ty::FnPtr(sig_tys, hdr), ty::FnDef(did, args)) => { + let sig = sig_tys.with(*hdr); + let expected_sig = &(self.normalize_fn_sig)(sig); let found_sig = &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args)); @@ -402,11 +403,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name }, (true, true) => { diag.subdiagnostic(FnItemsAreDistinct); - FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig } + FunctionPointerSuggestion::CastRef { span, fn_name, sig } } (false, false) => { diag.subdiagnostic(FnItemsAreDistinct); - FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig } + FunctionPointerSuggestion::Cast { span, fn_name, sig } } }; diag.subdiagnostic(sugg); @@ -449,10 +450,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { diag.subdiagnostic(sug); } - (ty::FnDef(did, args), ty::FnPtr(sig)) => { + (ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => { let expected_sig = &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args)); - let found_sig = &(self.normalize_fn_sig)(*sig); + let found_sig = &(self.normalize_fn_sig)(sig_tys.with(*hdr)); if !self.same_type_modulo_infer(*found_sig, *expected_sig) { return; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 1cee82f04ea0e..a5f9a22ad2be3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -375,7 +375,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let impl_candidates = self.find_similar_impl_candidates(leaf_trait_predicate); suggested = if let &[cand] = &impl_candidates[..] { let cand = cand.trait_ref; - if let (ty::FnPtr(_), ty::FnDef(..)) = + if let (ty::FnPtr(..), ty::FnDef(..)) = (cand.self_ty().kind(), main_trait_ref.self_ty().skip_binder().kind()) { err.span_suggestion( @@ -793,8 +793,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // is unimplemented is because async closures don't implement `Fn`/`FnMut` // if they have captures. if let Some(by_ref_captures) = by_ref_captures - && let ty::FnPtr(sig) = by_ref_captures.kind() - && !sig.skip_binder().output().is_unit() + && let ty::FnPtr(sig_tys, _) = by_ref_captures.kind() + && !sig_tys.skip_binder().output().is_unit() { let mut err = self.dcx().create_err(AsyncClosureNotFn { span: self.tcx.def_span(closure_def_id), @@ -1061,7 +1061,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "`{ty}` is forbidden as the type of a const generic parameter", ) } - ty::FnPtr(_) => { + ty::FnPtr(..) => { struct_span_code_err!( self.dcx(), span, @@ -1844,10 +1844,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let &[cand] = &candidates[..] { let (desc, mention_castable) = match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) { - (ty::FnPtr(_), ty::FnDef(..)) => { + (ty::FnPtr(..), ty::FnDef(..)) => { (" implemented for fn pointer `", ", cast using `as`") } - (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""), + (ty::FnPtr(..), _) => (" implemented for fn pointer `", ""), _ => (" implemented for `", ""), }; err.highlighted_help(vec![ diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 0d15ef55e24e7..39c5dbebbf7a1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1077,10 +1077,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let Some((def_id_or_name, output, inputs)) = (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| { match *found.kind() { - ty::FnPtr(fn_sig) => Some(( + ty::FnPtr(sig_tys, _) => Some(( DefIdOrName::Name("function pointer"), - fn_sig.output(), - fn_sig.inputs(), + sig_tys.output(), + sig_tys.inputs(), )), ty::FnDef(def_id, _) => { let fn_sig = found.fn_sig(self.tcx); @@ -1977,20 +1977,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = cause else { return; }; - let ty::FnPtr(expected) = expected.kind() else { + let ty::FnPtr(sig_tys, hdr) = expected.kind() else { return; }; - let ty::FnPtr(found) = found.kind() else { + let expected = sig_tys.with(*hdr); + let ty::FnPtr(sig_tys, hdr) = found.kind() else { return; }; + let found = sig_tys.with(*hdr); let Node::Expr(arg) = self.tcx.hir_node(*arg_hir_id) else { return; }; let hir::ExprKind::Path(path) = arg.kind else { return; }; - let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(*expected).inputs(); - let found_inputs = self.tcx.instantiate_bound_regions_with_erased(*found).inputs(); + let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(expected).inputs(); + let found_inputs = self.tcx.instantiate_bound_regions_with_erased(found).inputs(); let both_tys = expected_inputs.iter().copied().zip(found_inputs.iter().copied()); let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| { @@ -4790,13 +4792,13 @@ fn hint_missing_borrow<'tcx>( } let found_args = match found.kind() { - ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()), + ty::FnPtr(sig_tys, _) => infcx.enter_forall(*sig_tys, |sig_tys| sig_tys.inputs().iter()), kind => { span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind) } }; let expected_args = match expected.kind() { - ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()), + ty::FnPtr(sig_tys, _) => infcx.enter_forall(*sig_tys, |sig_tys| sig_tys.inputs().iter()), kind => { span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind) } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 4b62a5c59b2f0..d74fafa4817ca 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1636,7 +1636,7 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( .generics_of(def_id) .host_effect_index .map_or(tcx.consts.true_, |idx| args.const_at(idx)), - ty::FnPtr(_) => tcx.consts.true_, + ty::FnPtr(..) => tcx.consts.true_, _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"), }; diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index d3a1ed52d2e62..7d30e6524490c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -33,7 +33,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Float(_) | ty::Never | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Char | ty::CoroutineWitness(..) | ty::RawPtr(_, _) @@ -224,7 +224,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::CoroutineWitness(..) => { // these types never have a destructor } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 9de62031311b7..cb8deeaedb66f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -468,8 +468,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.vec.push(AsyncClosureCandidate); } // Provide an impl, but only for suitable `fn` pointers. - ty::FnPtr(sig) => { - if sig.is_fn_trait_compatible() { + ty::FnPtr(sig_tys, hdr) => { + if sig_tys.with(hdr).is_fn_trait_compatible() { candidates.vec.push(AsyncClosureCandidate); } } @@ -535,8 +535,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.ambiguous = true; // Could wind up being a fn() type. } // Provide an impl, but only for suitable `fn` pointers. - ty::FnPtr(sig) => { - if sig.is_fn_trait_compatible() { + ty::FnPtr(sig_tys, hdr) => { + if sig_tys.with(hdr).is_fn_trait_compatible() { candidates .vec .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ }); @@ -819,7 +819,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) @@ -1207,7 +1207,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Never | ty::Foreign(_) | ty::Array(..) @@ -1290,7 +1290,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::Pat(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -1339,7 +1339,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = self.infcx.resolve_vars_if_possible(obligation.self_ty()); match self_ty.skip_binder().kind() { - ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }), + ty::FnPtr(..) => candidates.vec.push(BuiltinCandidate { has_nested: false }), ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index ddd8b970cc813..0d7ceca430174 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1398,7 +1398,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Never | ty::Foreign(_) => {} diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1d9a90f0300ab..cfc0fa4e3a84f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2114,7 +2114,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Bool | ty::Float(_) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::RawPtr(..) | ty::Char | ty::Ref(..) @@ -2171,7 +2171,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { use self::BuiltinImplConditions::{Ambiguous, None, Where}; match *self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), ty::Uint(_) | ty::Int(_) @@ -2333,7 +2333,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Bool | ty::Float(_) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Never diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 7e5fe7e3c942b..a3982c3d987ef 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -812,7 +812,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { return upvars.visit_with(self); } - ty::FnPtr(_) => { + ty::FnPtr(..) => { // Let the visitor iterate into the argument/return // types appearing in the fn signature. } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 43e491387091e..c7ed6e6110fab 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -248,7 +248,7 @@ fn resolve_associated_item<'tcx>( if name == sym::clone { let self_ty = trait_ref.self_ty(); match self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) => (), + ty::FnDef(..) | ty::FnPtr(..) => (), ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Closure(..) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 3ef10f4e43c4b..53b198a2da9aa 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -184,7 +184,7 @@ fn layout_of_uncached<'tcx>( ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)), ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)), ty::Float(fty) => scalar(Float(Float::from_float_ty(fty))), - ty::FnPtr(_) => { + ty::FnPtr(..) => { let mut ptr = scalar_unit(Pointer(dl.instruction_address_space)); ptr.valid_range_mut().start = 1; tcx.mk_layout(LayoutS::scalar(cx, ptr)) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 84ff5cebba1ec..652201f11e385 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -86,6 +86,7 @@ macro_rules! impl_binder_encode_decode { #[cfg(feature = "nightly")] impl_binder_encode_decode! { ty::FnSig, + ty::FnSigTys, ty::TraitPredicate, ty::ExistentialPredicate, ty::TraitRef, diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 456accd1a1b78..718e13ffe943f 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -135,7 +135,9 @@ pub fn simplify_type( ty::CoroutineWitness(def_id, _) => Some(SimplifiedType::CoroutineWitness(def_id)), ty::Never => Some(SimplifiedType::Never), ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())), - ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())), + ty::FnPtr(sig_tys, _hdr) => { + Some(SimplifiedType::Function(sig_tys.skip_binder().inputs().len())) + } ty::Placeholder(..) => Some(SimplifiedType::Placeholder), ty::Param(_) => match treat_params { TreatParams::ForLookup => Some(SimplifiedType::Placeholder), @@ -307,17 +309,14 @@ impl DeepRejectCtxt { obl_preds.principal_def_id() == impl_preds.principal_def_id() ) } - ty::FnPtr(obl_sig) => match k { - ty::FnPtr(impl_sig) => { - let ty::FnSig { inputs_and_output, c_variadic, safety, abi } = - obl_sig.skip_binder(); - let impl_sig = impl_sig.skip_binder(); + ty::FnPtr(obl_sig_tys, obl_hdr) => match k { + ty::FnPtr(impl_sig_tys, impl_hdr) => { + let obl_sig_tys = obl_sig_tys.skip_binder().0; + let impl_sig_tys = impl_sig_tys.skip_binder().0; - abi == impl_sig.abi - && c_variadic == impl_sig.c_variadic - && safety == impl_sig.safety - && inputs_and_output.len() == impl_sig.inputs_and_output.len() - && iter::zip(inputs_and_output.iter(), impl_sig.inputs_and_output.iter()) + obl_hdr == impl_hdr + && obl_sig_tys.len() == impl_sig_tys.len() + && iter::zip(obl_sig_tys.iter(), impl_sig_tys.iter()) .all(|(obl, imp)| self.types_may_unify(obl, imp)) } _ => false, diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 6ca2fed038065..958360faede57 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -133,12 +133,12 @@ pub trait Ty>: } fn is_fn_ptr(self) -> bool { - matches!(self.kind(), ty::FnPtr(_)) + matches!(self.kind(), ty::FnPtr(..)) } fn fn_sig(self, interner: I) -> ty::Binder> { match self.kind() { - ty::FnPtr(sig) => sig, + ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr), ty::FnDef(def_id, args) => interner.fn_sig(def_id).instantiate(interner, args), ty::Error(_) => { // ignore errors (#54954) @@ -181,7 +181,7 @@ pub trait Ty>: | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index 2f26a43918383..bfcea6a81d313 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -186,7 +186,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::Tuple(_) => { ty.super_visit_with(self); diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 9fd3534d1fa6b..578436b622a87 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -524,8 +524,8 @@ pub fn structurally_relate_tys>( Ok(Ty::new_fn_def(cx, a_def_id, args)) } - (ty::FnPtr(a_fty), ty::FnPtr(b_fty)) => { - let fty = relation.relate(a_fty, b_fty)?; + (ty::FnPtr(a_sig_tys, a_hdr), ty::FnPtr(b_sig_tys, b_hdr)) => { + let fty = relation.relate(a_sig_tys.with(a_hdr), b_sig_tys.with(b_hdr))?; Ok(Ty::new_fn_ptr(cx, fty)) } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 828f90212c48e..8640a6019800a 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -143,7 +143,12 @@ pub enum TyKind { /// fn foo() -> i32 { 1 } /// let bar: fn() -> i32 = foo; /// ``` - FnPtr(ty::Binder>), + /// + /// These two fields are equivalent to a `ty::Binder>`. But by + /// splitting that into two pieces, we get a more compact data layout that + /// reduces the size of `TyKind` by 8 bytes. It is a very hot type, so it's + /// worth the mild inconvenience. + FnPtr(ty::Binder>, FnHeader), /// A trait object. Written as `dyn for<'b> Trait<'b, Assoc = u32> + Send + 'a`. Dynamic(I::BoundExistentialPredicates, I::Region, DynKind), @@ -288,7 +293,7 @@ impl fmt::Debug for TyKind { RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), ty), Ref(r, t, m) => write!(f, "&{:?} {}{:?}", r, m.prefix_str(), t), FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&s).finish(), - FnPtr(s) => write!(f, "{s:?}"), + FnPtr(sig_tys, hdr) => write!(f, "{:?}", sig_tys.with(*hdr)), Dynamic(p, r, repr) => match repr { DynKind::Dyn => write!(f, "dyn {p:?} + {r:?}"), DynKind::DynStar => write!(f, "dyn* {p:?} + {r:?}"), @@ -918,6 +923,13 @@ impl ty::Binder> { pub fn is_fn_trait_compatible(&self) -> bool { self.skip_binder().is_fn_trait_compatible() } + + // Used to split a single value into the two fields in `TyKind::FnPtr`. + pub fn split(self) -> (ty::Binder>, FnHeader) { + let hdr = + FnHeader { c_variadic: self.c_variadic(), safety: self.safety(), abi: self.abi() }; + (self.map_bound(|sig| FnSigTys(sig.inputs_and_output)), hdr) + } } impl fmt::Debug for FnSig { @@ -954,3 +966,60 @@ impl fmt::Debug for FnSig { } } } + +// This is just a `FnSig` without the `FnHeader` fields. +#[derive_where(Clone, Copy, Debug, PartialEq, Eq, Hash; I: Interner)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +pub struct FnSigTys(pub I::Tys); + +impl FnSigTys { + pub fn inputs(self) -> I::FnInputTys { + self.0.inputs() + } + + pub fn output(self) -> I::Ty { + self.0.output() + } +} + +impl ty::Binder> { + // Used to combine the two fields in `TyKind::FnPtr` into a single value. + pub fn with(self, hdr: FnHeader) -> ty::Binder> { + self.map_bound(|sig_tys| FnSig { + inputs_and_output: sig_tys.0, + c_variadic: hdr.c_variadic, + safety: hdr.safety, + abi: hdr.abi, + }) + } + + #[inline] + pub fn inputs(self) -> ty::Binder { + self.map_bound(|sig_tys| sig_tys.inputs()) + } + + #[inline] + #[track_caller] + pub fn input(self, index: usize) -> ty::Binder { + self.map_bound(|sig_tys| sig_tys.inputs().get(index).unwrap()) + } + + pub fn inputs_and_output(self) -> ty::Binder { + self.map_bound(|sig_tys| sig_tys.0) + } + + #[inline] + pub fn output(self) -> ty::Binder { + self.map_bound(|sig_tys| sig_tys.output()) + } +} + +#[derive_where(Clone, Copy, Debug, PartialEq, Eq, Hash; I: Interner)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +pub struct FnHeader { + pub c_variadic: bool, + pub safety: I::Safety, + pub abi: I::Abi, +} diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 81717ce4a2266..9d907baeeb302 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -197,7 +197,7 @@ impl ClosureArgs { /// Extracts the signature from the closure. pub fn sig(self) -> ty::Binder> { match self.sig_as_fn_ptr_ty().kind() { - ty::FnPtr(sig) => sig, + ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr), ty => panic!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {ty:?}"), } } @@ -292,21 +292,23 @@ impl CoroutineClosureArgs { pub fn coroutine_closure_sig(self) -> ty::Binder> { let interior = self.coroutine_witness_ty(); - let ty::FnPtr(sig) = self.signature_parts_ty().kind() else { panic!() }; - sig.map_bound(|sig| { - let [resume_ty, tupled_inputs_ty] = *sig.inputs().as_slice() else { + let ty::FnPtr(sig_tys, hdr) = self.signature_parts_ty().kind() else { panic!() }; + sig_tys.map_bound(|sig_tys| { + let [resume_ty, tupled_inputs_ty] = *sig_tys.inputs().as_slice() else { panic!(); }; - let [yield_ty, return_ty] = *sig.output().tuple_fields().as_slice() else { panic!() }; + let [yield_ty, return_ty] = *sig_tys.output().tuple_fields().as_slice() else { + panic!() + }; CoroutineClosureSignature { interior, tupled_inputs_ty, resume_ty, yield_ty, return_ty, - c_variadic: sig.c_variadic, - safety: sig.safety, - abi: sig.abi, + c_variadic: hdr.c_variadic, + safety: hdr.safety, + abi: hdr.abi, } }) } @@ -321,7 +323,7 @@ impl CoroutineClosureArgs { pub fn has_self_borrows(&self) -> bool { match self.coroutine_captures_by_ref_ty().kind() { - ty::FnPtr(sig) => sig + ty::FnPtr(sig_tys, _) => sig_tys .skip_binder() .visit_with(&mut HasRegionsBoundAt { binder: ty::INNERMOST }) .is_break(), @@ -460,11 +462,11 @@ impl CoroutineClosureSignature { ) -> I::Ty { match kind { ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { - let ty::FnPtr(sig) = coroutine_captures_by_ref_ty.kind() else { + let ty::FnPtr(sig_tys, _) = coroutine_captures_by_ref_ty.kind() else { panic!(); }; let coroutine_captures_by_ref_ty = - sig.output().skip_binder().fold_with(&mut FoldEscapingRegions { + sig_tys.output().skip_binder().fold_with(&mut FoldEscapingRegions { interner: cx, region: env_region, debruijn: ty::INNERMOST, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index cffadc7c10a91..53757349a9b2c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2069,7 +2069,7 @@ pub(crate) fn clean_middle_ty<'tcx>( Some(ContainerTy::Ref(r)), )), }, - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { // FIXME: should we merge the outer and inner binders somehow? let sig = bound_ty.skip_binder().fn_sig(cx.tcx); let decl = clean_poly_fn_sig(cx, None, sig); diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 9c7eee4040bdf..5b96529fed70d 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -495,7 +495,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ty::RawPtr(_, _) => Res::Primitive(RawPointer), ty::Ref(..) => Res::Primitive(Reference), ty::FnDef(..) => panic!("type alias to a function definition"), - ty::FnPtr(_) => Res::Primitive(Fn), + ty::FnPtr(..) => Res::Primitive(Fn), ty::Never => Res::Primitive(Never), ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) | ty::Foreign(did) => { Res::from_def_id(self.cx.tcx, did) diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs index f263bec1576d0..dbe03e4ae8095 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -15,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, } match cast_from.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 75de53f73ee7a..5dc6df1e907d0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => { /* continue to checks */ }, } - if let ty::FnDef(..) | ty::FnPtr(_) = cast_from.kind() { + if let ty::FnDef(..) | ty::FnPtr(..) = cast_from.kind() { let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index 0e11bcfb8ecdb..dfbae1618ac62 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => return, } match cast_from.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index a74b3a8c8362c..05c3cd3c81407 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -236,7 +236,7 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option Some(cx.tcx.fn_sig(*def_id).instantiate_identity()), - ty::FnPtr(fn_sig) => Some(*fn_sig), + ty::FnPtr(sig_tys, hdr) => Some(sig_tys.with(*hdr)), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index d0cb24884686a..0e55d3db469ae 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -872,7 +872,7 @@ impl TyCoercionStability { | ty::Pat(..) | ty::Float(_) | ty::RawPtr(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Str | ty::Slice(..) | ty::Adt(..) diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 5a7226d590c4d..a7e831fdc42a8 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -158,7 +158,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc cx.tcx.fn_sig(def).skip_binder().skip_binder() }, - ty::FnPtr(sig) => sig.skip_binder(), + ty::FnPtr(sig_tys, hdr) => sig_tys.with(*hdr).skip_binder(), ty::Closure(_, subs) => cx .tcx .signature_unclosure(subs.as_closure().sig(), Safety::Safe) diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index def8be2ef73dc..22a03825194e0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -58,7 +58,7 @@ fn try_get_caller_ty_name_and_method_name( fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { let map_closure_ty = cx.typeck_results().expr_ty(map_arg); match map_closure_ty.kind() { - ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { + ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(..) => { let map_closure_sig = match map_closure_ty.kind() { ty::Closure(_, args) => args.as_closure().sig(), _ => map_closure_ty.fn_sig(cx.tcx), diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 6964d8c8dbb33..0b3769ecb7cc4 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -166,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { ExprKind::Call(func, _) => { let typ = self.cx.typeck_results().expr_ty(func); match typ.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { let sig = typ.fn_sig(self.cx.tcx); if self.cx.tcx.instantiate_bound_regions_with_erased(sig).output().kind() == &ty::Never { self.report_diverging_sub_expr(e); diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index da74a7c7145a6..0bde0da3cd814 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -130,7 +130,7 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Call(path_expr, _) => { let sig = match *cx.typeck_results().expr_ty(path_expr).kind() { ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(), - ty::FnPtr(sig) => sig, + ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr), _ => return Continue(Descend::Yes), }; if sig.safety() == Safety::Unsafe { diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index 0a3b769c3e604..3c0f06f66d105 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -79,7 +79,7 @@ fn check_arguments<'tcx>( fn_kind: &str, ) { match type_definition.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); for (argument, parameter) in iter::zip(arguments, parameters) { match parameter.kind() { diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index bd48990aea95f..2f6faba073e95 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -541,7 +541,7 @@ pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { /// Returns `true` if the given type is an `unsafe` function. pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).safety() == Safety::Unsafe, + ty::FnDef(..) | ty::FnPtr(..) => ty.fn_sig(cx.tcx).safety() == Safety::Unsafe, _ => false, } } @@ -721,7 +721,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(sig, None)), + ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), ty::Dynamic(bounds, _, _) => { let lang_items = cx.tcx.lang_items(); match bounds.principal() { diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 2a5d3536ff6b7..e5b6d3965e930 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -441,7 +441,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe => { self.is_unsafe = true; }, - ty::FnPtr(sig) if sig.safety() == Safety::Unsafe => self.is_unsafe = true, + ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => self.is_unsafe = true, _ => walk_expr(self, e), }, ExprKind::Path(ref p) From acb024110f337e77c515ebcafffd6c4c09c29ee7 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Fri, 9 Aug 2024 01:32:13 +0000 Subject: [PATCH 061/178] Add windows-targets crate to std's sysroot --- library/Cargo.lock | 7 ++++++- library/std/Cargo.toml | 5 ++++- library/std/src/sys/pal/windows/alloc.rs | 2 +- library/std/src/sys/pal/windows/c.rs | 2 -- library/std/src/sys/pal/windows/c/windows_sys.rs | 1 - library/windows_targets/Cargo.toml | 10 ++++++++++ .../windows_targets.rs => windows_targets/src/lib.rs} | 4 ++++ src/tools/generate-windows-sys/src/main.rs | 1 - src/tools/tidy/src/pal.rs | 1 + 9 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 library/windows_targets/Cargo.toml rename library/{std/src/sys/pal/windows/c/windows_targets.rs => windows_targets/src/lib.rs} (95%) diff --git a/library/Cargo.lock b/library/Cargo.lock index c5182516f7d14..8430b30ae9b79 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -339,6 +339,7 @@ dependencies = [ "std_detect", "unwind", "wasi", + "windows-targets 0.0.0", ] [[package]] @@ -421,9 +422,13 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", ] +[[package]] +name = "windows-targets" +version = "0.0.0" + [[package]] name = "windows-targets" version = "0.52.5" diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 06e818fb7c09d..cea74f651acd8 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -57,6 +57,9 @@ object = { version = "0.36.0", default-features = false, optional = true, featur 'archive', ] } +[target.'cfg(windows)'.dependencies.windows-targets] +path = "../windows_targets" + [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } rand_xorshift = "0.3.0" @@ -116,7 +119,7 @@ std_detect_env_override = ["std_detect/std_detect_env_override"] # Enable using raw-dylib for Windows imports. # This will eventually be the default. -windows_raw_dylib = [] +windows_raw_dylib = ["windows-targets/windows_raw_dylib"] [package.metadata.fortanix-sgx] # Maximum possible number of threads when testing diff --git a/library/std/src/sys/pal/windows/alloc.rs b/library/std/src/sys/pal/windows/alloc.rs index 92b68b26032c6..2205885687dea 100644 --- a/library/std/src/sys/pal/windows/alloc.rs +++ b/library/std/src/sys/pal/windows/alloc.rs @@ -4,7 +4,7 @@ use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ffi::c_void; use crate::ptr; use crate::sync::atomic::{AtomicPtr, Ordering}; -use crate::sys::c::{self, windows_targets}; +use crate::sys::c; use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[cfg(test)] diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 08b75186aef90..2f5d75dc4bc23 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -8,8 +8,6 @@ use core::ffi::{c_uint, c_ulong, c_ushort, c_void, CStr}; use core::{mem, ptr}; -pub(super) mod windows_targets; - mod windows_sys; pub use windows_sys::*; diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 9f22f54819509..529c96a0e1e6b 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -3317,4 +3317,3 @@ pub struct WSADATA { #[cfg(target_arch = "arm")] pub enum CONTEXT {} // ignore-tidy-filelength -use super::windows_targets; diff --git a/library/windows_targets/Cargo.toml b/library/windows_targets/Cargo.toml new file mode 100644 index 0000000000000..94d7c8210647c --- /dev/null +++ b/library/windows_targets/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "windows-targets" +description = "A drop-in replacement for the real windows-targets crate for use in std only." +version = "0.0.0" +edition = "2021" + +[features] +# Enable using raw-dylib for Windows imports. +# This will eventually be the default. +windows_raw_dylib = [] diff --git a/library/std/src/sys/pal/windows/c/windows_targets.rs b/library/windows_targets/src/lib.rs similarity index 95% rename from library/std/src/sys/pal/windows/c/windows_targets.rs rename to library/windows_targets/src/lib.rs index 252bceb70942b..1965b6cf4ce8f 100644 --- a/library/std/src/sys/pal/windows/c/windows_targets.rs +++ b/library/windows_targets/src/lib.rs @@ -2,6 +2,10 @@ //! //! This is a simple wrapper around an `extern` block with a `#[link]` attribute. //! It's very roughly equivalent to the windows-targets crate. +#![no_std] +#![no_core] +#![feature(decl_macro)] +#![feature(no_core)] #[cfg(feature = "windows_raw_dylib")] pub macro link { diff --git a/src/tools/generate-windows-sys/src/main.rs b/src/tools/generate-windows-sys/src/main.rs index fe1b1bd5ceb14..6dbf29d957f12 100644 --- a/src/tools/generate-windows-sys/src/main.rs +++ b/src/tools/generate-windows-sys/src/main.rs @@ -35,7 +35,6 @@ fn main() -> Result<(), Box> { let mut f = std::fs::File::options().append(true).open("windows_sys.rs")?; f.write_all(ARM32_SHIM.as_bytes())?; writeln!(&mut f, "// ignore-tidy-filelength")?; - writeln!(&mut f, "use super::windows_targets;")?; Ok(()) } diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index b7ddf47186d99..c650fd0eec6d8 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -36,6 +36,7 @@ use crate::walk::{filter_dirs, walk}; // Paths that may contain platform-specific code. const EXCEPTION_PATHS: &[&str] = &[ + "library/windows_targets", "library/panic_abort", "library/panic_unwind", "library/unwind", From a1b2b7ff78b9dbaeffe8341b3616a8c8c4fd4351 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Fri, 9 Aug 2024 11:04:25 +0000 Subject: [PATCH 062/178] Exclude windows-targets from the workspace --- library/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/library/Cargo.toml b/library/Cargo.toml index c4513b4c127d8..d8ece6b0ebd3e 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -8,6 +8,7 @@ members = [ exclude = [ # stdarch has its own Cargo workspace "stdarch", + "windows_targets" ] [profile.release.package.compiler_builtins] From 03a7abc48fb13723bdf8132aa46bc9a5ecf0225b Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 23:24:57 +0200 Subject: [PATCH 063/178] Use Sender directly instead of a boxed closure --- .../rust-analyzer/crates/rust-analyzer/src/flycheck.rs | 8 ++++---- .../rust-analyzer/crates/rust-analyzer/src/reload.rs | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index c2b943d1d6f41..168f9702d1c8b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -109,7 +109,7 @@ pub(crate) struct FlycheckHandle { impl FlycheckHandle { pub(crate) fn spawn( id: usize, - sender: Box, + sender: Sender, config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, @@ -199,7 +199,7 @@ enum StateChange { struct FlycheckActor { /// The workspace id of this flycheck instance. id: usize, - sender: Box, + sender: Sender, config: FlycheckConfig, manifest_path: Option, /// Either the workspace root of the workspace we are flychecking, @@ -235,7 +235,7 @@ pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; impl FlycheckActor { fn new( id: usize, - sender: Box, + sender: Sender, config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, @@ -479,7 +479,7 @@ impl FlycheckActor { } fn send(&self, check_task: FlycheckMessage) { - (self.sender)(check_task); + self.sender.send(check_task).unwrap(); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index e432f5d5cff73..dee34b1b393d2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -758,7 +758,7 @@ impl GlobalState { self.flycheck = match invocation_strategy { crate::flycheck::InvocationStrategy::Once => vec![FlycheckHandle::spawn( 0, - Box::new(move |msg| sender.send(msg).unwrap()), + sender, config, None, self.config.root_path().clone(), @@ -793,10 +793,9 @@ impl GlobalState { )) }) .map(|(id, (root, manifest_path), sysroot_root)| { - let sender = sender.clone(); FlycheckHandle::spawn( id, - Box::new(move |msg| sender.send(msg).unwrap()), + sender.clone(), config.clone(), sysroot_root, root.to_path_buf(), From f4b1b4bb829daa9463092522d9dc253b9e141fd4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 23:40:32 +0200 Subject: [PATCH 064/178] Use Sender instead of boxed closure in vfs --- src/tools/rust-analyzer/Cargo.lock | 1 + .../crates/load-cargo/src/lib.rs | 3 +- .../crates/rust-analyzer/src/global_state.rs | 3 +- .../crates/vfs-notify/src/lib.rs | 59 +++++++++++-------- src/tools/rust-analyzer/crates/vfs/Cargo.toml | 1 + .../rust-analyzer/crates/vfs/src/loader.rs | 2 +- 6 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index b6bf516af1542..265590fe2e84a 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2317,6 +2317,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "vfs" version = "0.0.0" dependencies = [ + "crossbeam-channel", "fst", "indexmap", "nohash-hasher", diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index eed7d9360e0c6..db4e10f7ba1e9 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -64,8 +64,7 @@ pub fn load_workspace( let (sender, receiver) = unbounded(); let mut vfs = vfs::Vfs::default(); let mut loader = { - let loader = - vfs_notify::NotifyHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap())); + let loader = vfs_notify::NotifyHandle::spawn(sender); Box::new(loader) }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index df809c0723553..bb883a9eaf5cf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -185,8 +185,7 @@ impl GlobalState { pub(crate) fn new(sender: Sender, config: Config) -> GlobalState { let loader = { let (sender, receiver) = unbounded::(); - let handle: vfs_notify::NotifyHandle = - vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap())); + let handle: vfs_notify::NotifyHandle = vfs::loader::Handle::spawn(sender); let handle = Box::new(handle) as Box; Handle { handle, receiver } }; diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 57e83ac0a89b2..7b0f67024c6f1 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -119,18 +119,19 @@ impl NotifyActor { self.watched_dir_entries.clear(); self.watched_file_entries.clear(); - let send = |msg| (self.sender)(msg); - send(loader::Message::Progress { - n_total, - n_done: LoadingProgress::Started, - config_version, - dir: None, - }); + self.sender + .send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Started, + config_version, + dir: None, + }) + .unwrap(); let (entry_tx, entry_rx) = unbounded(); let (watch_tx, watch_rx) = unbounded(); let processed = AtomicUsize::new(0); - config.load.into_par_iter().enumerate().for_each(move |(i, entry)| { + config.load.into_par_iter().enumerate().for_each(|(i, entry)| { let do_watch = config.watch.contains(&i); if do_watch { _ = entry_tx.send(entry.clone()); @@ -140,25 +141,31 @@ impl NotifyActor { entry, do_watch, |file| { - send(loader::Message::Progress { - n_total, - n_done: LoadingProgress::Progress( - processed.load(std::sync::atomic::Ordering::Relaxed), - ), - dir: Some(file), - config_version, - }) + self.sender + .send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Progress( + processed + .load(std::sync::atomic::Ordering::Relaxed), + ), + dir: Some(file), + config_version, + }) + .unwrap() }, ); - send(loader::Message::Loaded { files }); - send(loader::Message::Progress { - n_total, - n_done: LoadingProgress::Progress( - processed.fetch_add(1, std::sync::atomic::Ordering::AcqRel) + 1, - ), - config_version, - dir: None, - }); + self.sender.send(loader::Message::Loaded { files }).unwrap(); + self.sender + .send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Progress( + processed.fetch_add(1, std::sync::atomic::Ordering::AcqRel) + + 1, + ), + config_version, + dir: None, + }) + .unwrap(); }); for path in watch_rx { self.watch(&path); @@ -317,7 +324,7 @@ impl NotifyActor { } fn send(&self, msg: loader::Message) { - (self.sender)(msg); + self.sender.send(msg).unwrap(); } } diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml index c6f34e2af082c..e8a6195036ed7 100644 --- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml @@ -18,6 +18,7 @@ tracing.workspace = true fst = "0.4.7" indexmap.workspace = true nohash-hasher.workspace = true +crossbeam-channel.workspace = true paths.workspace = true stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs/src/loader.rs b/src/tools/rust-analyzer/crates/vfs/src/loader.rs index 30c08a9ff2b9c..f24354cb493ca 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/loader.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/loader.rs @@ -72,7 +72,7 @@ pub enum Message { } /// Type that will receive [`Messages`](Message) from a [`Handle`]. -pub type Sender = Box; +pub type Sender = crossbeam_channel::Sender; /// Interface for reading and watching files. pub trait Handle: fmt::Debug { From 58c614f5b4892b946fad8222f16b01bb6fab3919 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 23:48:03 +0200 Subject: [PATCH 065/178] Use crossbeam-channel from the workspace --- src/tools/rust-analyzer/crates/ide-db/Cargo.toml | 2 +- .../crates/rust-analyzer/Cargo.toml | 16 ++++++++-------- src/tools/rust-analyzer/crates/stdx/Cargo.toml | 2 +- .../rust-analyzer/crates/vfs-notify/Cargo.toml | 2 +- .../rust-analyzer/lib/lsp-server/Cargo.toml | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 6714a99f80eb4..8f3cae2fa1703 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -crossbeam-channel = "0.5.5" +crossbeam-channel.workspace = true tracing.workspace = true rayon.workspace = true fst = { version = "0.4.7", default-features = false } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index f0878b25dd30c..eb95f42d755c9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -21,7 +21,7 @@ path = "src/bin/main.rs" [dependencies] anyhow.workspace = true -crossbeam-channel = "0.5.5" +crossbeam-channel.workspace = true dirs = "5.0.1" dissimilar.workspace = true itertools.workspace = true @@ -90,13 +90,13 @@ jemalloc = ["jemallocator", "profile/jemalloc"] force-always-assert = ["always-assert/force"] sysroot-abi = [] in-rust-tree = [ - "sysroot-abi", - "syntax/in-rust-tree", - "parser/in-rust-tree", - "hir/in-rust-tree", - "hir-def/in-rust-tree", - "hir-ty/in-rust-tree", - "load-cargo/in-rust-tree", + "sysroot-abi", + "syntax/in-rust-tree", + "parser/in-rust-tree", + "hir/in-rust-tree", + "hir-def/in-rust-tree", + "hir-ty/in-rust-tree", + "load-cargo/in-rust-tree", ] [lints] diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index 77c9f3cb1486c..bf0d6df9ad810 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -17,7 +17,7 @@ backtrace = { version = "0.3.67", optional = true } always-assert = { version = "0.2.0", features = ["tracing"] } jod-thread = "0.1.2" libc.workspace = true -crossbeam-channel = "0.5.5" +crossbeam-channel.workspace = true itertools.workspace = true # Think twice before adding anything here diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index d2f7079915ffc..09296dc6dd533 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -15,7 +15,7 @@ doctest = false [dependencies] tracing.workspace = true walkdir = "2.3.2" -crossbeam-channel = "0.5.5" +crossbeam-channel.workspace = true notify = "6.1.1" rayon = "1.10.0" diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index a89eb4b144c68..fb3411c8ab431 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -10,11 +10,11 @@ edition = "2021" log = "0.4.17" serde_json = "1.0.108" serde = { version = "1.0.192", features = ["derive"] } -crossbeam-channel = "0.5.8" +crossbeam-channel.workspace = true [dev-dependencies] lsp-types = "=0.95" ctrlc = "3.4.1" [lints] -workspace = true \ No newline at end of file +workspace = true From 7341b88e6f1fd39e4650aa26530ac53b1888d9ca Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 23:59:42 +0200 Subject: [PATCH 066/178] Remove unneeded `send` method --- .../crates/rust-analyzer/src/flycheck.rs | 26 ++++++++++--------- .../crates/rust-analyzer/src/global_state.rs | 12 +++------ .../crates/vfs-notify/src/lib.rs | 22 +++++++--------- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 168f9702d1c8b..0ea782e1dee72 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -256,7 +256,7 @@ impl FlycheckActor { } fn report_progress(&self, progress: Progress) { - self.send(FlycheckMessage::Progress { id: self.id, progress }); + self.sender.send(FlycheckMessage::Progress { id: self.id, progress }).unwrap(); } fn next_event(&self, inbox: &Receiver) -> Option { @@ -329,7 +329,9 @@ impl FlycheckActor { ); } if self.status == FlycheckStatus::Started { - self.send(FlycheckMessage::ClearDiagnostics { id: self.id }); + self.sender + .send(FlycheckMessage::ClearDiagnostics { id: self.id }) + .unwrap(); } self.report_progress(Progress::DidFinish(res)); self.status = FlycheckStatus::Finished; @@ -351,13 +353,17 @@ impl FlycheckActor { "diagnostic received" ); if self.status == FlycheckStatus::Started { - self.send(FlycheckMessage::ClearDiagnostics { id: self.id }); + self.sender + .send(FlycheckMessage::ClearDiagnostics { id: self.id }) + .unwrap(); } - self.send(FlycheckMessage::AddDiagnostic { - id: self.id, - workspace_root: self.root.clone(), - diagnostic: msg, - }); + self.sender + .send(FlycheckMessage::AddDiagnostic { + id: self.id, + workspace_root: self.root.clone(), + diagnostic: msg, + }) + .unwrap(); self.status = FlycheckStatus::DiagnosticSent; } }, @@ -477,10 +483,6 @@ impl FlycheckActor { cmd.args(args); Some(cmd) } - - fn send(&self, check_task: FlycheckMessage) { - self.sender.send(check_task).unwrap(); - } } #[allow(clippy::large_enum_variant)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index bb883a9eaf5cf..71f4896727110 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -504,7 +504,7 @@ impl GlobalState { handler: ReqHandler, ) { let request = self.req_queue.outgoing.register(R::METHOD.to_owned(), params, handler); - self.send(request.into()); + self.sender.send(request.into()).unwrap(); } pub(crate) fn complete_request(&mut self, response: lsp_server::Response) { @@ -521,7 +521,7 @@ impl GlobalState { params: N::Params, ) { let not = lsp_server::Notification::new(N::METHOD.to_owned(), params); - self.send(not.into()); + self.sender.send(not.into()).unwrap(); } pub(crate) fn register_request( @@ -544,13 +544,13 @@ impl GlobalState { let duration = start.elapsed(); tracing::debug!("handled {} - ({}) in {:0.2?}", method, response.id, duration); - self.send(response.into()); + self.sender.send(response.into()).unwrap(); } } pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) { if let Some(response) = self.req_queue.incoming.cancel(request_id) { - self.send(response.into()); + self.sender.send(response.into()).unwrap(); } } @@ -558,10 +558,6 @@ impl GlobalState { self.req_queue.incoming.is_completed(&request.id) } - fn send(&self, message: lsp_server::Message) { - self.sender.send(message).unwrap() - } - pub(crate) fn publish_diagnostics( &mut self, uri: Url, diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 7b0f67024c6f1..2bd4eb671353d 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -180,17 +180,19 @@ impl NotifyActor { } } } - self.send(loader::Message::Progress { - n_total, - n_done: LoadingProgress::Finished, - config_version, - dir: None, - }); + self.sender + .send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Finished, + config_version, + dir: None, + }) + .unwrap(); } Message::Invalidate(path) => { let contents = read(path.as_path()); let files = vec![(path, contents)]; - self.send(loader::Message::Changed { files }); + self.sender.send(loader::Message::Changed { files }).unwrap(); } }, Event::NotifyEvent(event) => { @@ -238,7 +240,7 @@ impl NotifyActor { Some((path, contents)) }) .collect(); - self.send(loader::Message::Changed { files }); + self.sender.send(loader::Message::Changed { files }).unwrap(); } } } @@ -322,10 +324,6 @@ impl NotifyActor { log_notify_error(watcher.watch(path, RecursiveMode::NonRecursive)); } } - - fn send(&self, msg: loader::Message) { - self.sender.send(msg).unwrap(); - } } fn read(path: &AbsPath) -> Option> { From dd90d4e122fd1bc40f948809c524cf86d90dc81f Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 10 Aug 2024 00:24:55 +0200 Subject: [PATCH 067/178] Simplify `check_command` while avoiding allocations --- .../crates/rust-analyzer/src/flycheck.rs | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index c2b943d1d6f41..da6e694e87957 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -388,7 +388,7 @@ impl FlycheckActor { package: Option<&str>, saved_file: Option<&AbsPath>, ) -> Option { - let (mut cmd, args) = match &self.config { + match &self.config { FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { let mut cmd = Command::new(Tool::Cargo.path()); if let Some(sysroot_root) = &self.sysroot_root { @@ -419,7 +419,8 @@ impl FlycheckActor { cmd.arg("--keep-going"); options.apply_on_command(&mut cmd); - (cmd, options.extra_args.clone()) + cmd.args(&options.extra_args); + Some(cmd) } FlycheckConfig::CustomCommand { command, @@ -448,34 +449,31 @@ impl FlycheckActor { } } - if args.contains(&SAVED_FILE_PLACEHOLDER.to_owned()) { - // If the custom command has a $saved_file placeholder, and - // we're saving a file, replace the placeholder in the arguments. - if let Some(saved_file) = saved_file { - let args = args - .iter() - .map(|arg| { - if arg == SAVED_FILE_PLACEHOLDER { - saved_file.to_string() - } else { - arg.clone() - } - }) - .collect(); - (cmd, args) - } else { - // The custom command has a $saved_file placeholder, - // but we had an IDE event that wasn't a file save. Do nothing. - return None; + // If the custom command has a $saved_file placeholder, and + // we're saving a file, replace the placeholder in the arguments. + if let Some(saved_file) = saved_file { + for arg in args { + if arg == SAVED_FILE_PLACEHOLDER { + cmd.arg(saved_file); + } else { + cmd.arg(arg); + } } } else { - (cmd, args.clone()) + for arg in args { + if arg == SAVED_FILE_PLACEHOLDER { + // The custom command has a $saved_file placeholder, + // but we had an IDE event that wasn't a file save. Do nothing. + return None; + } + + cmd.arg(arg); + } } - } - }; - cmd.args(args); - Some(cmd) + Some(cmd) + } + } } fn send(&self, check_task: FlycheckMessage) { From 35903a22aab2536afd939130a26c41289c2ca326 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 10 Aug 2024 02:05:08 +0200 Subject: [PATCH 068/178] Use select_biased --- .../crates/rust-analyzer/src/flycheck.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index da6e694e87957..797bc8e45ae19 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -3,7 +3,7 @@ use std::{fmt, io, process::Command, time::Duration}; -use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; +use crossbeam_channel::{select_biased, unbounded, Receiver, Sender}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::Deserialize; @@ -260,13 +260,14 @@ impl FlycheckActor { } fn next_event(&self, inbox: &Receiver) -> Option { - if let Ok(msg) = inbox.try_recv() { - // give restarts a preference so check outputs don't block a restart or stop - return Some(Event::RequestStateChange(msg)); - } - select! { + let Some(command_receiver) = &self.command_receiver else { + return inbox.recv().ok().map(Event::RequestStateChange); + }; + + // Biased to give restarts a preference so check outputs don't block a restart or stop + select_biased! { recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange), - recv(self.command_receiver.as_ref().unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())), + recv(command_receiver) -> msg => Some(Event::CheckEvent(msg.ok())), } } From 204fb5bd4d85a811ae832413c0bbc76196f1a55d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 10 Aug 2024 02:12:09 +0200 Subject: [PATCH 069/178] Avoid the overhead of select! when possible --- src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 57e83ac0a89b2..fa561040c9339 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -13,7 +13,7 @@ use std::{ sync::atomic::AtomicUsize, }; -use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; +use crossbeam_channel::{select, unbounded, Receiver, Sender}; use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rayon::iter::{IndexedParallelIterator as _, IntoParallelIterator as _, ParallelIterator}; @@ -85,10 +85,13 @@ impl NotifyActor { } fn next_event(&self, receiver: &Receiver) -> Option { - let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver); + let Some((_, watcher_receiver)) = &self.watcher else { + return receiver.recv().ok().map(Event::Message); + }; + select! { recv(receiver) -> it => it.ok().map(Event::Message), - recv(watcher_receiver.unwrap_or(&never())) -> it => Some(Event::NotifyEvent(it.unwrap())), + recv(watcher_receiver) -> it => Some(Event::NotifyEvent(it.unwrap())), } } From 18b9458d64d05ccb6618119d8d75e7f772502355 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sat, 10 Aug 2024 14:55:46 +0900 Subject: [PATCH 070/178] feat: Implement TAIT --- .../rust-analyzer/crates/hir-def/src/db.rs | 2 +- .../crates/hir-ty/src/chalk_db.rs | 2 +- .../crates/hir-ty/src/chalk_ext.rs | 4 +- .../crates/hir-ty/src/display.rs | 7 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 164 ++++++++++++------ .../crates/hir-ty/src/infer/coerce.rs | 6 +- .../crates/hir-ty/src/infer/unify.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 4 +- .../crates/hir-ty/src/mir/monomorphization.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 1 + .../crates/hir-ty/src/tests/traits.rs | 113 ------------ .../src/tests/type_alias_impl_traits.rs | 161 +++++++++++++++++ 14 files changed, 288 insertions(+), 188 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 56feb0163e13a..9ba99788fb29c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -160,7 +160,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; #[salsa::invoke(StaticData::static_data_query)] - fn static_data(&self, konst: StaticId) -> Arc; + fn static_data(&self, statik: StaticId) -> Arc; #[salsa::invoke(Macro2Data::macro2_data_query)] fn macro2_data(&self, makro: Macro2Id) -> Arc; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index d506e00ca1239..a151ee01e6455 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -275,7 +275,7 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { }; chalk_ir::Binders::new(binders, bound) } - crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => { let datas = self .db .type_alias_impl_traits(alias) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 5765262b08bea..302558162ac96 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -276,7 +276,7 @@ impl TyExt for Ty { data.substitute(Interner, &subst).into_value_and_skipped_binders().0 }) } - ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + ImplTraitId::TypeAliasImplTrait(alias, idx) => { db.type_alias_impl_traits(alias).map(|it| { let data = (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); @@ -295,7 +295,7 @@ impl TyExt for Ty { data.substitute(Interner, &opaque_ty.substitution) }) } - ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + ImplTraitId::TypeAliasImplTrait(alias, idx) => { db.type_alias_impl_traits(alias).map(|it| { let data = (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 47ea2f5347c5f..2e093d76e57eb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1151,11 +1151,10 @@ impl HirDisplay for Ty { )?; // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } - ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + ImplTraitId::TypeAliasImplTrait(alias, idx) => { let datas = db.type_alias_impl_traits(alias).expect("impl trait id without data"); - let data = - (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let data = (*datas).as_ref().map(|it| it.impl_traits[idx].bounds.clone()); let bounds = data.substitute(Interner, ¶meters); let krate = alias.krate(db.upcast()); write_bounds_like_dyn_trait_with_prefix( @@ -1338,7 +1337,7 @@ impl HirDisplay for Ty { SizedByDefault::Sized { anchor: krate }, )?; } - ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + ImplTraitId::TypeAliasImplTrait(alias, idx) => { let datas = db.type_alias_impl_traits(alias).expect("impl trait id without data"); let data = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 804bc53905ad7..45d423d03c021 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -36,15 +36,14 @@ use hir_def::{ body::Body, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, data::{ConstData, StaticData}, - hir::LabelId, - hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId}, + hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::{LangItem, LangItemTarget}, layout::Integer, path::{ModPath, Path}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, type_ref::{LifetimeRef, TypeRef}, - AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId, - TupleFieldId, TupleId, TypeAliasId, VariantId, + AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ImplId, ItemContainerId, Lookup, + TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, }; use hir_expand::name::Name; use indexmap::IndexSet; @@ -785,14 +784,19 @@ impl<'a> InferenceContext<'a> { fn collect_const(&mut self, data: &ConstData) { let return_ty = self.make_ty(&data.type_ref); - // Constants might be associated items that define ATPITs. - self.insert_atpit_coercion_table(iter::once(&return_ty)); + // Constants might be defining usage sites of TAITs. + self.make_tait_coercion_table(iter::once(&return_ty)); self.return_ty = return_ty; } fn collect_static(&mut self, data: &StaticData) { - self.return_ty = self.make_ty(&data.type_ref); + let return_ty = self.make_ty(&data.type_ref); + + // Statics might be defining usage sites of TAITs. + self.make_tait_coercion_table(iter::once(&return_ty)); + + self.return_ty = return_ty; } fn collect_fn(&mut self, func: FunctionId) { @@ -857,11 +861,11 @@ impl<'a> InferenceContext<'a> { self.return_ty = self.normalize_associated_types_in(return_ty); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); - // Functions might be associated items that define ATPITs. - // To define an ATPITs, that ATPIT must appear in the function's signatures. + // Functions might be defining usage sites of TAITs. + // To define an TAITs, that TAIT must appear in the function's signatures. // So, it suffices to check for params and return types. params_and_ret_tys.push(self.return_ty.clone()); - self.insert_atpit_coercion_table(params_and_ret_tys.iter()); + self.make_tait_coercion_table(params_and_ret_tys.iter()); } fn insert_inference_vars_for_impl_trait(&mut self, t: T, placeholders: Substitution) -> T @@ -880,7 +884,7 @@ impl<'a> InferenceContext<'a> { ImplTraitId::ReturnTypeImplTrait(def, idx) => { (self.db.return_type_impl_traits(def), idx) } - ImplTraitId::AssociatedTypeImplTrait(def, idx) => { + ImplTraitId::TypeAliasImplTrait(def, idx) => { (self.db.type_alias_impl_traits(def), idx) } _ => unreachable!(), @@ -909,23 +913,25 @@ impl<'a> InferenceContext<'a> { } /// The coercion of a non-inference var into an opaque type should fail, - /// but not in the defining sites of the ATPITs. - /// In such cases, we insert an proxy inference var for each ATPIT, - /// and coerce into it instead of ATPIT itself. + /// but not in the defining sites of the TAITs. + /// In such cases, we insert an proxy inference var for each TAIT, + /// and coerce into it instead of TAIT itself. /// /// The inference var stretagy is effective because; /// - /// - It can still unify types that coerced into ATPIT + /// - It can still unify types that coerced into TAITs /// - We are pushing `impl Trait` bounds into it /// /// This function inserts a map that maps the opaque type to that proxy inference var. - fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator) { - struct OpaqueTyCollector<'a, 'b> { + fn make_tait_coercion_table<'b>(&mut self, tait_candidates: impl Iterator) { + struct TypeAliasImplTraitCollector<'a, 'b> { + db: &'b dyn HirDatabase, table: &'b mut InferenceTable<'a>, - opaque_tys: FxHashMap, + assocs: FxHashMap, + non_assocs: FxHashMap, } - impl<'a, 'b> TypeVisitor for OpaqueTyCollector<'a, 'b> { + impl<'a, 'b> TypeVisitor for TypeAliasImplTraitCollector<'a, 'b> { type BreakTy = (); fn as_dyn(&mut self) -> &mut dyn TypeVisitor { @@ -944,59 +950,105 @@ impl<'a> InferenceContext<'a> { let ty = self.table.resolve_ty_shallow(ty); if let TyKind::OpaqueType(id, _) = ty.kind(Interner) { - self.opaque_tys.insert(*id, ty.clone()); + if let ImplTraitId::TypeAliasImplTrait(alias_id, _) = + self.db.lookup_intern_impl_trait_id((*id).into()) + { + let loc = self.db.lookup_intern_type_alias(alias_id); + match loc.container { + ItemContainerId::ImplId(impl_id) => { + self.assocs.insert(*id, (impl_id, ty.clone())); + } + ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => { + self.non_assocs.insert(*id, ty.clone()); + } + _ => {} + } + } } ty.super_visit_with(self, outer_binder) } } - // Early return if this is not happening inside the impl block - let impl_id = if let Some(impl_id) = self.resolver.impl_def() { - impl_id - } else { - return; + let mut collector = TypeAliasImplTraitCollector { + db: self.db, + table: &mut self.table, + assocs: FxHashMap::default(), + non_assocs: FxHashMap::default(), }; - - let assoc_tys: FxHashSet<_> = self - .db - .impl_data(impl_id) - .items - .iter() - .filter_map(|item| match item { - AssocItemId::TypeAliasId(alias) => Some(*alias), - _ => None, - }) - .collect(); - if assoc_tys.is_empty() { - return; + for ty in tait_candidates { + ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); } - let mut collector = - OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() }; - for ty in tys { - ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); + // Non-assoc TAITs can be define-used everywhere as long as they are + // in function signatures or const types, etc + let mut taits = collector.non_assocs; + + // assoc TAITs(ATPITs) can be only define-used inside their impl block. + // They cannot be define-used in inner items like in the following; + // + // ``` + // impl Trait for Struct { + // type Assoc = impl Default; + // + // fn assoc_fn() -> Self::Assoc { + // let foo: Self::Assoc = true; // Allowed here + // + // fn inner() -> Self::Assoc { + // false // Not allowed here + // } + // + // foo + // } + // } + // ``` + let impl_id = match self.owner { + DefWithBodyId::FunctionId(it) => { + let loc = self.db.lookup_intern_function(it); + if let ItemContainerId::ImplId(impl_id) = loc.container { + Some(impl_id) + } else { + None + } + } + DefWithBodyId::ConstId(it) => { + let loc = self.db.lookup_intern_const(it); + if let ItemContainerId::ImplId(impl_id) = loc.container { + Some(impl_id) + } else { + None + } + } + _ => None, + }; + + if let Some(impl_id) = impl_id { + taits.extend(collector.assocs.into_iter().filter_map(|(id, (impl_, ty))| { + if impl_ == impl_id { + Some((id, ty)) + } else { + None + } + })); } - let atpit_coercion_table: FxHashMap<_, _> = collector - .opaque_tys + + let tait_coercion_table: FxHashMap<_, _> = taits .into_iter() - .filter_map(|(opaque_ty_id, ty)| { - if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) = - self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) + .filter_map(|(id, ty)| { + if let ImplTraitId::TypeAliasImplTrait(alias_id, _) = + self.db.lookup_intern_impl_trait_id(id.into()) { - if assoc_tys.contains(&alias_id) { - let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id); - let ty = self.insert_inference_vars_for_impl_trait(ty, alias_placeholders); - return Some((opaque_ty_id, ty)); - } + let subst = TyBuilder::placeholder_subst(self.db, alias_id); + let ty = self.insert_inference_vars_for_impl_trait(ty, subst); + Some((id, ty)) + } else { + None } - - None }) .collect(); - if !atpit_coercion_table.is_empty() { - self.table.atpit_coercion_table = Some(atpit_coercion_table); + if !tait_coercion_table.is_empty() { + self.table.tait_coercion_table = Some(tait_coercion_table); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 72928851f123c..6f85a4a4247c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -276,16 +276,16 @@ impl InferenceTable<'_> { return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); } - // If we are coercing into an ATPIT, coerce into its proxy inference var, instead. + // If we are coercing into a TAIT, coerce into its proxy inference var, instead. let mut to_ty = to_ty; let _to; - if let Some(atpit_table) = &self.atpit_coercion_table { + if let Some(tait_table) = &self.tait_coercion_table { if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) { if !matches!( from_ty.kind(Interner), TyKind::InferenceVar(..) | TyKind::OpaqueType(..) ) { - if let Some(ty) = atpit_table.get(opaque_ty_id) { + if let Some(ty) = tait_table.get(opaque_ty_id) { _to = ty.clone(); to_ty = &_to; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 7ee63af1c229e..3e3578b9f9b87 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -224,7 +224,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) trait_env: Arc, - pub(crate) atpit_coercion_table: Option>, + pub(crate) tait_coercion_table: Option>, var_unification_table: ChalkInferenceTable, type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, @@ -244,7 +244,7 @@ impl<'a> InferenceTable<'a> { InferenceTable { db, trait_env, - atpit_coercion_table: None, + tait_coercion_table: None, var_unification_table: ChalkInferenceTable::new(), type_variable_table: SmallVec::new(), pending_obligations: Vec::new(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 034b9c773c216..47cc2a2f1e6bd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -391,7 +391,7 @@ pub fn layout_of_ty_query( let infer = db.infer(func.into()); return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); } - crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + crate::ImplTraitId::TypeAliasImplTrait(..) => { return Err(LayoutError::NotImplemented); } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 2f93ce31816c3..4c9e0a1e11836 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -595,7 +595,7 @@ impl TypeFoldable for CallableSig { #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum ImplTraitId { ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), - AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx), + TypeAliasImplTrait(hir_def::TypeAliasId, ImplTraitIdx), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), } impl InternValueTrivial for ImplTraitId {} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 444628ff521d6..f0fcdff7e3f01 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -341,7 +341,7 @@ impl<'a> TyLoweringContext<'a> { let impl_trait_id = origin.either( |f| ImplTraitId::ReturnTypeImplTrait(f, idx), - |a| ImplTraitId::AssociatedTypeImplTrait(a, idx), + |a| ImplTraitId::TypeAliasImplTrait(a, idx), ); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); let generics = @@ -2131,7 +2131,6 @@ pub(crate) fn type_alias_impl_traits( if let Some(type_ref) = &data.type_ref { let _ty = ctx.lower_ty(type_ref); } - let generics = generics(db.upcast(), def.into()); let type_alias_impl_traits = ImplTraits { impl_traits: match ctx.impl_trait_mode { ImplTraitLoweringState::Opaque(x) => x.into_inner(), @@ -2141,6 +2140,7 @@ pub(crate) fn type_alias_impl_traits( if type_alias_impl_traits.impl_traits.is_empty() { None } else { + let generics = generics(db.upcast(), def.into()); Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits))) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index 172dea02e6194..8f6582b7f8022 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -82,8 +82,8 @@ impl FallibleTypeFolder for Filler<'_> { }; filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) } - crate::ImplTraitId::AssociatedTypeImplTrait(..) => { - not_supported!("associated type impl trait"); + crate::ImplTraitId::TypeAliasImplTrait(..) => { + not_supported!("type alias impl trait"); } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { not_supported!("async block impl trait"); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 19619008e3d57..0fcd789f761c3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -9,6 +9,7 @@ mod patterns; mod regression; mod simple; mod traits; +mod type_alias_impl_traits; use std::env; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index fb07e718d102c..a98cff2a08b26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4691,119 +4691,6 @@ fn f() { ); } -#[test] -fn associated_type_impl_trait() { - check_types( - r#" -trait Foo {} -struct S1; -impl Foo for S1 {} - -trait Bar { - type Item; - fn bar(&self) -> Self::Item; -} -struct S2; -impl Bar for S2 { - type Item = impl Foo; - fn bar(&self) -> Self::Item { - S1 - } -} - -fn test() { - let x = S2.bar(); - //^ impl Foo + ?Sized -} - "#, - ); -} - -#[test] -fn associated_type_impl_traits_complex() { - check_types( - r#" -struct Unary(T); -struct Binary(T, U); - -trait Foo {} -struct S1; -impl Foo for S1 {} - -trait Bar { - type Item; - fn bar(&self) -> Unary; -} -struct S2; -impl Bar for S2 { - type Item = Unary; - fn bar(&self) -> Unary<::Item> { - Unary(Unary(S1)) - } -} - -trait Baz { - type Target1; - type Target2; - fn baz(&self) -> Binary; -} -struct S3; -impl Baz for S3 { - type Target1 = impl Foo; - type Target2 = Unary; - fn baz(&self) -> Binary { - Binary(S1, Unary(S2)) - } -} - -fn test() { - let x = S3.baz(); - //^ Binary> - let y = x.1.0.bar(); - //^ Unary> -} - "#, - ); -} - -#[test] -fn associated_type_with_impl_trait_in_tuple() { - check_no_mismatches( - r#" -pub trait Iterator { - type Item; -} - -pub trait Value {} - -fn bar>() {} - -fn foo() { - bar(); -} -"#, - ); -} - -#[test] -fn associated_type_with_impl_trait_in_nested_tuple() { - check_no_mismatches( - r#" -pub trait Iterator { - type Item; -} - -pub trait Value {} - -fn bar>() {} - -fn foo() { - bar(); -} -"#, - ); -} - #[test] fn dyn_trait_with_lifetime_in_rpit() { check_types( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs new file mode 100644 index 0000000000000..e2b7bf379cc3b --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs @@ -0,0 +1,161 @@ +use expect_test::expect; + +use super::{check_infer_with_mismatches, check_no_mismatches, check_types}; + +#[test] +fn associated_type_impl_trait() { + check_types( + r#" +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Self::Item; +} +struct S2; +impl Bar for S2 { + type Item = impl Foo; + fn bar(&self) -> Self::Item { + S1 + } +} + +fn test() { + let x = S2.bar(); + //^ impl Foo + ?Sized +} + "#, + ); +} + +#[test] +fn associated_type_impl_traits_complex() { + check_types( + r#" +struct Unary(T); +struct Binary(T, U); + +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Unary; +} +struct S2; +impl Bar for S2 { + type Item = Unary; + fn bar(&self) -> Unary<::Item> { + Unary(Unary(S1)) + } +} + +trait Baz { + type Target1; + type Target2; + fn baz(&self) -> Binary; +} +struct S3; +impl Baz for S3 { + type Target1 = impl Foo; + type Target2 = Unary; + fn baz(&self) -> Binary { + Binary(S1, Unary(S2)) + } +} + +fn test() { + let x = S3.baz(); + //^ Binary> + let y = x.1.0.bar(); + //^ Unary> +} + "#, + ); +} + +#[test] +fn associated_type_with_impl_trait_in_tuple() { + check_no_mismatches( + r#" +pub trait Iterator { + type Item; +} + +pub trait Value {} + +fn bar>() {} + +fn foo() { + bar(); +} +"#, + ); +} + +#[test] +fn associated_type_with_impl_trait_in_nested_tuple() { + check_no_mismatches( + r#" +pub trait Iterator { + type Item; +} + +pub trait Value {} + +fn bar>() {} + +fn foo() { + bar(); +} +"#, + ); +} + +#[test] +fn type_alias_impl_trait_simple() { + check_no_mismatches( + r#" +trait Trait {} + +struct Struct; + +impl Trait for Struct {} + +type AliasTy = impl Trait; + +static ALIAS: AliasTy = { + let res: AliasTy = Struct; + res +}; +"#, + ); + + check_infer_with_mismatches( + r#" +trait Trait {} + +struct Struct; + +impl Trait for Struct {} + +type AliasTy = impl Trait; + +static ALIAS: i32 = { + // TATIs cannot be define-used if not in signature or type annotations + let _a: AliasTy = Struct; + 5 +}; +"#, + expect![[r#" + 106..220 '{ ... 5 }': i32 + 191..193 '_a': impl Trait + ?Sized + 205..211 'Struct': Struct + 217..218 '5': i32 + 205..211: expected impl Trait + ?Sized, got Struct + "#]], + ) +} From cbd00f1dda7f396cd14c0fd13d907d53ebdbf49f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Aug 2024 08:02:03 +0200 Subject: [PATCH 071/178] fix: Fix find_path not respecting non-std preference config correctly --- .../crates/hir-def/src/find_path.rs | 143 ++++++++++++------ .../rust-analyzer/crates/hir-def/src/lib.rs | 2 +- .../crates/hir-ty/src/display.rs | 1 + .../crates/ide-completion/src/completions.rs | 8 +- .../ide-completion/src/completions/expr.rs | 14 +- .../src/completions/flyimport.rs | 21 +-- .../ide-completion/src/completions/postfix.rs | 8 +- .../crates/ide-completion/src/config.rs | 9 ++ .../crates/ide-completion/src/lib.rs | 7 +- .../crates/ide-completion/src/render.rs | 8 +- .../crates/ide-completion/src/snippet.rs | 7 +- 11 files changed, 124 insertions(+), 104 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 91594aecd0441..5a3a3e9189790 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -50,13 +50,13 @@ pub fn find_path( prefix: prefix_kind, cfg, ignore_local_imports, + is_std_item: db.crate_graph()[item_module.krate()].origin.is_lang(), from, from_def_map: &from.def_map(db), fuel: Cell::new(FIND_PATH_FUEL), }, item, MAX_PATH_LEN, - db.crate_graph()[item_module.krate()].origin.is_lang(), ) } @@ -98,20 +98,16 @@ struct FindPathCtx<'db> { prefix: PrefixKind, cfg: ImportPathConfig, ignore_local_imports: bool, + is_std_item: bool, from: ModuleId, from_def_map: &'db DefMap, fuel: Cell, } /// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId -fn find_path_inner( - ctx: &FindPathCtx<'_>, - item: ItemInNs, - max_len: usize, - is_std_item: bool, -) -> Option { +fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option { // - if the item is a module, jump straight to module search - if !is_std_item { + if !ctx.is_std_item { if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { return find_path_for_module(ctx, &mut FxHashSet::default(), module_id, true, max_len) .map(|choice| choice.path); @@ -138,12 +134,9 @@ fn find_path_inner( if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { // - if the item is an enum variant, refer to it via the enum - if let Some(mut path) = find_path_inner( - ctx, - ItemInNs::Types(variant.lookup(ctx.db).parent.into()), - max_len, - is_std_item, - ) { + if let Some(mut path) = + find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len) + { path.push_segment(ctx.db.enum_variant_data(variant).name.clone()); return Some(path); } @@ -152,16 +145,6 @@ fn find_path_inner( // variant somewhere } - if is_std_item { - // The item we are searching for comes from the sysroot libraries, so skip prefer looking in - // the sysroot libraries directly. - // We do need to fallback as the item in question could be re-exported by another crate - // while not being a transitive dependency of the current crate. - if let Some(choice) = find_in_sysroot(ctx, &mut FxHashSet::default(), item, max_len) { - return Some(choice.path); - } - } - let mut best_choice = None; calculate_best_path(ctx, &mut FxHashSet::default(), item, max_len, &mut best_choice); best_choice.map(|choice| choice.path) @@ -366,6 +349,12 @@ fn calculate_best_path( // Item was defined in the same crate that wants to import it. It cannot be found in any // dependency in this case. calculate_best_path_local(ctx, visited_modules, item, max_len, best_choice) + } else if ctx.is_std_item { + // The item we are searching for comes from the sysroot libraries, so skip prefer looking in + // the sysroot libraries directly. + // We do need to fallback as the item in question could be re-exported by another crate + // while not being a transitive dependency of the current crate. + find_in_sysroot(ctx, visited_modules, item, max_len, best_choice) } else { // Item was defined in some upstream crate. This means that it must be exported from one, // too (unless we can't name it at all). It could *also* be (re)exported by the same crate @@ -382,10 +371,10 @@ fn find_in_sysroot( visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, item: ItemInNs, max_len: usize, -) -> Option { + best_choice: &mut Option, +) { let crate_graph = ctx.db.crate_graph(); let dependencies = &crate_graph[ctx.from.krate].dependencies; - let mut best_choice = None; let mut search = |lang, best_choice: &mut _| { if let Some(dep) = dependencies.iter().filter(|it| it.is_sysroot()).find(|dep| { match crate_graph[dep.crate_id].origin { @@ -397,29 +386,31 @@ fn find_in_sysroot( } }; if ctx.cfg.prefer_no_std { - search(LangCrateOrigin::Core, &mut best_choice); + search(LangCrateOrigin::Core, best_choice); if matches!(best_choice, Some(Choice { stability: Stable, .. })) { - return best_choice; + return; } - search(LangCrateOrigin::Std, &mut best_choice); + search(LangCrateOrigin::Std, best_choice); if matches!(best_choice, Some(Choice { stability: Stable, .. })) { - return best_choice; + return; } } else { - search(LangCrateOrigin::Std, &mut best_choice); + search(LangCrateOrigin::Std, best_choice); if matches!(best_choice, Some(Choice { stability: Stable, .. })) { - return best_choice; + return; } - search(LangCrateOrigin::Core, &mut best_choice); + search(LangCrateOrigin::Core, best_choice); if matches!(best_choice, Some(Choice { stability: Stable, .. })) { - return best_choice; + return; } } - let mut best_choice = None; - dependencies.iter().filter(|it| it.is_sysroot()).for_each(|dep| { - find_in_dep(ctx, visited_modules, item, max_len, &mut best_choice, dep.crate_id); - }); - best_choice + dependencies + .iter() + .filter(|it| it.is_sysroot()) + .chain(dependencies.iter().filter(|it| !it.is_sysroot())) + .for_each(|dep| { + find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id); + }); } fn find_in_dep( @@ -491,6 +482,7 @@ fn calculate_best_path_local( ); } +#[derive(Debug)] struct Choice { path: ModPath, /// The length in characters of the path @@ -676,6 +668,7 @@ mod tests { path: &str, prefer_prelude: bool, prefer_absolute: bool, + prefer_no_std: bool, expect: Expect, ) { let (db, pos) = TestDB::with_position(ra_fixture); @@ -717,7 +710,7 @@ mod tests { module, prefix, ignore_local_imports, - ImportPathConfig { prefer_no_std: false, prefer_prelude, prefer_absolute }, + ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute }, ); format_to!( res, @@ -732,15 +725,19 @@ mod tests { } fn check_found_path(ra_fixture: &str, path: &str, expect: Expect) { - check_found_path_(ra_fixture, path, false, false, expect); + check_found_path_(ra_fixture, path, false, false, false, expect); } fn check_found_path_prelude(ra_fixture: &str, path: &str, expect: Expect) { - check_found_path_(ra_fixture, path, true, false, expect); + check_found_path_(ra_fixture, path, true, false, false, expect); } fn check_found_path_absolute(ra_fixture: &str, path: &str, expect: Expect) { - check_found_path_(ra_fixture, path, false, true, expect); + check_found_path_(ra_fixture, path, false, true, false, expect); + } + + fn check_found_path_prefer_no_std(ra_fixture: &str, path: &str, expect: Expect) { + check_found_path_(ra_fixture, path, false, false, true, expect); } #[test] @@ -1361,9 +1358,66 @@ pub mod sync { "#]], ); } + #[test] + fn prefer_core_paths_over_std_for_mod_reexport() { + check_found_path_prefer_no_std( + r#" +//- /main.rs crate:main deps:core,std + +$0 + +//- /stdlib.rs crate:std deps:core + +pub use core::pin; + +//- /corelib.rs crate:core + +pub mod pin { + pub struct Pin; +} + "#, + "std::pin::Pin", + expect![[r#" + Plain (imports ✔): core::pin::Pin + Plain (imports ✖): core::pin::Pin + ByCrate(imports ✔): core::pin::Pin + ByCrate(imports ✖): core::pin::Pin + BySelf (imports ✔): core::pin::Pin + BySelf (imports ✖): core::pin::Pin + "#]], + ); + } #[test] fn prefer_core_paths_over_std() { + check_found_path_prefer_no_std( + r#" +//- /main.rs crate:main deps:core,std + +$0 + +//- /std.rs crate:std deps:core + +pub mod fmt { + pub use core::fmt::Error; +} + +//- /zzz.rs crate:core + +pub mod fmt { + pub struct Error; +} + "#, + "core::fmt::Error", + expect![[r#" + Plain (imports ✔): core::fmt::Error + Plain (imports ✖): core::fmt::Error + ByCrate(imports ✔): core::fmt::Error + ByCrate(imports ✖): core::fmt::Error + BySelf (imports ✔): core::fmt::Error + BySelf (imports ✖): core::fmt::Error + "#]], + ); check_found_path( r#" //- /main.rs crate:main deps:core,std @@ -1878,10 +1932,9 @@ pub mod ops { #[test] fn respect_unstable_modules() { - check_found_path( + check_found_path_prefer_no_std( r#" //- /main.rs crate:main deps:std,core -#![no_std] extern crate std; $0 //- /longer.rs crate:std deps:core diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 66412b26a0085..4ced30c81dced 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -105,7 +105,7 @@ use crate::{ type FxIndexMap = indexmap::IndexMap>; -/// A wrapper around two booleans, [`ImportPathConfig::prefer_no_std`] and [`ImportPathConfig::prefer_prelude`]. +/// A wrapper around three booleans #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] pub struct ImportPathConfig { /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 47ea2f5347c5f..64825ea2158c2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1069,6 +1069,7 @@ impl HirDisplay for Ty { module_id, PrefixKind::Plain, false, + // FIXME: no_std Cfg? ImportPathConfig { prefer_no_std: false, prefer_prelude: true, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 414b096ad475c..58e9b724df2a0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -24,7 +24,7 @@ pub(crate) mod vis; use std::iter; -use hir::{sym, HasAttrs, ImportPathConfig, Name, ScopeDef, Variant}; +use hir::{sym, HasAttrs, Name, ScopeDef, Variant}; use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind}; use syntax::{ast, SmolStr, ToSmolStr}; @@ -645,11 +645,7 @@ fn enum_variants_with_paths( if let Some(path) = ctx.module.find_path( ctx.db, hir::ModuleDef::from(variant), - ImportPathConfig { - prefer_no_std: ctx.config.prefer_no_std, - prefer_prelude: ctx.config.prefer_prelude, - prefer_absolute: ctx.config.prefer_absolute, - }, + ctx.config.import_path_config(), ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 71ff6b5aea37a..ff2c8da421306 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -1,6 +1,6 @@ //! Completion of names from the current scope in expression position. -use hir::{sym, ImportPathConfig, Name, ScopeDef}; +use hir::{sym, Name, ScopeDef}; use syntax::ast; use crate::{ @@ -174,11 +174,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(strukt), - ImportPathConfig { - prefer_no_std: ctx.config.prefer_no_std, - prefer_prelude: ctx.config.prefer_prelude, - prefer_absolute: ctx.config.prefer_absolute, - }, + ctx.config.import_path_config(), ) .filter(|it| it.len() > 1); @@ -200,11 +196,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(un), - ImportPathConfig { - prefer_no_std: ctx.config.prefer_no_std, - prefer_prelude: ctx.config.prefer_prelude, - prefer_absolute: ctx.config.prefer_absolute, - }, + ctx.config.import_path_config(), ) .filter(|it| it.len() > 1); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index e803072fa8f0a..fdce7c547a490 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -1,5 +1,5 @@ //! See [`import_on_the_fly`]. -use hir::{ImportPathConfig, ItemInNs, ModuleDef}; +use hir::{ItemInNs, ModuleDef}; use ide_db::imports::{ import_assets::{ImportAssets, LocatedImport}, insert_use::ImportScope, @@ -256,11 +256,7 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); - let import_cfg = ImportPathConfig { - prefer_no_std: ctx.config.prefer_no_std, - prefer_prelude: ctx.config.prefer_prelude, - prefer_absolute: ctx.config.prefer_absolute, - }; + let import_cfg = ctx.config.import_path_config(); import_assets .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind) @@ -306,12 +302,7 @@ fn import_on_the_fly_pat_( ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)), }; let user_input_lowercased = potential_import_name.to_lowercase(); - - let cfg = ImportPathConfig { - prefer_no_std: ctx.config.prefer_no_std, - prefer_prelude: ctx.config.prefer_prelude, - prefer_absolute: ctx.config.prefer_absolute, - }; + let cfg = ctx.config.import_path_config(); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) @@ -353,11 +344,7 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ImportPathConfig { - prefer_no_std: ctx.config.prefer_no_std, - prefer_prelude: ctx.config.prefer_prelude, - prefer_absolute: ctx.config.prefer_absolute, - }; + let cfg = ctx.config.import_path_config(); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index d919609237a6b..977e0d80a4d46 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -2,7 +2,7 @@ mod format_like; -use hir::{ImportPathConfig, ItemInNs}; +use hir::ItemInNs; use ide_db::{ documentation::{Documentation, HasDocs}, imports::insert_use::ImportScope, @@ -60,11 +60,7 @@ pub(crate) fn complete_postfix( None => return, }; - let cfg = ImportPathConfig { - prefer_no_std: ctx.config.prefer_no_std, - prefer_prelude: ctx.config.prefer_prelude, - prefer_absolute: ctx.config.prefer_absolute, - }; + let cfg = ctx.config.import_path_config(); if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 7d062cb23e5ed..d885b82ec90df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -4,6 +4,7 @@ //! module, and we use to statically check that we only produce snippet //! completions if we are allowed to. +use hir::ImportPathConfig; use ide_db::{imports::insert_use::InsertUseConfig, SnippetCap}; use crate::snippet::Snippet; @@ -45,4 +46,12 @@ impl CompletionConfig { .iter() .flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip))) } + + pub fn import_path_config(&self) -> ImportPathConfig { + ImportPathConfig { + prefer_no_std: self.prefer_no_std, + prefer_prelude: self.prefer_prelude, + prefer_absolute: self.prefer_absolute, + } + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 424f94457e3bf..90c1728074d4d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -10,7 +10,6 @@ mod snippet; #[cfg(test)] mod tests; -use hir::ImportPathConfig; use ide_db::{ helpers::mod_path_to_ast, imports::{ @@ -249,11 +248,7 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); - let cfg = ImportPathConfig { - prefer_no_std: config.prefer_no_std, - prefer_prelude: config.prefer_prelude, - prefer_absolute: config.prefer_absolute, - }; + let cfg = config.import_path_config(); imports.into_iter().for_each(|(full_import_path, imported_name)| { let items_with_name = items_locator::items_with_name( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index abcff62341b59..02d667c52056a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -10,7 +10,7 @@ pub(crate) mod type_alias; pub(crate) mod union_literal; pub(crate) mod variant; -use hir::{sym, AsAssocItem, HasAttrs, HirDisplay, ImportPathConfig, ModuleDef, ScopeDef, Type}; +use hir::{sym, AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type}; use ide_db::{ documentation::{Documentation, HasDocs}, helpers::item_name, @@ -294,11 +294,7 @@ pub(crate) fn render_expr( .unwrap_or_else(|| String::from("...")) }; - let cfg = ImportPathConfig { - prefer_no_std: ctx.config.prefer_no_std, - prefer_prelude: ctx.config.prefer_prelude, - prefer_absolute: ctx.config.prefer_absolute, - }; + let cfg = ctx.config.import_path_config(); let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg).ok()?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 1eb8c574bd1c4..5265aa8515b6a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -100,7 +100,6 @@ // } // ---- -use hir::ImportPathConfig; use ide_db::imports::import_assets::LocatedImport; use itertools::Itertools; use syntax::{ast, AstNode, GreenNode, SyntaxNode}; @@ -169,11 +168,7 @@ impl Snippet { } fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option> { - let import_cfg = ImportPathConfig { - prefer_no_std: ctx.config.prefer_no_std, - prefer_prelude: ctx.config.prefer_prelude, - prefer_absolute: ctx.config.prefer_absolute, - }; + let import_cfg = ctx.config.import_path_config(); let resolve = |import: &GreenNode| { let path = ast::Path::cast(SyntaxNode::new_root(import.clone()))?; From e219ac64c06fd119c536701bb082b8611f120d72 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Aug 2024 16:46:53 +0200 Subject: [PATCH 072/178] Move some stuff --- .../crates/rust-analyzer/src/config.rs | 2 +- .../crates/rust-analyzer/src/diff.rs | 53 ------------------- .../src/{ => handlers}/dispatch.rs | 0 .../rust-analyzer/src/handlers/request.rs | 45 +++++++++++++++- .../crates/rust-analyzer/src/lib.rs | 6 +-- .../crates/rust-analyzer/src/lsp.rs | 2 + .../src/{ => lsp}/capabilities.rs | 0 .../crates/rust-analyzer/src/main_loop.rs | 2 +- 8 files changed, 50 insertions(+), 60 deletions(-) delete mode 100644 src/tools/rust-analyzer/crates/rust-analyzer/src/diff.rs rename src/tools/rust-analyzer/crates/rust-analyzer/src/{ => handlers}/dispatch.rs (100%) rename src/tools/rust-analyzer/crates/rust-analyzer/src/{ => lsp}/capabilities.rs (100%) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 486046c47c7bb..02f5d75136e9e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -34,9 +34,9 @@ use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, VfsPath}; use crate::{ - capabilities::ClientCapabilities, diagnostics::DiagnosticsMapConfig, flycheck::{CargoOptions, FlycheckConfig}, + lsp::capabilities::ClientCapabilities, lsp_ext::{WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope}, }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diff.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diff.rs deleted file mode 100644 index 3fcfb4a1b08aa..0000000000000 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diff.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Generate minimal `TextEdit`s from different text versions -use dissimilar::Chunk; -use ide::{TextEdit, TextRange, TextSize}; - -pub(crate) fn diff(left: &str, right: &str) -> TextEdit { - let chunks = dissimilar::diff(left, right); - textedit_from_chunks(chunks) -} - -fn textedit_from_chunks(chunks: Vec>) -> TextEdit { - let mut builder = TextEdit::builder(); - let mut pos = TextSize::default(); - - let mut chunks = chunks.into_iter().peekable(); - while let Some(chunk) = chunks.next() { - if let (Chunk::Delete(deleted), Some(&Chunk::Insert(inserted))) = (chunk, chunks.peek()) { - chunks.next().unwrap(); - let deleted_len = TextSize::of(deleted); - builder.replace(TextRange::at(pos, deleted_len), inserted.into()); - pos += deleted_len; - continue; - } - - match chunk { - Chunk::Equal(text) => { - pos += TextSize::of(text); - } - Chunk::Delete(deleted) => { - let deleted_len = TextSize::of(deleted); - builder.delete(TextRange::at(pos, deleted_len)); - pos += deleted_len; - } - Chunk::Insert(inserted) => { - builder.insert(pos, inserted.into()); - } - } - } - builder.finish() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn diff_applies() { - let mut original = String::from("fn foo(a:u32){\n}"); - let result = "fn foo(a: u32) {}"; - let edit = diff(&original, result); - edit.apply(&mut original); - assert_eq!(original, result); - } -} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs similarity index 100% rename from src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index a77d31167a750..34325ac7a93aa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -36,7 +36,6 @@ use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath}; use crate::{ config::{Config, RustfmtConfig, WorkspaceSymbolConfig}, - diff::diff, global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot}, hack_recover_crate_name, line_index::LineEndings, @@ -2370,3 +2369,47 @@ fn resolve_resource_op(op: &ResourceOp) -> ResourceOperationKind { ResourceOp::Delete(_) => ResourceOperationKind::Delete, } } + +pub(crate) fn diff(left: &str, right: &str) -> TextEdit { + use dissimilar::Chunk; + + let chunks = dissimilar::diff(left, right); + + let mut builder = TextEdit::builder(); + let mut pos = TextSize::default(); + + let mut chunks = chunks.into_iter().peekable(); + while let Some(chunk) = chunks.next() { + if let (Chunk::Delete(deleted), Some(&Chunk::Insert(inserted))) = (chunk, chunks.peek()) { + chunks.next().unwrap(); + let deleted_len = TextSize::of(deleted); + builder.replace(TextRange::at(pos, deleted_len), inserted.into()); + pos += deleted_len; + continue; + } + + match chunk { + Chunk::Equal(text) => { + pos += TextSize::of(text); + } + Chunk::Delete(deleted) => { + let deleted_len = TextSize::of(deleted); + builder.delete(TextRange::at(pos, deleted_len)); + pos += deleted_len; + } + Chunk::Insert(inserted) => { + builder.insert(pos, inserted.into()); + } + } + } + builder.finish() +} + +#[test] +fn diff_smoke_test() { + let mut original = String::from("fn foo(a:u32){\n}"); + let result = "fn foo(a: u32) {}"; + let edit = diff(&original, result); + edit.apply(&mut original); + assert_eq!(original, result); +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 56eb420770e9b..714991e81167a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -11,12 +11,9 @@ pub mod cli; -mod capabilities; mod command; mod diagnostics; -mod diff; mod discover; -mod dispatch; mod flycheck; mod hack_recover_crate_name; mod line_index; @@ -30,6 +27,7 @@ mod test_runner; mod version; mod handlers { + pub(crate) mod dispatch; pub(crate) mod notification; pub(crate) mod request; } @@ -51,7 +49,7 @@ mod integrated_benchmarks; use serde::de::DeserializeOwned; pub use crate::{ - capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph, + lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph, version::version, }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs index 9e0d42faed43f..122ad20d65ee6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs @@ -3,6 +3,8 @@ use core::fmt; pub mod ext; + +pub(crate) mod capabilities; pub(crate) mod from_proto; pub(crate) mod semantic_tokens; pub(crate) mod to_proto; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/capabilities.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs similarity index 100% rename from src/tools/rust-analyzer/crates/rust-analyzer/src/capabilities.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 85e7d81fce3d7..8035b7867cc78 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -20,10 +20,10 @@ use crate::{ config::Config, diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind}, discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage}, - dispatch::{NotificationDispatcher, RequestDispatcher}, flycheck::{self, FlycheckMessage}, global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState}, hack_recover_crate_name, + handlers::dispatch::{NotificationDispatcher, RequestDispatcher}, lsp::{ from_proto, to_proto, utils::{notification_is, Progress}, From 01262d972a03aa0eee275bea07876d44402fbb4c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Aug 2024 17:04:38 +0200 Subject: [PATCH 073/178] Add comments regarding workspace structure change querying --- .../rust-analyzer/crates/rust-analyzer/src/global_state.rs | 5 +++++ .../crates/rust-analyzer/src/handlers/notification.rs | 2 ++ .../rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 1 + 3 files changed, 8 insertions(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index df809c0723553..d1f107a62a45f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -460,6 +460,11 @@ impl GlobalState { } } + // FIXME: `workspace_structure_change` is computed from `should_refresh_for_change` which is + // path syntax based. That is not sufficient for all cases so we should lift that check out + // into a `QueuedTask`, see `handle_did_save_text_document`. + // Or maybe instead of replacing that check, kick off a semantic one if the syntactic one + // didn't find anything (to make up for the lack of precision). { if !matches!(&workspace_structure_change, Some((.., true))) { _ = self diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index a2f9229047ec8..de5d1f231368b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -158,6 +158,8 @@ pub(crate) fn handle_did_save_text_document( .map(|cfg| cfg.files_to_watch.iter().map(String::as_str).collect::>()) .unwrap_or_default(); + // FIXME: We should move this check into a QueuedTask and do semantic resolution of + // the files. There is only so much we can tell syntactically from the path. if reload::should_refresh_for_change(path, ChangeKind::Modify, additional_files) { state.fetch_workspaces_queue.request_op( format!("workspace vfs file change saved {path}"), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 8035b7867cc78..e303765aab6c6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -105,6 +105,7 @@ pub(crate) enum Task { FetchWorkspace(ProjectWorkspaceProgress), FetchBuildData(BuildDataProgress), LoadProcMacros(ProcMacroProgress), + // FIXME: Remove this in favor of a more general QueuedTask, see `handle_did_save_text_document` BuildDepsHaveChanged, } From 97a6fc65f17febf77b88f08a19b9b461fb7126bc Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 11 Aug 2024 14:58:50 +0200 Subject: [PATCH 074/178] Fix deadlock --- src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 2bd4eb671353d..bf96788d373df 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -131,6 +131,7 @@ impl NotifyActor { let (entry_tx, entry_rx) = unbounded(); let (watch_tx, watch_rx) = unbounded(); let processed = AtomicUsize::new(0); + config.load.into_par_iter().enumerate().for_each(|(i, entry)| { let do_watch = config.watch.contains(&i); if do_watch { @@ -167,9 +168,13 @@ impl NotifyActor { }) .unwrap(); }); + + drop(watch_tx); for path in watch_rx { self.watch(&path); } + + drop(entry_tx); for entry in entry_rx { match entry { loader::Entry::Files(files) => { @@ -180,6 +185,7 @@ impl NotifyActor { } } } + self.sender .send(loader::Message::Progress { n_total, From 9e95a2b9a5c434db5ad42fe3f8848cbbd161e642 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 12 Aug 2024 15:20:58 +1000 Subject: [PATCH 075/178] Remove some unnecessary `skip_binder` calls. `is_fn_trait_compatible` is defined on both `FnSig` and `Binder`. --- .../src/solve/assembly/structural_traits.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index f32531821ec47..a57338acaab29 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -461,7 +461,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable { let sig = self_ty.fn_sig(cx); - if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) { + if sig.is_fn_trait_compatible() && !cx.has_target_features(def_id) { fn_item_to_async_callable(cx, sig) } else { Err(NoSolution) @@ -469,7 +469,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable { let sig = self_ty.fn_sig(cx); - if sig.skip_binder().is_fn_trait_compatible() { + if sig.is_fn_trait_compatible() { fn_item_to_async_callable(cx, sig) } else { Err(NoSolution) From f4a3ed02439adb6b168e6979fcfef7dc2a9fc33a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 12 Aug 2024 15:37:28 +1000 Subject: [PATCH 076/178] Avoid a `FnPtr` deconstruct-and-recreate. --- compiler/rustc_middle/src/ty/layout.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 55511d0833ce0..f16f4baf723ed 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -986,10 +986,11 @@ where safe: None, }) } - ty::FnPtr(sig_tys, hdr) if offset.bytes() == 0 => { - let fn_sig = sig_tys.with(hdr); - tcx.layout_of(param_env.and(Ty::new_fn_ptr(tcx, fn_sig))).ok().map(|layout| { - PointeeInfo { size: layout.size, align: layout.align.abi, safe: None } + ty::FnPtr(..) if offset.bytes() == 0 => { + tcx.layout_of(param_env.and(this.ty)).ok().map(|layout| PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: None, }) } ty::Ref(_, ty, mt) if offset.bytes() == 0 => { From b9f3db6adb77cce9ec5a5476003a5cf1803b6262 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 12 Aug 2024 15:51:04 +1000 Subject: [PATCH 077/178] Give the field in `FnSigTys` a name. --- compiler/rustc_type_ir/src/fast_reject.rs | 4 ++-- compiler/rustc_type_ir/src/ty_kind.rs | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 718e13ffe943f..fab4a0991175d 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -311,8 +311,8 @@ impl DeepRejectCtxt { } ty::FnPtr(obl_sig_tys, obl_hdr) => match k { ty::FnPtr(impl_sig_tys, impl_hdr) => { - let obl_sig_tys = obl_sig_tys.skip_binder().0; - let impl_sig_tys = impl_sig_tys.skip_binder().0; + let obl_sig_tys = obl_sig_tys.skip_binder().inputs_and_output; + let impl_sig_tys = impl_sig_tys.skip_binder().inputs_and_output; obl_hdr == impl_hdr && obl_sig_tys.len() == impl_sig_tys.len() diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 8640a6019800a..328b6739d9756 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -928,7 +928,7 @@ impl ty::Binder> { pub fn split(self) -> (ty::Binder>, FnHeader) { let hdr = FnHeader { c_variadic: self.c_variadic(), safety: self.safety(), abi: self.abi() }; - (self.map_bound(|sig| FnSigTys(sig.inputs_and_output)), hdr) + (self.map_bound(|sig| FnSigTys { inputs_and_output: sig.inputs_and_output }), hdr) } } @@ -971,15 +971,17 @@ impl fmt::Debug for FnSig { #[derive_where(Clone, Copy, Debug, PartialEq, Eq, Hash; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -pub struct FnSigTys(pub I::Tys); +pub struct FnSigTys { + pub inputs_and_output: I::Tys, +} impl FnSigTys { pub fn inputs(self) -> I::FnInputTys { - self.0.inputs() + self.inputs_and_output.inputs() } pub fn output(self) -> I::Ty { - self.0.output() + self.inputs_and_output.output() } } @@ -987,7 +989,7 @@ impl ty::Binder> { // Used to combine the two fields in `TyKind::FnPtr` into a single value. pub fn with(self, hdr: FnHeader) -> ty::Binder> { self.map_bound(|sig_tys| FnSig { - inputs_and_output: sig_tys.0, + inputs_and_output: sig_tys.inputs_and_output, c_variadic: hdr.c_variadic, safety: hdr.safety, abi: hdr.abi, @@ -1006,7 +1008,7 @@ impl ty::Binder> { } pub fn inputs_and_output(self) -> ty::Binder { - self.map_bound(|sig_tys| sig_tys.0) + self.map_bound(|sig_tys| sig_tys.inputs_and_output) } #[inline] From bbd1c3ab73a931bf4823eec836de57e837d2161c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 12 Aug 2024 15:53:45 +1000 Subject: [PATCH 078/178] Streamline some inputs/output traversals. --- compiler/rustc_middle/src/ty/flags.rs | 3 +-- compiler/rustc_middle/src/ty/walk.rs | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 1e38e30628aa9..fc079592583e6 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -251,8 +251,7 @@ impl FlagComputation { } &ty::FnPtr(sig_tys, _) => self.bound_computation(sig_tys, |computation, sig_tys| { - computation.add_tys(sig_tys.inputs()); - computation.add_ty(sig_tys.output()); + computation.add_tys(sig_tys.inputs_and_output); }), } } diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 0a328352e2e97..abd6df17514bb 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -189,11 +189,9 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) stack.extend(args.iter().rev()); } ty::Tuple(ts) => stack.extend(ts.iter().rev().map(GenericArg::from)), - ty::FnPtr(sig_tys, hdr) => { - let fn_sig = sig_tys.with(hdr); - stack.push(fn_sig.skip_binder().output().into()); + ty::FnPtr(sig_tys, _hdr) => { stack.extend( - fn_sig.skip_binder().inputs().iter().copied().rev().map(|ty| ty.into()), + sig_tys.skip_binder().inputs_and_output.iter().rev().map(|ty| ty.into()), ); } }, From 7b86c98068f0b4280598f36e655b5e83d21f9258 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Jul 2024 14:21:52 +0200 Subject: [PATCH 079/178] do not use the global solver cache for proof trees doing so requires overwriting global cache entries and generally adds significant complexity to the solver. This is also only ever done for root goals, so it feels easier to wrap the `evaluate_canonical_goal` in an ordinary query if necessary. --- compiler/rustc_middle/src/arena.rs | 4 - compiler/rustc_middle/src/ty/context.rs | 9 -- .../src/solve/inspect/build.rs | 106 +++--------------- .../src/solve/search_graph.rs | 7 +- .../src/solve/inspect/analyse.rs | 10 +- compiler/rustc_type_ir/src/interner.rs | 12 -- .../src/search_graph/global_cache.rs | 36 ++---- .../rustc_type_ir/src/search_graph/mod.rs | 87 +++++++------- compiler/rustc_type_ir/src/solve/inspect.rs | 4 +- compiler/rustc_type_ir/src/solve/mod.rs | 8 -- 10 files changed, 74 insertions(+), 209 deletions(-) diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index e3d7dff3c66bb..37c10b14054c5 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -61,10 +61,6 @@ macro_rules! arena_types { [] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>, [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>, [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>, - [] canonical_goal_evaluation: - rustc_type_ir::solve::inspect::CanonicalGoalEvaluationStep< - rustc_middle::ty::TyCtxt<'tcx> - >, [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>, [] type_op_subtype: rustc_middle::infer::canonical::Canonical<'tcx, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8f8fd09c9e4d9..8198b2fdc8930 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -107,8 +107,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.mk_predefined_opaques_in_body(data) } type DefiningOpaqueTypes = &'tcx ty::List; - type CanonicalGoalEvaluationStepRef = - &'tcx solve::inspect::CanonicalGoalEvaluationStep>; type CanonicalVars = CanonicalVarInfos<'tcx>; fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars { self.mk_canonical_var_infos(infos) @@ -277,13 +275,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.debug_assert_args_compatible(def_id, args); } - fn intern_canonical_goal_evaluation_step( - self, - step: solve::inspect::CanonicalGoalEvaluationStep>, - ) -> &'tcx solve::inspect::CanonicalGoalEvaluationStep> { - self.arena.alloc(step) - } - fn mk_type_list_from_iter(self, args: I) -> T::Output where I: Iterator, diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index a3c21666bd67c..86fb036cd3df8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -5,11 +5,10 @@ //! see the comment on [ProofTreeBuilder]. use std::marker::PhantomData; -use std::mem; use derive_where::derive_where; use rustc_type_ir::inherent::*; -use rustc_type_ir::{self as ty, search_graph, Interner}; +use rustc_type_ir::{self as ty, Interner}; use crate::delegate::SolverDelegate; use crate::solve::eval_ctxt::canonical; @@ -94,31 +93,10 @@ impl WipGoalEvaluation { } } -#[derive_where(PartialEq, Eq; I: Interner)] -pub(in crate::solve) enum WipCanonicalGoalEvaluationKind { - Overflow, - CycleInStack, - ProvisionalCacheHit, - Interned { final_revision: I::CanonicalGoalEvaluationStepRef }, -} - -impl std::fmt::Debug for WipCanonicalGoalEvaluationKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Overflow => write!(f, "Overflow"), - Self::CycleInStack => write!(f, "CycleInStack"), - Self::ProvisionalCacheHit => write!(f, "ProvisionalCacheHit"), - Self::Interned { final_revision: _ } => { - f.debug_struct("Interned").finish_non_exhaustive() - } - } - } -} - #[derive_where(PartialEq, Eq, Debug; I: Interner)] struct WipCanonicalGoalEvaluation { goal: CanonicalInput, - kind: Option>, + encountered_overflow: bool, /// Only used for uncached goals. After we finished evaluating /// the goal, this is interned and moved into `kind`. final_revision: Option>, @@ -127,25 +105,17 @@ struct WipCanonicalGoalEvaluation { impl WipCanonicalGoalEvaluation { fn finalize(self) -> inspect::CanonicalGoalEvaluation { - // We've already interned the final revision in - // `fn finalize_canonical_goal_evaluation`. - assert!(self.final_revision.is_none()); - let kind = match self.kind.unwrap() { - WipCanonicalGoalEvaluationKind::Overflow => { + inspect::CanonicalGoalEvaluation { + goal: self.goal, + kind: if self.encountered_overflow { + assert!(self.final_revision.is_none()); inspect::CanonicalGoalEvaluationKind::Overflow - } - WipCanonicalGoalEvaluationKind::CycleInStack => { - inspect::CanonicalGoalEvaluationKind::CycleInStack - } - WipCanonicalGoalEvaluationKind::ProvisionalCacheHit => { - inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit - } - WipCanonicalGoalEvaluationKind::Interned { final_revision } => { + } else { + let final_revision = self.final_revision.unwrap().finalize(); inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } - } - }; - - inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() } + }, + result: self.result.unwrap(), + } } } @@ -308,7 +278,7 @@ impl, I: Interner> ProofTreeBuilder { ) -> ProofTreeBuilder { self.nested(|| WipCanonicalGoalEvaluation { goal, - kind: None, + encountered_overflow: false, final_revision: None, result: None, }) @@ -329,11 +299,11 @@ impl, I: Interner> ProofTreeBuilder { } } - pub fn canonical_goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind) { + pub fn canonical_goal_evaluation_overflow(&mut self) { if let Some(this) = self.as_mut() { match this { DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => { - assert_eq!(canonical_goal_evaluation.kind.replace(kind), None); + canonical_goal_evaluation.encountered_overflow = true; } _ => unreachable!(), }; @@ -547,51 +517,3 @@ impl, I: Interner> ProofTreeBuilder { } } } - -impl search_graph::ProofTreeBuilder for ProofTreeBuilder -where - D: SolverDelegate, - I: Interner, -{ - fn try_apply_proof_tree( - &mut self, - proof_tree: Option, - ) -> bool { - if !self.is_noop() { - if let Some(final_revision) = proof_tree { - let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision }; - self.canonical_goal_evaluation_kind(kind); - true - } else { - false - } - } else { - true - } - } - - fn on_provisional_cache_hit(&mut self) { - self.canonical_goal_evaluation_kind(WipCanonicalGoalEvaluationKind::ProvisionalCacheHit); - } - - fn on_cycle_in_stack(&mut self) { - self.canonical_goal_evaluation_kind(WipCanonicalGoalEvaluationKind::CycleInStack); - } - - fn finalize_canonical_goal_evaluation( - &mut self, - tcx: I, - ) -> Option { - self.as_mut().map(|this| match this { - DebugSolver::CanonicalGoalEvaluation(evaluation) => { - let final_revision = mem::take(&mut evaluation.final_revision).unwrap(); - let final_revision = - tcx.intern_canonical_goal_evaluation_step(final_revision.finalize()); - let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision }; - assert_eq!(evaluation.kind.replace(kind), None); - final_revision - } - _ => unreachable!(), - }) - } -} diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index fe053a506e712..1f2c65191a618 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -5,7 +5,7 @@ use rustc_type_ir::search_graph::{self, CycleKind, UsageKind}; use rustc_type_ir::solve::{CanonicalInput, Certainty, QueryResult}; use rustc_type_ir::Interner; -use super::inspect::{self, ProofTreeBuilder}; +use super::inspect::ProofTreeBuilder; use super::FIXPOINT_STEP_LIMIT; use crate::delegate::SolverDelegate; @@ -25,6 +25,9 @@ where const FIXPOINT_STEP_LIMIT: usize = FIXPOINT_STEP_LIMIT; type ProofTreeBuilder = ProofTreeBuilder; + fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool { + inspect.is_noop() + } fn recursion_limit(cx: I) -> usize { cx.recursion_limit() @@ -68,7 +71,7 @@ where inspect: &mut ProofTreeBuilder, input: CanonicalInput, ) -> QueryResult { - inspect.canonical_goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow); + inspect.canonical_goal_evaluation_overflow(); response_no_constraints(cx, input, Certainty::overflow(true)) } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index e8de8457440ff..4e4022830d46e 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -332,13 +332,9 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { pub fn candidates(&'a self) -> Vec> { let mut candidates = vec![]; - let last_eval_step = match self.evaluation_kind { - inspect::CanonicalGoalEvaluationKind::Overflow - | inspect::CanonicalGoalEvaluationKind::CycleInStack - | inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => { - warn!("unexpected root evaluation: {:?}", self.evaluation_kind); - return vec![]; - } + let last_eval_step = match &self.evaluation_kind { + // An annoying edge case in case the recursion limit is 0. + inspect::CanonicalGoalEvaluationKind::Overflow => return vec![], inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } => final_revision, }; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index c251540c0fc29..f2492ede4f5ea 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -11,7 +11,6 @@ use crate::inherent::*; use crate::ir_print::IrPrint; use crate::lang_items::TraitSolverLangItem; use crate::relate::Relate; -use crate::solve::inspect::CanonicalGoalEvaluationStep; use crate::solve::{ CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, SolverMode, }; @@ -65,11 +64,6 @@ pub trait Interner: + Eq + TypeVisitable + SliceLike; - type CanonicalGoalEvaluationStepRef: Copy - + Debug - + Hash - + Eq - + Deref>; type CanonicalVars: Copy + Debug @@ -177,11 +171,6 @@ pub trait Interner: fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs); - fn intern_canonical_goal_evaluation_step( - self, - step: CanonicalGoalEvaluationStep, - ) -> Self::CanonicalGoalEvaluationStepRef; - fn mk_type_list_from_iter(self, args: I) -> T::Output where I: Iterator, @@ -390,7 +379,6 @@ impl CollectAndApply for Result { } impl search_graph::Cx for I { - type ProofTree = Option; type Input = CanonicalInput; type Result = QueryResult; diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index be4f1069cd167..d63a8d16bea73 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -4,14 +4,8 @@ use rustc_index::IndexVec; use super::{AvailableDepth, Cx, StackDepth, StackEntry}; use crate::data_structures::{HashMap, HashSet}; -#[derive_where(Debug, Clone, Copy; X: Cx)] -struct QueryData { - result: X::Result, - proof_tree: X::ProofTree, -} - struct Success { - data: X::Tracked>, + result: X::Tracked, additional_depth: usize, } @@ -28,13 +22,12 @@ struct CacheEntry { /// See the doc comment of `StackEntry::cycle_participants` for more /// details. nested_goals: HashSet, - with_overflow: HashMap>>, + with_overflow: HashMap>, } #[derive_where(Debug; X: Cx)] pub(super) struct CacheData<'a, X: Cx> { pub(super) result: X::Result, - pub(super) proof_tree: X::ProofTree, pub(super) additional_depth: usize, pub(super) encountered_overflow: bool, // FIXME: This is currently unused, but impacts the design @@ -55,20 +48,19 @@ impl GlobalCache { input: X::Input, result: X::Result, - proof_tree: X::ProofTree, dep_node: X::DepNodeIndex, additional_depth: usize, encountered_overflow: bool, nested_goals: &HashSet, ) { - let data = cx.mk_tracked(QueryData { result, proof_tree }, dep_node); + let result = cx.mk_tracked(result, dep_node); let entry = self.map.entry(input).or_default(); entry.nested_goals.extend(nested_goals); if encountered_overflow { - entry.with_overflow.insert(additional_depth, data); + entry.with_overflow.insert(additional_depth, result); } else { - entry.success = Some(Success { data, additional_depth }); + entry.success = Some(Success { result, additional_depth }); } } @@ -90,10 +82,8 @@ impl GlobalCache { if let Some(ref success) = entry.success { if available_depth.cache_entry_is_applicable(success.additional_depth) { - let QueryData { result, proof_tree } = cx.get_tracked(&success.data); return Some(CacheData { - result, - proof_tree, + result: cx.get_tracked(&success.result), additional_depth: success.additional_depth, encountered_overflow: false, nested_goals: &entry.nested_goals, @@ -101,15 +91,11 @@ impl GlobalCache { } } - entry.with_overflow.get(&available_depth.0).map(|e| { - let QueryData { result, proof_tree } = cx.get_tracked(e); - CacheData { - result, - proof_tree, - additional_depth: available_depth.0, - encountered_overflow: true, - nested_goals: &entry.nested_goals, - } + entry.with_overflow.get(&available_depth.0).map(|e| CacheData { + result: cx.get_tracked(e), + additional_depth: available_depth.0, + encountered_overflow: true, + nested_goals: &entry.nested_goals, }) } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 4abf99b1ded8a..8e0c668b6b5bd 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -22,7 +22,6 @@ mod validate; /// about `Input` and `Result` as they are implementation details /// of the search graph. pub trait Cx: Copy { - type ProofTree: Debug + Copy; type Input: Debug + Eq + Hash + Copy; type Result: Debug + Eq + Hash + Copy; @@ -43,17 +42,12 @@ pub trait Cx: Copy { ) -> R; } -pub trait ProofTreeBuilder { - fn try_apply_proof_tree(&mut self, proof_tree: X::ProofTree) -> bool; - fn on_provisional_cache_hit(&mut self); - fn on_cycle_in_stack(&mut self); - fn finalize_canonical_goal_evaluation(&mut self, cx: X) -> X::ProofTree; -} - pub trait Delegate { type Cx: Cx; const FIXPOINT_STEP_LIMIT: usize; - type ProofTreeBuilder: ProofTreeBuilder; + + type ProofTreeBuilder; + fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool; fn recursion_limit(cx: Self::Cx) -> usize; @@ -362,8 +356,10 @@ impl, X: Cx> SearchGraph { return D::on_stack_overflow(cx, inspect, input); }; - if let Some(result) = self.lookup_global_cache(cx, input, available_depth, inspect) { - return result; + if D::inspect_is_noop(inspect) { + if let Some(result) = self.lookup_global_cache(cx, input, available_depth) { + return result; + } } // Check whether the goal is in the provisional cache. @@ -386,7 +382,6 @@ impl, X: Cx> SearchGraph { // We have a nested goal which is already in the provisional cache, use // its result. We do not provide any usage kind as that should have been // already set correctly while computing the cache entry. - inspect.on_provisional_cache_hit(); Self::tag_cycle_participants(&mut self.stack, None, entry.head); return entry.result; } else if let Some(stack_depth) = cache_entry.stack_depth { @@ -397,8 +392,6 @@ impl, X: Cx> SearchGraph { // // Finally we can return either the provisional response or the initial response // in case we're in the first fixpoint iteration for this goal. - inspect.on_cycle_in_stack(); - let is_coinductive_cycle = Self::stack_coinductive_from(cx, &self.stack, stack_depth); let cycle_kind = if is_coinductive_cycle { CycleKind::Coinductive } else { CycleKind::Inductive }; @@ -453,8 +446,6 @@ impl, X: Cx> SearchGraph { (current_entry, result) }); - let proof_tree = inspect.finalize_canonical_goal_evaluation(cx); - self.update_parent_goal(final_entry.reached_depth, final_entry.encountered_overflow); // We're now done with this goal. In case this goal is involved in a larger cycle @@ -471,28 +462,10 @@ impl, X: Cx> SearchGraph { entry.with_inductive_stack = Some(DetachedEntry { head, result }); } } else { - // When encountering a cycle, both inductive and coinductive, we only - // move the root into the global cache. We also store all other cycle - // participants involved. - // - // We must not use the global cache entry of a root goal if a cycle - // participant is on the stack. This is necessary to prevent unstable - // results. See the comment of `StackEntry::nested_goals` for - // more details. self.provisional_cache.remove(&input); - let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len(); - cx.with_global_cache(self.mode, |cache| { - cache.insert( - cx, - input, - result, - proof_tree, - dep_node, - additional_depth, - final_entry.encountered_overflow, - &final_entry.nested_goals, - ) - }) + if D::inspect_is_noop(inspect) { + self.insert_global_cache(cx, input, final_entry, result, dep_node) + } } self.check_invariants(); @@ -508,25 +481,15 @@ impl, X: Cx> SearchGraph { cx: X, input: X::Input, available_depth: AvailableDepth, - inspect: &mut D::ProofTreeBuilder, ) -> Option { cx.with_global_cache(self.mode, |cache| { let CacheData { result, - proof_tree, additional_depth, encountered_overflow, nested_goals: _, // FIXME: consider nested goals here. } = cache.get(cx, input, &self.stack, available_depth)?; - // If we're building a proof tree and the current cache entry does not - // contain a proof tree, we do not use the entry but instead recompute - // the goal. We simply overwrite the existing entry once we're done, - // caching the proof tree. - if !inspect.try_apply_proof_tree(proof_tree) { - return None; - } - // Update the reached depth of the current goal to make sure // its state is the same regardless of whether we've used the // global cache or not. @@ -537,6 +500,36 @@ impl, X: Cx> SearchGraph { Some(result) }) } + + /// When encountering a cycle, both inductive and coinductive, we only + /// move the root into the global cache. We also store all other cycle + /// participants involved. + /// + /// We must not use the global cache entry of a root goal if a cycle + /// participant is on the stack. This is necessary to prevent unstable + /// results. See the comment of `StackEntry::nested_goals` for + /// more details. + fn insert_global_cache( + &mut self, + cx: X, + input: X::Input, + final_entry: StackEntry, + result: X::Result, + dep_node: X::DepNodeIndex, + ) { + let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len(); + cx.with_global_cache(self.mode, |cache| { + cache.insert( + cx, + input, + result, + dep_node, + additional_depth, + final_entry.encountered_overflow, + &final_entry.nested_goals, + ) + }) + } } enum StepResult { diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs index 47d5e0dace71f..099c66f6bdc81 100644 --- a/compiler/rustc_type_ir/src/solve/inspect.rs +++ b/compiler/rustc_type_ir/src/solve/inspect.rs @@ -69,9 +69,7 @@ pub struct CanonicalGoalEvaluation { #[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub enum CanonicalGoalEvaluationKind { Overflow, - CycleInStack, - ProvisionalCacheHit, - Evaluation { final_revision: I::CanonicalGoalEvaluationStepRef }, + Evaluation { final_revision: CanonicalGoalEvaluationStep }, } #[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 444fd01f01281..00fc6ba1c5c8f 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -340,11 +340,3 @@ impl MaybeCause { } } } - -#[derive_where(PartialEq, Eq, Debug; I: Interner)] -pub struct CacheData { - pub result: QueryResult, - pub proof_tree: Option, - pub additional_depth: usize, - pub encountered_overflow: bool, -} From 51338ca0eb9a6b83fa3b743e102155ef145faf1f Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Jul 2024 13:12:23 +0200 Subject: [PATCH 080/178] expand fuzzing support this allows us to only sometimes disable the global cache. --- .../src/solve/search_graph.rs | 9 +++ .../rustc_type_ir/src/search_graph/mod.rs | 56 ++++++++++++++++--- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 1f2c65191a618..0994d0e3b3d88 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -1,3 +1,4 @@ +use std::convert::Infallible; use std::marker::PhantomData; use rustc_type_ir::inherent::*; @@ -22,6 +23,14 @@ where { type Cx = D::Interner; + type ValidationScope = Infallible; + fn enter_validation_scope( + _cx: Self::Cx, + _input: ::Input, + ) -> Option { + None + } + const FIXPOINT_STEP_LIMIT: usize = FIXPOINT_STEP_LIMIT; type ProofTreeBuilder = ProofTreeBuilder; diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 8e0c668b6b5bd..7d4ddb71461d1 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -44,6 +44,19 @@ pub trait Cx: Copy { pub trait Delegate { type Cx: Cx; + type ValidationScope; + /// Returning `Some` disables the global cache for the current goal. + /// + /// The `ValidationScope` is used when fuzzing the search graph to track + /// for which goals the global cache has been disabled. This is necessary + /// as we may otherwise ignore the global cache entry for some goal `G` + /// only to later use it, failing to detect a cycle goal and potentially + /// changing the result. + fn enter_validation_scope( + cx: Self::Cx, + input: ::Input, + ) -> Option; + const FIXPOINT_STEP_LIMIT: usize; type ProofTreeBuilder; @@ -356,11 +369,21 @@ impl, X: Cx> SearchGraph { return D::on_stack_overflow(cx, inspect, input); }; - if D::inspect_is_noop(inspect) { - if let Some(result) = self.lookup_global_cache(cx, input, available_depth) { - return result; - } - } + let validate_cache = if !D::inspect_is_noop(inspect) { + None + } else if let Some(scope) = D::enter_validation_scope(cx, input) { + // When validating the global cache we need to track the goals for which the + // global cache has been disabled as it may otherwise change the result for + // cyclic goals. We don't care about goals which are not on the current stack + // so it's fine to drop their scope eagerly. + self.lookup_global_cache_untracked(cx, input, available_depth) + .inspect(|expected| debug!(?expected, "validate cache entry")) + .map(|r| (scope, r)) + } else if let Some(result) = self.lookup_global_cache(cx, input, available_depth) { + return result; + } else { + None + }; // Check whether the goal is in the provisional cache. // The provisional result may rely on the path to its cycle roots, @@ -452,6 +475,7 @@ impl, X: Cx> SearchGraph { // do not remove it from the provisional cache and update its provisional result. // We only add the root of cycles to the global cache. if let Some(head) = final_entry.non_root_cycle_participant { + debug_assert!(validate_cache.is_none()); let coinductive_stack = Self::stack_coinductive_from(cx, &self.stack, head); let entry = self.provisional_cache.get_mut(&input).unwrap(); @@ -463,16 +487,29 @@ impl, X: Cx> SearchGraph { } } else { self.provisional_cache.remove(&input); - if D::inspect_is_noop(inspect) { + if let Some((_scope, expected)) = validate_cache { + // Do not try to move a goal into the cache again if we're testing + // the global cache. + assert_eq!(result, expected, "input={input:?}"); + } else if D::inspect_is_noop(inspect) { self.insert_global_cache(cx, input, final_entry, result, dep_node) } } - self.check_invariants(); - result } + fn lookup_global_cache_untracked( + &self, + cx: X, + input: X::Input, + available_depth: AvailableDepth, + ) -> Option { + cx.with_global_cache(self.mode, |cache| { + cache.get(cx, input, &self.stack, available_depth).map(|c| c.result) + }) + } + /// Try to fetch a previously computed result from the global cache, /// making sure to only do so if it would match the result of reevaluating /// this goal. @@ -496,7 +533,7 @@ impl, X: Cx> SearchGraph { let reached_depth = self.stack.next_index().plus(additional_depth); self.update_parent_goal(reached_depth, encountered_overflow); - debug!("global cache hit"); + debug!(?additional_depth, "global cache hit"); Some(result) }) } @@ -518,6 +555,7 @@ impl, X: Cx> SearchGraph { dep_node: X::DepNodeIndex, ) { let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len(); + debug!(?final_entry, ?result, "insert global cache"); cx.with_global_cache(self.mode, |cache| { cache.insert( cx, From e87157ba2a63edae640f294531d1b9f3e8d37a2e Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Jul 2024 13:14:19 +0200 Subject: [PATCH 081/178] simplify match + move `debug!` call --- compiler/rustc_type_ir/src/search_graph/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 7d4ddb71461d1..6c67bf13ac73e 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -106,6 +106,7 @@ pub enum UsageKind { impl UsageKind { fn merge(self, other: Self) -> Self { match (self, other) { + (UsageKind::Mixed, _) | (_, UsageKind::Mixed) => UsageKind::Mixed, (UsageKind::Single(lhs), UsageKind::Single(rhs)) => { if lhs == rhs { UsageKind::Single(lhs) @@ -113,9 +114,6 @@ impl UsageKind { UsageKind::Mixed } } - (UsageKind::Mixed, UsageKind::Mixed) - | (UsageKind::Mixed, UsageKind::Single(_)) - | (UsageKind::Single(_), UsageKind::Mixed) => UsageKind::Mixed, } } } @@ -458,7 +456,7 @@ impl, X: Cx> SearchGraph { for _ in 0..D::FIXPOINT_STEP_LIMIT { match self.fixpoint_step_in_task(cx, input, inspect, &mut prove_goal) { StepResult::Done(final_entry, result) => return (final_entry, result), - StepResult::HasChanged => debug!("fixpoint changed provisional results"), + StepResult::HasChanged => {} } } @@ -623,6 +621,7 @@ impl, X: Cx> SearchGraph { if D::reached_fixpoint(cx, usage_kind, input, stack_entry.provisional_result, result) { StepResult::Done(stack_entry, result) } else { + debug!(?result, "fixpoint changed provisional results"); let depth = self.stack.push(StackEntry { has_been_used: None, provisional_result: Some(result), From 9308401df57318cf3b3ad72bd3674516fe9d1c6c Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Jul 2024 14:59:01 +0200 Subject: [PATCH 082/178] tracing: debug to trace --- compiler/rustc_type_ir/src/binder.rs | 24 ++++++++---------------- compiler/rustc_type_ir/src/fold.rs | 11 +++++------ 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index c1f6fb36324ed..8797288070e71 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -8,7 +8,7 @@ use derive_where::derive_where; use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; #[cfg(feature = "nightly")] use rustc_serialize::Decodable; -use tracing::debug; +use tracing::instrument; use crate::data_structures::SsoHashSet; use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -831,28 +831,20 @@ impl<'a, I: Interner> ArgFolder<'a, I> { /// As indicated in the diagram, here the same type `&'a i32` is instantiated once, but in the /// first case we do not increase the De Bruijn index and in the second case we do. The reason /// is that only in the second case have we passed through a fn binder. + #[instrument(level = "trace", skip(self), fields(binders_passed = self.binders_passed), ret)] fn shift_vars_through_binders>(&self, val: T) -> T { - debug!( - "shift_vars(val={:?}, binders_passed={:?}, has_escaping_bound_vars={:?})", - val, - self.binders_passed, - val.has_escaping_bound_vars() - ); - if self.binders_passed == 0 || !val.has_escaping_bound_vars() { - return val; + val + } else { + ty::fold::shift_vars(self.cx, val, self.binders_passed) } - - let result = ty::fold::shift_vars(TypeFolder::cx(self), val, self.binders_passed); - debug!("shift_vars: shifted result = {:?}", result); - - result } fn shift_region_through_binders(&self, region: I::Region) -> I::Region { if self.binders_passed == 0 || !region.has_escaping_bound_vars() { - return region; + region + } else { + ty::fold::shift_region(self.cx, region, self.binders_passed) } - ty::fold::shift_region(self.cx, region, self.binders_passed) } } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index d37bacc7d359f..8e3534b0e9eb4 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -48,7 +48,7 @@ use std::mem; use rustc_index::{Idx, IndexVec}; -use tracing::debug; +use tracing::instrument; use crate::data_structures::Lrc; use crate::inherent::*; @@ -417,15 +417,14 @@ pub fn shift_region(cx: I, region: I::Region, amount: u32) -> I::Re } } +#[instrument(level = "trace", skip(cx), ret)] pub fn shift_vars(cx: I, value: T, amount: u32) -> T where T: TypeFoldable, { - debug!("shift_vars(value={:?}, amount={})", value, amount); - if amount == 0 || !value.has_escaping_bound_vars() { - return value; + value + } else { + value.fold_with(&mut Shifter::new(cx, amount)) } - - value.fold_with(&mut Shifter::new(cx, amount)) } From e83eacdfaa9002559d3a301a0d1a0f54fa253f1d Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Jul 2024 23:03:34 +0200 Subject: [PATCH 083/178] move behavior out of shared fn --- .../rustc_type_ir/src/search_graph/mod.rs | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 6c67bf13ac73e..18a5a85dfa83a 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -300,17 +300,7 @@ impl, X: Cx> SearchGraph { // We update both the head of this cycle to rerun its evaluation until // we reach a fixpoint and all other cycle participants to make sure that // their result does not get moved to the global cache. - fn tag_cycle_participants( - stack: &mut IndexVec>, - usage_kind: Option, - head: StackDepth, - ) { - if let Some(usage_kind) = usage_kind { - stack[head].has_been_used = - Some(stack[head].has_been_used.map_or(usage_kind, |prev| prev.merge(usage_kind))); - } - debug_assert!(stack[head].has_been_used.is_some()); - + fn tag_cycle_participants(stack: &mut IndexVec>, head: StackDepth) { // The current root of these cycles. Note that this may not be the final // root in case a later goal depends on a goal higher up the stack. let mut current_root = head; @@ -403,7 +393,8 @@ impl, X: Cx> SearchGraph { // We have a nested goal which is already in the provisional cache, use // its result. We do not provide any usage kind as that should have been // already set correctly while computing the cache entry. - Self::tag_cycle_participants(&mut self.stack, None, entry.head); + debug_assert!(self.stack[entry.head].has_been_used.is_some()); + Self::tag_cycle_participants(&mut self.stack, entry.head); return entry.result; } else if let Some(stack_depth) = cache_entry.stack_depth { debug!("encountered cycle with depth {stack_depth:?}"); @@ -416,11 +407,13 @@ impl, X: Cx> SearchGraph { let is_coinductive_cycle = Self::stack_coinductive_from(cx, &self.stack, stack_depth); let cycle_kind = if is_coinductive_cycle { CycleKind::Coinductive } else { CycleKind::Inductive }; - Self::tag_cycle_participants( - &mut self.stack, - Some(UsageKind::Single(cycle_kind)), - stack_depth, + let usage_kind = UsageKind::Single(cycle_kind); + self.stack[stack_depth].has_been_used = Some( + self.stack[stack_depth] + .has_been_used + .map_or(usage_kind, |prev| prev.merge(usage_kind)), ); + Self::tag_cycle_participants(&mut self.stack, stack_depth); // Return the provisional result or, if we're in the first iteration, // start with no constraints. From 0c75c080a7d0662d79c9392ea4a57a805d3b1883 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Jul 2024 23:08:14 +0200 Subject: [PATCH 084/178] merge impl blocks --- compiler/rustc_type_ir/src/search_graph/mod.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 18a5a85dfa83a..bd93c86b0860b 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -118,6 +118,11 @@ impl UsageKind { } } +enum StepResult { + Done(StackEntry, X::Result), + HasChanged, +} + #[derive(Debug, Clone, Copy)] struct AvailableDepth(usize); impl AvailableDepth { @@ -559,14 +564,7 @@ impl, X: Cx> SearchGraph { ) }) } -} -enum StepResult { - Done(StackEntry, X::Result), - HasChanged, -} - -impl, X: Cx> SearchGraph { /// When we encounter a coinductive cycle, we have to fetch the /// result of that cycle while we are still computing it. Because /// of this we continuously recompute the cycle until the result From f860873983d1db578aea09d47cd594d9da512e49 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 23 Jul 2024 23:19:29 +0200 Subject: [PATCH 085/178] split provisional cache and stack lookup this makes it easier to maintain and modify going forward. There may be a small performance cost as we now need to access the provisional cache *and* walk through the stack to detect cycles. However, the provisional cache should be mostly empty and the stack should only have a few elements so the performance impact is likely minimal. Given the complexity of the search graph maintainability trumps linear performance improvements. --- .../rustc_type_ir/src/search_graph/mod.rs | 204 +++++++++--------- .../src/search_graph/validate.rs | 18 +- 2 files changed, 104 insertions(+), 118 deletions(-) diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index bd93c86b0860b..a1aa63c2e7d75 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -224,8 +224,8 @@ struct DetachedEntry { result: X::Result, } -/// Stores the stack depth of a currently evaluated goal *and* already -/// computed results for goals which depend on other goals still on the stack. +/// Stores the provisional result of already computed results for goals which +/// depend on other goals still on the stack. /// /// The provisional result may depend on whether the stack above it is inductive /// or coinductive. Because of this, we store separate provisional results for @@ -238,16 +238,13 @@ struct DetachedEntry { /// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs. #[derive_where(Default; X: Cx)] struct ProvisionalCacheEntry { - stack_depth: Option, with_inductive_stack: Option>, with_coinductive_stack: Option>, } impl ProvisionalCacheEntry { fn is_empty(&self) -> bool { - self.stack_depth.is_none() - && self.with_inductive_stack.is_none() - && self.with_coinductive_stack.is_none() + self.with_inductive_stack.is_none() && self.with_coinductive_stack.is_none() } } @@ -378,71 +375,26 @@ impl, X: Cx> SearchGraph { None }; - // Check whether the goal is in the provisional cache. - // The provisional result may rely on the path to its cycle roots, - // so we have to check the path of the current goal matches that of - // the cache entry. - let cache_entry = self.provisional_cache.entry(input).or_default(); - if let Some(entry) = cache_entry - .with_coinductive_stack - .as_ref() - .filter(|p| Self::stack_coinductive_from(cx, &self.stack, p.head)) - .or_else(|| { - cache_entry - .with_inductive_stack - .as_ref() - .filter(|p| !Self::stack_coinductive_from(cx, &self.stack, p.head)) - }) - { - debug!("provisional cache hit"); - // We have a nested goal which is already in the provisional cache, use - // its result. We do not provide any usage kind as that should have been - // already set correctly while computing the cache entry. - debug_assert!(self.stack[entry.head].has_been_used.is_some()); - Self::tag_cycle_participants(&mut self.stack, entry.head); - return entry.result; - } else if let Some(stack_depth) = cache_entry.stack_depth { - debug!("encountered cycle with depth {stack_depth:?}"); - // We have a nested goal which directly relies on a goal deeper in the stack. - // - // We start by tagging all cycle participants, as that's necessary for caching. - // - // Finally we can return either the provisional response or the initial response - // in case we're in the first fixpoint iteration for this goal. - let is_coinductive_cycle = Self::stack_coinductive_from(cx, &self.stack, stack_depth); - let cycle_kind = - if is_coinductive_cycle { CycleKind::Coinductive } else { CycleKind::Inductive }; - let usage_kind = UsageKind::Single(cycle_kind); - self.stack[stack_depth].has_been_used = Some( - self.stack[stack_depth] - .has_been_used - .map_or(usage_kind, |prev| prev.merge(usage_kind)), - ); - Self::tag_cycle_participants(&mut self.stack, stack_depth); - - // Return the provisional result or, if we're in the first iteration, - // start with no constraints. - return if let Some(result) = self.stack[stack_depth].provisional_result { - result - } else { - D::initial_provisional_result(cx, cycle_kind, input) - }; - } else { - // No entry, we push this goal on the stack and try to prove it. - let depth = self.stack.next_index(); - let entry = StackEntry { - input, - available_depth, - reached_depth: depth, - non_root_cycle_participant: None, - encountered_overflow: false, - has_been_used: None, - nested_goals: Default::default(), - provisional_result: None, - }; - assert_eq!(self.stack.push(entry), depth); - cache_entry.stack_depth = Some(depth); + if let Some(result) = self.lookup_provisional_cache(cx, input) { + return result; + } + + if let Some(result) = self.check_cycle_on_stack(cx, input) { + return result; + } + + let depth = self.stack.next_index(); + let entry = StackEntry { + input, + available_depth, + reached_depth: depth, + non_root_cycle_participant: None, + encountered_overflow: false, + has_been_used: None, + nested_goals: Default::default(), + provisional_result: None, }; + assert_eq!(self.stack.push(entry), depth); // This is for global caching, so we properly track query dependencies. // Everything that affects the `result` should be performed within this @@ -474,8 +426,7 @@ impl, X: Cx> SearchGraph { debug_assert!(validate_cache.is_none()); let coinductive_stack = Self::stack_coinductive_from(cx, &self.stack, head); - let entry = self.provisional_cache.get_mut(&input).unwrap(); - entry.stack_depth = None; + let entry = self.provisional_cache.entry(input).or_default(); if coinductive_stack { entry.with_coinductive_stack = Some(DetachedEntry { head, result }); } else { @@ -534,35 +485,52 @@ impl, X: Cx> SearchGraph { }) } - /// When encountering a cycle, both inductive and coinductive, we only - /// move the root into the global cache. We also store all other cycle - /// participants involved. - /// - /// We must not use the global cache entry of a root goal if a cycle - /// participant is on the stack. This is necessary to prevent unstable - /// results. See the comment of `StackEntry::nested_goals` for - /// more details. - fn insert_global_cache( - &mut self, - cx: X, - input: X::Input, - final_entry: StackEntry, - result: X::Result, - dep_node: X::DepNodeIndex, - ) { - let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len(); - debug!(?final_entry, ?result, "insert global cache"); - cx.with_global_cache(self.mode, |cache| { - cache.insert( - cx, - input, - result, - dep_node, - additional_depth, - final_entry.encountered_overflow, - &final_entry.nested_goals, - ) - }) + fn lookup_provisional_cache(&mut self, cx: X, input: X::Input) -> Option { + let cache_entry = self.provisional_cache.get(&input)?; + let &DetachedEntry { head, result } = cache_entry + .with_coinductive_stack + .as_ref() + .filter(|p| Self::stack_coinductive_from(cx, &self.stack, p.head)) + .or_else(|| { + cache_entry + .with_inductive_stack + .as_ref() + .filter(|p| !Self::stack_coinductive_from(cx, &self.stack, p.head)) + })?; + + debug!("provisional cache hit"); + // We have a nested goal which is already in the provisional cache, use + // its result. We do not provide any usage kind as that should have been + // already set correctly while computing the cache entry. + Self::tag_cycle_participants(&mut self.stack, head); + debug_assert!(self.stack[head].has_been_used.is_some()); + Some(result) + } + + fn check_cycle_on_stack(&mut self, cx: X, input: X::Input) -> Option { + let (head, _stack_entry) = self.stack.iter_enumerated().find(|(_, e)| e.input == input)?; + debug!("encountered cycle with depth {head:?}"); + // We have a nested goal which directly relies on a goal deeper in the stack. + // + // We start by tagging all cycle participants, as that's necessary for caching. + // + // Finally we can return either the provisional response or the initial response + // in case we're in the first fixpoint iteration for this goal. + let is_coinductive_cycle = Self::stack_coinductive_from(cx, &self.stack, head); + let cycle_kind = + if is_coinductive_cycle { CycleKind::Coinductive } else { CycleKind::Inductive }; + let usage_kind = UsageKind::Single(cycle_kind); + self.stack[head].has_been_used = + Some(self.stack[head].has_been_used.map_or(usage_kind, |prev| prev.merge(usage_kind))); + Self::tag_cycle_participants(&mut self.stack, head); + + // Return the provisional result or, if we're in the first iteration, + // start with no constraints. + if let Some(result) = self.stack[head].provisional_result { + Some(result) + } else { + Some(D::initial_provisional_result(cx, cycle_kind, input)) + } } /// When we encounter a coinductive cycle, we have to fetch the @@ -613,13 +581,43 @@ impl, X: Cx> SearchGraph { StepResult::Done(stack_entry, result) } else { debug!(?result, "fixpoint changed provisional results"); - let depth = self.stack.push(StackEntry { + self.stack.push(StackEntry { has_been_used: None, provisional_result: Some(result), ..stack_entry }); - debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth)); StepResult::HasChanged } } + + /// When encountering a cycle, both inductive and coinductive, we only + /// move the root into the global cache. We also store all other cycle + /// participants involved. + /// + /// We must not use the global cache entry of a root goal if a cycle + /// participant is on the stack. This is necessary to prevent unstable + /// results. See the comment of `StackEntry::nested_goals` for + /// more details. + fn insert_global_cache( + &mut self, + cx: X, + input: X::Input, + final_entry: StackEntry, + result: X::Result, + dep_node: X::DepNodeIndex, + ) { + let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len(); + debug!(?final_entry, ?result, "insert global cache"); + cx.with_global_cache(self.mode, |cache| { + cache.insert( + cx, + input, + result, + dep_node, + additional_depth, + final_entry.encountered_overflow, + &final_entry.nested_goals, + ) + }) + } } diff --git a/compiler/rustc_type_ir/src/search_graph/validate.rs b/compiler/rustc_type_ir/src/search_graph/validate.rs index 1ae806834ba7d..b4802811b0f57 100644 --- a/compiler/rustc_type_ir/src/search_graph/validate.rs +++ b/compiler/rustc_type_ir/src/search_graph/validate.rs @@ -23,8 +23,6 @@ impl, X: Cx> SearchGraph { ref nested_goals, provisional_result, } = *entry; - let cache_entry = provisional_cache.get(&entry.input).unwrap(); - assert_eq!(cache_entry.stack_depth, Some(depth)); if let Some(head) = non_root_cycle_participant { assert!(head < depth); assert!(nested_goals.is_empty()); @@ -45,19 +43,9 @@ impl, X: Cx> SearchGraph { } } - for (&input, entry) in &self.provisional_cache { - let ProvisionalCacheEntry { stack_depth, with_coinductive_stack, with_inductive_stack } = - entry; - assert!( - stack_depth.is_some() - || with_coinductive_stack.is_some() - || with_inductive_stack.is_some() - ); - - if let &Some(stack_depth) = stack_depth { - assert_eq!(stack[stack_depth].input, input); - } - + for (&_input, entry) in &self.provisional_cache { + let ProvisionalCacheEntry { with_coinductive_stack, with_inductive_stack } = entry; + assert!(with_coinductive_stack.is_some() || with_inductive_stack.is_some()); let check_detached = |detached_entry: &DetachedEntry| { let DetachedEntry { head, result: _ } = *detached_entry; assert_ne!(stack[head].has_been_used, None); From 94bd4da3f107952a6128f094858f19f13417a204 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 12 Aug 2024 10:42:35 +0200 Subject: [PATCH 086/178] Revert "Remove unneeded `send` method" This reverts commit 567bde603cfeedb5cfc44e441578c5416bfc4f35. --- .../crates/rust-analyzer/src/flycheck.rs | 26 +++++++++---------- .../crates/rust-analyzer/src/global_state.rs | 12 ++++++--- .../crates/vfs-notify/src/lib.rs | 22 +++++++++------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 0ea782e1dee72..168f9702d1c8b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -256,7 +256,7 @@ impl FlycheckActor { } fn report_progress(&self, progress: Progress) { - self.sender.send(FlycheckMessage::Progress { id: self.id, progress }).unwrap(); + self.send(FlycheckMessage::Progress { id: self.id, progress }); } fn next_event(&self, inbox: &Receiver) -> Option { @@ -329,9 +329,7 @@ impl FlycheckActor { ); } if self.status == FlycheckStatus::Started { - self.sender - .send(FlycheckMessage::ClearDiagnostics { id: self.id }) - .unwrap(); + self.send(FlycheckMessage::ClearDiagnostics { id: self.id }); } self.report_progress(Progress::DidFinish(res)); self.status = FlycheckStatus::Finished; @@ -353,17 +351,13 @@ impl FlycheckActor { "diagnostic received" ); if self.status == FlycheckStatus::Started { - self.sender - .send(FlycheckMessage::ClearDiagnostics { id: self.id }) - .unwrap(); + self.send(FlycheckMessage::ClearDiagnostics { id: self.id }); } - self.sender - .send(FlycheckMessage::AddDiagnostic { - id: self.id, - workspace_root: self.root.clone(), - diagnostic: msg, - }) - .unwrap(); + self.send(FlycheckMessage::AddDiagnostic { + id: self.id, + workspace_root: self.root.clone(), + diagnostic: msg, + }); self.status = FlycheckStatus::DiagnosticSent; } }, @@ -483,6 +477,10 @@ impl FlycheckActor { cmd.args(args); Some(cmd) } + + fn send(&self, check_task: FlycheckMessage) { + self.sender.send(check_task).unwrap(); + } } #[allow(clippy::large_enum_variant)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 71f4896727110..bb883a9eaf5cf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -504,7 +504,7 @@ impl GlobalState { handler: ReqHandler, ) { let request = self.req_queue.outgoing.register(R::METHOD.to_owned(), params, handler); - self.sender.send(request.into()).unwrap(); + self.send(request.into()); } pub(crate) fn complete_request(&mut self, response: lsp_server::Response) { @@ -521,7 +521,7 @@ impl GlobalState { params: N::Params, ) { let not = lsp_server::Notification::new(N::METHOD.to_owned(), params); - self.sender.send(not.into()).unwrap(); + self.send(not.into()); } pub(crate) fn register_request( @@ -544,13 +544,13 @@ impl GlobalState { let duration = start.elapsed(); tracing::debug!("handled {} - ({}) in {:0.2?}", method, response.id, duration); - self.sender.send(response.into()).unwrap(); + self.send(response.into()); } } pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) { if let Some(response) = self.req_queue.incoming.cancel(request_id) { - self.sender.send(response.into()).unwrap(); + self.send(response.into()); } } @@ -558,6 +558,10 @@ impl GlobalState { self.req_queue.incoming.is_completed(&request.id) } + fn send(&self, message: lsp_server::Message) { + self.sender.send(message).unwrap() + } + pub(crate) fn publish_diagnostics( &mut self, uri: Url, diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index bf96788d373df..5788fac547497 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -186,19 +186,17 @@ impl NotifyActor { } } - self.sender - .send(loader::Message::Progress { - n_total, - n_done: LoadingProgress::Finished, - config_version, - dir: None, - }) - .unwrap(); + self.send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Finished, + config_version, + dir: None, + }); } Message::Invalidate(path) => { let contents = read(path.as_path()); let files = vec![(path, contents)]; - self.sender.send(loader::Message::Changed { files }).unwrap(); + self.send(loader::Message::Changed { files }); } }, Event::NotifyEvent(event) => { @@ -246,7 +244,7 @@ impl NotifyActor { Some((path, contents)) }) .collect(); - self.sender.send(loader::Message::Changed { files }).unwrap(); + self.send(loader::Message::Changed { files }); } } } @@ -330,6 +328,10 @@ impl NotifyActor { log_notify_error(watcher.watch(path, RecursiveMode::NonRecursive)); } } + + fn send(&self, msg: loader::Message) { + self.sender.send(msg).unwrap(); + } } fn read(path: &AbsPath) -> Option> { From 7586ba6bfb0e046e4a9f902fe28132b8290445c0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 12 Aug 2024 10:44:00 +0200 Subject: [PATCH 087/178] Add track_caller --- src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 5788fac547497..cc10793ba47b2 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -329,6 +329,7 @@ impl NotifyActor { } } + #[track_caller] fn send(&self, msg: loader::Message) { self.sender.send(msg).unwrap(); } From 285285d3fd9c931f1425bb13f320569b84d40139 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 12 Aug 2024 10:54:46 +0200 Subject: [PATCH 088/178] Add more track_caller --- src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs | 1 + .../rust-analyzer/crates/rust-analyzer/src/global_state.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 168f9702d1c8b..acd6c3fcd2dd2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -478,6 +478,7 @@ impl FlycheckActor { Some(cmd) } + #[track_caller] fn send(&self, check_task: FlycheckMessage) { self.sender.send(check_task).unwrap(); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index bb883a9eaf5cf..88ff25e5d120d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -558,8 +558,9 @@ impl GlobalState { self.req_queue.incoming.is_completed(&request.id) } + #[track_caller] fn send(&self, message: lsp_server::Message) { - self.sender.send(message).unwrap() + self.sender.send(message).unwrap(); } pub(crate) fn publish_diagnostics( From f7c4716dbfd77e94af774b4531213519821faba6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 12 Aug 2024 10:55:04 +0200 Subject: [PATCH 089/178] Use the send method --- .../crates/vfs-notify/src/lib.rs | 54 ++++++++----------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index cc10793ba47b2..fa2b6669664c0 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -119,14 +119,12 @@ impl NotifyActor { self.watched_dir_entries.clear(); self.watched_file_entries.clear(); - self.sender - .send(loader::Message::Progress { - n_total, - n_done: LoadingProgress::Started, - config_version, - dir: None, - }) - .unwrap(); + self.send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Started, + config_version, + dir: None, + }); let (entry_tx, entry_rx) = unbounded(); let (watch_tx, watch_rx) = unbounded(); @@ -142,31 +140,25 @@ impl NotifyActor { entry, do_watch, |file| { - self.sender - .send(loader::Message::Progress { - n_total, - n_done: LoadingProgress::Progress( - processed - .load(std::sync::atomic::Ordering::Relaxed), - ), - dir: Some(file), - config_version, - }) - .unwrap() + self.send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Progress( + processed.load(std::sync::atomic::Ordering::Relaxed), + ), + dir: Some(file), + config_version, + }); }, ); - self.sender.send(loader::Message::Loaded { files }).unwrap(); - self.sender - .send(loader::Message::Progress { - n_total, - n_done: LoadingProgress::Progress( - processed.fetch_add(1, std::sync::atomic::Ordering::AcqRel) - + 1, - ), - config_version, - dir: None, - }) - .unwrap(); + self.send(loader::Message::Loaded { files }); + self.send(loader::Message::Progress { + n_total, + n_done: LoadingProgress::Progress( + processed.fetch_add(1, std::sync::atomic::Ordering::AcqRel) + 1, + ), + config_version, + dir: None, + }); }); drop(watch_tx); From 3355c788f18d17e61ac6a87af921f902e3ffa39a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 12 Aug 2024 10:56:59 +0200 Subject: [PATCH 090/178] fix: Correctly support `#[rustc_deprecated_safe_2024]` --- .../rust-analyzer/crates/hir-def/src/body.rs | 2 +- .../rust-analyzer/crates/hir-def/src/data.rs | 14 +++++++---- .../hir-ty/src/diagnostics/unsafe_check.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/utils.rs | 7 +----- .../rust-analyzer/crates/hir/src/display.rs | 8 +++---- src/tools/rust-analyzer/crates/hir/src/lib.rs | 4 ++-- .../src/handlers/missing_unsafe.rs | 24 +++++++++++++++++++ 8 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index 58812479ddf58..a988317e046ef 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -158,7 +158,7 @@ impl Body { }), ) }); - is_async_fn = data.has_async_kw(); + is_async_fn = data.is_async(); src.map(|it| it.body().map(ast::Expr::from)) } DefWithBodyId::ConstId(c) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index c3c2e51fd0385..d17ebd7ff920b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -94,6 +94,12 @@ impl FunctionData { .filter(|it| !it.is_empty()) .map(Box::new); let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists(); + if flags.contains(FnFlags::HAS_UNSAFE_KW) + && !crate_graph[krate].edition.at_least_2024() + && attrs.by_key(&sym::rustc_deprecated_safe_2024).exists() + { + flags.remove(FnFlags::HAS_UNSAFE_KW); + } Arc::new(FunctionData { name: func.name.clone(), @@ -126,19 +132,19 @@ impl FunctionData { self.flags.contains(FnFlags::HAS_SELF_PARAM) } - pub fn has_default_kw(&self) -> bool { + pub fn is_default(&self) -> bool { self.flags.contains(FnFlags::HAS_DEFAULT_KW) } - pub fn has_const_kw(&self) -> bool { + pub fn is_const(&self) -> bool { self.flags.contains(FnFlags::HAS_CONST_KW) } - pub fn has_async_kw(&self) -> bool { + pub fn is_async(&self) -> bool { self.flags.contains(FnFlags::HAS_ASYNC_KW) } - pub fn has_unsafe_kw(&self) -> bool { + pub fn is_unsafe(&self) -> bool { self.flags.contains(FnFlags::HAS_UNSAFE_KW) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 22aa5c69bb03d..3f54cdd20cee3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -17,7 +17,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { let mut res = Vec::new(); let is_unsafe = match def { - DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(), + DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 444628ff521d6..c37cde731199f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1857,7 +1857,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { params, ret, data.is_varargs(), - if data.has_unsafe_kw() { Safety::Unsafe } else { Safety::Safe }, + if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), ); make_binders(db, &generics, sig) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index fbec332885dfa..d1ce68da6d6d8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -253,12 +253,7 @@ impl<'a> ClosureSubst<'a> { pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { let data = db.function_data(func); - if data.has_unsafe_kw() { - // Functions that are `#[rustc_deprecated_safe_2024]` are safe to call before 2024. - if db.attrs(func.into()).by_key(&sym::rustc_deprecated_safe_2024).exists() { - // FIXME: Properly check the caller span and mark it as unsafe after 2024. - return false; - } + if data.is_unsafe() { return true; } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 7def828e95f3c..12dd8b5bf4f5d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -69,13 +69,13 @@ impl HirDisplay for Function { write_visibility(module_id, self.visibility(db), f)?; - if data.has_default_kw() { + if data.is_default() { f.write_str("default ")?; } - if data.has_const_kw() { + if data.is_const() { f.write_str("const ")?; } - if data.has_async_kw() { + if data.is_async() { f.write_str("async ")?; } if self.is_unsafe_to_call(db) { @@ -125,7 +125,7 @@ impl HirDisplay for Function { // `FunctionData::ret_type` will be `::core::future::Future` for async fns. // Use ugly pattern match to strip the Future trait. // Better way? - let ret_type = if !data.has_async_kw() { + let ret_type = if !data.is_async() { &data.ret_type } else { match &*data.ret_type { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 67fbe3b789c49..1a3becdf50e9b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2189,11 +2189,11 @@ impl Function { } pub fn is_const(self, db: &dyn HirDatabase) -> bool { - db.function_data(self.id).has_const_kw() + db.function_data(self.id).is_const() } pub fn is_async(self, db: &dyn HirDatabase) -> bool { - db.function_data(self.id).has_async_kw() + db.function_data(self.id).is_async() } /// Does this function have `#[test]` attribute? diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 30dd26a118d27..af8ac6005d7a7 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -483,6 +483,30 @@ unsafe fn foo() -> u8 { fn main() { let x = format!("foo: {}", foo$0()); +} + "#, + ) + } + + #[test] + fn rustc_deprecated_safe_2024() { + check_diagnostics( + r#" +//- /ed2021.rs crate:ed2021 edition:2021 +#[rustc_deprecated_safe_2024] +unsafe fn safe() -> u8 { + 0 +} +//- /ed2024.rs crate:ed2024 edition:2024 +#[rustc_deprecated_safe_2024] +unsafe fn not_safe() -> u8 { + 0 +} +//- /main.rs crate:main deps:ed2021,ed2024 +fn main() { + ed2021::safe(); + ed2024::not_safe(); + //^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block } "#, ) From 72ef357ea31ed9b8c926bea6c1ae608ccc9dc0d2 Mon Sep 17 00:00:00 2001 From: burlinchen Date: Mon, 12 Aug 2024 17:34:00 +0800 Subject: [PATCH 091/178] chore(lib): Enhance documentation for core::fmt::Formatter's write_fmt method --- library/core/src/fmt/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 60c0dc7685253..8143ff4addcdd 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1626,6 +1626,11 @@ impl<'a> Formatter<'a> { self.buf.write_str(data) } + /// Glue for usage of the [`write!`] macro with implementors of this trait. + /// + /// This method should generally not be invoked manually, but rather through + /// the [`write!`] macro itself. + /// /// Writes some formatted information into this instance. /// /// # Examples From 69f613892ad984504112ad16feab718937936ac9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 12 Aug 2024 11:51:43 +0200 Subject: [PATCH 092/178] minor: Bump lockfile --- src/tools/rust-analyzer/Cargo.lock | 294 ++++++++++++++++------------- 1 file changed, 160 insertions(+), 134 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index b6bf516af1542..d587f0f72cc20 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -52,16 +52,16 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", - "object 0.35.0", + "object 0.36.3", "rustc-demangle", ] @@ -92,9 +92,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "byteorder" @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" [[package]] name = "cfg" @@ -185,7 +185,7 @@ version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f2eb1cd6054da221bd1ac0197fb2fe5e2caf3dcb93619398fc1433f8f09093" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "chalk-derive", ] @@ -226,9 +226,9 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" [[package]] name = "cov-mark" -version = "2.0.0-pre.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d48d8f76bd9331f19fe2aaf3821a9f9fb32c3963e1e3d6ce82a8c09cef7444a" +checksum = "0570650661aa447e7335f1d5e4f499d8e58796e617bedc9267d971e51c8b49d4" [[package]] name = "crc32fast" @@ -366,9 +366,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "ena" @@ -397,14 +397,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -415,9 +415,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" dependencies = [ "crc32fast", "miniz_oxide", @@ -512,7 +512,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg", "cov-mark", "dashmap", @@ -578,7 +578,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.5.0", + "bitflags 2.6.0", "chalk-derive", "chalk-ir", "chalk-recursive", @@ -707,7 +707,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.5.0", + "bitflags 2.6.0", "cov-mark", "crossbeam-channel", "either", @@ -788,9 +788,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown", @@ -880,9 +880,9 @@ checksum = "3752f229dcc5a481d60f385fa479ff46818033d881d2d801aa27dffcfb5e8306" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" @@ -892,19 +892,19 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] name = "libmimalloc-sys" -version = "0.1.38" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7bb23d733dfcc8af652a78b7bf232f0e967710d044732185e561e47c0336b6" +checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44" dependencies = [ "cc", "libc", @@ -916,8 +916,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", + "redox_syscall", ] [[package]] @@ -981,9 +982,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lsp-server" @@ -1049,9 +1050,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -1073,18 +1074,18 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9186d86b79b52f4a77af65604b51225e8db1d6ee7e3f41aec1e40829c71a176" +checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633" dependencies = [ "libmimalloc-sys", ] [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -1116,7 +1117,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", @@ -1134,7 +1135,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -1149,11 +1150,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1183,9 +1184,9 @@ dependencies = [ [[package]] name = "object" -version = "0.35.0" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -1198,9 +1199,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" -version = "11.1.3" +version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "option-ext" @@ -1226,9 +1227,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -1306,9 +1307,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro-api" @@ -1365,9 +1369,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -1445,7 +1449,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "memchr", "unicase", ] @@ -1474,7 +1478,7 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "ra-ap-rustc_index", "tracing", ] @@ -1587,20 +1591,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -1702,9 +1697,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_apfloat" -version = "0.2.0+llvm-462a31f5a5ab" +version = "0.2.1+llvm-462a31f5a5ab" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465187772033a5ee566f69fe008df03628fce549a0899aae76f0a0c2e34696be" +checksum = "886d94c63c812a8037c4faca2607453a0fa4cf82f734665266876b022244543f" dependencies = [ "bitflags 1.3.2", "smallvec", @@ -1787,18 +1782,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" dependencies = [ "proc-macro2", "quote", @@ -1807,12 +1802,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" dependencies = [ "indexmap", "itoa", + "memchr", "ryu", "serde", ] @@ -1830,9 +1826,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -1909,9 +1905,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", @@ -2012,18 +2008,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -2092,9 +2088,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -2107,9 +2103,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.8.14" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", @@ -2119,18 +2115,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", @@ -2216,9 +2212,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2cb4fbb9995eeb36ac86fadf24031ccd58f99d6b4b2d7b911db70bddb80d90" +checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" dependencies = [ "serde", "stable_deref_trait", @@ -2291,9 +2287,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2309,9 +2305,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vfs" @@ -2359,11 +2355,11 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2373,7 +2369,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" dependencies = [ "windows-core", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2385,7 +2381,7 @@ dependencies = [ "windows-implement", "windows-interface", "windows-result", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2412,11 +2408,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2434,7 +2430,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -2454,18 +2459,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2476,9 +2481,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2488,9 +2493,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2500,15 +2505,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2518,9 +2523,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2530,9 +2535,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2542,9 +2547,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2554,15 +2559,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.11" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -2622,6 +2627,27 @@ dependencies = [ "zip", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zip" version = "0.6.6" From 407944764ae9bc71ec7957adf36a414bd2f55e29 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 12 Aug 2024 11:50:35 +0200 Subject: [PATCH 093/178] fix: Fix publish libs workflow --- src/tools/rust-analyzer/.github/workflows/publish-libs.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml index 34ca53e2e53ad..945cbf6767bc5 100644 --- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml +++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml @@ -31,6 +31,6 @@ jobs: git config --global user.email "runner@gha.local" git config --global user.name "GitHub Action" # Remove r-a crates from the workspaces so we don't auto-publish them as well - sed -i 's/ "crates\/\*"//' ./Cargo.toml - sed -i 's/ "xtask\/"//' ./Cargo.toml + sed -i 's/"crates\/\*"//' ./Cargo.toml + sed -i 's/"xtask\/"//' ./Cargo.toml cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty From 234d383f5fea762a02a85e20f2282c623ca76739 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Aug 2024 17:45:44 +0200 Subject: [PATCH 094/178] internal: Reply to requests with defaults when vfs is still loading --- src/tools/rust-analyzer/Cargo.toml | 2 +- .../rust-analyzer/src/handlers/dispatch.rs | 43 +++++++++++++++---- .../crates/rust-analyzer/src/lsp/ext.rs | 10 ++++- .../crates/rust-analyzer/src/main_loop.rs | 41 ++++++++++++------ .../rust-analyzer/docs/dev/lsp-extensions.md | 2 +- 5 files changed, 71 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index b2cf3451467fb..561c533a98d99 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.78" +rust-version = "1.80" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index ebdc196a658e4..a105ec638203b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -97,16 +97,45 @@ impl RequestDispatcher<'_> { self } - /// Dispatches a non-latency-sensitive request onto the thread pool. + /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not + /// ready this will return a default constructed [`R::Result`]. pub(crate) fn on( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, ) -> &mut Self where - R: lsp_types::request::Request + 'static, - R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, - R::Result: Serialize, + R: lsp_types::request::Request< + Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, + Result: Serialize + Default, + > + 'static, + { + if !self.global_state.vfs_done { + if let Some(lsp_server::Request { id, .. }) = + self.req.take_if(|it| it.method == R::METHOD) + { + self.global_state.respond(lsp_server::Response::new_ok(id, R::Result::default())); + } + return self; + } + self.on_with_thread_intent::(ThreadIntent::Worker, f) + } + + /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not + /// ready this will return the parameter as is. + pub(crate) fn on_identity( + &mut self, + f: fn(GlobalStateSnapshot, Params) -> anyhow::Result, + ) -> &mut Self + where + R: lsp_types::request::Request + 'static, + Params: Serialize + DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, { + if !self.global_state.vfs_done { + if let Some((request, params, _)) = self.parse::() { + self.global_state.respond(lsp_server::Response::new_ok(request.id, ¶ms)) + } + return self; + } self.on_with_thread_intent::(ThreadIntent::Worker, f) } @@ -198,11 +227,7 @@ impl RequestDispatcher<'_> { R: lsp_types::request::Request, R::Params: DeserializeOwned + fmt::Debug, { - let req = match &self.req { - Some(req) if req.method == R::METHOD => self.req.take()?, - _ => return None, - }; - + let req = self.req.take_if(|it| it.method == R::METHOD)?; let res = crate::from_json(R::METHOD, &req.params); match res { Ok(params) => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 1fcb636f85606..8d1a686dc4d41 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -61,7 +61,7 @@ impl Request for FetchDependencyList { #[serde(rename_all = "camelCase")] pub struct FetchDependencyListParams {} -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Default)] #[serde(rename_all = "camelCase")] pub struct FetchDependencyListResult { pub crates: Vec, @@ -194,7 +194,7 @@ pub struct TestItem { pub runnable: Option, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Default)] #[serde(rename_all = "camelCase")] pub struct DiscoverTestResults { pub tests: Vec, @@ -690,6 +690,12 @@ pub enum ExternalDocsResponse { WithLocal(ExternalDocsPair), } +impl Default for ExternalDocsResponse { + fn default() -> Self { + ExternalDocsResponse::Simple(None) + } +} + #[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ExternalDocsPair { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index e303765aab6c6..1d4ee71e5c1a0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -173,8 +173,10 @@ impl GlobalState { } if self.config.discover_workspace_config().is_none() { - let req = FetchWorkspaceRequest { path: None, force_crate_graph_reload: false }; - self.fetch_workspaces_queue.request_op("startup".to_owned(), req); + self.fetch_workspaces_queue.request_op( + "startup".to_owned(), + FetchWorkspaceRequest { path: None, force_crate_graph_reload: false }, + ); if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) = self.fetch_workspaces_queue.should_start_op() { @@ -545,6 +547,10 @@ impl GlobalState { let snapshot = self.snapshot(); self.task_pool.handle.spawn_with_sender(ThreadIntent::LatencySensitive, { let subscriptions = subscriptions.clone(); + // Do not fetch semantic diagnostics (and populate query results) if we haven't even + // loaded the initial workspace yet. + let fetch_semantic = + self.vfs_done && self.fetch_workspaces_queue.last_op_result().is_some(); move |sender| { let diags = fetch_native_diagnostics( &snapshot, @@ -556,15 +562,19 @@ impl GlobalState { .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags))) .unwrap(); - let diags = fetch_native_diagnostics( - &snapshot, - subscriptions, - slice, - NativeDiagnosticsFetchKind::Semantic, - ); - sender - .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic(generation, diags))) - .unwrap(); + if fetch_semantic { + let diags = fetch_native_diagnostics( + &snapshot, + subscriptions, + slice, + NativeDiagnosticsFetchKind::Semantic, + ); + sender + .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic( + generation, diags, + ))) + .unwrap(); + } } }); start = end; @@ -572,6 +582,9 @@ impl GlobalState { } fn update_tests(&mut self) { + if !self.vfs_done { + return; + } let db = self.analysis_host.raw_database(); let subscriptions = self .mem_docs @@ -1052,9 +1065,9 @@ impl GlobalState { .on::(handlers::handle_goto_implementation) .on::(handlers::handle_goto_type_definition) .on::(handlers::handle_inlay_hints) - .on::(handlers::handle_inlay_hints_resolve) + .on_identity::(handlers::handle_inlay_hints_resolve) .on::(handlers::handle_code_lens) - .on::(handlers::handle_code_lens_resolve) + .on_identity::(handlers::handle_code_lens_resolve) .on::(handlers::handle_prepare_rename) .on::(handlers::handle_rename) .on::(handlers::handle_references) @@ -1081,7 +1094,7 @@ impl GlobalState { .on::(handlers::handle_runnables) .on::(handlers::handle_related_tests) .on::(handlers::handle_code_action) - .on::(handlers::handle_code_action_resolve) + .on_identity::(handlers::handle_code_action_resolve) .on::(handlers::handle_hover) .on::(handlers::handle_open_docs) .on::(handlers::handle_open_cargo_toml) diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index e559f88e23353..4786bd54d59be 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/bad-interconversion.rs:27:33 @@ -56,7 +56,7 @@ LL | Some(ControlFlow::Break(123)?) | ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Option` | = help: the trait `FromResidual>` is not implemented for `Option` - = help: the trait `FromResidual` is implemented for `Option` + = help: the trait `FromResidual>` is implemented for `Option` error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow` --> $DIR/bad-interconversion.rs:32:39 diff --git a/tests/ui/try-trait/option-to-result.stderr b/tests/ui/try-trait/option-to-result.stderr index 8055b2a0b0452..1a5a925f92fce 100644 --- a/tests/ui/try-trait/option-to-result.stderr +++ b/tests/ui/try-trait/option-to-result.stderr @@ -20,7 +20,7 @@ LL | a?; | ^ use `.ok()?` if you want to discard the `Result` error information | = help: the trait `FromResidual>` is not implemented for `Option` - = help: the trait `FromResidual` is implemented for `Option` + = help: the trait `FromResidual>` is implemented for `Option` error: aborting due to 2 previous errors From c4333026a008741e5edba4bca1aae77aecba8d78 Mon Sep 17 00:00:00 2001 From: burlinchen Date: Tue, 13 Aug 2024 08:40:08 +0800 Subject: [PATCH 103/178] chore(lib): fmt core::fmt::Formatter's write_fmt method --- library/core/src/fmt/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 8143ff4addcdd..6078a71cb69fd 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1630,7 +1630,7 @@ impl<'a> Formatter<'a> { /// /// This method should generally not be invoked manually, but rather through /// the [`write!`] macro itself. - /// + /// /// Writes some formatted information into this instance. /// /// # Examples From 1e445f48d4d0880d4c158c3ae96be8f0d9ad2e02 Mon Sep 17 00:00:00 2001 From: Henry Sloan Date: Mon, 12 Aug 2024 18:48:03 -0700 Subject: [PATCH 104/178] Add must_use attribute to Coroutine trait --- library/core/src/ops/coroutine.rs | 1 + tests/ui/coroutine/issue-58888.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/ops/coroutine.rs b/library/core/src/ops/coroutine.rs index 13df888d24c5c..c7d596d74c383 100644 --- a/library/core/src/ops/coroutine.rs +++ b/library/core/src/ops/coroutine.rs @@ -69,6 +69,7 @@ pub enum CoroutineState { #[lang = "coroutine"] #[unstable(feature = "coroutine_trait", issue = "43122")] #[fundamental] +#[must_use = "coroutines are lazy and do nothing unless resumed"] pub trait Coroutine { /// The type of value this coroutine yields. /// diff --git a/tests/ui/coroutine/issue-58888.rs b/tests/ui/coroutine/issue-58888.rs index 6266f97ce8c46..e4fada0cd432e 100644 --- a/tests/ui/coroutine/issue-58888.rs +++ b/tests/ui/coroutine/issue-58888.rs @@ -13,7 +13,8 @@ impl Database { } fn check_connection(&self) -> impl Coroutine + '_ { - #[coroutine] move || { + #[coroutine] + move || { let iter = self.get_connection(); for i in iter { yield i @@ -23,5 +24,5 @@ impl Database { } fn main() { - Database.check_connection(); + let _ = Database.check_connection(); } From ac88b330b875e8058589b1804ac5d95fcd40905d Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 13 Aug 2024 08:21:16 +0300 Subject: [PATCH 105/178] Revert to original loop for const pow exponents Give LLVM the for original, optimizable loop in pow and wrapped_pow functions in the case when the exponent is statically known. --- library/core/src/num/int_macros.rs | 135 ++++++++++++---------------- library/core/src/num/uint_macros.rs | 135 ++++++++++++---------------- 2 files changed, 110 insertions(+), 160 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index be0e6a2a03b70..d8ef36f21ac63 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2174,54 +2174,41 @@ macro_rules! int_impl { #[inline] #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn wrapping_pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } let mut base = self; + let mut acc: Self = 1; if intrinsics::is_val_statically_known(exp) { - // Unroll multiplications for small exponent values. - // This gives the optimizer a way to efficiently inline call sites - // for the most common use cases with constant exponents. - // Currently, LLVM is unable to unroll the loop below. - match exp { - 0 => return 1, - 1 => return base, - 2 => return base.wrapping_mul(base), - 3 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(base); - } - 4 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(squared); + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); } - 5 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(squared).wrapping_mul(base); - } - 6 => { - let cubed = base.wrapping_mul(base).wrapping_mul(base); - return cubed.wrapping_mul(cubed); - } - _ => {} + exp /= 2; + base = base.wrapping_mul(base); } - } else { - if exp == 0 { - return 1; - } - } - debug_assert!(exp != 0); - let mut acc: Self = 1; - - loop { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - // since exp!=0, finally the exp must be 1. - if exp == 1 { - return acc; + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary. + acc.wrapping_mul(base) + } else { + // This is faster than the above when the exponent is not known + // at compile time. We can't use the same code for the constant + // exponent case because LLVM is currently unable to unroll + // this loop. + loop { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } + exp /= 2; + base = base.wrapping_mul(base); } - exp /= 2; - base = base.wrapping_mul(base); } } @@ -2753,54 +2740,42 @@ macro_rules! int_impl { #[rustc_inherit_overflow_checks] #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } let mut base = self; + let mut acc = 1; if intrinsics::is_val_statically_known(exp) { - // Unroll multiplications for small exponent values. - // This gives the optimizer a way to efficiently inline call sites - // for the most common use cases with constant exponents. - // Currently, LLVM is unable to unroll the loop below. - match exp { - 0 => return 1, - 1 => return base, - 2 => return base * base, - 3 => { - let squared = base * base; - return squared * base; - } - 4 => { - let squared = base * base; - return squared * squared; + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; } - 5 => { - let squared = base * base; - return squared * squared * base; - } - 6 => { - let cubed = base * base * base; - return cubed * cubed; - } - _ => {} + exp /= 2; + base = base * base; } - } else { - if exp == 0 { - return 1; - } - } - debug_assert!(exp != 0); - let mut acc = 1; - - loop { - if (exp & 1) == 1 { - acc = acc * base; - // since exp!=0, finally the exp must be 1. - if exp == 1 { - return acc; + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base + } else { + // This is faster than the above when the exponent is not known + // at compile time. We can't use the same code for the constant + // exponent case because LLVM is currently unable to unroll + // this loop. + loop { + if (exp & 1) == 1 { + acc = acc * base; + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } + exp /= 2; + base = base * base; } - exp /= 2; - base = base * base; } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 24352593fca3d..5b3ef78d39a3d 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2050,54 +2050,41 @@ macro_rules! uint_impl { #[inline] #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn wrapping_pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } let mut base = self; + let mut acc: Self = 1; if intrinsics::is_val_statically_known(exp) { - // Unroll multiplications for small exponent values. - // This gives the optimizer a way to efficiently inline call sites - // for the most common use cases with constant exponents. - // Currently, LLVM is unable to unroll the loop below. - match exp { - 0 => return 1, - 1 => return base, - 2 => return base.wrapping_mul(base), - 3 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(base); - } - 4 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(squared); + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); } - 5 => { - let squared = base.wrapping_mul(base); - return squared.wrapping_mul(squared).wrapping_mul(base); - } - 6 => { - let cubed = base.wrapping_mul(base).wrapping_mul(base); - return cubed.wrapping_mul(cubed); - } - _ => {} + exp /= 2; + base = base.wrapping_mul(base); } - } else { - if exp == 0 { - return 1; - } - } - debug_assert!(exp != 0); - let mut acc: Self = 1; - - loop { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - // since exp!=0, finally the exp must be 1. - if exp == 1 { - return acc; + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary. + acc.wrapping_mul(base) + } else { + // This is faster than the above when the exponent is not known + // at compile time. We can't use the same code for the constant + // exponent case because LLVM is currently unable to unroll + // this loop. + loop { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } + exp /= 2; + base = base.wrapping_mul(base); } - exp /= 2; - base = base.wrapping_mul(base); } } @@ -2578,54 +2565,42 @@ macro_rules! uint_impl { #[rustc_inherit_overflow_checks] #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } let mut base = self; + let mut acc = 1; if intrinsics::is_val_statically_known(exp) { - // Unroll multiplications for small exponent values. - // This gives the optimizer a way to efficiently inline call sites - // for the most common use cases with constant exponents. - // Currently, LLVM is unable to unroll the loop below. - match exp { - 0 => return 1, - 1 => return base, - 2 => return base * base, - 3 => { - let squared = base * base; - return squared * base; - } - 4 => { - let squared = base * base; - return squared * squared; + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; } - 5 => { - let squared = base * base; - return squared * squared * base; - } - 6 => { - let cubed = base * base * base; - return cubed * cubed; - } - _ => {} + exp /= 2; + base = base * base; } - } else { - if exp == 0 { - return 1; - } - } - debug_assert!(exp != 0); - let mut acc = 1; - - loop { - if (exp & 1) == 1 { - acc = acc * base; - // since exp!=0, finally the exp must be 1. - if exp == 1 { - return acc; + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base + } else { + // This is faster than the above when the exponent is not known + // at compile time. We can't use the same code for the constant + // exponent case because LLVM is currently unable to unroll + // this loop. + loop { + if (exp & 1) == 1 { + acc = acc * base; + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } + exp /= 2; + base = base * base; } - exp /= 2; - base = base * base; } } From 705a89e72fcd1e91246ae127e7277472752161b8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 13 Aug 2024 09:40:45 +0200 Subject: [PATCH 106/178] Remove unreachable logic for include token mapping --- .../rust-analyzer/crates/hir/src/semantics.rs | 91 +------------------ .../crates/ide/src/inlay_hints/bind_pat.rs | 15 +++ 2 files changed, 18 insertions(+), 88 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 4089e52f51205..a377163162c2c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -28,7 +28,7 @@ use hir_expand::{ use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; -use span::{EditionedFileId, FileId, Span, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; +use span::{EditionedFileId, FileId}; use stdx::TupleExt; use syntax::{ algo::skip_trivia_token, @@ -757,84 +757,9 @@ impl<'db> SemanticsImpl<'db> { res } - // return: - // SourceAnalyzer(file_id that original call include!) - // macro file id - // token in include! macro mapped from token in params - // span for the mapped token - fn is_from_include_file( - &self, - token: SyntaxToken, - ) -> Option<(SourceAnalyzer, HirFileId, SyntaxToken, Span)> { - let parent = token.parent()?; - let file_id = self.find_file(&parent).file_id.file_id()?; - - // iterate related crates and find all include! invocations that include_file_id matches - for iter in self - .db - .relevant_crates(file_id.file_id()) - .iter() - .map(|krate| self.db.include_macro_invoc(*krate)) - { - for (invoc, _) in - iter.iter().filter(|&&(_, include_file_id)| include_file_id == file_id) - { - let macro_file = invoc.as_macro_file(); - let expansion_info = { - self.with_ctx(|ctx| { - ctx.cache - .expansion_info_cache - .entry(macro_file) - .or_insert_with(|| { - let exp_info = macro_file.expansion_info(self.db.upcast()); - - let InMacroFile { file_id, value } = exp_info.expanded(); - if let InFile { file_id, value: Some(value) } = exp_info.arg() { - self.cache(value.ancestors().last().unwrap(), file_id); - } - self.cache(value, file_id.into()); - - exp_info - }) - .clone() - }) - }; - - // FIXME: uncached parse - // Create the source analyzer for the macro call scope - let Some(sa) = expansion_info - .arg() - .value - .and_then(|it| self.analyze_no_infer(&it.ancestors().last().unwrap())) - else { - continue; - }; - - // get mapped token in the include! macro file - let span = span::Span { - range: token.text_range(), - anchor: span::SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID }, - ctx: SyntaxContextId::ROOT, - }; - let Some(InMacroFile { file_id, value: mut mapped_tokens }) = - expansion_info.map_range_down_exact(span) - else { - continue; - }; - - // if we find one, then return - if let Some(t) = mapped_tokens.next() { - return Some((sa, file_id.into(), t, span)); - } - } - } - - None - } - fn descend_into_macros_impl( &self, - mut token: SyntaxToken, + token: SyntaxToken, f: &mut dyn FnMut(InFile) -> ControlFlow<()>, ) { let _p = tracing::info_span!("descend_into_macros_impl").entered(); @@ -851,17 +776,7 @@ impl<'db> SemanticsImpl<'db> { return; } }, - None => { - // if we cannot find a source analyzer for this token, then we try to find out - // whether this file is an included file and treat that as the include input - let Some((it, macro_file_id, mapped_token, s)) = - self.is_from_include_file(token) - else { - return; - }; - token = mapped_token; - (it, s, macro_file_id) - } + None => return, }; let mut m_cache = self.macro_call_cache.borrow_mut(); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 5775abaeb18c6..7310852b8ed57 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -1144,6 +1144,21 @@ async fn main() { //^^ impl Future return 8_i32; }; +}"#, + ); + } + + #[test] + fn works_in_included_file() { + check_types( + r#" +//- minicore: include +//- /main.rs +include!("foo.rs"); +//- /foo.rs +fn main() { + let _x = 42; + //^^ i32 }"#, ); } From 399ef23d2bf2b1619d360a87de9b83edf9d99762 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Wed, 17 Jul 2024 13:45:31 +0200 Subject: [PATCH 107/178] Allow to customize `// TODO:` comment for deprecated safe autofix Relevant for the deprecation of `CommandExt::before_exit` in #125970. --- compiler/rustc_feature/src/builtin_attrs.rs | 4 ++-- compiler/rustc_mir_build/messages.ftl | 2 +- .../rustc_mir_build/src/check_unsafety.rs | 22 +++++++++++++++++-- compiler/rustc_mir_build/src/errors.rs | 6 ++--- compiler/rustc_span/src/symbol.rs | 1 + library/std/src/env.rs | 16 ++++++++++++-- .../ui/rust-2024/unsafe-env-suggestion.stderr | 4 ++-- 7 files changed, 42 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 72ea55d5999a2..1b4c18e96fc74 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -643,8 +643,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ through unstable paths" ), rustc_attr!( - rustc_deprecated_safe_2024, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::Yes, + rustc_deprecated_safe_2024, Normal, template!(List: r#"todo = "...""#), + ErrorFollowing, EncodeCrossCrate::Yes, "rustc_deprecated_safe_2024 is supposed to be used in libstd only", ), diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index dda4debecec67..91c4de7963665 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -30,7 +30,7 @@ mir_build_call_to_deprecated_safe_fn_requires_unsafe = call to deprecated safe function `{$function}` is unsafe and requires unsafe block .note = consult the function's documentation for information on how to avoid undefined behavior .label = call to unsafe function - .suggestion = you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code + .suggestion = you can wrap the call in an `unsafe` block if you can guarantee its unsafe preconditions mir_build_call_to_fn_with_requires_unsafe = call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 54a4204da71e8..f856555a95c21 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -96,9 +96,27 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { // from an edition before 2024. &UnsafeOpKind::CallToUnsafeFunction(Some(id)) if !span.at_least_rust_2024() - && self.tcx.has_attr(id, sym::rustc_deprecated_safe_2024) => + && let Some(attr) = self.tcx.get_attr(id, sym::rustc_deprecated_safe_2024) => { + let suggestion = attr + .meta_item_list() + .unwrap_or_default() + .into_iter() + .find(|item| item.has_name(sym::todo)) + .map(|item| { + item.value_str().expect( + "`#[rustc_deprecated_safe_2024(todo)]` must have a string value", + ) + }); + let sm = self.tcx.sess.source_map(); + let suggestion = suggestion + .and_then(|suggestion| { + sm.indentation_before(span) + .map(|indent| format!("{}// TODO: {}\n", indent, suggestion)) // ignore-tidy-todo + }) + .unwrap_or_default(); + self.tcx.emit_node_span_lint( DEPRECATED_SAFE_2024, self.hir_context, @@ -107,7 +125,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { span, function: with_no_trimmed_paths!(self.tcx.def_path_str(id)), sub: CallToDeprecatedSafeFnRequiresUnsafeSub { - indent: sm.indentation_before(span).unwrap_or_default(), + start_of_line_suggestion: suggestion, start_of_line: sm.span_extend_to_line(span).shrink_to_lo(), left: span.shrink_to_lo(), right: span.shrink_to_hi(), diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 42eca71ca3f30..8c45f949e4350 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -35,10 +35,8 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe { #[derive(Subdiagnostic)] #[multipart_suggestion(mir_build_suggestion, applicability = "machine-applicable")] pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafeSub { - pub(crate) indent: String, - #[suggestion_part( - code = "{indent}// TODO: Audit that the environment access only happens in single-threaded code.\n" // ignore-tidy-todo - )] + pub(crate) start_of_line_suggestion: String, + #[suggestion_part(code = "{start_of_line_suggestion}")] pub(crate) start_of_line: Span, #[suggestion_part(code = "unsafe {{ ")] pub(crate) left: Span, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9cb729ec48588..95810a9a8379a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1897,6 +1897,7 @@ symbols! { to_string, to_string_method, to_vec, + todo, todo_macro, tool_attributes, tool_lints, diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 50ae83090c7e1..631d86dbe6e00 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -355,7 +355,13 @@ impl Error for VarError { /// } /// assert_eq!(env::var(key), Ok("VALUE".to_string())); /// ``` -#[rustc_deprecated_safe_2024] +#[cfg_attr(bootstrap, rustc_deprecated_safe_2024)] +#[cfg_attr( + not(bootstrap), + rustc_deprecated_safe_2024( + todo = "Audit that the environment access only happens in single-threaded code." + ) +)] #[stable(feature = "env", since = "1.0.0")] pub unsafe fn set_var, V: AsRef>(key: K, value: V) { let (key, value) = (key.as_ref(), value.as_ref()); @@ -419,7 +425,13 @@ pub unsafe fn set_var, V: AsRef>(key: K, value: V) { /// } /// assert!(env::var(key).is_err()); /// ``` -#[rustc_deprecated_safe_2024] +#[cfg_attr(bootstrap, rustc_deprecated_safe_2024)] +#[cfg_attr( + not(bootstrap), + rustc_deprecated_safe_2024( + todo = "Audit that the environment access only happens in single-threaded code." + ) +)] #[stable(feature = "env", since = "1.0.0")] pub unsafe fn remove_var>(key: K) { let key = key.as_ref(); diff --git a/tests/ui/rust-2024/unsafe-env-suggestion.stderr b/tests/ui/rust-2024/unsafe-env-suggestion.stderr index 3aa10a3bed682..5c90c08e2ddfb 100644 --- a/tests/ui/rust-2024/unsafe-env-suggestion.stderr +++ b/tests/ui/rust-2024/unsafe-env-suggestion.stderr @@ -11,7 +11,7 @@ note: the lint level is defined here | LL | #![deny(deprecated_safe_2024)] | ^^^^^^^^^^^^^^^^^^^^ -help: you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code +help: you can wrap the call in an `unsafe` block if you can guarantee its unsafe preconditions | LL + // TODO: Audit that the environment access only happens in single-threaded code. LL ~ unsafe { env::set_var("FOO", "BAR") }; @@ -25,7 +25,7 @@ LL | env::remove_var("FOO"); | = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! = note: for more information, see issue #27970 -help: you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code +help: you can wrap the call in an `unsafe` block if you can guarantee its unsafe preconditions | LL + // TODO: Audit that the environment access only happens in single-threaded code. LL ~ unsafe { env::remove_var("FOO") }; From 811d7dd11302e86d1678f7d61586d51e54c47e27 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Mon, 29 Jul 2024 13:31:59 +0200 Subject: [PATCH 108/178] `#[deprecated_safe_2024]`: Also use the `// TODO:` hint in the compiler error This doesn't work for translated compiler error messages. --- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- compiler/rustc_mir_build/messages.ftl | 2 +- compiler/rustc_mir_build/src/check_unsafety.rs | 14 ++++++++++---- compiler/rustc_mir_build/src/errors.rs | 1 + compiler/rustc_span/src/symbol.rs | 2 +- library/std/src/env.rs | 4 ++-- tests/ui/rust-2024/unsafe-env-suggestion.stderr | 4 ++-- 7 files changed, 18 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 1b4c18e96fc74..d593f05c8c6db 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -643,7 +643,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ through unstable paths" ), rustc_attr!( - rustc_deprecated_safe_2024, Normal, template!(List: r#"todo = "...""#), + rustc_deprecated_safe_2024, Normal, template!(List: r#"audit_that = "...""#), ErrorFollowing, EncodeCrossCrate::Yes, "rustc_deprecated_safe_2024 is supposed to be used in libstd only", ), diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 91c4de7963665..7baf0256dd890 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -30,7 +30,7 @@ mir_build_call_to_deprecated_safe_fn_requires_unsafe = call to deprecated safe function `{$function}` is unsafe and requires unsafe block .note = consult the function's documentation for information on how to avoid undefined behavior .label = call to unsafe function - .suggestion = you can wrap the call in an `unsafe` block if you can guarantee its unsafe preconditions + .suggestion = you can wrap the call in an `unsafe` block if you can guarantee {$guarantee} mir_build_call_to_fn_with_requires_unsafe = call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index f856555a95c21..9b85ad0ad0891 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -102,18 +102,23 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { .meta_item_list() .unwrap_or_default() .into_iter() - .find(|item| item.has_name(sym::todo)) + .find(|item| item.has_name(sym::audit_that)) .map(|item| { item.value_str().expect( - "`#[rustc_deprecated_safe_2024(todo)]` must have a string value", + "`#[rustc_deprecated_safe_2024(audit_that)]` must have a string value", ) }); let sm = self.tcx.sess.source_map(); + let guarantee = suggestion + .as_ref() + .map(|suggestion| format!("that {}", suggestion)) + .unwrap_or_else(|| String::from("its unsafe preconditions")); let suggestion = suggestion .and_then(|suggestion| { - sm.indentation_before(span) - .map(|indent| format!("{}// TODO: {}\n", indent, suggestion)) // ignore-tidy-todo + sm.indentation_before(span).map(|indent| { + format!("{}// TODO: Audit that {}.\n", indent, suggestion) // ignore-tidy-todo + }) }) .unwrap_or_default(); @@ -124,6 +129,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { CallToDeprecatedSafeFnRequiresUnsafe { span, function: with_no_trimmed_paths!(self.tcx.def_path_str(id)), + guarantee, sub: CallToDeprecatedSafeFnRequiresUnsafeSub { start_of_line_suggestion: suggestion, start_of_line: sm.span_extend_to_line(span).shrink_to_lo(), diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 8c45f949e4350..34577f102d1c5 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -28,6 +28,7 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe { #[label] pub(crate) span: Span, pub(crate) function: String, + pub(crate) guarantee: String, #[subdiagnostic] pub(crate) sub: CallToDeprecatedSafeFnRequiresUnsafeSub, } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 95810a9a8379a..a2e94492f8c23 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -472,6 +472,7 @@ symbols! { attr, attr_literals, attributes, + audit_that, augmented_assignments, auto_traits, automatically_derived, @@ -1897,7 +1898,6 @@ symbols! { to_string, to_string_method, to_vec, - todo, todo_macro, tool_attributes, tool_lints, diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 631d86dbe6e00..80890e61471c6 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -359,7 +359,7 @@ impl Error for VarError { #[cfg_attr( not(bootstrap), rustc_deprecated_safe_2024( - todo = "Audit that the environment access only happens in single-threaded code." + audit_that = "the environment access only happens in single-threaded code" ) )] #[stable(feature = "env", since = "1.0.0")] @@ -429,7 +429,7 @@ pub unsafe fn set_var, V: AsRef>(key: K, value: V) { #[cfg_attr( not(bootstrap), rustc_deprecated_safe_2024( - todo = "Audit that the environment access only happens in single-threaded code." + audit_that = "the environment access only happens in single-threaded code" ) )] #[stable(feature = "env", since = "1.0.0")] diff --git a/tests/ui/rust-2024/unsafe-env-suggestion.stderr b/tests/ui/rust-2024/unsafe-env-suggestion.stderr index 5c90c08e2ddfb..1506741f6bc9b 100644 --- a/tests/ui/rust-2024/unsafe-env-suggestion.stderr +++ b/tests/ui/rust-2024/unsafe-env-suggestion.stderr @@ -11,7 +11,7 @@ note: the lint level is defined here | LL | #![deny(deprecated_safe_2024)] | ^^^^^^^^^^^^^^^^^^^^ -help: you can wrap the call in an `unsafe` block if you can guarantee its unsafe preconditions +help: you can wrap the call in an `unsafe` block if you can guarantee that the environment access only happens in single-threaded code | LL + // TODO: Audit that the environment access only happens in single-threaded code. LL ~ unsafe { env::set_var("FOO", "BAR") }; @@ -25,7 +25,7 @@ LL | env::remove_var("FOO"); | = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! = note: for more information, see issue #27970 -help: you can wrap the call in an `unsafe` block if you can guarantee its unsafe preconditions +help: you can wrap the call in an `unsafe` block if you can guarantee that the environment access only happens in single-threaded code | LL + // TODO: Audit that the environment access only happens in single-threaded code. LL ~ unsafe { env::remove_var("FOO") }; From 2a000c8d7073ad1b857c8028a5bbaa21eb12ced5 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 13 Aug 2024 20:23:48 +1000 Subject: [PATCH 109/178] Don't panic on unknown JSON-like output lines This function is called for both compiler and non-compiler output, so if the line isn't recognized as JSON from the compiler then just print it as-is. --- src/tools/compiletest/src/json.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 76b83f02b149a..70c05925b50fc 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -127,11 +127,10 @@ pub fn extract_rendered(output: &str) -> String { // Ignore the notification. None } else { - print!( - "failed to decode compiler output as json: line: {}\noutput: {}", - line, output - ); - panic!() + // This function is called for both compiler and non-compiler output, + // so if the line isn't recognized as JSON from the compiler then + // just print it as-is. + Some(format!("{line}\n")) } } else { // preserve non-JSON lines, such as ICEs From 355f264d32cf449e00adeeaccfb17a1e2f3fdd18 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 13 Aug 2024 20:24:53 +1000 Subject: [PATCH 110/178] Remove a confusing comment The JSON messages parsed by this file are from the _compiler_, not from libtest. --- src/tools/compiletest/src/json.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 70c05925b50fc..0da93dcafa20b 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -1,5 +1,4 @@ //! These structs are a subset of the ones found in `rustc_errors::json`. -//! They are only used for deserialization of JSON output provided by libtest. use std::path::{Path, PathBuf}; use std::str::FromStr; From 53e87d211ca314c5e3e664ef254bd20d134c9a00 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 13 Aug 2024 15:03:18 +0200 Subject: [PATCH 111/178] Remove duplicated rustdoc ui test --- .../generate-link-to-definition-opt2.rs | 6 ------ .../generate-link-to-definition-opt2.stderr | 2 -- 2 files changed, 8 deletions(-) delete mode 100644 tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt2.rs delete mode 100644 tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt2.stderr diff --git a/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt2.rs b/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt2.rs deleted file mode 100644 index 718522059799e..0000000000000 --- a/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt2.rs +++ /dev/null @@ -1,6 +0,0 @@ -// This test purpose is to check that the "--generate-link-to-definition" -// option can only be used with HTML generation. - -//@ compile-flags: -Zunstable-options --generate-link-to-definition --show-coverage - -pub fn f() {} diff --git a/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt2.stderr b/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt2.stderr deleted file mode 100644 index 4c8c607e7da23..0000000000000 --- a/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt2.stderr +++ /dev/null @@ -1,2 +0,0 @@ -error: --generate-link-to-definition option can only be used with HTML output format - From d2177d90b04e7af7d52279fe4664002f1e2a9c58 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 13 Aug 2024 15:07:26 +0200 Subject: [PATCH 112/178] Emit a warning instead of an error if `--generate-link-to-definition` is used with other output formats than HTML --- src/librustdoc/config.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 9c7a9f8467f5a..d599fead2668a 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -733,9 +733,11 @@ impl Options { let html_no_source = matches.opt_present("html-no-source"); if generate_link_to_definition && (show_coverage || output_format != OutputFormat::Html) { - dcx.fatal( - "--generate-link-to-definition option can only be used with HTML output format", - ); + dcx.struct_warn( + "`--generate-link-to-definition` option can only be used with HTML output format", + ) + .with_note("`--generate-link-to-definition` option will be ignored") + .emit(); } let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx); From afbab80681de5f5eb205d1ace743225baf27a9f0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 13 Aug 2024 15:08:07 +0200 Subject: [PATCH 113/178] Update rustdoc-ui test for `--generate-link-to-definition` option --- .../generate-link-to-definition-opt.rs | 1 + .../generate-link-to-definition-opt.stderr | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt.rs b/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt.rs index f11b94bb036c6..babdbd0a69216 100644 --- a/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt.rs +++ b/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt.rs @@ -2,5 +2,6 @@ // option can only be used with HTML generation. //@ compile-flags: -Zunstable-options --generate-link-to-definition --output-format json +//@ check-pass pub fn f() {} diff --git a/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt.stderr b/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt.stderr index 4c8c607e7da23..62b0e3ce408b7 100644 --- a/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt.stderr +++ b/tests/rustdoc-ui/generate-link-to-definition/generate-link-to-definition-opt.stderr @@ -1,2 +1,4 @@ -error: --generate-link-to-definition option can only be used with HTML output format +warning: `--generate-link-to-definition` option can only be used with HTML output format + | + = note: `--generate-link-to-definition` option will be ignored From 20971602d538fd3a7936171708fba36349b6fe22 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 11 Aug 2024 22:48:37 +0900 Subject: [PATCH 114/178] feat: `min-exhaustive-patterns --- .../crates/hir-ty/src/diagnostics/expr.rs | 65 ++++++++++++++++--- .../diagnostics/match_check/pat_analysis.rs | 11 ++-- .../src/handlers/missing_match_arms.rs | 38 +++++++++++ .../src/handlers/non_exhaustive_let.rs | 32 +++++++++ 4 files changed, 131 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 06c9b2e0e5b35..6e5a7cce9c976 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -4,20 +4,25 @@ use std::fmt; +use chalk_solve::rust_ir::AdtKind; use either::Either; -use hir_def::lang_item::LangItem; -use hir_def::{resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule}; -use hir_def::{ItemContainerId, Lookup}; +use hir_def::{ + lang_item::LangItem, + resolver::{HasResolver, ValueNs}, + AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup, +}; use intern::sym; use itertools::Itertools; use rustc_hash::FxHashSet; use rustc_pattern_analysis::constructor::Constructor; -use syntax::{ast, AstNode}; +use syntax::{ + ast::{self, UnaryOp}, + AstNode, +}; use tracing::debug; use triomphe::Arc; use typed_arena::Arena; -use crate::Interner; use crate::{ db::HirDatabase, diagnostics::match_check::{ @@ -25,7 +30,7 @@ use crate::{ pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat}, }, display::HirDisplay, - InferenceResult, Ty, TyExt, + Adjust, InferenceResult, Interner, Ty, TyExt, TyKind, }; pub(crate) use hir_def::{ @@ -236,7 +241,12 @@ impl ExprValidator { return; } - let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) { + let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr, db)); + let report = match cx.compute_match_usefulness( + m_arms.as_slice(), + scrut_ty.clone(), + known_valid_scrutinee, + ) { Ok(report) => report, Err(()) => return, }; @@ -253,6 +263,45 @@ impl ExprValidator { } } + // [rustc's `is_known_valid_scrutinee`](https://github.com/rust-lang/rust/blob/c9bd03cb724e13cca96ad320733046cbdb16fbbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L288) + // + // While the above function in rustc uses thir exprs, r-a doesn't have them. + // So, the logic here is getting same result as "hir lowering + match with lowered thir" + // with "hir only" + fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId, db: &dyn HirDatabase) -> bool { + if self + .infer + .expr_adjustments + .get(&scrutinee_expr) + .is_some_and(|adjusts| adjusts.iter().any(|a| matches!(a.kind, Adjust::Deref(..)))) + { + return false; + } + + match &self.body[scrutinee_expr] { + Expr::UnaryOp { op: UnaryOp::Deref, .. } => false, + Expr::Path(path) => { + let value_or_partial = self + .owner + .resolver(db.upcast()) + .resolve_path_in_value_ns_fully(db.upcast(), path); + value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_))) + } + Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) { + TyKind::Adt(adt, ..) + if db.adt_datum(self.owner.krate(db.upcast()), *adt).kind == AdtKind::Union => + { + false + } + _ => self.is_known_valid_scrutinee(*expr, db), + }, + Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db), + Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr, db), + Expr::Missing => false, + _ => true, + } + } + fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) { let (Expr::Block { statements, .. } | Expr::Async { statements, .. } @@ -285,7 +334,7 @@ impl ExprValidator { has_guard: false, arm_data: (), }; - let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) { + let report = match cx.compute_match_usefulness(&[match_arm], ty.clone(), None) { Ok(v) => v, Err(e) => { debug!(?e, "match usefulness error"); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index a12e201cf3d97..b57e290b4823b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -69,22 +69,20 @@ pub(crate) struct MatchCheckCtx<'db> { body: DefWithBodyId, pub(crate) db: &'db dyn HirDatabase, exhaustive_patterns: bool, - min_exhaustive_patterns: bool, } impl<'db> MatchCheckCtx<'db> { pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self { let def_map = db.crate_def_map(module.krate()); let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); - let min_exhaustive_patterns = - def_map.is_unstable_feature_enabled(&sym::min_exhaustive_patterns); - Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns } + Self { module, body, db, exhaustive_patterns } } pub(crate) fn compute_match_usefulness( &self, arms: &[MatchArm<'db>], scrut_ty: Ty, + known_valid_scrutinee: Option, ) -> Result, ()> { if scrut_ty.contains_unknown() { return Err(()); @@ -95,8 +93,7 @@ impl<'db> MatchCheckCtx<'db> { } } - // FIXME: Determine place validity correctly. For now, err on the safe side. - let place_validity = PlaceValidity::MaybeInvalid; + let place_validity = PlaceValidity::from_bool(known_valid_scrutinee.unwrap_or(true)); // Measured to take ~100ms on modern hardware. let complexity_limit = Some(500000); compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit) @@ -328,7 +325,7 @@ impl<'db> PatCx for MatchCheckCtx<'db> { self.exhaustive_patterns } fn is_min_exhaustive_patterns_feature_on(&self) -> bool { - self.min_exhaustive_patterns + true } fn ctor_arity( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 97296278c3a3a..f39738f2fb80c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -1032,6 +1032,44 @@ fn f() { check_diagnostics_no_bails(&code); } + #[test] + fn min_exhaustive() { + check_diagnostics( + r#" +//- minicore: result +fn test(x: Result) { + match x { + Ok(_y) => {} + } +} +"#, + ); + check_diagnostics( + r#" +//- minicore: result +fn test(ptr: *const Result) { + unsafe { + match *ptr { + //^^^^ error: missing match arm: `Err(!)` not covered + Ok(_x) => {} + } + } +} +"#, + ); + check_diagnostics( + r#" +//- minicore: result +fn test(x: Result) { + match x { + //^ error: missing match arm: `Err(_)` not covered + Ok(_y) => {} + } +} +"#, + ); + } + mod rust_unstable { use super::*; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index 5a6977c2553bc..9aace992ae14f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -80,6 +80,38 @@ fn main() { //^^^^ error: non-exhaustive pattern: `Some(_)` not covered } } +"# + ); + } + + #[test] + fn min_exhaustive() { + check_diagnostics( + r#" +//- minicore: result +fn test(x: Result) { + let Ok(_y) = x; +} +"#, + ); + check_diagnostics( + r#" +//- minicore: result +fn test(ptr: *const Result) { + unsafe { + let Ok(_x) = *ptr; + //^^^^^^ error: non-exhaustive pattern: `Err(_)` not covered + } +} +"#, + ); + check_diagnostics( + r#" +//- minicore: result +fn test(x: Result) { + let Ok(_y) = x; + //^^^^^^ error: non-exhaustive pattern: `Err(_)` not covered +} "#, ); } From 6911d9f0664a67975c3a435a3c76d3c0a1b7118a Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 13 Aug 2024 00:25:19 +0900 Subject: [PATCH 115/178] Temporarily remove non-working test case --- .../src/handlers/non_exhaustive_let.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index 9aace992ae14f..ff1eeb0516a9e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -80,7 +80,7 @@ fn main() { //^^^^ error: non-exhaustive pattern: `Some(_)` not covered } } -"# +"#, ); } @@ -94,17 +94,7 @@ fn test(x: Result) { } "#, ); - check_diagnostics( - r#" -//- minicore: result -fn test(ptr: *const Result) { - unsafe { - let Ok(_x) = *ptr; - //^^^^^^ error: non-exhaustive pattern: `Err(_)` not covered - } -} -"#, - ); + check_diagnostics( r#" //- minicore: result From 4ea0db907cf3069750b578a1086f61b1241abb0f Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 13 Aug 2024 00:39:51 +0900 Subject: [PATCH 116/178] Bump `rustc_pattern_analysis` --- src/tools/rust-analyzer/Cargo.lock | 25 +++++++++---------- src/tools/rust-analyzer/Cargo.toml | 21 ++++++++-------- .../diagnostics/match_check/pat_analysis.rs | 16 ++++++------ 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 0fb476de6a3de..41dc440509934 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1474,9 +1474,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.53.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46" +checksum = "b011c39d409940a890414e3a7b239762ac16d88029ad71b050a8374831b93790" dependencies = [ "bitflags 2.6.0", "ra-ap-rustc_index", @@ -1485,9 +1485,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.53.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072060ac77e9e1a02cc20028095993af7e72cc0804779c68bcbf47b16de49c9" +checksum = "9027acdee649b0b27eb10b7db5be833efee3362d394935c5eed8f0745a9d43ce" dependencies = [ "arrayvec", "ra-ap-rustc_index_macros", @@ -1496,21 +1496,20 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.53.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82f3d6dcb30a66905388e14756b8f2216131d9f8004922c07f13335840e058d1" +checksum = "540b86dc0384141ac8e825fc2874cd44bffd4277d99d8ec63ee416f1a98d5997" dependencies = [ "proc-macro2", "quote", "syn", - "synstructure", ] [[package]] name = "ra-ap-rustc_lexer" -version = "0.53.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd8a2b0bdcba9892cbce0b25f6c953d31b0febc1f3420fc692884fce5a23ad8" +checksum = "3bdf98bb457b47b9ae4aeebf867d0ca440c86925e0b6381658c4a02589748c9d" dependencies = [ "unicode-properties", "unicode-xid", @@ -1518,9 +1517,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.53.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dad7a491c2554590222e0c9212dcb7c2e7aceb668875075012a35ea780d135" +checksum = "e8fe3556ab6311bb775220563a300e2bf62ec56404521fe0c511a583937683d5" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1528,9 +1527,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.53.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34768e1faf88c31f2e9ad57b48318a52b507dafac0cddbf01b5d63bfc0b0a365" +checksum = "1709080fdeb5db630e1c2644026c2962aaa32416cd92f0190c04b0c21e114b91" dependencies = [ "ra-ap-rustc_index", "rustc-hash", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 561c533a98d99..56e80e11e774f 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.53.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.53.0", default-features = false } -ra-ap-rustc_index = { version = "0.53.0", default-features = false } -ra-ap-rustc_abi = { version = "0.53.0", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.53.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.63.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.63.0", default-features = false } +ra-ap-rustc_index = { version = "0.63.0", default-features = false } +ra-ap-rustc_abi = { version = "0.63.0", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.63.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. test-fixture = { path = "./crates/test-fixture" } @@ -125,11 +125,11 @@ memmap2 = "0.5.4" nohash-hasher = "0.2.0" oorandom = "11.1.3" object = { version = "0.33.0", default-features = false, features = [ - "std", - "read_core", - "elf", - "macho", - "pe", + "std", + "read_core", + "elf", + "macho", + "pe", ] } process-wrap = { version = "8.0.2", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" @@ -159,7 +159,6 @@ url = "2.3.1" xshell = "0.2.5" - # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=5.5.3", features = ["raw-api"] } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index b57e290b4823b..7b3abf501d293 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -304,7 +304,8 @@ impl<'db> MatchCheckCtx<'db> { &Str(void) => match void {}, Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild, Never => PatKind::Never, - Missing | F32Range(..) | F64Range(..) | Opaque(..) | Or => { + Missing | F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) + | Or => { never!("can't convert to pattern: {:?}", pat.ctor()); PatKind::Wild } @@ -324,9 +325,6 @@ impl<'db> PatCx for MatchCheckCtx<'db> { fn is_exhaustive_patterns_feature_on(&self) -> bool { self.exhaustive_patterns } - fn is_min_exhaustive_patterns_feature_on(&self) -> bool { - true - } fn ctor_arity( &self, @@ -353,8 +351,9 @@ impl<'db> PatCx for MatchCheckCtx<'db> { }, Ref => 1, Slice(..) => unimplemented!(), - Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) - | Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => 0, + Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) + | F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited + | Hidden | Missing | Wildcard => 0, Or => { never!("The `Or` constructor doesn't have a fixed arity"); 0 @@ -416,8 +415,9 @@ impl<'db> PatCx for MatchCheckCtx<'db> { } }, Slice(_) => unreachable!("Found a `Slice` constructor in match checking"), - Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) - | Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => { + Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) + | F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited + | Hidden | Missing | Wildcard => { smallvec![] } Or => { From ddb8551e03a1310a841da05b0418b49fd6287482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 13 Aug 2024 17:56:37 +0300 Subject: [PATCH 117/178] Preparing for merge from rust-lang/rust --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 001b900b207e3..d4f1703d850c5 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -1b51d80027919563004918eaadfa0d890ac0eb93 +80eb5a8e910e5185d47cdefe3732d839c78a5e7e From 0aa17a4c4de2daaf44e38e40f3ac8c2b4275d6bd Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 24 Jul 2024 17:07:22 +0200 Subject: [PATCH 118/178] implement a performant and fuzzed solver cache --- .../src/solve/search_graph.rs | 52 +- .../src/search_graph/global_cache.rs | 72 +- .../rustc_type_ir/src/search_graph/mod.rs | 913 +++++++++++++----- .../src/search_graph/validate.rs | 63 -- 4 files changed, 741 insertions(+), 359 deletions(-) delete mode 100644 compiler/rustc_type_ir/src/search_graph/validate.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 0994d0e3b3d88..81c89fad8e8a0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -2,12 +2,12 @@ use std::convert::Infallible; use std::marker::PhantomData; use rustc_type_ir::inherent::*; -use rustc_type_ir::search_graph::{self, CycleKind, UsageKind}; +use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{CanonicalInput, Certainty, QueryResult}; use rustc_type_ir::Interner; use super::inspect::ProofTreeBuilder; -use super::FIXPOINT_STEP_LIMIT; +use super::{has_no_inference_or_external_constraints, FIXPOINT_STEP_LIMIT}; use crate::delegate::SolverDelegate; /// This type is never constructed. We only use it to implement `search_graph::Delegate` @@ -23,10 +23,11 @@ where { type Cx = D::Interner; + const ENABLE_PROVISIONAL_CACHE: bool = true; type ValidationScope = Infallible; fn enter_validation_scope( _cx: Self::Cx, - _input: ::Input, + _input: CanonicalInput, ) -> Option { None } @@ -38,39 +39,32 @@ where inspect.is_noop() } + const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize = 4; fn recursion_limit(cx: I) -> usize { cx.recursion_limit() } fn initial_provisional_result( cx: I, - kind: CycleKind, + kind: PathKind, input: CanonicalInput, ) -> QueryResult { match kind { - CycleKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes), - CycleKind::Inductive => response_no_constraints(cx, input, Certainty::overflow(false)), + PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes), + PathKind::Inductive => response_no_constraints(cx, input, Certainty::overflow(false)), } } - fn reached_fixpoint( - cx: I, - kind: UsageKind, + fn is_initial_provisional_result( + cx: Self::Cx, + kind: PathKind, input: CanonicalInput, - provisional_result: Option>, result: QueryResult, ) -> bool { - if let Some(r) = provisional_result { - r == result - } else { - match kind { - UsageKind::Single(CycleKind::Coinductive) => { - response_no_constraints(cx, input, Certainty::Yes) == result - } - UsageKind::Single(CycleKind::Inductive) => { - response_no_constraints(cx, input, Certainty::overflow(false)) == result - } - UsageKind::Mixed => false, + match kind { + PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes) == result, + PathKind::Inductive => { + response_no_constraints(cx, input, Certainty::overflow(false)) == result } } } @@ -88,6 +82,22 @@ where response_no_constraints(cx, input, Certainty::overflow(false)) } + fn is_ambiguous_result(result: QueryResult) -> bool { + result.is_ok_and(|response| { + has_no_inference_or_external_constraints(response) + && matches!(response.value.certainty, Certainty::Maybe(_)) + }) + } + + fn propagate_ambiguity( + cx: I, + for_input: CanonicalInput, + from_result: QueryResult, + ) -> QueryResult { + let certainty = from_result.unwrap().value.certainty; + response_no_constraints(cx, for_input, certainty) + } + fn step_is_coinductive(cx: I, input: CanonicalInput) -> bool { input.value.goal.predicate.is_coinductive(cx) } diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index d63a8d16bea73..47f7cefac6ad1 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -1,12 +1,17 @@ use derive_where::derive_where; -use rustc_index::IndexVec; -use super::{AvailableDepth, Cx, StackDepth, StackEntry}; -use crate::data_structures::{HashMap, HashSet}; +use super::{AvailableDepth, Cx, NestedGoals}; +use crate::data_structures::HashMap; struct Success { - result: X::Tracked, additional_depth: usize, + nested_goals: NestedGoals, + result: X::Tracked, +} + +struct WithOverflow { + nested_goals: NestedGoals, + result: X::Tracked, } /// The cache entry for a given input. @@ -17,12 +22,7 @@ struct Success { #[derive_where(Default; X: Cx)] struct CacheEntry { success: Option>, - /// We have to be careful when caching roots of cycles. - /// - /// See the doc comment of `StackEntry::cycle_participants` for more - /// details. - nested_goals: HashSet, - with_overflow: HashMap>, + with_overflow: HashMap>, } #[derive_where(Debug; X: Cx)] @@ -30,10 +30,7 @@ pub(super) struct CacheData<'a, X: Cx> { pub(super) result: X::Result, pub(super) additional_depth: usize, pub(super) encountered_overflow: bool, - // FIXME: This is currently unused, but impacts the design - // by requiring a closure for `Cx::with_global_cache`. - #[allow(dead_code)] - pub(super) nested_goals: &'a HashSet, + pub(super) nested_goals: &'a NestedGoals, } #[derive_where(Default; X: Cx)] pub struct GlobalCache { @@ -52,15 +49,17 @@ impl GlobalCache { additional_depth: usize, encountered_overflow: bool, - nested_goals: &HashSet, + nested_goals: NestedGoals, ) { let result = cx.mk_tracked(result, dep_node); let entry = self.map.entry(input).or_default(); - entry.nested_goals.extend(nested_goals); if encountered_overflow { - entry.with_overflow.insert(additional_depth, result); + let with_overflow = WithOverflow { nested_goals, result }; + let prev = entry.with_overflow.insert(additional_depth, with_overflow); + assert!(prev.is_none()); } else { - entry.success = Some(Success { result, additional_depth }); + let prev = entry.success.replace(Success { additional_depth, nested_goals, result }); + assert!(prev.is_none()); } } @@ -72,30 +71,37 @@ impl GlobalCache { &'a self, cx: X, input: X::Input, - stack: &IndexVec>, available_depth: AvailableDepth, + mut candidate_is_applicable: impl FnMut(&NestedGoals) -> bool, ) -> Option> { let entry = self.map.get(&input)?; - if stack.iter().any(|e| entry.nested_goals.contains(&e.input)) { - return None; + if let Some(Success { additional_depth, ref nested_goals, ref result }) = entry.success { + if available_depth.cache_entry_is_applicable(additional_depth) + && candidate_is_applicable(nested_goals) + { + return Some(CacheData { + result: cx.get_tracked(&result), + additional_depth, + encountered_overflow: false, + nested_goals, + }); + } } - if let Some(ref success) = entry.success { - if available_depth.cache_entry_is_applicable(success.additional_depth) { + let additional_depth = available_depth.0; + if let Some(WithOverflow { nested_goals, result }) = + entry.with_overflow.get(&additional_depth) + { + if candidate_is_applicable(nested_goals) { return Some(CacheData { - result: cx.get_tracked(&success.result), - additional_depth: success.additional_depth, - encountered_overflow: false, - nested_goals: &entry.nested_goals, + result: cx.get_tracked(result), + additional_depth, + encountered_overflow: true, + nested_goals, }); } } - entry.with_overflow.get(&available_depth.0).map(|e| CacheData { - result: cx.get_tracked(e), - additional_depth: available_depth.0, - encountered_overflow: true, - nested_goals: &entry.nested_goals, - }) + None } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index a1aa63c2e7d75..d47c9e725f350 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -1,19 +1,32 @@ +/// The search graph is responsible for caching and cycle detection in the trait +/// solver. Making sure that caching doesn't result in soundness bugs or unstable +/// query results is very challenging and makes this one of the most-involved +/// self-contained components of the compiler. +/// +/// We added fuzzing support to test its correctness. The fuzzers used to verify +/// the current implementation can be found in https://github.com/lcnr/search_graph_fuzz. +/// +/// This is just a quick overview of the general design, please check out the relevant +/// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for +/// more details. Caching is split between a global cache and the per-cycle `provisional_cache`. +/// The global cache has to be completely unobservable, while the per-cycle cache may impact +/// behavior as long as the resulting behavior is still correct. +use std::cmp::Ordering; +use std::collections::BTreeSet; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; -use std::mem; use derive_where::derive_where; use rustc_index::{Idx, IndexVec}; use tracing::debug; -use crate::data_structures::{HashMap, HashSet}; +use crate::data_structures::HashMap; use crate::solve::SolverMode; mod global_cache; use global_cache::CacheData; pub use global_cache::GlobalCache; -mod validate; /// The search graph does not simply use `Interner` directly /// to enable its fuzzing without having to stub the rest of @@ -44,6 +57,9 @@ pub trait Cx: Copy { pub trait Delegate { type Cx: Cx; + /// Whether to use the provisional cache. Set to `false` by a fuzzer when + /// validating the search graph. + const ENABLE_PROVISIONAL_CACHE: bool; type ValidationScope; /// Returning `Some` disables the global cache for the current goal. /// @@ -62,18 +78,18 @@ pub trait Delegate { type ProofTreeBuilder; fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool; + const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize; fn recursion_limit(cx: Self::Cx) -> usize; fn initial_provisional_result( cx: Self::Cx, - kind: CycleKind, + kind: PathKind, input: ::Input, ) -> ::Result; - fn reached_fixpoint( + fn is_initial_provisional_result( cx: Self::Cx, - kind: UsageKind, + kind: PathKind, input: ::Input, - provisional_result: Option<::Result>, result: ::Result, ) -> bool; fn on_stack_overflow( @@ -86,6 +102,13 @@ pub trait Delegate { input: ::Input, ) -> ::Result; + fn is_ambiguous_result(result: ::Result) -> bool; + fn propagate_ambiguity( + cx: Self::Cx, + for_input: ::Input, + from_result: ::Result, + ) -> ::Result; + fn step_is_coinductive(cx: Self::Cx, input: ::Input) -> bool; } @@ -93,14 +116,14 @@ pub trait Delegate { /// result. In the case we return an initial provisional result depending /// on the kind of cycle. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CycleKind { +pub enum PathKind { Coinductive, Inductive, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UsageKind { - Single(CycleKind), + Single(PathKind), Mixed, } impl UsageKind { @@ -116,11 +139,9 @@ impl UsageKind { } } } -} - -enum StepResult { - Done(StackEntry, X::Result), - HasChanged, + fn and_merge(&mut self, other: Self) { + *self = self.merge(other); + } } #[derive(Debug, Clone, Copy)] @@ -142,7 +163,7 @@ impl AvailableDepth { } Some(if last.encountered_overflow { - AvailableDepth(last.available_depth.0 / 2) + AvailableDepth(last.available_depth.0 / D::DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW) } else { AvailableDepth(last.available_depth.0 - 1) }) @@ -158,94 +179,181 @@ impl AvailableDepth { } } +/// All cycle heads a given goal depends on, ordered by their stack depth. +/// +/// We therefore pop the cycle heads from highest to lowest. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +struct CycleHeads { + heads: BTreeSet, +} + +impl CycleHeads { + fn is_empty(&self) -> bool { + self.heads.is_empty() + } + + fn highest_cycle_head(&self) -> StackDepth { + *self.heads.last().unwrap() + } + + fn opt_highest_cycle_head(&self) -> Option { + self.heads.last().copied() + } + + fn opt_lowest_cycle_head(&self) -> Option { + self.heads.first().copied() + } + + fn remove_highest_cycle_head(&mut self) { + let last = self.heads.pop_last(); + debug_assert_ne!(last, None); + } + + fn insert(&mut self, head: StackDepth) { + self.heads.insert(head); + } + + fn merge(&mut self, heads: &CycleHeads) { + for &head in heads.heads.iter() { + self.insert(head); + } + } + + /// Update the cycle heads of a goal at depth `this` given the cycle heads + /// of a nested goal. This merges the heads after filtering the parent goal + /// itself. + fn extend_from_child(&mut self, this: StackDepth, child: &CycleHeads) { + for &head in child.heads.iter() { + match head.cmp(&this) { + Ordering::Less => {} + Ordering::Equal => continue, + Ordering::Greater => unreachable!(), + } + + self.insert(head); + } + } +} + +/// The nested goals of each stack entry and the path from the +/// stack entry to that nested goal. +/// +/// We only start tracking nested goals once we've either encountered +/// overflow or a solver cycle. This is a performance optimization to +/// avoid tracking nested goals on the happy path. +/// +/// We use nested goals for two reasons: +/// - when rebasing provisional cache entries +/// - when checking whether we have to ignore a global cache entry as reevaluating +/// it would encounter a cycle or use a provisional cache entry. +/// +/// We need to disable the global cache if using it would hide a cycle, as +/// cycles can impact behavior. The cycle ABA may have different final +/// results from a the cycle BAB depending on the cycle root. +#[derive_where(Debug, Default; X: Cx)] +struct NestedGoals { + nested_goals: HashMap, +} +impl NestedGoals { + fn is_empty(&self) -> bool { + self.nested_goals.is_empty() + } + + fn insert(&mut self, input: X::Input, path_from_entry: UsageKind) { + self.nested_goals.entry(input).or_insert(path_from_entry).and_merge(path_from_entry); + } + + fn merge(&mut self, nested_goals: &NestedGoals) { + #[allow(rustc::potential_query_instability)] + for (input, path_from_entry) in nested_goals.iter() { + self.insert(input, path_from_entry); + } + } + + /// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal + /// to the parent goal. + /// + /// If the path from this goal to the nested goal is inductive, the paths from this goal + /// to all nested goals of that nested goal are also inductive. Otherwise the paths are + /// the same as for the child. + fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals) { + #[allow(rustc::potential_query_instability)] + for (input, path_from_entry) in nested_goals.iter() { + let path_from_entry = match step_kind { + PathKind::Coinductive => path_from_entry, + PathKind::Inductive => UsageKind::Single(PathKind::Inductive), + }; + self.insert(input, path_from_entry); + } + } + + #[rustc_lint_query_instability] + #[allow(rustc::potential_query_instability)] + fn iter(&self) -> impl Iterator + '_ { + self.nested_goals.iter().map(|(i, p)| (*i, *p)) + } + + fn get(&self, input: X::Input) -> Option { + self.nested_goals.get(&input).copied() + } + + fn contains(&self, input: X::Input) -> bool { + self.nested_goals.contains_key(&input) + } +} + rustc_index::newtype_index! { #[orderable] #[gate_rustc_only] pub struct StackDepth {} } +/// Stack entries of the evaluation stack. Its fields tend to be lazily +/// when popping a child goal or completely immutable. #[derive_where(Debug; X: Cx)] struct StackEntry { input: X::Input, + /// The available depth of a given goal, immutable. available_depth: AvailableDepth, /// The maximum depth reached by this stack entry, only up-to date /// for the top of the stack and lazily updated for the rest. reached_depth: StackDepth, - /// Whether this entry is a non-root cycle participant. - /// - /// We must not move the result of non-root cycle participants to the - /// global cache. We store the highest stack depth of a head of a cycle - /// this goal is involved in. This necessary to soundly cache its - /// provisional result. - non_root_cycle_participant: Option, + /// All cycle heads this goal depends on. Lazily updated and only + /// up-to date for the top of the stack. + heads: CycleHeads, + /// Whether evaluating this goal encountered overflow. Lazily updated. encountered_overflow: bool, + /// Whether this goal has been used as the root of a cycle. This gets + /// eagerly updated when encountering a cycle. has_been_used: Option, - /// We put only the root goal of a coinductive cycle into the global cache. - /// - /// If we were to use that result when later trying to prove another cycle - /// participant, we can end up with unstable query results. - /// - /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for - /// an example of where this is needed. - /// - /// There can be multiple roots on the same stack, so we need to track - /// cycle participants per root: - /// ```plain - /// A :- B - /// B :- A, C - /// C :- D - /// D :- C - /// ``` - nested_goals: HashSet, + /// The nested goals of this goal, see the doc comment of the type. + nested_goals: NestedGoals, + /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. provisional_result: Option, } -/// The provisional result for a goal which is not on the stack. -#[derive(Debug)] -struct DetachedEntry { - /// The head of the smallest non-trivial cycle involving this entry. - /// - /// Given the following rules, when proving `A` the head for - /// the provisional entry of `C` would be `B`. - /// ```plain - /// A :- B - /// B :- C - /// C :- A + B + C - /// ``` - head: StackDepth, - result: X::Result, -} - -/// Stores the provisional result of already computed results for goals which -/// depend on other goals still on the stack. -/// -/// The provisional result may depend on whether the stack above it is inductive -/// or coinductive. Because of this, we store separate provisional results for -/// each case. If an provisional entry is not applicable, it may be the case -/// that we already have provisional result while computing a goal. In this case -/// we prefer the provisional result to potentially avoid fixpoint iterations. -/// See tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs for an example. -/// -/// The provisional cache can theoretically result in changes to the observable behavior, -/// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs. -#[derive_where(Default; X: Cx)] +/// A provisional result of an already computed goals which depends on other +/// goals still on the stack. +#[derive_where(Debug; X: Cx)] struct ProvisionalCacheEntry { - with_inductive_stack: Option>, - with_coinductive_stack: Option>, -} - -impl ProvisionalCacheEntry { - fn is_empty(&self) -> bool { - self.with_inductive_stack.is_none() && self.with_coinductive_stack.is_none() - } + /// Whether evaluating the goal encountered overflow. This is used to + /// disable the cache entry except if the last goal on the stack is + /// already involved in this cycle. + encountered_overflow: bool, + /// All cycle heads this cache entry depends on. + heads: CycleHeads, + /// The path from the highest cycle head to this goal. + path_from_head: PathKind, + nested_goals: NestedGoals, + result: X::Result, } pub struct SearchGraph, X: Cx = ::Cx> { @@ -254,7 +362,11 @@ pub struct SearchGraph, X: Cx = ::Cx> { /// /// An element is *deeper* in the stack if its index is *lower*. stack: IndexVec>, - provisional_cache: HashMap>, + /// The provisional cache contains entries for already computed goals which + /// still depend on goals higher-up in the stack. We don't move them to the + /// global cache and track them locally instead. A provisional cache entry + /// is only valid until the result of one of its cycle heads changes. + provisional_cache: HashMap>>, _marker: PhantomData, } @@ -273,67 +385,66 @@ impl, X: Cx> SearchGraph { self.mode } - fn update_parent_goal(&mut self, reached_depth: StackDepth, encountered_overflow: bool) { - if let Some(parent) = self.stack.raw.last_mut() { + /// Lazily update the stack entry for the parent goal. + /// This behavior is shared between actually evaluating goals + /// and using existing global cache entries to make sure they + /// have the same impact on the remaining evaluation. + fn update_parent_goal( + cx: X, + stack: &mut IndexVec>, + reached_depth: StackDepth, + heads: &CycleHeads, + encountered_overflow: bool, + nested_goals: &NestedGoals, + ) { + if let Some(parent_index) = stack.last_index() { + let parent = &mut stack[parent_index]; parent.reached_depth = parent.reached_depth.max(reached_depth); parent.encountered_overflow |= encountered_overflow; + + parent.heads.extend_from_child(parent_index, heads); + let step_kind = Self::step_kind(cx, parent.input); + parent.nested_goals.extend_from_child(step_kind, nested_goals); + // Once we've got goals which encountered overflow or a cycle, + // we track all goals whose behavior may depend depend on these + // goals as this change may cause them to now depend on additional + // goals, resulting in new cycles. See the dev-guide for examples. + if !nested_goals.is_empty() { + parent.nested_goals.insert(parent.input, UsageKind::Single(PathKind::Coinductive)) + } } } pub fn is_empty(&self) -> bool { - self.stack.is_empty() + if self.stack.is_empty() { + debug_assert!(self.provisional_cache.is_empty()); + true + } else { + false + } } - fn stack_coinductive_from( - cx: X, - stack: &IndexVec>, - head: StackDepth, - ) -> bool { - stack.raw[head.index()..].iter().all(|entry| D::step_is_coinductive(cx, entry.input)) - } - - // When encountering a solver cycle, the result of the current goal - // depends on goals lower on the stack. - // - // We have to therefore be careful when caching goals. Only the final result - // of the cycle root, i.e. the lowest goal on the stack involved in this cycle, - // is moved to the global cache while all others are stored in a provisional cache. - // - // We update both the head of this cycle to rerun its evaluation until - // we reach a fixpoint and all other cycle participants to make sure that - // their result does not get moved to the global cache. - fn tag_cycle_participants(stack: &mut IndexVec>, head: StackDepth) { - // The current root of these cycles. Note that this may not be the final - // root in case a later goal depends on a goal higher up the stack. - let mut current_root = head; - while let Some(parent) = stack[current_root].non_root_cycle_participant { - current_root = parent; - debug_assert!(stack[current_root].has_been_used.is_some()); - } + /// The number of goals currently in the search graph. This should only be + /// used for debugging purposes. + pub fn debug_current_depth(&self) -> usize { + self.stack.len() + } - let (stack, cycle_participants) = stack.raw.split_at_mut(head.index() + 1); - let current_cycle_root = &mut stack[current_root.as_usize()]; - for entry in cycle_participants { - entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head)); - current_cycle_root.nested_goals.insert(entry.input); - current_cycle_root.nested_goals.extend(mem::take(&mut entry.nested_goals)); - } + fn step_kind(cx: X, input: X::Input) -> PathKind { + if D::step_is_coinductive(cx, input) { PathKind::Coinductive } else { PathKind::Inductive } } - fn clear_dependent_provisional_results( - provisional_cache: &mut HashMap>, + /// Whether the path from `head` to the current stack entry is inductive or coinductive. + fn stack_path_kind( + cx: X, + stack: &IndexVec>, head: StackDepth, - ) { - #[allow(rustc::potential_query_instability)] - provisional_cache.retain(|_, entry| { - if entry.with_coinductive_stack.as_ref().is_some_and(|p| p.head == head) { - entry.with_coinductive_stack.take(); - } - if entry.with_inductive_stack.as_ref().is_some_and(|p| p.head == head) { - entry.with_inductive_stack.take(); - } - !entry.is_empty() - }); + ) -> PathKind { + if stack.raw[head.index()..].iter().all(|entry| D::step_is_coinductive(cx, entry.input)) { + PathKind::Coinductive + } else { + PathKind::Inductive + } } /// Probably the most involved method of the whole solver. @@ -345,20 +456,27 @@ impl, X: Cx> SearchGraph { cx: X, input: X::Input, inspect: &mut D::ProofTreeBuilder, - mut prove_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, + mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, ) -> X::Result { - self.check_invariants(); - // Check for overflow. let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::(cx, &self.stack) else { - if let Some(last) = self.stack.raw.last_mut() { - last.encountered_overflow = true; - } - - debug!("encountered stack overflow"); - return D::on_stack_overflow(cx, inspect, input); + return self.handle_overflow(cx, input, inspect); }; + // We check the provisional cache before checking the global cache. This simplifies + // the implementation as we can avoid worrying about cases where both the global and + // provisional cache may apply, e.g. consider the following example + // + // - xxBA overflow + // - A + // - BA cycle + // - CB :x: + if let Some(result) = self.lookup_provisional_cache(cx, input) { + return result; + } + + // Lookup the global cache unless we're building proof trees or are currently + // fuzzing. let validate_cache = if !D::inspect_is_noop(inspect) { None } else if let Some(scope) = D::enter_validation_scope(cx, input) { @@ -375,20 +493,22 @@ impl, X: Cx> SearchGraph { None }; - if let Some(result) = self.lookup_provisional_cache(cx, input) { - return result; - } - + // Detect cycles on the stack. We do this after the global cache lookup to + // avoid iterating over the stack in case a goal has already been computed. + // This may not have an actual performance impact and we could reorder them + // as it may reduce the number of `nested_goals` we need to track. if let Some(result) = self.check_cycle_on_stack(cx, input) { + debug_assert!(validate_cache.is_none(), "global cache and cycle on stack"); return result; } + // Unfortunate, it looks like we actually have to compute this goalrar. let depth = self.stack.next_index(); let entry = StackEntry { input, available_depth, reached_depth: depth, - non_root_cycle_participant: None, + heads: Default::default(), encountered_overflow: false, has_been_used: None, nested_goals: Default::default(), @@ -403,37 +523,23 @@ impl, X: Cx> SearchGraph { // must not be added to the global cache. Notably, this is the case for // trait solver cycles participants. let ((final_entry, result), dep_node) = cx.with_cached_task(|| { - for _ in 0..D::FIXPOINT_STEP_LIMIT { - match self.fixpoint_step_in_task(cx, input, inspect, &mut prove_goal) { - StepResult::Done(final_entry, result) => return (final_entry, result), - StepResult::HasChanged => {} - } - } - - debug!("canonical cycle overflow"); - let current_entry = self.stack.pop().unwrap(); - debug_assert!(current_entry.has_been_used.is_none()); - let result = D::on_fixpoint_overflow(cx, input); - (current_entry, result) + self.evaluate_goal_in_task(cx, input, inspect, &mut evaluate_goal) }); - self.update_parent_goal(final_entry.reached_depth, final_entry.encountered_overflow); - - // We're now done with this goal. In case this goal is involved in a larger cycle - // do not remove it from the provisional cache and update its provisional result. - // We only add the root of cycles to the global cache. - if let Some(head) = final_entry.non_root_cycle_participant { - debug_assert!(validate_cache.is_none()); - let coinductive_stack = Self::stack_coinductive_from(cx, &self.stack, head); + // We've finished computing the goal and have popped it from the stack, + // lazily update its parent goal. + Self::update_parent_goal( + cx, + &mut self.stack, + final_entry.reached_depth, + &final_entry.heads, + final_entry.encountered_overflow, + &final_entry.nested_goals, + ); - let entry = self.provisional_cache.entry(input).or_default(); - if coinductive_stack { - entry.with_coinductive_stack = Some(DetachedEntry { head, result }); - } else { - entry.with_inductive_stack = Some(DetachedEntry { head, result }); - } - } else { - self.provisional_cache.remove(&input); + // We're now done with this goal. We only add the root of cycles to the global cache. + // In case this goal is involved in a larger cycle add it to the provisional cache. + if final_entry.heads.is_empty() { if let Some((_scope, expected)) = validate_cache { // Do not try to move a goal into the cache again if we're testing // the global cache. @@ -441,11 +547,278 @@ impl, X: Cx> SearchGraph { } else if D::inspect_is_noop(inspect) { self.insert_global_cache(cx, input, final_entry, result, dep_node) } + } else if D::ENABLE_PROVISIONAL_CACHE { + debug_assert!(validate_cache.is_none()); + let entry = self.provisional_cache.entry(input).or_default(); + let StackEntry { heads, nested_goals, encountered_overflow, .. } = final_entry; + let path_from_head = Self::stack_path_kind(cx, &self.stack, heads.highest_cycle_head()); + entry.push(ProvisionalCacheEntry { + encountered_overflow, + heads, + path_from_head, + nested_goals, + result, + }); + } else { + debug_assert!(validate_cache.is_none()); } result } + fn handle_overflow( + &mut self, + cx: X, + input: X::Input, + inspect: &mut D::ProofTreeBuilder, + ) -> X::Result { + if let Some(last) = self.stack.raw.last_mut() { + last.encountered_overflow = true; + // If computing a goal `B` depends on another goal `A` and + // `A` has a nested goal which overflows, then computing `B` + // at the same depth, but with `A` already on the stack, + // would encounter a solver cycle instead, potentially + // changing the result. + // + // We must therefore not use the global cache entry for `B` in that case. + // See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs + last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Coinductive)); + } + + debug!("encountered stack overflow"); + D::on_stack_overflow(cx, inspect, input) + } + + /// When reevaluating a goal with a changed provisional result, all provisional cache entry + /// which depend on this goal get invalidated. + fn clear_dependent_provisional_results(&mut self) { + let head = self.stack.next_index(); + #[allow(rustc::potential_query_instability)] + self.provisional_cache.retain(|_, entries| { + entries.retain(|entry| entry.heads.highest_cycle_head() != head); + !entries.is_empty() + }); + } + + /// A necessary optimization to handle complex solver cycles. A provisional cache entry + /// relies on a set of cycle heads and the path towards these heads. When popping a cycle + /// head from the stack after we've finished computing it, we can't be sure that the + /// provisional cache entry is still applicable. We need to keep the cache entries to + /// prevent hangs. + /// + /// What we therefore do is check whether the cycle kind of all cycles the goal of a + /// provisional cache entry is involved in would stay the same when computing the + /// goal without its cycle head on the stack. For more details, see the relevant + /// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html). + /// + /// This can be thought of rotating the sub-tree of this provisional result and changing + /// its entry point while making sure that all paths through this sub-tree stay the same. + /// + /// + /// In case the popped cycle head failed to reach a fixpoint anything which depends on + /// its provisional result is invalid. Actually discarding provisional cache entries in + /// this case would cause hangs, so we instead change the result of dependant provisional + /// cache entries to also be ambiguous. This causes some undesirable ambiguity for nested + /// goals whose result doesn't actually depend on this cycle head, but that's acceptable + /// to me. + fn rebase_provisional_cache_entries( + &mut self, + cx: X, + stack_entry: &StackEntry, + mut mutate_result: impl FnMut(X::Input, X::Result) -> X::Result, + ) { + let head = self.stack.next_index(); + #[allow(rustc::potential_query_instability)] + self.provisional_cache.retain(|&input, entries| { + entries.retain_mut(|entry| { + let ProvisionalCacheEntry { + encountered_overflow: _, + heads, + path_from_head, + nested_goals, + result, + } = entry; + if heads.highest_cycle_head() != head { + return true; + } + + // We don't try rebasing if the path from the current head + // to the cache entry is not coinductive or if the path from + // the cache entry to the current head is not coinductive. + // + // Both of these constraints could be weakened, but by only + // accepting coinductive paths we don't have to worry about + // changing the cycle kind of the remaining cycles. We can + // extend this in the future once there's a known issue + // caused by it. + if *path_from_head != PathKind::Coinductive + || nested_goals.get(stack_entry.input).unwrap() + != UsageKind::Single(PathKind::Coinductive) + { + return false; + } + + // Merge the cycle heads of the provisional cache entry and the + // popped head. If the popped cycle head was a root, discard all + // provisional cache entries which depend on it. + heads.remove_highest_cycle_head(); + heads.merge(&stack_entry.heads); + let Some(head) = heads.opt_highest_cycle_head() else { + return false; + }; + + // As we've made sure that the path from the new highest cycle + // head to the uses of the popped cycle head are fully coinductive, + // we can be sure that the paths to all nested goals of the popped + // cycle head remain the same. We can simply merge them. + nested_goals.merge(&stack_entry.nested_goals); + // We now care about the path from the next highest cycle head to the + // provisional cache entry. + *path_from_head = Self::stack_path_kind(cx, &self.stack, head); + // Mutate the result of the provisional cache entry in case we did + // not reach a fixpoint. + *result = mutate_result(input, *result); + true + }); + !entries.is_empty() + }); + } + + fn lookup_provisional_cache(&mut self, cx: X, input: X::Input) -> Option { + if !D::ENABLE_PROVISIONAL_CACHE { + return None; + } + + let entries = self.provisional_cache.get(&input)?; + for &ProvisionalCacheEntry { + encountered_overflow, + ref heads, + path_from_head, + ref nested_goals, + result, + } in entries + { + let head = heads.highest_cycle_head(); + if encountered_overflow { + // This check is overly strict and very subtle. We need to make sure that if + // a global cache entry depends on some goal without adding it to its + // `nested_goals`, that goal must never have an applicable provisional + // cache entry to avoid incorrectly applying the cache entry. + // + // As we'd have to otherwise track literally all nested goals, we only + // apply provisional cache entries which encountered overflow once the + // current goal is already part of the same cycle. This check could be + // improved but seems to be good enough for now. + let last = self.stack.raw.last().unwrap(); + if !last.heads.opt_lowest_cycle_head().is_some_and(|lowest| lowest <= head) { + continue; + } + } + + // A provisional cache entry is only valid if the current path from its + // highest cycle head to the goal is the same. + if path_from_head == Self::stack_path_kind(cx, &self.stack, head) { + // While we don't have to track the full depth of the provisional cache entry, + // we do have to increment the required depth by one as we'd have already failed + // with overflow otherwise + let next_index = self.stack.next_index(); + let last = &mut self.stack.raw.last_mut().unwrap(); + let path_from_entry = Self::step_kind(cx, last.input); + last.nested_goals.insert(input, UsageKind::Single(path_from_entry)); + + Self::update_parent_goal( + cx, + &mut self.stack, + next_index, + heads, + false, + nested_goals, + ); + debug_assert!(self.stack[head].has_been_used.is_some()); + debug!(?head, ?path_from_head, "provisional cache hit"); + return Some(result); + } + } + + None + } + + /// Even if there is a global cache entry for a given goal, we need to make sure + /// evaluating this entry would not have ended up depending on either a goal + /// already on the stack or a provisional cache entry. + fn candidate_is_applicable( + cx: X, + stack: &IndexVec>, + provisional_cache: &HashMap>>, + nested_goals: &NestedGoals, + ) -> bool { + // If the global cache entry didn't depend on any nested goals, it always + // applies. + if nested_goals.is_empty() { + return true; + } + + // If a nested goal of the global cache entry is on the stack, we would + // definitely encounter a cycle. + if stack.iter().any(|e| nested_goals.contains(e.input)) { + debug!("cache entry not applicable due to stack"); + return false; + } + + // The global cache entry is also invalid if there's a provisional cache entry + // would apply for any of its nested goals. + #[allow(rustc::potential_query_instability)] + for (input, path_from_global_entry) in nested_goals.iter() { + let Some(entries) = provisional_cache.get(&input) else { + continue; + }; + + debug!(?input, ?path_from_global_entry, ?entries, "candidate_is_applicable"); + // A provisional cache entry is applicable if the path to + // its highest cycle head is equal to the expected path. + for &ProvisionalCacheEntry { + encountered_overflow, + ref heads, + path_from_head, + nested_goals: _, + result: _, + } in entries.iter() + { + // We don't have to worry about provisional cache entries which encountered + // overflow, see the relevant comment in `lookup_provisional_cache`. + if encountered_overflow { + continue; + } + + // A provisional cache entry only applies if the path from its highest head + // matches the path when encountering the goal. + let head = heads.highest_cycle_head(); + let full_path = match Self::stack_path_kind(cx, stack, head) { + PathKind::Coinductive => path_from_global_entry, + PathKind::Inductive => UsageKind::Single(PathKind::Inductive), + }; + + match (full_path, path_from_head) { + (UsageKind::Mixed, _) + | (UsageKind::Single(PathKind::Coinductive), PathKind::Coinductive) + | (UsageKind::Single(PathKind::Inductive), PathKind::Inductive) => { + debug!( + ?full_path, + ?path_from_head, + "cache entry not applicable due to matching paths" + ); + return false; + } + _ => debug!(?full_path, ?path_from_head, "paths don't match"), + } + } + } + + true + } + + /// Used when fuzzing the global cache. Accesses the global cache without + /// updating the state of the search graph. fn lookup_global_cache_untracked( &self, cx: X, @@ -453,7 +826,16 @@ impl, X: Cx> SearchGraph { available_depth: AvailableDepth, ) -> Option { cx.with_global_cache(self.mode, |cache| { - cache.get(cx, input, &self.stack, available_depth).map(|c| c.result) + cache + .get(cx, input, available_depth, |nested_goals| { + Self::candidate_is_applicable( + cx, + &self.stack, + &self.provisional_cache, + nested_goals, + ) + }) + .map(|c| c.result) }) } @@ -467,46 +849,37 @@ impl, X: Cx> SearchGraph { available_depth: AvailableDepth, ) -> Option { cx.with_global_cache(self.mode, |cache| { - let CacheData { - result, - additional_depth, - encountered_overflow, - nested_goals: _, // FIXME: consider nested goals here. - } = cache.get(cx, input, &self.stack, available_depth)?; + let CacheData { result, additional_depth, encountered_overflow, nested_goals } = cache + .get(cx, input, available_depth, |nested_goals| { + Self::candidate_is_applicable( + cx, + &self.stack, + &self.provisional_cache, + nested_goals, + ) + })?; // Update the reached depth of the current goal to make sure // its state is the same regardless of whether we've used the // global cache or not. let reached_depth = self.stack.next_index().plus(additional_depth); - self.update_parent_goal(reached_depth, encountered_overflow); + // We don't move cycle participants to the global cache, so the + // cycle heads are always empty. + let heads = Default::default(); + Self::update_parent_goal( + cx, + &mut self.stack, + reached_depth, + &heads, + encountered_overflow, + nested_goals, + ); debug!(?additional_depth, "global cache hit"); Some(result) }) } - fn lookup_provisional_cache(&mut self, cx: X, input: X::Input) -> Option { - let cache_entry = self.provisional_cache.get(&input)?; - let &DetachedEntry { head, result } = cache_entry - .with_coinductive_stack - .as_ref() - .filter(|p| Self::stack_coinductive_from(cx, &self.stack, p.head)) - .or_else(|| { - cache_entry - .with_inductive_stack - .as_ref() - .filter(|p| !Self::stack_coinductive_from(cx, &self.stack, p.head)) - })?; - - debug!("provisional cache hit"); - // We have a nested goal which is already in the provisional cache, use - // its result. We do not provide any usage kind as that should have been - // already set correctly while computing the cache entry. - Self::tag_cycle_participants(&mut self.stack, head); - debug_assert!(self.stack[head].has_been_used.is_some()); - Some(result) - } - fn check_cycle_on_stack(&mut self, cx: X, input: X::Input) -> Option { let (head, _stack_entry) = self.stack.iter_enumerated().find(|(_, e)| e.input == input)?; debug!("encountered cycle with depth {head:?}"); @@ -516,20 +889,48 @@ impl, X: Cx> SearchGraph { // // Finally we can return either the provisional response or the initial response // in case we're in the first fixpoint iteration for this goal. - let is_coinductive_cycle = Self::stack_coinductive_from(cx, &self.stack, head); - let cycle_kind = - if is_coinductive_cycle { CycleKind::Coinductive } else { CycleKind::Inductive }; - let usage_kind = UsageKind::Single(cycle_kind); + let path_kind = Self::stack_path_kind(cx, &self.stack, head); + let usage_kind = UsageKind::Single(path_kind); self.stack[head].has_been_used = Some(self.stack[head].has_been_used.map_or(usage_kind, |prev| prev.merge(usage_kind))); - Self::tag_cycle_participants(&mut self.stack, head); + + // Subtle: when encountering a cyclic goal, we still first checked for overflow, + // so we have to update the reached depth. + let next_index = self.stack.next_index(); + let last_index = self.stack.last_index().unwrap(); + let last = &mut self.stack[last_index]; + last.reached_depth = last.reached_depth.max(next_index); + + let path_from_entry = Self::step_kind(cx, last.input); + last.nested_goals.insert(input, UsageKind::Single(path_from_entry)); + last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Coinductive)); + if last_index != head { + last.heads.insert(head); + } // Return the provisional result or, if we're in the first iteration, // start with no constraints. if let Some(result) = self.stack[head].provisional_result { Some(result) } else { - Some(D::initial_provisional_result(cx, cycle_kind, input)) + Some(D::initial_provisional_result(cx, path_kind, input)) + } + } + + /// Whether we've reached a fixpoint when evaluating a cycle head. + fn reached_fixpoint( + &mut self, + cx: X, + stack_entry: &StackEntry, + usage_kind: UsageKind, + result: X::Result, + ) -> bool { + if let Some(prev) = stack_entry.provisional_result { + prev == result + } else if let UsageKind::Single(kind) = usage_kind { + D::is_initial_provisional_result(cx, kind, stack_entry.input, result) + } else { + false } } @@ -538,55 +939,83 @@ impl, X: Cx> SearchGraph { /// of this we continuously recompute the cycle until the result /// of the previous iteration is equal to the final result, at which /// point we are done. - fn fixpoint_step_in_task( + fn evaluate_goal_in_task( &mut self, cx: X, input: X::Input, inspect: &mut D::ProofTreeBuilder, - prove_goal: &mut F, - ) -> StepResult - where - F: FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, - { - let result = prove_goal(self, inspect); - let stack_entry = self.stack.pop().unwrap(); - debug_assert_eq!(stack_entry.input, input); - - // If the current goal is not the root of a cycle, we are done. - let Some(usage_kind) = stack_entry.has_been_used else { - return StepResult::Done(stack_entry, result); - }; + mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, + ) -> (StackEntry, X::Result) { + let mut i = 0; + loop { + let result = evaluate_goal(self, inspect); + let stack_entry = self.stack.pop().unwrap(); + debug_assert_eq!(stack_entry.input, input); + + // If the current goal is not the root of a cycle, we are done. + // + // There are no provisional cache entries which depend on this goal. + let Some(usage_kind) = stack_entry.has_been_used else { + return (stack_entry, result); + }; + + // If it is a cycle head, we have to keep trying to prove it until + // we reach a fixpoint. We need to do so for all cycle heads, + // not only for the root. + // + // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs + // for an example. + // + // Check whether we reached a fixpoint, either because the final result + // is equal to the provisional result of the previous iteration, or because + // this was only the root of either coinductive or inductive cycles, and the + // final result is equal to the initial response for that case. + if self.reached_fixpoint(cx, &stack_entry, usage_kind, result) { + self.rebase_provisional_cache_entries(cx, &stack_entry, |_, result| result); + return (stack_entry, result); + } - // If it is a cycle head, we have to keep trying to prove it until - // we reach a fixpoint. We need to do so for all cycle heads, - // not only for the root. - // - // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs - // for an example. - - // Start by clearing all provisional cache entries which depend on this - // the current goal. - Self::clear_dependent_provisional_results( - &mut self.provisional_cache, - self.stack.next_index(), - ); + // If computing this goal results in ambiguity with no constraints, + // we do not rerun it. It's incredibly difficult to get a different + // response in the next iteration in this case. These changes would + // likely either be caused by incompleteness or can change the maybe + // cause from ambiguity to overflow. Returning ambiguity always + // preserves soundness and completeness even if the goal is be known + // to succeed or fail. + // + // This prevents exponential blowup affecting multiple major crates. + // As we only get to this branch if we haven't yet reached a fixpoint, + // we also taint all provisional cache entries which depend on the + // current goal. + if D::is_ambiguous_result(result) { + self.rebase_provisional_cache_entries(cx, &stack_entry, |input, _| { + D::propagate_ambiguity(cx, input, result) + }); + return (stack_entry, result); + }; + + // If we've reached the fixpoint step limit, we bail with overflow and taint all + // provisional cache entries which depend on the current goal. + i += 1; + if i >= D::FIXPOINT_STEP_LIMIT { + debug!("canonical cycle overflow"); + let result = D::on_fixpoint_overflow(cx, input); + self.rebase_provisional_cache_entries(cx, &stack_entry, |input, _| { + D::on_fixpoint_overflow(cx, input) + }); + return (stack_entry, result); + } + + // Clear all provisional cache entries which depend on a previous provisional + // result of this goal and rerun. + self.clear_dependent_provisional_results(); - // Check whether we reached a fixpoint, either because the final result - // is equal to the provisional result of the previous iteration, or because - // this was only the root of either coinductive or inductive cycles, and the - // final result is equal to the initial response for that case. - // - // If we did not reach a fixpoint, update the provisional result and reevaluate. - if D::reached_fixpoint(cx, usage_kind, input, stack_entry.provisional_result, result) { - StepResult::Done(stack_entry, result) - } else { debug!(?result, "fixpoint changed provisional results"); self.stack.push(StackEntry { has_been_used: None, provisional_result: Some(result), ..stack_entry }); - StepResult::HasChanged } } @@ -616,7 +1045,7 @@ impl, X: Cx> SearchGraph { dep_node, additional_depth, final_entry.encountered_overflow, - &final_entry.nested_goals, + final_entry.nested_goals, ) }) } diff --git a/compiler/rustc_type_ir/src/search_graph/validate.rs b/compiler/rustc_type_ir/src/search_graph/validate.rs deleted file mode 100644 index b4802811b0f57..0000000000000 --- a/compiler/rustc_type_ir/src/search_graph/validate.rs +++ /dev/null @@ -1,63 +0,0 @@ -use super::*; - -impl, X: Cx> SearchGraph { - #[allow(rustc::potential_query_instability)] - pub(super) fn check_invariants(&self) { - if !cfg!(debug_assertions) { - return; - } - - let SearchGraph { mode: _, stack, provisional_cache, _marker } = self; - if stack.is_empty() { - assert!(provisional_cache.is_empty()); - } - - for (depth, entry) in stack.iter_enumerated() { - let StackEntry { - input, - available_depth: _, - reached_depth: _, - non_root_cycle_participant, - encountered_overflow: _, - has_been_used, - ref nested_goals, - provisional_result, - } = *entry; - if let Some(head) = non_root_cycle_participant { - assert!(head < depth); - assert!(nested_goals.is_empty()); - assert_ne!(stack[head].has_been_used, None); - - let mut current_root = head; - while let Some(parent) = stack[current_root].non_root_cycle_participant { - current_root = parent; - } - assert!(stack[current_root].nested_goals.contains(&input)); - } - - if !nested_goals.is_empty() { - assert!(provisional_result.is_some() || has_been_used.is_some()); - for entry in stack.iter().take(depth.as_usize()) { - assert_eq!(nested_goals.get(&entry.input), None); - } - } - } - - for (&_input, entry) in &self.provisional_cache { - let ProvisionalCacheEntry { with_coinductive_stack, with_inductive_stack } = entry; - assert!(with_coinductive_stack.is_some() || with_inductive_stack.is_some()); - let check_detached = |detached_entry: &DetachedEntry| { - let DetachedEntry { head, result: _ } = *detached_entry; - assert_ne!(stack[head].has_been_used, None); - }; - - if let Some(with_coinductive_stack) = with_coinductive_stack { - check_detached(with_coinductive_stack); - } - - if let Some(with_inductive_stack) = with_inductive_stack { - check_detached(with_inductive_stack); - } - } - } -} From 87a4c325d2767e5e0baf0c2a4d0cc12bb78ca356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 13 Aug 2024 18:08:30 +0200 Subject: [PATCH 119/178] Fix target triple in bootstrap --- src/bootstrap/src/core/download.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 8131666fcb225..fd85650bc5620 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -519,7 +519,7 @@ impl Config { extra_components: &[&str], download_component: fn(&Config, String, &str, &str), ) { - let host = self.build; + let host = self.build.triple; let bin_root = self.out.join(host).join(sysroot); let rustc_stamp = bin_root.join(".rustc-stamp"); From c6fb0f344e0c6edb34966b1d73c3c1d8c5afbe34 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 13 Aug 2024 09:33:12 -0700 Subject: [PATCH 120/178] diagnostics: use `DeepRejectCtxt` for check This makes more things match, particularly applicable blankets. --- .../error_reporting/infer/note_and_explain.rs | 37 ++++++++++++++----- .../ptr-to-trait-obj-different-args.stderr | 3 ++ tests/ui/traits/upcast_soundness_bug.stderr | 1 + 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 0c140f93d94b2..eab4addf97072 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::print::{FmtPrinter, Printer}; use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; use rustc_span::def_id::DefId; @@ -313,9 +314,15 @@ impl Trait for X { (ty::Dynamic(t, _, ty::DynKind::Dyn), _) if let Some(def_id) = t.principal_def_id() => { - let has_non_blanket_impl = - tcx.non_blanket_impls_for_ty(def_id, values.found).next().is_some(); - if has_non_blanket_impl { + let mut has_matching_impl = false; + tcx.for_each_relevant_impl(def_id, values.found, |did| { + if DeepRejectCtxt::new(tcx, TreatParams::ForLookup) + .types_may_unify(values.found, tcx.type_of(did).skip_binder()) + { + has_matching_impl = true; + } + }); + if has_matching_impl { let trait_name = tcx.item_name(def_id); diag.help(format!( "`{}` implements `{trait_name}` so you could box the found value \ @@ -328,9 +335,15 @@ impl Trait for X { (_, ty::Dynamic(t, _, ty::DynKind::Dyn)) if let Some(def_id) = t.principal_def_id() => { - let has_non_blanket_impl = - tcx.non_blanket_impls_for_ty(def_id, values.expected).next().is_some(); - if has_non_blanket_impl { + let mut has_matching_impl = false; + tcx.for_each_relevant_impl(def_id, values.expected, |did| { + if DeepRejectCtxt::new(tcx, TreatParams::ForLookup) + .types_may_unify(values.expected, tcx.type_of(did).skip_binder()) + { + has_matching_impl = true; + } + }); + if has_matching_impl { let trait_name = tcx.item_name(def_id); diag.help(format!( "`{}` implements `{trait_name}` so you could change the expected \ @@ -342,9 +355,15 @@ impl Trait for X { (ty::Dynamic(t, _, ty::DynKind::DynStar), _) if let Some(def_id) = t.principal_def_id() => { - let has_non_blanket_impl = - tcx.non_blanket_impls_for_ty(def_id, values.found).next().is_some(); - if has_non_blanket_impl { + let mut has_matching_impl = false; + tcx.for_each_relevant_impl(def_id, values.found, |did| { + if DeepRejectCtxt::new(tcx, TreatParams::ForLookup) + .types_may_unify(values.found, tcx.type_of(did).skip_binder()) + { + has_matching_impl = true; + } + }); + if has_matching_impl { let trait_name = tcx.item_name(def_id); diag.help(format!( "`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \ diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr index b04289ae74748..8e60ca42f0a52 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -14,6 +14,7 @@ LL | let y: *const dyn Trait = x as _; | = note: expected trait object `dyn Trait` found trait object `dyn Trait` + = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0308]: mismatched types --> $DIR/ptr-to-trait-obj-different-args.rs:27:34 @@ -25,6 +26,7 @@ LL | let _: *const dyn Trait = x as _; | = note: expected trait object `dyn Trait` found trait object `dyn Trait` + = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0308]: mismatched types --> $DIR/ptr-to-trait-obj-different-args.rs:28:34 @@ -37,6 +39,7 @@ LL | let _: *const dyn Trait = t as _; | = note: expected trait object `dyn Trait` found trait object `dyn Trait` + = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0308]: mismatched types --> $DIR/ptr-to-trait-obj-different-args.rs:36:5 diff --git a/tests/ui/traits/upcast_soundness_bug.stderr b/tests/ui/traits/upcast_soundness_bug.stderr index 31f77b52b5fbf..5864abcdb41f5 100644 --- a/tests/ui/traits/upcast_soundness_bug.stderr +++ b/tests/ui/traits/upcast_soundness_bug.stderr @@ -6,6 +6,7 @@ LL | let p = p as *const dyn Trait; // <- this is bad! | = note: expected trait object `dyn Trait` found trait object `dyn Trait` + = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error: aborting due to 1 previous error From 05fbfde17d34a0685877e7590cd30fcbaa27c0bd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 8 Jun 2024 13:46:02 +0200 Subject: [PATCH 121/178] Clean up rustdoc make_test function code --- src/librustdoc/doctest/make.rs | 42 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 74833c113623b..322881dcd9c5b 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -25,6 +25,7 @@ pub(crate) fn make_test( dont_insert_main: bool, opts: &GlobalTestOptions, edition: Edition, + // If `test_id` is `None`, it means we're generating code for a code example "run" link. test_id: Option<&str>, ) -> (String, usize, bool) { let (crate_attrs, everything_else, crates) = partition_source(s, edition); @@ -65,21 +66,22 @@ pub(crate) fn make_test( // Don't inject `extern crate std` because it's already injected by the // compiler. - if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") { - if let Some(crate_name) = crate_name { - // Don't inject `extern crate` if the crate is never used. - // NOTE: this is terribly inaccurate because it doesn't actually - // parse the source, but only has false positives, not false - // negatives. - if s.contains(crate_name) { - // rustdoc implicitly inserts an `extern crate` item for the own crate - // which may be unused, so we need to allow the lint. - prog.push_str("#[allow(unused_extern_crates)]\n"); - - prog.push_str(&format!("extern crate r#{crate_name};\n")); - line_offset += 1; - } - } + if !already_has_extern_crate && + !opts.no_crate_inject && + let Some(crate_name) = crate_name && + crate_name != "std" && + // Don't inject `extern crate` if the crate is never used. + // NOTE: this is terribly inaccurate because it doesn't actually + // parse the source, but only has false positives, not false + // negatives. + s.contains(crate_name) + { + // rustdoc implicitly inserts an `extern crate` item for the own crate + // which may be unused, so we need to allow the lint. + prog.push_str("#[allow(unused_extern_crates)]\n"); + + prog.push_str(&format!("extern crate r#{crate_name};\n")); + line_offset += 1; } // FIXME: This code cannot yet handle no_std test cases yet @@ -234,22 +236,20 @@ fn check_for_main_and_extern_crate( (found_main, found_extern_crate, found_macro) }) }); - let (already_has_main, already_has_extern_crate, found_macro) = result?; + let (mut already_has_main, already_has_extern_crate, found_macro) = result?; // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't // see it. In that case, run the old text-based scan to see if they at least have a main // function written inside a macro invocation. See // https://github.com/rust-lang/rust/issues/56898 - let already_has_main = if found_macro && !already_has_main { - source + if found_macro && !already_has_main { + already_has_main = source .lines() .map(|line| { let comment = line.find("//"); if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } }) - .any(|code| code.contains("fn main")) - } else { - already_has_main + .any(|code| code.contains("fn main")); }; Ok((already_has_main, already_has_extern_crate)) From 5e244370fe1a4d8facb8c02326d076c0e3b85def Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 8 Jun 2024 16:31:53 +0200 Subject: [PATCH 122/178] Add `DocTest` type --- src/librustdoc/doctest.rs | 12 +- src/librustdoc/doctest/make.rs | 293 ++++++++++++++++++-------------- src/librustdoc/doctest/tests.rs | 18 +- src/librustdoc/html/markdown.rs | 5 +- 4 files changed, 190 insertions(+), 138 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 08d6a5a52b21a..f8f5744e4db43 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -10,7 +10,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::{panic, str}; -pub(crate) use make::make_test; +pub(crate) use make::DocTest; pub(crate) use markdown::test as test_markdown; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -732,13 +732,12 @@ fn doctest_run_fn( unused_externs.lock().unwrap().push(uext); }; let edition = scraped_test.edition(&rustdoc_options); - let (full_test_code, full_test_line_offset, supports_color) = make_test( - &scraped_test.text, - Some(&global_opts.crate_name), + let doctest = DocTest::new(&scraped_test.text, Some(&global_opts.crate_name), edition); + let (full_test_code, full_test_line_offset) = doctest.generate_unique_doctest( scraped_test.langstr.test_harness, &global_opts, - edition, Some(&test_opts.test_id), + Some(&global_opts.crate_name), ); let runnable_test = RunnableDoctest { full_test_code, @@ -747,7 +746,8 @@ fn doctest_run_fn( global_opts, scraped_test, }; - let res = run_test(runnable_test, &rustdoc_options, supports_color, report_unused_externs); + let res = + run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs); if let Err(err) = res { match err { diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 322881dcd9c5b..3ba0f36ef7af1 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -13,139 +13,173 @@ use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; -use rustc_span::FileName; +use rustc_span::{FileName, Span, DUMMY_SP}; use super::GlobalTestOptions; -/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of -/// lines before the test code begins as well as if the output stream supports colors or not. -pub(crate) fn make_test( - s: &str, - crate_name: Option<&str>, - dont_insert_main: bool, - opts: &GlobalTestOptions, - edition: Edition, - // If `test_id` is `None`, it means we're generating code for a code example "run" link. - test_id: Option<&str>, -) -> (String, usize, bool) { - let (crate_attrs, everything_else, crates) = partition_source(s, edition); - let everything_else = everything_else.trim(); - let mut line_offset = 0; - let mut prog = String::new(); - let mut supports_color = false; - - if opts.attrs.is_empty() { - // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some - // lints that are commonly triggered in doctests. The crate-level test attributes are - // commonly used to make tests fail in case they trigger warnings, so having this there in - // that case may cause some tests to pass when they shouldn't have. - prog.push_str("#![allow(unused)]\n"); - line_offset += 1; - } +pub(crate) struct DocTest { + pub(crate) test_code: String, + pub(crate) supports_color: bool, + pub(crate) already_has_extern_crate: bool, + pub(crate) main_fn_span: Option, + pub(crate) crate_attrs: String, + pub(crate) crates: String, + pub(crate) everything_else: String, +} - // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. - for attr in &opts.attrs { - prog.push_str(&format!("#![{attr}]\n")); - line_offset += 1; +impl DocTest { + pub(crate) fn new(source: &str, crate_name: Option<&str>, edition: Edition) -> Self { + let (crate_attrs, everything_else, crates) = partition_source(source, edition); + let mut supports_color = false; + + // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern + // crate already is included. + let Ok((main_fn_span, already_has_extern_crate)) = + check_for_main_and_extern_crate(crate_name, source, edition, &mut supports_color) + else { + // If the parser panicked due to a fatal error, pass the test code through unchanged. + // The error will be reported during compilation. + return DocTest { + test_code: source.to_string(), + supports_color: false, + main_fn_span: None, + crate_attrs, + crates, + everything_else, + already_has_extern_crate: false, + }; + }; + Self { + test_code: source.to_string(), + supports_color, + main_fn_span, + crate_attrs, + crates, + everything_else, + already_has_extern_crate, + } } - // Now push any outer attributes from the example, assuming they - // are intended to be crate attributes. - prog.push_str(&crate_attrs); - prog.push_str(&crates); - - // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern - // crate already is included. - let Ok((already_has_main, already_has_extern_crate)) = - check_for_main_and_extern_crate(crate_name, s.to_owned(), edition, &mut supports_color) - else { - // If the parser panicked due to a fatal error, pass the test code through unchanged. - // The error will be reported during compilation. - return (s.to_owned(), 0, false); - }; - - // Don't inject `extern crate std` because it's already injected by the - // compiler. - if !already_has_extern_crate && - !opts.no_crate_inject && - let Some(crate_name) = crate_name && - crate_name != "std" && - // Don't inject `extern crate` if the crate is never used. - // NOTE: this is terribly inaccurate because it doesn't actually - // parse the source, but only has false positives, not false - // negatives. - s.contains(crate_name) - { - // rustdoc implicitly inserts an `extern crate` item for the own crate - // which may be unused, so we need to allow the lint. - prog.push_str("#[allow(unused_extern_crates)]\n"); + /// Transforms a test into code that can be compiled into a Rust binary, and returns the number of + /// lines before the test code begins. + pub(crate) fn generate_unique_doctest( + &self, + dont_insert_main: bool, + opts: &GlobalTestOptions, + // If `test_id` is `None`, it means we're generating code for a code example "run" link. + test_id: Option<&str>, + crate_name: Option<&str>, + ) -> (String, usize) { + let mut line_offset = 0; + let mut prog = String::new(); + let everything_else = self.everything_else.trim(); + if opts.attrs.is_empty() { + // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some + // lints that are commonly triggered in doctests. The crate-level test attributes are + // commonly used to make tests fail in case they trigger warnings, so having this there in + // that case may cause some tests to pass when they shouldn't have. + prog.push_str("#![allow(unused)]\n"); + line_offset += 1; + } - prog.push_str(&format!("extern crate r#{crate_name};\n")); - line_offset += 1; - } + // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. + for attr in &opts.attrs { + prog.push_str(&format!("#![{attr}]\n")); + line_offset += 1; + } - // FIXME: This code cannot yet handle no_std test cases yet - if dont_insert_main || already_has_main || prog.contains("![no_std]") { - prog.push_str(everything_else); - } else { - let returns_result = everything_else.trim_end().ends_with("(())"); - // Give each doctest main function a unique name. - // This is for example needed for the tooling around `-C instrument-coverage`. - let inner_fn_name = if let Some(test_id) = test_id { - format!("_doctest_main_{test_id}") - } else { - "_inner".into() - }; - let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; - let (main_pre, main_post) = if returns_result { - ( - format!( - "fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n", - ), - format!("\n}} {inner_fn_name}().unwrap() }}"), - ) - } else if test_id.is_some() { - ( - format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",), - format!("\n}} {inner_fn_name}() }}"), - ) - } else { - ("fn main() {\n".into(), "\n}".into()) - }; - // Note on newlines: We insert a line/newline *before*, and *after* - // the doctest and adjust the `line_offset` accordingly. - // In the case of `-C instrument-coverage`, this means that the generated - // inner `main` function spans from the doctest opening codeblock to the - // closing one. For example - // /// ``` <- start of the inner main - // /// <- code under doctest - // /// ``` <- end of the inner main - line_offset += 1; - - // add extra 4 spaces for each line to offset the code block - let content = if opts.insert_indent_space { - everything_else - .lines() - .map(|line| format!(" {}", line)) - .collect::>() - .join("\n") + // Now push any outer attributes from the example, assuming they + // are intended to be crate attributes. + prog.push_str(&self.crate_attrs); + prog.push_str(&self.crates); + + // Don't inject `extern crate std` because it's already injected by the + // compiler. + if !self.already_has_extern_crate && + !opts.no_crate_inject && + let Some(crate_name) = crate_name && + crate_name != "std" && + // Don't inject `extern crate` if the crate is never used. + // NOTE: this is terribly inaccurate because it doesn't actually + // parse the source, but only has false positives, not false + // negatives. + self.test_code.contains(crate_name) + { + // rustdoc implicitly inserts an `extern crate` item for the own crate + // which may be unused, so we need to allow the lint. + prog.push_str("#[allow(unused_extern_crates)]\n"); + + prog.push_str(&format!("extern crate r#{crate_name};\n")); + line_offset += 1; + } + + // FIXME: This code cannot yet handle no_std test cases yet + if dont_insert_main || self.main_fn_span.is_some() || prog.contains("![no_std]") { + prog.push_str(everything_else); } else { - everything_else.to_string() - }; - prog.extend([&main_pre, content.as_str(), &main_post].iter().cloned()); - } + let returns_result = everything_else.ends_with("(())"); + // Give each doctest main function a unique name. + // This is for example needed for the tooling around `-C instrument-coverage`. + let inner_fn_name = if let Some(test_id) = test_id { + format!("_doctest_main_{test_id}") + } else { + "_inner".into() + }; + let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; + let (main_pre, main_post) = if returns_result { + ( + format!( + "fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n", + ), + format!("\n}} {inner_fn_name}().unwrap() }}"), + ) + } else if test_id.is_some() { + ( + format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",), + format!("\n}} {inner_fn_name}() }}"), + ) + } else { + ("fn main() {\n".into(), "\n}".into()) + }; + // Note on newlines: We insert a line/newline *before*, and *after* + // the doctest and adjust the `line_offset` accordingly. + // In the case of `-C instrument-coverage`, this means that the generated + // inner `main` function spans from the doctest opening codeblock to the + // closing one. For example + // /// ``` <- start of the inner main + // /// <- code under doctest + // /// ``` <- end of the inner main + line_offset += 1; + + prog.push_str(&main_pre); + + // add extra 4 spaces for each line to offset the code block + if opts.insert_indent_space { + prog.push_str( + &everything_else + .lines() + .map(|line| format!(" {}", line)) + .collect::>() + .join("\n"), + ); + } else { + prog.push_str(everything_else); + }; + prog.push_str(&main_post); + } - debug!("final doctest:\n{prog}"); + debug!("final doctest:\n{prog}"); - (prog, line_offset, supports_color) + (prog, line_offset) + } } fn check_for_main_and_extern_crate( crate_name: Option<&str>, - source: String, + source: &str, edition: Edition, supports_color: &mut bool, -) -> Result<(bool, bool), FatalError> { +) -> Result<(Option, bool), FatalError> { let result = rustc_driver::catch_fatal_errors(|| { rustc_span::create_session_if_not_set_then(edition, |_| { use rustc_errors::emitter::{Emitter, HumanEmitter}; @@ -153,7 +187,7 @@ fn check_for_main_and_extern_crate( use rustc_parse::parser::ForceCollect; use rustc_span::source_map::FilePathMapping; - let filename = FileName::anon_source_code(&source); + let filename = FileName::anon_source_code(source); // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. @@ -172,11 +206,11 @@ fn check_for_main_and_extern_crate( let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let psess = ParseSess::with_dcx(dcx, sm); - let mut found_main = false; + let mut found_main = None; let mut found_extern_crate = crate_name.is_none(); let mut found_macro = false; - let mut parser = match new_parser_from_source_str(&psess, filename, source.clone()) { + let mut parser = match new_parser_from_source_str(&psess, filename, source.to_owned()) { Ok(p) => p, Err(errs) => { errs.into_iter().for_each(|err| err.cancel()); @@ -187,11 +221,11 @@ fn check_for_main_and_extern_crate( loop { match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => { - if !found_main + if found_main.is_none() && let ast::ItemKind::Fn(..) = item.kind && item.ident.name == sym::main { - found_main = true; + found_main = Some(item.span); } if !found_extern_crate @@ -211,7 +245,7 @@ fn check_for_main_and_extern_crate( found_macro = true; } - if found_main && found_extern_crate { + if found_main.is_some() && found_extern_crate { break; } } @@ -236,23 +270,26 @@ fn check_for_main_and_extern_crate( (found_main, found_extern_crate, found_macro) }) }); - let (mut already_has_main, already_has_extern_crate, found_macro) = result?; + let (mut main_fn_span, already_has_extern_crate, found_macro) = result?; // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't // see it. In that case, run the old text-based scan to see if they at least have a main // function written inside a macro invocation. See // https://github.com/rust-lang/rust/issues/56898 - if found_macro && !already_has_main { - already_has_main = source + if found_macro + && main_fn_span.is_none() + && source .lines() .map(|line| { let comment = line.find("//"); if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } }) - .any(|code| code.contains("fn main")); - }; + .any(|code| code.contains("fn main")) + { + main_fn_span = Some(DUMMY_SP); + } - Ok((already_has_main, already_has_extern_crate)) + Ok((main_fn_span, already_has_extern_crate)) } fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 0f13ee404c682..fc631de7d2a3f 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -1,8 +1,22 @@ use std::path::PathBuf; -use rustc_span::edition::DEFAULT_EDITION; +use super::{DocTest, GlobalTestOptions}; +use rustc_span::edition::{Edition, DEFAULT_EDITION}; -use super::{make_test, GlobalTestOptions}; +// FIXME: remove the last element of the returned tuple and simplify arguments of this helper. +fn make_test( + test_code: &str, + crate_name: Option<&str>, + dont_insert_main: bool, + opts: &GlobalTestOptions, + edition: Edition, + test_id: Option<&str>, +) -> (String, usize, ()) { + let doctest = DocTest::new(test_code, crate_name, edition); + let (code, line_offset) = + doctest.generate_unique_doctest(dont_insert_main, opts, test_id, crate_name); + (code, line_offset, ()) +} /// Default [`GlobalTestOptions`] for these unit tests. fn default_global_opts(crate_name: impl Into) -> GlobalTestOptions { diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index c41db654112eb..90ff71396d75b 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -297,10 +297,11 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { attrs: vec![], args_file: PathBuf::new(), }; - let (test, _, _) = doctest::make_test(&test, krate, false, &opts, edition, None); + let doctest = doctest::DocTest::new(&test, krate, edition); + let (test, _) = doctest.generate_unique_doctest(false, &opts, None, krate); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; - let test_escaped = small_url_encode(test); + let test_escaped = small_url_encode(doctest.test_code); Some(format!( " Date: Sat, 8 Jun 2024 16:35:33 +0200 Subject: [PATCH 123/178] Simplify doctest tests --- src/librustdoc/doctest/tests.rs | 51 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index fc631de7d2a3f..77322b59b653c 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -1,21 +1,19 @@ use std::path::PathBuf; use super::{DocTest, GlobalTestOptions}; -use rustc_span::edition::{Edition, DEFAULT_EDITION}; +use rustc_span::edition::DEFAULT_EDITION; -// FIXME: remove the last element of the returned tuple and simplify arguments of this helper. fn make_test( test_code: &str, crate_name: Option<&str>, dont_insert_main: bool, opts: &GlobalTestOptions, - edition: Edition, test_id: Option<&str>, -) -> (String, usize, ()) { - let doctest = DocTest::new(test_code, crate_name, edition); +) -> (String, usize) { + let doctest = DocTest::new(test_code, crate_name, DEFAULT_EDITION); let (code, line_offset) = doctest.generate_unique_doctest(dont_insert_main, opts, test_id, crate_name); - (code, line_offset, ()) + (code, line_offset) } /// Default [`GlobalTestOptions`] for these unit tests. @@ -39,7 +37,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, None, false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -54,7 +52,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("asdf"), false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -73,7 +71,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("asdf"), false, &opts, None); assert_eq!((output, len), (expected, 3)); } @@ -90,7 +88,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("asdf"), false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -108,7 +106,7 @@ use std::*; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("std"), false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -127,7 +125,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("asdf"), false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -144,7 +142,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("asdf"), false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -164,7 +162,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("asdf"), false, &opts, None); assert_eq!((output, len), (expected, 3)); // Adding more will also bump the returned line offset. @@ -178,7 +176,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("asdf"), false, &opts, None); assert_eq!((output, len), (expected, 4)); } @@ -195,7 +193,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, None, false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -211,7 +209,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, None, false, &opts, None); assert_eq!((output, len), (expected, 1)); } @@ -227,7 +225,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, None, false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -241,7 +239,7 @@ assert_eq!(2+2, 4);"; //Ceci n'est pas une `fn main` assert_eq!(2+2, 4);" .to_string(); - let (output, len, _) = make_test(input, None, true, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, None, true, &opts, None); assert_eq!((output, len), (expected, 1)); } @@ -259,7 +257,7 @@ assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, None, false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -279,7 +277,7 @@ assert_eq!(asdf::foo, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("asdf"), false, &opts, None); assert_eq!((output, len), (expected, 3)); } @@ -297,7 +295,7 @@ test_wrapper! { }" .to_string(); - let (output, len, _) = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, Some("my_crate"), false, &opts, None); assert_eq!((output, len), (expected, 1)); } @@ -317,7 +315,7 @@ io::stdin().read_line(&mut input)?; Ok::<(), io:Error>(()) } _inner().unwrap() }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, None, false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -331,8 +329,7 @@ fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() { assert_eq!(2+2, 4); } _doctest_main__some_unique_name() }" .to_string(); - let (output, len, _) = - make_test(input, None, false, &opts, DEFAULT_EDITION, Some("_some_unique_name")); + let (output, len) = make_test(input, None, false, &opts, Some("_some_unique_name")); assert_eq!((output, len), (expected, 2)); } @@ -351,7 +348,7 @@ fn main() { eprintln!(\"hello anan\"); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, None, false, &opts, None); assert_eq!((output, len), (expected, 2)); } @@ -371,6 +368,6 @@ fn main() { eprintln!(\"hello anan\"); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); + let (output, len) = make_test(input, None, false, &opts, None); assert_eq!((output, len), (expected, 1)); } From 39f029a852418952716cbd28f8d0d922584198e2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 8 Jun 2024 17:32:51 +0200 Subject: [PATCH 124/178] Split doctests between standalone and mergeable ones --- src/librustdoc/doctest.rs | 76 +++++++++++++++++++++--------- src/librustdoc/doctest/make.rs | 6 +-- src/librustdoc/doctest/markdown.rs | 2 +- src/librustdoc/doctest/tests.rs | 2 +- src/librustdoc/html/markdown.rs | 4 +- 5 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index f8f5744e4db43..65ddaedf26c5d 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -164,7 +164,8 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() let args_path = temp_dir.path().join("rustdoc-cfgs"); crate::wrap_return(dcx, generate_args_file(&args_path, &options))?; - let (tests, unused_extern_reports, compiling_test_count) = + // FIXME: use mergeable tests! + let (standalone_tests, unused_extern_reports, compiling_test_count) = interface::run_compiler(config, |compiler| { compiler.enter(|queries| { let collector = queries.global_ctxt()?.enter(|tcx| { @@ -192,11 +193,11 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() let unused_extern_reports = collector.unused_extern_reports.clone(); let compiling_test_count = collector.compiling_test_count.load(Ordering::SeqCst); - Ok((collector.tests, unused_extern_reports, compiling_test_count)) + Ok((collector.standalone_tests, unused_extern_reports, compiling_test_count)) }) })?; - run_tests(test_args, nocapture, tests); + run_tests(test_args, nocapture, standalone_tests); // Collect and warn about unused externs, but only if we've gotten // reports for each doctest @@ -617,7 +618,8 @@ pub(crate) trait DoctestVisitor { } struct CreateRunnableDoctests { - tests: Vec, + standalone_tests: Vec, + mergeable_tests: FxHashMap>, rustdoc_options: Arc, opts: GlobalTestOptions, @@ -629,7 +631,8 @@ struct CreateRunnableDoctests { impl CreateRunnableDoctests { fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests { CreateRunnableDoctests { - tests: Vec::new(), + standalone_tests: Vec::new(), + mergeable_tests: FxHashMap::default(), rustdoc_options: Arc::new(rustdoc_options), opts, visited_tests: FxHashMap::default(), @@ -647,16 +650,40 @@ impl CreateRunnableDoctests { format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly()) } - fn add_test(&mut self, test: ScrapedDoctest) { - let name = self.generate_name(&test.filename, test.line, &test.logical_path); + fn add_test(&mut self, scraped_test: ScrapedDoctest) { + let edition = scraped_test.edition(&self.rustdoc_options); + let doctest = DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition); + let is_standalone = scraped_test.langstr.compile_fail + || scraped_test.langstr.test_harness + || self.rustdoc_options.nocapture + || self.rustdoc_options.test_args.iter().any(|arg| arg == "--show-output") + || doctest.crate_attrs.contains("#![no_std]"); + if is_standalone { + let test_desc = self.generate_test_desc_and_fn(doctest, scraped_test); + self.standalone_tests.push(test_desc); + } else { + self.mergeable_tests.entry(edition).or_default().push((doctest, scraped_test)); + } + } + + fn generate_test_desc_and_fn( + &mut self, + test: DocTest, + scraped_test: ScrapedDoctest, + ) -> test::TestDescAndFn { + let name = self.generate_name( + &scraped_test.filename, + scraped_test.line, + &scraped_test.logical_path, + ); let opts = self.opts.clone(); let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - if !test.langstr.compile_fail { + if !scraped_test.langstr.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } - let path = match &test.filename { + let path = match &scraped_test.filename { FileName::Real(path) => { if let Some(local_path) = path.local_path() { local_path.to_path_buf() @@ -669,7 +696,7 @@ impl CreateRunnableDoctests { }; // For example `module/file.rs` would become `module_file_rs` - let file = test + let file = scraped_test .filename .prefer_local() .to_string_lossy() @@ -679,12 +706,12 @@ impl CreateRunnableDoctests { let test_id = format!( "{file}_{line}_{number}", file = file, - line = test.line, + line = scraped_test.line, number = { // Increases the current test number, if this file already // exists or it creates a new entry with a test number of 0. self.visited_tests - .entry((file.clone(), test.line)) + .entry((file.clone(), scraped_test.line)) .and_modify(|v| *v += 1) .or_insert(0) }, @@ -693,11 +720,11 @@ impl CreateRunnableDoctests { let rustdoc_options = self.rustdoc_options.clone(); let rustdoc_test_options = IndividualTestOptions::new(&self.rustdoc_options, test_id, path); - debug!("creating test {name}: {}", test.text); - self.tests.push(test::TestDescAndFn { + debug!("creating test {name}: {}", scraped_test.text); + test::TestDescAndFn { desc: test::TestDesc { name: test::DynTestName(name), - ignore: match test.langstr.ignore { + ignore: match scraped_test.langstr.ignore { Ignore::All => true, Ignore::None => false, Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)), @@ -710,20 +737,28 @@ impl CreateRunnableDoctests { end_col: 0, // compiler failures are test failures should_panic: test::ShouldPanic::No, - compile_fail: test.langstr.compile_fail, - no_run: test.no_run(&rustdoc_options), + compile_fail: scraped_test.langstr.compile_fail, + no_run: scraped_test.no_run(&rustdoc_options), test_type: test::TestType::DocTest, }, testfn: test::DynTestFn(Box::new(move || { - doctest_run_fn(rustdoc_test_options, opts, test, rustdoc_options, unused_externs) + doctest_run_fn( + rustdoc_test_options, + opts, + test, + scraped_test, + rustdoc_options, + unused_externs, + ) })), - }); + } } } fn doctest_run_fn( test_opts: IndividualTestOptions, global_opts: GlobalTestOptions, + doctest: DocTest, scraped_test: ScrapedDoctest, rustdoc_options: Arc, unused_externs: Arc>>, @@ -731,9 +766,8 @@ fn doctest_run_fn( let report_unused_externs = |uext| { unused_externs.lock().unwrap().push(uext); }; - let edition = scraped_test.edition(&rustdoc_options); - let doctest = DocTest::new(&scraped_test.text, Some(&global_opts.crate_name), edition); let (full_test_code, full_test_line_offset) = doctest.generate_unique_doctest( + &scraped_test.text, scraped_test.langstr.test_harness, &global_opts, Some(&test_opts.test_id), diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 3ba0f36ef7af1..759a3e31b2391 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -18,7 +18,6 @@ use rustc_span::{FileName, Span, DUMMY_SP}; use super::GlobalTestOptions; pub(crate) struct DocTest { - pub(crate) test_code: String, pub(crate) supports_color: bool, pub(crate) already_has_extern_crate: bool, pub(crate) main_fn_span: Option, @@ -40,7 +39,6 @@ impl DocTest { // If the parser panicked due to a fatal error, pass the test code through unchanged. // The error will be reported during compilation. return DocTest { - test_code: source.to_string(), supports_color: false, main_fn_span: None, crate_attrs, @@ -50,7 +48,6 @@ impl DocTest { }; }; Self { - test_code: source.to_string(), supports_color, main_fn_span, crate_attrs, @@ -64,6 +61,7 @@ impl DocTest { /// lines before the test code begins. pub(crate) fn generate_unique_doctest( &self, + test_code: &str, dont_insert_main: bool, opts: &GlobalTestOptions, // If `test_id` is `None`, it means we're generating code for a code example "run" link. @@ -103,7 +101,7 @@ impl DocTest { // NOTE: this is terribly inaccurate because it doesn't actually // parse the source, but only has false positives, not false // negatives. - self.test_code.contains(crate_name) + test_code.contains(crate_name) { // rustdoc implicitly inserts an `extern crate` item for the own crate // which may be unused, so we need to allow the lint. diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index b8ab7adb36e8c..ff2adffe5c281 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -120,6 +120,6 @@ pub(crate) fn test(options: Options) -> Result<(), String> { let mut collector = CreateRunnableDoctests::new(options.clone(), opts); md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); - crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); + crate::doctest::run_tests(options.test_args, options.nocapture, collector.standalone_tests); Ok(()) } diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 77322b59b653c..7c1cdaf8236e2 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -12,7 +12,7 @@ fn make_test( ) -> (String, usize) { let doctest = DocTest::new(test_code, crate_name, DEFAULT_EDITION); let (code, line_offset) = - doctest.generate_unique_doctest(dont_insert_main, opts, test_id, crate_name); + doctest.generate_unique_doctest(test_code, dont_insert_main, opts, test_id, crate_name); (code, line_offset) } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 90ff71396d75b..c1f5f3d7e2392 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -298,10 +298,10 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { args_file: PathBuf::new(), }; let doctest = doctest::DocTest::new(&test, krate, edition); - let (test, _) = doctest.generate_unique_doctest(false, &opts, None, krate); + let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, None, krate); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; - let test_escaped = small_url_encode(doctest.test_code); + let test_escaped = small_url_encode(test); Some(format!( " Date: Sat, 8 Jun 2024 22:55:52 +0200 Subject: [PATCH 125/178] Split standalone and mergeable doctests --- src/librustdoc/doctest.rs | 363 ++++++++++++++++++----------- src/librustdoc/doctest/make.rs | 19 +- src/librustdoc/doctest/markdown.rs | 8 +- src/librustdoc/doctest/runner.rs | 188 +++++++++++++++ src/librustdoc/doctest/rust.rs | 12 +- src/librustdoc/html/markdown.rs | 4 +- 6 files changed, 440 insertions(+), 154 deletions(-) create mode 100644 src/librustdoc/doctest/runner.rs diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 65ddaedf26c5d..f00aef491f5c0 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,5 +1,6 @@ mod make; mod markdown; +mod runner; mod rust; use std::fs::File; @@ -164,40 +165,54 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() let args_path = temp_dir.path().join("rustdoc-cfgs"); crate::wrap_return(dcx, generate_args_file(&args_path, &options))?; - // FIXME: use mergeable tests! - let (standalone_tests, unused_extern_reports, compiling_test_count) = - interface::run_compiler(config, |compiler| { - compiler.enter(|queries| { - let collector = queries.global_ctxt()?.enter(|tcx| { - let crate_name = tcx.crate_name(LOCAL_CRATE).to_string(); - let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); - let opts = scrape_test_config(crate_name, crate_attrs, args_path); - let enable_per_target_ignores = options.enable_per_target_ignores; - - let mut collector = CreateRunnableDoctests::new(options, opts); - let hir_collector = HirCollector::new( - &compiler.sess, - tcx.hir(), - ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), - enable_per_target_ignores, - tcx, - ); - let tests = hir_collector.collect_crate(); - tests.into_iter().for_each(|t| collector.add_test(t)); - - collector - }); - if compiler.sess.dcx().has_errors().is_some() { - FatalError.raise(); - } + let CreateRunnableDoctests { + standalone_tests, + mergeable_tests, + rustdoc_options, + opts, + unused_extern_reports, + compiling_test_count, + .. + } = interface::run_compiler(config, |compiler| { + compiler.enter(|queries| { + let collector = queries.global_ctxt()?.enter(|tcx| { + let crate_name = tcx.crate_name(LOCAL_CRATE).to_string(); + let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); + let opts = scrape_test_config(crate_name, crate_attrs, args_path); + let enable_per_target_ignores = options.enable_per_target_ignores; + + let mut collector = CreateRunnableDoctests::new(options, opts); + let hir_collector = HirCollector::new( + &compiler.sess, + tcx.hir(), + ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), + enable_per_target_ignores, + tcx, + ); + let tests = hir_collector.collect_crate(); + tests.into_iter().for_each(|t| collector.add_test(t)); + + collector + }); + if compiler.sess.dcx().has_errors().is_some() { + FatalError.raise(); + } - let unused_extern_reports = collector.unused_extern_reports.clone(); - let compiling_test_count = collector.compiling_test_count.load(Ordering::SeqCst); - Ok((collector.standalone_tests, unused_extern_reports, compiling_test_count)) - }) - })?; + Ok(collector) + }) + })?; + + run_tests( + test_args, + nocapture, + opts, + rustdoc_options, + &unused_extern_reports, + standalone_tests, + mergeable_tests, + ); - run_tests(test_args, nocapture, standalone_tests); + let compiling_test_count = compiling_test_count.load(Ordering::SeqCst); // Collect and warn about unused externs, but only if we've gotten // reports for each doctest @@ -243,14 +258,74 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() pub(crate) fn run_tests( mut test_args: Vec, nocapture: bool, - mut tests: Vec, + opts: GlobalTestOptions, + rustdoc_options: RustdocOptions, + unused_extern_reports: &Arc>>, + mut standalone_tests: Vec, + mut mergeable_tests: FxHashMap>, ) { test_args.insert(0, "rustdoctest".to_string()); if nocapture { test_args.push("--nocapture".to_string()); } - tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice())); - test::test_main(&test_args, tests, None); + + let mut nb_errors = 0; + + for (edition, mut doctests) in mergeable_tests { + if doctests.is_empty() { + continue; + } + doctests.sort_by(|(_, a), (_, b)| a.name.cmp(&b.name)); + let outdir = Arc::clone(&doctests[0].outdir); + + let mut tests_runner = runner::DocTestRunner::new(); + + let rustdoc_test_options = IndividualTestOptions::new( + &rustdoc_options, + format!("merged_doctest"), + PathBuf::from(r"doctest.rs"), + ); + + for (doctest, scraped_test) in &doctests { + tests_runner.add_test(doctest, scraped_test); + } + if let Ok(success) = + tests_runner.run_tests(rustdoc_test_options, edition, &opts, &test_args, &outdir) + { + if !success { + nb_errors += 1; + } + continue; + } else { + // We failed to compile all compatible tests as one so we push them into the + // `standalone_tests` doctests. + debug!("Failed to compile compatible doctests for edition {} all at once", edition); + for (doctest, scraped_test) in doctests { + doctest.generate_unique_doctest( + &scraped_test.text, + scraped_test.langstr.test_harness, + &opts, + Some(&opts.crate_name), + ); + standalone_tests.push(generate_test_desc_and_fn( + doctest, + scraped_test, + opts.clone(), + rustdoc_test_options.clone(), + unused_extern_reports.clone(), + )); + } + } + } + + if !standalone_tests.is_empty() { + standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice())); + test::test_main(&test_args, standalone_tests, None); + } + if nb_errors != 0 { + // libtest::ERROR_EXIT_CODE is not public but it's the same value. + std::process::exit(101); + } } // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade. @@ -365,7 +440,10 @@ struct RunnableDoctest { full_test_line_offset: usize, test_opts: IndividualTestOptions, global_opts: GlobalTestOptions, - scraped_test: ScrapedDoctest, + langstr: LangString, + line: usize, + edition: Edition, + no_run: bool, } fn run_test( @@ -374,8 +452,7 @@ fn run_test( supports_color: bool, report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { - let scraped_test = &doctest.scraped_test; - let langstr = &scraped_test.langstr; + let langstr = &doctest.langstr; // Make sure we emit well-formed executable names for our target. let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); let output_file = doctest.test_opts.outdir.path().join(rust_out); @@ -392,11 +469,11 @@ fn run_test( compiler.arg(format!("--sysroot={}", sysroot.display())); } - compiler.arg("--edition").arg(&scraped_test.edition(rustdoc_options).to_string()); + compiler.arg("--edition").arg(&doctest.edition.to_string()); compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); compiler.env( "UNSTABLE_RUSTDOC_TEST_LINE", - format!("{}", scraped_test.line as isize - doctest.full_test_line_offset as isize), + format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize), ); compiler.arg("-o").arg(&output_file); if langstr.test_harness { @@ -409,10 +486,7 @@ fn run_test( compiler.arg("-Z").arg("unstable-options"); } - if scraped_test.no_run(rustdoc_options) - && !langstr.compile_fail - && rustdoc_options.persist_doctests.is_none() - { + if doctest.no_run && !langstr.compile_fail && rustdoc_options.persist_doctests.is_none() { // FIXME: why does this code check if it *shouldn't* persist doctests // -- shouldn't it be the negation? compiler.arg("--emit=metadata"); @@ -493,8 +567,7 @@ fn run_test( // We used to check if the output contained "error[{}]: " but since we added the // colored output, we can't anymore because of the color escape characters before // the ":". - let missing_codes: Vec = scraped_test - .langstr + let missing_codes: Vec = langstr .error_codes .iter() .filter(|err| !out.contains(&format!("error[{err}]"))) @@ -511,7 +584,7 @@ fn run_test( } } - if scraped_test.no_run(rustdoc_options) { + if doctest.no_run { return Ok(()); } @@ -600,9 +673,27 @@ struct ScrapedDoctest { logical_path: Vec, langstr: LangString, text: String, + name: String, } impl ScrapedDoctest { + fn new( + filename: FileName, + line: usize, + logical_path: Vec, + langstr: LangString, + text: String, + ) -> Self { + let mut item_path = logical_path.join("::"); + item_path.retain(|c| c != ' '); + if !item_path.is_empty() { + item_path.push(' '); + } + let name = + format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly()); + + Self { filename, line, logical_path, langstr, text, name } + } fn edition(&self, opts: &RustdocOptions) -> Edition { self.langstr.edition.unwrap_or(opts.edition) } @@ -641,18 +732,32 @@ impl CreateRunnableDoctests { } } - fn generate_name(&self, filename: &FileName, line: usize, logical_path: &[String]) -> String { - let mut item_path = logical_path.join("::"); - item_path.retain(|c| c != ' '); - if !item_path.is_empty() { - item_path.push(' '); - } - format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly()) - } - fn add_test(&mut self, scraped_test: ScrapedDoctest) { + // For example `module/file.rs` would become `module_file_rs` + let file = scraped_test + .filename + .prefer_local() + .to_string_lossy() + .chars() + .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' }) + .collect::(); + let test_id = format!( + "{file}_{line}_{number}", + file = file, + line = scraped_test.line, + number = { + // Increases the current test number, if this file already + // exists or it creates a new entry with a test number of 0. + self.visited_tests + .entry((file.clone(), scraped_test.line)) + .and_modify(|v| *v += 1) + .or_insert(0) + }, + ); + let edition = scraped_test.edition(&self.rustdoc_options); - let doctest = DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition); + let doctest = + DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition, test_id); let is_standalone = scraped_test.langstr.compile_fail || scraped_test.langstr.test_harness || self.rustdoc_options.nocapture @@ -671,87 +776,77 @@ impl CreateRunnableDoctests { test: DocTest, scraped_test: ScrapedDoctest, ) -> test::TestDescAndFn { - let name = self.generate_name( - &scraped_test.filename, - scraped_test.line, - &scraped_test.logical_path, - ); - let opts = self.opts.clone(); - let target_str = self.rustdoc_options.target.to_string(); - let unused_externs = self.unused_extern_reports.clone(); if !scraped_test.langstr.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } - let path = match &scraped_test.filename { - FileName::Real(path) => { - if let Some(local_path) = path.local_path() { - local_path.to_path_buf() - } else { - // Somehow we got the filename from the metadata of another crate, should never happen - unreachable!("doctest from a different crate"); - } - } - _ => PathBuf::from(r"doctest.rs"), - }; + generate_test_desc_and_fn( + test, + scraped_test, + self.opts.clone(), + self.rustdoc_options.clone(), + self.unused_extern_reports.clone(), + ) + } +} - // For example `module/file.rs` would become `module_file_rs` - let file = scraped_test - .filename - .prefer_local() - .to_string_lossy() - .chars() - .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' }) - .collect::(); - let test_id = format!( - "{file}_{line}_{number}", - file = file, - line = scraped_test.line, - number = { - // Increases the current test number, if this file already - // exists or it creates a new entry with a test number of 0. - self.visited_tests - .entry((file.clone(), scraped_test.line)) - .and_modify(|v| *v += 1) - .or_insert(0) - }, - ); +fn generate_test_desc_and_fn( + test: DocTest, + scraped_test: ScrapedDoctest, + opts: GlobalTestOptions, + rustdoc_options: IndividualTestOptions, + unused_externs: Arc>>, +) -> test::TestDescAndFn { + let target_str = rustdoc_options.target.to_string(); - let rustdoc_options = self.rustdoc_options.clone(); - let rustdoc_test_options = IndividualTestOptions::new(&self.rustdoc_options, test_id, path); - - debug!("creating test {name}: {}", scraped_test.text); - test::TestDescAndFn { - desc: test::TestDesc { - name: test::DynTestName(name), - ignore: match scraped_test.langstr.ignore { - Ignore::All => true, - Ignore::None => false, - Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)), - }, - ignore_message: None, - source_file: "", - start_line: 0, - start_col: 0, - end_line: 0, - end_col: 0, - // compiler failures are test failures - should_panic: test::ShouldPanic::No, - compile_fail: scraped_test.langstr.compile_fail, - no_run: scraped_test.no_run(&rustdoc_options), - test_type: test::TestType::DocTest, - }, - testfn: test::DynTestFn(Box::new(move || { - doctest_run_fn( - rustdoc_test_options, - opts, - test, - scraped_test, - rustdoc_options, - unused_externs, - ) - })), + let path = match &scraped_test.filename { + FileName::Real(path) => { + if let Some(local_path) = path.local_path() { + local_path.to_path_buf() + } else { + // Somehow we got the filename from the metadata of another crate, should never happen + unreachable!("doctest from a different crate"); + } } + _ => PathBuf::from(r"doctest.rs"), + }; + + let name = &test.name; + let rustdoc_test_options = + IndividualTestOptions::new(&rustdoc_options, test.test_id.clone(), path); + // let rustdoc_options_clone = rustdoc_options.clone(); + + debug!("creating test {name}: {}", scraped_test.text); + test::TestDescAndFn { + desc: test::TestDesc { + name: test::DynTestName(name), + ignore: match scraped_test.langstr.ignore { + Ignore::All => true, + Ignore::None => false, + Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)), + }, + ignore_message: None, + source_file: "", + start_line: 0, + start_col: 0, + end_line: 0, + end_col: 0, + // compiler failures are test failures + should_panic: test::ShouldPanic::No, + compile_fail: scraped_test.langstr.compile_fail, + no_run: scraped_test.no_run(&rustdoc_options), + test_type: test::TestType::DocTest, + }, + testfn: test::DynTestFn(Box::new(move || { + doctest_run_fn( + rustdoc_test_options, + opts, + test, + scraped_test, + rustdoc_options, + unused_externs, + ) + })), } } @@ -770,7 +865,6 @@ fn doctest_run_fn( &scraped_test.text, scraped_test.langstr.test_harness, &global_opts, - Some(&test_opts.test_id), Some(&global_opts.crate_name), ); let runnable_test = RunnableDoctest { @@ -778,7 +872,10 @@ fn doctest_run_fn( full_test_line_offset, test_opts, global_opts, - scraped_test, + langstr: scraped_test.langstr.clone(), + line: scraped_test.line, + edition: scraped_test.edition(&rustdoc_options), + no_run: scraped_test.no_run(&rustdoc_options), }; let res = run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs); diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 759a3e31b2391..c1d1e45ff042e 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -24,10 +24,17 @@ pub(crate) struct DocTest { pub(crate) crate_attrs: String, pub(crate) crates: String, pub(crate) everything_else: String, + pub(crate) test_id: Option, } impl DocTest { - pub(crate) fn new(source: &str, crate_name: Option<&str>, edition: Edition) -> Self { + pub(crate) fn new( + source: &str, + crate_name: Option<&str>, + edition: Edition, + // If `test_id` is `None`, it means we're generating code for a code example "run" link. + test_id: Option, + ) -> Self { let (crate_attrs, everything_else, crates) = partition_source(source, edition); let mut supports_color = false; @@ -45,6 +52,7 @@ impl DocTest { crates, everything_else, already_has_extern_crate: false, + test_id, }; }; Self { @@ -54,6 +62,7 @@ impl DocTest { crates, everything_else, already_has_extern_crate, + test_id, } } @@ -64,8 +73,6 @@ impl DocTest { test_code: &str, dont_insert_main: bool, opts: &GlobalTestOptions, - // If `test_id` is `None`, it means we're generating code for a code example "run" link. - test_id: Option<&str>, crate_name: Option<&str>, ) -> (String, usize) { let mut line_offset = 0; @@ -118,12 +125,12 @@ impl DocTest { let returns_result = everything_else.ends_with("(())"); // Give each doctest main function a unique name. // This is for example needed for the tooling around `-C instrument-coverage`. - let inner_fn_name = if let Some(test_id) = test_id { + let inner_fn_name = if let Some(ref test_id) = self.test_id { format!("_doctest_main_{test_id}") } else { "_inner".into() }; - let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; + let inner_attr = if self.test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; let (main_pre, main_post) = if returns_result { ( format!( @@ -131,7 +138,7 @@ impl DocTest { ), format!("\n}} {inner_fn_name}().unwrap() }}"), ) - } else if test_id.is_some() { + } else if self.test_id.is_some() { ( format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",), format!("\n}} {inner_fn_name}() }}"), diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index ff2adffe5c281..a5514857fff02 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -22,13 +22,7 @@ impl DoctestVisitor for MdCollector { let filename = self.filename.clone(); // First line of Markdown is line 1. let line = 1 + rel_line.offset(); - self.tests.push(ScrapedDoctest { - filename, - line, - logical_path: self.cur_path.clone(), - langstr: config, - text: test, - }); + self.tests.push(ScrapedDoctest::new(filename, line, self.cur_path.clone(), config, test)); } fn visit_header(&mut self, name: &str, level: u32) { diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs new file mode 100644 index 0000000000000..a672bb1bd9ba1 --- /dev/null +++ b/src/librustdoc/doctest/runner.rs @@ -0,0 +1,188 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_span::edition::Edition; + +use std::fmt::Write; +use std::sync::{Arc, Mutex}; + +use crate::doctest::{ + run_test, DirState, DocTest, GlobalTestOptions, IndividualTestOptions, RunnableDoctest, + RustdocOptions, ScrapedDoctest, TestFailure, UnusedExterns, +}; +use crate::html::markdown::LangString; + +/// Convenient type to merge compatible doctests into one. +pub(crate) struct DocTestRunner { + crate_attrs: FxHashSet, + ids: String, + output: String, + supports_color: bool, + nb_tests: usize, + doctests: Vec, +} + +impl DocTestRunner { + pub(crate) fn new() -> Self { + Self { + crate_attrs: FxHashSet::default(), + ids: String::new(), + output: String::new(), + supports_color: true, + nb_tests: 0, + doctests: Vec::with_capacity(10), + } + } + + pub(crate) fn add_test(&mut self, doctest: &DocTest, scraped_test: &ScrapedDoctest) { + if !doctest.ignore { + for line in doctest.crate_attrs.split('\n') { + self.crate_attrs.insert(line.to_string()); + } + } + if !self.ids.is_empty() { + self.ids.push(','); + } + self.ids.push_str(&format!( + "{}::TEST", + generate_mergeable_doctest(doctest, scraped_test, self.nb_tests, &mut self.output), + )); + self.supports_color &= doctest.supports_color; + self.nb_tests += 1; + self.doctests.push(doctest); + } + + pub(crate) fn run_tests( + &mut self, + test_options: IndividualTestOptions, + edition: Edition, + opts: &GlobalTestOptions, + test_args: &[String], + outdir: &Arc, + rustdoc_options: &RustdocOptions, + unused_externs: Arc>>, + ) -> Result { + let mut code = "\ +#![allow(unused_extern_crates)] +#![allow(internal_features)] +#![feature(test)] +#![feature(rustc_attrs)] +#![feature(coverage_attribute)]\n" + .to_string(); + + for crate_attr in &self.crate_attrs { + code.push_str(crate_attr); + code.push('\n'); + } + + DocTest::push_attrs(&mut code, opts, &mut 0); + code.push_str("extern crate test;\n"); + + let test_args = + test_args.iter().map(|arg| format!("{arg:?}.to_string(),")).collect::(); + write!( + code, + "\ +{output} +#[rustc_main] +#[coverage(off)] +fn main() {{ +test::test_main(&[{test_args}], vec![{ids}], None); +}}", + output = self.output, + ids = self.ids, + ) + .expect("failed to generate test code"); + // let out_dir = build_test_dir(outdir, true, ""); + let runnable_test = RunnableDoctest { + full_test_code: code, + full_test_line_offset: 0, + test_opts: test_options, + global_opts: opts.clone(), + langstr: LangString::default(), + line: 0, + edition, + no_run: false, + }; + let ret = run_test(runnable_test, rustdoc_options, self.supports_color, unused_externs); + if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) } + } +} + +/// Push new doctest content into `output`. Returns the test ID for this doctest. +fn generate_mergeable_doctest( + doctest: &DocTest, + scraped_test: &ScrapedDoctest, + id: usize, + output: &mut String, +) -> String { + let test_id = format!("__doctest_{id}"); + + if doctest.ignore { + // We generate nothing else. + writeln!(output, "mod {test_id} {{\n").unwrap(); + } else { + writeln!(output, "mod {test_id} {{\n{}", doctest.crates).unwrap(); + if doctest.main_fn_span.is_some() { + output.push_str(&doctest.everything_else); + } else { + let returns_result = if doctest.everything_else.trim_end().ends_with("(())") { + "-> Result<(), impl core::fmt::Debug>" + } else { + "" + }; + write!( + output, + "\ + fn main() {returns_result} {{ + {} + }}", + doctest.everything_else + ) + .unwrap(); + } + } + writeln!( + output, + " +#[rustc_test_marker = {test_name:?}] +pub const TEST: test::TestDescAndFn = test::TestDescAndFn {{ + desc: test::TestDesc {{ + name: test::StaticTestName({test_name:?}), + ignore: {ignore}, + ignore_message: None, + source_file: {file:?}, + start_line: {line}, + start_col: 0, + end_line: 0, + end_col: 0, + compile_fail: false, + no_run: {no_run}, + should_panic: test::ShouldPanic::{should_panic}, + test_type: test::TestType::UnitTest, + }}, + testfn: test::StaticTestFn( + #[coverage(off)] + || test::assert_test_result({runner}), + ) +}}; +}}", + test_name = scraped_test.name, + ignore = scraped_test.langstr.ignore, + file = scraped_test.file, + line = scraped_test.line, + no_run = scraped_test.langstr.no_run, + should_panic = if !scraped_test.langstr.no_run && scraped_test.langstr.should_panic { + "Yes" + } else { + "No" + }, + // Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply + // don't give it the function to run. + runner = if scraped_test.langstr.no_run || scraped_test.langstr.ignore { + "Ok::<(), String>(())" + } else { + "self::main()" + }, + ) + .unwrap(); + test_id +} diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index f179f3aa1c99b..17c29ba413a4a 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -51,13 +51,13 @@ impl RustCollector { impl DoctestVisitor for RustCollector { fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) { let line = self.get_base_line() + rel_line.offset(); - self.tests.push(ScrapedDoctest { - filename: self.get_filename(), + self.tests.push(ScrapedDoctest::new( + self.get_filename(), line, - logical_path: self.cur_path.clone(), - langstr: config, - text: test, - }); + self.cur_path.clone(), + config, + test, + )); } fn visit_header(&mut self, _name: &str, _level: u32) {} diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index c1f5f3d7e2392..a268a2d704e54 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -297,8 +297,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { attrs: vec![], args_file: PathBuf::new(), }; - let doctest = doctest::DocTest::new(&test, krate, edition); - let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, None, krate); + let doctest = doctest::DocTest::new(&test, krate, edition, None); + let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; let test_escaped = small_url_encode(test); From 6ae35248358f1e9ac4e2535ceeb160dc1927a720 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jun 2024 15:31:19 +0200 Subject: [PATCH 126/178] Split doctests into two categories: mergeable ones and standalone ones --- src/librustdoc/doctest.rs | 83 ++++++++++++++++-------------- src/librustdoc/doctest/markdown.rs | 13 ++++- src/librustdoc/doctest/runner.rs | 60 ++++++++++++++------- src/librustdoc/doctest/tests.rs | 5 +- 4 files changed, 99 insertions(+), 62 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index f00aef491f5c0..bfe5c9767b929 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -206,7 +206,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() test_args, nocapture, opts, - rustdoc_options, + &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests, @@ -259,10 +259,10 @@ pub(crate) fn run_tests( mut test_args: Vec, nocapture: bool, opts: GlobalTestOptions, - rustdoc_options: RustdocOptions, + rustdoc_options: &Arc, unused_extern_reports: &Arc>>, mut standalone_tests: Vec, - mut mergeable_tests: FxHashMap>, + mergeable_tests: FxHashMap>, ) { test_args.insert(0, "rustdoctest".to_string()); if nocapture { @@ -270,28 +270,32 @@ pub(crate) fn run_tests( } let mut nb_errors = 0; + let target_str = rustdoc_options.target.to_string(); for (edition, mut doctests) in mergeable_tests { if doctests.is_empty() { continue; } doctests.sort_by(|(_, a), (_, b)| a.name.cmp(&b.name)); - let outdir = Arc::clone(&doctests[0].outdir); let mut tests_runner = runner::DocTestRunner::new(); let rustdoc_test_options = IndividualTestOptions::new( &rustdoc_options, - format!("merged_doctest"), - PathBuf::from(r"doctest.rs"), + &format!("merged_doctest_{edition}"), + PathBuf::from(format!("doctest_{edition}.rs")), ); for (doctest, scraped_test) in &doctests { - tests_runner.add_test(doctest, scraped_test); + tests_runner.add_test(doctest, scraped_test, &target_str); } - if let Ok(success) = - tests_runner.run_tests(rustdoc_test_options, edition, &opts, &test_args, &outdir) - { + if let Ok(success) = tests_runner.run_tests( + rustdoc_test_options, + edition, + &opts, + &test_args, + rustdoc_options, + ) { if !success { nb_errors += 1; } @@ -311,7 +315,7 @@ pub(crate) fn run_tests( doctest, scraped_test, opts.clone(), - rustdoc_test_options.clone(), + Arc::clone(&rustdoc_options), unused_extern_reports.clone(), )); } @@ -406,7 +410,7 @@ impl DirState { // We could unify this struct the one in rustc but they have different // ownership semantics, so doing so would create wasteful allocations. #[derive(serde::Serialize, serde::Deserialize)] -struct UnusedExterns { +pub(crate) struct UnusedExterns { /// Lint level of the unused_crate_dependencies lint lint_level: String, /// List of unused externs by their names. @@ -642,12 +646,11 @@ fn make_maybe_absolute_path(path: PathBuf) -> PathBuf { } struct IndividualTestOptions { outdir: DirState, - test_id: String, path: PathBuf, } impl IndividualTestOptions { - fn new(options: &RustdocOptions, test_id: String, test_path: PathBuf) -> Self { + fn new(options: &RustdocOptions, test_id: &str, test_path: PathBuf) -> Self { let outdir = if let Some(ref path) = options.persist_doctests { let mut path = path.clone(); path.push(&test_id); @@ -662,15 +665,14 @@ impl IndividualTestOptions { DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir")) }; - Self { outdir, test_id, path: test_path } + Self { outdir, path: test_path } } } /// A doctest scraped from the code, ready to be turned into a runnable test. -struct ScrapedDoctest { +pub(crate) struct ScrapedDoctest { filename: FileName, line: usize, - logical_path: Vec, langstr: LangString, text: String, name: String, @@ -692,7 +694,7 @@ impl ScrapedDoctest { let name = format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly()); - Self { filename, line, logical_path, langstr, text, name } + Self { filename, line, langstr, text, name } } fn edition(&self, opts: &RustdocOptions) -> Edition { self.langstr.edition.unwrap_or(opts.edition) @@ -701,6 +703,19 @@ impl ScrapedDoctest { fn no_run(&self, opts: &RustdocOptions) -> bool { self.langstr.no_run || opts.no_run } + fn path(&self) -> PathBuf { + match &self.filename { + FileName::Real(path) => { + if let Some(local_path) = path.local_path() { + local_path.to_path_buf() + } else { + // Somehow we got the filename from the metadata of another crate, should never happen + unreachable!("doctest from a different crate"); + } + } + _ => PathBuf::from(r"doctest.rs"), + } + } } pub(crate) trait DoctestVisitor { @@ -757,7 +772,7 @@ impl CreateRunnableDoctests { let edition = scraped_test.edition(&self.rustdoc_options); let doctest = - DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition, test_id); + DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition, Some(test_id)); let is_standalone = scraped_test.langstr.compile_fail || scraped_test.langstr.test_harness || self.rustdoc_options.nocapture @@ -784,7 +799,7 @@ impl CreateRunnableDoctests { test, scraped_test, self.opts.clone(), - self.rustdoc_options.clone(), + Arc::clone(&self.rustdoc_options), self.unused_extern_reports.clone(), ) } @@ -794,32 +809,20 @@ fn generate_test_desc_and_fn( test: DocTest, scraped_test: ScrapedDoctest, opts: GlobalTestOptions, - rustdoc_options: IndividualTestOptions, + rustdoc_options: Arc, unused_externs: Arc>>, ) -> test::TestDescAndFn { let target_str = rustdoc_options.target.to_string(); + let rustdoc_test_options = IndividualTestOptions::new( + &rustdoc_options, + test.test_id.as_deref().unwrap_or_else(|| ""), + scraped_test.path(), + ); - let path = match &scraped_test.filename { - FileName::Real(path) => { - if let Some(local_path) = path.local_path() { - local_path.to_path_buf() - } else { - // Somehow we got the filename from the metadata of another crate, should never happen - unreachable!("doctest from a different crate"); - } - } - _ => PathBuf::from(r"doctest.rs"), - }; - - let name = &test.name; - let rustdoc_test_options = - IndividualTestOptions::new(&rustdoc_options, test.test_id.clone(), path); - // let rustdoc_options_clone = rustdoc_options.clone(); - - debug!("creating test {name}: {}", scraped_test.text); + debug!("creating test {}: {}", scraped_test.name, scraped_test.text); test::TestDescAndFn { desc: test::TestDesc { - name: test::DynTestName(name), + name: test::DynTestName(scraped_test.name.clone()), ignore: match scraped_test.langstr.ignore { Ignore::All => true, Ignore::None => false, diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index a5514857fff02..5f821634a82d2 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -1,6 +1,7 @@ //! Doctest functionality used only for doctests in `.md` Markdown files. use std::fs::read_to_string; +use std::sync::{Arc, Mutex}; use rustc_span::FileName; use tempfile::tempdir; @@ -114,6 +115,16 @@ pub(crate) fn test(options: Options) -> Result<(), String> { let mut collector = CreateRunnableDoctests::new(options.clone(), opts); md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); - crate::doctest::run_tests(options.test_args, options.nocapture, collector.standalone_tests); + let CreateRunnableDoctests { opts, rustdoc_options, standalone_tests, mergeable_tests, .. } = + collector; + crate::doctest::run_tests( + options.test_args, + options.nocapture, + opts, + &rustdoc_options, + &Arc::new(Mutex::new(Vec::new())), + standalone_tests, + mergeable_tests, + ); Ok(()) } diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index a672bb1bd9ba1..9bad83d4669dc 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -2,13 +2,12 @@ use rustc_data_structures::fx::FxHashSet; use rustc_span::edition::Edition; use std::fmt::Write; -use std::sync::{Arc, Mutex}; use crate::doctest::{ - run_test, DirState, DocTest, GlobalTestOptions, IndividualTestOptions, RunnableDoctest, - RustdocOptions, ScrapedDoctest, TestFailure, UnusedExterns, + run_test, DocTest, GlobalTestOptions, IndividualTestOptions, RunnableDoctest, RustdocOptions, + ScrapedDoctest, TestFailure, UnusedExterns, }; -use crate::html::markdown::LangString; +use crate::html::markdown::{Ignore, LangString}; /// Convenient type to merge compatible doctests into one. pub(crate) struct DocTestRunner { @@ -17,7 +16,6 @@ pub(crate) struct DocTestRunner { output: String, supports_color: bool, nb_tests: usize, - doctests: Vec, } impl DocTestRunner { @@ -28,12 +26,21 @@ impl DocTestRunner { output: String::new(), supports_color: true, nb_tests: 0, - doctests: Vec::with_capacity(10), } } - pub(crate) fn add_test(&mut self, doctest: &DocTest, scraped_test: &ScrapedDoctest) { - if !doctest.ignore { + pub(crate) fn add_test( + &mut self, + doctest: &DocTest, + scraped_test: &ScrapedDoctest, + target_str: &str, + ) { + let ignore = match scraped_test.langstr.ignore { + Ignore::All => true, + Ignore::None => false, + Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)), + }; + if !ignore { for line in doctest.crate_attrs.split('\n') { self.crate_attrs.insert(line.to_string()); } @@ -43,11 +50,16 @@ impl DocTestRunner { } self.ids.push_str(&format!( "{}::TEST", - generate_mergeable_doctest(doctest, scraped_test, self.nb_tests, &mut self.output), + generate_mergeable_doctest( + doctest, + scraped_test, + ignore, + self.nb_tests, + &mut self.output + ), )); self.supports_color &= doctest.supports_color; self.nb_tests += 1; - self.doctests.push(doctest); } pub(crate) fn run_tests( @@ -56,9 +68,7 @@ impl DocTestRunner { edition: Edition, opts: &GlobalTestOptions, test_args: &[String], - outdir: &Arc, rustdoc_options: &RustdocOptions, - unused_externs: Arc>>, ) -> Result { let mut code = "\ #![allow(unused_extern_crates)] @@ -73,7 +83,19 @@ impl DocTestRunner { code.push('\n'); } - DocTest::push_attrs(&mut code, opts, &mut 0); + if opts.attrs.is_empty() { + // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some + // lints that are commonly triggered in doctests. The crate-level test attributes are + // commonly used to make tests fail in case they trigger warnings, so having this there in + // that case may cause some tests to pass when they shouldn't have. + code.push_str("#![allow(unused)]\n"); + } + + // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. + for attr in &opts.attrs { + code.push_str(&format!("#![{attr}]\n")); + } + code.push_str("extern crate test;\n"); let test_args = @@ -91,7 +113,6 @@ test::test_main(&[{test_args}], vec![{ids}], None); ids = self.ids, ) .expect("failed to generate test code"); - // let out_dir = build_test_dir(outdir, true, ""); let runnable_test = RunnableDoctest { full_test_code: code, full_test_line_offset: 0, @@ -102,7 +123,8 @@ test::test_main(&[{test_args}], vec![{ids}], None); edition, no_run: false, }; - let ret = run_test(runnable_test, rustdoc_options, self.supports_color, unused_externs); + let ret = + run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {}); if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) } } } @@ -111,12 +133,13 @@ test::test_main(&[{test_args}], vec![{ids}], None); fn generate_mergeable_doctest( doctest: &DocTest, scraped_test: &ScrapedDoctest, + ignore: bool, id: usize, output: &mut String, ) -> String { let test_id = format!("__doctest_{id}"); - if doctest.ignore { + if ignore { // We generate nothing else. writeln!(output, "mod {test_id} {{\n").unwrap(); } else { @@ -166,8 +189,7 @@ pub const TEST: test::TestDescAndFn = test::TestDescAndFn {{ }}; }}", test_name = scraped_test.name, - ignore = scraped_test.langstr.ignore, - file = scraped_test.file, + file = scraped_test.path(), line = scraped_test.line, no_run = scraped_test.langstr.no_run, should_panic = if !scraped_test.langstr.no_run && scraped_test.langstr.should_panic { @@ -177,7 +199,7 @@ pub const TEST: test::TestDescAndFn = test::TestDescAndFn {{ }, // Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply // don't give it the function to run. - runner = if scraped_test.langstr.no_run || scraped_test.langstr.ignore { + runner = if ignore || scraped_test.langstr.no_run { "Ok::<(), String>(())" } else { "self::main()" diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 7c1cdaf8236e2..533fc3a56eddd 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -10,9 +10,10 @@ fn make_test( opts: &GlobalTestOptions, test_id: Option<&str>, ) -> (String, usize) { - let doctest = DocTest::new(test_code, crate_name, DEFAULT_EDITION); + let doctest = + DocTest::new(test_code, crate_name, DEFAULT_EDITION, test_id.map(|s| s.to_string())); let (code, line_offset) = - doctest.generate_unique_doctest(test_code, dont_insert_main, opts, test_id, crate_name); + doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name); (code, line_offset) } From 58cd70e062566eac07987fd1351599fb82c09098 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jun 2024 15:51:35 +0200 Subject: [PATCH 127/178] Only merge doctests starting 2024 edition --- src/librustdoc/doctest.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index bfe5c9767b929..aba2af35f7b98 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -732,10 +732,12 @@ struct CreateRunnableDoctests { visited_tests: FxHashMap<(String, usize), usize>, unused_extern_reports: Arc>>, compiling_test_count: AtomicUsize, + can_merge_doctests: bool, } impl CreateRunnableDoctests { fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests { + let can_merge_doctests = rustdoc_options.edition >= Edition::Edition2024; CreateRunnableDoctests { standalone_tests: Vec::new(), mergeable_tests: FxHashMap::default(), @@ -744,6 +746,7 @@ impl CreateRunnableDoctests { visited_tests: FxHashMap::default(), unused_extern_reports: Default::default(), compiling_test_count: AtomicUsize::new(0), + can_merge_doctests, } } @@ -773,7 +776,8 @@ impl CreateRunnableDoctests { let edition = scraped_test.edition(&self.rustdoc_options); let doctest = DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition, Some(test_id)); - let is_standalone = scraped_test.langstr.compile_fail + let is_standalone = !self.can_merge_doctests + || scraped_test.langstr.compile_fail || scraped_test.langstr.test_harness || self.rustdoc_options.nocapture || self.rustdoc_options.test_args.iter().any(|arg| arg == "--show-output") From 3147520d3475a816e21b6b352bfd8c2b38ab05f5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jun 2024 15:40:17 +0200 Subject: [PATCH 128/178] Add new doc codeblock `standalone` attribute --- src/librustdoc/doctest.rs | 1 + src/librustdoc/html/markdown.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index aba2af35f7b98..250cfa0d4c59a 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -779,6 +779,7 @@ impl CreateRunnableDoctests { let is_standalone = !self.can_merge_doctests || scraped_test.langstr.compile_fail || scraped_test.langstr.test_harness + || scraped_test.langstr.standalone || self.rustdoc_options.nocapture || self.rustdoc_options.test_args.iter().any(|arg| arg == "--show-output") || doctest.crate_attrs.contains("#![no_std]"); diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index a268a2d704e54..da6aa47fff262 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -869,6 +869,7 @@ pub(crate) struct LangString { pub(crate) rust: bool, pub(crate) test_harness: bool, pub(crate) compile_fail: bool, + pub(crate) standalone: bool, pub(crate) error_codes: Vec, pub(crate) edition: Option, pub(crate) added_classes: Vec, @@ -1191,6 +1192,7 @@ impl Default for LangString { rust: true, test_harness: false, compile_fail: false, + standalone: false, error_codes: Vec::new(), edition: None, added_classes: Vec::new(), @@ -1260,6 +1262,10 @@ impl LangString { seen_rust_tags = !seen_other_tags || seen_rust_tags; data.no_run = true; } + LangStringToken::LangToken("standalone") => { + data.standalone = true; + seen_rust_tags = !seen_other_tags || seen_rust_tags; + } LangStringToken::LangToken(x) if x.starts_with("edition") => { data.edition = x[7..].parse::().ok(); } From 23badff4fef474232f7ad02c3380035435f1a1cf Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 30 Apr 2024 17:45:09 +0200 Subject: [PATCH 129/178] Add documentation for the doctest `standalone` attribute --- .../documentation-tests.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/doc/rustdoc/src/write-documentation/documentation-tests.md b/src/doc/rustdoc/src/write-documentation/documentation-tests.md index 9526f33359e4b..7ed2e9720fed5 100644 --- a/src/doc/rustdoc/src/write-documentation/documentation-tests.md +++ b/src/doc/rustdoc/src/write-documentation/documentation-tests.md @@ -376,6 +376,57 @@ that the code sample should be compiled using the respective edition of Rust. # fn foo() {} ``` +Starting in the 2024 edition[^edition-note], compatible doctests are merged as one before being +run. We combine doctests for performance reasons: the slowest part of doctests is to compile them. +Merging all of them into one file and compiling this new file, then running the doctests is much +faster. Whether doctests are merged or not, they are run in their own process. + +An example of time spent when running doctests: + +[sysinfo crate](https://crates.io/crates/sysinfo): + +```text +wall-time duration: 4.59s +total compile time: 27.067s +total runtime: 3.969s +``` + +Rust core library: + +```text +wall-time duration: 102s +total compile time: 775.204s +total runtime: 15.487s +``` + +[^edition-note]: This is based on the edition of the whole crate, not the edition of the individual +test case that may be specified in its code attribute. + +In some cases, doctests cannot be merged. For example, if you have: + +```rust +//! ``` +//! let location = std::panic::Location::caller(); +//! assert_eq!(location.line(), 4); +//! ``` +``` + +The problem with this code is that, if you change any other doctests, it'll likely break when +runing `rustdoc --test`, making it tricky to maintain. + +This is where the `standalone` attribute comes in: it tells `rustdoc` that a doctest +should not be merged with the others. So the previous code should use it: + +```rust +//! ```standalone +//! let location = std::panic::Location::caller(); +//! assert_eq!(location.line(), 4); +//! ``` +``` + +In this case, it means that the line information will not change if you add/remove other +doctests. + ### Custom CSS classes for code blocks ```rust From b7079c5c83419b92a99bb991940363ae01633a93 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jun 2024 16:29:50 +0200 Subject: [PATCH 130/178] Prevent merged doctests to break stdin if the generated file is too big --- src/librustdoc/doctest.rs | 56 ++++++++++++++++++++++++-------- src/librustdoc/doctest/runner.rs | 11 +++++-- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 250cfa0d4c59a..3d0dc2378a5fd 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -289,7 +289,7 @@ pub(crate) fn run_tests( for (doctest, scraped_test) in &doctests { tests_runner.add_test(doctest, scraped_test, &target_str); } - if let Ok(success) = tests_runner.run_tests( + if let Ok(success) = tests_runner.run_merged_tests( rustdoc_test_options, edition, &opts, @@ -454,6 +454,7 @@ fn run_test( doctest: RunnableDoctest, rustdoc_options: &RustdocOptions, supports_color: bool, + is_multiple_tests: bool, report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { let langstr = &doctest.langstr; @@ -474,11 +475,14 @@ fn run_test( } compiler.arg("--edition").arg(&doctest.edition.to_string()); - compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); - compiler.env( - "UNSTABLE_RUSTDOC_TEST_LINE", - format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize), - ); + if !is_multiple_tests { + // Setting these environment variables is unneeded if this is a merged doctest. + compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); + compiler.env( + "UNSTABLE_RUSTDOC_TEST_LINE", + format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize), + ); + } compiler.arg("-o").arg(&output_file); if langstr.test_harness { compiler.arg("--test"); @@ -521,18 +525,37 @@ fn run_test( } } - compiler.arg("-"); - compiler.stdin(Stdio::piped()); - compiler.stderr(Stdio::piped()); + // If this is a merged doctest, we need to write it into a file instead of using stdin + // because if the size of the merged doctests is too big, it'll simply break stdin. + if is_multiple_tests { + // It makes the compilation failure much faster if it is for a combined doctest. + compiler.arg("--error-format=short"); + let input_file = + doctest.test_opts.outdir.path().join(&format!("doctest_{}.rs", doctest.edition)); + if std::fs::write(&input_file, &doctest.full_test_code).is_err() { + // If we cannot write this file for any reason, we leave. All combined tests will be + // tested as standalone tests. + return Err(TestFailure::CompileError); + } + compiler.arg(input_file); + compiler.stderr(Stdio::null()); + } else { + compiler.arg("-"); + compiler.stdin(Stdio::piped()); + compiler.stderr(Stdio::piped()); + } debug!("compiler invocation for doctest: {compiler:?}"); let mut child = compiler.spawn().expect("Failed to spawn rustc process"); - { + let output = if is_multiple_tests { + let status = child.wait().expect("Failed to wait"); + process::Output { status, stdout: Vec::new(), stderr: Vec::new() } + } else { let stdin = child.stdin.as_mut().expect("Failed to open stdin"); stdin.write_all(doctest.full_test_code.as_bytes()).expect("could write out test sources"); - } - let output = child.wait_with_output().expect("Failed to read stdout"); + child.wait_with_output().expect("Failed to read stdout") + }; struct Bomb<'a>(&'a str); impl Drop for Bomb<'_> { @@ -885,8 +908,13 @@ fn doctest_run_fn( edition: scraped_test.edition(&rustdoc_options), no_run: scraped_test.no_run(&rustdoc_options), }; - let res = - run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs); + let res = run_test( + runnable_test, + &rustdoc_options, + doctest.supports_color, + false, + report_unused_externs, + ); if let Err(err) = res { match err { diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 9bad83d4669dc..a3b461cdc06ae 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -62,7 +62,7 @@ impl DocTestRunner { self.nb_tests += 1; } - pub(crate) fn run_tests( + pub(crate) fn run_merged_tests( &mut self, test_options: IndividualTestOptions, edition: Edition, @@ -123,8 +123,13 @@ test::test_main(&[{test_args}], vec![{ids}], None); edition, no_run: false, }; - let ret = - run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {}); + let ret = run_test( + runnable_test, + rustdoc_options, + self.supports_color, + true, + |_: UnusedExterns| {}, + ); if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) } } } From a0ae8ac861d5e1096ff128833b17935e5ddef1e2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jun 2024 17:37:10 +0200 Subject: [PATCH 131/178] If there is any AST error with a doctest, we make it a standalone test To do so, AST error detection was improved in order to not filter out too many doctests. --- src/librustdoc/doctest.rs | 1 + src/librustdoc/doctest/make.rs | 270 ++++++++++++++++++++++----------- 2 files changed, 183 insertions(+), 88 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 3d0dc2378a5fd..818ee16a4acf8 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -800,6 +800,7 @@ impl CreateRunnableDoctests { let doctest = DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition, Some(test_id)); let is_standalone = !self.can_merge_doctests + || doctest.failed_ast || scraped_test.langstr.compile_fail || scraped_test.langstr.test_harness || scraped_test.langstr.standalone diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index c1d1e45ff042e..492a9a6e38ded 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -25,6 +25,7 @@ pub(crate) struct DocTest { pub(crate) crates: String, pub(crate) everything_else: String, pub(crate) test_id: Option, + pub(crate) failed_ast: bool, } impl DocTest { @@ -40,8 +41,15 @@ impl DocTest { // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern // crate already is included. - let Ok((main_fn_span, already_has_extern_crate)) = - check_for_main_and_extern_crate(crate_name, source, edition, &mut supports_color) + let Ok((main_fn_span, already_has_extern_crate, failed_ast)) = + check_for_main_and_extern_crate( + crate_name, + source, + &everything_else, + &crates, + edition, + &mut supports_color, + ) else { // If the parser panicked due to a fatal error, pass the test code through unchanged. // The error will be reported during compilation. @@ -53,6 +61,7 @@ impl DocTest { everything_else, already_has_extern_crate: false, test_id, + failed_ast: true, }; }; Self { @@ -63,6 +72,7 @@ impl DocTest { everything_else, already_has_extern_crate, test_id, + failed_ast, } } @@ -179,103 +189,187 @@ impl DocTest { } } -fn check_for_main_and_extern_crate( - crate_name: Option<&str>, - source: &str, - edition: Edition, - supports_color: &mut bool, -) -> Result<(Option, bool), FatalError> { - let result = rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_if_not_set_then(edition, |_| { - use rustc_errors::emitter::{Emitter, HumanEmitter}; - use rustc_errors::DiagCtxt; - use rustc_parse::parser::ForceCollect; - use rustc_span::source_map::FilePathMapping; - - let filename = FileName::anon_source_code(source); +#[derive(PartialEq, Eq, Debug)] +enum ParsingResult { + Failed, + AstError, + Ok, +} - // Any errors in parsing should also appear when the doctest is compiled for real, so just - // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - *supports_color = - HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) - .supports_color(); +fn cancel_error_count(psess: &ParseSess) { + // Reset errors so that they won't be reported as compiler bugs when dropping the + // dcx. Any errors in the tests will be reported when the test file is compiled, + // Note that we still need to cancel the errors above otherwise `Diag` will panic on + // drop. + psess.dcx().reset_err_count(); +} - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); +fn parse_source( + source: String, + found_main_span: &mut Option, + found_extern_crate: &mut bool, + found_macro: &mut bool, + crate_name: &Option<&str>, + supports_color: &mut bool, +) -> ParsingResult { + use rustc_errors::emitter::{Emitter, HumanEmitter}; + use rustc_errors::DiagCtxt; + use rustc_parse::parser::ForceCollect; + use rustc_span::source_map::FilePathMapping; + + let filename = FileName::anon_source_code(&source); + + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); + *supports_color = + HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) + .supports_color(); + + let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); + + // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser + let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); + let psess = ParseSess::with_dcx(dcx, sm); + + let mut parser = match new_parser_from_source_str(&psess, filename, source) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + cancel_error_count(&psess); + return ParsingResult::Failed; + } + }; + let mut parsing_result = ParsingResult::Ok; + + // Recurse through functions body. It is necessary because the doctest source code is + // wrapped in a function to limit the number of AST errors. If we don't recurse into + // functions, we would thing all top-level items (so basically nothing). + fn check_item( + item: &ast::Item, + found_main_span: &mut Option, + found_extern_crate: &mut bool, + found_macro: &mut bool, + crate_name: &Option<&str>, + ) { + match item.kind { + ast::ItemKind::Fn(ref fn_item) if found_main_span.is_none() => { + if item.ident.name == sym::main { + *found_main_span = Some(item.span); + } + if let Some(ref body) = fn_item.body { + for stmt in &body.stmts { + match stmt.kind { + ast::StmtKind::Item(ref item) => check_item( + item, + found_main_span, + found_extern_crate, + found_macro, + crate_name, + ), + ast::StmtKind::MacCall(..) => *found_macro = true, + _ => {} + } + } + } + } + ast::ItemKind::ExternCrate(original) => { + if !*found_extern_crate && let Some(ref crate_name) = crate_name { + *found_extern_crate = match original { + Some(name) => name.as_str() == *crate_name, + None => item.ident.as_str() == *crate_name, + }; + } + } + ast::ItemKind::MacCall(..) => *found_macro = true, + _ => {} + } + } - // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - let psess = ParseSess::with_dcx(dcx, sm); + loop { + match parser.parse_item(ForceCollect::No) { + Ok(Some(item)) => { + check_item(&item, found_main_span, found_extern_crate, found_macro, crate_name); - let mut found_main = None; - let mut found_extern_crate = crate_name.is_none(); - let mut found_macro = false; - - let mut parser = match new_parser_from_source_str(&psess, filename, source.to_owned()) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - return (found_main, found_extern_crate, found_macro); + if found_main_span.is_some() && *found_extern_crate { + break; } - }; - - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => { - if found_main.is_none() - && let ast::ItemKind::Fn(..) = item.kind - && item.ident.name == sym::main - { - found_main = Some(item.span); - } + } + Ok(None) => break, + Err(e) => { + parsing_result = ParsingResult::AstError; + e.cancel(); + break; + } + } - if !found_extern_crate - && let ast::ItemKind::ExternCrate(original) = item.kind - { - // This code will never be reached if `crate_name` is none because - // `found_extern_crate` is initialized to `true` if it is none. - let crate_name = crate_name.unwrap(); - - match original { - Some(name) => found_extern_crate = name.as_str() == crate_name, - None => found_extern_crate = item.ident.as_str() == crate_name, - } - } + // The supplied slice is only used for diagnostics, + // which are swallowed here anyway. + parser.maybe_consume_incorrect_semicolon(None); + } - if !found_macro && let ast::ItemKind::MacCall(..) = item.kind { - found_macro = true; - } + cancel_error_count(&psess); + parsing_result +} - if found_main.is_some() && found_extern_crate { - break; - } - } - Ok(None) => break, - Err(e) => { - e.cancel(); - break; - } - } +fn check_for_main_and_extern_crate( + crate_name: Option<&str>, + original_source_code: &str, + everything_else: &str, + crates: &str, + edition: Edition, + supports_color: &mut bool, +) -> Result<(Option, bool, bool), FatalError> { + let result = rustc_driver::catch_fatal_errors(|| { + rustc_span::create_session_if_not_set_then(edition, |_| { + let mut found_main_span = None; + let mut found_extern_crate = crate_name.is_none(); + let mut found_macro = false; - // The supplied item is only used for diagnostics, - // which are swallowed here anyway. - parser.maybe_consume_incorrect_semicolon(None); + let mut parsing_result = parse_source( + format!("{crates}{everything_else}"), + &mut found_main_span, + &mut found_extern_crate, + &mut found_macro, + &crate_name, + supports_color, + ); + // No need to double-check this if the "merged doctests" feature isn't enabled (so + // before the 2024 edition). + if edition >= Edition::Edition2024 && parsing_result != ParsingResult::Ok { + // If we found an AST error, we want to ensure it's because of an expression being + // used outside of a function. + // + // To do so, we wrap in a function in order to make sure that the doctest AST is + // correct. For example, if your doctest is `foo::bar()`, if we don't wrap it in a + // block, it would emit an AST error, which would be problematic for us since we + // want to filter out such errors which aren't "real" errors. + // + // The end goal is to be able to merge as many doctests as possible as one for much + // faster doctests run time. + parsing_result = parse_source( + format!("{crates}\nfn __doctest_wrap(){{{everything_else}\n}}"), + &mut found_main_span, + &mut found_extern_crate, + &mut found_macro, + &crate_name, + supports_color, + ); } - // Reset errors so that they won't be reported as compiler bugs when dropping the - // dcx. Any errors in the tests will be reported when the test file is compiled, - // Note that we still need to cancel the errors above otherwise `Diag` will panic on - // drop. - psess.dcx().reset_err_count(); - - (found_main, found_extern_crate, found_macro) + (found_main_span, found_extern_crate, found_macro, parsing_result) }) }); - let (mut main_fn_span, already_has_extern_crate, found_macro) = result?; + let (mut main_fn_span, already_has_extern_crate, found_macro, parsing_result) = match result { + Err(..) | Ok((_, _, _, ParsingResult::Failed)) => return Err(FatalError), + Ok((main_fn_span, already_has_extern_crate, found_macro, parsing_result)) => { + (main_fn_span, already_has_extern_crate, found_macro, parsing_result) + } + }; // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't // see it. In that case, run the old text-based scan to see if they at least have a main @@ -283,7 +377,7 @@ fn check_for_main_and_extern_crate( // https://github.com/rust-lang/rust/issues/56898 if found_macro && main_fn_span.is_none() - && source + && original_source_code .lines() .map(|line| { let comment = line.find("//"); @@ -294,7 +388,7 @@ fn check_for_main_and_extern_crate( main_fn_span = Some(DUMMY_SP); } - Ok((main_fn_span, already_has_extern_crate)) + Ok((main_fn_span, already_has_extern_crate, parsing_result != ParsingResult::Ok)) } fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { From 59a9e0986d061d095c813db1dc418ab55e9e7771 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jun 2024 18:03:40 +0200 Subject: [PATCH 132/178] Correctly handle the case where there is no doctests to run --- src/librustdoc/doctest.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 818ee16a4acf8..5586a23595def 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -270,6 +270,7 @@ pub(crate) fn run_tests( } let mut nb_errors = 0; + let mut ran_edition_tests = 0; let target_str = rustdoc_options.target.to_string(); for (edition, mut doctests) in mergeable_tests { @@ -296,6 +297,7 @@ pub(crate) fn run_tests( &test_args, rustdoc_options, ) { + ran_edition_tests += 1; if !success { nb_errors += 1; } @@ -322,7 +324,9 @@ pub(crate) fn run_tests( } } - if !standalone_tests.is_empty() { + // We need to call `test_main` even if there is no doctest to run to get the output + // `running 0 tests...`. + if ran_edition_tests == 0 || !standalone_tests.is_empty() { standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice())); test::test_main(&test_args, standalone_tests, None); } From 7ec3cabe1776b1a3d0340b24a5a6901797836430 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jun 2024 18:29:33 +0200 Subject: [PATCH 133/178] Correctly handle doctests with invalid AST --- src/librustdoc/doctest.rs | 13 ++++--- src/librustdoc/doctest/make.rs | 19 ++++++++-- src/librustdoc/doctest/tests.rs | 2 +- src/librustdoc/html/markdown.rs | 2 +- tests/rustdoc-ui/doctest/wrong-ast-2024.rs | 20 +++++++++++ .../rustdoc-ui/doctest/wrong-ast-2024.stdout | 35 ++++++++++++++++++ tests/rustdoc-ui/doctest/wrong-ast.rs | 20 +++++++++++ tests/rustdoc-ui/doctest/wrong-ast.stdout | 36 +++++++++++++++++++ 8 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 tests/rustdoc-ui/doctest/wrong-ast-2024.rs create mode 100644 tests/rustdoc-ui/doctest/wrong-ast-2024.stdout create mode 100644 tests/rustdoc-ui/doctest/wrong-ast.rs create mode 100644 tests/rustdoc-ui/doctest/wrong-ast.stdout diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 5586a23595def..07bdccff278b8 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -635,7 +635,7 @@ fn run_test( cmd.current_dir(run_directory); } - let result = if rustdoc_options.nocapture { + let result = if is_multiple_tests || rustdoc_options.nocapture { cmd.status().map(|status| process::Output { status, stdout: Vec::new(), @@ -801,10 +801,15 @@ impl CreateRunnableDoctests { ); let edition = scraped_test.edition(&self.rustdoc_options); - let doctest = - DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition, Some(test_id)); + let doctest = DocTest::new( + &scraped_test.text, + Some(&self.opts.crate_name), + edition, + self.can_merge_doctests, + Some(test_id), + ); let is_standalone = !self.can_merge_doctests - || doctest.failed_ast + || !doctest.can_be_merged || scraped_test.langstr.compile_fail || scraped_test.langstr.test_harness || scraped_test.langstr.standalone diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 492a9a6e38ded..c95dace1a81d4 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -26,6 +26,7 @@ pub(crate) struct DocTest { pub(crate) everything_else: String, pub(crate) test_id: Option, pub(crate) failed_ast: bool, + pub(crate) can_be_merged: bool, } impl DocTest { @@ -33,6 +34,7 @@ impl DocTest { source: &str, crate_name: Option<&str>, edition: Edition, + can_merge_doctests: bool, // If `test_id` is `None`, it means we're generating code for a code example "run" link. test_id: Option, ) -> Self { @@ -49,6 +51,7 @@ impl DocTest { &crates, edition, &mut supports_color, + can_merge_doctests, ) else { // If the parser panicked due to a fatal error, pass the test code through unchanged. @@ -62,6 +65,7 @@ impl DocTest { already_has_extern_crate: false, test_id, failed_ast: true, + can_be_merged: false, }; }; Self { @@ -72,7 +76,10 @@ impl DocTest { everything_else, already_has_extern_crate, test_id, - failed_ast, + failed_ast: false, + // If the AST returned an error, we don't want this doctest to be merged with the + // others. + can_be_merged: !failed_ast, } } @@ -85,6 +92,11 @@ impl DocTest { opts: &GlobalTestOptions, crate_name: Option<&str>, ) -> (String, usize) { + if self.failed_ast { + // If the AST failed to compile, no need to go generate a complete doctest, the error + // will be better this way. + return (test_code.to_string(), 0); + } let mut line_offset = 0; let mut prog = String::new(); let everything_else = self.everything_else.trim(); @@ -323,6 +335,7 @@ fn check_for_main_and_extern_crate( crates: &str, edition: Edition, supports_color: &mut bool, + can_merge_doctests: bool, ) -> Result<(Option, bool, bool), FatalError> { let result = rustc_driver::catch_fatal_errors(|| { rustc_span::create_session_if_not_set_then(edition, |_| { @@ -340,7 +353,7 @@ fn check_for_main_and_extern_crate( ); // No need to double-check this if the "merged doctests" feature isn't enabled (so // before the 2024 edition). - if edition >= Edition::Edition2024 && parsing_result != ParsingResult::Ok { + if can_merge_doctests && parsing_result != ParsingResult::Ok { // If we found an AST error, we want to ensure it's because of an expression being // used outside of a function. // @@ -525,5 +538,5 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { debug!("crates:\n{crates}"); debug!("after:\n{after}"); - (before, after, crates) + (before, after.trim().to_owned(), crates) } diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 533fc3a56eddd..982bfae588376 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -11,7 +11,7 @@ fn make_test( test_id: Option<&str>, ) -> (String, usize) { let doctest = - DocTest::new(test_code, crate_name, DEFAULT_EDITION, test_id.map(|s| s.to_string())); + DocTest::new(test_code, crate_name, DEFAULT_EDITION, false, test_id.map(|s| s.to_string())); let (code, line_offset) = doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name); (code, line_offset) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index da6aa47fff262..879e44c171223 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -297,7 +297,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { attrs: vec![], args_file: PathBuf::new(), }; - let doctest = doctest::DocTest::new(&test, krate, edition, None); + let doctest = doctest::DocTest::new(&test, krate, edition, false, None); let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.rs b/tests/rustdoc-ui/doctest/wrong-ast-2024.rs new file mode 100644 index 0000000000000..b0a700477b247 --- /dev/null +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.rs @@ -0,0 +1,20 @@ +//@ compile-flags:--test --test-args=--test-threads=1 -Zunstable-options --edition 2024 +//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test "wrong-ast.rs:\d+:\d+" -> "wrong-ast.rs:$$LINE:$$COL" +//@ failure-status: 101 + +/// ``` +/// /* plop +/// ``` +pub fn one() {} + +/// ``` +/// } mod __doctest_1 { fn main() { +/// ``` +pub fn two() {} + +/// ```should_panic +/// panic!() +/// ``` +pub fn three() {} diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout new file mode 100644 index 0000000000000..1dea271939967 --- /dev/null +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout @@ -0,0 +1,35 @@ + +running 2 tests +test $DIR/wrong-ast-2024.rs - one (line 7) ... FAILED +test $DIR/wrong-ast-2024.rs - two (line 12) ... FAILED + +failures: + +---- $DIR/wrong-ast-2024.rs - one (line 7) stdout ---- +error[E0758]: unterminated block comment + --> $DIR/wrong-ast-2024.rs:8:1 + | +LL | /* plop + | ^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0758`. +Couldn't compile the test. +---- $DIR/wrong-ast-2024.rs - two (line 12) stdout ---- +error: unexpected closing delimiter: `}` + --> $DIR/wrong-ast-2024.rs:13:1 + | +LL | } mod __doctest_1 { fn main() { + | ^ unexpected closing delimiter + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + $DIR/wrong-ast-2024.rs - one (line 7) + $DIR/wrong-ast-2024.rs - two (line 12) + +test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/wrong-ast.rs b/tests/rustdoc-ui/doctest/wrong-ast.rs new file mode 100644 index 0000000000000..b3fbf630c327c --- /dev/null +++ b/tests/rustdoc-ui/doctest/wrong-ast.rs @@ -0,0 +1,20 @@ +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test "wrong-ast.rs:\d+:\d+" -> "wrong-ast.rs:$$LINE:$$COL" +//@ failure-status: 101 + +/// ``` +/// /* plop +/// ``` +pub fn one() {} + +/// ``` +/// } mod __doctest_1 { fn main() { +/// ``` +pub fn two() {} + +/// ```should_panic +/// panic!() +/// ``` +pub fn three() {} diff --git a/tests/rustdoc-ui/doctest/wrong-ast.stdout b/tests/rustdoc-ui/doctest/wrong-ast.stdout new file mode 100644 index 0000000000000..b50999d17d77e --- /dev/null +++ b/tests/rustdoc-ui/doctest/wrong-ast.stdout @@ -0,0 +1,36 @@ + +running 3 tests +test $DIR/wrong-ast.rs - one (line 7) ... FAILED +test $DIR/wrong-ast.rs - three (line 17) ... ok +test $DIR/wrong-ast.rs - two (line 12) ... FAILED + +failures: + +---- $DIR/wrong-ast.rs - one (line 7) stdout ---- +error[E0758]: unterminated block comment + --> $DIR/wrong-ast.rs:$LINE:$COL + | +LL | /* plop + | ^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0758`. +Couldn't compile the test. +---- $DIR/wrong-ast.rs - two (line 12) stdout ---- +error: unexpected closing delimiter: `}` + --> $DIR/wrong-ast.rs:$LINE:$COL + | +LL | } mod __doctest_1 { fn main() { + | ^ unexpected closing delimiter + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + $DIR/wrong-ast.rs - one (line 7) + $DIR/wrong-ast.rs - two (line 12) + +test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + From 2fd8d1c1cee3bce5c088cd5537ec293f04d34e15 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jun 2024 22:58:24 +0200 Subject: [PATCH 134/178] Add/update `rustdoc-ui` tests to check new merged doctests --- .../doctest/failed-doctest-should-panic-2021.rs | 12 ++++++++++++ .../failed-doctest-should-panic-2021.stdout | 14 ++++++++++++++ .../doctest/failed-doctest-should-panic.rs | 2 +- .../doctest/failed-doctest-should-panic.stdout | 5 +++-- tests/rustdoc-ui/doctest/wrong-ast-2024.rs | 2 +- tests/rustdoc-ui/doctest/wrong-ast-2024.stdout | 12 +++++++++--- tests/rustdoc-ui/doctest/wrong-ast.rs | 2 +- tests/rustdoc-ui/doctest/wrong-ast.stdout | 2 +- 8 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.rs create mode 100644 tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.stdout diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.rs b/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.rs new file mode 100644 index 0000000000000..ad78bb545533d --- /dev/null +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.rs @@ -0,0 +1,12 @@ +// FIXME: if/when the output of the test harness can be tested on its own, this test should be +// adapted to use that, and that normalize line can go away + +//@ compile-flags:--test --edition 2021 +//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ failure-status: 101 + +/// ```should_panic +/// println!("Hello, world!"); +/// ``` +pub struct Foo; diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.stdout b/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.stdout new file mode 100644 index 0000000000000..63d987de8a9fa --- /dev/null +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.stdout @@ -0,0 +1,14 @@ + +running 1 test +test $DIR/failed-doctest-should-panic-2021.rs - Foo (line 9) ... FAILED + +failures: + +---- $DIR/failed-doctest-should-panic-2021.rs - Foo (line 9) stdout ---- +Test executable succeeded, but it's marked `should_panic`. + +failures: + $DIR/failed-doctest-should-panic-2021.rs - Foo (line 9) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.rs b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.rs index b24687993e5dc..4018e37105ff1 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.rs +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.rs @@ -1,7 +1,7 @@ // FIXME: if/when the output of the test harness can be tested on its own, this test should be // adapted to use that, and that normalize line can go away -//@ compile-flags:--test +//@ compile-flags:--test -Z unstable-options --edition 2024 //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ failure-status: 101 diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout index 57a20092a5d6c..71b0b10fa72e2 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout @@ -1,11 +1,12 @@ running 1 test -test $DIR/failed-doctest-should-panic.rs - Foo (line 9) ... FAILED +test $DIR/failed-doctest-should-panic.rs - Foo (line 9) - should panic ... FAILED failures: ---- $DIR/failed-doctest-should-panic.rs - Foo (line 9) stdout ---- -Test executable succeeded, but it's marked `should_panic`. +Hello, world! +note: test did not panic as expected failures: $DIR/failed-doctest-should-panic.rs - Foo (line 9) diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.rs b/tests/rustdoc-ui/doctest/wrong-ast-2024.rs index b0a700477b247..fdcd3baa64273 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast-2024.rs +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.rs @@ -1,7 +1,7 @@ //@ compile-flags:--test --test-args=--test-threads=1 -Zunstable-options --edition 2024 //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ normalize-stdout-test "wrong-ast.rs:\d+:\d+" -> "wrong-ast.rs:$$LINE:$$COL" +//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" //@ failure-status: 101 /// ``` diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout index 1dea271939967..22c8ce468fd7c 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout @@ -1,4 +1,10 @@ +running 1 test +test $DIR/wrong-ast-2024.rs - three (line 17) - should panic ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + running 2 tests test $DIR/wrong-ast-2024.rs - one (line 7) ... FAILED test $DIR/wrong-ast-2024.rs - two (line 12) ... FAILED @@ -7,10 +13,10 @@ failures: ---- $DIR/wrong-ast-2024.rs - one (line 7) stdout ---- error[E0758]: unterminated block comment - --> $DIR/wrong-ast-2024.rs:8:1 + --> $DIR/wrong-ast-2024.rs:$LINE:$COL | LL | /* plop - | ^^^^^^^^ + | ^^^^^^^ error: aborting due to 1 previous error @@ -18,7 +24,7 @@ For more information about this error, try `rustc --explain E0758`. Couldn't compile the test. ---- $DIR/wrong-ast-2024.rs - two (line 12) stdout ---- error: unexpected closing delimiter: `}` - --> $DIR/wrong-ast-2024.rs:13:1 + --> $DIR/wrong-ast-2024.rs:$LINE:$COL | LL | } mod __doctest_1 { fn main() { | ^ unexpected closing delimiter diff --git a/tests/rustdoc-ui/doctest/wrong-ast.rs b/tests/rustdoc-ui/doctest/wrong-ast.rs index b3fbf630c327c..dae86fbfc59a1 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast.rs +++ b/tests/rustdoc-ui/doctest/wrong-ast.rs @@ -1,7 +1,7 @@ //@ compile-flags:--test --test-args=--test-threads=1 //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ normalize-stdout-test "wrong-ast.rs:\d+:\d+" -> "wrong-ast.rs:$$LINE:$$COL" +//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" //@ failure-status: 101 /// ``` diff --git a/tests/rustdoc-ui/doctest/wrong-ast.stdout b/tests/rustdoc-ui/doctest/wrong-ast.stdout index b50999d17d77e..c827254d8c0f5 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast.stdout +++ b/tests/rustdoc-ui/doctest/wrong-ast.stdout @@ -11,7 +11,7 @@ error[E0758]: unterminated block comment --> $DIR/wrong-ast.rs:$LINE:$COL | LL | /* plop - | ^^^^^^^^ + | ^^^^^^^ error: aborting due to 1 previous error From 010731d5b6d32b2fa464d8479fb5e99ea05fff8a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jun 2024 23:33:14 +0200 Subject: [PATCH 135/178] Add new `run-make` tests for doctests --- .../doctests-keep-binaries-2024/rmake.rs | 65 +++++++++++++++++++ .../run-make/doctests-keep-binaries-2024/t.rs | 11 ++++ .../doctests-merge/doctest-2021.stdout | 7 ++ .../doctests-merge/doctest-2024.stdout | 7 ++ .../doctests-merge/doctest-standalone.rs | 18 +++++ .../doctests-merge/doctest-standalone.stdout | 7 ++ tests/run-make/doctests-merge/doctest.rs | 18 +++++ tests/run-make/doctests-merge/rmake.rs | 38 +++++++++++ 8 files changed, 171 insertions(+) create mode 100644 tests/run-make/doctests-keep-binaries-2024/rmake.rs create mode 100644 tests/run-make/doctests-keep-binaries-2024/t.rs create mode 100644 tests/run-make/doctests-merge/doctest-2021.stdout create mode 100644 tests/run-make/doctests-merge/doctest-2024.stdout create mode 100644 tests/run-make/doctests-merge/doctest-standalone.rs create mode 100644 tests/run-make/doctests-merge/doctest-standalone.stdout create mode 100644 tests/run-make/doctests-merge/doctest.rs create mode 100644 tests/run-make/doctests-merge/rmake.rs diff --git a/tests/run-make/doctests-keep-binaries-2024/rmake.rs b/tests/run-make/doctests-keep-binaries-2024/rmake.rs new file mode 100644 index 0000000000000..a6fddf6d3365e --- /dev/null +++ b/tests/run-make/doctests-keep-binaries-2024/rmake.rs @@ -0,0 +1,65 @@ +// Check that valid binaries are persisted by running them, regardless of whether the +// --run or --no-run option is used. + +use run_make_support::fs_wrapper::{create_dir, remove_dir_all}; +use run_make_support::{run, rustc, rustdoc}; +use std::path::Path; + +fn setup_test_env(callback: F) { + let out_dir = Path::new("doctests"); + create_dir(&out_dir); + rustc().input("t.rs").crate_type("rlib").run(); + callback(&out_dir, Path::new("libt.rlib")); + remove_dir_all(out_dir); +} + +fn check_generated_binaries() { + run("doctests/merged_doctest_2024/rust_out"); +} + +fn main() { + setup_test_env(|out_dir, extern_path| { + rustdoc() + .input("t.rs") + .arg("-Zunstable-options") + .arg("--test") + .arg("--persist-doctests") + .arg(out_dir) + .extern_("t", extern_path) + .edition("2024") + .run(); + check_generated_binaries(); + }); + setup_test_env(|out_dir, extern_path| { + rustdoc() + .input("t.rs") + .arg("-Zunstable-options") + .arg("--test") + .arg("--persist-doctests") + .arg(out_dir) + .extern_("t", extern_path) + .arg("--no-run") + .edition("2024") + .run(); + check_generated_binaries(); + }); + // Behavior with --test-run-directory with relative paths. + setup_test_env(|_, _| { + let run_dir_path = Path::new("rundir"); + create_dir(&run_dir_path); + + rustdoc() + .input("t.rs") + .arg("-Zunstable-options") + .arg("--test") + .arg("--persist-doctests") + .arg("doctests") + .arg("--test-run-directory") + .arg(run_dir_path) + .extern_("t", "libt.rlib") + .edition("2024") + .run(); + + remove_dir_all(run_dir_path); + }); +} diff --git a/tests/run-make/doctests-keep-binaries-2024/t.rs b/tests/run-make/doctests-keep-binaries-2024/t.rs new file mode 100644 index 0000000000000..c38cf0a0b25d4 --- /dev/null +++ b/tests/run-make/doctests-keep-binaries-2024/t.rs @@ -0,0 +1,11 @@ +/// Fungle the foople. +/// ``` +/// t::foople(); +/// ``` +pub fn foople() {} + +/// Flomble the florp +/// ``` +/// t::florp(); +/// ``` +pub fn florp() {} diff --git a/tests/run-make/doctests-merge/doctest-2021.stdout b/tests/run-make/doctests-merge/doctest-2021.stdout new file mode 100644 index 0000000000000..7da08d68faae3 --- /dev/null +++ b/tests/run-make/doctests-merge/doctest-2021.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test doctest.rs - (line 4) ... ok +test doctest.rs - init (line 8) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/run-make/doctests-merge/doctest-2024.stdout b/tests/run-make/doctests-merge/doctest-2024.stdout new file mode 100644 index 0000000000000..7da08d68faae3 --- /dev/null +++ b/tests/run-make/doctests-merge/doctest-2024.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test doctest.rs - (line 4) ... ok +test doctest.rs - init (line 8) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/run-make/doctests-merge/doctest-standalone.rs b/tests/run-make/doctests-merge/doctest-standalone.rs new file mode 100644 index 0000000000000..134ffb58285e8 --- /dev/null +++ b/tests/run-make/doctests-merge/doctest-standalone.rs @@ -0,0 +1,18 @@ +#![crate_name = "foo"] +#![crate_type = "lib"] + +//! ```standalone +//! foo::init(); +//! ``` + +/// ```standalone +/// foo::init(); +/// ``` +pub fn init() { + static mut IS_INIT: bool = false; + + unsafe { + assert!(!IS_INIT); + IS_INIT = true; + } +} diff --git a/tests/run-make/doctests-merge/doctest-standalone.stdout b/tests/run-make/doctests-merge/doctest-standalone.stdout new file mode 100644 index 0000000000000..ee9f62326ab02 --- /dev/null +++ b/tests/run-make/doctests-merge/doctest-standalone.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test doctest-standalone.rs - (line 4) ... ok +test doctest-standalone.rs - init (line 8) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/run-make/doctests-merge/doctest.rs b/tests/run-make/doctests-merge/doctest.rs new file mode 100644 index 0000000000000..66a5d88db67f4 --- /dev/null +++ b/tests/run-make/doctests-merge/doctest.rs @@ -0,0 +1,18 @@ +#![crate_name = "foo"] +#![crate_type = "lib"] + +//! ``` +//! foo::init(); +//! ``` + +/// ``` +/// foo::init(); +/// ``` +pub fn init() { + static mut IS_INIT: bool = false; + + unsafe { + assert!(!IS_INIT); + IS_INIT = true; + } +} diff --git a/tests/run-make/doctests-merge/rmake.rs b/tests/run-make/doctests-merge/rmake.rs new file mode 100644 index 0000000000000..ac3951c6ceb3b --- /dev/null +++ b/tests/run-make/doctests-merge/rmake.rs @@ -0,0 +1,38 @@ +use run_make_support::{cwd, diff, rustc, rustdoc}; +use std::path::Path; + +fn test_and_compare(input_file: &str, stdout_file: &str, edition: &str, dep: &Path) { + let mut cmd = rustdoc(); + + let output = cmd + .input(input_file) + .arg("--test") + .arg("-Zunstable-options") + .edition(edition) + .arg("--test-args=--test-threads=1") + .extern_("foo", dep.display().to_string()) + .env("RUST_BACKTRACE", "short") + .run(); + + diff() + .expected_file(stdout_file) + .actual_text("output", output.stdout_utf8()) + .normalize(r#"finished in \d+\.\d+s"#, "finished in $$TIME") + .run(); +} + +fn main() { + let out_file = cwd().join("libfoo.rlib"); + + rustc().input("doctest.rs").crate_type("rlib").output(&out_file).run(); + + // First we ensure that running with the 2024 edition will not fail at runtime. + test_and_compare("doctest.rs", "doctest-2024.stdout", "2024", &out_file); + + // Then we ensure that running with an edition < 2024 will not fail at runtime. + test_and_compare("doctest.rs", "doctest-2021.stdout", "2021", &out_file); + + // Now we check with the standalone attribute which should succeed in all cases. + test_and_compare("doctest-standalone.rs", "doctest-standalone.stdout", "2024", &out_file); + test_and_compare("doctest-standalone.rs", "doctest-standalone.stdout", "2021", &out_file); +} From 03118fa80a9596cc7cc1b55be49cb2001a39dbe2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 11 Jun 2024 00:01:59 +0200 Subject: [PATCH 136/178] Simplify `has_main_fn` to be a boolean instead of a `Option` --- src/librustdoc/doctest.rs | 8 +++-- src/librustdoc/doctest/make.rs | 58 +++++++++++++++++--------------- src/librustdoc/doctest/runner.rs | 2 +- 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 07bdccff278b8..1f7ccdd01713f 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -536,13 +536,17 @@ fn run_test( compiler.arg("--error-format=short"); let input_file = doctest.test_opts.outdir.path().join(&format!("doctest_{}.rs", doctest.edition)); + eprintln!("OUUUUUUUT>>>>>>> {input_file:?}"); if std::fs::write(&input_file, &doctest.full_test_code).is_err() { // If we cannot write this file for any reason, we leave. All combined tests will be // tested as standalone tests. return Err(TestFailure::CompileError); } compiler.arg(input_file); - compiler.stderr(Stdio::null()); + // compiler.stderr(Stdio::null()); + let mut buffer = String::new(); + eprintln!("Press ENTER"); + let _ = std::io::stdin().read_line(&mut buffer); } else { compiler.arg("-"); compiler.stdin(Stdio::piped()); @@ -764,7 +768,7 @@ struct CreateRunnableDoctests { impl CreateRunnableDoctests { fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests { - let can_merge_doctests = rustdoc_options.edition >= Edition::Edition2024; + let can_merge_doctests = true;//rustdoc_options.edition >= Edition::Edition2024; CreateRunnableDoctests { standalone_tests: Vec::new(), mergeable_tests: FxHashMap::default(), diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index c95dace1a81d4..2e067c6277ec6 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -13,14 +13,14 @@ use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; -use rustc_span::{FileName, Span, DUMMY_SP}; +use rustc_span::FileName; use super::GlobalTestOptions; pub(crate) struct DocTest { pub(crate) supports_color: bool, pub(crate) already_has_extern_crate: bool, - pub(crate) main_fn_span: Option, + pub(crate) has_main_fn: bool, pub(crate) crate_attrs: String, pub(crate) crates: String, pub(crate) everything_else: String, @@ -43,7 +43,7 @@ impl DocTest { // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern // crate already is included. - let Ok((main_fn_span, already_has_extern_crate, failed_ast)) = + let Ok((has_main_fn, already_has_extern_crate, failed_ast)) = check_for_main_and_extern_crate( crate_name, source, @@ -58,7 +58,7 @@ impl DocTest { // The error will be reported during compilation. return DocTest { supports_color: false, - main_fn_span: None, + has_main_fn: false, crate_attrs, crates, everything_else, @@ -70,7 +70,7 @@ impl DocTest { }; Self { supports_color, - main_fn_span, + has_main_fn, crate_attrs, crates, everything_else, @@ -141,7 +141,7 @@ impl DocTest { } // FIXME: This code cannot yet handle no_std test cases yet - if dont_insert_main || self.main_fn_span.is_some() || prog.contains("![no_std]") { + if dont_insert_main || self.has_main_fn || prog.contains("![no_std]") { prog.push_str(everything_else); } else { let returns_result = everything_else.ends_with("(())"); @@ -218,7 +218,7 @@ fn cancel_error_count(psess: &ParseSess) { fn parse_source( source: String, - found_main_span: &mut Option, + has_main_fn: &mut bool, found_extern_crate: &mut bool, found_macro: &mut bool, crate_name: &Option<&str>, @@ -263,22 +263,22 @@ fn parse_source( // functions, we would thing all top-level items (so basically nothing). fn check_item( item: &ast::Item, - found_main_span: &mut Option, + has_main_fn: &mut bool, found_extern_crate: &mut bool, found_macro: &mut bool, crate_name: &Option<&str>, ) { match item.kind { - ast::ItemKind::Fn(ref fn_item) if found_main_span.is_none() => { + ast::ItemKind::Fn(ref fn_item) if !*has_main_fn => { if item.ident.name == sym::main { - *found_main_span = Some(item.span); + *has_main_fn = true; } if let Some(ref body) = fn_item.body { for stmt in &body.stmts { match stmt.kind { ast::StmtKind::Item(ref item) => check_item( item, - found_main_span, + has_main_fn, found_extern_crate, found_macro, crate_name, @@ -305,9 +305,9 @@ fn parse_source( loop { match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => { - check_item(&item, found_main_span, found_extern_crate, found_macro, crate_name); + check_item(&item, has_main_fn, found_extern_crate, found_macro, crate_name); - if found_main_span.is_some() && *found_extern_crate { + if *has_main_fn && *found_extern_crate { break; } } @@ -319,7 +319,7 @@ fn parse_source( } } - // The supplied slice is only used for diagnostics, + // The supplied item is only used for diagnostics, // which are swallowed here anyway. parser.maybe_consume_incorrect_semicolon(None); } @@ -328,6 +328,7 @@ fn parse_source( parsing_result } +/// Returns `(has_main_fn, already_has_extern_crate, failed_ast)`. fn check_for_main_and_extern_crate( crate_name: Option<&str>, original_source_code: &str, @@ -336,16 +337,16 @@ fn check_for_main_and_extern_crate( edition: Edition, supports_color: &mut bool, can_merge_doctests: bool, -) -> Result<(Option, bool, bool), FatalError> { +) -> Result<(bool, bool, bool), FatalError> { let result = rustc_driver::catch_fatal_errors(|| { rustc_span::create_session_if_not_set_then(edition, |_| { - let mut found_main_span = None; + let mut has_main_fn = false; let mut found_extern_crate = crate_name.is_none(); let mut found_macro = false; let mut parsing_result = parse_source( format!("{crates}{everything_else}"), - &mut found_main_span, + &mut has_main_fn, &mut found_extern_crate, &mut found_macro, &crate_name, @@ -366,7 +367,7 @@ fn check_for_main_and_extern_crate( // faster doctests run time. parsing_result = parse_source( format!("{crates}\nfn __doctest_wrap(){{{everything_else}\n}}"), - &mut found_main_span, + &mut has_main_fn, &mut found_extern_crate, &mut found_macro, &crate_name, @@ -374,13 +375,13 @@ fn check_for_main_and_extern_crate( ); } - (found_main_span, found_extern_crate, found_macro, parsing_result) + (has_main_fn, found_extern_crate, found_macro, parsing_result) }) }); - let (mut main_fn_span, already_has_extern_crate, found_macro, parsing_result) = match result { + let (mut has_main_fn, already_has_extern_crate, found_macro, parsing_result) = match result { Err(..) | Ok((_, _, _, ParsingResult::Failed)) => return Err(FatalError), - Ok((main_fn_span, already_has_extern_crate, found_macro, parsing_result)) => { - (main_fn_span, already_has_extern_crate, found_macro, parsing_result) + Ok((has_main_fn, already_has_extern_crate, found_macro, parsing_result)) => { + (has_main_fn, already_has_extern_crate, found_macro, parsing_result) } }; @@ -389,7 +390,7 @@ fn check_for_main_and_extern_crate( // function written inside a macro invocation. See // https://github.com/rust-lang/rust/issues/56898 if found_macro - && main_fn_span.is_none() + && !has_main_fn && original_source_code .lines() .map(|line| { @@ -398,10 +399,10 @@ fn check_for_main_and_extern_crate( }) .any(|code| code.contains("fn main")) { - main_fn_span = Some(DUMMY_SP); + has_main_fn = true; } - Ok((main_fn_span, already_has_extern_crate, parsing_result != ParsingResult::Ok)) + Ok((has_main_fn, already_has_extern_crate, parsing_result != ParsingResult::Ok)) } fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { @@ -448,6 +449,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { .unwrap_or(false) } +/// Returns `(crate_attrs, content, crates)`. fn partition_source(s: &str, edition: Edition) -> (String, String, String) { #[derive(Copy, Clone, PartialEq)] enum PartitionState { @@ -456,7 +458,7 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { Other, } let mut state = PartitionState::Attrs; - let mut before = String::new(); + let mut crate_attrs = String::new(); let mut crates = String::new(); let mut after = String::new(); @@ -520,8 +522,8 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { match state { PartitionState::Attrs => { - before.push_str(line); - before.push('\n'); + crate_attrs.push_str(line); + crate_attrs.push('\n'); } PartitionState::Crates => { crates.push_str(line); diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index a3b461cdc06ae..994a97adee28d 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -149,7 +149,7 @@ fn generate_mergeable_doctest( writeln!(output, "mod {test_id} {{\n").unwrap(); } else { writeln!(output, "mod {test_id} {{\n{}", doctest.crates).unwrap(); - if doctest.main_fn_span.is_some() { + if doctest.has_main_fn { output.push_str(&doctest.everything_else); } else { let returns_result = if doctest.everything_else.trim_end().ends_with("(())") { From 6eabffbaec94510e40c344d2b2f43538ee78bef6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 13 Jun 2024 01:17:36 +0200 Subject: [PATCH 137/178] Greatly improve handling of doctests attributes, making it possible to merge doctests more efficiently --- src/librustdoc/doctest.rs | 11 +-- src/librustdoc/doctest/make.rs | 148 ++++++++++++++++++++++--------- src/librustdoc/doctest/runner.rs | 9 +- 3 files changed, 116 insertions(+), 52 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 1f7ccdd01713f..88f54be4cb183 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -536,17 +536,15 @@ fn run_test( compiler.arg("--error-format=short"); let input_file = doctest.test_opts.outdir.path().join(&format!("doctest_{}.rs", doctest.edition)); - eprintln!("OUUUUUUUT>>>>>>> {input_file:?}"); if std::fs::write(&input_file, &doctest.full_test_code).is_err() { // If we cannot write this file for any reason, we leave. All combined tests will be // tested as standalone tests. return Err(TestFailure::CompileError); } compiler.arg(input_file); + // FIXME: Remove once done fixing bugs. + // FIXME: Should this call only be done if `nocapture` is not set? // compiler.stderr(Stdio::null()); - let mut buffer = String::new(); - eprintln!("Press ENTER"); - let _ = std::io::stdin().read_line(&mut buffer); } else { compiler.arg("-"); compiler.stdin(Stdio::piped()); @@ -768,7 +766,7 @@ struct CreateRunnableDoctests { impl CreateRunnableDoctests { fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests { - let can_merge_doctests = true;//rustdoc_options.edition >= Edition::Edition2024; + let can_merge_doctests = true; //rustdoc_options.edition >= Edition::Edition2024; CreateRunnableDoctests { standalone_tests: Vec::new(), mergeable_tests: FxHashMap::default(), @@ -818,8 +816,7 @@ impl CreateRunnableDoctests { || scraped_test.langstr.test_harness || scraped_test.langstr.standalone || self.rustdoc_options.nocapture - || self.rustdoc_options.test_args.iter().any(|arg| arg == "--show-output") - || doctest.crate_attrs.contains("#![no_std]"); + || self.rustdoc_options.test_args.iter().any(|arg| arg == "--show-output"); if is_standalone { let test_desc = self.generate_test_desc_and_fn(doctest, scraped_test); self.standalone_tests.push(test_desc); diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 2e067c6277ec6..92ab8eb56dd48 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -22,6 +22,9 @@ pub(crate) struct DocTest { pub(crate) already_has_extern_crate: bool, pub(crate) has_main_fn: bool, pub(crate) crate_attrs: String, + /// If this is a merged doctest, it will be put into `everything_else`, otherwise it will + /// put into `crate_attrs`. + pub(crate) maybe_crate_attrs: String, pub(crate) crates: String, pub(crate) everything_else: String, pub(crate) test_id: Option, @@ -38,7 +41,14 @@ impl DocTest { // If `test_id` is `None`, it means we're generating code for a code example "run" link. test_id: Option, ) -> Self { - let (crate_attrs, everything_else, crates) = partition_source(source, edition); + let SourceInfo { + crate_attrs, + maybe_crate_attrs, + crates, + everything_else, + has_features, + has_no_std, + } = partition_source(source, edition); let mut supports_color = false; // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern @@ -56,10 +66,11 @@ impl DocTest { else { // If the parser panicked due to a fatal error, pass the test code through unchanged. // The error will be reported during compilation. - return DocTest { + return Self { supports_color: false, has_main_fn: false, crate_attrs, + maybe_crate_attrs, crates, everything_else, already_has_extern_crate: false, @@ -72,14 +83,15 @@ impl DocTest { supports_color, has_main_fn, crate_attrs, + maybe_crate_attrs, crates, everything_else, already_has_extern_crate, test_id, failed_ast: false, // If the AST returned an error, we don't want this doctest to be merged with the - // others. - can_be_merged: !failed_ast, + // others. Same if it contains `#[feature]` or `#[no_std]`. + can_be_merged: !failed_ast && !has_no_std && !has_features, } } @@ -118,6 +130,7 @@ impl DocTest { // Now push any outer attributes from the example, assuming they // are intended to be crate attributes. prog.push_str(&self.crate_attrs); + prog.push_str(&self.maybe_crate_attrs); prog.push_str(&self.crates); // Don't inject `extern crate std` because it's already injected by the @@ -405,11 +418,22 @@ fn check_for_main_and_extern_crate( Ok((has_main_fn, already_has_extern_crate, parsing_result != ParsingResult::Ok)) } -fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { +enum AttrKind { + CrateAttr, + Attr, + Feature, + NoStd, +} + +/// Returns `Some` if the attribute is complete and `Some(true)` if it is an attribute that can be +/// placed at the crate root. +fn check_if_attr_is_complete(source: &str, edition: Edition) -> Option { if source.is_empty() { // Empty content so nothing to check in here... - return true; + return None; } + let not_crate_attrs = [sym::forbid, sym::allow, sym::warn, sym::deny]; + rustc_driver::catch_fatal_errors(|| { rustc_span::create_session_if_not_set_then(edition, |_| { use rustc_errors::emitter::HumanEmitter; @@ -435,33 +459,77 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { errs.into_iter().for_each(|err| err.cancel()); // If there is an unclosed delimiter, an error will be returned by the // tokentrees. - return false; + return None; } }; // If a parsing error happened, it's very likely that the attribute is incomplete. - if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) { - e.cancel(); - return false; - } - true + let ret = match parser.parse_attribute(InnerAttrPolicy::Permitted) { + Ok(attr) => { + let attr_name = attr.name_or_empty(); + + if attr_name == sym::feature { + Some(AttrKind::Feature) + } else if attr_name == sym::no_std { + Some(AttrKind::NoStd) + } else if not_crate_attrs.contains(&attr_name) { + Some(AttrKind::Attr) + } else { + Some(AttrKind::CrateAttr) + } + } + Err(e) => { + e.cancel(); + None + } + }; + ret }) }) - .unwrap_or(false) + .unwrap_or(None) } -/// Returns `(crate_attrs, content, crates)`. -fn partition_source(s: &str, edition: Edition) -> (String, String, String) { +fn handle_attr(mod_attr_pending: &mut String, source_info: &mut SourceInfo, edition: Edition) { + if let Some(attr_kind) = check_if_attr_is_complete(mod_attr_pending, edition) { + let push_to = match attr_kind { + AttrKind::CrateAttr => &mut source_info.crate_attrs, + AttrKind::Attr => &mut source_info.maybe_crate_attrs, + AttrKind::Feature => { + source_info.has_features = true; + &mut source_info.crate_attrs + } + AttrKind::NoStd => { + source_info.has_no_std = true; + &mut source_info.crate_attrs + } + }; + push_to.push_str(mod_attr_pending); + push_to.push('\n'); + // If it's complete, then we can clear the pending content. + mod_attr_pending.clear(); + } else if mod_attr_pending.ends_with('\\') { + mod_attr_pending.push('n'); + } +} + +#[derive(Default)] +struct SourceInfo { + crate_attrs: String, + maybe_crate_attrs: String, + crates: String, + everything_else: String, + has_features: bool, + has_no_std: bool, +} + +fn partition_source(s: &str, edition: Edition) -> SourceInfo { #[derive(Copy, Clone, PartialEq)] enum PartitionState { Attrs, Crates, Other, } + let mut source_info = SourceInfo::default(); let mut state = PartitionState::Attrs; - let mut crate_attrs = String::new(); - let mut crates = String::new(); - let mut after = String::new(); - let mut mod_attr_pending = String::new(); for line in s.lines() { @@ -472,12 +540,9 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { match state { PartitionState::Attrs => { state = if trimline.starts_with("#![") { - if !check_if_attr_is_complete(line, edition) { - mod_attr_pending = line.to_owned(); - } else { - mod_attr_pending.clear(); - } - PartitionState::Attrs + mod_attr_pending = line.to_owned(); + handle_attr(&mut mod_attr_pending, &mut source_info, edition); + continue; } else if trimline.chars().all(|c| c.is_whitespace()) || (trimline.starts_with("//") && !trimline.starts_with("///")) { @@ -492,15 +557,10 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { // If not, then we append the new line into the pending attribute to check // if this time it's complete... mod_attr_pending.push_str(line); - if !trimline.is_empty() - && check_if_attr_is_complete(&mod_attr_pending, edition) - { - // If it's complete, then we can clear the pending content. - mod_attr_pending.clear(); + if !trimline.is_empty() { + handle_attr(&mut mod_attr_pending, &mut source_info, edition); } - // In any case, this is considered as `PartitionState::Attrs` so it's - // prepended before rustdoc's inserts. - PartitionState::Attrs + continue; } else { PartitionState::Other } @@ -522,23 +582,25 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { match state { PartitionState::Attrs => { - crate_attrs.push_str(line); - crate_attrs.push('\n'); + source_info.crate_attrs.push_str(line); + source_info.crate_attrs.push('\n'); } PartitionState::Crates => { - crates.push_str(line); - crates.push('\n'); + source_info.crates.push_str(line); + source_info.crates.push('\n'); } PartitionState::Other => { - after.push_str(line); - after.push('\n'); + source_info.everything_else.push_str(line); + source_info.everything_else.push('\n'); } } } - debug!("before:\n{before}"); - debug!("crates:\n{crates}"); - debug!("after:\n{after}"); + source_info.everything_else = source_info.everything_else.trim().to_string(); + + debug!("crate_attrs:\n{}{}", source_info.crate_attrs, source_info.maybe_crate_attrs); + debug!("crates:\n{}", source_info.crates); + debug!("after:\n{}", source_info.everything_else); - (before, after.trim().to_owned(), crates) + source_info } diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 994a97adee28d..adff94233bcf6 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -107,7 +107,11 @@ impl DocTestRunner { #[rustc_main] #[coverage(off)] fn main() {{ -test::test_main(&[{test_args}], vec![{ids}], None); +test::test_main_static_with_args( + &[{test_args}], + &mut [{ids}], + None, +); }}", output = self.output, ids = self.ids, @@ -148,7 +152,8 @@ fn generate_mergeable_doctest( // We generate nothing else. writeln!(output, "mod {test_id} {{\n").unwrap(); } else { - writeln!(output, "mod {test_id} {{\n{}", doctest.crates).unwrap(); + writeln!(output, "mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs) + .unwrap(); if doctest.has_main_fn { output.push_str(&doctest.everything_else); } else { From b6831bbdda3aa9ea3cc5e6d02a75f93c025818d4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 13 Jun 2024 16:39:56 +0200 Subject: [PATCH 138/178] Don't merge doctests with `#[global_allocator]` --- src/librustdoc/doctest/make.rs | 131 +++++++++++++++---------------- src/librustdoc/doctest/runner.rs | 14 ++-- 2 files changed, 70 insertions(+), 75 deletions(-) diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 92ab8eb56dd48..c67dc4525d2c3 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -49,20 +49,26 @@ impl DocTest { has_features, has_no_std, } = partition_source(source, edition); - let mut supports_color = false; // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern // crate already is included. - let Ok((has_main_fn, already_has_extern_crate, failed_ast)) = - check_for_main_and_extern_crate( - crate_name, - source, - &everything_else, - &crates, - edition, - &mut supports_color, - can_merge_doctests, - ) + let Ok(( + ParseSourceInfo { + has_main_fn, + found_extern_crate, + supports_color, + has_global_allocator, + .. + }, + failed_ast, + )) = check_for_main_and_extern_crate( + crate_name, + source, + &everything_else, + &crates, + edition, + can_merge_doctests, + ) else { // If the parser panicked due to a fatal error, pass the test code through unchanged. // The error will be reported during compilation. @@ -86,12 +92,12 @@ impl DocTest { maybe_crate_attrs, crates, everything_else, - already_has_extern_crate, + already_has_extern_crate: found_extern_crate, test_id, failed_ast: false, // If the AST returned an error, we don't want this doctest to be merged with the // others. Same if it contains `#[feature]` or `#[no_std]`. - can_be_merged: !failed_ast && !has_no_std && !has_features, + can_be_merged: !failed_ast && !has_no_std && !has_features && !has_global_allocator, } } @@ -231,11 +237,8 @@ fn cancel_error_count(psess: &ParseSess) { fn parse_source( source: String, - has_main_fn: &mut bool, - found_extern_crate: &mut bool, - found_macro: &mut bool, + info: &mut ParseSourceInfo, crate_name: &Option<&str>, - supports_color: &mut bool, ) -> ParsingResult { use rustc_errors::emitter::{Emitter, HumanEmitter}; use rustc_errors::DiagCtxt; @@ -251,7 +254,7 @@ fn parse_source( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, ); - *supports_color = + info.supports_color = HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) .supports_color(); @@ -274,43 +277,38 @@ fn parse_source( // Recurse through functions body. It is necessary because the doctest source code is // wrapped in a function to limit the number of AST errors. If we don't recurse into // functions, we would thing all top-level items (so basically nothing). - fn check_item( - item: &ast::Item, - has_main_fn: &mut bool, - found_extern_crate: &mut bool, - found_macro: &mut bool, - crate_name: &Option<&str>, - ) { + fn check_item(item: &ast::Item, info: &mut ParseSourceInfo, crate_name: &Option<&str>) { + if !info.has_global_allocator + && item.attrs.iter().any(|attr| attr.name_or_empty() == sym::global_allocator) + { + info.has_global_allocator = true; + } match item.kind { - ast::ItemKind::Fn(ref fn_item) if !*has_main_fn => { + ast::ItemKind::Fn(ref fn_item) if !info.has_main_fn => { if item.ident.name == sym::main { - *has_main_fn = true; + info.has_main_fn = true; } if let Some(ref body) = fn_item.body { for stmt in &body.stmts { match stmt.kind { - ast::StmtKind::Item(ref item) => check_item( - item, - has_main_fn, - found_extern_crate, - found_macro, - crate_name, - ), - ast::StmtKind::MacCall(..) => *found_macro = true, + ast::StmtKind::Item(ref item) => check_item(item, info, crate_name), + ast::StmtKind::MacCall(..) => info.found_macro = true, _ => {} } } } } ast::ItemKind::ExternCrate(original) => { - if !*found_extern_crate && let Some(ref crate_name) = crate_name { - *found_extern_crate = match original { + if !info.found_extern_crate + && let Some(ref crate_name) = crate_name + { + info.found_extern_crate = match original { Some(name) => name.as_str() == *crate_name, None => item.ident.as_str() == *crate_name, }; } } - ast::ItemKind::MacCall(..) => *found_macro = true, + ast::ItemKind::MacCall(..) => info.found_macro = true, _ => {} } } @@ -318,9 +316,9 @@ fn parse_source( loop { match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => { - check_item(&item, has_main_fn, found_extern_crate, found_macro, crate_name); + check_item(&item, info, crate_name); - if *has_main_fn && *found_extern_crate { + if info.has_main_fn && info.found_extern_crate { break; } } @@ -341,30 +339,30 @@ fn parse_source( parsing_result } -/// Returns `(has_main_fn, already_has_extern_crate, failed_ast)`. +#[derive(Default)] +struct ParseSourceInfo { + has_main_fn: bool, + found_extern_crate: bool, + found_macro: bool, + supports_color: bool, + has_global_allocator: bool, +} + fn check_for_main_and_extern_crate( crate_name: Option<&str>, original_source_code: &str, everything_else: &str, crates: &str, edition: Edition, - supports_color: &mut bool, can_merge_doctests: bool, -) -> Result<(bool, bool, bool), FatalError> { +) -> Result<(ParseSourceInfo, bool), FatalError> { let result = rustc_driver::catch_fatal_errors(|| { rustc_span::create_session_if_not_set_then(edition, |_| { - let mut has_main_fn = false; - let mut found_extern_crate = crate_name.is_none(); - let mut found_macro = false; - - let mut parsing_result = parse_source( - format!("{crates}{everything_else}"), - &mut has_main_fn, - &mut found_extern_crate, - &mut found_macro, - &crate_name, - supports_color, - ); + let mut info = + ParseSourceInfo { found_extern_crate: crate_name.is_none(), ..Default::default() }; + + let mut parsing_result = + parse_source(format!("{crates}{everything_else}"), &mut info, &crate_name); // No need to double-check this if the "merged doctests" feature isn't enabled (so // before the 2024 edition). if can_merge_doctests && parsing_result != ParsingResult::Ok { @@ -380,30 +378,25 @@ fn check_for_main_and_extern_crate( // faster doctests run time. parsing_result = parse_source( format!("{crates}\nfn __doctest_wrap(){{{everything_else}\n}}"), - &mut has_main_fn, - &mut found_extern_crate, - &mut found_macro, + &mut info, &crate_name, - supports_color, ); } - (has_main_fn, found_extern_crate, found_macro, parsing_result) + (info, parsing_result) }) }); - let (mut has_main_fn, already_has_extern_crate, found_macro, parsing_result) = match result { - Err(..) | Ok((_, _, _, ParsingResult::Failed)) => return Err(FatalError), - Ok((has_main_fn, already_has_extern_crate, found_macro, parsing_result)) => { - (has_main_fn, already_has_extern_crate, found_macro, parsing_result) - } + let (mut info, parsing_result) = match result { + Err(..) | Ok((_, ParsingResult::Failed)) => return Err(FatalError), + Ok((info, parsing_result)) => (info, parsing_result), }; // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't // see it. In that case, run the old text-based scan to see if they at least have a main // function written inside a macro invocation. See // https://github.com/rust-lang/rust/issues/56898 - if found_macro - && !has_main_fn + if info.found_macro + && !info.has_main_fn && original_source_code .lines() .map(|line| { @@ -412,10 +405,10 @@ fn check_for_main_and_extern_crate( }) .any(|code| code.contains("fn main")) { - has_main_fn = true; + info.has_main_fn = true; } - Ok((has_main_fn, already_has_extern_crate, parsing_result != ParsingResult::Ok)) + Ok((info, parsing_result != ParsingResult::Ok)) } enum AttrKind { diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index adff94233bcf6..1a4a375aa16a4 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -45,11 +45,11 @@ impl DocTestRunner { self.crate_attrs.insert(line.to_string()); } } - if !self.ids.is_empty() { - self.ids.push(','); - } + // if !self.ids.is_empty() { + // self.ids.push(','); + // } self.ids.push_str(&format!( - "{}::TEST", + "tests.push({}::TEST);\n", generate_mergeable_doctest( doctest, scraped_test, @@ -107,9 +107,11 @@ impl DocTestRunner { #[rustc_main] #[coverage(off)] fn main() {{ -test::test_main_static_with_args( +let mut tests = Vec::new(); +{ids} +test::test_main( &[{test_args}], - &mut [{ids}], + tests, None, ); }}", From dcc77b4cbcaf94d5bca34680934c87d172a8d02f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 13 Jun 2024 18:43:53 +0200 Subject: [PATCH 139/178] Fix weird memory allocation failure in merged doctests by storing doctest list into a const --- src/librustdoc/doctest/runner.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 1a4a375aa16a4..3f1ab7208f239 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -45,11 +45,11 @@ impl DocTestRunner { self.crate_attrs.insert(line.to_string()); } } - // if !self.ids.is_empty() { - // self.ids.push(','); - // } + if !self.ids.is_empty() { + self.ids.push(','); + } self.ids.push_str(&format!( - "tests.push({}::TEST);\n", + "{}::TEST", generate_mergeable_doctest( doctest, scraped_test, @@ -107,14 +107,14 @@ impl DocTestRunner { #[rustc_main] #[coverage(off)] fn main() {{ -let mut tests = Vec::new(); -{ids} +const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}]; test::test_main( &[{test_args}], - tests, + Vec::from(TESTS), None, ); }}", + nb_tests = self.nb_tests, output = self.output, ids = self.ids, ) @@ -192,7 +192,7 @@ pub const TEST: test::TestDescAndFn = test::TestDescAndFn {{ compile_fail: false, no_run: {no_run}, should_panic: test::ShouldPanic::{should_panic}, - test_type: test::TestType::UnitTest, + test_type: test::TestType::DocTest, }}, testfn: test::StaticTestFn( #[coverage(off)] From 0f0681e941f7352ea16dba2ad227bbc0e029ccd9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 14 Jun 2024 00:10:30 +0200 Subject: [PATCH 140/178] Make merged doctests run in their own process --- src/librustdoc/doctest.rs | 8 ++- src/librustdoc/doctest/runner.rs | 96 +++++++++++++++++++++++++++----- 2 files changed, 87 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 88f54be4cb183..74adcb415fa78 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -629,9 +629,13 @@ fn run_test( let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); cmd.args(&rustdoc_options.runtool_args); - cmd.arg(output_file); + cmd.arg(&output_file); } else { - cmd = Command::new(output_file); + cmd = Command::new(&output_file); + if is_multiple_tests { + cmd.arg("*doctest-bin-path"); + cmd.arg(&output_file); + } } if let Some(run_directory) = &rustdoc_options.test_run_directory { cmd.current_dir(run_directory); diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 3f1ab7208f239..c774f7735bb06 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -75,8 +75,9 @@ impl DocTestRunner { #![allow(internal_features)] #![feature(test)] #![feature(rustc_attrs)] -#![feature(coverage_attribute)]\n" - .to_string(); +#![feature(coverage_attribute)] +" + .to_string(); for crate_attr in &self.crate_attrs { code.push_str(crate_attr); @@ -104,15 +105,67 @@ impl DocTestRunner { code, "\ {output} + +mod __doctest_mod {{ + pub static mut BINARY_PATH: Option = None; + pub const RUN_OPTION: &str = \"*doctest-inner-test\"; + pub const BIN_OPTION: &str = \"*doctest-bin-path\"; + + #[allow(unused)] + pub fn get_doctest_path() -> Option<&'static std::path::Path> {{ + unsafe {{ self::BINARY_PATH.as_deref() }} + }} + + #[allow(unused)] + pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> Result<(), String> {{ + let out = std::process::Command::new(bin) + .arg(self::RUN_OPTION) + .arg(test_nb.to_string()) + .output() + .expect(\"failed to run command\"); + if !out.status.success() {{ + Err(String::from_utf8_lossy(&out.stderr).to_string()) + }} else {{ + Ok(()) + }} + }} +}} + #[rustc_main] #[coverage(off)] -fn main() {{ +fn main() -> std::process::ExitCode {{ const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}]; -test::test_main( - &[{test_args}], - Vec::from(TESTS), - None, -); +let bin_marker = std::ffi::OsStr::new(__doctest_mod::BIN_OPTION); +let test_marker = std::ffi::OsStr::new(__doctest_mod::RUN_OPTION); + +let mut args = std::env::args_os().skip(1); +while let Some(arg) = args.next() {{ + if arg == bin_marker {{ + let Some(binary) = args.next() else {{ + panic!(\"missing argument after `{{}}`\", __doctest_mod::BIN_OPTION); + }}; + unsafe {{ crate::__doctest_mod::BINARY_PATH = Some(binary.into()); }} + return std::process::Termination::report(test::test_main( + &[{test_args}], + Vec::from(TESTS), + None, + )); + }} else if arg == test_marker {{ + let Some(nb_test) = args.next() else {{ + panic!(\"missing argument after `{{}}`\", __doctest_mod::RUN_OPTION); + }}; + if let Some(nb_test) = nb_test.to_str().and_then(|nb| nb.parse::().ok()) {{ + if let Some(test) = TESTS.get(nb_test) {{ + if let test::StaticTestFn(f) = test.testfn {{ + return std::process::Termination::report(f()); + }} + }} + }} + panic!(\"Unexpected value after `{{}}`\", __doctest_mod::RUN_OPTION); + }} +}} + +panic!(\"missing argument for merged doctest binary\"); }}", nb_tests = self.nb_tests, output = self.output, @@ -156,6 +209,10 @@ fn generate_mergeable_doctest( } else { writeln!(output, "mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs) .unwrap(); + if scraped_test.langstr.no_run { + // To prevent having warnings about unused items since they're not called. + writeln!(output, "#![allow(unused)]").unwrap(); + } if doctest.has_main_fn { output.push_str(&doctest.everything_else); } else { @@ -167,14 +224,15 @@ fn generate_mergeable_doctest( write!( output, "\ - fn main() {returns_result} {{ - {} - }}", +fn main() {returns_result} {{ + {} +}}", doctest.everything_else ) .unwrap(); } } + let not_running = ignore || scraped_test.langstr.no_run; writeln!( output, " @@ -196,7 +254,7 @@ pub const TEST: test::TestDescAndFn = test::TestDescAndFn {{ }}, testfn: test::StaticTestFn( #[coverage(off)] - || test::assert_test_result({runner}), + || {{{runner}}}, ) }}; }}", @@ -211,10 +269,18 @@ pub const TEST: test::TestDescAndFn = test::TestDescAndFn {{ }, // Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply // don't give it the function to run. - runner = if ignore || scraped_test.langstr.no_run { - "Ok::<(), String>(())" + runner = if not_running { + "test::assert_test_result(Ok::<(), String>(()))".to_string() } else { - "self::main()" + format!( + " +if let Some(bin_path) = crate::__doctest_mod::get_doctest_path() {{ + test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id})) +}} else {{ + test::assert_test_result(self::main()) +}} +", + ) }, ) .unwrap(); From 995858eed36f649d91c04e8095370877e64a0560 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 14 Jun 2024 22:54:53 +0200 Subject: [PATCH 141/178] Don't change indent in merged doctests --- src/librustdoc/doctest/runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index c774f7735bb06..58f40ff11b97f 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -225,7 +225,7 @@ fn generate_mergeable_doctest( output, "\ fn main() {returns_result} {{ - {} +{} }}", doctest.everything_else ) From 475824d8110ffd67065609b0e00e28ad220d62b2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 14 Jun 2024 22:55:44 +0200 Subject: [PATCH 142/178] Mark location doctest as standalone since file information will not work in merged doctest file --- library/core/src/panic/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 8c04994ac0fc4..930edffd50517 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -44,7 +44,7 @@ impl<'a> Location<'a> { /// /// # Examples /// - /// ``` + /// ```standalone /// use std::panic::Location; /// /// /// Returns the [`Location`] at which it is called. From ec4d9101907bcb20cac7ff055fd1c1776292e0bf Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 15 Jun 2024 18:20:58 +0200 Subject: [PATCH 143/178] Correctly handle `internal_features` attribute --- src/librustdoc/doctest/make.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index c67dc4525d2c3..aaa8cb5ccf7d6 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -465,7 +465,19 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> Option } else if attr_name == sym::no_std { Some(AttrKind::NoStd) } else if not_crate_attrs.contains(&attr_name) { - Some(AttrKind::Attr) + // There is one exception to these attributes: + // `#![allow(internal_features)]`. If this attribute is used, we need to + // consider it only as a crate-level attribute. + if attr_name == sym::allow + && let Some(list) = attr.meta_item_list() + && list.iter().any(|sub_attr| { + sub_attr.name_or_empty().as_str() == "internal_features" + }) + { + Some(AttrKind::CrateAttr) + } else { + Some(AttrKind::Attr) + } } else { Some(AttrKind::CrateAttr) } From c5ae545fbd285cfd0aab251585c555021b6992d1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 18 Jun 2024 15:23:01 +0200 Subject: [PATCH 144/178] If no argument is provided to merged doctests binary, they will be run in the same process (needed for miri) --- src/librustdoc/doctest/runner.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 58f40ff11b97f..72ebe33bd0b5e 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -137,6 +137,7 @@ fn main() -> std::process::ExitCode {{ const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}]; let bin_marker = std::ffi::OsStr::new(__doctest_mod::BIN_OPTION); let test_marker = std::ffi::OsStr::new(__doctest_mod::RUN_OPTION); +let test_args = &[{test_args}]; let mut args = std::env::args_os().skip(1); while let Some(arg) = args.next() {{ @@ -145,11 +146,7 @@ while let Some(arg) = args.next() {{ panic!(\"missing argument after `{{}}`\", __doctest_mod::BIN_OPTION); }}; unsafe {{ crate::__doctest_mod::BINARY_PATH = Some(binary.into()); }} - return std::process::Termination::report(test::test_main( - &[{test_args}], - Vec::from(TESTS), - None, - )); + return std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None)); }} else if arg == test_marker {{ let Some(nb_test) = args.next() else {{ panic!(\"missing argument after `{{}}`\", __doctest_mod::RUN_OPTION); @@ -165,7 +162,8 @@ while let Some(arg) = args.next() {{ }} }} -panic!(\"missing argument for merged doctest binary\"); +eprintln!(\"WARNING: No argument provided so doctests will be run in the same process\"); +std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None)) }}", nb_tests = self.nb_tests, output = self.output, From 0bd2c99ce8a3df2ae734748a69b4dc7644b5db1c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 18 Jun 2024 22:49:27 +0200 Subject: [PATCH 145/178] Only show rustdoc doctest compilation output if `nocapture` is used --- src/librustdoc/doctest.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 74adcb415fa78..7028666371bd5 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -542,9 +542,11 @@ fn run_test( return Err(TestFailure::CompileError); } compiler.arg(input_file); - // FIXME: Remove once done fixing bugs. - // FIXME: Should this call only be done if `nocapture` is not set? - // compiler.stderr(Stdio::null()); + if !rustdoc_options.nocapture { + // If `nocapture` is disabled, then we don't display rustc's output when compiling + // the merged doctests. + compiler.stderr(Stdio::null()); + } } else { compiler.arg("-"); compiler.stdin(Stdio::piped()); From 112e44740e8344a139c551864471544b193a6188 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 19 Jun 2024 00:16:05 +0200 Subject: [PATCH 146/178] Disable merged doctests by default --- src/librustdoc/doctest.rs | 2 +- tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 7028666371bd5..74ccc7a871bff 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -772,7 +772,7 @@ struct CreateRunnableDoctests { impl CreateRunnableDoctests { fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests { - let can_merge_doctests = true; //rustdoc_options.edition >= Edition::Edition2024; + let can_merge_doctests = rustdoc_options.edition >= Edition::Edition2024; CreateRunnableDoctests { standalone_tests: Vec::new(), mergeable_tests: FxHashMap::default(), diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout index 71b0b10fa72e2..cb3456e087ebe 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout @@ -5,7 +5,6 @@ test $DIR/failed-doctest-should-panic.rs - Foo (line 9) - should panic ... FAILE failures: ---- $DIR/failed-doctest-should-panic.rs - Foo (line 9) stdout ---- -Hello, world! note: test did not panic as expected failures: From 14f80f9e69be3802792736eb11584d9f39229c48 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 19 Jun 2024 15:29:25 +0200 Subject: [PATCH 147/178] Correctly handle macros using `$crate` in merged doctests --- src/librustdoc/doctest/make.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index aaa8cb5ccf7d6..f7334c1815e73 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -58,6 +58,7 @@ impl DocTest { found_extern_crate, supports_color, has_global_allocator, + has_macro_def, .. }, failed_ast, @@ -85,6 +86,16 @@ impl DocTest { can_be_merged: false, }; }; + // If the AST returned an error, we don't want this doctest to be merged with the + // others. Same if it contains `#[feature]` or `#[no_std]`. + let can_be_merged = can_merge_doctests + && !failed_ast + && !has_no_std + && !has_features + && !has_global_allocator + // If this is a merged doctest and a defined macro uses `$crate`, then the path will + // not work, so better not put it into merged doctests. + && !(has_macro_def && everything_else.contains("$crate")); Self { supports_color, has_main_fn, @@ -95,9 +106,7 @@ impl DocTest { already_has_extern_crate: found_extern_crate, test_id, failed_ast: false, - // If the AST returned an error, we don't want this doctest to be merged with the - // others. Same if it contains `#[feature]` or `#[no_std]`. - can_be_merged: !failed_ast && !has_no_std && !has_features && !has_global_allocator, + can_be_merged, } } @@ -309,6 +318,7 @@ fn parse_source( } } ast::ItemKind::MacCall(..) => info.found_macro = true, + ast::ItemKind::MacroDef(..) => info.has_macro_def = true, _ => {} } } @@ -346,6 +356,7 @@ struct ParseSourceInfo { found_macro: bool, supports_color: bool, has_global_allocator: bool, + has_macro_def: bool, } fn check_for_main_and_extern_crate( From 84d9b67dabd195c21fd210150facd2a8eccac908 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 19 Jun 2024 15:54:08 +0200 Subject: [PATCH 148/178] Add 2024 edition doctests to cover corner cases --- tests/rustdoc-ui/2024-doctests-checks.rs | 41 ++++++++++++++++++++ tests/rustdoc-ui/2024-doctests-checks.stdout | 14 +++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/rustdoc-ui/2024-doctests-checks.rs create mode 100644 tests/rustdoc-ui/2024-doctests-checks.stdout diff --git a/tests/rustdoc-ui/2024-doctests-checks.rs b/tests/rustdoc-ui/2024-doctests-checks.rs new file mode 100644 index 0000000000000..6f62c0f73ad81 --- /dev/null +++ b/tests/rustdoc-ui/2024-doctests-checks.rs @@ -0,0 +1,41 @@ +//@ check-pass +//@ compile-flags: --test --test-args=--test-threads=1 -Zunstable-options --edition 2024 +//@ normalize-stdout-test: "tests/rustdoc-ui" -> "$$DIR" +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test "wrong-ast.rs:\d+:\d+" -> "wrong-ast.rs:$$LINE:$$COL" + +/// This one should fail: crate attributes should remain crate attributes +/// in standalone doctests. +/// +/// ```compile_fail +/// #![deny(missing_docs)] +/// +/// pub struct Bar; +/// ``` +/// +/// This one should not impact the other merged doctests. +/// +/// ``` +/// #![deny(unused)] +/// ``` +/// +/// ``` +/// let x = 12; +/// ``` +/// +/// This one should not be a merged doctest (because of `$crate`): +/// +/// ``` +/// macro_rules! bla { +/// () => {{ +/// $crate::foo(); +/// }} +/// } +/// +/// fn foo() {} +/// +/// fn main() { +/// bla!(); +/// } +/// ``` +pub struct Foo; diff --git a/tests/rustdoc-ui/2024-doctests-checks.stdout b/tests/rustdoc-ui/2024-doctests-checks.stdout new file mode 100644 index 0000000000000..2c0136f767448 --- /dev/null +++ b/tests/rustdoc-ui/2024-doctests-checks.stdout @@ -0,0 +1,14 @@ + +running 2 tests +test $DIR/2024-doctests-checks.rs - Foo (line 18) ... ok +test $DIR/2024-doctests-checks.rs - Foo (line 22) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 2 tests +test $DIR/2024-doctests-checks.rs - Foo (line 10) - compile fail ... ok +test $DIR/2024-doctests-checks.rs - Foo (line 28) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + From 6d552ba44698f7b1aed7fa0b96ad3214d15d433c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 20 Jun 2024 14:51:59 +0200 Subject: [PATCH 149/178] Improve code by removing unneeded function arguments --- src/librustdoc/doctest.rs | 31 +++++++++--------------------- src/librustdoc/doctest/markdown.rs | 2 -- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 74ccc7a871bff..446fc83f9f244 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -151,8 +151,6 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() expanded_args: options.expanded_args.clone(), }; - let test_args = options.test_args.clone(); - let nocapture = options.nocapture; let externs = options.externs.clone(); let json_unused_externs = options.json_unused_externs; @@ -202,15 +200,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() }) })?; - run_tests( - test_args, - nocapture, - opts, - &rustdoc_options, - &unused_extern_reports, - standalone_tests, - mergeable_tests, - ); + run_tests(opts, &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests); let compiling_test_count = compiling_test_count.load(Ordering::SeqCst); @@ -256,16 +246,16 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() } pub(crate) fn run_tests( - mut test_args: Vec, - nocapture: bool, opts: GlobalTestOptions, rustdoc_options: &Arc, unused_extern_reports: &Arc>>, mut standalone_tests: Vec, mergeable_tests: FxHashMap>, ) { + let mut test_args = Vec::with_capacity(rustdoc_options.test_args.len() + 1); test_args.insert(0, "rustdoctest".to_string()); - if nocapture { + test_args.extend_from_slice(&rustdoc_options.test_args); + if rustdoc_options.nocapture { test_args.push("--nocapture".to_string()); } @@ -283,7 +273,7 @@ pub(crate) fn run_tests( let rustdoc_test_options = IndividualTestOptions::new( &rustdoc_options, - &format!("merged_doctest_{edition}"), + &Some(format!("merged_doctest_{edition}")), PathBuf::from(format!("doctest_{edition}.rs")), ); @@ -685,10 +675,10 @@ struct IndividualTestOptions { } impl IndividualTestOptions { - fn new(options: &RustdocOptions, test_id: &str, test_path: PathBuf) -> Self { + fn new(options: &RustdocOptions, test_id: &Option, test_path: PathBuf) -> Self { let outdir = if let Some(ref path) = options.persist_doctests { let mut path = path.clone(); - path.push(&test_id); + path.push(&test_id.as_deref().unwrap_or_else(|| "")); if let Err(err) = std::fs::create_dir_all(&path) { eprintln!("Couldn't create directory for doctest executables: {err}"); @@ -858,11 +848,8 @@ fn generate_test_desc_and_fn( unused_externs: Arc>>, ) -> test::TestDescAndFn { let target_str = rustdoc_options.target.to_string(); - let rustdoc_test_options = IndividualTestOptions::new( - &rustdoc_options, - test.test_id.as_deref().unwrap_or_else(|| ""), - scraped_test.path(), - ); + let rustdoc_test_options = + IndividualTestOptions::new(&rustdoc_options, &test.test_id, scraped_test.path()); debug!("creating test {}: {}", scraped_test.name, scraped_test.text); test::TestDescAndFn { diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index 5f821634a82d2..5c9fd1e949c90 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -118,8 +118,6 @@ pub(crate) fn test(options: Options) -> Result<(), String> { let CreateRunnableDoctests { opts, rustdoc_options, standalone_tests, mergeable_tests, .. } = collector; crate::doctest::run_tests( - options.test_args, - options.nocapture, opts, &rustdoc_options, &Arc::new(Mutex::new(Vec::new())), From bfabf1db446349e3cfce48f4fdd6d734e7031d5b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 20 Jun 2024 15:42:46 +0200 Subject: [PATCH 150/178] Add documentation on `DocTest` and `RunnableDoctest` structs --- src/librustdoc/doctest.rs | 1 + src/librustdoc/doctest/make.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 446fc83f9f244..000683d069359 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -433,6 +433,7 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com command } +/// This struct contains information needed for running a doctest. struct RunnableDoctest { full_test_code: String, full_test_line_offset: usize, diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index f7334c1815e73..5d248586739de 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -17,6 +17,8 @@ use rustc_span::FileName; use super::GlobalTestOptions; +/// This struct contains information about the doctest itself which is then used to generate +/// doctest source code appropriately. pub(crate) struct DocTest { pub(crate) supports_color: bool, pub(crate) already_has_extern_crate: bool, From d512438435aa1ee1731e2d5a45d01b6f14493e15 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 20 Jun 2024 15:55:18 +0200 Subject: [PATCH 151/178] Run mergeable doctest as part of standalone doctests if there is only one --- src/librustdoc/doctest.rs | 77 ++++++++++--------- tests/rustdoc-ui/2024-doctests-checks.rs | 2 +- .../failed-doctest-should-panic.stdout | 4 +- .../rustdoc-ui/doctest/wrong-ast-2024.stdout | 11 +-- tests/rustdoc-ui/run-as-standalone.rs | 17 ++++ tests/rustdoc-ui/run-as-standalone.stdout | 7 ++ 6 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 tests/rustdoc-ui/run-as-standalone.rs create mode 100644 tests/rustdoc-ui/run-as-standalone.stdout diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 000683d069359..d0e08d07e2409 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -267,50 +267,53 @@ pub(crate) fn run_tests( if doctests.is_empty() { continue; } - doctests.sort_by(|(_, a), (_, b)| a.name.cmp(&b.name)); + // If there is only one mergeable doctest, the cost to run it would be higher than just + // running it alonside standalone doctests. + if doctests.len() > 1 { + doctests.sort_by(|(_, a), (_, b)| a.name.cmp(&b.name)); - let mut tests_runner = runner::DocTestRunner::new(); + let mut tests_runner = runner::DocTestRunner::new(); - let rustdoc_test_options = IndividualTestOptions::new( - &rustdoc_options, - &Some(format!("merged_doctest_{edition}")), - PathBuf::from(format!("doctest_{edition}.rs")), - ); + let rustdoc_test_options = IndividualTestOptions::new( + &rustdoc_options, + &Some(format!("merged_doctest_{edition}")), + PathBuf::from(format!("doctest_{edition}.rs")), + ); - for (doctest, scraped_test) in &doctests { - tests_runner.add_test(doctest, scraped_test, &target_str); - } - if let Ok(success) = tests_runner.run_merged_tests( - rustdoc_test_options, - edition, - &opts, - &test_args, - rustdoc_options, - ) { - ran_edition_tests += 1; - if !success { - nb_errors += 1; + for (doctest, scraped_test) in &doctests { + tests_runner.add_test(doctest, scraped_test, &target_str); + } + if let Ok(success) = tests_runner.run_merged_tests( + rustdoc_test_options, + edition, + &opts, + &test_args, + rustdoc_options, + ) { + ran_edition_tests += 1; + if !success { + nb_errors += 1; + } + continue; } - continue; - } else { // We failed to compile all compatible tests as one so we push them into the // `standalone_tests` doctests. debug!("Failed to compile compatible doctests for edition {} all at once", edition); - for (doctest, scraped_test) in doctests { - doctest.generate_unique_doctest( - &scraped_test.text, - scraped_test.langstr.test_harness, - &opts, - Some(&opts.crate_name), - ); - standalone_tests.push(generate_test_desc_and_fn( - doctest, - scraped_test, - opts.clone(), - Arc::clone(&rustdoc_options), - unused_extern_reports.clone(), - )); - } + } + for (doctest, scraped_test) in doctests { + doctest.generate_unique_doctest( + &scraped_test.text, + scraped_test.langstr.test_harness, + &opts, + Some(&opts.crate_name), + ); + standalone_tests.push(generate_test_desc_and_fn( + doctest, + scraped_test, + opts.clone(), + Arc::clone(&rustdoc_options), + unused_extern_reports.clone(), + )); } } diff --git a/tests/rustdoc-ui/2024-doctests-checks.rs b/tests/rustdoc-ui/2024-doctests-checks.rs index 6f62c0f73ad81..f6b6faa8c6a2c 100644 --- a/tests/rustdoc-ui/2024-doctests-checks.rs +++ b/tests/rustdoc-ui/2024-doctests-checks.rs @@ -2,7 +2,7 @@ //@ compile-flags: --test --test-args=--test-threads=1 -Zunstable-options --edition 2024 //@ normalize-stdout-test: "tests/rustdoc-ui" -> "$$DIR" //@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ normalize-stdout-test "wrong-ast.rs:\d+:\d+" -> "wrong-ast.rs:$$LINE:$$COL" +//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" /// This one should fail: crate attributes should remain crate attributes /// in standalone doctests. diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout index cb3456e087ebe..57a20092a5d6c 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout @@ -1,11 +1,11 @@ running 1 test -test $DIR/failed-doctest-should-panic.rs - Foo (line 9) - should panic ... FAILED +test $DIR/failed-doctest-should-panic.rs - Foo (line 9) ... FAILED failures: ---- $DIR/failed-doctest-should-panic.rs - Foo (line 9) stdout ---- -note: test did not panic as expected +Test executable succeeded, but it's marked `should_panic`. failures: $DIR/failed-doctest-should-panic.rs - Foo (line 9) diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout index 22c8ce468fd7c..08ec69199d390 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout @@ -1,12 +1,7 @@ -running 1 test -test $DIR/wrong-ast-2024.rs - three (line 17) - should panic ... ok - -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - - -running 2 tests +running 3 tests test $DIR/wrong-ast-2024.rs - one (line 7) ... FAILED +test $DIR/wrong-ast-2024.rs - three (line 17) ... ok test $DIR/wrong-ast-2024.rs - two (line 12) ... FAILED failures: @@ -37,5 +32,5 @@ failures: $DIR/wrong-ast-2024.rs - one (line 7) $DIR/wrong-ast-2024.rs - two (line 12) -test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/run-as-standalone.rs b/tests/rustdoc-ui/run-as-standalone.rs new file mode 100644 index 0000000000000..a0fb0340f8dfb --- /dev/null +++ b/tests/rustdoc-ui/run-as-standalone.rs @@ -0,0 +1,17 @@ +// This test ensures that if there is only one mergeable doctest, then it is +// instead run as part of standalone doctests. + +//@ compile-flags:--test --test-args=--test-threads=1 -Zunstable-options --edition 2024 +//@ normalize-stdout-test: "tests/rustdoc-ui" -> "$$DIR" +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" +//@ check-pass + +/// ``` +/// let x = 12; +/// ``` +/// +/// ```compile_fail +/// let y = x; +/// ``` +pub fn one() {} diff --git a/tests/rustdoc-ui/run-as-standalone.stdout b/tests/rustdoc-ui/run-as-standalone.stdout new file mode 100644 index 0000000000000..ec66f6753b10a --- /dev/null +++ b/tests/rustdoc-ui/run-as-standalone.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test $DIR/run-as-standalone.rs - one (line 10) ... ok +test $DIR/run-as-standalone.rs - one (line 14) - compile fail ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + From 1e1743a379885209ed969695d56762d38a9da09c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 20 Jun 2024 16:39:52 +0200 Subject: [PATCH 152/178] Reduce merged doctest source code size --- library/test/src/types.rs | 34 ++++++++++++++++++++++++++++++++ src/librustdoc/doctest/runner.rs | 32 +++++++----------------------- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/library/test/src/types.rs b/library/test/src/types.rs index c3be3466cb928..802cab989c6a9 100644 --- a/library/test/src/types.rs +++ b/library/test/src/types.rs @@ -250,3 +250,37 @@ pub struct TestDescAndFn { pub desc: TestDesc, pub testfn: TestFn, } + +impl TestDescAndFn { + pub const fn new_doctest( + test_name: &'static str, + ignore: bool, + source_file: &'static str, + start_line: usize, + no_run: bool, + should_panic: bool, + testfn: TestFn, + ) -> Self { + Self { + desc: TestDesc { + name: StaticTestName(test_name), + ignore, + ignore_message: None, + source_file, + start_line, + start_col: 0, + end_line: 0, + end_col: 0, + compile_fail: false, + no_run, + should_panic: if should_panic { + options::ShouldPanic::Yes + } else { + options::ShouldPanic::No + }, + test_type: TestType::DocTest, + }, + testfn, + } + } +} diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 72ebe33bd0b5e..55673fd562e32 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -235,36 +235,18 @@ fn main() {returns_result} {{ output, " #[rustc_test_marker = {test_name:?}] -pub const TEST: test::TestDescAndFn = test::TestDescAndFn {{ - desc: test::TestDesc {{ - name: test::StaticTestName({test_name:?}), - ignore: {ignore}, - ignore_message: None, - source_file: {file:?}, - start_line: {line}, - start_col: 0, - end_line: 0, - end_col: 0, - compile_fail: false, - no_run: {no_run}, - should_panic: test::ShouldPanic::{should_panic}, - test_type: test::TestType::DocTest, - }}, - testfn: test::StaticTestFn( - #[coverage(off)] - || {{{runner}}}, - ) -}}; +pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest( +{test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic}, +test::StaticTestFn( + #[coverage(off)] + || {{{runner}}}, +)); }}", test_name = scraped_test.name, file = scraped_test.path(), line = scraped_test.line, no_run = scraped_test.langstr.no_run, - should_panic = if !scraped_test.langstr.no_run && scraped_test.langstr.should_panic { - "Yes" - } else { - "No" - }, + should_panic = !scraped_test.langstr.no_run && scraped_test.langstr.should_panic, // Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply // don't give it the function to run. runner = if not_running { From ffc18c5cfb4deeb7d04544fcb3561a952d95db1f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 23 Jun 2024 14:33:13 +0200 Subject: [PATCH 153/178] Rename `DocTest` into `DocTestBuilder` --- src/librustdoc/doctest.rs | 14 +++++++------- src/librustdoc/doctest/make.rs | 4 ++-- src/librustdoc/doctest/runner.rs | 8 ++++---- src/librustdoc/doctest/tests.rs | 11 ++++++++--- src/librustdoc/html/markdown.rs | 2 +- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index d0e08d07e2409..f0a13c9923090 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::{panic, str}; -pub(crate) use make::DocTest; +pub(crate) use make::DocTestBuilder; pub(crate) use markdown::test as test_markdown; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -250,7 +250,7 @@ pub(crate) fn run_tests( rustdoc_options: &Arc, unused_extern_reports: &Arc>>, mut standalone_tests: Vec, - mergeable_tests: FxHashMap>, + mergeable_tests: FxHashMap>, ) { let mut test_args = Vec::with_capacity(rustdoc_options.test_args.len() + 1); test_args.insert(0, "rustdoctest".to_string()); @@ -754,7 +754,7 @@ pub(crate) trait DoctestVisitor { struct CreateRunnableDoctests { standalone_tests: Vec, - mergeable_tests: FxHashMap>, + mergeable_tests: FxHashMap>, rustdoc_options: Arc, opts: GlobalTestOptions, @@ -803,7 +803,7 @@ impl CreateRunnableDoctests { ); let edition = scraped_test.edition(&self.rustdoc_options); - let doctest = DocTest::new( + let doctest = DocTestBuilder::new( &scraped_test.text, Some(&self.opts.crate_name), edition, @@ -827,7 +827,7 @@ impl CreateRunnableDoctests { fn generate_test_desc_and_fn( &mut self, - test: DocTest, + test: DocTestBuilder, scraped_test: ScrapedDoctest, ) -> test::TestDescAndFn { if !scraped_test.langstr.compile_fail { @@ -845,7 +845,7 @@ impl CreateRunnableDoctests { } fn generate_test_desc_and_fn( - test: DocTest, + test: DocTestBuilder, scraped_test: ScrapedDoctest, opts: GlobalTestOptions, rustdoc_options: Arc, @@ -892,7 +892,7 @@ fn generate_test_desc_and_fn( fn doctest_run_fn( test_opts: IndividualTestOptions, global_opts: GlobalTestOptions, - doctest: DocTest, + doctest: DocTestBuilder, scraped_test: ScrapedDoctest, rustdoc_options: Arc, unused_externs: Arc>>, diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 5d248586739de..ed09fa8e88ebd 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -19,7 +19,7 @@ use super::GlobalTestOptions; /// This struct contains information about the doctest itself which is then used to generate /// doctest source code appropriately. -pub(crate) struct DocTest { +pub(crate) struct DocTestBuilder { pub(crate) supports_color: bool, pub(crate) already_has_extern_crate: bool, pub(crate) has_main_fn: bool, @@ -34,7 +34,7 @@ pub(crate) struct DocTest { pub(crate) can_be_merged: bool, } -impl DocTest { +impl DocTestBuilder { pub(crate) fn new( source: &str, crate_name: Option<&str>, diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 55673fd562e32..5c0a7bcaef9d2 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -4,8 +4,8 @@ use rustc_span::edition::Edition; use std::fmt::Write; use crate::doctest::{ - run_test, DocTest, GlobalTestOptions, IndividualTestOptions, RunnableDoctest, RustdocOptions, - ScrapedDoctest, TestFailure, UnusedExterns, + run_test, DocTestBuilder, GlobalTestOptions, IndividualTestOptions, RunnableDoctest, + RustdocOptions, ScrapedDoctest, TestFailure, UnusedExterns, }; use crate::html::markdown::{Ignore, LangString}; @@ -31,7 +31,7 @@ impl DocTestRunner { pub(crate) fn add_test( &mut self, - doctest: &DocTest, + doctest: &DocTestBuilder, scraped_test: &ScrapedDoctest, target_str: &str, ) { @@ -193,7 +193,7 @@ std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), N /// Push new doctest content into `output`. Returns the test ID for this doctest. fn generate_mergeable_doctest( - doctest: &DocTest, + doctest: &DocTestBuilder, scraped_test: &ScrapedDoctest, ignore: bool, id: usize, diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 982bfae588376..8c4079bcd8143 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use super::{DocTest, GlobalTestOptions}; +use super::{DocTestBuilder, GlobalTestOptions}; use rustc_span::edition::DEFAULT_EDITION; fn make_test( @@ -10,8 +10,13 @@ fn make_test( opts: &GlobalTestOptions, test_id: Option<&str>, ) -> (String, usize) { - let doctest = - DocTest::new(test_code, crate_name, DEFAULT_EDITION, false, test_id.map(|s| s.to_string())); + let doctest = DocTestBuilder::new( + test_code, + crate_name, + DEFAULT_EDITION, + false, + test_id.map(|s| s.to_string()), + ); let (code, line_offset) = doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name); (code, line_offset) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 879e44c171223..971337dda1e8f 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -297,7 +297,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { attrs: vec![], args_file: PathBuf::new(), }; - let doctest = doctest::DocTest::new(&test, krate, edition, false, None); + let doctest = doctest::DocTestBuilder::new(&test, krate, edition, false, None); let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; From 0fbc32ce42ac842a98771b1c11c3f358f43d7814 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 23 Jun 2024 14:56:27 +0200 Subject: [PATCH 154/178] If there are crate attributes, we prevent doctest to be merged with others --- src/librustdoc/doctest/make.rs | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index ed09fa8e88ebd..848f447d4a842 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -43,14 +43,8 @@ impl DocTestBuilder { // If `test_id` is `None`, it means we're generating code for a code example "run" link. test_id: Option, ) -> Self { - let SourceInfo { - crate_attrs, - maybe_crate_attrs, - crates, - everything_else, - has_features, - has_no_std, - } = partition_source(source, edition); + let SourceInfo { crate_attrs, maybe_crate_attrs, crates, everything_else } = + partition_source(source, edition); // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern // crate already is included. @@ -92,9 +86,8 @@ impl DocTestBuilder { // others. Same if it contains `#[feature]` or `#[no_std]`. let can_be_merged = can_merge_doctests && !failed_ast - && !has_no_std - && !has_features && !has_global_allocator + && crate_attrs.is_empty() // If this is a merged doctest and a defined macro uses `$crate`, then the path will // not work, so better not put it into merged doctests. && !(has_macro_def && everything_else.contains("$crate")); @@ -427,8 +420,6 @@ fn check_for_main_and_extern_crate( enum AttrKind { CrateAttr, Attr, - Feature, - NoStd, } /// Returns `Some` if the attribute is complete and `Some(true)` if it is an attribute that can be @@ -473,11 +464,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> Option Ok(attr) => { let attr_name = attr.name_or_empty(); - if attr_name == sym::feature { - Some(AttrKind::Feature) - } else if attr_name == sym::no_std { - Some(AttrKind::NoStd) - } else if not_crate_attrs.contains(&attr_name) { + if not_crate_attrs.contains(&attr_name) { // There is one exception to these attributes: // `#![allow(internal_features)]`. If this attribute is used, we need to // consider it only as a crate-level attribute. @@ -511,14 +498,6 @@ fn handle_attr(mod_attr_pending: &mut String, source_info: &mut SourceInfo, edit let push_to = match attr_kind { AttrKind::CrateAttr => &mut source_info.crate_attrs, AttrKind::Attr => &mut source_info.maybe_crate_attrs, - AttrKind::Feature => { - source_info.has_features = true; - &mut source_info.crate_attrs - } - AttrKind::NoStd => { - source_info.has_no_std = true; - &mut source_info.crate_attrs - } }; push_to.push_str(mod_attr_pending); push_to.push('\n'); @@ -535,8 +514,6 @@ struct SourceInfo { maybe_crate_attrs: String, crates: String, everything_else: String, - has_features: bool, - has_no_std: bool, } fn partition_source(s: &str, edition: Edition) -> SourceInfo { From a3bc2c74786631846554915c7e1295ee48f5be85 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 24 Jun 2024 13:26:02 +0200 Subject: [PATCH 155/178] Improve code readability --- src/librustdoc/doctest.rs | 13 +++++++++---- src/librustdoc/doctest/make.rs | 8 ++++++++ src/librustdoc/doctest/tests.rs | 1 + src/librustdoc/html/markdown.rs | 2 +- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index f0a13c9923090..812e129865572 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -448,6 +448,12 @@ struct RunnableDoctest { no_run: bool, } +impl RunnableDoctest { + fn path_for_merged_doctest(&self) -> PathBuf { + self.test_opts.outdir.path().join(&format!("doctest_{}.rs", self.edition)) + } +} + fn run_test( doctest: RunnableDoctest, rustdoc_options: &RustdocOptions, @@ -528,8 +534,7 @@ fn run_test( if is_multiple_tests { // It makes the compilation failure much faster if it is for a combined doctest. compiler.arg("--error-format=short"); - let input_file = - doctest.test_opts.outdir.path().join(&format!("doctest_{}.rs", doctest.edition)); + let input_file = doctest.path_for_merged_doctest(); if std::fs::write(&input_file, &doctest.full_test_code).is_err() { // If we cannot write this file for any reason, we leave. All combined tests will be // tested as standalone tests. @@ -809,9 +814,9 @@ impl CreateRunnableDoctests { edition, self.can_merge_doctests, Some(test_id), + Some(&scraped_test.langstr), ); - let is_standalone = !self.can_merge_doctests - || !doctest.can_be_merged + let is_standalone = !doctest.can_be_merged || scraped_test.langstr.compile_fail || scraped_test.langstr.test_harness || scraped_test.langstr.standalone diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 848f447d4a842..1e8e403985bc8 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -1,6 +1,8 @@ //! Logic for transforming the raw code given by the user into something actually //! runnable, e.g. by adding a `main` function if it doesn't already exist. +use crate::html::markdown::LangString; + use std::io; use rustc_ast as ast; @@ -42,7 +44,13 @@ impl DocTestBuilder { can_merge_doctests: bool, // If `test_id` is `None`, it means we're generating code for a code example "run" link. test_id: Option, + lang_str: Option<&LangString>, ) -> Self { + let can_merge_doctests = can_merge_doctests + && lang_str.is_some_and(|lang_str| { + !lang_str.compile_fail && !lang_str.test_harness && !lang_str.standalone + }); + let SourceInfo { crate_attrs, maybe_crate_attrs, crates, everything_else } = partition_source(source, edition); diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 8c4079bcd8143..b076b6dccf5c7 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -16,6 +16,7 @@ fn make_test( DEFAULT_EDITION, false, test_id.map(|s| s.to_string()), + None, ); let (code, line_offset) = doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name); diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 971337dda1e8f..bc30f7fa43b2b 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -297,7 +297,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { attrs: vec![], args_file: PathBuf::new(), }; - let doctest = doctest::DocTestBuilder::new(&test, krate, edition, false, None); + let doctest = doctest::DocTestBuilder::new(&test, krate, edition, false, None, None); let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; From 72aeffa78cccc8f368d6e8ad8027894e0b152f23 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 8 Jul 2024 14:40:36 +0200 Subject: [PATCH 156/178] Remove need for `unsafe` code in merged doctests --- src/librustdoc/doctest/runner.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 5c0a7bcaef9d2..46122bbeafd6f 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -107,13 +107,16 @@ impl DocTestRunner { {output} mod __doctest_mod {{ - pub static mut BINARY_PATH: Option = None; + use std::sync::OnceLock; + use std::path::PathBuf; + + pub static BINARY_PATH: OnceLock = OnceLock::new(); pub const RUN_OPTION: &str = \"*doctest-inner-test\"; pub const BIN_OPTION: &str = \"*doctest-bin-path\"; #[allow(unused)] - pub fn get_doctest_path() -> Option<&'static std::path::Path> {{ - unsafe {{ self::BINARY_PATH.as_deref() }} + pub fn doctest_path() -> Option<&'static PathBuf> {{ + self::BINARY_PATH.get() }} #[allow(unused)] @@ -145,7 +148,9 @@ while let Some(arg) = args.next() {{ let Some(binary) = args.next() else {{ panic!(\"missing argument after `{{}}`\", __doctest_mod::BIN_OPTION); }}; - unsafe {{ crate::__doctest_mod::BINARY_PATH = Some(binary.into()); }} + if crate::__doctest_mod::BINARY_PATH.set(binary.into()).is_err() {{ + panic!(\"`{{}}` option was used more than once\", bin_marker.to_string_lossy()); + }} return std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None)); }} else if arg == test_marker {{ let Some(nb_test) = args.next() else {{ @@ -254,7 +259,7 @@ test::StaticTestFn( } else { format!( " -if let Some(bin_path) = crate::__doctest_mod::get_doctest_path() {{ +if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{ test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id})) }} else {{ test::assert_test_result(self::main()) From 4b1db071d13a2c27ac1fb33158bcab93b56fb761 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 8 Jul 2024 15:01:17 +0200 Subject: [PATCH 157/178] Don't special-case if there is only one merged doctest --- src/librustdoc/doctest.rs | 52 +++++++++---------- .../failed-doctest-should-panic.stdout | 4 +- .../rustdoc-ui/doctest/wrong-ast-2024.stdout | 11 ++-- tests/rustdoc-ui/doctest/wrong-ast.rs | 1 - tests/rustdoc-ui/doctest/wrong-ast.stdout | 18 +++---- tests/rustdoc-ui/run-as-standalone.rs | 17 ------ tests/rustdoc-ui/run-as-standalone.stdout | 7 --- 7 files changed, 43 insertions(+), 67 deletions(-) delete mode 100644 tests/rustdoc-ui/run-as-standalone.rs delete mode 100644 tests/rustdoc-ui/run-as-standalone.stdout diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 812e129865572..24e5e8761126b 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -267,39 +267,35 @@ pub(crate) fn run_tests( if doctests.is_empty() { continue; } - // If there is only one mergeable doctest, the cost to run it would be higher than just - // running it alonside standalone doctests. - if doctests.len() > 1 { - doctests.sort_by(|(_, a), (_, b)| a.name.cmp(&b.name)); + doctests.sort_by(|(_, a), (_, b)| a.name.cmp(&b.name)); - let mut tests_runner = runner::DocTestRunner::new(); + let mut tests_runner = runner::DocTestRunner::new(); - let rustdoc_test_options = IndividualTestOptions::new( - &rustdoc_options, - &Some(format!("merged_doctest_{edition}")), - PathBuf::from(format!("doctest_{edition}.rs")), - ); + let rustdoc_test_options = IndividualTestOptions::new( + &rustdoc_options, + &Some(format!("merged_doctest_{edition}")), + PathBuf::from(format!("doctest_{edition}.rs")), + ); - for (doctest, scraped_test) in &doctests { - tests_runner.add_test(doctest, scraped_test, &target_str); - } - if let Ok(success) = tests_runner.run_merged_tests( - rustdoc_test_options, - edition, - &opts, - &test_args, - rustdoc_options, - ) { - ran_edition_tests += 1; - if !success { - nb_errors += 1; - } - continue; + for (doctest, scraped_test) in &doctests { + tests_runner.add_test(doctest, scraped_test, &target_str); + } + if let Ok(success) = tests_runner.run_merged_tests( + rustdoc_test_options, + edition, + &opts, + &test_args, + rustdoc_options, + ) { + ran_edition_tests += 1; + if !success { + nb_errors += 1; } - // We failed to compile all compatible tests as one so we push them into the - // `standalone_tests` doctests. - debug!("Failed to compile compatible doctests for edition {} all at once", edition); + continue; } + // We failed to compile all compatible tests as one so we push them into the + // `standalone_tests` doctests. + debug!("Failed to compile compatible doctests for edition {} all at once", edition); for (doctest, scraped_test) in doctests { doctest.generate_unique_doctest( &scraped_test.text, diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout index 57a20092a5d6c..cb3456e087ebe 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout @@ -1,11 +1,11 @@ running 1 test -test $DIR/failed-doctest-should-panic.rs - Foo (line 9) ... FAILED +test $DIR/failed-doctest-should-panic.rs - Foo (line 9) - should panic ... FAILED failures: ---- $DIR/failed-doctest-should-panic.rs - Foo (line 9) stdout ---- -Test executable succeeded, but it's marked `should_panic`. +note: test did not panic as expected failures: $DIR/failed-doctest-should-panic.rs - Foo (line 9) diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout index 08ec69199d390..22c8ce468fd7c 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout @@ -1,7 +1,12 @@ -running 3 tests +running 1 test +test $DIR/wrong-ast-2024.rs - three (line 17) - should panic ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 2 tests test $DIR/wrong-ast-2024.rs - one (line 7) ... FAILED -test $DIR/wrong-ast-2024.rs - three (line 17) ... ok test $DIR/wrong-ast-2024.rs - two (line 12) ... FAILED failures: @@ -32,5 +37,5 @@ failures: $DIR/wrong-ast-2024.rs - one (line 7) $DIR/wrong-ast-2024.rs - two (line 12) -test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/doctest/wrong-ast.rs b/tests/rustdoc-ui/doctest/wrong-ast.rs index dae86fbfc59a1..e8faaea97ee20 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast.rs +++ b/tests/rustdoc-ui/doctest/wrong-ast.rs @@ -1,7 +1,6 @@ //@ compile-flags:--test --test-args=--test-threads=1 //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" //@ failure-status: 101 /// ``` diff --git a/tests/rustdoc-ui/doctest/wrong-ast.stdout b/tests/rustdoc-ui/doctest/wrong-ast.stdout index c827254d8c0f5..15494706c1643 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast.stdout +++ b/tests/rustdoc-ui/doctest/wrong-ast.stdout @@ -1,14 +1,14 @@ running 3 tests -test $DIR/wrong-ast.rs - one (line 7) ... FAILED -test $DIR/wrong-ast.rs - three (line 17) ... ok -test $DIR/wrong-ast.rs - two (line 12) ... FAILED +test $DIR/wrong-ast.rs - one (line 6) ... FAILED +test $DIR/wrong-ast.rs - three (line 16) ... ok +test $DIR/wrong-ast.rs - two (line 11) ... FAILED failures: ----- $DIR/wrong-ast.rs - one (line 7) stdout ---- +---- $DIR/wrong-ast.rs - one (line 6) stdout ---- error[E0758]: unterminated block comment - --> $DIR/wrong-ast.rs:$LINE:$COL + --> $DIR/wrong-ast.rs:7:1 | LL | /* plop | ^^^^^^^ @@ -17,9 +17,9 @@ error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0758`. Couldn't compile the test. ----- $DIR/wrong-ast.rs - two (line 12) stdout ---- +---- $DIR/wrong-ast.rs - two (line 11) stdout ---- error: unexpected closing delimiter: `}` - --> $DIR/wrong-ast.rs:$LINE:$COL + --> $DIR/wrong-ast.rs:12:1 | LL | } mod __doctest_1 { fn main() { | ^ unexpected closing delimiter @@ -29,8 +29,8 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/wrong-ast.rs - one (line 7) - $DIR/wrong-ast.rs - two (line 12) + $DIR/wrong-ast.rs - one (line 6) + $DIR/wrong-ast.rs - two (line 11) test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/run-as-standalone.rs b/tests/rustdoc-ui/run-as-standalone.rs deleted file mode 100644 index a0fb0340f8dfb..0000000000000 --- a/tests/rustdoc-ui/run-as-standalone.rs +++ /dev/null @@ -1,17 +0,0 @@ -// This test ensures that if there is only one mergeable doctest, then it is -// instead run as part of standalone doctests. - -//@ compile-flags:--test --test-args=--test-threads=1 -Zunstable-options --edition 2024 -//@ normalize-stdout-test: "tests/rustdoc-ui" -> "$$DIR" -//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" -//@ check-pass - -/// ``` -/// let x = 12; -/// ``` -/// -/// ```compile_fail -/// let y = x; -/// ``` -pub fn one() {} diff --git a/tests/rustdoc-ui/run-as-standalone.stdout b/tests/rustdoc-ui/run-as-standalone.stdout deleted file mode 100644 index ec66f6753b10a..0000000000000 --- a/tests/rustdoc-ui/run-as-standalone.stdout +++ /dev/null @@ -1,7 +0,0 @@ - -running 2 tests -test $DIR/run-as-standalone.rs - one (line 10) ... ok -test $DIR/run-as-standalone.rs - one (line 14) - compile fail ... ok - -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - From ab3d90e037a3f37e6130b1aec0aca89f2e0de90b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 9 Jul 2024 12:07:18 +0200 Subject: [PATCH 158/178] Unify naming of `DocTest` --- src/librustdoc/doctest.rs | 47 ++++++++++--------- src/librustdoc/doctest/markdown.rs | 12 ++--- src/librustdoc/doctest/runner.rs | 10 ++-- src/librustdoc/doctest/rust.rs | 13 +++-- src/librustdoc/html/markdown.rs | 4 +- .../passes/check_doc_test_visibility.rs | 2 +- 6 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 24e5e8761126b..91b93a9990323 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -163,7 +163,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() let args_path = temp_dir.path().join("rustdoc-cfgs"); crate::wrap_return(dcx, generate_args_file(&args_path, &options))?; - let CreateRunnableDoctests { + let CreateRunnableDocTests { standalone_tests, mergeable_tests, rustdoc_options, @@ -179,7 +179,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() let opts = scrape_test_config(crate_name, crate_attrs, args_path); let enable_per_target_ignores = options.enable_per_target_ignores; - let mut collector = CreateRunnableDoctests::new(options, opts); + let mut collector = CreateRunnableDocTests::new(options, opts); let hir_collector = HirCollector::new( &compiler.sess, tcx.hir(), @@ -250,7 +250,7 @@ pub(crate) fn run_tests( rustdoc_options: &Arc, unused_extern_reports: &Arc>>, mut standalone_tests: Vec, - mergeable_tests: FxHashMap>, + mergeable_tests: FxHashMap>, ) { let mut test_args = Vec::with_capacity(rustdoc_options.test_args.len() + 1); test_args.insert(0, "rustdoctest".to_string()); @@ -432,8 +432,13 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com command } -/// This struct contains information needed for running a doctest. -struct RunnableDoctest { +/// Information needed for running a bundle of doctests. +/// +/// This data structure contains the "full" test code, including the wrappers +/// (if multiple doctests are merged), `main` function, +/// and everything needed to calculate the compiler's command-line arguments. +/// The `# ` prefix on boring lines has also been stripped. +struct RunnableDocTest { full_test_code: String, full_test_line_offset: usize, test_opts: IndividualTestOptions, @@ -444,14 +449,14 @@ struct RunnableDoctest { no_run: bool, } -impl RunnableDoctest { +impl RunnableDocTest { fn path_for_merged_doctest(&self) -> PathBuf { self.test_opts.outdir.path().join(&format!("doctest_{}.rs", self.edition)) } } fn run_test( - doctest: RunnableDoctest, + doctest: RunnableDocTest, rustdoc_options: &RustdocOptions, supports_color: bool, is_multiple_tests: bool, @@ -700,7 +705,7 @@ impl IndividualTestOptions { } /// A doctest scraped from the code, ready to be turned into a runnable test. -pub(crate) struct ScrapedDoctest { +pub(crate) struct ScrapedDocTest { filename: FileName, line: usize, langstr: LangString, @@ -708,7 +713,7 @@ pub(crate) struct ScrapedDoctest { name: String, } -impl ScrapedDoctest { +impl ScrapedDocTest { fn new( filename: FileName, line: usize, @@ -748,14 +753,14 @@ impl ScrapedDoctest { } } -pub(crate) trait DoctestVisitor { +pub(crate) trait DocTestVisitor { fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine); fn visit_header(&mut self, _name: &str, _level: u32) {} } -struct CreateRunnableDoctests { +struct CreateRunnableDocTests { standalone_tests: Vec, - mergeable_tests: FxHashMap>, + mergeable_tests: FxHashMap>, rustdoc_options: Arc, opts: GlobalTestOptions, @@ -765,10 +770,10 @@ struct CreateRunnableDoctests { can_merge_doctests: bool, } -impl CreateRunnableDoctests { - fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests { +impl CreateRunnableDocTests { + fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDocTests { let can_merge_doctests = rustdoc_options.edition >= Edition::Edition2024; - CreateRunnableDoctests { + CreateRunnableDocTests { standalone_tests: Vec::new(), mergeable_tests: FxHashMap::default(), rustdoc_options: Arc::new(rustdoc_options), @@ -780,7 +785,7 @@ impl CreateRunnableDoctests { } } - fn add_test(&mut self, scraped_test: ScrapedDoctest) { + fn add_test(&mut self, scraped_test: ScrapedDocTest) { // For example `module/file.rs` would become `module_file_rs` let file = scraped_test .filename @@ -829,7 +834,7 @@ impl CreateRunnableDoctests { fn generate_test_desc_and_fn( &mut self, test: DocTestBuilder, - scraped_test: ScrapedDoctest, + scraped_test: ScrapedDocTest, ) -> test::TestDescAndFn { if !scraped_test.langstr.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); @@ -847,7 +852,7 @@ impl CreateRunnableDoctests { fn generate_test_desc_and_fn( test: DocTestBuilder, - scraped_test: ScrapedDoctest, + scraped_test: ScrapedDocTest, opts: GlobalTestOptions, rustdoc_options: Arc, unused_externs: Arc>>, @@ -894,7 +899,7 @@ fn doctest_run_fn( test_opts: IndividualTestOptions, global_opts: GlobalTestOptions, doctest: DocTestBuilder, - scraped_test: ScrapedDoctest, + scraped_test: ScrapedDocTest, rustdoc_options: Arc, unused_externs: Arc>>, ) -> Result<(), String> { @@ -907,7 +912,7 @@ fn doctest_run_fn( &global_opts, Some(&global_opts.crate_name), ); - let runnable_test = RunnableDoctest { + let runnable_test = RunnableDocTest { full_test_code, full_test_line_offset, test_opts, @@ -980,7 +985,7 @@ fn doctest_run_fn( } #[cfg(test)] // used in tests -impl DoctestVisitor for Vec { +impl DocTestVisitor for Vec { fn visit_test(&mut self, _test: String, _config: LangString, rel_line: MdRelLine) { self.push(1 + rel_line.offset()); } diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index 5c9fd1e949c90..4806d86558997 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -7,23 +7,23 @@ use rustc_span::FileName; use tempfile::tempdir; use super::{ - generate_args_file, CreateRunnableDoctests, DoctestVisitor, GlobalTestOptions, ScrapedDoctest, + generate_args_file, CreateRunnableDocTests, DocTestVisitor, GlobalTestOptions, ScrapedDocTest, }; use crate::config::Options; use crate::html::markdown::{find_testable_code, ErrorCodes, LangString, MdRelLine}; struct MdCollector { - tests: Vec, + tests: Vec, cur_path: Vec, filename: FileName, } -impl DoctestVisitor for MdCollector { +impl DocTestVisitor for MdCollector { fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) { let filename = self.filename.clone(); // First line of Markdown is line 1. let line = 1 + rel_line.offset(); - self.tests.push(ScrapedDoctest::new(filename, line, self.cur_path.clone(), config, test)); + self.tests.push(ScrapedDocTest::new(filename, line, self.cur_path.clone(), config, test)); } fn visit_header(&mut self, name: &str, level: u32) { @@ -113,9 +113,9 @@ pub(crate) fn test(options: Options) -> Result<(), String> { None, ); - let mut collector = CreateRunnableDoctests::new(options.clone(), opts); + let mut collector = CreateRunnableDocTests::new(options.clone(), opts); md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); - let CreateRunnableDoctests { opts, rustdoc_options, standalone_tests, mergeable_tests, .. } = + let CreateRunnableDocTests { opts, rustdoc_options, standalone_tests, mergeable_tests, .. } = collector; crate::doctest::run_tests( opts, diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 46122bbeafd6f..0d73e02cbd751 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -4,8 +4,8 @@ use rustc_span::edition::Edition; use std::fmt::Write; use crate::doctest::{ - run_test, DocTestBuilder, GlobalTestOptions, IndividualTestOptions, RunnableDoctest, - RustdocOptions, ScrapedDoctest, TestFailure, UnusedExterns, + run_test, DocTestBuilder, GlobalTestOptions, IndividualTestOptions, RunnableDocTest, + RustdocOptions, ScrapedDocTest, TestFailure, UnusedExterns, }; use crate::html::markdown::{Ignore, LangString}; @@ -32,7 +32,7 @@ impl DocTestRunner { pub(crate) fn add_test( &mut self, doctest: &DocTestBuilder, - scraped_test: &ScrapedDoctest, + scraped_test: &ScrapedDocTest, target_str: &str, ) { let ignore = match scraped_test.langstr.ignore { @@ -175,7 +175,7 @@ std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), N ids = self.ids, ) .expect("failed to generate test code"); - let runnable_test = RunnableDoctest { + let runnable_test = RunnableDocTest { full_test_code: code, full_test_line_offset: 0, test_opts: test_options, @@ -199,7 +199,7 @@ std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), N /// Push new doctest content into `output`. Returns the test ID for this doctest. fn generate_mergeable_doctest( doctest: &DocTestBuilder, - scraped_test: &ScrapedDoctest, + scraped_test: &ScrapedDocTest, ignore: bool, id: usize, output: &mut String, diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 17c29ba413a4a..243fb5d05fdb7 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -14,14 +14,13 @@ use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; -use super::{DoctestVisitor, ScrapedDoctest}; -use crate::clean::types::AttributesExt; -use crate::clean::Attributes; +use super::{DocTestVisitor, ScrapedDocTest}; +use crate::clean::{types::AttributesExt, Attributes}; use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine}; struct RustCollector { source_map: Lrc, - tests: Vec, + tests: Vec, cur_path: Vec, position: Span, } @@ -48,10 +47,10 @@ impl RustCollector { } } -impl DoctestVisitor for RustCollector { +impl DocTestVisitor for RustCollector { fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) { let line = self.get_base_line() + rel_line.offset(); - self.tests.push(ScrapedDoctest::new( + self.tests.push(ScrapedDocTest::new( self.get_filename(), line, self.cur_path.clone(), @@ -89,7 +88,7 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> { Self { sess, map, codes, enable_per_target_ignores, tcx, collector } } - pub fn collect_crate(mut self) -> Vec { + pub fn collect_crate(mut self) -> Vec { let tcx = self.tcx; self.visit_testable("".to_string(), CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), |this| { tcx.hir().walk_toplevel_module(this) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index bc30f7fa43b2b..7bfe5d87d399f 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -738,7 +738,7 @@ impl MdRelLine { } } -pub(crate) fn find_testable_code( +pub(crate) fn find_testable_code( doc: &str, tests: &mut T, error_codes: ErrorCodes, @@ -748,7 +748,7 @@ pub(crate) fn find_testable_code( find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false) } -pub(crate) fn find_codes( +pub(crate) fn find_codes( doc: &str, tests: &mut T, error_codes: ErrorCodes, diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index d78afdffc626d..5015d66595504 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -45,7 +45,7 @@ pub(crate) struct Tests { pub(crate) found_tests: usize, } -impl crate::doctest::DoctestVisitor for Tests { +impl crate::doctest::DocTestVisitor for Tests { fn visit_test(&mut self, _: String, config: LangString, _: MdRelLine) { if config.rust && config.ignore == Ignore::None { self.found_tests += 1; From baf8ce83b82dce7d263aeb48d5a1cc7241d05739 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 9 Jul 2024 14:21:43 +0200 Subject: [PATCH 159/178] Move `is_multiple_tests` argument into `RunnableDocTest` --- src/librustdoc/doctest.rs | 22 +++++++++------------- src/librustdoc/doctest/runner.rs | 10 +++------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 91b93a9990323..de641901e7a53 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -447,6 +447,7 @@ struct RunnableDocTest { line: usize, edition: Edition, no_run: bool, + is_multiple_tests: bool, } impl RunnableDocTest { @@ -459,7 +460,6 @@ fn run_test( doctest: RunnableDocTest, rustdoc_options: &RustdocOptions, supports_color: bool, - is_multiple_tests: bool, report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { let langstr = &doctest.langstr; @@ -480,7 +480,7 @@ fn run_test( } compiler.arg("--edition").arg(&doctest.edition.to_string()); - if !is_multiple_tests { + if !doctest.is_multiple_tests { // Setting these environment variables is unneeded if this is a merged doctest. compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); compiler.env( @@ -532,7 +532,7 @@ fn run_test( // If this is a merged doctest, we need to write it into a file instead of using stdin // because if the size of the merged doctests is too big, it'll simply break stdin. - if is_multiple_tests { + if doctest.is_multiple_tests { // It makes the compilation failure much faster if it is for a combined doctest. compiler.arg("--error-format=short"); let input_file = doctest.path_for_merged_doctest(); @@ -556,7 +556,7 @@ fn run_test( debug!("compiler invocation for doctest: {compiler:?}"); let mut child = compiler.spawn().expect("Failed to spawn rustc process"); - let output = if is_multiple_tests { + let output = if doctest.is_multiple_tests { let status = child.wait().expect("Failed to wait"); process::Output { status, stdout: Vec::new(), stderr: Vec::new() } } else { @@ -634,7 +634,7 @@ fn run_test( cmd.arg(&output_file); } else { cmd = Command::new(&output_file); - if is_multiple_tests { + if doctest.is_multiple_tests { cmd.arg("*doctest-bin-path"); cmd.arg(&output_file); } @@ -643,7 +643,7 @@ fn run_test( cmd.current_dir(run_directory); } - let result = if is_multiple_tests || rustdoc_options.nocapture { + let result = if doctest.is_multiple_tests || rustdoc_options.nocapture { cmd.status().map(|status| process::Output { status, stdout: Vec::new(), @@ -921,14 +921,10 @@ fn doctest_run_fn( line: scraped_test.line, edition: scraped_test.edition(&rustdoc_options), no_run: scraped_test.no_run(&rustdoc_options), + is_multiple_tests: false, }; - let res = run_test( - runnable_test, - &rustdoc_options, - doctest.supports_color, - false, - report_unused_externs, - ); + let res = + run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs); if let Err(err) = res { match err { diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 0d73e02cbd751..b7d9bffb23a04 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -184,14 +184,10 @@ std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), N line: 0, edition, no_run: false, + is_multiple_tests: true, }; - let ret = run_test( - runnable_test, - rustdoc_options, - self.supports_color, - true, - |_: UnusedExterns| {}, - ); + let ret = + run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {}); if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) } } } From c9f730e2236a2ee37f59b03e6a017b198ed40b38 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 9 Jul 2024 14:24:04 +0200 Subject: [PATCH 160/178] Improve documentation for internal doctest API --- src/librustdoc/doctest.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index de641901e7a53..29cece6faaa15 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -456,6 +456,10 @@ impl RunnableDocTest { } } +/// Execute a `RunnableDoctest`. +/// +/// This is the function that calculates the compiler command line, invokes the compiler, then +/// invokes the test or tests in a separate executable (if applicable). fn run_test( doctest: RunnableDocTest, rustdoc_options: &RustdocOptions, @@ -705,6 +709,15 @@ impl IndividualTestOptions { } /// A doctest scraped from the code, ready to be turned into a runnable test. +/// +/// The pipeline goes: [`clean`] AST -> `ScrapedDoctest` -> [`RunnableDoctest`]. +/// [`run_merged_tests`] converts a bunch of scraped doctests to a single runnable doctest, +/// while [`generate_unique_doctest`] does the standalones. +/// +/// [`clean`]: crate::clean +/// [`RunnableDoctest`]: crate::doctest::RunnableDoctest +/// [`run_merged_tests`]: crate::doctest::runner::DocTestRunner::run_merged_tests +/// [`generate_unique_doctest`]: crate::doctest::make::DocTestBuilder::generate_unique_doctest pub(crate) struct ScrapedDocTest { filename: FileName, line: usize, From cbf6fe05e78c59e98d09a21bc90a7559b0cd8d5a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 9 Jul 2024 14:34:55 +0200 Subject: [PATCH 161/178] Add more merged doctests tests --- tests/rustdoc-ui/doctest/merged-ignore-no_run.rs | 14 ++++++++++++++ .../rustdoc-ui/doctest/merged-ignore-no_run.stdout | 7 +++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/rustdoc-ui/doctest/merged-ignore-no_run.rs create mode 100644 tests/rustdoc-ui/doctest/merged-ignore-no_run.stdout diff --git a/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs b/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs new file mode 100644 index 0000000000000..1a2f9492057e0 --- /dev/null +++ b/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs @@ -0,0 +1,14 @@ +//@ compile-flags:--test --test-args=--test-threads=1 -Zunstable-options --edition 2024 +//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + +/// ```ignore (test) +/// let x = 12; +/// ``` +pub fn ignored() {} + +/// ```no_run +/// panic!("blob"); +/// ``` +pub fn no_run() {} diff --git a/tests/rustdoc-ui/doctest/merged-ignore-no_run.stdout b/tests/rustdoc-ui/doctest/merged-ignore-no_run.stdout new file mode 100644 index 0000000000000..f2cb1e7e72f70 --- /dev/null +++ b/tests/rustdoc-ui/doctest/merged-ignore-no_run.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test $DIR/merged-ignore-no_run.rs - ignored (line 6) ... ignored +test $DIR/merged-ignore-no_run.rs - no_run (line 11) - compile ... ok + +test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME + From 7dcb841de040c85cf8e53e533917d6eade2c1777 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 11 Jul 2024 22:53:30 +0200 Subject: [PATCH 162/178] Add doctest to ensure that doctests with crate-level attributes are not part of merged doctest --- .../2024-doctests-crate-attribute.rs | 22 +++++++++++++++++++ .../2024-doctests-crate-attribute.stdout | 12 ++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/rustdoc-ui/2024-doctests-crate-attribute.rs create mode 100644 tests/rustdoc-ui/2024-doctests-crate-attribute.stdout diff --git a/tests/rustdoc-ui/2024-doctests-crate-attribute.rs b/tests/rustdoc-ui/2024-doctests-crate-attribute.rs new file mode 100644 index 0000000000000..781a773d952a8 --- /dev/null +++ b/tests/rustdoc-ui/2024-doctests-crate-attribute.rs @@ -0,0 +1,22 @@ +//@ check-pass +//@ compile-flags: --test --test-args=--test-threads=1 -Zunstable-options --edition 2024 +//@ normalize-stdout-test: "tests/rustdoc-ui" -> "$$DIR" +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" + +/// This doctest is used to ensure that if a crate attribute is present, +/// it will not be part of the merged doctests. +/// +/// ``` +/// #![doc(html_playground_url = "foo")] +/// +/// pub struct Bar; +/// ``` +/// +/// This one will allow us to confirm that the doctest above will be a +/// standalone one (there will be two separate doctests passes). +/// +/// ``` +/// let x = 12; +/// ``` +pub struct Foo; diff --git a/tests/rustdoc-ui/2024-doctests-crate-attribute.stdout b/tests/rustdoc-ui/2024-doctests-crate-attribute.stdout new file mode 100644 index 0000000000000..29702ce8929bd --- /dev/null +++ b/tests/rustdoc-ui/2024-doctests-crate-attribute.stdout @@ -0,0 +1,12 @@ + +running 1 test +test $DIR/2024-doctests-crate-attribute.rs - Foo (line 19) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 1 test +test $DIR/2024-doctests-crate-attribute.rs - Foo (line 10) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + From 903d2db4d22e947354385ed2fff42d0816143cfc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 11 Jul 2024 23:37:55 +0200 Subject: [PATCH 163/178] Only keep "useful" code in `tests/rustdoc-ui/2024-doctests-checks.rs` --- tests/rustdoc-ui/2024-doctests-checks.rs | 18 ++---------------- tests/rustdoc-ui/2024-doctests-checks.stdout | 14 ++++++-------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/tests/rustdoc-ui/2024-doctests-checks.rs b/tests/rustdoc-ui/2024-doctests-checks.rs index f6b6faa8c6a2c..6f4db3a90f818 100644 --- a/tests/rustdoc-ui/2024-doctests-checks.rs +++ b/tests/rustdoc-ui/2024-doctests-checks.rs @@ -4,26 +4,12 @@ //@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" //@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" -/// This one should fail: crate attributes should remain crate attributes -/// in standalone doctests. -/// -/// ```compile_fail -/// #![deny(missing_docs)] -/// -/// pub struct Bar; -/// ``` -/// -/// This one should not impact the other merged doctests. -/// -/// ``` -/// #![deny(unused)] -/// ``` -/// /// ``` /// let x = 12; /// ``` /// -/// This one should not be a merged doctest (because of `$crate`): +/// This one should not be a merged doctest (because of `$crate`). The output +/// will confirm it by displaying both merged and standalone doctest passes. /// /// ``` /// macro_rules! bla { diff --git a/tests/rustdoc-ui/2024-doctests-checks.stdout b/tests/rustdoc-ui/2024-doctests-checks.stdout index 2c0136f767448..d1064084a8564 100644 --- a/tests/rustdoc-ui/2024-doctests-checks.stdout +++ b/tests/rustdoc-ui/2024-doctests-checks.stdout @@ -1,14 +1,12 @@ -running 2 tests -test $DIR/2024-doctests-checks.rs - Foo (line 18) ... ok -test $DIR/2024-doctests-checks.rs - Foo (line 22) ... ok +running 1 test +test $DIR/2024-doctests-checks.rs - Foo (line 7) ... ok -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME -running 2 tests -test $DIR/2024-doctests-checks.rs - Foo (line 10) - compile fail ... ok -test $DIR/2024-doctests-checks.rs - Foo (line 28) ... ok +running 1 test +test $DIR/2024-doctests-checks.rs - Foo (line 14) ... ok -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME From a708d0bc779afad3b23eea4a323b00d5de7a2fa9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 12 Jul 2024 11:26:25 +0200 Subject: [PATCH 164/178] Fix commands syntax in rustdoc-ui tests --- tests/rustdoc-ui/2024-doctests-checks.rs | 4 ++-- tests/rustdoc-ui/2024-doctests-crate-attribute.rs | 4 ++-- tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.rs | 2 +- tests/rustdoc-ui/doctest/merged-ignore-no_run.rs | 2 +- tests/rustdoc-ui/doctest/wrong-ast-2024.rs | 4 ++-- tests/rustdoc-ui/doctest/wrong-ast.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/rustdoc-ui/2024-doctests-checks.rs b/tests/rustdoc-ui/2024-doctests-checks.rs index 6f4db3a90f818..464cf5b200df0 100644 --- a/tests/rustdoc-ui/2024-doctests-checks.rs +++ b/tests/rustdoc-ui/2024-doctests-checks.rs @@ -1,8 +1,8 @@ //@ check-pass //@ compile-flags: --test --test-args=--test-threads=1 -Zunstable-options --edition 2024 //@ normalize-stdout-test: "tests/rustdoc-ui" -> "$$DIR" -//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" +//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" /// ``` /// let x = 12; diff --git a/tests/rustdoc-ui/2024-doctests-crate-attribute.rs b/tests/rustdoc-ui/2024-doctests-crate-attribute.rs index 781a773d952a8..4984fdfe1949a 100644 --- a/tests/rustdoc-ui/2024-doctests-crate-attribute.rs +++ b/tests/rustdoc-ui/2024-doctests-crate-attribute.rs @@ -1,8 +1,8 @@ //@ check-pass //@ compile-flags: --test --test-args=--test-threads=1 -Zunstable-options --edition 2024 //@ normalize-stdout-test: "tests/rustdoc-ui" -> "$$DIR" -//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" +//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" /// This doctest is used to ensure that if a crate attribute is present, /// it will not be part of the merged doctests. diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.rs b/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.rs index ad78bb545533d..4fe513b406691 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.rs +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic-2021.rs @@ -3,7 +3,7 @@ //@ compile-flags:--test --edition 2021 //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" -//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ failure-status: 101 /// ```should_panic diff --git a/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs b/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs index 1a2f9492057e0..4c21d54295106 100644 --- a/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs +++ b/tests/rustdoc-ui/doctest/merged-ignore-no_run.rs @@ -1,6 +1,6 @@ //@ compile-flags:--test --test-args=--test-threads=1 -Zunstable-options --edition 2024 //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" -//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ check-pass /// ```ignore (test) diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.rs b/tests/rustdoc-ui/doctest/wrong-ast-2024.rs index fdcd3baa64273..7b4fa8fd2c9b9 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast-2024.rs +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.rs @@ -1,7 +1,7 @@ //@ compile-flags:--test --test-args=--test-threads=1 -Zunstable-options --edition 2024 //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" -//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ normalize-stdout-test ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" +//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" //@ failure-status: 101 /// ``` diff --git a/tests/rustdoc-ui/doctest/wrong-ast.rs b/tests/rustdoc-ui/doctest/wrong-ast.rs index e8faaea97ee20..92286b33dcfb3 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast.rs +++ b/tests/rustdoc-ui/doctest/wrong-ast.rs @@ -1,6 +1,6 @@ //@ compile-flags:--test --test-args=--test-threads=1 //@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" -//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ failure-status: 101 /// ``` From f1c1c4921637f39e617f3128977773b8ff6b7de0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 29 Jul 2024 14:38:47 +0200 Subject: [PATCH 165/178] Run fmt --- src/librustdoc/doctest/make.rs | 3 +-- src/librustdoc/doctest/runner.rs | 4 ++-- src/librustdoc/doctest/rust.rs | 3 ++- src/librustdoc/doctest/tests.rs | 3 ++- tests/run-make/doctests-keep-binaries-2024/rmake.rs | 3 ++- tests/run-make/doctests-merge/rmake.rs | 3 ++- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 1e8e403985bc8..aed079e5887b0 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -1,8 +1,6 @@ //! Logic for transforming the raw code given by the user into something actually //! runnable, e.g. by adding a `main` function if it doesn't already exist. -use crate::html::markdown::LangString; - use std::io; use rustc_ast as ast; @@ -18,6 +16,7 @@ use rustc_span::symbol::sym; use rustc_span::FileName; use super::GlobalTestOptions; +use crate::html::markdown::LangString; /// This struct contains information about the doctest itself which is then used to generate /// doctest source code appropriately. diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index b7d9bffb23a04..b91333e5f8135 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -1,8 +1,8 @@ +use std::fmt::Write; + use rustc_data_structures::fx::FxHashSet; use rustc_span::edition::Edition; -use std::fmt::Write; - use crate::doctest::{ run_test, DocTestBuilder, GlobalTestOptions, IndividualTestOptions, RunnableDocTest, RustdocOptions, ScrapedDocTest, TestFailure, UnusedExterns, diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 243fb5d05fdb7..abd66f15dc0c1 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -15,7 +15,8 @@ use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use super::{DocTestVisitor, ScrapedDocTest}; -use crate::clean::{types::AttributesExt, Attributes}; +use crate::clean::types::AttributesExt; +use crate::clean::Attributes; use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine}; struct RustCollector { diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index b076b6dccf5c7..160d0f222b4e0 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -1,8 +1,9 @@ use std::path::PathBuf; -use super::{DocTestBuilder, GlobalTestOptions}; use rustc_span::edition::DEFAULT_EDITION; +use super::{DocTestBuilder, GlobalTestOptions}; + fn make_test( test_code: &str, crate_name: Option<&str>, diff --git a/tests/run-make/doctests-keep-binaries-2024/rmake.rs b/tests/run-make/doctests-keep-binaries-2024/rmake.rs index a6fddf6d3365e..791867fa05f10 100644 --- a/tests/run-make/doctests-keep-binaries-2024/rmake.rs +++ b/tests/run-make/doctests-keep-binaries-2024/rmake.rs @@ -1,9 +1,10 @@ // Check that valid binaries are persisted by running them, regardless of whether the // --run or --no-run option is used. +use std::path::Path; + use run_make_support::fs_wrapper::{create_dir, remove_dir_all}; use run_make_support::{run, rustc, rustdoc}; -use std::path::Path; fn setup_test_env(callback: F) { let out_dir = Path::new("doctests"); diff --git a/tests/run-make/doctests-merge/rmake.rs b/tests/run-make/doctests-merge/rmake.rs index ac3951c6ceb3b..a25da7403e24b 100644 --- a/tests/run-make/doctests-merge/rmake.rs +++ b/tests/run-make/doctests-merge/rmake.rs @@ -1,6 +1,7 @@ -use run_make_support::{cwd, diff, rustc, rustdoc}; use std::path::Path; +use run_make_support::{cwd, diff, rustc, rustdoc}; + fn test_and_compare(input_file: &str, stdout_file: &str, edition: &str, dep: &Path) { let mut cmd = rustdoc(); From 488614d2dd0f2e42281792a189f6e7d2bf55230d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 29 Jul 2024 15:45:16 +0200 Subject: [PATCH 166/178] Update `tests/run-make/doctests-keep-binaries-2024/rmake.rs` test to new run-make API --- tests/run-make/doctests-keep-binaries-2024/rmake.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/run-make/doctests-keep-binaries-2024/rmake.rs b/tests/run-make/doctests-keep-binaries-2024/rmake.rs index 791867fa05f10..c3e586fc1054d 100644 --- a/tests/run-make/doctests-keep-binaries-2024/rmake.rs +++ b/tests/run-make/doctests-keep-binaries-2024/rmake.rs @@ -3,15 +3,14 @@ use std::path::Path; -use run_make_support::fs_wrapper::{create_dir, remove_dir_all}; -use run_make_support::{run, rustc, rustdoc}; +use run_make_support::{rfs, run, rustc, rustdoc}; fn setup_test_env(callback: F) { let out_dir = Path::new("doctests"); - create_dir(&out_dir); + rfs::create_dir(&out_dir); rustc().input("t.rs").crate_type("rlib").run(); callback(&out_dir, Path::new("libt.rlib")); - remove_dir_all(out_dir); + rfs::remove_dir_all(out_dir); } fn check_generated_binaries() { @@ -47,7 +46,7 @@ fn main() { // Behavior with --test-run-directory with relative paths. setup_test_env(|_, _| { let run_dir_path = Path::new("rundir"); - create_dir(&run_dir_path); + rfs::create_dir(&run_dir_path); rustdoc() .input("t.rs") @@ -61,6 +60,6 @@ fn main() { .edition("2024") .run(); - remove_dir_all(run_dir_path); + rfs::remove_dir_all(run_dir_path); }); } From 1d75f78ea26f79b21f63cdc45bd3f711d0bd3f20 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 Aug 2024 19:53:34 +0200 Subject: [PATCH 167/178] Ignore cross compile check for `tests/run-make/doctests-keep-binaries-2024` test --- tests/run-make/doctests-keep-binaries-2024/rmake.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-make/doctests-keep-binaries-2024/rmake.rs b/tests/run-make/doctests-keep-binaries-2024/rmake.rs index c3e586fc1054d..3e8ffcbf24457 100644 --- a/tests/run-make/doctests-keep-binaries-2024/rmake.rs +++ b/tests/run-make/doctests-keep-binaries-2024/rmake.rs @@ -1,6 +1,8 @@ // Check that valid binaries are persisted by running them, regardless of whether the // --run or --no-run option is used. +//@ ignore-cross-compile + use std::path::Path; use run_make_support::{rfs, run, rustc, rustdoc}; From 05fb8ff4f5602ebf470f6c870906aef9df16d8db Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Aug 2024 22:59:06 +0200 Subject: [PATCH 168/178] Fix intra-doc link --- src/librustdoc/doctest.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 29cece6faaa15..743c1ed507eeb 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -438,7 +438,7 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com /// (if multiple doctests are merged), `main` function, /// and everything needed to calculate the compiler's command-line arguments. /// The `# ` prefix on boring lines has also been stripped. -struct RunnableDocTest { +pub(crate) struct RunnableDocTest { full_test_code: String, full_test_line_offset: usize, test_opts: IndividualTestOptions, @@ -710,12 +710,11 @@ impl IndividualTestOptions { /// A doctest scraped from the code, ready to be turned into a runnable test. /// -/// The pipeline goes: [`clean`] AST -> `ScrapedDoctest` -> [`RunnableDoctest`]. +/// The pipeline goes: [`clean`] AST -> `ScrapedDoctest` -> `RunnableDoctest`. /// [`run_merged_tests`] converts a bunch of scraped doctests to a single runnable doctest, /// while [`generate_unique_doctest`] does the standalones. /// /// [`clean`]: crate::clean -/// [`RunnableDoctest`]: crate::doctest::RunnableDoctest /// [`run_merged_tests`]: crate::doctest::runner::DocTestRunner::run_merged_tests /// [`generate_unique_doctest`]: crate::doctest::make::DocTestBuilder::generate_unique_doctest pub(crate) struct ScrapedDocTest { From 5082e25263064e93aa03ba7374ebbbdf5454eac5 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 13 Aug 2024 20:16:14 +0200 Subject: [PATCH 169/178] Add mw back to review rotation --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 33108f743cb94..a98d5f6a7c215 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -913,7 +913,7 @@ cc = ["@kobzol"] [assign] warn_non_default_branch = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" -users_on_vacation = ["jyn514", "jhpratt", "oli-obk", "michaelwoerister"] +users_on_vacation = ["jyn514", "jhpratt", "oli-obk"] [assign.adhoc_groups] compiler-team = [ From 850bcbdc2e18b3e80d7c12cfcc72a5a219ad3872 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 13 Aug 2024 14:17:04 -0400 Subject: [PATCH 170/178] Test showing previous behavior --- .../build_correct_coerce.main.built.after.mir | 18 ++++++++++++++++++ tests/mir-opt/build_correct_coerce.rs | 12 ++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/mir-opt/build_correct_coerce.main.built.after.mir create mode 100644 tests/mir-opt/build_correct_coerce.rs diff --git a/tests/mir-opt/build_correct_coerce.main.built.after.mir b/tests/mir-opt/build_correct_coerce.main.built.after.mir new file mode 100644 index 0000000000000..10778bb605e0e --- /dev/null +++ b/tests/mir-opt/build_correct_coerce.main.built.after.mir @@ -0,0 +1,18 @@ +// MIR for `main` after built + +fn main() -> () { + let mut _0: (); + let _1: for<'a> fn(&'a (), &'a ()); + scope 1 { + debug x => _1; + } + + bb0: { + StorageLive(_1); + _1 = foo as for<'a, 'b> fn(&'a (), &'b ()) (PointerCoercion(ReifyFnPointer)); + FakeRead(ForLet(None), _1); + _0 = const (); + StorageDead(_1); + return; + } +} diff --git a/tests/mir-opt/build_correct_coerce.rs b/tests/mir-opt/build_correct_coerce.rs new file mode 100644 index 0000000000000..b6c861636dcaa --- /dev/null +++ b/tests/mir-opt/build_correct_coerce.rs @@ -0,0 +1,12 @@ +// skip-filecheck + +// Validate that we record the target for the `as` coercion as `for<'a> fn(&'a (), &'a ())`, +// and not `for<'a, 'b>(&'a (), &'b ())`. We previously did the latter due to a bug in +// the code that records adjustments in HIR typeck. + +fn foo<'a, 'b>(_: &'a (), _: &'b ()) {} + +// EMIT_MIR build_correct_coerce.main.built.after.mir +fn main() { + let x = foo as for<'a> fn(&'a (), &'a ()); +} From 5df13af56fb1e4454a057e62d96f7bf2a331a563 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 12 Aug 2024 11:59:04 -0400 Subject: [PATCH 171/178] Use the right type when coercing fn items to pointers --- compiler/rustc_borrowck/src/type_check/mod.rs | 8 ++++---- compiler/rustc_hir_typeck/src/coercion.rs | 2 +- .../mir-opt/build_correct_coerce.main.built.after.mir | 2 +- tests/ui/higher-ranked/subtyping-fn-ptr-coercion.rs | 10 ++++++++++ tests/ui/impl-trait/recursive-ice-101862.stderr | 4 ++-- tests/ui/traits/next-solver/alias-bound-unsound.rs | 1 + tests/ui/traits/next-solver/alias-bound-unsound.stderr | 8 +++++++- 7 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 tests/ui/higher-ranked/subtyping-fn-ptr-coercion.rs diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index b13773ffe1460..7da181544324a 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1989,9 +1989,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig); - if let Err(terr) = self.eq_types( - *ty, + if let Err(terr) = self.sub_types( ty_fn_ptr_from, + *ty, location.to_locations(), ConstraintCategory::Cast { unsize_to: None }, ) { @@ -2014,9 +2014,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, tcx.signature_unclosure(sig, *safety)); - if let Err(terr) = self.eq_types( - *ty, + if let Err(terr) = self.sub_types( ty_fn_ptr_from, + *ty, location.to_locations(), ConstraintCategory::Cast { unsize_to: None }, ) { diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index fcd3798eb48e6..d2fc5a39b1ca0 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -137,7 +137,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { at.lub(DefineOpaqueTypes::Yes, b, a) } else { at.sup(DefineOpaqueTypes::Yes, b, a) - .map(|InferOk { value: (), obligations }| InferOk { value: a, obligations }) + .map(|InferOk { value: (), obligations }| InferOk { value: b, obligations }) }; // In the new solver, lazy norm may allow us to shallowly equate diff --git a/tests/mir-opt/build_correct_coerce.main.built.after.mir b/tests/mir-opt/build_correct_coerce.main.built.after.mir index 10778bb605e0e..061174d69bb05 100644 --- a/tests/mir-opt/build_correct_coerce.main.built.after.mir +++ b/tests/mir-opt/build_correct_coerce.main.built.after.mir @@ -9,7 +9,7 @@ fn main() -> () { bb0: { StorageLive(_1); - _1 = foo as for<'a, 'b> fn(&'a (), &'b ()) (PointerCoercion(ReifyFnPointer)); + _1 = foo as for<'a> fn(&'a (), &'a ()) (PointerCoercion(ReifyFnPointer)); FakeRead(ForLet(None), _1); _0 = const (); StorageDead(_1); diff --git a/tests/ui/higher-ranked/subtyping-fn-ptr-coercion.rs b/tests/ui/higher-ranked/subtyping-fn-ptr-coercion.rs new file mode 100644 index 0000000000000..0cecf6808f229 --- /dev/null +++ b/tests/ui/higher-ranked/subtyping-fn-ptr-coercion.rs @@ -0,0 +1,10 @@ +//@ check-pass + +// Check that we use subtyping when reifying a closure into a function pointer. + +fn foo(x: &str) {} + +fn main() { + let c = |_: &str| {}; + let x = c as fn(&'static str); +} diff --git a/tests/ui/impl-trait/recursive-ice-101862.stderr b/tests/ui/impl-trait/recursive-ice-101862.stderr index f4148720c3331..970373422e8a5 100644 --- a/tests/ui/impl-trait/recursive-ice-101862.stderr +++ b/tests/ui/impl-trait/recursive-ice-101862.stderr @@ -11,13 +11,13 @@ LL | vec![].append(&mut ice(x.as_ref())); = note: `#[warn(unconditional_recursion)]` on by default error[E0792]: expected generic type parameter, found `&str` - --> $DIR/recursive-ice-101862.rs:6:5 + --> $DIR/recursive-ice-101862.rs:6:19 | LL | pub fn ice(x: impl AsRef) -> impl IntoIterator { | --------------- this generic parameter must be used with a generic type parameter LL | LL | vec![].append(&mut ice(x.as_ref())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.rs b/tests/ui/traits/next-solver/alias-bound-unsound.rs index a5bd3e7afa846..272e5db3b7ad2 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.rs +++ b/tests/ui/traits/next-solver/alias-bound-unsound.rs @@ -27,5 +27,6 @@ fn main() { //~| ERROR overflow evaluating the requirement `&<() as Foo>::Item well-formed` //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` + //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` println!("{x}"); } diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.stderr b/tests/ui/traits/next-solver/alias-bound-unsound.stderr index a5c2f215134a9..e5cf5b6bc3d30 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/next-solver/alias-bound-unsound.stderr @@ -44,6 +44,12 @@ LL | drop(<() as Foo>::copy_me(&x)); | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 6 previous errors +error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` + --> $DIR/alias-bound-unsound.rs:24:31 + | +LL | drop(<() as Foo>::copy_me(&x)); + | ^^ + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0275`. From bac19686a5f7a4cfa7dd4e311769a7e5c50931a3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 13 Aug 2024 16:44:37 -0400 Subject: [PATCH 172/178] Use is_lang_item more --- compiler/rustc_hir_typeck/src/check.rs | 10 +++------- compiler/rustc_lint/src/for_loops_over_fallibles.rs | 7 +++++-- compiler/rustc_lint/src/traits.rs | 2 +- compiler/rustc_middle/src/ty/diagnostics.rs | 8 ++++---- compiler/rustc_middle/src/ty/instance.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 4 +++- compiler/rustc_middle/src/ty/sty.rs | 2 +- compiler/rustc_monomorphize/src/partitioning.rs | 4 +++- .../src/error_reporting/traits/fulfillment_errors.rs | 3 +-- .../src/error_reporting/traits/suggestions.rs | 2 +- .../src/traits/query/type_op/prove_predicate.rs | 4 ++-- compiler/rustc_ty_utils/src/abi.rs | 2 +- 12 files changed, 26 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 89df464cca040..cd357e4a7adb5 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -160,15 +160,11 @@ pub(super) fn check_fn<'a, 'tcx>( fcx.demand_suptype(span, ret_ty, actual_return_ty); // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` - if let Some(panic_impl_did) = tcx.lang_items().panic_impl() - && panic_impl_did == fn_def_id.to_def_id() - { - check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig); + if tcx.is_lang_item(fn_def_id.to_def_id(), LangItem::PanicImpl) { + check_panic_info_fn(tcx, fn_def_id, fn_sig); } - if let Some(lang_start_defid) = tcx.lang_items().start_fn() - && lang_start_defid == fn_def_id.to_def_id() - { + if tcx.is_lang_item(fn_def_id.to_def_id(), LangItem::Start) { check_lang_start_fn(tcx, fn_sig, fn_def_id); } diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index 6cb5263ac5432..2793d48dc512b 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -1,5 +1,5 @@ use hir::{Expr, Pat}; -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; use rustc_middle::ty; @@ -126,7 +126,10 @@ fn extract_iterator_next_call<'tcx>( ) -> Option<&'tcx Expr<'tcx>> { // This won't work for `Iterator::next(iter)`, is this an issue? if let hir::ExprKind::MethodCall(_, recv, _, _) = expr.kind - && cx.typeck_results().type_dependent_def_id(expr.hir_id) == cx.tcx.lang_items().next_fn() + && cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::IteratorNext)) { Some(recv) } else { diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index fea96b5366e6c..a0fe4b5af7402 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; for (bound, modifier) in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); - if cx.tcx.lang_items().drop_trait() == def_id + if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) && *modifier != hir::TraitBoundModifier::Maybe { let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return }; diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 5acc0b7ac7ff1..c14dadc68c903 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -6,10 +6,9 @@ use std::ops::ControlFlow; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{into_diag_arg_using_display, Applicability, Diag, DiagArgValue, IntoDiagArg}; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{PredicateOrigin, WherePredicate}; +use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicate}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; @@ -290,8 +289,9 @@ pub fn suggest_constraining_type_params<'a>( let Some(param) = param else { return false }; { - let mut sized_constraints = - constraints.extract_if(|(_, def_id)| *def_id == tcx.lang_items().sized_trait()); + let mut sized_constraints = constraints.extract_if(|(_, def_id)| { + def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized)) + }); if let Some((_, def_id)) = sized_constraints.next() { applicability = Applicability::MaybeIncorrect; diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 0496c571f5e0a..6f19739de45ff 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -838,7 +838,7 @@ impl<'tcx> Instance<'tcx> { return None; }; - if tcx.lang_items().get(coroutine_callable_item) == Some(trait_item_id) { + if tcx.is_lang_item(trait_item_id, coroutine_callable_item) { let ty::Coroutine(_, id_args) = *tcx.type_of(coroutine_def_id).skip_binder().kind() else { bug!() diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 29d72183dd3ff..4635de02e90a8 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1145,7 +1145,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let term = if let Some(ty) = term.skip_binder().as_type() && let ty::Alias(ty::Projection, proj) = ty.kind() && let Some(assoc) = tcx.opt_associated_item(proj.def_id) - && assoc.trait_container(tcx) == tcx.lang_items().coroutine_trait() + && assoc + .trait_container(tcx) + .is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Coroutine)) && assoc.name == rustc_span::sym::Return { if let ty::Coroutine(_, args) = args.type_at(0).kind() { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 8c97de1c59b26..03de8a5e68901 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1915,7 +1915,7 @@ impl<'tcx> Ty<'tcx> { pub fn is_c_void(self, tcx: TyCtxt<'_>) -> bool { match self.kind() { - ty::Adt(adt, _) => tcx.lang_items().get(LangItem::CVoid) == Some(adt.did()), + ty::Adt(adt, _) => tcx.is_lang_item(adt.did(), LangItem::CVoid), _ => false, } } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 65a3d8d1742d9..f96329a3403b3 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -648,7 +648,9 @@ fn characteristic_def_id_of_mono_item<'tcx>( if let Some(impl_def_id) = tcx.impl_of_method(def_id) { if tcx.sess.opts.incremental.is_some() - && tcx.trait_id_of_impl(impl_def_id) == tcx.lang_items().drop_trait() + && tcx + .trait_id_of_impl(impl_def_id) + .is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Drop)) { // Put `Drop::drop` into the same cgu as `drop_in_place` // since `drop_in_place` is the only thing that can diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 95d4509c100a2..98df664adfcca 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -230,8 +230,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { post_message, ); - let (err_msg, safe_transmute_explanation) = if Some(main_trait_ref.def_id()) - == self.tcx.lang_items().transmute_trait() + let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_ref.def_id(), LangItem::TransmuteTrait) { // Recompute the safe transmute reason and use that for the error reporting match self.get_safe_transmute_error_and_reason( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 9269177eb5035..7bc74e2afe5fc 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2829,7 +2829,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Do not suggest relaxing if there is an explicit `Sized` obligation. && !bounds.iter() .filter_map(|bound| bound.trait_ref()) - .any(|tr| tr.trait_def_id() == tcx.lang_items().sized_trait()) + .any(|tr| tr.trait_def_id().is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))) { let (span, separator) = if let [.., last] = bounds { (last.span().shrink_to_hi(), " +") diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 294c6bfc1243d..d6687c762c311 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -1,3 +1,4 @@ +use rustc_hir::LangItem; use rustc_infer::traits::Obligation; pub use rustc_middle::traits::query::type_op::ProvePredicate; use rustc_middle::traits::query::NoSolution; @@ -20,8 +21,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { // such cases. if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) = key.value.predicate.kind().skip_binder() - && let Some(sized_def_id) = tcx.lang_items().sized_trait() - && trait_ref.def_id() == sized_def_id + && tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) && trait_ref.self_ty().is_trivially_sized(tcx) { return Some(()); diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index d90c3bedc7017..34c426f2aa6df 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -621,7 +621,7 @@ fn fn_abi_new_uncached<'tcx>( let rust_abi = matches!(sig.abi, RustIntrinsic | Rust | RustCall); let is_drop_in_place = - fn_def_id.is_some() && fn_def_id == cx.tcx.lang_items().drop_in_place_fn(); + fn_def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::DropInPlace)); let arg_of = |ty: Ty<'tcx>, arg_idx: Option| -> Result<_, &'tcx FnAbiError<'tcx>> { let span = tracing::debug_span!("arg_of"); From 249a588cadc31b78cb6e57017a2a86f861648638 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2024 22:58:54 +0200 Subject: [PATCH 173/178] Remove a no-longer-true `assert` --- .../src/thir/pattern/check_match.rs | 10 ++- .../empty-types.exhaustive_patterns.stderr | 44 +++++----- .../usefulness/empty-types.never_pats.stderr | 86 +++++++++++-------- .../usefulness/empty-types.normal.stderr | 86 +++++++++++-------- tests/ui/pattern/usefulness/empty-types.rs | 11 ++- 5 files changed, 138 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 07bf222fcca46..85b9dacb1293c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -702,10 +702,12 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { && adt.is_enum() && let Constructor::Variant(variant_index) = witness_1.ctor() { - let variant = adt.variant(*variant_index); - let inhabited = variant.inhabited_predicate(self.tcx, *adt).instantiate(self.tcx, args); - assert!(inhabited.apply(self.tcx, cx.param_env, cx.module)); - !inhabited.apply_ignore_module(self.tcx, cx.param_env) + let variant_inhabited = adt + .variant(*variant_index) + .inhabited_predicate(self.tcx, *adt) + .instantiate(self.tcx, args); + variant_inhabited.apply(self.tcx, cx.param_env, cx.module) + && !variant_inhabited.apply_ignore_module(self.tcx, cx.param_env) } else { false }; diff --git a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr index 17cb6fbd94b88..f6f341d6f2f1e 100644 --- a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr @@ -254,7 +254,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:279:9 + --> $DIR/empty-types.rs:281:9 | LL | _ => {} | ^ @@ -262,7 +262,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:282:9 + --> $DIR/empty-types.rs:284:9 | LL | (_, _) => {} | ^^^^^^ @@ -270,7 +270,7 @@ LL | (_, _) => {} = note: this pattern matches no values because `(!, !)` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:285:9 + --> $DIR/empty-types.rs:287:9 | LL | Ok(_) => {} | ^^^^^ @@ -278,7 +278,7 @@ LL | Ok(_) => {} = note: this pattern matches no values because `Result` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:286:9 + --> $DIR/empty-types.rs:288:9 | LL | Err(_) => {} | ^^^^^^ @@ -286,7 +286,7 @@ LL | Err(_) => {} = note: this pattern matches no values because `Result` is uninhabited error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty - --> $DIR/empty-types.rs:318:11 + --> $DIR/empty-types.rs:327:11 | LL | match slice_never {} | ^^^^^^^^^^^ @@ -300,7 +300,7 @@ LL + } | error[E0004]: non-exhaustive patterns: `&[]` not covered - --> $DIR/empty-types.rs:329:11 + --> $DIR/empty-types.rs:338:11 | LL | match slice_never { | ^^^^^^^^^^^ pattern `&[]` not covered @@ -313,7 +313,7 @@ LL + &[] => todo!() | error[E0004]: non-exhaustive patterns: `&[]` not covered - --> $DIR/empty-types.rs:343:11 + --> $DIR/empty-types.rs:352:11 | LL | match slice_never { | ^^^^^^^^^^^ pattern `&[]` not covered @@ -327,7 +327,7 @@ LL + &[] => todo!() | error[E0004]: non-exhaustive patterns: type `[!]` is non-empty - --> $DIR/empty-types.rs:350:11 + --> $DIR/empty-types.rs:359:11 | LL | match *slice_never {} | ^^^^^^^^^^^^ @@ -341,7 +341,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:359:9 + --> $DIR/empty-types.rs:368:9 | LL | _ => {} | ^ @@ -349,7 +349,7 @@ LL | _ => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:362:9 + --> $DIR/empty-types.rs:371:9 | LL | [_, _, _] => {} | ^^^^^^^^^ @@ -357,7 +357,7 @@ LL | [_, _, _] => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:365:9 + --> $DIR/empty-types.rs:374:9 | LL | [_, ..] => {} | ^^^^^^^ @@ -365,7 +365,7 @@ LL | [_, ..] => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty - --> $DIR/empty-types.rs:379:11 + --> $DIR/empty-types.rs:388:11 | LL | match array_0_never {} | ^^^^^^^^^^^^^ @@ -379,7 +379,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:386:9 + --> $DIR/empty-types.rs:395:9 | LL | [] => {} | -- matches all the values already @@ -387,7 +387,7 @@ LL | _ => {} | ^ unreachable pattern error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/empty-types.rs:388:11 + --> $DIR/empty-types.rs:397:11 | LL | match array_0_never { | ^^^^^^^^^^^^^ pattern `[]` not covered @@ -401,7 +401,7 @@ LL + [] => todo!() | error: unreachable pattern - --> $DIR/empty-types.rs:407:9 + --> $DIR/empty-types.rs:416:9 | LL | Some(_) => {} | ^^^^^^^ @@ -409,7 +409,7 @@ LL | Some(_) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:412:9 + --> $DIR/empty-types.rs:421:9 | LL | Some(_a) => {} | ^^^^^^^^ @@ -417,7 +417,7 @@ LL | Some(_a) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:417:9 + --> $DIR/empty-types.rs:426:9 | LL | None => {} | ---- matches all the values already @@ -426,7 +426,7 @@ LL | _ => {} | ^ unreachable pattern error: unreachable pattern - --> $DIR/empty-types.rs:422:9 + --> $DIR/empty-types.rs:431:9 | LL | None => {} | ---- matches all the values already @@ -435,7 +435,7 @@ LL | _a => {} | ^^ unreachable pattern error: unreachable pattern - --> $DIR/empty-types.rs:594:9 + --> $DIR/empty-types.rs:603:9 | LL | _ => {} | ^ @@ -443,7 +443,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:597:9 + --> $DIR/empty-types.rs:606:9 | LL | _x => {} | ^^ @@ -451,7 +451,7 @@ LL | _x => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:600:9 + --> $DIR/empty-types.rs:609:9 | LL | _ if false => {} | ^ @@ -459,7 +459,7 @@ LL | _ if false => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:603:9 + --> $DIR/empty-types.rs:612:9 | LL | _x if false => {} | ^^ diff --git a/tests/ui/pattern/usefulness/empty-types.never_pats.stderr b/tests/ui/pattern/usefulness/empty-types.never_pats.stderr index 1ecb15f2cae3a..55a138c2d1cb2 100644 --- a/tests/ui/pattern/usefulness/empty-types.never_pats.stderr +++ b/tests/ui/pattern/usefulness/empty-types.never_pats.stderr @@ -296,7 +296,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:279:9 + --> $DIR/empty-types.rs:281:9 | LL | _ => {} | ^ @@ -304,7 +304,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:282:9 + --> $DIR/empty-types.rs:284:9 | LL | (_, _) => {} | ^^^^^^ @@ -312,7 +312,7 @@ LL | (_, _) => {} = note: this pattern matches no values because `(!, !)` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:285:9 + --> $DIR/empty-types.rs:287:9 | LL | Ok(_) => {} | ^^^^^ @@ -320,15 +320,29 @@ LL | Ok(_) => {} = note: this pattern matches no values because `Result` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:286:9 + --> $DIR/empty-types.rs:288:9 | LL | Err(_) => {} | ^^^^^^ | = note: this pattern matches no values because `Result` is uninhabited +error[E0005]: refutable pattern in local binding + --> $DIR/empty-types.rs:297:13 + | +LL | let Ok(_) = *ptr_result_never_err; + | ^^^^^ pattern `Err(!)` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `Result` +help: you might want to use `if let` to ignore the variant that isn't matched + | +LL | if let Ok(_) = *ptr_result_never_err { todo!() }; + | ++ +++++++++++ + error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:307:11 + --> $DIR/empty-types.rs:316:11 | LL | match *x {} | ^^ @@ -342,7 +356,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty - --> $DIR/empty-types.rs:309:11 + --> $DIR/empty-types.rs:318:11 | LL | match *x {} | ^^ @@ -356,7 +370,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: `Ok(!)` and `Err(!)` not covered - --> $DIR/empty-types.rs:311:11 + --> $DIR/empty-types.rs:320:11 | LL | match *x {} | ^^ patterns `Ok(!)` and `Err(!)` not covered @@ -378,7 +392,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty - --> $DIR/empty-types.rs:313:11 + --> $DIR/empty-types.rs:322:11 | LL | match *x {} | ^^ @@ -392,7 +406,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty - --> $DIR/empty-types.rs:318:11 + --> $DIR/empty-types.rs:327:11 | LL | match slice_never {} | ^^^^^^^^^^^ @@ -406,7 +420,7 @@ LL + } | error[E0004]: non-exhaustive patterns: `&[!, ..]` not covered - --> $DIR/empty-types.rs:320:11 + --> $DIR/empty-types.rs:329:11 | LL | match slice_never { | ^^^^^^^^^^^ pattern `&[!, ..]` not covered @@ -420,7 +434,7 @@ LL + &[!, ..] | error[E0004]: non-exhaustive patterns: `&[]`, `&[!]` and `&[!, !]` not covered - --> $DIR/empty-types.rs:329:11 + --> $DIR/empty-types.rs:338:11 | LL | match slice_never { | ^^^^^^^^^^^ patterns `&[]`, `&[!]` and `&[!, !]` not covered @@ -433,7 +447,7 @@ LL + &[] | &[!] | &[!, !] => todo!() | error[E0004]: non-exhaustive patterns: `&[]` and `&[!, ..]` not covered - --> $DIR/empty-types.rs:343:11 + --> $DIR/empty-types.rs:352:11 | LL | match slice_never { | ^^^^^^^^^^^ patterns `&[]` and `&[!, ..]` not covered @@ -447,7 +461,7 @@ LL + &[] | &[!, ..] => todo!() | error[E0004]: non-exhaustive patterns: type `[!]` is non-empty - --> $DIR/empty-types.rs:350:11 + --> $DIR/empty-types.rs:359:11 | LL | match *slice_never {} | ^^^^^^^^^^^^ @@ -461,7 +475,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:359:9 + --> $DIR/empty-types.rs:368:9 | LL | _ => {} | ^ @@ -469,7 +483,7 @@ LL | _ => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:362:9 + --> $DIR/empty-types.rs:371:9 | LL | [_, _, _] => {} | ^^^^^^^^^ @@ -477,7 +491,7 @@ LL | [_, _, _] => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:365:9 + --> $DIR/empty-types.rs:374:9 | LL | [_, ..] => {} | ^^^^^^^ @@ -485,7 +499,7 @@ LL | [_, ..] => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty - --> $DIR/empty-types.rs:379:11 + --> $DIR/empty-types.rs:388:11 | LL | match array_0_never {} | ^^^^^^^^^^^^^ @@ -499,7 +513,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:386:9 + --> $DIR/empty-types.rs:395:9 | LL | [] => {} | -- matches all the values already @@ -507,7 +521,7 @@ LL | _ => {} | ^ unreachable pattern error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/empty-types.rs:388:11 + --> $DIR/empty-types.rs:397:11 | LL | match array_0_never { | ^^^^^^^^^^^^^ pattern `[]` not covered @@ -521,7 +535,7 @@ LL + [] => todo!() | error: unreachable pattern - --> $DIR/empty-types.rs:407:9 + --> $DIR/empty-types.rs:416:9 | LL | Some(_) => {} | ^^^^^^^ @@ -529,7 +543,7 @@ LL | Some(_) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:412:9 + --> $DIR/empty-types.rs:421:9 | LL | Some(_a) => {} | ^^^^^^^^ @@ -537,7 +551,7 @@ LL | Some(_a) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:417:9 + --> $DIR/empty-types.rs:426:9 | LL | None => {} | ---- matches all the values already @@ -546,7 +560,7 @@ LL | _ => {} | ^ unreachable pattern error: unreachable pattern - --> $DIR/empty-types.rs:422:9 + --> $DIR/empty-types.rs:431:9 | LL | None => {} | ---- matches all the values already @@ -555,7 +569,7 @@ LL | _a => {} | ^^ unreachable pattern error[E0004]: non-exhaustive patterns: `&Some(!)` not covered - --> $DIR/empty-types.rs:442:11 + --> $DIR/empty-types.rs:451:11 | LL | match ref_opt_never { | ^^^^^^^^^^^^^ pattern `&Some(!)` not covered @@ -574,7 +588,7 @@ LL + &Some(!) | error[E0004]: non-exhaustive patterns: `Some(!)` not covered - --> $DIR/empty-types.rs:483:11 + --> $DIR/empty-types.rs:492:11 | LL | match *ref_opt_never { | ^^^^^^^^^^^^^^ pattern `Some(!)` not covered @@ -593,7 +607,7 @@ LL + Some(!) | error[E0004]: non-exhaustive patterns: `Err(!)` not covered - --> $DIR/empty-types.rs:531:11 + --> $DIR/empty-types.rs:540:11 | LL | match *ref_res_never { | ^^^^^^^^^^^^^^ pattern `Err(!)` not covered @@ -612,7 +626,7 @@ LL + Err(!) | error[E0004]: non-exhaustive patterns: `Err(!)` not covered - --> $DIR/empty-types.rs:542:11 + --> $DIR/empty-types.rs:551:11 | LL | match *ref_res_never { | ^^^^^^^^^^^^^^ pattern `Err(!)` not covered @@ -631,7 +645,7 @@ LL + Err(!) | error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:561:11 + --> $DIR/empty-types.rs:570:11 | LL | match *ref_tuple_half_never {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -645,7 +659,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:594:9 + --> $DIR/empty-types.rs:603:9 | LL | _ => {} | ^ @@ -653,7 +667,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:597:9 + --> $DIR/empty-types.rs:606:9 | LL | _x => {} | ^^ @@ -661,7 +675,7 @@ LL | _x => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:600:9 + --> $DIR/empty-types.rs:609:9 | LL | _ if false => {} | ^ @@ -669,7 +683,7 @@ LL | _ if false => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:603:9 + --> $DIR/empty-types.rs:612:9 | LL | _x if false => {} | ^^ @@ -677,7 +691,7 @@ LL | _x if false => {} = note: this pattern matches no values because `!` is uninhabited error[E0004]: non-exhaustive patterns: `&!` not covered - --> $DIR/empty-types.rs:628:11 + --> $DIR/empty-types.rs:637:11 | LL | match ref_never { | ^^^^^^^^^ pattern `&!` not covered @@ -693,7 +707,7 @@ LL + &! | error[E0004]: non-exhaustive patterns: `Ok(!)` not covered - --> $DIR/empty-types.rs:644:11 + --> $DIR/empty-types.rs:653:11 | LL | match *ref_result_never { | ^^^^^^^^^^^^^^^^^ pattern `Ok(!)` not covered @@ -712,7 +726,7 @@ LL + Ok(!) | error[E0004]: non-exhaustive patterns: `Some(!)` not covered - --> $DIR/empty-types.rs:664:11 + --> $DIR/empty-types.rs:673:11 | LL | match *x { | ^^ pattern `Some(!)` not covered @@ -730,7 +744,7 @@ LL ~ None => {}, LL + Some(!) | -error: aborting due to 64 previous errors; 1 warning emitted +error: aborting due to 65 previous errors; 1 warning emitted Some errors have detailed explanations: E0004, E0005. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-types.normal.stderr b/tests/ui/pattern/usefulness/empty-types.normal.stderr index c3421cd592edb..83b3989ffdefb 100644 --- a/tests/ui/pattern/usefulness/empty-types.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-types.normal.stderr @@ -287,7 +287,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:279:9 + --> $DIR/empty-types.rs:281:9 | LL | _ => {} | ^ @@ -295,7 +295,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:282:9 + --> $DIR/empty-types.rs:284:9 | LL | (_, _) => {} | ^^^^^^ @@ -303,7 +303,7 @@ LL | (_, _) => {} = note: this pattern matches no values because `(!, !)` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:285:9 + --> $DIR/empty-types.rs:287:9 | LL | Ok(_) => {} | ^^^^^ @@ -311,15 +311,29 @@ LL | Ok(_) => {} = note: this pattern matches no values because `Result` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:286:9 + --> $DIR/empty-types.rs:288:9 | LL | Err(_) => {} | ^^^^^^ | = note: this pattern matches no values because `Result` is uninhabited +error[E0005]: refutable pattern in local binding + --> $DIR/empty-types.rs:297:13 + | +LL | let Ok(_) = *ptr_result_never_err; + | ^^^^^ pattern `Err(_)` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `Result` +help: you might want to use `if let` to ignore the variant that isn't matched + | +LL | if let Ok(_) = *ptr_result_never_err { todo!() }; + | ++ +++++++++++ + error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:307:11 + --> $DIR/empty-types.rs:316:11 | LL | match *x {} | ^^ @@ -333,7 +347,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty - --> $DIR/empty-types.rs:309:11 + --> $DIR/empty-types.rs:318:11 | LL | match *x {} | ^^ @@ -347,7 +361,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered - --> $DIR/empty-types.rs:311:11 + --> $DIR/empty-types.rs:320:11 | LL | match *x {} | ^^ patterns `Ok(_)` and `Err(_)` not covered @@ -369,7 +383,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty - --> $DIR/empty-types.rs:313:11 + --> $DIR/empty-types.rs:322:11 | LL | match *x {} | ^^ @@ -383,7 +397,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty - --> $DIR/empty-types.rs:318:11 + --> $DIR/empty-types.rs:327:11 | LL | match slice_never {} | ^^^^^^^^^^^ @@ -397,7 +411,7 @@ LL + } | error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered - --> $DIR/empty-types.rs:320:11 + --> $DIR/empty-types.rs:329:11 | LL | match slice_never { | ^^^^^^^^^^^ pattern `&[_, ..]` not covered @@ -411,7 +425,7 @@ LL + &[_, ..] => todo!() | error[E0004]: non-exhaustive patterns: `&[]`, `&[_]` and `&[_, _]` not covered - --> $DIR/empty-types.rs:329:11 + --> $DIR/empty-types.rs:338:11 | LL | match slice_never { | ^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _]` not covered @@ -424,7 +438,7 @@ LL + &[] | &[_] | &[_, _] => todo!() | error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered - --> $DIR/empty-types.rs:343:11 + --> $DIR/empty-types.rs:352:11 | LL | match slice_never { | ^^^^^^^^^^^ patterns `&[]` and `&[_, ..]` not covered @@ -438,7 +452,7 @@ LL + &[] | &[_, ..] => todo!() | error[E0004]: non-exhaustive patterns: type `[!]` is non-empty - --> $DIR/empty-types.rs:350:11 + --> $DIR/empty-types.rs:359:11 | LL | match *slice_never {} | ^^^^^^^^^^^^ @@ -452,7 +466,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:359:9 + --> $DIR/empty-types.rs:368:9 | LL | _ => {} | ^ @@ -460,7 +474,7 @@ LL | _ => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:362:9 + --> $DIR/empty-types.rs:371:9 | LL | [_, _, _] => {} | ^^^^^^^^^ @@ -468,7 +482,7 @@ LL | [_, _, _] => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:365:9 + --> $DIR/empty-types.rs:374:9 | LL | [_, ..] => {} | ^^^^^^^ @@ -476,7 +490,7 @@ LL | [_, ..] => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty - --> $DIR/empty-types.rs:379:11 + --> $DIR/empty-types.rs:388:11 | LL | match array_0_never {} | ^^^^^^^^^^^^^ @@ -490,7 +504,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:386:9 + --> $DIR/empty-types.rs:395:9 | LL | [] => {} | -- matches all the values already @@ -498,7 +512,7 @@ LL | _ => {} | ^ unreachable pattern error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/empty-types.rs:388:11 + --> $DIR/empty-types.rs:397:11 | LL | match array_0_never { | ^^^^^^^^^^^^^ pattern `[]` not covered @@ -512,7 +526,7 @@ LL + [] => todo!() | error: unreachable pattern - --> $DIR/empty-types.rs:407:9 + --> $DIR/empty-types.rs:416:9 | LL | Some(_) => {} | ^^^^^^^ @@ -520,7 +534,7 @@ LL | Some(_) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:412:9 + --> $DIR/empty-types.rs:421:9 | LL | Some(_a) => {} | ^^^^^^^^ @@ -528,7 +542,7 @@ LL | Some(_a) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:417:9 + --> $DIR/empty-types.rs:426:9 | LL | None => {} | ---- matches all the values already @@ -537,7 +551,7 @@ LL | _ => {} | ^ unreachable pattern error: unreachable pattern - --> $DIR/empty-types.rs:422:9 + --> $DIR/empty-types.rs:431:9 | LL | None => {} | ---- matches all the values already @@ -546,7 +560,7 @@ LL | _a => {} | ^^ unreachable pattern error[E0004]: non-exhaustive patterns: `&Some(_)` not covered - --> $DIR/empty-types.rs:442:11 + --> $DIR/empty-types.rs:451:11 | LL | match ref_opt_never { | ^^^^^^^^^^^^^ pattern `&Some(_)` not covered @@ -565,7 +579,7 @@ LL + &Some(_) => todo!() | error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:483:11 + --> $DIR/empty-types.rs:492:11 | LL | match *ref_opt_never { | ^^^^^^^^^^^^^^ pattern `Some(_)` not covered @@ -584,7 +598,7 @@ LL + Some(_) => todo!() | error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:531:11 + --> $DIR/empty-types.rs:540:11 | LL | match *ref_res_never { | ^^^^^^^^^^^^^^ pattern `Err(_)` not covered @@ -603,7 +617,7 @@ LL + Err(_) => todo!() | error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:542:11 + --> $DIR/empty-types.rs:551:11 | LL | match *ref_res_never { | ^^^^^^^^^^^^^^ pattern `Err(_)` not covered @@ -622,7 +636,7 @@ LL + Err(_) => todo!() | error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:561:11 + --> $DIR/empty-types.rs:570:11 | LL | match *ref_tuple_half_never {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -636,7 +650,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:594:9 + --> $DIR/empty-types.rs:603:9 | LL | _ => {} | ^ @@ -644,7 +658,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:597:9 + --> $DIR/empty-types.rs:606:9 | LL | _x => {} | ^^ @@ -652,7 +666,7 @@ LL | _x => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:600:9 + --> $DIR/empty-types.rs:609:9 | LL | _ if false => {} | ^ @@ -660,7 +674,7 @@ LL | _ if false => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:603:9 + --> $DIR/empty-types.rs:612:9 | LL | _x if false => {} | ^^ @@ -668,7 +682,7 @@ LL | _x if false => {} = note: this pattern matches no values because `!` is uninhabited error[E0004]: non-exhaustive patterns: `&_` not covered - --> $DIR/empty-types.rs:628:11 + --> $DIR/empty-types.rs:637:11 | LL | match ref_never { | ^^^^^^^^^ pattern `&_` not covered @@ -684,7 +698,7 @@ LL + &_ => todo!() | error[E0004]: non-exhaustive patterns: `Ok(_)` not covered - --> $DIR/empty-types.rs:644:11 + --> $DIR/empty-types.rs:653:11 | LL | match *ref_result_never { | ^^^^^^^^^^^^^^^^^ pattern `Ok(_)` not covered @@ -703,7 +717,7 @@ LL + Ok(_) => todo!() | error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:664:11 + --> $DIR/empty-types.rs:673:11 | LL | match *x { | ^^ pattern `Some(_)` not covered @@ -721,7 +735,7 @@ LL ~ None => {}, LL + Some(_) => todo!() | -error: aborting due to 64 previous errors +error: aborting due to 65 previous errors Some errors have detailed explanations: E0004, E0005. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-types.rs b/tests/ui/pattern/usefulness/empty-types.rs index 639c48cea12c5..d561a0e9c128b 100644 --- a/tests/ui/pattern/usefulness/empty-types.rs +++ b/tests/ui/pattern/usefulness/empty-types.rs @@ -17,7 +17,7 @@ #[derive(Copy, Clone)] enum Void {} -/// A bunch of never situations that can't be normally constructed. +/// A bunch of never situations that can't be normally constructed so we take them as argument. #[derive(Copy, Clone)] struct NeverBundle { never: !, @@ -272,6 +272,8 @@ fn nested_validity_tracking(bundle: NeverBundle) { let ref_never: &! = &never; let tuple_never: (!, !) = bundle.tuple_never; let result_never: Result = bundle.result_never; + let result_never_err: Result = Ok(0); + let ptr_result_never_err: *const Result = &result_never_err as *const _; let union_never = Uninit::::new(); // These should be considered known_valid and warn unreachable. @@ -287,6 +289,13 @@ fn nested_validity_tracking(bundle: NeverBundle) { } // These should be considered !known_valid and not warn unreachable. + unsafe { + match *ptr_result_never_err { + Ok(_) => {} + Err(_) => {} + } + let Ok(_) = *ptr_result_never_err; //[normal,never_pats]~ ERROR refutable pattern + } match ref_never { &_ => {} } From 8419c0956e1cee2799984b7b9520a0118ec0cb9c Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 10 Aug 2024 18:13:03 +0200 Subject: [PATCH 174/178] stabilize `asm_const` --- compiler/rustc_ast_lowering/messages.ftl | 2 - compiler/rustc_ast_lowering/src/asm.rs | 17 +-- compiler/rustc_codegen_gcc/tests/run/asm.rs | 15 +- compiler/rustc_feature/src/accepted.rs | 2 + compiler/rustc_feature/src/unstable.rs | 2 - library/core/src/lib.rs | 2 +- .../src/language-features/asm-const.md | 11 -- tests/assembly/asm/global_asm.rs | 1 - tests/assembly/asm/msp430-types.rs | 2 +- tests/ui/asm/aarch64/bad-reg.rs | 2 - tests/ui/asm/aarch64/bad-reg.stderr | 44 +++--- tests/ui/asm/aarch64/const.rs | 2 - tests/ui/asm/aarch64/parse-error.rs | 2 - tests/ui/asm/aarch64/parse-error.stderr | 118 +++++++------- tests/ui/asm/aarch64/type-check-3.rs | 2 +- tests/ui/asm/aarch64/type-check-4.rs | 5 +- tests/ui/asm/aarch64/type-check-4.stderr | 6 +- tests/ui/asm/bad-template.rs | 2 +- tests/ui/asm/const-error.rs | 8 +- tests/ui/asm/const-error.stderr | 6 +- tests/ui/asm/fail-const-eval-issue-121099.rs | 1 - .../asm/fail-const-eval-issue-121099.stderr | 4 +- tests/ui/asm/generic-const.rs | 2 - tests/ui/asm/invalid-const-operand.rs | 2 - tests/ui/asm/invalid-const-operand.stderr | 16 +- tests/ui/asm/naked-functions.rs | 2 +- tests/ui/asm/named-asm-labels.rs | 6 +- tests/ui/asm/named-asm-labels.stderr | 44 +++--- tests/ui/asm/parse-error.rs | 2 - tests/ui/asm/parse-error.stderr | 144 +++++++++--------- tests/ui/asm/type-check-1.rs | 2 - tests/ui/asm/type-check-1.stderr | 16 +- tests/ui/asm/x86_64/bad-reg.rs | 2 - tests/ui/asm/x86_64/bad-reg.stderr | 60 ++++---- tests/ui/asm/x86_64/const.rs | 2 - tests/ui/asm/x86_64/type-check-3.rs | 2 - tests/ui/asm/x86_64/type-check-3.stderr | 26 ++-- tests/ui/asm/x86_64/type-check-4.rs | 2 - tests/ui/asm/x86_64/type-check-4.stderr | 6 +- tests/ui/asm/x86_64/x86_64_parse_error.rs | 2 - tests/ui/asm/x86_64/x86_64_parse_error.stderr | 10 +- .../feature-gates/feature-gate-asm_const.rs | 16 -- .../feature-gate-asm_const.stderr | 23 --- 43 files changed, 276 insertions(+), 367 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/asm-const.md delete mode 100644 tests/ui/feature-gates/feature-gate-asm_const.rs delete mode 100644 tests/ui/feature-gates/feature-gate-asm_const.stderr diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 0a7f75039f64d..a5ee6713be8ed 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -175,8 +175,6 @@ ast_lowering_underscore_expr_lhs_assign = .label = `_` not allowed here ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture -ast_lowering_unstable_inline_assembly_const_operands = - const operands for inline assembly are unstable ast_lowering_unstable_inline_assembly_label_operands = label operands for inline assembly are unstable ast_lowering_unstable_may_unwind = the `may_unwind` option is unstable diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 8acca78379b2e..7d9d689e6d7e9 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -183,20 +183,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { out_expr: out_expr.as_ref().map(|expr| self.lower_expr(expr)), } } - InlineAsmOperand::Const { anon_const } => { - if !self.tcx.features().asm_const { - feature_err( - sess, - sym::asm_const, - *op_sp, - fluent::ast_lowering_unstable_inline_assembly_const_operands, - ) - .emit(); - } - hir::InlineAsmOperand::Const { - anon_const: self.lower_anon_const_to_anon_const(anon_const), - } - } + InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const { + anon_const: self.lower_anon_const_to_anon_const(anon_const), + }, InlineAsmOperand::Sym { sym } => { let static_def_id = self .resolver diff --git a/compiler/rustc_codegen_gcc/tests/run/asm.rs b/compiler/rustc_codegen_gcc/tests/run/asm.rs index 56f2aac3d0a1e..4e05d026868e7 100644 --- a/compiler/rustc_codegen_gcc/tests/run/asm.rs +++ b/compiler/rustc_codegen_gcc/tests/run/asm.rs @@ -3,12 +3,10 @@ // Run-time: // status: 0 -#![feature(asm_const)] - -#[cfg(target_arch="x86_64")] +#[cfg(target_arch = "x86_64")] use std::arch::{asm, global_asm}; -#[cfg(target_arch="x86_64")] +#[cfg(target_arch = "x86_64")] global_asm!( " .global add_asm @@ -22,7 +20,7 @@ extern "C" { fn add_asm(a: i64, b: i64) -> i64; } -#[cfg(target_arch="x86_64")] +#[cfg(target_arch = "x86_64")] pub unsafe fn mem_cpy(dst: *mut u8, src: *const u8, len: usize) { asm!( "rep movsb", @@ -33,7 +31,7 @@ pub unsafe fn mem_cpy(dst: *mut u8, src: *const u8, len: usize) { ); } -#[cfg(target_arch="x86_64")] +#[cfg(target_arch = "x86_64")] fn asm() { unsafe { asm!("nop"); @@ -178,9 +176,8 @@ fn asm() { assert_eq!(array1, array2); } -#[cfg(not(target_arch="x86_64"))] -fn asm() { -} +#[cfg(not(target_arch = "x86_64"))] +fn asm() {} fn main() { asm(); diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 44286cfeeefaa..03b40e28f8b4b 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -60,6 +60,8 @@ declare_features! ( (accepted, adx_target_feature, "1.61.0", Some(44839)), /// Allows explicit discriminants on non-unit enum variants. (accepted, arbitrary_enum_discriminant, "1.66.0", Some(60553)), + /// Allows using `const` operands in inline assembly. + (accepted, asm_const, "CURRENT_RUSTC_VERSION", Some(93332)), /// Allows using `sym` operands in inline assembly. (accepted, asm_sym, "1.66.0", Some(93333)), /// Allows the definition of associated constants in `trait` or `impl` blocks. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 47810bc9165ef..24f691ea7fa20 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -348,8 +348,6 @@ declare_features! ( (unstable, alloc_error_handler, "1.29.0", Some(51540)), /// Allows trait methods with arbitrary self types. (unstable, arbitrary_self_types, "1.23.0", Some(44874)), - /// Allows using `const` operands in inline assembly. - (unstable, asm_const, "1.58.0", Some(93332)), /// Enables experimental inline assembly support for additional architectures. (unstable, asm_experimental_arch, "1.58.0", Some(93335)), /// Allows using `label` operands in inline assembly. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 07daa32afa8a3..7b8744e2e8202 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -192,12 +192,12 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(asm_const))] #![cfg_attr(bootstrap, feature(min_exhaustive_patterns))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] -#![feature(asm_const)] #![feature(auto_traits)] #![feature(cfg_sanitize)] #![feature(cfg_target_has_atomic)] diff --git a/src/doc/unstable-book/src/language-features/asm-const.md b/src/doc/unstable-book/src/language-features/asm-const.md deleted file mode 100644 index 670c4df414f31..0000000000000 --- a/src/doc/unstable-book/src/language-features/asm-const.md +++ /dev/null @@ -1,11 +0,0 @@ -# `asm_const` - -The tracking issue for this feature is: [#93332] - -[#93332]: https://github.com/rust-lang/rust/issues/93332 - ------------------------- - -This feature adds a `const ` operand type to `asm!` and `global_asm!`. -- `` must be an integer constant expression. -- The value of the expression is formatted as a string and substituted directly into the asm template string. diff --git a/tests/assembly/asm/global_asm.rs b/tests/assembly/asm/global_asm.rs index 22cf4bdb15b70..8a4bf98c7450b 100644 --- a/tests/assembly/asm/global_asm.rs +++ b/tests/assembly/asm/global_asm.rs @@ -4,7 +4,6 @@ //@ compile-flags: -C llvm-args=--x86-asm-syntax=intel //@ compile-flags: -C symbol-mangling-version=v0 -#![feature(asm_const)] #![crate_type = "rlib"] use std::arch::global_asm; diff --git a/tests/assembly/asm/msp430-types.rs b/tests/assembly/asm/msp430-types.rs index 4f51d4020a66b..ae09b8b070da2 100644 --- a/tests/assembly/asm/msp430-types.rs +++ b/tests/assembly/asm/msp430-types.rs @@ -2,7 +2,7 @@ //@ compile-flags: --target msp430-none-elf //@ needs-llvm-components: msp430 -#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch, asm_const)] +#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] diff --git a/tests/ui/asm/aarch64/bad-reg.rs b/tests/ui/asm/aarch64/bad-reg.rs index 1e54b6505db87..b99e5fe4b9e33 100644 --- a/tests/ui/asm/aarch64/bad-reg.rs +++ b/tests/ui/asm/aarch64/bad-reg.rs @@ -1,8 +1,6 @@ //@ only-aarch64 //@ compile-flags: -C target-feature=+neon -#![feature(asm_const)] - use std::arch::asm; fn main() { diff --git a/tests/ui/asm/aarch64/bad-reg.stderr b/tests/ui/asm/aarch64/bad-reg.stderr index 717a788caf67d..370752ad0f11c 100644 --- a/tests/ui/asm/aarch64/bad-reg.stderr +++ b/tests/ui/asm/aarch64/bad-reg.stderr @@ -1,17 +1,17 @@ error: invalid register class `foo`: unknown register class - --> $DIR/bad-reg.rs:14:20 + --> $DIR/bad-reg.rs:12:20 | LL | asm!("{}", in(foo) foo); | ^^^^^^^^^^^ error: invalid register `foo`: unknown register - --> $DIR/bad-reg.rs:16:18 + --> $DIR/bad-reg.rs:14:18 | LL | asm!("", in("foo") foo); | ^^^^^^^^^^^^^ error: invalid asm template modifier for this register class - --> $DIR/bad-reg.rs:18:15 + --> $DIR/bad-reg.rs:16:15 | LL | asm!("{:z}", in(reg) foo); | ^^^^ ----------- argument @@ -21,7 +21,7 @@ LL | asm!("{:z}", in(reg) foo); = note: the `reg` register class supports the following template modifiers: `w`, `x` error: invalid asm template modifier for this register class - --> $DIR/bad-reg.rs:20:15 + --> $DIR/bad-reg.rs:18:15 | LL | asm!("{:r}", in(vreg) foo); | ^^^^ ------------ argument @@ -31,7 +31,7 @@ LL | asm!("{:r}", in(vreg) foo); = note: the `vreg` register class supports the following template modifiers: `b`, `h`, `s`, `d`, `q`, `v` error: invalid asm template modifier for this register class - --> $DIR/bad-reg.rs:22:15 + --> $DIR/bad-reg.rs:20:15 | LL | asm!("{:r}", in(vreg_low16) foo); | ^^^^ ------------------ argument @@ -41,7 +41,7 @@ LL | asm!("{:r}", in(vreg_low16) foo); = note: the `vreg_low16` register class supports the following template modifiers: `b`, `h`, `s`, `d`, `q`, `v` error: asm template modifiers are not allowed for `const` arguments - --> $DIR/bad-reg.rs:24:15 + --> $DIR/bad-reg.rs:22:15 | LL | asm!("{:a}", const 0); | ^^^^ ------- argument @@ -49,7 +49,7 @@ LL | asm!("{:a}", const 0); | template modifier error: asm template modifiers are not allowed for `sym` arguments - --> $DIR/bad-reg.rs:26:15 + --> $DIR/bad-reg.rs:24:15 | LL | asm!("{:a}", sym main); | ^^^^ -------- argument @@ -57,49 +57,49 @@ LL | asm!("{:a}", sym main); | template modifier error: invalid register `x29`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", in("x29") foo); | ^^^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", in("sp") foo); | ^^^^^^^^^^^^ error: invalid register `xzr`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", in("xzr") foo); | ^^^^^^^^^^^^^ error: invalid register `x19`: x19 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", in("x19") foo); | ^^^^^^^^^^^^^ error: register class `preg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", in("p0") foo); | ^^^^^^^^^^^^ error: register class `preg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:41:20 + --> $DIR/bad-reg.rs:39:20 | LL | asm!("{}", in(preg) foo); | ^^^^^^^^^^^^ error: register class `preg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:44:20 + --> $DIR/bad-reg.rs:42:20 | LL | asm!("{}", out(preg) _); | ^^^^^^^^^^^ error: register `w0` conflicts with register `x0` - --> $DIR/bad-reg.rs:50:32 + --> $DIR/bad-reg.rs:48:32 | LL | asm!("", in("x0") foo, in("w0") bar); | ------------ ^^^^^^^^^^^^ register `w0` @@ -107,7 +107,7 @@ LL | asm!("", in("x0") foo, in("w0") bar); | register `x0` error: register `x0` conflicts with register `x0` - --> $DIR/bad-reg.rs:52:32 + --> $DIR/bad-reg.rs:50:32 | LL | asm!("", in("x0") foo, out("x0") bar); | ------------ ^^^^^^^^^^^^^ register `x0` @@ -115,13 +115,13 @@ LL | asm!("", in("x0") foo, out("x0") bar); | register `x0` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:50:18 | LL | asm!("", in("x0") foo, out("x0") bar); | ^^^^^^^^^^^^ error: register `q0` conflicts with register `v0` - --> $DIR/bad-reg.rs:55:32 + --> $DIR/bad-reg.rs:53:32 | LL | asm!("", in("v0") foo, in("q0") bar); | ------------ ^^^^^^^^^^^^ register `q0` @@ -129,7 +129,7 @@ LL | asm!("", in("v0") foo, in("q0") bar); | register `v0` error: register `q0` conflicts with register `v0` - --> $DIR/bad-reg.rs:57:32 + --> $DIR/bad-reg.rs:55:32 | LL | asm!("", in("v0") foo, out("q0") bar); | ------------ ^^^^^^^^^^^^^ register `q0` @@ -137,13 +137,13 @@ LL | asm!("", in("v0") foo, out("q0") bar); | register `v0` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:57:18 + --> $DIR/bad-reg.rs:55:18 | LL | asm!("", in("v0") foo, out("q0") bar); | ^^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:37:27 + --> $DIR/bad-reg.rs:35:27 | LL | asm!("", in("p0") foo); | ^^^ @@ -151,7 +151,7 @@ LL | asm!("", in("p0") foo); = note: register class `preg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:41:29 + --> $DIR/bad-reg.rs:39:29 | LL | asm!("{}", in(preg) foo); | ^^^ diff --git a/tests/ui/asm/aarch64/const.rs b/tests/ui/asm/aarch64/const.rs index a1fadb2115ba6..3eab5138d7d79 100644 --- a/tests/ui/asm/aarch64/const.rs +++ b/tests/ui/asm/aarch64/const.rs @@ -2,8 +2,6 @@ //@ run-pass //@ needs-asm-support -#![feature(asm_const)] - use std::arch::{asm, global_asm}; fn const_generic() -> usize { diff --git a/tests/ui/asm/aarch64/parse-error.rs b/tests/ui/asm/aarch64/parse-error.rs index ac73bbf99c9a3..aa731c35dda88 100644 --- a/tests/ui/asm/aarch64/parse-error.rs +++ b/tests/ui/asm/aarch64/parse-error.rs @@ -1,7 +1,5 @@ //@ only-aarch64 -#![feature(asm_const)] - use std::arch::{asm, global_asm}; fn main() { diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr index e2c798c798ee6..7b273282ee6a3 100644 --- a/tests/ui/asm/aarch64/parse-error.stderr +++ b/tests/ui/asm/aarch64/parse-error.stderr @@ -1,107 +1,107 @@ error: requires at least a template string argument - --> $DIR/parse-error.rs:11:9 + --> $DIR/parse-error.rs:9:9 | LL | asm!(); | ^^^^^^ error: asm template must be a string literal - --> $DIR/parse-error.rs:13:14 + --> $DIR/parse-error.rs:11:14 | LL | asm!(foo); | ^^^ error: expected token: `,` - --> $DIR/parse-error.rs:15:19 + --> $DIR/parse-error.rs:13:19 | LL | asm!("{}" foo); | ^^^ expected `,` error: expected operand, clobber_abi, options, or additional template string - --> $DIR/parse-error.rs:17:20 + --> $DIR/parse-error.rs:15:20 | LL | asm!("{}", foo); | ^^^ expected operand, clobber_abi, options, or additional template string error: expected `(`, found `foo` - --> $DIR/parse-error.rs:19:23 + --> $DIR/parse-error.rs:17:23 | LL | asm!("{}", in foo); | ^^^ expected `(` error: expected `)`, found `foo` - --> $DIR/parse-error.rs:21:27 + --> $DIR/parse-error.rs:19:27 | LL | asm!("{}", in(reg foo)); | ^^^ expected `)` error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:23:27 + --> $DIR/parse-error.rs:21:27 | LL | asm!("{}", in(reg)); | ^ expected expression error: expected register class or explicit register - --> $DIR/parse-error.rs:25:26 + --> $DIR/parse-error.rs:23:26 | LL | asm!("{}", inout(=) foo => bar); | ^ error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:27:37 + --> $DIR/parse-error.rs:25:37 | LL | asm!("{}", inout(reg) foo =>); | ^ expected expression error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` - --> $DIR/parse-error.rs:29:32 + --> $DIR/parse-error.rs:27:32 | LL | asm!("{}", in(reg) foo => bar); | ^^ expected one of 7 possible tokens error: expected a path for argument to `sym` - --> $DIR/parse-error.rs:31:24 + --> $DIR/parse-error.rs:29:24 | LL | asm!("{}", sym foo + bar); | ^^^^^^^^^ error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo` - --> $DIR/parse-error.rs:33:26 + --> $DIR/parse-error.rs:31:26 | LL | asm!("", options(foo)); | ^^^ expected one of 10 possible tokens error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:35:32 + --> $DIR/parse-error.rs:33:32 | LL | asm!("", options(nomem foo)); | ^^^ expected one of `)` or `,` error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo` - --> $DIR/parse-error.rs:37:33 + --> $DIR/parse-error.rs:35:33 | LL | asm!("", options(nomem, foo)); | ^^^ expected one of 10 possible tokens error: expected string literal - --> $DIR/parse-error.rs:41:30 + --> $DIR/parse-error.rs:39:30 | LL | asm!("", clobber_abi(foo)); | ^^^ not a string literal error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:43:34 + --> $DIR/parse-error.rs:41:34 | LL | asm!("", clobber_abi("C" foo)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:45:35 + --> $DIR/parse-error.rs:43:35 | LL | asm!("", clobber_abi("C", foo)); | ^^^ not a string literal error: duplicate argument named `a` - --> $DIR/parse-error.rs:52:36 + --> $DIR/parse-error.rs:50:36 | LL | asm!("{a}", a = const foo, a = const bar); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -109,7 +109,7 @@ LL | asm!("{a}", a = const foo, a = const bar); | previously here error: argument never used - --> $DIR/parse-error.rs:52:36 + --> $DIR/parse-error.rs:50:36 | LL | asm!("{a}", a = const foo, a = const bar); | ^^^^^^^^^^^^^ argument never used @@ -117,13 +117,13 @@ LL | asm!("{a}", a = const foo, a = const bar); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: explicit register arguments cannot have names - --> $DIR/parse-error.rs:57:18 + --> $DIR/parse-error.rs:55:18 | LL | asm!("", a = in("x0") foo); | ^^^^^^^^^^^^^^^^ error: positional arguments cannot follow named arguments or explicit register arguments - --> $DIR/parse-error.rs:63:35 + --> $DIR/parse-error.rs:61:35 | LL | asm!("{1}", in("x0") foo, const bar); | ------------ ^^^^^^^^^ positional argument @@ -131,19 +131,19 @@ LL | asm!("{1}", in("x0") foo, const bar); | explicit register argument error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` - --> $DIR/parse-error.rs:66:29 + --> $DIR/parse-error.rs:64:29 | LL | asm!("", options(), ""); | ^^ expected one of 10 possible tokens error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:68:33 + --> $DIR/parse-error.rs:66:33 | LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); | ^^^^ expected one of 10 possible tokens error: asm template must be a string literal - --> $DIR/parse-error.rs:70:14 + --> $DIR/parse-error.rs:68:14 | LL | asm!(format!("{{{}}}", 0), in(reg) foo); | ^^^^^^^^^^^^^^^^^^^^ @@ -151,7 +151,7 @@ LL | asm!(format!("{{{}}}", 0), in(reg) foo); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:72:21 + --> $DIR/parse-error.rs:70:21 | LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); | ^^^^^^^^^^^^^^^^^^^^ @@ -159,127 +159,127 @@ LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: _ cannot be used for input operands - --> $DIR/parse-error.rs:74:28 + --> $DIR/parse-error.rs:72:28 | LL | asm!("{}", in(reg) _); | ^ error: _ cannot be used for input operands - --> $DIR/parse-error.rs:76:31 + --> $DIR/parse-error.rs:74:31 | LL | asm!("{}", inout(reg) _); | ^ error: _ cannot be used for input operands - --> $DIR/parse-error.rs:78:35 + --> $DIR/parse-error.rs:76:35 | LL | asm!("{}", inlateout(reg) _); | ^ error: requires at least a template string argument - --> $DIR/parse-error.rs:85:1 + --> $DIR/parse-error.rs:83:1 | LL | global_asm!(); | ^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/parse-error.rs:87:13 + --> $DIR/parse-error.rs:85:13 | LL | global_asm!(FOO); | ^^^ error: expected token: `,` - --> $DIR/parse-error.rs:89:18 + --> $DIR/parse-error.rs:87:18 | LL | global_asm!("{}" FOO); | ^^^ expected `,` error: expected operand, options, or additional template string - --> $DIR/parse-error.rs:91:19 + --> $DIR/parse-error.rs:89:19 | LL | global_asm!("{}", FOO); | ^^^ expected operand, options, or additional template string error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:93:24 + --> $DIR/parse-error.rs:91:24 | LL | global_asm!("{}", const); | ^ expected expression error: expected one of `,`, `.`, `?`, or an operator, found `FOO` - --> $DIR/parse-error.rs:95:30 + --> $DIR/parse-error.rs:93:30 | LL | global_asm!("{}", const(reg) FOO); | ^^^ expected one of `,`, `.`, `?`, or an operator error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:97:25 + --> $DIR/parse-error.rs:95:25 | LL | global_asm!("", options(FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:99:25 + --> $DIR/parse-error.rs:97:25 | LL | global_asm!("", options(nomem FOO)); | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:99:31 + --> $DIR/parse-error.rs:97:31 | LL | global_asm!("", options(nomem FOO)); | ^^^ expected one of `)` or `,` error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:102:25 + --> $DIR/parse-error.rs:100:25 | LL | global_asm!("", options(nomem, FOO)); | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:102:32 + --> $DIR/parse-error.rs:100:32 | LL | global_asm!("", options(nomem, FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected string literal - --> $DIR/parse-error.rs:106:29 + --> $DIR/parse-error.rs:104:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:108:33 + --> $DIR/parse-error.rs:106:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:110:34 + --> $DIR/parse-error.rs:108:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:112:19 + --> $DIR/parse-error.rs:110:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:114:28 + --> $DIR/parse-error.rs:112:28 | LL | global_asm!("", options(), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:116:30 + --> $DIR/parse-error.rs:114:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:118:35 + --> $DIR/parse-error.rs:116:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -287,7 +287,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:118:35 + --> $DIR/parse-error.rs:116:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -295,19 +295,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:121:28 + --> $DIR/parse-error.rs:119:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:123:30 + --> $DIR/parse-error.rs:121:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:125:13 + --> $DIR/parse-error.rs:123:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -315,7 +315,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:127:20 + --> $DIR/parse-error.rs:125:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -323,7 +323,7 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:39:37 + --> $DIR/parse-error.rs:37:37 | LL | asm!("{}", options(), const foo); | ^^^ non-constant value @@ -334,7 +334,7 @@ LL | const foo: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:47:44 + --> $DIR/parse-error.rs:45:44 | LL | asm!("{}", clobber_abi("C"), const foo); | ^^^ non-constant value @@ -345,7 +345,7 @@ LL | const foo: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:50:55 + --> $DIR/parse-error.rs:48:55 | LL | asm!("{}", options(), clobber_abi("C"), const foo); | ^^^ non-constant value @@ -356,7 +356,7 @@ LL | const foo: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:52:31 + --> $DIR/parse-error.rs:50:31 | LL | asm!("{a}", a = const foo, a = const bar); | ^^^ non-constant value @@ -367,7 +367,7 @@ LL | const foo: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:52:46 + --> $DIR/parse-error.rs:50:46 | LL | asm!("{a}", a = const foo, a = const bar); | ^^^ non-constant value @@ -378,7 +378,7 @@ LL | const bar: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:59:45 + --> $DIR/parse-error.rs:57:45 | LL | asm!("{a}", in("x0") foo, a = const bar); | ^^^ non-constant value @@ -389,7 +389,7 @@ LL | const bar: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:61:45 + --> $DIR/parse-error.rs:59:45 | LL | asm!("{a}", in("x0") foo, a = const bar); | ^^^ non-constant value @@ -400,7 +400,7 @@ LL | const bar: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:63:41 + --> $DIR/parse-error.rs:61:41 | LL | asm!("{1}", in("x0") foo, const bar); | ^^^ non-constant value diff --git a/tests/ui/asm/aarch64/type-check-3.rs b/tests/ui/asm/aarch64/type-check-3.rs index 3fc8e5060693a..b64473f98c090 100644 --- a/tests/ui/asm/aarch64/type-check-3.rs +++ b/tests/ui/asm/aarch64/type-check-3.rs @@ -1,7 +1,7 @@ //@ only-aarch64 //@ compile-flags: -C target-feature=+neon -#![feature(repr_simd, asm_const)] +#![feature(repr_simd)] use std::arch::aarch64::float64x2_t; use std::arch::{asm, global_asm}; diff --git a/tests/ui/asm/aarch64/type-check-4.rs b/tests/ui/asm/aarch64/type-check-4.rs index f00b4d4c46fa5..41eb9de5669f6 100644 --- a/tests/ui/asm/aarch64/type-check-4.rs +++ b/tests/ui/asm/aarch64/type-check-4.rs @@ -1,7 +1,7 @@ //@ only-aarch64 //@ compile-flags: -C target-feature=+neon -#![feature(repr_simd, asm_const)] +#![feature(repr_simd)] use std::arch::aarch64::float64x2_t; use std::arch::{asm, global_asm}; @@ -10,8 +10,7 @@ use std::arch::{asm, global_asm}; #[derive(Copy, Clone)] struct Simd256bit(f64, f64, f64, f64); -fn main() { -} +fn main() {} // Constants must be... constant diff --git a/tests/ui/asm/aarch64/type-check-4.stderr b/tests/ui/asm/aarch64/type-check-4.stderr index 3e675f69e842b..89eb8467cdefd 100644 --- a/tests/ui/asm/aarch64/type-check-4.stderr +++ b/tests/ui/asm/aarch64/type-check-4.stderr @@ -1,5 +1,5 @@ error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:25:25 + --> $DIR/type-check-4.rs:24:25 | LL | global_asm!("{}", const S); | ^ @@ -11,7 +11,7 @@ LL | global_asm!("{}", const S); = help: to fix this, the value can be extracted to a `const` and then used. error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:28:35 + --> $DIR/type-check-4.rs:27:35 | LL | global_asm!("{}", const const_foo(S)); | ^ @@ -23,7 +23,7 @@ LL | global_asm!("{}", const const_foo(S)); = help: to fix this, the value can be extracted to a `const` and then used. error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:31:35 + --> $DIR/type-check-4.rs:30:35 | LL | global_asm!("{}", const const_bar(S)); | ^ diff --git a/tests/ui/asm/bad-template.rs b/tests/ui/asm/bad-template.rs index 41a906e32a4ba..6b00905a39326 100644 --- a/tests/ui/asm/bad-template.rs +++ b/tests/ui/asm/bad-template.rs @@ -6,7 +6,7 @@ //@ [x86_64] needs-llvm-components: x86 //@ [aarch64] needs-llvm-components: aarch64 -#![feature(no_core, lang_items, rustc_attrs, asm_const)] +#![feature(no_core, lang_items, rustc_attrs)] #![no_core] #[rustc_builtin_macro] diff --git a/tests/ui/asm/const-error.rs b/tests/ui/asm/const-error.rs index f2cead399b6fb..40d0590c33e9d 100644 --- a/tests/ui/asm/const-error.rs +++ b/tests/ui/asm/const-error.rs @@ -1,15 +1,15 @@ //@ only-x86_64 //@ needs-asm-support -#![feature(asm_const)] - // Test to make sure that we emit const errors eagerly for inline asm use std::arch::asm; fn test() { - unsafe { asm!("/* {} */", const 1 / 0); } - //~^ ERROR evaluation of + unsafe { + asm!("/* {} */", const 1 / 0); + //~^ ERROR evaluation of + } } fn main() {} diff --git a/tests/ui/asm/const-error.stderr b/tests/ui/asm/const-error.stderr index fe31183217726..02e54457e89b9 100644 --- a/tests/ui/asm/const-error.stderr +++ b/tests/ui/asm/const-error.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation of `test::::{constant#0}` failed - --> $DIR/const-error.rs:11:37 + --> $DIR/const-error.rs:10:32 | -LL | unsafe { asm!("/* {} */", const 1 / 0); } - | ^^^^^ attempt to divide `1_i32` by zero +LL | asm!("/* {} */", const 1 / 0); + | ^^^^^ attempt to divide `1_i32` by zero error: aborting due to 1 previous error diff --git a/tests/ui/asm/fail-const-eval-issue-121099.rs b/tests/ui/asm/fail-const-eval-issue-121099.rs index bed6fc9b39f9d..36d00b1e5d23c 100644 --- a/tests/ui/asm/fail-const-eval-issue-121099.rs +++ b/tests/ui/asm/fail-const-eval-issue-121099.rs @@ -1,6 +1,5 @@ //@ build-fail //@ needs-asm-support -#![feature(asm_const)] use std::arch::global_asm; diff --git a/tests/ui/asm/fail-const-eval-issue-121099.stderr b/tests/ui/asm/fail-const-eval-issue-121099.stderr index 51d283218d227..5d86c3a5f7bdd 100644 --- a/tests/ui/asm/fail-const-eval-issue-121099.stderr +++ b/tests/ui/asm/fail-const-eval-issue-121099.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/fail-const-eval-issue-121099.rs:9:31 + --> $DIR/fail-const-eval-issue-121099.rs:8:31 | LL | global_asm!("/* {} */", const 1 << 500); | ^^^^^^^^ attempt to shift left by `500_i32`, which would overflow error[E0080]: evaluation of constant value failed - --> $DIR/fail-const-eval-issue-121099.rs:11:31 + --> $DIR/fail-const-eval-issue-121099.rs:10:31 | LL | global_asm!("/* {} */", const 1 / 0); | ^^^^^ attempt to divide `1_i32` by zero diff --git a/tests/ui/asm/generic-const.rs b/tests/ui/asm/generic-const.rs index 133d093d200ec..3b69a4e86e3e8 100644 --- a/tests/ui/asm/generic-const.rs +++ b/tests/ui/asm/generic-const.rs @@ -1,8 +1,6 @@ //@ needs-asm-support //@ build-pass -#![feature(asm_const)] - use std::arch::asm; fn foofoo() {} diff --git a/tests/ui/asm/invalid-const-operand.rs b/tests/ui/asm/invalid-const-operand.rs index eff335ff6aaa3..a688f5042db5c 100644 --- a/tests/ui/asm/invalid-const-operand.rs +++ b/tests/ui/asm/invalid-const-operand.rs @@ -2,8 +2,6 @@ //@ ignore-nvptx64 //@ ignore-spirv -#![feature(asm_const)] - use std::arch::{asm, global_asm}; // Const operands must be integers and must be constants. diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr index a6d742b53c217..bda4b0355b7c4 100644 --- a/tests/ui/asm/invalid-const-operand.stderr +++ b/tests/ui/asm/invalid-const-operand.stderr @@ -1,5 +1,5 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:42:26 + --> $DIR/invalid-const-operand.rs:40:26 | LL | asm!("{}", const x); | ^ non-constant value @@ -10,7 +10,7 @@ LL | const x: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:45:36 + --> $DIR/invalid-const-operand.rs:43:36 | LL | asm!("{}", const const_foo(x)); | ^ non-constant value @@ -21,7 +21,7 @@ LL | const x: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:48:36 + --> $DIR/invalid-const-operand.rs:46:36 | LL | asm!("{}", const const_bar(x)); | ^ non-constant value @@ -32,7 +32,7 @@ LL | const x: /* Type */ = 0; | ~~~~~ ++++++++++++ error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:14:19 + --> $DIR/invalid-const-operand.rs:12:19 | LL | global_asm!("{}", const 0f32); | ^^^^^^---- @@ -42,7 +42,7 @@ LL | global_asm!("{}", const 0f32); = help: `const` operands must be of an integer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:16:19 + --> $DIR/invalid-const-operand.rs:14:19 | LL | global_asm!("{}", const 0 as *mut u8); | ^^^^^^------------ @@ -52,7 +52,7 @@ LL | global_asm!("{}", const 0 as *mut u8); = help: `const` operands must be of an integer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:26:20 + --> $DIR/invalid-const-operand.rs:24:20 | LL | asm!("{}", const 0f32); | ^^^^^^---- @@ -62,7 +62,7 @@ LL | asm!("{}", const 0f32); = help: `const` operands must be of an integer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:28:20 + --> $DIR/invalid-const-operand.rs:26:20 | LL | asm!("{}", const 0 as *mut u8); | ^^^^^^------------ @@ -72,7 +72,7 @@ LL | asm!("{}", const 0 as *mut u8); = help: `const` operands must be of an integer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:30:20 + --> $DIR/invalid-const-operand.rs:28:20 | LL | asm!("{}", const &0); | ^^^^^^-- diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index cb1e5c325c239..116a84506c570 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -3,7 +3,7 @@ //@ ignore-spirv #![feature(naked_functions)] -#![feature(asm_const, asm_unwind, linkage)] +#![feature(asm_unwind, linkage)] #![crate_type = "lib"] use std::arch::asm; diff --git a/tests/ui/asm/named-asm-labels.rs b/tests/ui/asm/named-asm-labels.rs index d2ca6fe8808bc..043aab9029d7d 100644 --- a/tests/ui/asm/named-asm-labels.rs +++ b/tests/ui/asm/named-asm-labels.rs @@ -10,7 +10,7 @@ // which causes less readable LLVM errors and in the worst cases causes ICEs // or segfaults based on system dependent behavior and codegen flags. -#![feature(naked_functions, asm_const)] +#![feature(naked_functions)] use std::arch::{asm, global_asm}; @@ -128,6 +128,7 @@ fn main() { // Tests usage of colons in non-label positions asm!(":lo12:FOO"); // this is apparently valid aarch64 + // is there an example that is valid x86 for this test? asm!(":bbb nop"); @@ -176,7 +177,8 @@ fn main() { // label or LTO can cause labels to break #[naked] pub extern "C" fn foo() -> i32 { - unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } //~ ERROR avoid using named labels + unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } + //~^ ERROR avoid using named labels } // Make sure that non-naked attributes *do* still let the lint happen diff --git a/tests/ui/asm/named-asm-labels.stderr b/tests/ui/asm/named-asm-labels.stderr index 20b7d64f9e752..e5e177fb8b83e 100644 --- a/tests/ui/asm/named-asm-labels.stderr +++ b/tests/ui/asm/named-asm-labels.stderr @@ -328,7 +328,7 @@ LL | ab: nop // ab: does foo = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:143:19 + --> $DIR/named-asm-labels.rs:144:19 | LL | asm!("test_{}: nop", in(reg) 10); | ^^^^^^^ @@ -338,7 +338,7 @@ LL | asm!("test_{}: nop", in(reg) 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:145:15 + --> $DIR/named-asm-labels.rs:146:15 | LL | asm!("test_{}: nop", const 10); | ^^^^^^^ @@ -348,7 +348,7 @@ LL | asm!("test_{}: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:146:15 + --> $DIR/named-asm-labels.rs:147:15 | LL | asm!("test_{}: nop", sym main); | ^^^^^^^ @@ -358,7 +358,7 @@ LL | asm!("test_{}: nop", sym main); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:147:15 + --> $DIR/named-asm-labels.rs:148:15 | LL | asm!("{}_test: nop", const 10); | ^^^^^^^ @@ -368,7 +368,7 @@ LL | asm!("{}_test: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:148:15 + --> $DIR/named-asm-labels.rs:149:15 | LL | asm!("test_{}_test: nop", const 10); | ^^^^^^^^^^^^ @@ -378,7 +378,7 @@ LL | asm!("test_{}_test: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:149:15 + --> $DIR/named-asm-labels.rs:150:15 | LL | asm!("{}: nop", const 10); | ^^ @@ -388,7 +388,7 @@ LL | asm!("{}: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:151:15 + --> $DIR/named-asm-labels.rs:152:15 | LL | asm!("{uwu}: nop", uwu = const 10); | ^^^^^ @@ -398,7 +398,7 @@ LL | asm!("{uwu}: nop", uwu = const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:152:15 + --> $DIR/named-asm-labels.rs:153:15 | LL | asm!("{0}: nop", const 10); | ^^^ @@ -408,7 +408,7 @@ LL | asm!("{0}: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:153:15 + --> $DIR/named-asm-labels.rs:154:15 | LL | asm!("{1}: nop", "/* {0} */", const 10, const 20); | ^^^ @@ -418,7 +418,7 @@ LL | asm!("{1}: nop", "/* {0} */", const 10, const 20); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:156:14 + --> $DIR/named-asm-labels.rs:157:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -428,7 +428,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: the label may be declared in the expansion of a macro error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:156:14 + --> $DIR/named-asm-labels.rs:157:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -439,7 +439,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:156:14 + --> $DIR/named-asm-labels.rs:157:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -450,7 +450,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:156:14 + --> $DIR/named-asm-labels.rs:157:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -461,7 +461,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:170:19 + --> $DIR/named-asm-labels.rs:171:19 | LL | asm!("warned: nop"); | ^^^^^^ @@ -469,13 +469,13 @@ LL | asm!("warned: nop"); = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information note: the lint level is defined here - --> $DIR/named-asm-labels.rs:168:16 + --> $DIR/named-asm-labels.rs:169:16 | LL | #[warn(named_asm_labels)] | ^^^^^^^^^^^^^^^^ error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:179:20 + --> $DIR/named-asm-labels.rs:180:20 | LL | unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } | ^^^^^ @@ -484,7 +484,7 @@ LL | unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noret = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:185:20 + --> $DIR/named-asm-labels.rs:187:20 | LL | unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } | ^^^^^ @@ -493,7 +493,7 @@ LL | unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noret = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:193:20 + --> $DIR/named-asm-labels.rs:195:20 | LL | unsafe { asm!(".Laaa: nop; ret;", options(noreturn)) } | ^^^^^ @@ -502,7 +502,7 @@ LL | unsafe { asm!(".Laaa: nop; ret;", options(noreturn)) } = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:203:24 + --> $DIR/named-asm-labels.rs:205:24 | LL | unsafe { asm!(".Lbbb: nop; ret;", options(noreturn)) } | ^^^^^ @@ -511,7 +511,7 @@ LL | unsafe { asm!(".Lbbb: nop; ret;", options(noreturn)) } = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:212:15 + --> $DIR/named-asm-labels.rs:214:15 | LL | asm!("closure1: nop"); | ^^^^^^^^ @@ -520,7 +520,7 @@ LL | asm!("closure1: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:216:15 + --> $DIR/named-asm-labels.rs:218:15 | LL | asm!("closure2: nop"); | ^^^^^^^^ @@ -529,7 +529,7 @@ LL | asm!("closure2: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:226:19 + --> $DIR/named-asm-labels.rs:228:19 | LL | asm!("closure3: nop"); | ^^^^^^^^ diff --git a/tests/ui/asm/parse-error.rs b/tests/ui/asm/parse-error.rs index 16ae02828642d..4d7b522f5fc5b 100644 --- a/tests/ui/asm/parse-error.rs +++ b/tests/ui/asm/parse-error.rs @@ -1,7 +1,5 @@ //@ needs-asm-support -#![feature(asm_const)] - use std::arch::{asm, global_asm}; fn main() { diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index f5f8d537d86b8..6d0e629b93775 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -1,167 +1,167 @@ error: requires at least a template string argument - --> $DIR/parse-error.rs:11:9 + --> $DIR/parse-error.rs:9:9 | LL | asm!(); | ^^^^^^ error: asm template must be a string literal - --> $DIR/parse-error.rs:13:14 + --> $DIR/parse-error.rs:11:14 | LL | asm!(foo); | ^^^ error: expected token: `,` - --> $DIR/parse-error.rs:15:19 + --> $DIR/parse-error.rs:13:19 | LL | asm!("{}" foo); | ^^^ expected `,` error: expected operand, clobber_abi, options, or additional template string - --> $DIR/parse-error.rs:17:20 + --> $DIR/parse-error.rs:15:20 | LL | asm!("{}", foo); | ^^^ expected operand, clobber_abi, options, or additional template string error: expected `(`, found `foo` - --> $DIR/parse-error.rs:19:23 + --> $DIR/parse-error.rs:17:23 | LL | asm!("{}", in foo); | ^^^ expected `(` error: expected `)`, found `foo` - --> $DIR/parse-error.rs:21:27 + --> $DIR/parse-error.rs:19:27 | LL | asm!("{}", in(reg foo)); | ^^^ expected `)` error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:23:27 + --> $DIR/parse-error.rs:21:27 | LL | asm!("{}", in(reg)); | ^ expected expression error: expected register class or explicit register - --> $DIR/parse-error.rs:25:26 + --> $DIR/parse-error.rs:23:26 | LL | asm!("{}", inout(=) foo => bar); | ^ error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:27:37 + --> $DIR/parse-error.rs:25:37 | LL | asm!("{}", inout(reg) foo =>); | ^ expected expression error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` - --> $DIR/parse-error.rs:29:32 + --> $DIR/parse-error.rs:27:32 | LL | asm!("{}", in(reg) foo => bar); | ^^ expected one of 7 possible tokens error: expected a path for argument to `sym` - --> $DIR/parse-error.rs:31:24 + --> $DIR/parse-error.rs:29:24 | LL | asm!("{}", sym foo + bar); | ^^^^^^^^^ error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo` - --> $DIR/parse-error.rs:33:26 + --> $DIR/parse-error.rs:31:26 | LL | asm!("", options(foo)); | ^^^ expected one of 10 possible tokens error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:35:32 + --> $DIR/parse-error.rs:33:32 | LL | asm!("", options(nomem foo)); | ^^^ expected one of `)` or `,` error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo` - --> $DIR/parse-error.rs:37:33 + --> $DIR/parse-error.rs:35:33 | LL | asm!("", options(nomem, foo)); | ^^^ expected one of 10 possible tokens error: at least one abi must be provided as an argument to `clobber_abi` - --> $DIR/parse-error.rs:44:30 + --> $DIR/parse-error.rs:42:30 | LL | asm!("", clobber_abi()); | ^ error: expected string literal - --> $DIR/parse-error.rs:46:30 + --> $DIR/parse-error.rs:44:30 | LL | asm!("", clobber_abi(foo)); | ^^^ not a string literal error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:48:34 + --> $DIR/parse-error.rs:46:34 | LL | asm!("", clobber_abi("C" foo)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:50:35 + --> $DIR/parse-error.rs:48:35 | LL | asm!("", clobber_abi("C", foo)); | ^^^ not a string literal error: expected string literal - --> $DIR/parse-error.rs:52:30 + --> $DIR/parse-error.rs:50:30 | LL | asm!("", clobber_abi(1)); | ^ not a string literal error: expected string literal - --> $DIR/parse-error.rs:54:30 + --> $DIR/parse-error.rs:52:30 | LL | asm!("", clobber_abi(())); | ^ not a string literal error: expected string literal - --> $DIR/parse-error.rs:56:30 + --> $DIR/parse-error.rs:54:30 | LL | asm!("", clobber_abi(uwu)); | ^^^ not a string literal error: expected string literal - --> $DIR/parse-error.rs:58:30 + --> $DIR/parse-error.rs:56:30 | LL | asm!("", clobber_abi({})); | ^ not a string literal error: expected string literal - --> $DIR/parse-error.rs:60:30 + --> $DIR/parse-error.rs:58:30 | LL | asm!("", clobber_abi(loop {})); | ^^^^ not a string literal error: expected string literal - --> $DIR/parse-error.rs:62:30 + --> $DIR/parse-error.rs:60:30 | LL | asm!("", clobber_abi(if)); | ^^ not a string literal error: expected string literal - --> $DIR/parse-error.rs:64:30 + --> $DIR/parse-error.rs:62:30 | LL | asm!("", clobber_abi(do)); | ^^ not a string literal error: expected string literal - --> $DIR/parse-error.rs:66:30 + --> $DIR/parse-error.rs:64:30 | LL | asm!("", clobber_abi(<)); | ^ not a string literal error: expected string literal - --> $DIR/parse-error.rs:68:30 + --> $DIR/parse-error.rs:66:30 | LL | asm!("", clobber_abi(.)); | ^ not a string literal error: duplicate argument named `a` - --> $DIR/parse-error.rs:76:36 + --> $DIR/parse-error.rs:74:36 | LL | asm!("{a}", a = const foo, a = const bar); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -169,7 +169,7 @@ LL | asm!("{a}", a = const foo, a = const bar); | previously here error: argument never used - --> $DIR/parse-error.rs:76:36 + --> $DIR/parse-error.rs:74:36 | LL | asm!("{a}", a = const foo, a = const bar); | ^^^^^^^^^^^^^ argument never used @@ -177,19 +177,19 @@ LL | asm!("{a}", a = const foo, a = const bar); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` - --> $DIR/parse-error.rs:82:29 + --> $DIR/parse-error.rs:80:29 | LL | asm!("", options(), ""); | ^^ expected one of 10 possible tokens error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:84:33 + --> $DIR/parse-error.rs:82:33 | LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); | ^^^^ expected one of 10 possible tokens error: asm template must be a string literal - --> $DIR/parse-error.rs:86:14 + --> $DIR/parse-error.rs:84:14 | LL | asm!(format!("{{{}}}", 0), in(reg) foo); | ^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | asm!(format!("{{{}}}", 0), in(reg) foo); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:88:21 + --> $DIR/parse-error.rs:86:21 | LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); | ^^^^^^^^^^^^^^^^^^^^ @@ -205,139 +205,139 @@ LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: _ cannot be used for input operands - --> $DIR/parse-error.rs:90:28 + --> $DIR/parse-error.rs:88:28 | LL | asm!("{}", in(reg) _); | ^ error: _ cannot be used for input operands - --> $DIR/parse-error.rs:92:31 + --> $DIR/parse-error.rs:90:31 | LL | asm!("{}", inout(reg) _); | ^ error: _ cannot be used for input operands - --> $DIR/parse-error.rs:94:35 + --> $DIR/parse-error.rs:92:35 | LL | asm!("{}", inlateout(reg) _); | ^ error: requires at least a template string argument - --> $DIR/parse-error.rs:101:1 + --> $DIR/parse-error.rs:99:1 | LL | global_asm!(); | ^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/parse-error.rs:103:13 + --> $DIR/parse-error.rs:101:13 | LL | global_asm!(FOO); | ^^^ error: expected token: `,` - --> $DIR/parse-error.rs:105:18 + --> $DIR/parse-error.rs:103:18 | LL | global_asm!("{}" FOO); | ^^^ expected `,` error: expected operand, options, or additional template string - --> $DIR/parse-error.rs:107:19 + --> $DIR/parse-error.rs:105:19 | LL | global_asm!("{}", FOO); | ^^^ expected operand, options, or additional template string error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:109:24 + --> $DIR/parse-error.rs:107:24 | LL | global_asm!("{}", const); | ^ expected expression error: expected one of `,`, `.`, `?`, or an operator, found `FOO` - --> $DIR/parse-error.rs:111:30 + --> $DIR/parse-error.rs:109:30 | LL | global_asm!("{}", const(reg) FOO); | ^^^ expected one of `,`, `.`, `?`, or an operator error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:113:25 + --> $DIR/parse-error.rs:111:25 | LL | global_asm!("", options(FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:115:25 + --> $DIR/parse-error.rs:113:25 | LL | global_asm!("", options(FOO,)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:117:25 + --> $DIR/parse-error.rs:115:25 | LL | global_asm!("", options(nomem FOO)); | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:117:31 + --> $DIR/parse-error.rs:115:31 | LL | global_asm!("", options(nomem FOO)); | ^^^ expected one of `)` or `,` error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:120:25 + --> $DIR/parse-error.rs:118:25 | LL | global_asm!("", options(nomem, FOO)); | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:120:32 + --> $DIR/parse-error.rs:118:32 | LL | global_asm!("", options(nomem, FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected string literal - --> $DIR/parse-error.rs:124:29 + --> $DIR/parse-error.rs:122:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:126:33 + --> $DIR/parse-error.rs:124:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:128:34 + --> $DIR/parse-error.rs:126:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:130:19 + --> $DIR/parse-error.rs:128:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:132:28 + --> $DIR/parse-error.rs:130:28 | LL | global_asm!("", options(), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:134:30 + --> $DIR/parse-error.rs:132:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:136:17 + --> $DIR/parse-error.rs:134:17 | LL | global_asm!("", clobber_abi("C"), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:138:35 + --> $DIR/parse-error.rs:136:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -345,7 +345,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:138:35 + --> $DIR/parse-error.rs:136:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -353,19 +353,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:141:28 + --> $DIR/parse-error.rs:139:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:143:30 + --> $DIR/parse-error.rs:141:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:145:13 + --> $DIR/parse-error.rs:143:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -373,7 +373,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:147:20 + --> $DIR/parse-error.rs:145:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -381,43 +381,43 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: the `in` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:150:19 + --> $DIR/parse-error.rs:148:19 | LL | global_asm!("{}", in(reg)); | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `out` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:152:19 + --> $DIR/parse-error.rs:150:19 | LL | global_asm!("{}", out(reg)); | ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it error: the `lateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:154:19 + --> $DIR/parse-error.rs:152:19 | LL | global_asm!("{}", lateout(reg)); | ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:156:19 + --> $DIR/parse-error.rs:154:19 | LL | global_asm!("{}", inout(reg)); | ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inlateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:158:19 + --> $DIR/parse-error.rs:156:19 | LL | global_asm!("{}", inlateout(reg)); | ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `label` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:160:19 + --> $DIR/parse-error.rs:158:19 | LL | global_asm!("{}", label(reg)); | ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:39:37 + --> $DIR/parse-error.rs:37:37 | LL | asm!("{}", options(), const foo); | ^^^ non-constant value @@ -428,7 +428,7 @@ LL | const foo: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:71:44 + --> $DIR/parse-error.rs:69:44 | LL | asm!("{}", clobber_abi("C"), const foo); | ^^^ non-constant value @@ -439,7 +439,7 @@ LL | const foo: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:74:55 + --> $DIR/parse-error.rs:72:55 | LL | asm!("{}", options(), clobber_abi("C"), const foo); | ^^^ non-constant value @@ -450,7 +450,7 @@ LL | const foo: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:76:31 + --> $DIR/parse-error.rs:74:31 | LL | asm!("{a}", a = const foo, a = const bar); | ^^^ non-constant value @@ -461,7 +461,7 @@ LL | const foo: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:76:46 + --> $DIR/parse-error.rs:74:46 | LL | asm!("{a}", a = const foo, a = const bar); | ^^^ non-constant value diff --git a/tests/ui/asm/type-check-1.rs b/tests/ui/asm/type-check-1.rs index 22669dce280bb..4dc30fb58385c 100644 --- a/tests/ui/asm/type-check-1.rs +++ b/tests/ui/asm/type-check-1.rs @@ -2,8 +2,6 @@ //@ ignore-nvptx64 //@ ignore-spirv -#![feature(asm_const)] - use std::arch::{asm, global_asm}; fn main() { diff --git a/tests/ui/asm/type-check-1.stderr b/tests/ui/asm/type-check-1.stderr index d47e6ae1d2a97..aa9eed2fce65c 100644 --- a/tests/ui/asm/type-check-1.stderr +++ b/tests/ui/asm/type-check-1.stderr @@ -1,17 +1,17 @@ error: invalid asm output - --> $DIR/type-check-1.rs:14:29 + --> $DIR/type-check-1.rs:12:29 | LL | asm!("{}", out(reg) 1 + 2); | ^^^^^ cannot assign to this expression error: invalid asm output - --> $DIR/type-check-1.rs:16:31 + --> $DIR/type-check-1.rs:14:31 | LL | asm!("{}", inout(reg) 1 + 2); | ^^^^^ cannot assign to this expression error[E0277]: the size for values of type `[u64]` cannot be known at compilation time - --> $DIR/type-check-1.rs:22:28 + --> $DIR/type-check-1.rs:20:28 | LL | asm!("{}", in(reg) v[..]); | ^^^^^ doesn't have a size known at compile-time @@ -20,7 +20,7 @@ LL | asm!("{}", in(reg) v[..]); = note: all inline asm arguments must have a statically known size error[E0277]: the size for values of type `[u64]` cannot be known at compilation time - --> $DIR/type-check-1.rs:25:29 + --> $DIR/type-check-1.rs:23:29 | LL | asm!("{}", out(reg) v[..]); | ^^^^^ doesn't have a size known at compile-time @@ -29,7 +29,7 @@ LL | asm!("{}", out(reg) v[..]); = note: all inline asm arguments must have a statically known size error[E0277]: the size for values of type `[u64]` cannot be known at compilation time - --> $DIR/type-check-1.rs:28:31 + --> $DIR/type-check-1.rs:26:31 | LL | asm!("{}", inout(reg) v[..]); | ^^^^^ doesn't have a size known at compile-time @@ -38,7 +38,7 @@ LL | asm!("{}", inout(reg) v[..]); = note: all inline asm arguments must have a statically known size error: cannot use value of type `[u64]` for inline assembly - --> $DIR/type-check-1.rs:22:28 + --> $DIR/type-check-1.rs:20:28 | LL | asm!("{}", in(reg) v[..]); | ^^^^^ @@ -46,7 +46,7 @@ LL | asm!("{}", in(reg) v[..]); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: cannot use value of type `[u64]` for inline assembly - --> $DIR/type-check-1.rs:25:29 + --> $DIR/type-check-1.rs:23:29 | LL | asm!("{}", out(reg) v[..]); | ^^^^^ @@ -54,7 +54,7 @@ LL | asm!("{}", out(reg) v[..]); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: cannot use value of type `[u64]` for inline assembly - --> $DIR/type-check-1.rs:28:31 + --> $DIR/type-check-1.rs:26:31 | LL | asm!("{}", inout(reg) v[..]); | ^^^^^ diff --git a/tests/ui/asm/x86_64/bad-reg.rs b/tests/ui/asm/x86_64/bad-reg.rs index d41c46d57bb13..2a189a91c5a47 100644 --- a/tests/ui/asm/x86_64/bad-reg.rs +++ b/tests/ui/asm/x86_64/bad-reg.rs @@ -1,8 +1,6 @@ //@ only-x86_64 //@ compile-flags: -C target-feature=+avx2 -#![feature(asm_const)] - use std::arch::asm; fn main() { diff --git a/tests/ui/asm/x86_64/bad-reg.stderr b/tests/ui/asm/x86_64/bad-reg.stderr index 8017008e97d75..3df1f7b220869 100644 --- a/tests/ui/asm/x86_64/bad-reg.stderr +++ b/tests/ui/asm/x86_64/bad-reg.stderr @@ -1,17 +1,17 @@ error: invalid register class `foo`: unknown register class - --> $DIR/bad-reg.rs:14:20 + --> $DIR/bad-reg.rs:12:20 | LL | asm!("{}", in(foo) foo); | ^^^^^^^^^^^ error: invalid register `foo`: unknown register - --> $DIR/bad-reg.rs:16:18 + --> $DIR/bad-reg.rs:14:18 | LL | asm!("", in("foo") foo); | ^^^^^^^^^^^^^ error: invalid asm template modifier for this register class - --> $DIR/bad-reg.rs:18:15 + --> $DIR/bad-reg.rs:16:15 | LL | asm!("{:z}", in(reg) foo); | ^^^^ ----------- argument @@ -21,7 +21,7 @@ LL | asm!("{:z}", in(reg) foo); = note: the `reg` register class supports the following template modifiers: `l`, `x`, `e`, `r` error: invalid asm template modifier for this register class - --> $DIR/bad-reg.rs:20:15 + --> $DIR/bad-reg.rs:18:15 | LL | asm!("{:r}", in(xmm_reg) foo); | ^^^^ --------------- argument @@ -31,7 +31,7 @@ LL | asm!("{:r}", in(xmm_reg) foo); = note: the `xmm_reg` register class supports the following template modifiers: `x`, `y`, `z` error: asm template modifiers are not allowed for `const` arguments - --> $DIR/bad-reg.rs:22:15 + --> $DIR/bad-reg.rs:20:15 | LL | asm!("{:a}", const 0); | ^^^^ ------- argument @@ -39,7 +39,7 @@ LL | asm!("{:a}", const 0); | template modifier error: asm template modifiers are not allowed for `sym` arguments - --> $DIR/bad-reg.rs:24:15 + --> $DIR/bad-reg.rs:22:15 | LL | asm!("{:a}", sym main); | ^^^^ -------- argument @@ -47,67 +47,67 @@ LL | asm!("{:a}", sym main); | template modifier error: invalid register `ebp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:24:18 | LL | asm!("", in("ebp") foo); | ^^^^^^^^^^^^^ error: invalid register `rsp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", in("rsp") foo); | ^^^^^^^^^^^^^ error: invalid register `ip`: the instruction pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", in("ip") foo); | ^^^^^^^^^^^^ error: register class `x87_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", in("st(2)") foo); | ^^^^^^^^^^^^^^^ error: register class `mmx_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", in("mm0") foo); | ^^^^^^^^^^^^^ error: register class `kreg0` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", in("k0") foo); | ^^^^^^^^^^^^ error: register class `x87_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:44:20 + --> $DIR/bad-reg.rs:42:20 | LL | asm!("{}", in(x87_reg) foo); | ^^^^^^^^^^^^^^^ error: register class `mmx_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:47:20 + --> $DIR/bad-reg.rs:45:20 | LL | asm!("{}", in(mmx_reg) foo); | ^^^^^^^^^^^^^^^ error: register class `x87_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:50:20 + --> $DIR/bad-reg.rs:48:20 | LL | asm!("{}", out(x87_reg) _); | ^^^^^^^^^^^^^^ error: register class `mmx_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:52:20 + --> $DIR/bad-reg.rs:50:20 | LL | asm!("{}", out(mmx_reg) _); | ^^^^^^^^^^^^^^ error: register `al` conflicts with register `eax` - --> $DIR/bad-reg.rs:58:33 + --> $DIR/bad-reg.rs:56:33 | LL | asm!("", in("eax") foo, in("al") bar); | ------------- ^^^^^^^^^^^^ register `al` @@ -115,7 +115,7 @@ LL | asm!("", in("eax") foo, in("al") bar); | register `eax` error: register `rax` conflicts with register `rax` - --> $DIR/bad-reg.rs:61:33 + --> $DIR/bad-reg.rs:59:33 | LL | asm!("", in("rax") foo, out("rax") bar); | ------------- ^^^^^^^^^^^^^^ register `rax` @@ -123,13 +123,13 @@ LL | asm!("", in("rax") foo, out("rax") bar); | register `rax` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:61:18 + --> $DIR/bad-reg.rs:59:18 | LL | asm!("", in("rax") foo, out("rax") bar); | ^^^^^^^^^^^^^ error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:66:34 + --> $DIR/bad-reg.rs:64:34 | LL | asm!("", in("xmm0") foo, in("ymm0") bar); | -------------- ^^^^^^^^^^^^^^ register `ymm0` @@ -137,7 +137,7 @@ LL | asm!("", in("xmm0") foo, in("ymm0") bar); | register `xmm0` error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:68:34 + --> $DIR/bad-reg.rs:66:34 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | -------------- ^^^^^^^^^^^^^^^ register `ymm0` @@ -145,13 +145,13 @@ LL | asm!("", in("xmm0") foo, out("ymm0") bar); | register `xmm0` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:68:18 + --> $DIR/bad-reg.rs:66:18 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | ^^^^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:33:30 + --> $DIR/bad-reg.rs:31:30 | LL | asm!("", in("st(2)") foo); | ^^^ @@ -159,7 +159,7 @@ LL | asm!("", in("st(2)") foo); = note: register class `x87_reg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:36:28 + --> $DIR/bad-reg.rs:34:28 | LL | asm!("", in("mm0") foo); | ^^^ @@ -167,7 +167,7 @@ LL | asm!("", in("mm0") foo); = note: register class `mmx_reg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:39:27 + --> $DIR/bad-reg.rs:37:27 | LL | asm!("", in("k0") foo); | ^^^ @@ -175,7 +175,7 @@ LL | asm!("", in("k0") foo); = note: register class `kreg0` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:44:32 + --> $DIR/bad-reg.rs:42:32 | LL | asm!("{}", in(x87_reg) foo); | ^^^ @@ -183,7 +183,7 @@ LL | asm!("{}", in(x87_reg) foo); = note: register class `x87_reg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:47:32 + --> $DIR/bad-reg.rs:45:32 | LL | asm!("{}", in(mmx_reg) foo); | ^^^ @@ -191,7 +191,7 @@ LL | asm!("{}", in(mmx_reg) foo); = note: register class `mmx_reg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:58:42 + --> $DIR/bad-reg.rs:56:42 | LL | asm!("", in("eax") foo, in("al") bar); | ^^^ @@ -199,7 +199,7 @@ LL | asm!("", in("eax") foo, in("al") bar); = note: register class `reg_byte` supports these types: i8 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:63:27 + --> $DIR/bad-reg.rs:61:27 | LL | asm!("", in("al") foo, lateout("al") bar); | ^^^ @@ -207,7 +207,7 @@ LL | asm!("", in("al") foo, lateout("al") bar); = note: register class `reg_byte` supports these types: i8 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:63:46 + --> $DIR/bad-reg.rs:61:46 | LL | asm!("", in("al") foo, lateout("al") bar); | ^^^ diff --git a/tests/ui/asm/x86_64/const.rs b/tests/ui/asm/x86_64/const.rs index 817a338a5b99f..eaaaf92e823b4 100644 --- a/tests/ui/asm/x86_64/const.rs +++ b/tests/ui/asm/x86_64/const.rs @@ -2,8 +2,6 @@ //@ run-pass //@ needs-asm-support -#![feature(asm_const)] - use std::arch::{asm, global_asm}; fn const_generic() -> usize { diff --git a/tests/ui/asm/x86_64/type-check-3.rs b/tests/ui/asm/x86_64/type-check-3.rs index bd242af3dbc07..bfb795d262455 100644 --- a/tests/ui/asm/x86_64/type-check-3.rs +++ b/tests/ui/asm/x86_64/type-check-3.rs @@ -1,8 +1,6 @@ //@ only-x86_64 //@ compile-flags: -C target-feature=+avx512f -#![feature(asm_const)] - use std::arch::{asm, global_asm}; use std::arch::x86_64::{_mm256_setzero_ps, _mm_setzero_ps}; diff --git a/tests/ui/asm/x86_64/type-check-3.stderr b/tests/ui/asm/x86_64/type-check-3.stderr index 202b97ca5c0e8..5a7b349413e45 100644 --- a/tests/ui/asm/x86_64/type-check-3.stderr +++ b/tests/ui/asm/x86_64/type-check-3.stderr @@ -1,5 +1,5 @@ error: type `i128` cannot be used with this register class - --> $DIR/type-check-3.rs:14:28 + --> $DIR/type-check-3.rs:12:28 | LL | asm!("{}", in(reg) 0i128); | ^^^^^ @@ -7,7 +7,7 @@ LL | asm!("{}", in(reg) 0i128); = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64 error: type `__m128` cannot be used with this register class - --> $DIR/type-check-3.rs:16:28 + --> $DIR/type-check-3.rs:14:28 | LL | asm!("{}", in(reg) _mm_setzero_ps()); | ^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | asm!("{}", in(reg) _mm_setzero_ps()); = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64 error: type `__m256` cannot be used with this register class - --> $DIR/type-check-3.rs:18:28 + --> $DIR/type-check-3.rs:16:28 | LL | asm!("{}", in(reg) _mm256_setzero_ps()); | ^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | asm!("{}", in(reg) _mm256_setzero_ps()); = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64 error: type `u8` cannot be used with this register class - --> $DIR/type-check-3.rs:20:32 + --> $DIR/type-check-3.rs:18:32 | LL | asm!("{}", in(xmm_reg) 0u8); | ^^^ @@ -31,7 +31,7 @@ LL | asm!("{}", in(xmm_reg) 0u8); = note: register class `xmm_reg` supports these types: i32, i64, f16, f32, f64, f128, i8x16, i16x8, i32x4, i64x2, f16x8, f32x4, f64x2 error: `avx512bw` target feature is not enabled - --> $DIR/type-check-3.rs:29:29 + --> $DIR/type-check-3.rs:27:29 | LL | asm!("{}", in(kreg) 0u64); | ^^^^ @@ -39,7 +39,7 @@ LL | asm!("{}", in(kreg) 0u64); = note: this is required to use type `u64` with register class `kreg` warning: formatting may not be suitable for sub-register argument - --> $DIR/type-check-3.rs:34:15 + --> $DIR/type-check-3.rs:32:15 | LL | asm!("{0} {0}", in(reg) 0i16); | ^^^ ^^^ ---- for this argument @@ -49,7 +49,7 @@ LL | asm!("{0} {0}", in(reg) 0i16); = note: `#[warn(asm_sub_register)]` on by default warning: formatting may not be suitable for sub-register argument - --> $DIR/type-check-3.rs:36:15 + --> $DIR/type-check-3.rs:34:15 | LL | asm!("{0} {0:x}", in(reg) 0i16); | ^^^ ---- for this argument @@ -58,7 +58,7 @@ LL | asm!("{0} {0:x}", in(reg) 0i16); = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) warning: formatting may not be suitable for sub-register argument - --> $DIR/type-check-3.rs:38:15 + --> $DIR/type-check-3.rs:36:15 | LL | asm!("{}", in(reg) 0i32); | ^^ ---- for this argument @@ -67,7 +67,7 @@ LL | asm!("{}", in(reg) 0i32); = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) warning: formatting may not be suitable for sub-register argument - --> $DIR/type-check-3.rs:41:15 + --> $DIR/type-check-3.rs:39:15 | LL | asm!("{}", in(ymm_reg) 0i64); | ^^ ---- for this argument @@ -76,7 +76,7 @@ LL | asm!("{}", in(ymm_reg) 0i64); = help: or use `{0:y}` to keep the default formatting of `ymm0` (for 256-bit values) error: type `i8` cannot be used with this register class - --> $DIR/type-check-3.rs:52:28 + --> $DIR/type-check-3.rs:50:28 | LL | asm!("{}", in(reg) 0i8); | ^^^ @@ -85,7 +85,7 @@ LL | asm!("{}", in(reg) 0i8); = help: consider using the `reg_byte` register class instead error: incompatible types for asm inout argument - --> $DIR/type-check-3.rs:64:33 + --> $DIR/type-check-3.rs:62:33 | LL | asm!("{:r}", inout(reg) 0u32 => val_f32); | ^^^^ ^^^^^^^ type `f32` @@ -95,7 +95,7 @@ LL | asm!("{:r}", inout(reg) 0u32 => val_f32); = note: asm inout arguments must have the same type, unless they are both pointers or integers of the same size error: incompatible types for asm inout argument - --> $DIR/type-check-3.rs:66:33 + --> $DIR/type-check-3.rs:64:33 | LL | asm!("{:r}", inout(reg) 0u32 => val_ptr); | ^^^^ ^^^^^^^ type `*mut u8` @@ -105,7 +105,7 @@ LL | asm!("{:r}", inout(reg) 0u32 => val_ptr); = note: asm inout arguments must have the same type, unless they are both pointers or integers of the same size error: incompatible types for asm inout argument - --> $DIR/type-check-3.rs:68:33 + --> $DIR/type-check-3.rs:66:33 | LL | asm!("{:r}", inout(reg) main => val_u32); | ^^^^ ^^^^^^^ type `u32` diff --git a/tests/ui/asm/x86_64/type-check-4.rs b/tests/ui/asm/x86_64/type-check-4.rs index f7bf60d04dfdf..9503cd6d8ab2b 100644 --- a/tests/ui/asm/x86_64/type-check-4.rs +++ b/tests/ui/asm/x86_64/type-check-4.rs @@ -1,8 +1,6 @@ //@ only-x86_64 //@ compile-flags: -C target-feature=+avx512f -#![feature(asm_const)] - use std::arch::{asm, global_asm}; use std::arch::x86_64::{_mm256_setzero_ps, _mm_setzero_ps}; diff --git a/tests/ui/asm/x86_64/type-check-4.stderr b/tests/ui/asm/x86_64/type-check-4.stderr index cbdc051b3436b..f1bbc9e7d33de 100644 --- a/tests/ui/asm/x86_64/type-check-4.stderr +++ b/tests/ui/asm/x86_64/type-check-4.stderr @@ -1,5 +1,5 @@ error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:21:25 + --> $DIR/type-check-4.rs:19:25 | LL | global_asm!("{}", const S); | ^ @@ -11,7 +11,7 @@ LL | global_asm!("{}", const S); = help: to fix this, the value can be extracted to a `const` and then used. error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:24:35 + --> $DIR/type-check-4.rs:22:35 | LL | global_asm!("{}", const const_foo(S)); | ^ @@ -23,7 +23,7 @@ LL | global_asm!("{}", const const_foo(S)); = help: to fix this, the value can be extracted to a `const` and then used. error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:27:35 + --> $DIR/type-check-4.rs:25:35 | LL | global_asm!("{}", const const_bar(S)); | ^ diff --git a/tests/ui/asm/x86_64/x86_64_parse_error.rs b/tests/ui/asm/x86_64/x86_64_parse_error.rs index 850033d4ce03b..3df0febf6b070 100644 --- a/tests/ui/asm/x86_64/x86_64_parse_error.rs +++ b/tests/ui/asm/x86_64/x86_64_parse_error.rs @@ -1,7 +1,5 @@ //@ only-x86_64 -#![feature(asm_const)] - use std::arch::asm; fn main() { diff --git a/tests/ui/asm/x86_64/x86_64_parse_error.stderr b/tests/ui/asm/x86_64/x86_64_parse_error.stderr index 9751f7b09d0eb..b64f6c1127eb6 100644 --- a/tests/ui/asm/x86_64/x86_64_parse_error.stderr +++ b/tests/ui/asm/x86_64/x86_64_parse_error.stderr @@ -1,11 +1,11 @@ error: explicit register arguments cannot have names - --> $DIR/x86_64_parse_error.rs:11:18 + --> $DIR/x86_64_parse_error.rs:9:18 | LL | asm!("", a = in("eax") foo); | ^^^^^^^^^^^^^^^^^ error: positional arguments cannot follow named arguments or explicit register arguments - --> $DIR/x86_64_parse_error.rs:17:36 + --> $DIR/x86_64_parse_error.rs:15:36 | LL | asm!("{1}", in("eax") foo, const bar); | ------------- ^^^^^^^^^ positional argument @@ -13,7 +13,7 @@ LL | asm!("{1}", in("eax") foo, const bar); | explicit register argument error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/x86_64_parse_error.rs:13:46 + --> $DIR/x86_64_parse_error.rs:11:46 | LL | asm!("{a}", in("eax") foo, a = const bar); | ^^^ non-constant value @@ -24,7 +24,7 @@ LL | const bar: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/x86_64_parse_error.rs:15:46 + --> $DIR/x86_64_parse_error.rs:13:46 | LL | asm!("{a}", in("eax") foo, a = const bar); | ^^^ non-constant value @@ -35,7 +35,7 @@ LL | const bar: /* Type */ = 0; | ~~~~~ ++++++++++++ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/x86_64_parse_error.rs:17:42 + --> $DIR/x86_64_parse_error.rs:15:42 | LL | asm!("{1}", in("eax") foo, const bar); | ^^^ non-constant value diff --git a/tests/ui/feature-gates/feature-gate-asm_const.rs b/tests/ui/feature-gates/feature-gate-asm_const.rs deleted file mode 100644 index 42d5ba69222d0..0000000000000 --- a/tests/ui/feature-gates/feature-gate-asm_const.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ only-x86_64 - -use std::arch::asm; - -unsafe fn foo() { - asm!("mov eax, {}", const N + 1); - //~^ ERROR const operands for inline assembly are unstable -} - -fn main() { - unsafe { - foo::<0>(); - asm!("mov eax, {}", const 123); - //~^ ERROR const operands for inline assembly are unstable - } -} diff --git a/tests/ui/feature-gates/feature-gate-asm_const.stderr b/tests/ui/feature-gates/feature-gate-asm_const.stderr deleted file mode 100644 index 4f83fee675933..0000000000000 --- a/tests/ui/feature-gates/feature-gate-asm_const.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0658]: const operands for inline assembly are unstable - --> $DIR/feature-gate-asm_const.rs:6:25 - | -LL | asm!("mov eax, {}", const N + 1); - | ^^^^^^^^^^^ - | - = note: see issue #93332 for more information - = help: add `#![feature(asm_const)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: const operands for inline assembly are unstable - --> $DIR/feature-gate-asm_const.rs:13:29 - | -LL | asm!("mov eax, {}", const 123); - | ^^^^^^^^^ - | - = note: see issue #93332 for more information - = help: add `#![feature(asm_const)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. From 342b374043039a1903f632a69dc239f7ceff9866 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 14 Aug 2024 10:25:21 +1000 Subject: [PATCH 175/178] Port `run-make/sysroot-crates-are-unstable` to rmake --- src/tools/tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/sysroot-crates-are-unstable/Makefile | 2 -- tests/run-make/sysroot-crates-are-unstable/rmake.rs | 5 +++++ 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 tests/run-make/sysroot-crates-are-unstable/Makefile create mode 100644 tests/run-make/sysroot-crates-are-unstable/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 14f0a9cd23d21..89735359e1a75 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -21,6 +21,5 @@ run-make/reproducible-build/Makefile run-make/rlib-format-packed-bundled-libs/Makefile run-make/split-debuginfo/Makefile run-make/symbol-mangling-hashed/Makefile -run-make/sysroot-crates-are-unstable/Makefile run-make/translation/Makefile run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile diff --git a/tests/run-make/sysroot-crates-are-unstable/Makefile b/tests/run-make/sysroot-crates-are-unstable/Makefile deleted file mode 100644 index 1e267fb9576ba..0000000000000 --- a/tests/run-make/sysroot-crates-are-unstable/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - '$(PYTHON)' test.py diff --git a/tests/run-make/sysroot-crates-are-unstable/rmake.rs b/tests/run-make/sysroot-crates-are-unstable/rmake.rs new file mode 100644 index 0000000000000..24da387eb8016 --- /dev/null +++ b/tests/run-make/sysroot-crates-are-unstable/rmake.rs @@ -0,0 +1,5 @@ +use run_make_support::python_command; + +fn main() { + python_command().arg("test.py").run(); +} From 8557b56ec7ed3b9d94d0fcbbec97547ffb45e568 Mon Sep 17 00:00:00 2001 From: Jaic1 <506933131@qq.com> Date: Wed, 14 Aug 2024 13:38:03 +0800 Subject: [PATCH 176/178] Add `|` to make the html doc of `Level` rendered correctly --- compiler/rustc_errors/src/lib.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 3bc03a1e51655..fd203c3831897 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1837,23 +1837,23 @@ impl DelayedDiagInner { } } -/// Level is_error EmissionGuarantee Top-level Sub Used in lints? -/// ----- -------- ----------------- --------- --- -------------- -/// Bug yes BugAbort yes - - -/// Fatal yes FatalAbort/FatalError(*) yes - - -/// Error yes ErrorGuaranteed yes - yes -/// DelayedBug yes ErrorGuaranteed yes - - -/// ForceWarning - () yes - lint-only -/// Warning - () yes yes yes -/// Note - () rare yes - -/// OnceNote - () - yes lint-only -/// Help - () rare yes - -/// OnceHelp - () - yes lint-only -/// FailureNote - () rare - - -/// Allow - () yes - lint-only -/// Expect - () yes - lint-only +/// | Level | is_error | EmissionGuarantee | Top-level | Sub | Used in lints? +/// | ----- | -------- | ----------------- | --------- | --- | -------------- +/// | Bug | yes | BugAbort | yes | - | - +/// | Fatal | yes | FatalAbort/FatalError[^star] | yes | - | - +/// | Error | yes | ErrorGuaranteed | yes | - | yes +/// | DelayedBug | yes | ErrorGuaranteed | yes | - | - +/// | ForceWarning | - | () | yes | - | lint-only +/// | Warning | - | () | yes | yes | yes +/// | Note | - | () | rare | yes | - +/// | OnceNote | - | () | - | yes | lint-only +/// | Help | - | () | rare | yes | - +/// | OnceHelp | - | () | - | yes | lint-only +/// | FailureNote | - | () | rare | - | - +/// | Allow | - | () | yes | - | lint-only +/// | Expect | - | () | yes | - | lint-only /// -/// (*) `FatalAbort` normally, `FatalError` in the non-aborting "almost fatal" case that is +/// [^star]: `FatalAbort` normally, `FatalError` in the non-aborting "almost fatal" case that is /// occasionally used. /// #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)] From 5e04cefb01151aa8b09ef311d043cffde0a514ca Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 30 Jul 2024 14:20:16 -0400 Subject: [PATCH 177/178] rewrite min-global-align to rmake --- .../run-make-support/src/assertion_helpers.rs | 14 ++++++++++ src/tools/run-make-support/src/lib.rs | 2 +- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/min-global-align/Makefile | 22 --------------- tests/run-make/min-global-align/rmake.rs | 27 +++++++++++++++++++ 5 files changed, 42 insertions(+), 24 deletions(-) delete mode 100644 tests/run-make/min-global-align/Makefile create mode 100644 tests/run-make/min-global-align/rmake.rs diff --git a/src/tools/run-make-support/src/assertion_helpers.rs b/src/tools/run-make-support/src/assertion_helpers.rs index 6d256fc594d8e..b4da65aff4ab1 100644 --- a/src/tools/run-make-support/src/assertion_helpers.rs +++ b/src/tools/run-make-support/src/assertion_helpers.rs @@ -77,6 +77,20 @@ pub fn assert_not_contains_regex, N: AsRef>(haystack: H, need } } +/// Assert that `haystack` contains `needle` a `count` number of times. +#[track_caller] +pub fn assert_count_is, N: AsRef>(count: usize, haystack: H, needle: N) { + let haystack = haystack.as_ref(); + let needle = needle.as_ref(); + if count != haystack.matches(needle).count() { + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + panic!("needle did not appear {count} times in haystack"); + } +} + /// Assert that all files in `dir1` exist and have the same content in `dir2` pub fn assert_dirs_are_equal(dir1: impl AsRef, dir2: impl AsRef) { let dir2 = dir2.as_ref(); diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 4d0c1b0930ced..4bef4f0500784 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -87,7 +87,7 @@ pub use path_helpers::{ pub use scoped_run::{run_in_tmpdir, test_while_readonly}; pub use assertion_helpers::{ - assert_contains, assert_contains_regex, assert_dirs_are_equal, assert_equals, + assert_contains, assert_contains_regex, assert_count_is, assert_dirs_are_equal, assert_equals, assert_not_contains, assert_not_contains_regex, }; diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 2d25de46f6e4a..3752e933f9960 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -13,7 +13,6 @@ run-make/libtest-json/Makefile run-make/libtest-junit/Makefile run-make/libtest-thread-limit/Makefile run-make/macos-deployment-target/Makefile -run-make/min-global-align/Makefile run-make/native-link-modifier-bundle/Makefile run-make/no-alloc-shim/Makefile run-make/reproducible-build/Makefile diff --git a/tests/run-make/min-global-align/Makefile b/tests/run-make/min-global-align/Makefile deleted file mode 100644 index 82f38749e0092..0000000000000 --- a/tests/run-make/min-global-align/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -include ../tools.mk - -# only-linux - -# This tests ensure that global variables respect the target minimum alignment. -# The three bools `STATIC_BOOL`, `STATIC_MUT_BOOL`, and `CONST_BOOL` all have -# type-alignment of 1, but some targets require greater global alignment. - -SRC = min_global_align.rs -LL = $(TMPDIR)/min_global_align.ll - -all: -# Most targets are happy with default alignment -- take i686 for example. -ifeq ($(filter x86,$(LLVM_COMPONENTS)),x86) - $(RUSTC) --target=i686-unknown-linux-gnu --emit=llvm-ir $(SRC) - [ "$$(grep -c 'align 1' "$(LL)")" -eq "3" ] -endif -# SystemZ requires even alignment for PC-relative addressing. -ifeq ($(filter systemz,$(LLVM_COMPONENTS)),systemz) - $(RUSTC) --target=s390x-unknown-linux-gnu --emit=llvm-ir $(SRC) - [ "$$(grep -c 'align 2' "$(LL)")" -eq "3" ] -endif diff --git a/tests/run-make/min-global-align/rmake.rs b/tests/run-make/min-global-align/rmake.rs new file mode 100644 index 0000000000000..0db82b8340d42 --- /dev/null +++ b/tests/run-make/min-global-align/rmake.rs @@ -0,0 +1,27 @@ +// This tests ensure that global variables respect the target minimum alignment. +// The three bools `STATIC_BOOL`, `STATIC_MUT_BOOL`, and `CONST_BOOL` all have +// type-alignment of 1, but some targets require greater global alignment. +// See https://github.com/rust-lang/rust/pull/44440 + +//@ only-linux +// Reason: this test is target-independent, considering compilation is targeted +// towards linux architectures only. + +use run_make_support::{assert_count_is, llvm_components_contain, rfs, rustc}; + +fn main() { + // Most targets are happy with default alignment -- take i686 for example. + if llvm_components_contain("x86") { + rustc().target("i686-unknown-linux-gnu").emit("llvm-ir").input("min_global_align.rs").run(); + assert_count_is(3, rfs::read_to_string("min_global_align.ll"), "align 1"); + } + // SystemZ requires even alignment for PC-relative addressing. + if llvm_components_contain("systemz") { + rustc() + .target("s390x-unknown-linux-gnu") + .emit("llvm-ir") + .input("min_global_align.rs") + .run(); + assert_count_is(3, rfs::read_to_string("min_global_align.ll"), "align 2"); + } +} From b85bedcb24e31636e47c724e0a1cbedf09d03158 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 30 Jul 2024 14:39:11 -0400 Subject: [PATCH 178/178] rewrite no-alloc-shim to rmake --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/min-global-align/rmake.rs | 4 +- tests/run-make/no-alloc-shim/Makefile | 24 -------- tests/run-make/no-alloc-shim/rmake.rs | 55 +++++++++++++++++++ 4 files changed, 57 insertions(+), 27 deletions(-) delete mode 100644 tests/run-make/no-alloc-shim/Makefile create mode 100644 tests/run-make/no-alloc-shim/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 3752e933f9960..cd232f5140848 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -14,7 +14,6 @@ run-make/libtest-junit/Makefile run-make/libtest-thread-limit/Makefile run-make/macos-deployment-target/Makefile run-make/native-link-modifier-bundle/Makefile -run-make/no-alloc-shim/Makefile run-make/reproducible-build/Makefile run-make/rlib-format-packed-bundled-libs/Makefile run-make/split-debuginfo/Makefile diff --git a/tests/run-make/min-global-align/rmake.rs b/tests/run-make/min-global-align/rmake.rs index 0db82b8340d42..2adaaf172f470 100644 --- a/tests/run-make/min-global-align/rmake.rs +++ b/tests/run-make/min-global-align/rmake.rs @@ -1,10 +1,10 @@ -// This tests ensure that global variables respect the target minimum alignment. +// This test checks that global variables respect the target minimum alignment. // The three bools `STATIC_BOOL`, `STATIC_MUT_BOOL`, and `CONST_BOOL` all have // type-alignment of 1, but some targets require greater global alignment. // See https://github.com/rust-lang/rust/pull/44440 //@ only-linux -// Reason: this test is target-independent, considering compilation is targeted +// Reason: this test is specific to linux, considering compilation is targeted // towards linux architectures only. use run_make_support::{assert_count_is, llvm_components_contain, rfs, rustc}; diff --git a/tests/run-make/no-alloc-shim/Makefile b/tests/run-make/no-alloc-shim/Makefile deleted file mode 100644 index 568e3f9ba1d68..0000000000000 --- a/tests/run-make/no-alloc-shim/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -include ../tools.mk - -# ignore-cross-compile -# ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain - -TARGET_LIBDIR = $$($(RUSTC) --print target-libdir) - -all: - $(RUSTC) foo.rs --crate-type bin --emit obj -Cpanic=abort -ifdef IS_MSVC - $(CC) $(CFLAGS) $(TMPDIR)/foo.o $(call OUT_EXE,foo) /link $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib - $(call OUT_EXE,foo) -else - $(CC) $(CFLAGS) $(TMPDIR)/foo.o $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib -o $(call RUN_BINFILE,foo) - $(call RUN_BINFILE,foo) -endif - - # Check that linking without __rust_no_alloc_shim_is_unstable defined fails - $(RUSTC) foo.rs --crate-type bin --emit obj -Cpanic=abort --cfg check_feature_gate -ifdef IS_MSVC - $(CC) $(CFLAGS) $(TMPDIR)/foo.o $(call OUT_EXE,foo) /link $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib || exit 0 && exit 1 -else - $(CC) $(CFLAGS) $(TMPDIR)/foo.o $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib -o $(call RUN_BINFILE,foo) || exit 0 && exit 1 -endif diff --git a/tests/run-make/no-alloc-shim/rmake.rs b/tests/run-make/no-alloc-shim/rmake.rs new file mode 100644 index 0000000000000..c398a3177df63 --- /dev/null +++ b/tests/run-make/no-alloc-shim/rmake.rs @@ -0,0 +1,55 @@ +// This test checks the compatibility of the interaction between `--emit obj` and +// `#[global_allocator]`, as it is now possible to invoke the latter without the +// allocator shim since #86844. As this feature is unstable, it should fail if +// --cfg check_feature_gate is passed. +// See https://github.com/rust-lang/rust/pull/86844 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +//@ ignore-msvc +//FIXME(Oneirical): Getting this to work on MSVC requires passing libcmt.lib to CC, +// which is not trivial to do. +// Tracking issue: https://github.com/rust-lang/rust/issues/128602 +// Discussion: https://github.com/rust-lang/rust/pull/128407#discussion_r1702439172 + +use run_make_support::{cc, cwd, has_extension, has_prefix, run, rustc, shallow_find_files}; + +fn main() { + rustc().input("foo.rs").crate_type("bin").emit("obj").panic("abort").run(); + let libdir = rustc().print("target-libdir").run().stdout_utf8(); + let libdir = libdir.trim(); + + let alloc_libs = shallow_find_files(&libdir, |path| { + has_prefix(path, "liballoc-") && has_extension(path, "rlib") + }); + let core_libs = shallow_find_files(&libdir, |path| { + has_prefix(path, "libcore-") && has_extension(path, "rlib") + }); + let compiler_builtins_libs = shallow_find_files(libdir, |path| { + has_prefix(path, "libcompiler_builtins") && has_extension(path, "rlib") + }); + + cc().input("foo.o") + .out_exe("foo") + .args(&alloc_libs) + .args(&core_libs) + .args(&compiler_builtins_libs) + .run(); + run("foo"); + + // Check that linking without __rust_no_alloc_shim_is_unstable defined fails + rustc() + .input("foo.rs") + .crate_type("bin") + .emit("obj") + .panic("abort") + .cfg("check_feature_gate") + .run(); + cc().input("foo.o") + .out_exe("foo") + .args(&alloc_libs) + .args(&core_libs) + .args(&compiler_builtins_libs) + .run_fail(); +}