From 2dbaf1c7be0f10b958cca7a280eaf6c5ef6d33b4 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 15 May 2025 17:20:10 +0000 Subject: Create summary comment for lintcheck runs --- .github/workflows/lintcheck.yml | 28 +++++---- .github/workflows/lintcheck_summary.yml | 106 ++++++++++++++++++++++++++++++++ lintcheck/src/config.rs | 3 + lintcheck/src/json.rs | 105 ++++++++++++++++++++----------- lintcheck/src/main.rs | 7 ++- 5 files changed, 200 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/lintcheck_summary.yml diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 70c805903d3..003d0395739 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -128,21 +128,27 @@ jobs: - name: Download JSON uses: actions/download-artifact@v4 + - name: Store PR number + run: echo ${{ github.event.pull_request.number }} > pr.txt + - name: Diff results - # GH's summery has a maximum size of 1024k: - # https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary - # That's why we first log to file and then to the summary and logs + # GH's summery has a maximum size of 1MiB: + # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#step-isolation-and-limits + # We upload the full diff as an artifact in case it's truncated run: | - ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate >> truncated_diff.md - head -c 1024000 truncated_diff.md >> $GITHUB_STEP_SUMMARY - cat truncated_diff.md - ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json >> full_diff.md + ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate | head -c 1M > $GITHUB_STEP_SUMMARY + ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --write-summary summary.json > full_diff.md - name: Upload full diff uses: actions/upload-artifact@v4 with: - name: diff - if-no-files-found: ignore + name: full_diff + path: full_diff.md + + - name: Upload summary + uses: actions/upload-artifact@v4 + with: + name: summary path: | - full_diff.md - truncated_diff.md + summary.json + pr.txt diff --git a/.github/workflows/lintcheck_summary.yml b/.github/workflows/lintcheck_summary.yml new file mode 100644 index 00000000000..52f52e155a0 --- /dev/null +++ b/.github/workflows/lintcheck_summary.yml @@ -0,0 +1,106 @@ +name: Lintcheck summary + +# The workflow_run event runs in the context of the Clippy repo giving it write +# access, needed here to create a PR comment when the PR originates from a fork +# +# The summary artifact is a JSON file that we verify in this action to prevent +# the creation of arbitrary comments +# +# This action must not checkout/run code from the originating workflow_run +# or directly interpolate ${{}} untrusted fields into code +# +# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run +# https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections + +on: + workflow_run: + workflows: [Lintcheck] + types: [completed] + +# Restrict the default permission scope https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defining-access-for-the-github_token-scopes +permissions: + pull-requests: write + +jobs: + download: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: summary + path: untrusted + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ github.token }} + + - name: Format comment + uses: actions/github-script@v7 + with: + script: | + const fs = require("fs"); + const assert = require("assert/strict"); + + function validateName(s) { + assert.match(s, /^[a-z0-9_:]+$/); + return s; + } + + function validateInt(i) { + assert.ok(Number.isInteger(i)); + return i; + } + + function tryReadSummary() { + try { + return JSON.parse(fs.readFileSync("untrusted/summary.json")); + } catch { + return null; + } + } + + const prNumber = parseInt(fs.readFileSync("untrusted/pr.txt"), 10); + core.exportVariable("PR", prNumber.toString()); + + const untrustedSummary = tryReadSummary(); + if (!Array.isArray(untrustedSummary)) { + return; + } + + let summary = `Lintcheck changes for ${context.payload.workflow_run.head_sha} + + | Lint | Added | Removed | Changed | + | ---- | ----: | ------: | ------: | + `; + + for (const untrustedRow of untrustedSummary) { + const name = validateName(untrustedRow.name); + + const added = validateInt(untrustedRow.added); + const removed = validateInt(untrustedRow.removed); + const changed = validateInt(untrustedRow.changed); + + const id = name.replace("clippy::", "user-content-").replaceAll("_", "-"); + const url = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${context.payload.workflow_run.id}#${id}`; + + summary += `| [\`${name}\`](${url}) | ${added} | ${removed} | ${changed} |\n`; + } + + summary += "\nThis comment will be updated if you push new changes"; + + fs.writeFileSync("summary.md", summary); + + - name: Create/update comment + run: | + if [[ -f summary.md ]]; then + gh pr comment "$PR" --body-file summary.md --edit-last --create-if-none + else + # There were no changes detected by Lintcheck + # - If a comment exists from a previous run that did detect changes, edit it (--edit-last) + # - If no comment exists do not create one, the `gh` command exits with an error which + # `|| true` ignores + gh pr comment "$PR" --body "No changes for ${{ github.event.workflow_run.head_sha }}" --edit-last || true + fi + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 83c3d7aba02..3b2ebf0c28a 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -68,6 +68,9 @@ pub(crate) enum Commands { /// This will limit the number of warnings that will be printed for each lint #[clap(long)] truncate: bool, + /// Write the diff summary to a JSON file if there are any changes + #[clap(long, value_name = "PATH")] + write_summary: Option, }, /// Create a lintcheck crates TOML file containing the top N popular crates Popular { diff --git a/lintcheck/src/json.rs b/lintcheck/src/json.rs index 8ea0a41ed36..808997ff022 100644 --- a/lintcheck/src/json.rs +++ b/lintcheck/src/json.rs @@ -4,8 +4,8 @@ //! loading warnings from JSON files, and generating human-readable diffs //! between different linting runs. -use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::{fmt, fs}; use itertools::{EitherOrBoth, Itertools}; use serde::{Deserialize, Serialize}; @@ -17,7 +17,6 @@ const DEFAULT_LIMIT_PER_LINT: usize = 300; /// Target for total warnings to display across all lints when truncating output. const TRUNCATION_TOTAL_TARGET: usize = 1000; -/// Representation of a single Clippy warning for JSON serialization. #[derive(Debug, Deserialize, Serialize)] struct LintJson { /// The lint name e.g. `clippy::bytes_nth` @@ -29,7 +28,6 @@ struct LintJson { } impl LintJson { - /// Returns a tuple of name and `file_line` for sorting and comparison. fn key(&self) -> impl Ord + '_ { (self.name.as_str(), self.file_line.as_str()) } @@ -40,6 +38,57 @@ impl LintJson { } } +#[derive(Debug, Serialize)] +struct SummaryRow { + name: String, + added: usize, + removed: usize, + changed: usize, +} + +#[derive(Debug, Serialize)] +struct Summary(Vec); + +impl fmt::Display for Summary { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str( + "\ +| Lint | Added | Removed | Changed | +| ---- | ----: | ------: | ------: | +", + )?; + + for SummaryRow { + name, + added, + changed, + removed, + } in &self.0 + { + let html_id = to_html_id(name); + writeln!(f, "| [`{name}`](#{html_id}) | {added} | {changed} | {removed} |")?; + } + + Ok(()) + } +} + +impl Summary { + fn new(lints: &[LintWarnings]) -> Self { + Summary( + lints + .iter() + .map(|lint| SummaryRow { + name: lint.name.clone(), + added: lint.added.len(), + removed: lint.removed.len(), + changed: lint.changed.len(), + }) + .collect(), + ) + } +} + /// Creates the log file output for [`crate::config::OutputFormat::Json`] pub(crate) fn output(clippy_warnings: Vec) -> String { let mut lints: Vec = clippy_warnings @@ -74,7 +123,7 @@ fn load_warnings(path: &Path) -> Vec { /// /// Compares warnings from `old_path` and `new_path`, then displays a summary table /// and detailed information about added, removed, and changed warnings. -pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { +pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool, write_summary: Option) { let old_warnings = load_warnings(old_path); let new_warnings = load_warnings(new_path); @@ -108,13 +157,16 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { } } - print_summary_table(&lint_warnings); - println!(); - if lint_warnings.is_empty() { return; } + let summary = Summary::new(&lint_warnings); + if let Some(path) = write_summary { + let json = serde_json::to_string(&summary).unwrap(); + fs::write(path, json).unwrap(); + } + let truncate_after = if truncate { // Max 15 ensures that we at least have five messages per lint DEFAULT_LIMIT_PER_LINT @@ -126,6 +178,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { usize::MAX }; + println!("{summary}"); for lint in lint_warnings { print_lint_warnings(&lint, truncate_after); } @@ -140,13 +193,11 @@ struct LintWarnings { changed: Vec<(LintJson, LintJson)>, } -/// Prints a formatted report for a single lint type with its warnings. fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { let name = &lint.name; let html_id = to_html_id(name); - // The additional anchor is added for non GH viewers that don't prefix ID's - println!(r#"## `{name}` "#); + println!(r#"

{name}

"#); println!(); print!( @@ -162,22 +213,6 @@ fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { print_changed_diff(&lint.changed, truncate_after / 3); } -/// Prints a summary table of all lints with counts of added, removed, and changed warnings. -fn print_summary_table(lints: &[LintWarnings]) { - println!("| Lint | Added | Removed | Changed |"); - println!("| ------------------------------------------ | ------: | ------: | ------: |"); - - for lint in lints { - println!( - "| {:<62} | {:>7} | {:>7} | {:>7} |", - format!("[`{}`](#user-content-{})", lint.name, to_html_id(&lint.name)), - lint.added.len(), - lint.removed.len(), - lint.changed.len() - ); - } -} - /// Prints a section of warnings with a header and formatted code blocks. fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { if warnings.is_empty() { @@ -248,17 +283,16 @@ fn truncate(list: &[T], truncate_after: usize) -> &[T] { } } -/// Prints a level 3 heading with an appropriate HTML ID for linking. fn print_h3(lint: &str, title: &str) { let html_id = to_html_id(lint); - // We have to use HTML here to be able to manually add an id. - println!(r#"### {title} "#); + // We have to use HTML here to be able to manually add an id, GitHub doesn't add them automatically + println!(r#"

{title}

"#); } -/// GitHub's markdown parsers doesn't like IDs with `::` and `_`. This simplifies -/// the lint name for the HTML ID. +/// Creates a custom ID allowed by GitHub, they must start with `user-content-` and cannot contain +/// `::`/`_` fn to_html_id(lint_name: &str) -> String { - lint_name.replace("clippy::", "").replace('_', "-") + lint_name.replace("clippy::", "user-content-").replace('_', "-") } /// This generates the `x added` string for the start of the job summery. @@ -270,9 +304,6 @@ fn count_string(lint: &str, label: &str, count: usize) -> String { format!("0 {label}") } else { let html_id = to_html_id(lint); - // GitHub's job summaries don't add HTML ids to headings. That's why we - // manually have to add them. GitHub prefixes these manual ids with - // `user-content-` and that's how we end up with these awesome links :D - format!("[{count} {label}](#user-content-{html_id}-{label})") + format!("[{count} {label}](#{html_id}-{label})") } } diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index d4bf6cd48a1..8fc2a8a4e79 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -297,7 +297,12 @@ fn main() { let config = LintcheckConfig::new(); match config.subcommand { - Some(Commands::Diff { old, new, truncate }) => json::diff(&old, &new, truncate), + Some(Commands::Diff { + old, + new, + truncate, + write_summary, + }) => json::diff(&old, &new, truncate, write_summary), Some(Commands::Popular { output, number }) => popular_crates::fetch(output, number).unwrap(), None => lintcheck(config), } -- cgit 1.4.1-3-g733a5 From 8bb957eeac0e887649122a779f544e38bacb2a45 Mon Sep 17 00:00:00 2001 From: lolbinarycat Date: Thu, 22 May 2025 19:25:02 -0500 Subject: triagebot: enable issue transfer --- triagebot.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 389f22c6a2c..a3589122340 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -15,6 +15,8 @@ allow-unauthenticated = [ [close] +[transfer] + [issue-links] # Prevents mentions in commits to avoid users being spammed -- cgit 1.4.1-3-g733a5 From 709efc21d247744448c134f337c1815258520911 Mon Sep 17 00:00:00 2001 From: Félix Fischer Date: Wed, 28 May 2025 21:03:58 -0400 Subject: Correct the CC lint's documentation --- clippy_lints/src/cognitive_complexity.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 5c64216dd92..d5d937d9133 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -14,18 +14,25 @@ use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does - /// Checks for methods with high cognitive complexity. + /// We used to think it measured how hard a method is to understand. /// /// ### Why is this bad? - /// Methods of high cognitive complexity tend to be hard to - /// both read and maintain. Also LLVM will tend to optimize small methods better. + /// Ideally, we would like to be able to measure how hard a function is + /// to understand given its context (what we call its Cognitive Complexity). + /// But that's not what this lint does. See "Known problems" /// /// ### Known problems - /// Sometimes it's hard to find a way to reduce the - /// complexity. + /// The true Cognitive Complexity of a method is not something we can + /// calculate using modern technology. This lint has been left in the + /// `nursery` so as to not mislead users into using this lint as a + /// measurement tool. /// - /// ### Example - /// You'll see it when you get the warning. + /// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793) + /// + /// ### Lints to consider instead of this + /// + /// * [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting) + /// * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, nursery, -- cgit 1.4.1-3-g733a5 From 6d1e93d8cdea975303397d91f726c40f5a7396fd Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Sun, 13 Apr 2025 22:17:26 -0700 Subject: Add support for $crate to Ident --- library/proc_macro/src/bridge/symbol.rs | 4 +- tests/ui/proc-macro/auxiliary/mixed-site-span.rs | 110 ++++- tests/ui/proc-macro/auxiliary/token-site-span.rs | 30 ++ tests/ui/proc-macro/mixed-site-span.rs | 164 ++++++- tests/ui/proc-macro/mixed-site-span.stderr | 600 ++++++++++++++++++++++- 5 files changed, 859 insertions(+), 49 deletions(-) create mode 100644 tests/ui/proc-macro/auxiliary/token-site-span.rs diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index 6a1cecd69fb..57ca7db9fcd 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -33,7 +33,7 @@ impl Symbol { /// Validates and normalizes before converting it to a symbol. pub(crate) fn new_ident(string: &str, is_raw: bool) -> Self { // Fast-path: check if this is a valid ASCII identifier - if Self::is_valid_ascii_ident(string.as_bytes()) { + if Self::is_valid_ascii_ident(string.as_bytes()) || string == "$crate" { if is_raw && !Self::can_be_raw(string) { panic!("`{}` cannot be a raw identifier", string); } @@ -79,7 +79,7 @@ impl Symbol { // Mimics the behavior of `Symbol::can_be_raw` from `rustc_span` fn can_be_raw(string: &str) -> bool { match string { - "_" | "super" | "self" | "Self" | "crate" => false, + "_" | "super" | "self" | "Self" | "crate" | "$crate" => false, _ => true, } } diff --git a/tests/ui/proc-macro/auxiliary/mixed-site-span.rs b/tests/ui/proc-macro/auxiliary/mixed-site-span.rs index d837c88c955..18df712debc 100644 --- a/tests/ui/proc-macro/auxiliary/mixed-site-span.rs +++ b/tests/ui/proc-macro/auxiliary/mixed-site-span.rs @@ -3,33 +3,89 @@ extern crate proc_macro; use proc_macro::*; + +#[proc_macro] +pub fn proc_macro_item(input: TokenStream) -> TokenStream { + input +} + +#[proc_macro] +pub fn proc_macro_rules(_input: TokenStream) -> TokenStream { + let id = |s| TokenTree::from(Ident::new(s, Span::mixed_site())); + let item_def = id("ItemDef"); + let local_def = id("local_def"); + let item_use = id("ItemUse"); + let local_use = id("local_use"); + let mut single_quote = Punct::new('\'', Spacing::Joint); + single_quote.set_span(Span::mixed_site()); + let label_use: TokenStream = [ + TokenTree::from(single_quote), + id("label_use"), + ].iter().cloned().collect(); + let dollar_crate = id("$crate"); + quote!( + use $dollar_crate::proc_macro_item as _; // OK + type A = $dollar_crate::ItemUse; // ERROR + + struct $item_def; + let $local_def = 0; + + $item_use; // OK + $local_use; // ERROR + break $label_use; // ERROR + ) +} + +#[proc_macro] +pub fn with_crate(input: TokenStream) -> TokenStream { + let mut input = input.into_iter(); + let TokenTree::Ident(mut krate) = input.next().unwrap() else { panic!("missing $crate") }; + let TokenTree::Ident(span) = input.next().unwrap() else { panic!("missing span") }; + let TokenTree::Ident(ident) = input.next().unwrap() else { panic!("missing ident") }; + + match (krate.to_string().as_str(), span.to_string().as_str()) { + ("$crate", "input") => {}, + (_, "input") => krate = Ident::new("$crate", krate.span()), + + ("$crate", "mixed") => krate.set_span(Span::mixed_site()), + (_, "mixed") => krate = Ident::new("$crate", Span::mixed_site()), + + ("$crate", "call") => krate.set_span(Span::call_site()), + (_, "call") => krate = Ident::new("$crate", Span::call_site()), + + (_, x) => panic!("bad span {}", x), + } + + quote!(use $krate::$ident as _;) +} + #[proc_macro] -pub fn proc_macro_rules(input: TokenStream) -> TokenStream { - if input.is_empty() { - let id = |s| TokenTree::from(Ident::new(s, Span::mixed_site())); - let item_def = id("ItemDef"); - let local_def = id("local_def"); - let item_use = id("ItemUse"); - let local_use = id("local_use"); - let mut single_quote = Punct::new('\'', Spacing::Joint); - single_quote.set_span(Span::mixed_site()); - let label_use: TokenStream = [ - TokenTree::from(single_quote), - id("label_use"), - ].iter().cloned().collect(); - quote!( - struct $item_def; - let $local_def = 0; - - $item_use; // OK - $local_use; // ERROR - break $label_use; // ERROR - ) - } else { - let mut dollar_crate = input.into_iter().next().unwrap(); - dollar_crate.set_span(Span::mixed_site()); - quote!( - type A = $dollar_crate::ItemUse; - ) +pub fn declare_macro(input: TokenStream) -> TokenStream { + let mut input = input.into_iter(); + let TokenTree::Ident(mut krate) = input.next().unwrap() else { panic!("missing $crate") }; + let TokenTree::Ident(span) = input.next().unwrap() else { panic!("missing span") }; + let TokenTree::Ident(ident) = input.next().unwrap() else { panic!("missing ident") }; + + + match (krate.to_string().as_str(), span.to_string().as_str()) { + ("$crate", "input") => {}, + (_, "input") => krate = Ident::new("$crate", krate.span()), + + ("$crate", "mixed") => krate.set_span(Span::mixed_site()), + (_, "mixed") => krate = Ident::new("$crate", Span::mixed_site()), + + ("$crate", "call") => krate.set_span(Span::call_site()), + (_, "call") => krate = Ident::new("$crate", Span::call_site()), + + (_, x) => panic!("bad span {}", x), } + + quote!( + #[macro_export] + macro_rules! $ident { + ($$i:ident) => { + use $krate::$$i as _; + }; + } + ) } diff --git a/tests/ui/proc-macro/auxiliary/token-site-span.rs b/tests/ui/proc-macro/auxiliary/token-site-span.rs new file mode 100644 index 00000000000..39ad8368a50 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/token-site-span.rs @@ -0,0 +1,30 @@ +// Testing token span hygiene. + +//@ proc-macro: mixed-site-span.rs + +extern crate mixed_site_span; + +use mixed_site_span::declare_macro; + +pub struct TokenItem; + +#[macro_export] +macro_rules! invoke_with_crate { + ($s:ident $i:ident) => { with_crate!{$crate $s $i} }; +} + +#[macro_export] +macro_rules! invoke_with_ident { + ($s:ident $i:ident) => { with_crate!{krate $s $i} }; + ($m:ident $s:ident $i:ident) => { with_crate!{$m $s $i} }; +} + +macro_rules! local {() => { + declare_macro!{$crate input use_input_crate} + declare_macro!{$crate mixed use_mixed_crate} + declare_macro!{$crate call use_call_crate} +}} +local!{} +declare_macro!{krate input use_input_krate} +declare_macro!{krate mixed use_mixed_krate} +declare_macro!{krate call use_call_krate} diff --git a/tests/ui/proc-macro/mixed-site-span.rs b/tests/ui/proc-macro/mixed-site-span.rs index 2b5d9757043..442b440c121 100644 --- a/tests/ui/proc-macro/mixed-site-span.rs +++ b/tests/ui/proc-macro/mixed-site-span.rs @@ -1,24 +1,174 @@ // Proc macros using `mixed_site` spans exhibit usual properties of `macro_rules` hygiene. +//@ aux-build: token-site-span.rs //@ proc-macro: mixed-site-span.rs -#[macro_use] extern crate mixed_site_span; +extern crate token_site_span; -struct ItemUse; +use mixed_site_span::{proc_macro_rules, with_crate}; +use token_site_span::{ + invoke_with_crate, invoke_with_ident, + use_input_crate, use_mixed_crate, use_call_crate, + use_input_krate, use_mixed_krate, use_call_krate, +}; + +pub struct ItemUse; fn main() { 'label_use: loop { let local_use = 1; proc_macro_rules!(); - //~^ ERROR use of undeclared label `'label_use` + //~^ ERROR cannot find type `ItemUse` in crate `$crate` + //~| ERROR use of undeclared label `'label_use` //~| ERROR cannot find value `local_use` in this scope ItemDef; // OK local_def; //~ ERROR cannot find value `local_def` in this scope } } -macro_rules! pass_dollar_crate { - () => (proc_macro_rules!($crate);) //~ ERROR cannot find type `ItemUse` in crate `$crate` -} -pass_dollar_crate!(); +// Successful resolutions of `mixed_site_span::proc_macro_item` +const _: () = { + invoke_with_crate!{mixed proc_macro_item} + invoke_with_ident!{mixed proc_macro_item} + invoke_with_ident!{krate mixed proc_macro_item} + with_crate!{krate mixed proc_macro_item} + + macro_rules! test {() => { + invoke_with_ident!{$crate mixed proc_macro_item} + with_crate!{$crate mixed proc_macro_item} + }} + test!(); +}; + +// Failed resolutions of `proc_macro_item` +const _: () = { + // token_site_span::proc_macro_item + invoke_with_crate!{input proc_macro_item} //~ ERROR unresolved import `$crate` + invoke_with_ident!{input proc_macro_item} //~ ERROR unresolved import `$crate` + invoke_with_crate!{call proc_macro_item} //~ ERROR unresolved import `$crate` + invoke_with_ident!{call proc_macro_item} //~ ERROR unresolved import `$crate` + invoke_with_ident!{hello call proc_macro_item} //~ ERROR unresolved import `$crate` + + // crate::proc_macro_item + invoke_with_ident!{krate input proc_macro_item} //~ ERROR unresolved import `$crate::proc_macro_item` + with_crate!{krate input proc_macro_item} //~ ERROR unresolved import `$crate::proc_macro_item` + with_crate!{krate call proc_macro_item} //~ ERROR unresolved import `$crate` + + macro_rules! test {() => { + // crate::proc_macro_item + invoke_with_ident!{$crate input proc_macro_item} //~ ERROR unresolved import `$crate` + with_crate!{$crate input proc_macro_item} //~ ERROR unresolved import `$crate` + with_crate!{$crate call proc_macro_item} //~ ERROR unresolved import `$crate` + + // token_site_span::proc_macro_item + invoke_with_ident!{$crate call proc_macro_item} //~ ERROR unresolved import `$crate` + }} + test!(); +}; + +// Successful resolutions of `token_site_span::TokenItem` +const _: () = { + invoke_with_crate!{input TokenItem} + invoke_with_ident!{input TokenItem} + invoke_with_crate!{call TokenItem} + invoke_with_ident!{call TokenItem} + invoke_with_ident!{hello call TokenItem} + + macro_rules! test {() => { + invoke_with_ident!{$crate call TokenItem} + }} + test!(); +}; + +// Failed resolutions of `TokenItem` +const _: () = { + // crate::TokenItem + invoke_with_ident!{krate input TokenItem} //~ ERROR unresolved import `$crate::TokenItem` + with_crate!{krate input TokenItem} //~ ERROR unresolved import `$crate::TokenItem` + with_crate!{krate call TokenItem} //~ ERROR unresolved import `$crate` + + // mixed_site_span::TokenItem + invoke_with_crate!{mixed TokenItem} //~ ERROR unresolved import `$crate` + invoke_with_ident!{mixed TokenItem} //~ ERROR unresolved import `$crate` + invoke_with_ident!{krate mixed TokenItem} //~ ERROR unresolved import `$crate` + with_crate!{krate mixed TokenItem} //~ ERROR unresolved import `$crate` + + macro_rules! test {() => { + // crate::TokenItem + invoke_with_ident!{$crate input TokenItem} //~ ERROR unresolved import `$crate` + with_crate!{$crate input TokenItem} //~ ERROR unresolved import `$crate` + with_crate!{$crate call TokenItem} //~ ERROR unresolved import `$crate` + + // mixed_site_span::TokenItem + invoke_with_ident!{$crate mixed TokenItem} //~ ERROR unresolved import `$crate` + with_crate!{$crate mixed TokenItem} //~ ERROR unresolved import `$crate` + + }} + test!(); +}; + + +// Successful resolutions of `crate::ItemUse` +const _: () = { + invoke_with_ident!{krate input ItemUse} + with_crate!{krate input ItemUse} + with_crate!{krate call ItemUse} + + macro_rules! test {() => { + invoke_with_ident!{$crate input ItemUse} + with_crate!{$crate input ItemUse} + with_crate!{$crate call ItemUse} + }} + test!(); +}; + +// Failed resolutions of `ItemUse` +const _: () = { + // token_site_span::ItemUse + invoke_with_crate!{input ItemUse} //~ ERROR unresolved import `$crate` + invoke_with_ident!{input ItemUse} //~ ERROR unresolved import `$crate` + + // mixed_site_span::ItemUse + invoke_with_crate!{mixed ItemUse} //~ ERROR unresolved import `$crate` + invoke_with_ident!{mixed ItemUse} //~ ERROR unresolved import `$crate` + invoke_with_ident!{krate mixed ItemUse} //~ ERROR unresolved import `$crate` + with_crate!{krate mixed ItemUse} //~ ERROR unresolved import `$crate` + + invoke_with_crate!{call ItemUse} //~ ERROR unresolved import `$crate` + invoke_with_ident!{call ItemUse} //~ ERROR unresolved import `$crate` + invoke_with_ident!{hello call ItemUse} //~ ERROR unresolved import `$crate` + + macro_rules! test {() => { + invoke_with_ident!{$crate mixed ItemUse} //~ ERROR unresolved import `$crate` + with_crate!{$crate mixed ItemUse} //~ ERROR unresolved import `$crate` + + invoke_with_ident!{$crate call ItemUse} //~ ERROR unresolved import `$crate` + }} + test!(); +}; + + +// Only mixed should see mixed_site_span::proc_macro_item +use_input_crate!{proc_macro_item} //~ ERROR unresolved import `$crate` +use_input_krate!{proc_macro_item} //~ ERROR unresolved import `$crate` +use_mixed_crate!{proc_macro_item} +use_mixed_krate!{proc_macro_item} +use_call_crate!{proc_macro_item} //~ ERROR unresolved import `$crate` +use_call_krate!{proc_macro_item} //~ ERROR unresolved import `$crate` + +// Only mixed should fail to see token_site_span::TokenItem +use_input_crate!{TokenItem} +use_input_krate!{TokenItem} +use_mixed_crate!{TokenItem} //~ ERROR unresolved import `$crate` +use_mixed_krate!{TokenItem} //~ ERROR unresolved import `$crate` +use_call_crate!{TokenItem} +use_call_krate!{TokenItem} + +// Everything should fail to see crate::ItemUse +use_input_crate!{ItemUse} //~ ERROR unresolved import `$crate` +use_input_krate!{ItemUse} //~ ERROR unresolved import `$crate` +use_mixed_crate!{ItemUse} //~ ERROR unresolved import `$crate` +use_mixed_krate!{ItemUse} //~ ERROR unresolved import `$crate` +use_call_crate!{ItemUse} //~ ERROR unresolved import `$crate` +use_call_krate!{ItemUse} //~ ERROR unresolved import `$crate` diff --git a/tests/ui/proc-macro/mixed-site-span.stderr b/tests/ui/proc-macro/mixed-site-span.stderr index 13786080124..d62031a853c 100644 --- a/tests/ui/proc-macro/mixed-site-span.stderr +++ b/tests/ui/proc-macro/mixed-site-span.stderr @@ -1,13 +1,595 @@ +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:47:5 + | +LL | invoke_with_crate!{input proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `invoke_with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:48:5 + | +LL | invoke_with_ident!{input proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:49:5 + | +LL | invoke_with_crate!{call proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:50:5 + | +LL | invoke_with_ident!{call proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:51:5 + | +LL | invoke_with_ident!{hello call proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate::proc_macro_item` + --> $DIR/mixed-site-span.rs:54:5 + | +LL | invoke_with_ident!{krate input proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^ + | | | + | | help: a similar name exists in the module: `proc_macro_rules` + | no `proc_macro_item` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate::proc_macro_item` + --> $DIR/mixed-site-span.rs:55:5 + | +LL | with_crate!{krate input proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^---------------^ + | | | + | | help: a similar name exists in the module: `proc_macro_rules` + | no `proc_macro_item` in the root + | + = note: this error originates in the macro `with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:56:5 + | +LL | with_crate!{krate call proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ + | | | + | | help: a similar name exists in the module: `proc_macro_rules` + | no `proc_macro_item` in the root + | + = note: this error originates in the macro `with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:60:28 + | +LL | invoke_with_ident!{$crate input proc_macro_item} + | ^^^^^^ --------------- help: a similar name exists in the module: `proc_macro_rules` + | | + | no `proc_macro_item` in the root +... +LL | test!(); + | ------- in this macro invocation + | + = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:61:21 + | +LL | with_crate!{$crate input proc_macro_item} + | ^^^^^^ --------------- help: a similar name exists in the module: `proc_macro_rules` + | | + | no `proc_macro_item` in the root +... +LL | test!(); + | ------- in this macro invocation + | + = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:62:9 + | +LL | with_crate!{$crate call proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^---------------^ + | | | + | | help: a similar name exists in the module: `proc_macro_rules` + | no `proc_macro_item` in the root +... +LL | test!(); + | ------- in this macro invocation + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:67:5 + | +LL | test!(); + | ^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate::TokenItem` + --> $DIR/mixed-site-span.rs:87:5 + | +LL | invoke_with_ident!{krate input TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/mixed-site-span.rs:59:34 + | +LL | quote!(use $krate::$ident as token_site_span::TokenItem as _;) + | +++++++++++++++++++++++++++++ + +error[E0432]: unresolved import `$crate::TokenItem` + --> $DIR/mixed-site-span.rs:88:5 + | +LL | with_crate!{krate input TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/mixed-site-span.rs:59:34 + | +LL | quote!(use $krate::$ident as token_site_span::TokenItem as _;) + | +++++++++++++++++++++++++++++ + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:89:5 + | +LL | with_crate!{krate call TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + | +LL - with_crate!{krate call TokenItem} +LL + token_site_span::TokenItem as _ + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:92:5 + | +LL | invoke_with_crate!{mixed TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:13:30 + | +LL - ($s:ident $i:ident) => { with_crate!{$crate $s $i} }; +LL + ($s:ident $i:ident) => { token_site_span::TokenItem as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:93:5 + | +LL | invoke_with_ident!{mixed TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:18:30 + | +LL - ($s:ident $i:ident) => { with_crate!{krate $s $i} }; +LL + ($s:ident $i:ident) => { token_site_span::TokenItem as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:94:5 + | +LL | invoke_with_ident!{krate mixed TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:19:39 + | +LL - ($m:ident $s:ident $i:ident) => { with_crate!{$m $s $i} }; +LL + ($m:ident $s:ident $i:ident) => { token_site_span::TokenItem as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:95:5 + | +LL | with_crate!{krate mixed TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + | +LL - with_crate!{krate mixed TokenItem} +LL + token_site_span::TokenItem as _ + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:99:28 + | +LL | invoke_with_ident!{$crate input TokenItem} + | ^^^^^^ no `TokenItem` in the root +... +LL | test!(); + | ------- in this macro invocation + | + = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + | +LL - invoke_with_ident!{$crate input TokenItem} +LL + invoke_with_ident!{token_site_span::TokenItem as _ input TokenItem} + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:100:21 + | +LL | with_crate!{$crate input TokenItem} + | ^^^^^^ no `TokenItem` in the root +... +LL | test!(); + | ------- in this macro invocation + | + = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + | +LL - with_crate!{$crate input TokenItem} +LL + with_crate!{token_site_span::TokenItem as _ input TokenItem} + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:101:9 + | +LL | with_crate!{$crate call TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root +... +LL | test!(); + | ------- in this macro invocation + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + | +LL - with_crate!{$crate call TokenItem} +LL + token_site_span::TokenItem as _ + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:108:5 + | +LL | test!(); + | ^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:19:39 + | +LL - ($m:ident $s:ident $i:ident) => { with_crate!{$m $s $i} }; +LL + ($m:ident $s:ident $i:ident) => { token_site_span::TokenItem as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:105:9 + | +LL | with_crate!{$crate mixed TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root +... +LL | test!(); + | ------- in this macro invocation + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + | +LL - with_crate!{$crate mixed TokenItem} +LL + token_site_span::TokenItem as _ + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:129:5 + | +LL | invoke_with_crate!{input ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `invoke_with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:13:42 + | +LL - ($s:ident $i:ident) => { with_crate!{$crate $s $i} }; +LL + ($s:ident $i:ident) => { with_crate!{ItemUse as _ $s $i} }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:130:5 + | +LL | invoke_with_ident!{input ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:18:42 + | +LL - ($s:ident $i:ident) => { with_crate!{krate $s $i} }; +LL + ($s:ident $i:ident) => { with_crate!{ItemUse as _ $s $i} }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:133:5 + | +LL | invoke_with_crate!{mixed ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:13:30 + | +LL - ($s:ident $i:ident) => { with_crate!{$crate $s $i} }; +LL + ($s:ident $i:ident) => { ItemUse as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:134:5 + | +LL | invoke_with_ident!{mixed ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:18:30 + | +LL - ($s:ident $i:ident) => { with_crate!{krate $s $i} }; +LL + ($s:ident $i:ident) => { ItemUse as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:135:5 + | +LL | invoke_with_ident!{krate mixed ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:19:39 + | +LL - ($m:ident $s:ident $i:ident) => { with_crate!{$m $s $i} }; +LL + ($m:ident $s:ident $i:ident) => { ItemUse as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:136:5 + | +LL | with_crate!{krate mixed ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + | +LL - with_crate!{krate mixed ItemUse} +LL + ItemUse as _ + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:138:5 + | +LL | invoke_with_crate!{call ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_crate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:13:30 + | +LL - ($s:ident $i:ident) => { with_crate!{$crate $s $i} }; +LL + ($s:ident $i:ident) => { ItemUse as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:139:5 + | +LL | invoke_with_ident!{call ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:18:30 + | +LL - ($s:ident $i:ident) => { with_crate!{krate $s $i} }; +LL + ($s:ident $i:ident) => { ItemUse as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:140:5 + | +LL | invoke_with_ident!{hello call ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `invoke_with_ident` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:19:39 + | +LL - ($m:ident $s:ident $i:ident) => { with_crate!{$m $s $i} }; +LL + ($m:ident $s:ident $i:ident) => { ItemUse as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:148:5 + | +LL | test!(); + | ^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:19:39 + | +LL - ($m:ident $s:ident $i:ident) => { with_crate!{$m $s $i} }; +LL + ($m:ident $s:ident $i:ident) => { ItemUse as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:144:9 + | +LL | with_crate!{$crate mixed ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root +... +LL | test!(); + | ------- in this macro invocation + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + | +LL - with_crate!{$crate mixed ItemUse} +LL + ItemUse as _ + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:148:5 + | +LL | test!(); + | ^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `with_crate` which comes from the expansion of the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:19:39 + | +LL - ($m:ident $s:ident $i:ident) => { with_crate!{$m $s $i} }; +LL + ($m:ident $s:ident $i:ident) => { ItemUse as _ }; + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:153:1 + | +LL | use_input_crate!{proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `use_input_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:154:1 + | +LL | use_input_krate!{proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `use_input_krate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:157:1 + | +LL | use_call_crate!{proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `use_call_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:158:1 + | +LL | use_call_krate!{proc_macro_item} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `proc_macro_item` in the root + | + = note: this error originates in the macro `use_call_krate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:163:1 + | +LL | use_mixed_crate!{TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `use_mixed_crate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:24:5 + | +LL - declare_macro!{$crate mixed use_mixed_crate} +LL + token_site_span::TokenItem as _ + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:164:1 + | +LL | use_mixed_krate!{TokenItem} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `TokenItem` in the root + | + = note: this error originates in the macro `use_mixed_krate` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct instead + --> $DIR/auxiliary/token-site-span.rs:29:1 + | +LL - declare_macro!{krate mixed use_mixed_krate} +LL + token_site_span::TokenItem as _ + | + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:169:1 + | +LL | use_input_crate!{ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `use_input_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:170:1 + | +LL | use_input_krate!{ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `use_input_krate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:171:1 + | +LL | use_mixed_crate!{ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `use_mixed_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:172:1 + | +LL | use_mixed_krate!{ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `use_mixed_krate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:173:1 + | +LL | use_call_crate!{ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `use_call_crate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0432]: unresolved import `$crate` + --> $DIR/mixed-site-span.rs:174:1 + | +LL | use_call_krate!{ItemUse} + | ^^^^^^^^^^^^^^^^^^^^^^^^ no `ItemUse` in the root + | + = note: this error originates in the macro `use_call_krate` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0426]: use of undeclared label `'label_use` - --> $DIR/mixed-site-span.rs:13:9 + --> $DIR/mixed-site-span.rs:21:9 | LL | proc_macro_rules!(); | ^^^^^^^^^^^^^^^^^^^ undeclared label `'label_use` | = note: this error originates in the macro `proc_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) +error[E0412]: cannot find type `ItemUse` in crate `$crate` + --> $DIR/mixed-site-span.rs:21:9 + | +LL | proc_macro_rules!(); + | ^^^^^^^^^^^^^^^^^^^ not found in `$crate` + | + = note: this error originates in the macro `proc_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct + | +LL + use ItemUse; + | + error[E0425]: cannot find value `local_use` in this scope - --> $DIR/mixed-site-span.rs:13:9 + --> $DIR/mixed-site-span.rs:21:9 | LL | proc_macro_rules!(); | ^^^^^^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `local_def` @@ -15,20 +597,12 @@ LL | proc_macro_rules!(); = note: this error originates in the macro `proc_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `local_def` in this scope - --> $DIR/mixed-site-span.rs:17:9 + --> $DIR/mixed-site-span.rs:26:9 | LL | local_def; | ^^^^^^^^^ help: a local variable with a similar name exists: `local_use` -error[E0412]: cannot find type `ItemUse` in crate `$crate` - --> $DIR/mixed-site-span.rs:24:1 - | -LL | pass_dollar_crate!(); - | ^^^^^^^^^^^^^^^^^^^^ not found in `$crate` - | - = note: this error originates in the macro `proc_macro_rules` which comes from the expansion of the macro `pass_dollar_crate` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 4 previous errors +error: aborting due to 52 previous errors -Some errors have detailed explanations: E0412, E0425, E0426. +Some errors have detailed explanations: E0412, E0425, E0426, E0432. For more information about an error, try `rustc --explain E0412`. -- cgit 1.4.1-3-g733a5 From 3dc30176918fd9085f29006e19d5e250e9ab67d1 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 14 Jun 2025 12:08:25 +0200 Subject: use `is_multiple_of` to check if an addr is aligned --- library/compiler-builtins/compiler-builtins/src/arm.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/compiler-builtins/compiler-builtins/src/arm.rs b/library/compiler-builtins/compiler-builtins/src/arm.rs index a7d84e49b34..617cc8e507b 100644 --- a/library/compiler-builtins/compiler-builtins/src/arm.rs +++ b/library/compiler-builtins/compiler-builtins/src/arm.rs @@ -135,8 +135,8 @@ intrinsics! { /// eight bytes. #[cfg(not(target_vendor = "apple"))] pub unsafe extern "aapcs" fn __aeabi_memcpy8(dst: *mut u8, src: *const u8, n: usize) { - debug_assert!(dst.addr() & 7 == 0); - debug_assert!(src.addr() & 7 == 0); + debug_assert!(dst.addr().is_multiple_of(8)); + debug_assert!(src.addr().is_multiple_of(8)); // SAFETY: memcpy preconditions apply, less strict alignment. unsafe { __aeabi_memcpy4(dst, src, n) }; @@ -161,8 +161,8 @@ intrinsics! { /// four bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] pub unsafe extern "aapcs" fn __aeabi_memmove4(dst: *mut u8, src: *const u8, n: usize) { - debug_assert!(dst.addr() & 3 == 0); - debug_assert!(src.addr() & 3 == 0); + debug_assert!(dst.addr().is_multiple_of(4)); + debug_assert!(src.addr().is_multiple_of(4)); // SAFETY: same preconditions, less strict aligment. unsafe { __aeabi_memmove(dst, src, n) }; @@ -176,8 +176,8 @@ intrinsics! { /// eight bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] pub unsafe extern "aapcs" fn __aeabi_memmove8(dst: *mut u8, src: *const u8, n: usize) { - debug_assert!(dst.addr() & 7 == 0); - debug_assert!(src.addr() & 7 == 0); + debug_assert!(dst.addr().is_multiple_of(8)); + debug_assert!(src.addr().is_multiple_of(8)); // SAFETY: memmove preconditions apply, less strict alignment. unsafe { __aeabi_memmove(dst, src, n) }; @@ -236,7 +236,7 @@ intrinsics! { /// eight bytes. #[cfg(not(target_vendor = "apple"))] pub unsafe extern "aapcs" fn __aeabi_memset8(dst: *mut u8, n: usize, c: i32) { - debug_assert!(dst.addr() & 7 == 0); + debug_assert!(dst.addr().is_multiple_of(8)); // SAFETY: memset preconditions apply, less strict alignment. unsafe { __aeabi_memset4(dst, n, c) }; @@ -261,7 +261,7 @@ intrinsics! { /// four bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] pub unsafe extern "aapcs" fn __aeabi_memclr4(dst: *mut u8, n: usize) { - debug_assert!(dst.addr() & 3 == 0); + debug_assert!(dst.addr().is_multiple_of(4)); // SAFETY: memclr preconditions apply, less strict alignment. unsafe { __aeabi_memset4(dst, n, 0) }; @@ -275,7 +275,7 @@ intrinsics! { /// eight bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] pub unsafe extern "aapcs" fn __aeabi_memclr8(dst: *mut u8, n: usize) { - debug_assert!(dst.addr() & 7 == 0); + debug_assert!(dst.addr().is_multiple_of(8)); // SAFETY: memclr preconditions apply, less strict alignment. unsafe { __aeabi_memset4(dst, n, 0) }; -- cgit 1.4.1-3-g733a5 From a413cca7d39d45837b9c595a135c369779275963 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 14 Jun 2025 20:17:23 +0200 Subject: use `extern "custom"` on naked functions with a custom calling convention --- .../compiler-builtins/compiler-builtins/src/aarch64.rs | 2 +- library/compiler-builtins/compiler-builtins/src/arm.rs | 13 ++++++------- .../compiler-builtins/compiler-builtins/src/int/udiv.rs | 2 +- library/compiler-builtins/compiler-builtins/src/lib.rs | 1 + .../compiler-builtins/compiler-builtins/src/probestack.rs | 15 +++------------ library/compiler-builtins/compiler-builtins/src/x86.rs | 10 +++++----- library/compiler-builtins/compiler-builtins/src/x86_64.rs | 4 ++-- 7 files changed, 19 insertions(+), 28 deletions(-) diff --git a/library/compiler-builtins/compiler-builtins/src/aarch64.rs b/library/compiler-builtins/compiler-builtins/src/aarch64.rs index 80392187c89..a72b30d29f0 100644 --- a/library/compiler-builtins/compiler-builtins/src/aarch64.rs +++ b/library/compiler-builtins/compiler-builtins/src/aarch64.rs @@ -5,7 +5,7 @@ use core::intrinsics; intrinsics! { #[unsafe(naked)] #[cfg(all(target_os = "uefi", not(feature = "no-asm")))] - pub unsafe extern "C" fn __chkstk() { + pub unsafe extern "custom" fn __chkstk() { core::arch::naked_asm!( ".p2align 2", "lsl x16, x15, #4", diff --git a/library/compiler-builtins/compiler-builtins/src/arm.rs b/library/compiler-builtins/compiler-builtins/src/arm.rs index 617cc8e507b..fbec93ca431 100644 --- a/library/compiler-builtins/compiler-builtins/src/arm.rs +++ b/library/compiler-builtins/compiler-builtins/src/arm.rs @@ -9,11 +9,10 @@ unsafe extern "C" { } // SAFETY: these are defined in compiler-builtins -// FIXME(extern_custom), this isn't always the correct ABI -unsafe extern "aapcs" { +unsafe extern "custom" { // AAPCS is not always the correct ABI for these intrinsics, but we only use this to // forward another `__aeabi_` call so it doesn't matter. - fn __aeabi_idiv(a: i32, b: i32) -> i32; + fn __aeabi_idiv(); } intrinsics! { @@ -21,7 +20,7 @@ intrinsics! { // custom calling convention which can't be implemented using a normal Rust function. #[unsafe(naked)] #[cfg(not(target_env = "msvc"))] - pub unsafe extern "C" fn __aeabi_uidivmod() { + pub unsafe extern "custom" fn __aeabi_uidivmod() { core::arch::naked_asm!( "push {{lr}}", "sub sp, sp, #4", @@ -35,7 +34,7 @@ intrinsics! { } #[unsafe(naked)] - pub unsafe extern "C" fn __aeabi_uldivmod() { + pub unsafe extern "custom" fn __aeabi_uldivmod() { core::arch::naked_asm!( "push {{r4, lr}}", "sub sp, sp, #16", @@ -51,7 +50,7 @@ intrinsics! { } #[unsafe(naked)] - pub unsafe extern "C" fn __aeabi_idivmod() { + pub unsafe extern "custom" fn __aeabi_idivmod() { core::arch::naked_asm!( "push {{r0, r1, r4, lr}}", "bl {trampoline}", @@ -64,7 +63,7 @@ intrinsics! { } #[unsafe(naked)] - pub unsafe extern "C" fn __aeabi_ldivmod() { + pub unsafe extern "custom" fn __aeabi_ldivmod() { core::arch::naked_asm!( "push {{r4, lr}}", "sub sp, sp, #16", diff --git a/library/compiler-builtins/compiler-builtins/src/int/udiv.rs b/library/compiler-builtins/compiler-builtins/src/int/udiv.rs index b9dee63c4cc..017a81ac914 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/udiv.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/udiv.rs @@ -44,7 +44,7 @@ intrinsics! { } #[unsafe(naked)] - pub unsafe extern "C" fn __udivmodqi4() { + pub unsafe extern "custom" fn __udivmodqi4() { // compute unsigned 8-bit `n / d` and `n % d`. // // Note: GCC implements a [non-standard calling convention](https://gcc.gnu.org/wiki/avr-gcc#Exceptions_to_the_Calling_Convention) for this function. diff --git a/library/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs index 1cec39d8b41..dd9920cae01 100644 --- a/library/compiler-builtins/compiler-builtins/src/lib.rs +++ b/library/compiler-builtins/compiler-builtins/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(feature = "compiler-builtins", compiler_builtins)] #![cfg_attr(all(target_family = "wasm"), feature(wasm_numeric_instr))] +#![feature(abi_custom)] #![feature(abi_unadjusted)] #![feature(asm_experimental_arch)] #![feature(cfg_target_has_atomic)] diff --git a/library/compiler-builtins/compiler-builtins/src/probestack.rs b/library/compiler-builtins/compiler-builtins/src/probestack.rs index 1441fd73b8d..1d0010842cb 100644 --- a/library/compiler-builtins/compiler-builtins/src/probestack.rs +++ b/library/compiler-builtins/compiler-builtins/src/probestack.rs @@ -52,15 +52,12 @@ // Our goal here is to touch each page between %rsp+8 and %rsp+8-%rax, // ensuring that if any pages are unmapped we'll make a page fault. // -// FIXME(abi_custom): This function is unsafe because it uses a custom ABI, -// it does not actually match `extern "C"`. -// // The ABI here is that the stack frame size is located in `%rax`. Upon // return we're not supposed to modify `%rsp` or `%rax`. #[cfg(target_arch = "x86_64")] #[unsafe(naked)] #[rustc_std_internal_symbol] -pub unsafe extern "C" fn __rust_probestack() { +pub unsafe extern "custom" fn __rust_probestack() { #[cfg(not(all(target_env = "sgx", target_vendor = "fortanix")))] macro_rules! ret { () => { @@ -144,13 +141,10 @@ pub unsafe extern "C" fn __rust_probestack() { // that on Unix we're expected to restore everything as it was, this // function basically can't tamper with anything. // -// FIXME(abi_custom): This function is unsafe because it uses a custom ABI, -// it does not actually match `extern "C"`. -// // The ABI here is the same as x86_64, except everything is 32-bits large. #[unsafe(naked)] #[rustc_std_internal_symbol] -pub unsafe extern "C" fn __rust_probestack() { +pub unsafe extern "custom" fn __rust_probestack() { core::arch::naked_asm!( " .cfi_startproc @@ -192,9 +186,6 @@ pub unsafe extern "C" fn __rust_probestack() { // probestack function will also do things like _chkstk in MSVC. // So we need to sub %ax %sp in probestack when arch is x86. // -// FIXME(abi_custom): This function is unsafe because it uses a custom ABI, -// it does not actually match `extern "C"`. -// // REF: Rust commit(74e80468347) // rust\src\llvm-project\llvm\lib\Target\X86\X86FrameLowering.cpp: 805 // Comments in LLVM: @@ -203,7 +194,7 @@ pub unsafe extern "C" fn __rust_probestack() { // themselves. #[unsafe(naked)] #[rustc_std_internal_symbol] -pub unsafe extern "C" fn __rust_probestack() { +pub unsafe extern "custom" fn __rust_probestack() { core::arch::naked_asm!( " .cfi_startproc diff --git a/library/compiler-builtins/compiler-builtins/src/x86.rs b/library/compiler-builtins/compiler-builtins/src/x86.rs index 01152d9c798..16e50922a94 100644 --- a/library/compiler-builtins/compiler-builtins/src/x86.rs +++ b/library/compiler-builtins/compiler-builtins/src/x86.rs @@ -2,7 +2,7 @@ use core::intrinsics; -// NOTE These functions are implemented using assembly because they using a custom +// NOTE These functions are implemented using assembly because they use a custom // calling convention which can't be implemented using a normal Rust function // NOTE These functions are never mangled as they are not tested against compiler-rt @@ -13,10 +13,10 @@ intrinsics! { any(all(windows, target_env = "gnu"), target_os = "uefi"), not(feature = "no-asm") ))] - pub unsafe extern "C" fn __chkstk() { + pub unsafe extern "custom" fn __chkstk() { core::arch::naked_asm!( - "jmp __alloca", // Jump to __alloca since fallthrough may be unreliable" - options(att_syntax) + "jmp {}", // Jump to __alloca since fallthrough may be unreliable" + sym crate::x86::_alloca::_alloca, ); } @@ -25,7 +25,7 @@ intrinsics! { any(all(windows, target_env = "gnu"), target_os = "uefi"), not(feature = "no-asm") ))] - pub unsafe extern "C" fn _alloca() { + pub unsafe extern "custom" fn _alloca() { // __chkstk and _alloca are the same function core::arch::naked_asm!( "push %ecx", diff --git a/library/compiler-builtins/compiler-builtins/src/x86_64.rs b/library/compiler-builtins/compiler-builtins/src/x86_64.rs index fc1190f79b2..9b7133b482e 100644 --- a/library/compiler-builtins/compiler-builtins/src/x86_64.rs +++ b/library/compiler-builtins/compiler-builtins/src/x86_64.rs @@ -2,7 +2,7 @@ use core::intrinsics; -// NOTE These functions are implemented using assembly because they using a custom +// NOTE These functions are implemented using assembly because they use a custom // calling convention which can't be implemented using a normal Rust function // NOTE These functions are never mangled as they are not tested against compiler-rt @@ -17,7 +17,7 @@ intrinsics! { ), not(feature = "no-asm") ))] - pub unsafe extern "C" fn ___chkstk_ms() { + pub unsafe extern "custom" fn ___chkstk_ms() { core::arch::naked_asm!( "push %rcx", "push %rax", -- cgit 1.4.1-3-g733a5 From a2b234554d7c175ec578c6003f7a966b05258752 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sat, 14 Jun 2025 12:26:45 -0700 Subject: Fix coerce_container_to_any false positive on autoderef --- clippy_lints/src/coerce_container_to_any.rs | 27 +++++++++++++++------------ tests/ui/coerce_container_to_any.fixed | 4 ++++ tests/ui/coerce_container_to_any.rs | 4 ++++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs index 8c12a42ba4e..46759021995 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs @@ -4,6 +4,7 @@ use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::{self, ExistentialPredicate, Ty, TyCtxt}; use rustc_session::declare_lint_pass; @@ -49,23 +50,18 @@ declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]); impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - // If this expression has an effective type of `&dyn Any` ... - { - let coerced_ty = cx.typeck_results().expr_ty_adjusted(e); - - let ty::Ref(_, coerced_ref_ty, _) = *coerced_ty.kind() else { - return; - }; - if !is_dyn_any(cx.tcx, coerced_ref_ty) { - return; - } + // If this expression was coerced to `&dyn Any` ... + if !cx.typeck_results().expr_adjustments(e).last().is_some_and(|adj| { + matches!(adj.kind, Adjust::Pointer(PointerCoercion::Unsize)) && is_ref_dyn_any(cx.tcx, adj.target) + }) { + return; } let expr_ty = cx.typeck_results().expr_ty(e); let ty::Ref(_, expr_ref_ty, _) = *expr_ty.kind() else { return; }; - // ... but only due to coercion ... + // ... but it's not actually `&dyn Any` ... if is_dyn_any(cx.tcx, expr_ref_ty) { return; } @@ -89,12 +85,19 @@ impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { e.span, format!("coercing `{expr_ty}` to `&dyn Any`"), "consider dereferencing", - format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "x")), + format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "..")), Applicability::MaybeIncorrect, ); } } +fn is_ref_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { + let ty::Ref(_, ref_ty, _) = *ty.kind() else { + return false; + }; + is_dyn_any(tcx, ref_ty) +} + fn is_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { let ty::Dynamic(traits, ..) = ty.kind() else { return false; diff --git a/tests/ui/coerce_container_to_any.fixed b/tests/ui/coerce_container_to_any.fixed index ae9d3ef9656..eb8487bac17 100644 --- a/tests/ui/coerce_container_to_any.fixed +++ b/tests/ui/coerce_container_to_any.fixed @@ -21,6 +21,10 @@ fn main() { f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {} diff --git a/tests/ui/coerce_container_to_any.rs b/tests/ui/coerce_container_to_any.rs index 9948bd48e0d..7edb65fa0f7 100644 --- a/tests/ui/coerce_container_to_any.rs +++ b/tests/ui/coerce_container_to_any.rs @@ -21,6 +21,10 @@ fn main() { f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {} -- cgit 1.4.1-3-g733a5 From 8e285d6f1ae89a0edc4e93509b846661c012ca33 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sat, 14 Jun 2025 13:53:21 -0700 Subject: Use precedence-aware formatting for coerce_container_to_any suggestions --- clippy_lints/src/coerce_container_to_any.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs index 46759021995..fd3399decc1 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; +use clippy_utils::sugg::{self, Sugg}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -74,18 +74,22 @@ impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { } // ... that's probably not intended. - let (span, deref_count) = match e.kind { + let (target_expr, deref_count) = match e.kind { // If `e` was already an `&` expression, skip `*&` in the suggestion - ExprKind::AddrOf(_, _, referent) => (referent.span, depth), - _ => (e.span, depth + 1), + ExprKind::AddrOf(_, _, referent) => (referent, depth), + _ => (e, depth + 1), }; + let sugg = sugg::make_unop( + &format!("&{}", str::repeat("*", deref_count)), + Sugg::hir(cx, target_expr, ".."), + ); span_lint_and_sugg( cx, COERCE_CONTAINER_TO_ANY, e.span, format!("coercing `{expr_ty}` to `&dyn Any`"), "consider dereferencing", - format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "..")), + sugg.to_string(), Applicability::MaybeIncorrect, ); } -- cgit 1.4.1-3-g733a5 From dc1b9ce754202493d05a8d7e1627f1d6bc272528 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sat, 14 Jun 2025 14:02:53 -0700 Subject: Fix reference mutability in coerce_container_to_any diagnostics --- clippy_lints/src/coerce_container_to_any.rs | 7 +++++-- tests/ui/coerce_container_to_any.fixed | 9 ++++++++- tests/ui/coerce_container_to_any.rs | 9 ++++++++- tests/ui/coerce_container_to_any.stderr | 14 +++++++++++++- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs index fd3399decc1..3311a35ef79 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs @@ -79,15 +79,18 @@ impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { ExprKind::AddrOf(_, _, referent) => (referent, depth), _ => (e, depth + 1), }; + let ty::Ref(_, _, mutability) = *cx.typeck_results().expr_ty_adjusted(e).kind() else { + return; + }; let sugg = sugg::make_unop( - &format!("&{}", str::repeat("*", deref_count)), + &format!("{}{}", mutability.ref_prefix_str(), str::repeat("*", deref_count)), Sugg::hir(cx, target_expr, ".."), ); span_lint_and_sugg( cx, COERCE_CONTAINER_TO_ANY, e.span, - format!("coercing `{expr_ty}` to `&dyn Any`"), + format!("coercing `{expr_ty}` to `{}dyn Any`", mutability.ref_prefix_str()), "consider dereferencing", sugg.to_string(), Applicability::MaybeIncorrect, diff --git a/tests/ui/coerce_container_to_any.fixed b/tests/ui/coerce_container_to_any.fixed index eb8487bac17..b5b3f15b4de 100644 --- a/tests/ui/coerce_container_to_any.fixed +++ b/tests/ui/coerce_container_to_any.fixed @@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box = Box::new(()); + let mut x: Box = Box::new(()); let ref_x = &x; f(&*x); @@ -15,9 +15,16 @@ fn main() { let _: &dyn Any = &*x; //~^ coerce_container_to_any + let _: &dyn Any = &*x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut *x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; diff --git a/tests/ui/coerce_container_to_any.rs b/tests/ui/coerce_container_to_any.rs index 7edb65fa0f7..4d6527bb552 100644 --- a/tests/ui/coerce_container_to_any.rs +++ b/tests/ui/coerce_container_to_any.rs @@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box = Box::new(()); + let mut x: Box = Box::new(()); let ref_x = &x; f(&x); @@ -15,9 +15,16 @@ fn main() { let _: &dyn Any = &x; //~^ coerce_container_to_any + let _: &dyn Any = &mut x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; diff --git a/tests/ui/coerce_container_to_any.stderr b/tests/ui/coerce_container_to_any.stderr index 00ab77e0ce0..26389c9186e 100644 --- a/tests/ui/coerce_container_to_any.stderr +++ b/tests/ui/coerce_container_to_any.stderr @@ -19,5 +19,17 @@ error: coercing `&std::boxed::Box` to `&dyn Any` LL | let _: &dyn Any = &x; | ^^ help: consider dereferencing: `&*x` -error: aborting due to 3 previous errors +error: coercing `&mut std::boxed::Box` to `&dyn Any` + --> tests/ui/coerce_container_to_any.rs:18:23 + | +LL | let _: &dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&*x` + +error: coercing `&mut std::boxed::Box` to `&mut dyn Any` + --> tests/ui/coerce_container_to_any.rs:21:27 + | +LL | let _: &mut dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&mut *x` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From bcc96da3c3ee8350bbb212d3ed6c27c68fc2ba05 Mon Sep 17 00:00:00 2001 From: Urgau <3616612+Urgau@users.noreply.github.com> Date: Sun, 15 Jun 2025 00:43:17 +0200 Subject: Add minimal triagebot config This PR adds a minimal `triagebot.toml` config to make contributions to this repository respect upstream rust-lang/rust conventions and avoid issues when syncing this subtree. --- library/compiler-builtins/triagebot.toml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 library/compiler-builtins/triagebot.toml diff --git a/library/compiler-builtins/triagebot.toml b/library/compiler-builtins/triagebot.toml new file mode 100644 index 00000000000..ecc05da0195 --- /dev/null +++ b/library/compiler-builtins/triagebot.toml @@ -0,0 +1,21 @@ +## See for documentation +## of these features. + +# Warns when a PR contains merge commits +# Documentation at: https://forge.rust-lang.org/triagebot/no-merge.html +[no-merges] +exclude_titles = ["Update from"] + +# Canonicalize issue numbers to avoid closing the wrong issue +# when commits are included in subtrees, as well as warning links in commits. +# Documentation at: https://forge.rust-lang.org/triagebot/issue-links.html +[issue-links] +check-commits = false + +# Prevents mentions in commits to avoid users being spammed +# Documentation at: https://forge.rust-lang.org/triagebot/no-mentions.html +[no-mentions] + +# Enable issue transfers within the org +# Documentation at: https://forge.rust-lang.org/triagebot/transfer.html +[transfer] -- cgit 1.4.1-3-g733a5 From 9f4670fd2b3c204567f57e5580a3cd41e9ea4b58 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 14 Jun 2025 23:39:24 +0000 Subject: symcheck: Add a wrapper around an archive Rather than re-opening the archive file for each check, add a wrapper that keeps the data in memory. Additionally, collect the `--target` argument so it can be used within this crate. --- .../crates/symbol-check/src/main.rs | 104 ++++++++++++++++----- 1 file changed, 80 insertions(+), 24 deletions(-) diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs index d83cd318d6a..843a943fb5f 100644 --- a/library/compiler-builtins/crates/symbol-check/src/main.rs +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -8,7 +8,9 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use object::read::archive::{ArchiveFile, ArchiveMember}; -use object::{Object, ObjectSymbol, Symbol, SymbolKind, SymbolScope, SymbolSection}; +use object::{ + File as ObjFile, Object, ObjectSymbol, Symbol, SymbolKind, SymbolScope, SymbolSection, +}; use serde_json::Value; const CHECK_LIBRARIES: &[&str] = &["compiler_builtins", "builtins_test_intrinsics"]; @@ -28,13 +30,11 @@ fn main() { let args_ref = args.iter().map(String::as_str).collect::>(); match &args_ref[1..] { - ["build-and-check", rest @ ..] if !rest.is_empty() => { - let paths = exec_cargo_with_args(rest); - for path in paths { - println!("Checking {}", path.display()); - verify_no_duplicates(&path); - verify_core_symbols(&path); - } + ["build-and-check", "--target", target, args @ ..] if !args.is_empty() => { + run_build_and_check(Some(target), args); + } + ["build-and-check", args @ ..] if !args.is_empty() => { + run_build_and_check(None, args); } _ => { println!("{USAGE}"); @@ -43,12 +43,42 @@ fn main() { } } +fn run_build_and_check(target: Option<&str>, args: &[&str]) { + let paths = exec_cargo_with_args(target, args); + for path in paths { + println!("Checking {}", path.display()); + let archive = Archive::from_path(&path); + + verify_no_duplicates(&archive); + verify_core_symbols(&archive); + } +} + +fn host_target() -> String { + let out = Command::new("rustc") + .arg("--version") + .arg("--verbose") + .output() + .unwrap(); + assert!(out.status.success()); + let out = String::from_utf8(out.stdout).unwrap(); + out.lines() + .find_map(|s| s.strip_prefix("host: ")) + .unwrap() + .to_owned() +} + /// Run `cargo build` with the provided additional arguments, collecting the list of created /// libraries. -fn exec_cargo_with_args(args: &[&str]) -> Vec { +fn exec_cargo_with_args(target: Option<&str>, args: &[&str]) -> Vec { + let mut host = String::new(); + let target = target.unwrap_or_else(|| { + host = host_target(); + host.as_str() + }); + let mut cmd = Command::new("cargo"); - cmd.arg("build") - .arg("--message-format=json") + cmd.args(["build", "--target", target, "--message-format=json"]) .args(args) .stdout(Stdio::piped()); @@ -133,12 +163,12 @@ impl SymInfo { /// Note that this will also locate cases where a symbol is weakly defined in more than one place. /// Technically there are no linker errors that will come from this, but it keeps our binary more /// straightforward and saves some distribution size. -fn verify_no_duplicates(path: &Path) { +fn verify_no_duplicates(archive: &Archive) { let mut syms = BTreeMap::::new(); let mut dups = Vec::new(); let mut found_any = false; - for_each_symbol(path, |symbol, member| { + archive.for_each_symbol(|symbol, member| { // Only check defined globals if !symbol.is_global() || symbol.is_undefined() { return; @@ -185,12 +215,12 @@ fn verify_no_duplicates(path: &Path) { } /// Ensure that there are no references to symbols from `core` that aren't also (somehow) defined. -fn verify_core_symbols(path: &Path) { +fn verify_core_symbols(archive: &Archive) { let mut defined = BTreeSet::new(); let mut undefined = Vec::new(); let mut has_symbols = false; - for_each_symbol(path, |symbol, member| { + archive.for_each_symbol(|symbol, member| { has_symbols = true; // Find only symbols from `core` @@ -219,14 +249,40 @@ fn verify_core_symbols(path: &Path) { println!(" success: no undefined references to core found"); } -/// For a given archive path, do something with each symbol. -fn for_each_symbol(path: &Path, mut f: impl FnMut(Symbol, &ArchiveMember)) { - let data = fs::read(path).expect("reading file failed"); - let archive = ArchiveFile::parse(data.as_slice()).expect("archive parse failed"); - for member in archive.members() { - let member = member.expect("failed to access member"); - let obj_data = member.data(&*data).expect("failed to access object"); - let obj = object::File::parse(obj_data).expect("failed to parse object"); - obj.symbols().for_each(|sym| f(sym, &member)); +/// Thin wrapper for owning data used by `object`. +struct Archive { + data: Vec, +} + +impl Archive { + fn from_path(path: &Path) -> Self { + Self { + data: fs::read(path).expect("reading file failed"), + } + } + + fn file(&self) -> ArchiveFile<'_> { + ArchiveFile::parse(self.data.as_slice()).expect("archive parse failed") + } + + /// For a given archive, do something with each object file. + fn for_each_object(&self, mut f: impl FnMut(ObjFile, &ArchiveMember)) { + let archive = self.file(); + + for member in archive.members() { + let member = member.expect("failed to access member"); + let obj_data = member + .data(self.data.as_slice()) + .expect("failed to access object"); + let obj = ObjFile::parse(obj_data).expect("failed to parse object"); + f(obj, &member); + } + } + + /// For a given archive, do something with each symbol. + fn for_each_symbol(&self, mut f: impl FnMut(Symbol, &ArchiveMember)) { + self.for_each_object(|obj, member| { + obj.symbols().for_each(|sym| f(sym, member)); + }); } } -- cgit 1.4.1-3-g733a5 From f46cf875f44aa2416801ec743f6bad760a9672fa Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 17 Jun 2025 21:50:37 +0800 Subject: fix: `op_ref` wrongly showed macro definition in suggestions --- clippy_lints/src/operators/op_ref.rs | 30 +++++++++++++++--------------- tests/ui/op_ref.fixed | 34 ++++++++++++++++++++++++++++++++++ tests/ui/op_ref.rs | 34 ++++++++++++++++++++++++++++++++++ tests/ui/op_ref.stderr | 22 +++++++++++++++++++++- 4 files changed, 104 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index 0faa7b9e646..21e1ab0f4f2 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_enclosing_block; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -61,12 +61,13 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of both operands", |diag| { - let lsnip = snippet(cx, l.span, "...").to_string(); - let rsnip = snippet(cx, r.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability); + let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability); diag.multipart_suggestion( "use the values directly", - vec![(left.span, lsnip), (right.span, rsnip)], - Applicability::MachineApplicable, + vec![(left.span, lsnip.to_string()), (right.span, rsnip.to_string())], + applicability, ); }, ); @@ -80,13 +81,9 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of left operand", |diag| { - let lsnip = snippet(cx, l.span, "...").to_string(); - diag.span_suggestion( - left.span, - "use the left value directly", - lsnip, - Applicability::MachineApplicable, - ); + let mut applicability = Applicability::MachineApplicable; + let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability); + diag.span_suggestion(left.span, "use the left value directly", lsnip, applicability); }, ); } else if !lcpy @@ -99,7 +96,8 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of right operand", |diag| { - let rsnip = snippet(cx, r.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability); diag.span_suggestion( right.span, "use the right value directly", @@ -131,7 +129,8 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of left operand", |diag| { - let lsnip = snippet(cx, l.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability); diag.span_suggestion( left.span, "use the left value directly", @@ -158,7 +157,8 @@ pub(crate) fn check<'tcx>( && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()]) { span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| { - let rsnip = snippet(cx, r.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability); diag.span_suggestion( right.span, "use the right value directly", diff --git a/tests/ui/op_ref.fixed b/tests/ui/op_ref.fixed index f412190b9fd..4bf4b91888c 100644 --- a/tests/ui/op_ref.fixed +++ b/tests/ui/op_ref.fixed @@ -110,3 +110,37 @@ mod issue_2597 { &array[idx] < val } } + +#[allow(clippy::needless_if)] +fn issue15063() { + use std::ops::BitAnd; + + macro_rules! mac { + ($e:expr) => { + $e.clone() + }; + } + + let x = 1; + if x == mac!(1) {} + //~^ op_ref + + #[derive(Copy, Clone)] + struct Y(i32); + impl BitAnd for Y { + type Output = Y; + fn bitand(self, rhs: Y) -> Y { + Y(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a Y> for Y { + type Output = Y; + fn bitand(self, rhs: &'a Y) -> Y { + Y(self.0 & rhs.0) + } + } + let x = Y(1); + let y = Y(2); + let z = x & mac!(y); + //~^ op_ref +} diff --git a/tests/ui/op_ref.rs b/tests/ui/op_ref.rs index a4bbd86c7e9..9a192661aaf 100644 --- a/tests/ui/op_ref.rs +++ b/tests/ui/op_ref.rs @@ -110,3 +110,37 @@ mod issue_2597 { &array[idx] < val } } + +#[allow(clippy::needless_if)] +fn issue15063() { + use std::ops::BitAnd; + + macro_rules! mac { + ($e:expr) => { + $e.clone() + }; + } + + let x = 1; + if &x == &mac!(1) {} + //~^ op_ref + + #[derive(Copy, Clone)] + struct Y(i32); + impl BitAnd for Y { + type Output = Y; + fn bitand(self, rhs: Y) -> Y { + Y(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a Y> for Y { + type Output = Y; + fn bitand(self, rhs: &'a Y) -> Y { + Y(self.0 & rhs.0) + } + } + let x = Y(1); + let y = Y(2); + let z = x & &mac!(y); + //~^ op_ref +} diff --git a/tests/ui/op_ref.stderr b/tests/ui/op_ref.stderr index 51c2963a9ee..8a58b154c81 100644 --- a/tests/ui/op_ref.stderr +++ b/tests/ui/op_ref.stderr @@ -36,5 +36,25 @@ LL | let _ = two + &three; | | | help: use the right value directly: `three` -error: aborting due to 4 previous errors +error: needlessly taken reference of both operands + --> tests/ui/op_ref.rs:125:8 + | +LL | if &x == &mac!(1) {} + | ^^^^^^^^^^^^^^ + | +help: use the values directly + | +LL - if &x == &mac!(1) {} +LL + if x == mac!(1) {} + | + +error: taken reference of right operand + --> tests/ui/op_ref.rs:144:13 + | +LL | let z = x & &mac!(y); + | ^^^^-------- + | | + | help: use the right value directly: `mac!(y)` + +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From f29aa6934b289204b9ec14b7b919f84a89cd3417 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 17 Jun 2025 22:07:18 +0800 Subject: fix: `needless_bool_assign` missing curlies when on else if --- clippy_lints/src/needless_bool.rs | 7 ++++++- tests/ui/needless_bool_assign.fixed | 9 +++++++++ tests/ui/needless_bool_assign.rs | 13 +++++++++++++ tests/ui/needless_bool_assign.stderr | 13 ++++++++++++- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 3ed4b1c2ea9..b3aa1a7286a 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -199,11 +199,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { let mut applicability = Applicability::MachineApplicable; let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability); - let sugg = if a == b { + let mut sugg = if a == b { format!("{cond}; {lhs} = {a:?};") } else { format!("{lhs} = {};", if a { cond } else { !cond }) }; + + if is_else_clause(cx.tcx, e) { + sugg = format!("{{ {sugg} }}"); + } + span_lint_and_sugg( cx, NEEDLESS_BOOL_ASSIGN, diff --git a/tests/ui/needless_bool_assign.fixed b/tests/ui/needless_bool_assign.fixed index e0c717ecda2..d6fab4c51b5 100644 --- a/tests/ui/needless_bool_assign.fixed +++ b/tests/ui/needless_bool_assign.fixed @@ -33,3 +33,12 @@ fn main() { b = true; } } + +fn issue15063(x: bool, y: bool) { + let mut z = false; + + if x && y { + todo!() + } else { z = x || y; } + //~^^^^^ needless_bool_assign +} diff --git a/tests/ui/needless_bool_assign.rs b/tests/ui/needless_bool_assign.rs index 3e4fecefa78..c504f61f4dd 100644 --- a/tests/ui/needless_bool_assign.rs +++ b/tests/ui/needless_bool_assign.rs @@ -45,3 +45,16 @@ fn main() { b = true; } } + +fn issue15063(x: bool, y: bool) { + let mut z = false; + + if x && y { + todo!() + } else if x || y { + z = true; + } else { + z = false; + } + //~^^^^^ needless_bool_assign +} diff --git a/tests/ui/needless_bool_assign.stderr b/tests/ui/needless_bool_assign.stderr index f33a4bc0c59..1d09b8b25a0 100644 --- a/tests/ui/needless_bool_assign.stderr +++ b/tests/ui/needless_bool_assign.stderr @@ -51,5 +51,16 @@ LL | | } = note: `-D clippy::if-same-then-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::if_same_then_else)]` -error: aborting due to 4 previous errors +error: this if-then-else expression assigns a bool literal + --> tests/ui/needless_bool_assign.rs:54:12 + | +LL | } else if x || y { + | ____________^ +LL | | z = true; +LL | | } else { +LL | | z = false; +LL | | } + | |_____^ help: you can reduce it to: `{ z = x || y; }` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From dc3b8bef8052a6dbd877cd9ecc4c26592685aa4c Mon Sep 17 00:00:00 2001 From: klensy Date: Tue, 17 Jun 2025 17:21:46 +0300 Subject: precommit test --- tests/ui/or_fun_call.fixed | 18 ++++++++++++++++++ tests/ui/or_fun_call.rs | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 34f3e046841..bb46ce04466 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -439,4 +439,22 @@ fn test_option_get_or_insert() { //~^ or_fun_call } +fn test_option_and() { + // assume that this is slow call + fn g() -> Option { + Some(99) + } + let mut x = Some(42_u8); + let _ = x.and(g()); +} + +fn test_result_and() { + // assume that this is slow call + fn g() -> Result { + Ok(99) + } + let mut x: Result = Ok(42); + let _ = x.and(g()); +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index dc57bd6060a..68ef244a019 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -439,4 +439,22 @@ fn test_option_get_or_insert() { //~^ or_fun_call } +fn test_option_and() { + // assume that this is slow call + fn g() -> Option { + Some(99) + } + let mut x = Some(42_u8); + let _ = x.and(g()); +} + +fn test_result_and() { + // assume that this is slow call + fn g() -> Result { + Ok(99) + } + let mut x: Result = Ok(42); + let _ = x.and(g()); +} + fn main() {} -- cgit 1.4.1-3-g733a5 From a82c142f3e0be8f88e588a18bf897899d30f179d Mon Sep 17 00:00:00 2001 From: klensy Date: Tue, 17 Jun 2025 17:27:56 +0300 Subject: or_fun_call: lint Option/Result::and(_then) too --- clippy_lints/src/methods/or_fun_call.rs | 4 +++- tests/ui/or_fun_call.fixed | 6 ++++-- tests/ui/or_fun_call.rs | 2 ++ tests/ui/or_fun_call.stderr | 14 +++++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 2139466ce74..6ce7dd3d4d0 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -136,7 +136,7 @@ pub(super) fn check<'tcx>( fun_span: Option, ) -> bool { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 5] = [ + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ (sym::BTreeEntry, false, &[sym::or_insert], "with"), (sym::HashMapEntry, false, &[sym::or_insert], "with"), ( @@ -146,7 +146,9 @@ pub(super) fn check<'tcx>( "else", ), (sym::Option, false, &[sym::get_or_insert], "with"), + (sym::Option, true, &[sym::and], "then"), (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), + (sym::Result, true, &[sym::and], "then"), ]; if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index bb46ce04466..bcd2602edb6 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -445,7 +445,8 @@ fn test_option_and() { Some(99) } let mut x = Some(42_u8); - let _ = x.and(g()); + let _ = x.and_then(|_| g()); + //~^ or_fun_call } fn test_result_and() { @@ -454,7 +455,8 @@ fn test_result_and() { Ok(99) } let mut x: Result = Ok(42); - let _ = x.and(g()); + let _ = x.and_then(|_| g()); + //~^ or_fun_call } fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 68ef244a019..8d1202ebf91 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -446,6 +446,7 @@ fn test_option_and() { } let mut x = Some(42_u8); let _ = x.and(g()); + //~^ or_fun_call } fn test_result_and() { @@ -455,6 +456,7 @@ fn test_result_and() { } let mut x: Result = Ok(42); let _ = x.and(g()); + //~^ or_fun_call } fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 0f159fe8bff..585ee2d0e19 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -264,5 +264,17 @@ error: function call inside of `get_or_insert` LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` -error: aborting due to 41 previous errors +error: function call inside of `and` + --> tests/ui/or_fun_call.rs:448:15 + | +LL | let _ = x.and(g()); + | ^^^^^^^^ help: try: `and_then(|_| g())` + +error: function call inside of `and` + --> tests/ui/or_fun_call.rs:458:15 + | +LL | let _ = x.and(g()); + | ^^^^^^^^ help: try: `and_then(|_| g())` + +error: aborting due to 43 previous errors -- cgit 1.4.1-3-g733a5 From 0cf0f9fc74f7bd1c9f160cc1dbd67c92a9436704 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 22 Jun 2025 20:27:27 +1000 Subject: Use `join_with_double_colon` in `write_shared.rs`. For consistency. Also, it's faster because `join_with_double_colon` does a better job estimating the allocation size than `join` from `itertools`. --- src/librustdoc/html/render/write_shared.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fb2b45802a6..42007c81433 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -25,7 +25,6 @@ use std::str::FromStr; use std::{fmt, fs}; use indexmap::IndexMap; -use itertools::Itertools; use regex::Regex; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -44,6 +43,7 @@ use crate::docfs::PathError; use crate::error::Error; use crate::formats::Impl; use crate::formats::item_type::ItemType; +use crate::html::format::join_with_double_colon; use crate::html::layout; use crate::html::render::ordered_json::{EscapedJson, OrderedJson}; use crate::html::render::search_index::{SerializedSearchIndex, build_index}; @@ -612,7 +612,7 @@ impl TypeAliasPart { for &(type_alias_fqp, type_alias_item) in type_aliases { cx.id_map.borrow_mut().clear(); cx.deref_id_map.borrow_mut().clear(); - let type_alias_fqp = (*type_alias_fqp).iter().join("::"); + let type_alias_fqp = join_with_double_colon(&type_alias_fqp); if let Some(ret) = &mut ret { ret.aliases.push(type_alias_fqp); } else { -- cgit 1.4.1-3-g733a5 From 7a6f7bc7a668aa309249ca7e8a4275f7fedce173 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 27 May 2025 16:16:18 +0200 Subject: Update clippy source code to changes on `source_span_for_markdown_range` --- clippy_lints/src/doc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index e0fc2fd9347..d38588bb799 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -765,8 +765,8 @@ impl Fragments<'_> { /// get the span for the markdown range. Note that this function is not cheap, use it with /// caution. #[must_use] - fn span(&self, cx: &LateContext<'_>, range: Range) -> Option { - source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments) + fn span(self, cx: &LateContext<'_>, range: Range) -> Option { + source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments).map(|(sp, _)| sp) } } -- cgit 1.4.1-3-g733a5 From 89f7a4301d8a547ee819dfefd95ac7d40005b8cc Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 23 Jun 2025 11:15:04 +0200 Subject: Properly check that an expression might be the one returned The `TyCtxt::hir_get_fn_id_for_return_block()` function was too broad, as it will return positively even when given part of an expression that can be used as a return value. A new `potential_return_of_enclosing_body()` utility function has been made to represent the fact that an expression might be directly returned from its enclosing body. --- clippy_lints/src/methods/return_and_then.rs | 29 ++++-- clippy_utils/src/lib.rs | 61 +++++++++++++ tests/ui/return_and_then.fixed | 131 ++++++++++++++++++++++++++++ tests/ui/return_and_then.rs | 114 ++++++++++++++++++++++++ tests/ui/return_and_then.stderr | 96 +++++++++++++++++++- 5 files changed, 422 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/methods/return_and_then.rs b/clippy_lints/src/methods/return_and_then.rs index df8544f9220..54f38a322b8 100644 --- a/clippy_lints/src/methods/return_and_then.rs +++ b/clippy_lints/src/methods/return_and_then.rs @@ -1,5 +1,5 @@ use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, Node}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArg, Ty}; use rustc_span::sym; @@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::for_each_unconsumed_temporary; -use clippy_utils::{get_parent_expr, peel_blocks}; +use clippy_utils::{peel_blocks, potential_return_of_enclosing_body}; use super::RETURN_AND_THEN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'_>, ) { - if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() { + if !potential_return_of_enclosing_body(cx, expr) { return; } @@ -55,15 +55,28 @@ pub(super) fn check<'tcx>( None => &body_snip, }; - // If suggestion is going to get inserted as part of a `return` expression, it must be blockified. - let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { - let base_indent = indent_of(cx, parent_expr.span); + // If suggestion is going to get inserted as part of a `return` expression or as a match expression + // arm, it must be blockified. + let (parent_span_for_indent, opening_paren, closing_paren) = match cx.tcx.parent_hir_node(expr.hir_id) { + Node::Expr(parent_expr) if matches!(parent_expr.kind, hir::ExprKind::Break(..)) => { + (Some(parent_expr.span), "(", ")") + }, + Node::Expr(parent_expr) => (Some(parent_expr.span), "", ""), + Node::Arm(match_arm) => (Some(match_arm.span), "", ""), + _ => (None, "", ""), + }; + let sugg = if let Some(span) = parent_span_for_indent { + let base_indent = indent_of(cx, span); let inner_indent = base_indent.map(|i| i + 4); format!( "{}\n{}\n{}", - reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent), + reindent_multiline( + &format!("{opening_paren}{{\nlet {arg_snip} = {recv_snip}?;"), + true, + inner_indent + ), reindent_multiline(inner, false, inner_indent), - reindent_multiline("}", false, base_indent), + reindent_multiline(&format!("}}{closing_paren}"), false, base_indent), ) } else { format!( diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7fa52229fef..9ac851454af 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3497,3 +3497,64 @@ pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> false } } + +/// Checks if `expr` may be directly used as the return value of its enclosing body. +/// The following cases are covered: +/// - `expr` as the last expression of the body, or of a block that can be used as the return value +/// - `return expr` +/// - then or else part of a `if` in return position +/// - arm body of a `match` in a return position +/// - `break expr` or `break 'label expr` if the loop or block being exited is used as a return +/// value +/// +/// Contrary to [`TyCtxt::hir_get_fn_id_for_return_block()`], if `expr` is part of a +/// larger expression, for example a field expression of a `struct`, it will not be +/// considered as matching the condition and will return `false`. +/// +/// Also, even if `expr` is assigned to a variable which is later returned, this function +/// will still return `false` because `expr` is not used *directly* as the return value +/// as it goes through the intermediate variable. +pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let enclosing_body_owner = cx + .tcx + .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id)); + let mut prev_id = expr.hir_id; + let mut skip_until_id = None; + for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) { + if hir_id == enclosing_body_owner { + return true; + } + if let Some(id) = skip_until_id { + prev_id = hir_id; + if id == hir_id { + skip_until_id = None; + } + continue; + } + match node { + Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {}, + Node::Arm(arm) if arm.body.hir_id == prev_id => {}, + Node::Expr(expr) => match expr.kind { + ExprKind::Ret(_) => return true, + ExprKind::If(_, then, opt_else) + if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {}, + ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {}, + ExprKind::Block(block, _) if block.hir_id == prev_id => {}, + ExprKind::Break( + Destination { + target_id: Ok(target_id), + .. + }, + _, + ) => skip_until_id = Some(target_id), + _ => break, + }, + _ => break, + } + prev_id = hir_id; + } + + // `expr` is used as part of "something" and is not returned directly from its + // enclosing body. + false +} diff --git a/tests/ui/return_and_then.fixed b/tests/ui/return_and_then.fixed index 8d9481d1595..8ee259b97f3 100644 --- a/tests/ui/return_and_then.fixed +++ b/tests/ui/return_and_then.fixed @@ -99,6 +99,92 @@ fn main() { }; None } + + #[expect(clippy::diverging_sub_expression)] + fn with_return_in_expression() -> Option { + _ = ( + return { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + }, + //~^ return_and_then + 10, + ); + } + + fn inside_if(a: bool, i: Option) -> Option { + if a { + let i = i?; + if i > 3 { Some(i) } else { None } + //~^ return_and_then + } else { + Some(42) + } + } + + fn inside_match(a: u32, i: Option) -> Option { + match a { + 1 | 2 => { + let i = i?; + if i > 3 { Some(i) } else { None } + }, + //~^ return_and_then + 3 | 4 => Some(42), + _ => None, + } + } + + fn inside_match_and_block_and_if(a: u32, i: Option) -> Option { + match a { + 1 | 2 => { + let a = a * 3; + if a.is_multiple_of(2) { + let i = i?; + if i > 3 { Some(i) } else { None } + //~^ return_and_then + } else { + Some(10) + } + }, + 3 | 4 => Some(42), + _ => None, + } + } + + #[expect(clippy::never_loop)] + fn with_break(i: Option) -> Option { + match i { + Some(1) => loop { + break ({ + let i = i?; + if i > 3 { Some(i) } else { None } + }); + //~^ return_and_then + }, + Some(2) => 'foo: loop { + loop { + break 'foo ({ + let i = i?; + if i > 3 { Some(i) } else { None } + }); + //~^ return_and_then + } + }, + Some(3) => 'bar: { + break 'bar ({ + let i = i?; + if i > 3 { Some(i) } else { None } + }); + //~^ return_and_then + }, + Some(4) => 'baz: loop { + _ = loop { + break i.and_then(|i| if i > 3 { Some(i) } else { None }); + }; + }, + _ => None, + } + } } fn gen_option(n: i32) -> Option { @@ -124,3 +210,48 @@ mod issue14781 { Ok(()) } } + +mod issue15111 { + #[derive(Debug)] + struct EvenOdd { + even: Option, + odd: Option, + } + + impl EvenOdd { + fn new(i: Option) -> Self { + Self { + even: i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }), + odd: i.and_then(|i| if i.is_multiple_of(2) { None } else { Some(i) }), + } + } + } + + fn with_if_let(i: Option) -> u32 { + if let Some(x) = i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }) { + x + } else { + std::hint::black_box(0) + } + } + + fn main() { + let _ = EvenOdd::new(Some(2)); + } +} + +mod issue14927 { + use std::path::Path; + struct A { + pub func: fn(check: bool, a: &Path, b: Option<&Path>), + } + const MY_A: A = A { + func: |check, a, b| { + if check { + let _ = (); + } else if let Some(parent) = b.and_then(|p| p.parent()) { + let _ = (); + } + }, + }; +} diff --git a/tests/ui/return_and_then.rs b/tests/ui/return_and_then.rs index beada921a91..dcb344f142b 100644 --- a/tests/ui/return_and_then.rs +++ b/tests/ui/return_and_then.rs @@ -90,6 +90,75 @@ fn main() { }; None } + + #[expect(clippy::diverging_sub_expression)] + fn with_return_in_expression() -> Option { + _ = ( + return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }), + //~^ return_and_then + 10, + ); + } + + fn inside_if(a: bool, i: Option) -> Option { + if a { + i.and_then(|i| if i > 3 { Some(i) } else { None }) + //~^ return_and_then + } else { + Some(42) + } + } + + fn inside_match(a: u32, i: Option) -> Option { + match a { + 1 | 2 => i.and_then(|i| if i > 3 { Some(i) } else { None }), + //~^ return_and_then + 3 | 4 => Some(42), + _ => None, + } + } + + fn inside_match_and_block_and_if(a: u32, i: Option) -> Option { + match a { + 1 | 2 => { + let a = a * 3; + if a.is_multiple_of(2) { + i.and_then(|i| if i > 3 { Some(i) } else { None }) + //~^ return_and_then + } else { + Some(10) + } + }, + 3 | 4 => Some(42), + _ => None, + } + } + + #[expect(clippy::never_loop)] + fn with_break(i: Option) -> Option { + match i { + Some(1) => loop { + break i.and_then(|i| if i > 3 { Some(i) } else { None }); + //~^ return_and_then + }, + Some(2) => 'foo: loop { + loop { + break 'foo i.and_then(|i| if i > 3 { Some(i) } else { None }); + //~^ return_and_then + } + }, + Some(3) => 'bar: { + break 'bar i.and_then(|i| if i > 3 { Some(i) } else { None }); + //~^ return_and_then + }, + Some(4) => 'baz: loop { + _ = loop { + break i.and_then(|i| if i > 3 { Some(i) } else { None }); + }; + }, + _ => None, + } + } } fn gen_option(n: i32) -> Option { @@ -115,3 +184,48 @@ mod issue14781 { Ok(()) } } + +mod issue15111 { + #[derive(Debug)] + struct EvenOdd { + even: Option, + odd: Option, + } + + impl EvenOdd { + fn new(i: Option) -> Self { + Self { + even: i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }), + odd: i.and_then(|i| if i.is_multiple_of(2) { None } else { Some(i) }), + } + } + } + + fn with_if_let(i: Option) -> u32 { + if let Some(x) = i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }) { + x + } else { + std::hint::black_box(0) + } + } + + fn main() { + let _ = EvenOdd::new(Some(2)); + } +} + +mod issue14927 { + use std::path::Path; + struct A { + pub func: fn(check: bool, a: &Path, b: Option<&Path>), + } + const MY_A: A = A { + func: |check, a, b| { + if check { + let _ = (); + } else if let Some(parent) = b.and_then(|p| p.parent()) { + let _ = (); + } + }, + }; +} diff --git a/tests/ui/return_and_then.stderr b/tests/ui/return_and_then.stderr index 5feca882860..33867ea818a 100644 --- a/tests/ui/return_and_then.stderr +++ b/tests/ui/return_and_then.stderr @@ -146,5 +146,99 @@ LL + if x.len() > 2 { Some(3) } else { None } LL ~ }; | -error: aborting due to 10 previous errors +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:97:20 + | +LL | return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ return { +LL + let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }, + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:105:13 + | +LL | i.and_then(|i| if i > 3 { Some(i) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let i = i?; +LL + if i > 3 { Some(i) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:114:22 + | +LL | 1 | 2 => i.and_then(|i| if i > 3 { Some(i) } else { None }), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ 1 | 2 => { +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }, + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:126:21 + | +LL | i.and_then(|i| if i > 3 { Some(i) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let i = i?; +LL + if i > 3 { Some(i) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:141:23 + | +LL | break i.and_then(|i| if i > 3 { Some(i) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ break ({ +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }); + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:146:32 + | +LL | break 'foo i.and_then(|i| if i > 3 { Some(i) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ break 'foo ({ +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }); + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:151:28 + | +LL | break 'bar i.and_then(|i| if i > 3 { Some(i) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ break 'bar ({ +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }); + | + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From cd682446535c16b8c2d0b0e323bb8ee210371604 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 23 Jun 2025 10:57:51 -0700 Subject: Handle potentially-shadowing bindings in manual_let_else This commit also adds more test cases, which work fine but were mentioned in the issue. --- clippy_lints/src/manual_let_else.rs | 39 ++++++++++++++----- tests/ui/manual_let_else_match.fixed | 45 +++++++++++++++++++++ tests/ui/manual_let_else_match.rs | 73 +++++++++++++++++++++++++++++++++++ tests/ui/manual_let_else_match.stderr | 72 +++++++++++++++++++++++++++++++++- 4 files changed, 218 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 9ff82cdcb66..1f9a943f13d 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -245,17 +245,36 @@ fn replace_in_pattern( } match pat.kind { - PatKind::Binding(_ann, _id, binding_name, opt_subpt) => { - let Some((pat_to_put, binding_mode)) = ident_map.get(&binding_name.name) else { - break 'a; - }; - let sn_pfx = binding_mode.prefix_str(); - let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); - if let Some(subpt) = opt_subpt { - let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); - return format!("{sn_pfx}{sn_ptp} @ {subpt}"); + PatKind::Binding(ann, _id, binding_name, opt_subpt) => { + match (ident_map.get(&binding_name.name), opt_subpt) { + (Some((pat_to_put, binding_mode)), opt_subpt) => { + let sn_pfx = binding_mode.prefix_str(); + let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); + if let Some(subpt) = opt_subpt { + let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); + return format!("{sn_pfx}{sn_ptp} @ {subpt}"); + } + return format!("{sn_pfx}{sn_ptp}"); + }, + (None, Some(subpt)) => { + let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); + // scanning for a value that matches is not sensitive to order + #[expect(rustc::potential_query_instability)] + if ident_map.values().any(|(other_pat, _)| { + if let PatKind::Binding(_, _, other_name, _) = other_pat.kind { + other_name == binding_name + } else { + false + } + }) { + // this name is shadowed, and, therefore, not usable + return subpt; + } + let binding_pfx = ann.prefix_str(); + return format!("{binding_pfx}{binding_name} @ {subpt}"); + }, + (None, None) => break 'a, } - return format!("{sn_pfx}{sn_ptp}"); }, PatKind::Or(pats) => { let patterns = pats diff --git a/tests/ui/manual_let_else_match.fixed b/tests/ui/manual_let_else_match.fixed index 588ba5edd8f..15f604aec29 100644 --- a/tests/ui/manual_let_else_match.fixed +++ b/tests/ui/manual_let_else_match.fixed @@ -137,3 +137,48 @@ fn not_fire() { fn issue11579() { let Some(msg) = Some("hi") else { unreachable!("can't happen") }; } + +#[derive(Clone, Copy)] +struct Issue9939 { + avalanche: T, +} + +fn issue9939() { + let issue = Some(Issue9939 { avalanche: 1 }); + let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") }; + let issue = Some(Issue9939 { avalanche: true }); + let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") }; + assert_eq!(tornado, 1); + assert!(acid_rain); + + // without shadowing + let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") }; + + // with shadowing + let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") }; +} + +#[derive(Clone, Copy)] +struct Issue9939b { + earthquake: T, + hurricane: U, +} + +fn issue9939b() { + let issue = Some(Issue9939b { + earthquake: true, + hurricane: 1, + }); + let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") }; + assert_eq!(drought, 1); + assert!(flood); + assert!(issue.is_some()); + + // without shadowing + let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") }; + assert!(erosion); + + // with shadowing + let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") }; + assert!(erosion); +} diff --git a/tests/ui/manual_let_else_match.rs b/tests/ui/manual_let_else_match.rs index 6416753bac1..44a044b142b 100644 --- a/tests/ui/manual_let_else_match.rs +++ b/tests/ui/manual_let_else_match.rs @@ -177,3 +177,76 @@ fn issue11579() { _ => unreachable!("can't happen"), }; } + +#[derive(Clone, Copy)] +struct Issue9939 { + avalanche: T, +} + +fn issue9939() { + let issue = Some(Issue9939 { avalanche: 1 }); + let tornado = match issue { + //~^ manual_let_else + Some(Issue9939 { avalanche }) => avalanche, + _ => unreachable!("can't happen"), + }; + let issue = Some(Issue9939 { avalanche: true }); + let acid_rain = match issue { + //~^ manual_let_else + Some(Issue9939 { avalanche: tornado }) => tornado, + _ => unreachable!("can't happen"), + }; + assert_eq!(tornado, 1); + assert!(acid_rain); + + // without shadowing + let _y = match issue { + //~^ manual_let_else + _x @ Some(Issue9939 { avalanche }) => avalanche, + None => unreachable!("can't happen"), + }; + + // with shadowing + let _x = match issue { + //~^ manual_let_else + _x @ Some(Issue9939 { avalanche }) => avalanche, + None => unreachable!("can't happen"), + }; +} + +#[derive(Clone, Copy)] +struct Issue9939b { + earthquake: T, + hurricane: U, +} + +fn issue9939b() { + let issue = Some(Issue9939b { + earthquake: true, + hurricane: 1, + }); + let (issue, drought, flood) = match issue { + //~^ manual_let_else + flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert_eq!(drought, 1); + assert!(flood); + assert!(issue.is_some()); + + // without shadowing + let (_y, erosion) = match issue { + //~^ manual_let_else + _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert!(erosion); + + // with shadowing + let (_x, erosion) = match issue { + //~^ manual_let_else + _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert!(erosion); +} diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr index 393562c629b..ed6117ebffb 100644 --- a/tests/ui/manual_let_else_match.stderr +++ b/tests/ui/manual_let_else_match.stderr @@ -101,5 +101,75 @@ LL | | _ => unreachable!("can't happen"), LL | | }; | |______^ help: consider writing: `let Some(msg) = Some("hi") else { unreachable!("can't happen") };` -error: aborting due to 10 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:188:5 + | +LL | / let tornado = match issue { +LL | | +LL | | Some(Issue9939 { avalanche }) => avalanche, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:194:5 + | +LL | / let acid_rain = match issue { +LL | | +LL | | Some(Issue9939 { avalanche: tornado }) => tornado, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:203:5 + | +LL | / let _y = match issue { +LL | | +LL | | _x @ Some(Issue9939 { avalanche }) => avalanche, +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:210:5 + | +LL | / let _x = match issue { +LL | | +LL | | _x @ Some(Issue9939 { avalanche }) => avalanche, +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:228:5 + | +LL | / let (issue, drought, flood) = match issue { +LL | | +LL | | flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:238:5 + | +LL | / let (_y, erosion) = match issue { +LL | | +LL | | _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:246:5 + | +LL | / let (_x, erosion) = match issue { +LL | | +LL | | _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") };` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 962ff16101b66ea9937163de9bd9ae948aea9332 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 24 Jun 2025 20:40:08 +0200 Subject: Use `asm_cfg` in `probestack` cc https://www.github.com/rust-lang/rust/issues/140364 --- .../compiler-builtins/compiler-builtins/src/lib.rs | 1 + .../compiler-builtins/src/probestack.rs | 35 ++++++++-------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/library/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs index dd9920cae01..fe0ad81dd3a 100644 --- a/library/compiler-builtins/compiler-builtins/src/lib.rs +++ b/library/compiler-builtins/compiler-builtins/src/lib.rs @@ -7,6 +7,7 @@ #![feature(compiler_builtins)] #![feature(core_intrinsics)] #![feature(linkage)] +#![feature(asm_cfg)] #![feature(naked_functions)] #![feature(repr_simd)] #![feature(macro_metavar_expr_concat)] diff --git a/library/compiler-builtins/compiler-builtins/src/probestack.rs b/library/compiler-builtins/compiler-builtins/src/probestack.rs index 1d0010842cb..f4105dde57e 100644 --- a/library/compiler-builtins/compiler-builtins/src/probestack.rs +++ b/library/compiler-builtins/compiler-builtins/src/probestack.rs @@ -58,27 +58,6 @@ #[unsafe(naked)] #[rustc_std_internal_symbol] pub unsafe extern "custom" fn __rust_probestack() { - #[cfg(not(all(target_env = "sgx", target_vendor = "fortanix")))] - macro_rules! ret { - () => { - "ret" - }; - } - - #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))] - macro_rules! ret { - // for this target, [manually patch for LVI]. - // - // [manually patch for LVI]: https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions - () => { - " - pop %r11 - lfence - jmp *%r11 - " - }; - } - core::arch::naked_asm!( " .cfi_startproc @@ -128,8 +107,18 @@ pub unsafe extern "custom" fn __rust_probestack() { .cfi_def_cfa_register %rsp .cfi_adjust_cfa_offset -8 ", - ret!(), - " + #[cfg(not(all(target_env = "sgx", target_vendor = "fortanix")))] + " ret", + #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))] + " + // for this target, [manually patch for LVI]. + // + // [manually patch for LVI]: https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions + pop %r11 + lfence + jmp *%r11 + ", + " .cfi_endproc ", options(att_syntax) -- cgit 1.4.1-3-g733a5 From 71ff9a09d103e63a29d813b5ae54905f8a25774e Mon Sep 17 00:00:00 2001 From: Chengxu Bian Date: Wed, 25 Jun 2025 07:23:48 -0400 Subject: fix mul_add uncertain sugg --- clippy_lints/src/floating_point_arithmetic.rs | 36 +++++++++++++++++++++++++-- tests/ui/floating_point_mul_add.fixed | 20 +++++++++++++++ tests/ui/floating_point_mul_add.rs | 20 +++++++++++++++ tests/ui/floating_point_mul_add.stderr | 32 +++++++++++++++++++++++- 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index b3c9e860758..6b03a5a8f85 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -7,7 +7,8 @@ use clippy_utils::{ }; use rustc_ast::ast; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; +use rustc_hir::def::Res; +use rustc_hir::{BinOpKind, Expr, ExprKind, Node, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; @@ -455,7 +456,32 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&' None } -// TODO: Fix rust-lang/rust-clippy#4735 +// Check if any variable in an expression has an ambiguous type (could be f32 or f64) +fn has_ambiguous_float_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match &expr.kind { + ExprKind::Path(qpath) => { + if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id) { + if let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) { + // If the local has no type annotation and the initializer is an unsuffixed float literal, + // then the type is ambiguous + if local.ty.is_none() { + if let Some(init) = local.init { + if let ExprKind::Lit(lit) = &init.kind { + if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node { + return true; + } + } + } + } + } + } + false + }, + ExprKind::Binary(_, lhs, rhs) => has_ambiguous_float_type(cx, lhs) || has_ambiguous_float_type(cx, rhs), + _ => false, + } +} + fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Binary( Spanned { @@ -491,6 +517,12 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { return; }; + // Check if any variable in the expression has an ambiguous type (could be f32 or f64) + // see: https://github.com/rust-lang/rust-clippy/issues/14897 + if has_ambiguous_float_type(cx, recv) { + return; + } + span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index 83aeddb2a1f..3f647730f01 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed @@ -69,3 +69,23 @@ fn _issue11831() { let _ = a + b * c; } + +fn _issue12331() { + let x = 1.0; + let _ = x * 2.0 + 0.5; // should not suggest mul_add + let _ = 0.5 + x * 2.0; // should not suggest mul_add + + let _ = 2.0f64.mul_add(x, 0.5); + //~^ suboptimal_flops + let _ = 2.0f64.mul_add(x, 0.5); + //~^ suboptimal_flops + + let _ = 2.0f64.mul_add(4.0, x); + //~^ suboptimal_flops + + let y: f64 = 1.0; + let _ = y.mul_add(2.0, 0.5); + //~^ suboptimal_flops + let _ = 1.0f64.mul_add(2.0, 0.5); + //~^ suboptimal_flops +} diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 039ee8d053f..44b29a42743 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs @@ -69,3 +69,23 @@ fn _issue11831() { let _ = a + b * c; } + +fn _issue14897() { + let x = 1.0; + let _ = x * 2.0 + 0.5; // should not suggest mul_add + let _ = 0.5 + x * 2.0; // should not suggest mul_add + + let _ = 0.5 + 2.0 * x; + //~^ suboptimal_flops + let _ = 2.0 * x + 0.5; + //~^ suboptimal_flops + + let _ = x + 2.0 * 4.0; + //~^ suboptimal_flops + + let y: f64 = 1.0; + let _ = y * 2.0 + 0.5; + //~^ suboptimal_flops + let _ = 1.0 * 2.0 + 0.5; + //~^ suboptimal_flops +} diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index 6482127bcc0..58d6e61bea3 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -79,5 +79,35 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` -error: aborting due to 13 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:78:13 + | +LL | let _ = 0.5 + 2.0 * x; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:80:13 + | +LL | let _ = 2.0 * x + 0.5; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:83:13 + | +LL | let _ = x + 2.0 * 4.0; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:87:13 + | +LL | let _ = y * 2.0 + 0.5; + | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:89:13 + | +LL | let _ = 1.0 * 2.0 + 0.5; + | ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)` + +error: aborting due to 18 previous errors -- cgit 1.4.1-3-g733a5 From 1eb2920318ca139fbc1a1b10f071775f7a49da4a Mon Sep 17 00:00:00 2001 From: Chengxu Bian Date: Wed, 25 Jun 2025 08:28:29 -0400 Subject: add more test cases and clauses --- clippy_lints/src/floating_point_arithmetic.rs | 45 ++++++++++++++++++++++----- tests/ui/floating_point_mul_add.fixed | 16 +++++++++- tests/ui/floating_point_mul_add.rs | 14 +++++++++ tests/ui/floating_point_mul_add.stderr | 18 +++++++---- 4 files changed, 78 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 6b03a5a8f85..39b8e8c54fa 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -462,22 +462,51 @@ fn has_ambiguous_float_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { ExprKind::Path(qpath) => { if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id) { if let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) { - // If the local has no type annotation and the initializer is an unsuffixed float literal, - // then the type is ambiguous + // If the local has no type annotation, check if the initializer has ambiguous float literals if local.ty.is_none() { if let Some(init) = local.init { - if let ExprKind::Lit(lit) = &init.kind { - if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node { - return true; - } - } + return has_ambiguous_float_literal_in_expr(cx, init); } } } } false }, - ExprKind::Binary(_, lhs, rhs) => has_ambiguous_float_type(cx, lhs) || has_ambiguous_float_type(cx, rhs), + _ => false, // only check path + } +} + +// Recursively check if an expression contains any unsuffixed float literals +fn has_ambiguous_float_literal_in_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match &expr.kind { + ExprKind::Lit(lit) => { + if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node { + return true; + } + false + }, + ExprKind::Binary(_, lhs, rhs) => { + has_ambiguous_float_literal_in_expr(cx, lhs) || has_ambiguous_float_literal_in_expr(cx, rhs) + }, + ExprKind::Unary(_, expr) => has_ambiguous_float_literal_in_expr(cx, expr), + ExprKind::If(_, then, else_) => { + has_ambiguous_float_literal_in_expr(cx, then) + || else_ + .as_ref() + .map_or(false, |else_expr| has_ambiguous_float_literal_in_expr(cx, else_expr)) + }, + ExprKind::Block(block, _) => block + .expr + .as_ref() + .map_or(false, |expr| has_ambiguous_float_literal_in_expr(cx, expr)), + ExprKind::MethodCall(_, receiver, args, _) => { + has_ambiguous_float_literal_in_expr(cx, receiver) + || args.iter().any(|arg| has_ambiguous_float_literal_in_expr(cx, arg)) + }, + ExprKind::Call(func, args) => { + has_ambiguous_float_literal_in_expr(cx, func) + || args.iter().any(|arg| has_ambiguous_float_literal_in_expr(cx, arg)) + }, _ => false, } } diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index 3f647730f01..b07f3a6cc69 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed @@ -70,10 +70,24 @@ fn _issue11831() { let _ = a + b * c; } -fn _issue12331() { +fn _issue14897() { let x = 1.0; let _ = x * 2.0 + 0.5; // should not suggest mul_add let _ = 0.5 + x * 2.0; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + let _ = 1.2 + x * 1.2; // should not suggest mul_add + + let x = -1.0; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = { 4.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = if 1 > 2 { 1.0 } else { 2.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 2.4 + 1.2; + let _ = 0.5 + x * 1.2; // should not suggest mul_add let _ = 2.0f64.mul_add(x, 0.5); //~^ suboptimal_flops diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 44b29a42743..8af9c51f271 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs @@ -74,6 +74,20 @@ fn _issue14897() { let x = 1.0; let _ = x * 2.0 + 0.5; // should not suggest mul_add let _ = 0.5 + x * 2.0; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + let _ = 1.2 + x * 1.2; // should not suggest mul_add + + let x = -1.0; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = { 4.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = if 1 > 2 { 1.0 } else { 2.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 2.4 + 1.2; + let _ = 0.5 + x * 1.2; // should not suggest mul_add let _ = 0.5 + 2.0 * x; //~^ suboptimal_flops diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index 58d6e61bea3..80d08c7de73 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -80,34 +80,40 @@ LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:78:13 + --> tests/ui/floating_point_mul_add.rs:85:13 + | +LL | let _ = 0.5 + x * 1.2; // should not suggest mul_add + | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(1.2, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:96:13 | LL | let _ = 0.5 + 2.0 * x; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:80:13 + --> tests/ui/floating_point_mul_add.rs:98:13 | LL | let _ = 2.0 * x + 0.5; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:83:13 + --> tests/ui/floating_point_mul_add.rs:101:13 | LL | let _ = x + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:87:13 + --> tests/ui/floating_point_mul_add.rs:105:13 | LL | let _ = y * 2.0 + 0.5; | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:89:13 + --> tests/ui/floating_point_mul_add.rs:107:13 | LL | let _ = 1.0 * 2.0 + 0.5; | ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)` -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From 962404697732f3e5d3eb2efdf1782189fae9f2ae Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Tue, 24 Jun 2025 19:51:24 +0200 Subject: deny AMBIGUOUS_GLOB_IMPORTS; tests fail in this commit --- compiler/rustc_lint_defs/src/builtin.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 5fa00fcc4a0..582ddec7676 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4343,11 +4343,12 @@ declare_lint! { /// /// [future-incompatible]: ../index.md#future-incompatible-lints pub AMBIGUOUS_GLOB_IMPORTS, - Warn, + Deny, "detects certain glob imports that require reporting an ambiguity error", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseError, reference: "issue #114095 ", + report_in_deps: true, }; } -- cgit 1.4.1-3-g733a5 From 3be0b6b72cb41eaf1094f0b19b3e54fb2d722968 Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Tue, 24 Jun 2025 21:08:15 +0200 Subject: update ui/import tests --- tests/ui/imports/ambiguous-1.rs | 3 +- tests/ui/imports/ambiguous-1.stderr | 38 +++++++++--- tests/ui/imports/ambiguous-10.rs | 3 +- tests/ui/imports/ambiguous-10.stderr | 36 +++++++++-- tests/ui/imports/ambiguous-12.rs | 3 +- tests/ui/imports/ambiguous-12.stderr | 36 +++++++++-- tests/ui/imports/ambiguous-13.rs | 3 +- tests/ui/imports/ambiguous-13.stderr | 36 +++++++++-- tests/ui/imports/ambiguous-14.rs | 3 +- tests/ui/imports/ambiguous-14.stderr | 36 +++++++++-- tests/ui/imports/ambiguous-15.rs | 3 +- tests/ui/imports/ambiguous-15.stderr | 36 +++++++++-- tests/ui/imports/ambiguous-16.rs | 3 +- tests/ui/imports/ambiguous-16.stderr | 36 +++++++++-- tests/ui/imports/ambiguous-17.rs | 3 +- tests/ui/imports/ambiguous-17.stderr | 38 +++++++++--- tests/ui/imports/ambiguous-2.rs | 3 +- tests/ui/imports/ambiguous-3.rs | 3 +- tests/ui/imports/ambiguous-3.stderr | 36 +++++++++-- tests/ui/imports/ambiguous-4-extern.rs | 3 +- tests/ui/imports/ambiguous-4-extern.stderr | 38 +++++++++--- tests/ui/imports/ambiguous-4.rs | 3 +- tests/ui/imports/ambiguous-5.rs | 3 +- tests/ui/imports/ambiguous-5.stderr | 36 +++++++++-- tests/ui/imports/ambiguous-6.rs | 3 +- tests/ui/imports/ambiguous-6.stderr | 36 +++++++++-- tests/ui/imports/ambiguous-9.rs | 5 +- tests/ui/imports/ambiguous-9.stderr | 72 ++++++++++++++++++---- tests/ui/imports/duplicate.rs | 2 +- tests/ui/imports/duplicate.stderr | 30 ++++++++- tests/ui/imports/unresolved-seg-after-ambiguous.rs | 2 +- .../imports/unresolved-seg-after-ambiguous.stderr | 30 ++++++++- 32 files changed, 497 insertions(+), 124 deletions(-) diff --git a/tests/ui/imports/ambiguous-1.rs b/tests/ui/imports/ambiguous-1.rs index d175444c0f2..dca26b17466 100644 --- a/tests/ui/imports/ambiguous-1.rs +++ b/tests/ui/imports/ambiguous-1.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/112743#issuecomment-1601986883 macro_rules! m { @@ -25,6 +24,6 @@ pub use openssl::*; fn main() { id(); - //~^ WARNING `id` is ambiguous + //~^ ERROR `id` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-1.stderr b/tests/ui/imports/ambiguous-1.stderr index 61b3077c354..c4b7fc41932 100644 --- a/tests/ui/imports/ambiguous-1.stderr +++ b/tests/ui/imports/ambiguous-1.stderr @@ -1,5 +1,5 @@ warning: ambiguous glob re-exports - --> $DIR/ambiguous-1.rs:11:13 + --> $DIR/ambiguous-1.rs:10:13 | LL | pub use self::evp::*; | ^^^^^^^^^^^^ the name `id` in the value namespace is first re-exported here @@ -9,8 +9,8 @@ LL | pub use self::handwritten::*; | = note: `#[warn(ambiguous_glob_reexports)]` on by default -warning: `id` is ambiguous - --> $DIR/ambiguous-1.rs:27:5 +error: `id` is ambiguous + --> $DIR/ambiguous-1.rs:26:5 | LL | id(); | ^^ ambiguous name @@ -19,18 +19,42 @@ LL | id(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function imported here - --> $DIR/ambiguous-1.rs:11:13 + --> $DIR/ambiguous-1.rs:10:13 | LL | pub use self::evp::*; | ^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate note: `id` could also refer to the function imported here - --> $DIR/ambiguous-1.rs:13:13 + --> $DIR/ambiguous-1.rs:12:13 | LL | pub use self::handwritten::*; | ^^^^^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default -warning: 2 warnings emitted +error: aborting due to 1 previous error; 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +error: `id` is ambiguous + --> $DIR/ambiguous-1.rs:26:5 + | +LL | id(); + | ^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `id` could refer to the function imported here + --> $DIR/ambiguous-1.rs:10:13 + | +LL | pub use self::evp::*; + | ^^^^^^^^^^^^ + = help: consider adding an explicit import of `id` to disambiguate +note: `id` could also refer to the function imported here + --> $DIR/ambiguous-1.rs:12:13 + | +LL | pub use self::handwritten::*; + | ^^^^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `id` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-10.rs b/tests/ui/imports/ambiguous-10.rs index 7c14e3343eb..166b01ede12 100644 --- a/tests/ui/imports/ambiguous-10.rs +++ b/tests/ui/imports/ambiguous-10.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/113099#issuecomment-1637022296 mod a { @@ -14,6 +13,6 @@ mod b { use crate::a::*; use crate::b::*; fn c(_: Token) {} -//~^ WARNING `Token` is ambiguous +//~^ ERROR `Token` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! fn main() { } diff --git a/tests/ui/imports/ambiguous-10.stderr b/tests/ui/imports/ambiguous-10.stderr index 704af616b43..cd36795b3c0 100644 --- a/tests/ui/imports/ambiguous-10.stderr +++ b/tests/ui/imports/ambiguous-10.stderr @@ -1,5 +1,5 @@ -warning: `Token` is ambiguous - --> $DIR/ambiguous-10.rs:16:9 +error: `Token` is ambiguous + --> $DIR/ambiguous-10.rs:15:9 | LL | fn c(_: Token) {} | ^^^^^ ambiguous name @@ -8,18 +8,42 @@ LL | fn c(_: Token) {} = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `Token` could refer to the enum imported here - --> $DIR/ambiguous-10.rs:14:5 + --> $DIR/ambiguous-10.rs:13:5 | LL | use crate::a::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `Token` to disambiguate note: `Token` could also refer to the enum imported here - --> $DIR/ambiguous-10.rs:15:5 + --> $DIR/ambiguous-10.rs:14:5 | LL | use crate::b::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `Token` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default + +error: aborting due to 1 previous error -warning: 1 warning emitted +Future incompatibility report: Future breakage diagnostic: +error: `Token` is ambiguous + --> $DIR/ambiguous-10.rs:15:9 + | +LL | fn c(_: Token) {} + | ^^^^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `Token` could refer to the enum imported here + --> $DIR/ambiguous-10.rs:13:5 + | +LL | use crate::a::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `Token` to disambiguate +note: `Token` could also refer to the enum imported here + --> $DIR/ambiguous-10.rs:14:5 + | +LL | use crate::b::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `Token` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-12.rs b/tests/ui/imports/ambiguous-12.rs index a033b51f709..543396b8dfe 100644 --- a/tests/ui/imports/ambiguous-12.rs +++ b/tests/ui/imports/ambiguous-12.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/113099#issuecomment-1637022296 macro_rules! m { @@ -20,6 +19,6 @@ use crate::public::*; fn main() { b(); - //~^ WARNING `b` is ambiguous + //~^ ERROR `b` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-12.stderr b/tests/ui/imports/ambiguous-12.stderr index 4725c38849c..273a4ed3c0f 100644 --- a/tests/ui/imports/ambiguous-12.stderr +++ b/tests/ui/imports/ambiguous-12.stderr @@ -1,5 +1,5 @@ -warning: `b` is ambiguous - --> $DIR/ambiguous-12.rs:22:5 +error: `b` is ambiguous + --> $DIR/ambiguous-12.rs:21:5 | LL | b(); | ^ ambiguous name @@ -8,18 +8,42 @@ LL | b(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `b` could refer to the function imported here - --> $DIR/ambiguous-12.rs:18:5 + --> $DIR/ambiguous-12.rs:17:5 | LL | use crate::ciphertext::*; | ^^^^^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `b` to disambiguate note: `b` could also refer to the function imported here - --> $DIR/ambiguous-12.rs:19:5 + --> $DIR/ambiguous-12.rs:18:5 | LL | use crate::public::*; | ^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `b` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default + +error: aborting due to 1 previous error -warning: 1 warning emitted +Future incompatibility report: Future breakage diagnostic: +error: `b` is ambiguous + --> $DIR/ambiguous-12.rs:21:5 + | +LL | b(); + | ^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `b` could refer to the function imported here + --> $DIR/ambiguous-12.rs:17:5 + | +LL | use crate::ciphertext::*; + | ^^^^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `b` to disambiguate +note: `b` could also refer to the function imported here + --> $DIR/ambiguous-12.rs:18:5 + | +LL | use crate::public::*; + | ^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `b` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-13.rs b/tests/ui/imports/ambiguous-13.rs index 1ea04e05d57..3569dd5d9ad 100644 --- a/tests/ui/imports/ambiguous-13.rs +++ b/tests/ui/imports/ambiguous-13.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/113099#issuecomment-1637022296 pub mod object { @@ -17,6 +16,6 @@ use crate::object::*; use crate::content::*; fn a(_: Rect) {} -//~^ WARNING `Rect` is ambiguous +//~^ ERROR `Rect` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! fn main() { } diff --git a/tests/ui/imports/ambiguous-13.stderr b/tests/ui/imports/ambiguous-13.stderr index 3e78100b658..c4a42c01c91 100644 --- a/tests/ui/imports/ambiguous-13.stderr +++ b/tests/ui/imports/ambiguous-13.stderr @@ -1,5 +1,5 @@ -warning: `Rect` is ambiguous - --> $DIR/ambiguous-13.rs:19:9 +error: `Rect` is ambiguous + --> $DIR/ambiguous-13.rs:18:9 | LL | fn a(_: Rect) {} | ^^^^ ambiguous name @@ -8,18 +8,42 @@ LL | fn a(_: Rect) {} = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `Rect` could refer to the struct imported here - --> $DIR/ambiguous-13.rs:16:5 + --> $DIR/ambiguous-13.rs:15:5 | LL | use crate::object::*; | ^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `Rect` to disambiguate note: `Rect` could also refer to the struct imported here - --> $DIR/ambiguous-13.rs:17:5 + --> $DIR/ambiguous-13.rs:16:5 | LL | use crate::content::*; | ^^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `Rect` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default + +error: aborting due to 1 previous error -warning: 1 warning emitted +Future incompatibility report: Future breakage diagnostic: +error: `Rect` is ambiguous + --> $DIR/ambiguous-13.rs:18:9 + | +LL | fn a(_: Rect) {} + | ^^^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `Rect` could refer to the struct imported here + --> $DIR/ambiguous-13.rs:15:5 + | +LL | use crate::object::*; + | ^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `Rect` to disambiguate +note: `Rect` could also refer to the struct imported here + --> $DIR/ambiguous-13.rs:16:5 + | +LL | use crate::content::*; + | ^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `Rect` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-14.rs b/tests/ui/imports/ambiguous-14.rs index 30d14be9d0e..f752387aa7e 100644 --- a/tests/ui/imports/ambiguous-14.rs +++ b/tests/ui/imports/ambiguous-14.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/issues/98467 mod a { @@ -21,6 +20,6 @@ mod g { fn main() { g::foo(); - //~^ WARNING `foo` is ambiguous + //~^ ERROR `foo` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-14.stderr b/tests/ui/imports/ambiguous-14.stderr index bece5853668..f3115f8c8b5 100644 --- a/tests/ui/imports/ambiguous-14.stderr +++ b/tests/ui/imports/ambiguous-14.stderr @@ -1,5 +1,5 @@ -warning: `foo` is ambiguous - --> $DIR/ambiguous-14.rs:23:8 +error: `foo` is ambiguous + --> $DIR/ambiguous-14.rs:22:8 | LL | g::foo(); | ^^^ ambiguous name @@ -8,18 +8,42 @@ LL | g::foo(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `foo` could refer to the function imported here - --> $DIR/ambiguous-14.rs:13:13 + --> $DIR/ambiguous-14.rs:12:13 | LL | pub use a::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate note: `foo` could also refer to the function imported here - --> $DIR/ambiguous-14.rs:14:13 + --> $DIR/ambiguous-14.rs:13:13 | LL | pub use b::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default + +error: aborting due to 1 previous error -warning: 1 warning emitted +Future incompatibility report: Future breakage diagnostic: +error: `foo` is ambiguous + --> $DIR/ambiguous-14.rs:22:8 + | +LL | g::foo(); + | ^^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `foo` could refer to the function imported here + --> $DIR/ambiguous-14.rs:12:13 + | +LL | pub use a::*; + | ^^^^ + = help: consider adding an explicit import of `foo` to disambiguate +note: `foo` could also refer to the function imported here + --> $DIR/ambiguous-14.rs:13:13 + | +LL | pub use b::*; + | ^^^^ + = help: consider adding an explicit import of `foo` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-15.rs b/tests/ui/imports/ambiguous-15.rs index b9e8f020d43..07d8893b2de 100644 --- a/tests/ui/imports/ambiguous-15.rs +++ b/tests/ui/imports/ambiguous-15.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/113099#issuecomment-1638206152 mod t2 { @@ -21,7 +20,7 @@ mod t3 { use self::t3::*; fn a(_: E) {} -//~^ WARNING `Error` is ambiguous +//~^ ERROR `Error` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! fn main() {} diff --git a/tests/ui/imports/ambiguous-15.stderr b/tests/ui/imports/ambiguous-15.stderr index 838256752d0..1312f2c63c4 100644 --- a/tests/ui/imports/ambiguous-15.stderr +++ b/tests/ui/imports/ambiguous-15.stderr @@ -1,5 +1,5 @@ -warning: `Error` is ambiguous - --> $DIR/ambiguous-15.rs:23:9 +error: `Error` is ambiguous + --> $DIR/ambiguous-15.rs:22:9 | LL | fn a(_: E) {} | ^^^^^ ambiguous name @@ -8,18 +8,42 @@ LL | fn a(_: E) {} = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `Error` could refer to the trait imported here - --> $DIR/ambiguous-15.rs:22:5 + --> $DIR/ambiguous-15.rs:21:5 | LL | use self::t3::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `Error` to disambiguate note: `Error` could also refer to the enum imported here - --> $DIR/ambiguous-15.rs:16:9 + --> $DIR/ambiguous-15.rs:15:9 | LL | pub use t2::*; | ^^^^^ = help: consider adding an explicit import of `Error` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default -warning: 1 warning emitted +error: aborting due to 1 previous error + +Future incompatibility report: Future breakage diagnostic: +error: `Error` is ambiguous + --> $DIR/ambiguous-15.rs:22:9 + | +LL | fn a(_: E) {} + | ^^^^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `Error` could refer to the trait imported here + --> $DIR/ambiguous-15.rs:21:5 + | +LL | use self::t3::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `Error` to disambiguate +note: `Error` could also refer to the enum imported here + --> $DIR/ambiguous-15.rs:15:9 + | +LL | pub use t2::*; + | ^^^^^ + = help: consider adding an explicit import of `Error` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-16.rs b/tests/ui/imports/ambiguous-16.rs index ed30c9d241a..f31c78d18a3 100644 --- a/tests/ui/imports/ambiguous-16.rs +++ b/tests/ui/imports/ambiguous-16.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/113099 mod framing { @@ -21,7 +20,7 @@ mod framing { } use crate::framing::ConfirmedTranscriptHashInput; -//~^ WARNING `ConfirmedTranscriptHashInput` is ambiguous +//~^ ERROR `ConfirmedTranscriptHashInput` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! fn main() { } diff --git a/tests/ui/imports/ambiguous-16.stderr b/tests/ui/imports/ambiguous-16.stderr index 7366cabc47a..ae65f9a84fc 100644 --- a/tests/ui/imports/ambiguous-16.stderr +++ b/tests/ui/imports/ambiguous-16.stderr @@ -1,5 +1,5 @@ -warning: `ConfirmedTranscriptHashInput` is ambiguous - --> $DIR/ambiguous-16.rs:23:21 +error: `ConfirmedTranscriptHashInput` is ambiguous + --> $DIR/ambiguous-16.rs:22:21 | LL | use crate::framing::ConfirmedTranscriptHashInput; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ambiguous name @@ -8,18 +8,42 @@ LL | use crate::framing::ConfirmedTranscriptHashInput; = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `ConfirmedTranscriptHashInput` could refer to the struct imported here - --> $DIR/ambiguous-16.rs:19:13 + --> $DIR/ambiguous-16.rs:18:13 | LL | pub use self::public_message::*; | ^^^^^^^^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `ConfirmedTranscriptHashInput` to disambiguate note: `ConfirmedTranscriptHashInput` could also refer to the struct imported here - --> $DIR/ambiguous-16.rs:20:13 + --> $DIR/ambiguous-16.rs:19:13 | LL | pub use self::public_message_in::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `ConfirmedTranscriptHashInput` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default + +error: aborting due to 1 previous error -warning: 1 warning emitted +Future incompatibility report: Future breakage diagnostic: +error: `ConfirmedTranscriptHashInput` is ambiguous + --> $DIR/ambiguous-16.rs:22:21 + | +LL | use crate::framing::ConfirmedTranscriptHashInput; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `ConfirmedTranscriptHashInput` could refer to the struct imported here + --> $DIR/ambiguous-16.rs:18:13 + | +LL | pub use self::public_message::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `ConfirmedTranscriptHashInput` to disambiguate +note: `ConfirmedTranscriptHashInput` could also refer to the struct imported here + --> $DIR/ambiguous-16.rs:19:13 + | +LL | pub use self::public_message_in::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `ConfirmedTranscriptHashInput` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-17.rs b/tests/ui/imports/ambiguous-17.rs index 28c9c1cc864..520ac70c6f1 100644 --- a/tests/ui/imports/ambiguous-17.rs +++ b/tests/ui/imports/ambiguous-17.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/113099#issuecomment-1638206152 pub use evp::*; //~ WARNING ambiguous glob re-exports @@ -24,6 +23,6 @@ mod handwritten { fn main() { id(); - //~^ WARNING `id` is ambiguous + //~^ ERROR `id` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-17.stderr b/tests/ui/imports/ambiguous-17.stderr index 55bc01095c7..a87e2572d63 100644 --- a/tests/ui/imports/ambiguous-17.stderr +++ b/tests/ui/imports/ambiguous-17.stderr @@ -1,5 +1,5 @@ warning: ambiguous glob re-exports - --> $DIR/ambiguous-17.rs:4:9 + --> $DIR/ambiguous-17.rs:3:9 | LL | pub use evp::*; | ^^^^^^ the name `id` in the value namespace is first re-exported here @@ -8,8 +8,8 @@ LL | pub use handwritten::*; | = note: `#[warn(ambiguous_glob_reexports)]` on by default -warning: `id` is ambiguous - --> $DIR/ambiguous-17.rs:26:5 +error: `id` is ambiguous + --> $DIR/ambiguous-17.rs:25:5 | LL | id(); | ^^ ambiguous name @@ -18,18 +18,42 @@ LL | id(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function imported here - --> $DIR/ambiguous-17.rs:4:9 + --> $DIR/ambiguous-17.rs:3:9 | LL | pub use evp::*; | ^^^^^^ = help: consider adding an explicit import of `id` to disambiguate note: `id` could also refer to the function imported here - --> $DIR/ambiguous-17.rs:5:9 + --> $DIR/ambiguous-17.rs:4:9 | LL | pub use handwritten::*; | ^^^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default -warning: 2 warnings emitted +error: aborting due to 1 previous error; 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +error: `id` is ambiguous + --> $DIR/ambiguous-17.rs:25:5 + | +LL | id(); + | ^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `id` could refer to the function imported here + --> $DIR/ambiguous-17.rs:3:9 + | +LL | pub use evp::*; + | ^^^^^^ + = help: consider adding an explicit import of `id` to disambiguate +note: `id` could also refer to the function imported here + --> $DIR/ambiguous-17.rs:4:9 + | +LL | pub use handwritten::*; + | ^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `id` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-2.rs b/tests/ui/imports/ambiguous-2.rs index 087431485ad..cffdcb9d922 100644 --- a/tests/ui/imports/ambiguous-2.rs +++ b/tests/ui/imports/ambiguous-2.rs @@ -1,4 +1,5 @@ -//@ check-pass +//@ ignore-test +// ^^ don't know how to change this test //@ aux-build: ../ambiguous-1.rs // https://github.com/rust-lang/rust/pull/113099#issuecomment-1633574396 diff --git a/tests/ui/imports/ambiguous-3.rs b/tests/ui/imports/ambiguous-3.rs index aa98ffe395e..ff0dcc221ec 100644 --- a/tests/ui/imports/ambiguous-3.rs +++ b/tests/ui/imports/ambiguous-3.rs @@ -1,10 +1,9 @@ -//@ check-pass // https://github.com/rust-lang/rust/issues/47525 fn main() { use a::*; x(); - //~^ WARNING `x` is ambiguous + //~^ ERROR `x` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-3.stderr b/tests/ui/imports/ambiguous-3.stderr index f019f6d8957..8766db5654a 100644 --- a/tests/ui/imports/ambiguous-3.stderr +++ b/tests/ui/imports/ambiguous-3.stderr @@ -1,5 +1,5 @@ -warning: `x` is ambiguous - --> $DIR/ambiguous-3.rs:6:5 +error: `x` is ambiguous + --> $DIR/ambiguous-3.rs:5:5 | LL | x(); | ^ ambiguous name @@ -8,18 +8,42 @@ LL | x(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `x` could refer to the function imported here - --> $DIR/ambiguous-3.rs:19:13 + --> $DIR/ambiguous-3.rs:18:13 | LL | pub use self::b::*; | ^^^^^^^^^^ = help: consider adding an explicit import of `x` to disambiguate note: `x` could also refer to the function imported here - --> $DIR/ambiguous-3.rs:20:13 + --> $DIR/ambiguous-3.rs:19:13 | LL | pub use self::c::*; | ^^^^^^^^^^ = help: consider adding an explicit import of `x` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default + +error: aborting due to 1 previous error -warning: 1 warning emitted +Future incompatibility report: Future breakage diagnostic: +error: `x` is ambiguous + --> $DIR/ambiguous-3.rs:5:5 + | +LL | x(); + | ^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `x` could refer to the function imported here + --> $DIR/ambiguous-3.rs:18:13 + | +LL | pub use self::b::*; + | ^^^^^^^^^^ + = help: consider adding an explicit import of `x` to disambiguate +note: `x` could also refer to the function imported here + --> $DIR/ambiguous-3.rs:19:13 + | +LL | pub use self::c::*; + | ^^^^^^^^^^ + = help: consider adding an explicit import of `x` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-4-extern.rs b/tests/ui/imports/ambiguous-4-extern.rs index a045ab3d8a5..b70d0484801 100644 --- a/tests/ui/imports/ambiguous-4-extern.rs +++ b/tests/ui/imports/ambiguous-4-extern.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/112743#issuecomment-1601986883 macro_rules! m { @@ -21,6 +20,6 @@ mod handwritten { fn main() { id(); - //~^ WARNING `id` is ambiguous + //~^ ERROR `id` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-4-extern.stderr b/tests/ui/imports/ambiguous-4-extern.stderr index 0011973212b..3e79c455c42 100644 --- a/tests/ui/imports/ambiguous-4-extern.stderr +++ b/tests/ui/imports/ambiguous-4-extern.stderr @@ -1,5 +1,5 @@ warning: ambiguous glob re-exports - --> $DIR/ambiguous-4-extern.rs:10:9 + --> $DIR/ambiguous-4-extern.rs:9:9 | LL | pub use evp::*; | ^^^^^^ the name `id` in the value namespace is first re-exported here @@ -8,8 +8,8 @@ LL | pub use handwritten::*; | = note: `#[warn(ambiguous_glob_reexports)]` on by default -warning: `id` is ambiguous - --> $DIR/ambiguous-4-extern.rs:23:5 +error: `id` is ambiguous + --> $DIR/ambiguous-4-extern.rs:22:5 | LL | id(); | ^^ ambiguous name @@ -18,18 +18,42 @@ LL | id(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function imported here - --> $DIR/ambiguous-4-extern.rs:10:9 + --> $DIR/ambiguous-4-extern.rs:9:9 | LL | pub use evp::*; | ^^^^^^ = help: consider adding an explicit import of `id` to disambiguate note: `id` could also refer to the function imported here - --> $DIR/ambiguous-4-extern.rs:11:9 + --> $DIR/ambiguous-4-extern.rs:10:9 | LL | pub use handwritten::*; | ^^^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default -warning: 2 warnings emitted +error: aborting due to 1 previous error; 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +error: `id` is ambiguous + --> $DIR/ambiguous-4-extern.rs:22:5 + | +LL | id(); + | ^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `id` could refer to the function imported here + --> $DIR/ambiguous-4-extern.rs:9:9 + | +LL | pub use evp::*; + | ^^^^^^ + = help: consider adding an explicit import of `id` to disambiguate +note: `id` could also refer to the function imported here + --> $DIR/ambiguous-4-extern.rs:10:9 + | +LL | pub use handwritten::*; + | ^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `id` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-4.rs b/tests/ui/imports/ambiguous-4.rs index fcb7b5c6671..e367c120adf 100644 --- a/tests/ui/imports/ambiguous-4.rs +++ b/tests/ui/imports/ambiguous-4.rs @@ -1,4 +1,5 @@ -//@ build-pass +//@ ignore-test +// ^^ don't know how to change this test //@ aux-build: ../ambiguous-4-extern.rs extern crate ambiguous_4_extern; diff --git a/tests/ui/imports/ambiguous-5.rs b/tests/ui/imports/ambiguous-5.rs index 28447e10d1b..8f89c966d4a 100644 --- a/tests/ui/imports/ambiguous-5.rs +++ b/tests/ui/imports/ambiguous-5.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/113099#issuecomment-1637022296 mod a { @@ -11,7 +10,7 @@ mod gpos { use super::gsubgpos::*; use super::*; struct MarkRecord(Class); - //~^ WARNING `Class` is ambiguous + //~^ ERROR`Class` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-5.stderr b/tests/ui/imports/ambiguous-5.stderr index 4bc35f86d3a..41c15809351 100644 --- a/tests/ui/imports/ambiguous-5.stderr +++ b/tests/ui/imports/ambiguous-5.stderr @@ -1,5 +1,5 @@ -warning: `Class` is ambiguous - --> $DIR/ambiguous-5.rs:13:23 +error: `Class` is ambiguous + --> $DIR/ambiguous-5.rs:12:23 | LL | struct MarkRecord(Class); | ^^^^^ ambiguous name @@ -8,18 +8,42 @@ LL | struct MarkRecord(Class); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `Class` could refer to the struct imported here - --> $DIR/ambiguous-5.rs:12:9 + --> $DIR/ambiguous-5.rs:11:9 | LL | use super::*; | ^^^^^^^^ = help: consider adding an explicit import of `Class` to disambiguate note: `Class` could also refer to the struct imported here - --> $DIR/ambiguous-5.rs:11:9 + --> $DIR/ambiguous-5.rs:10:9 | LL | use super::gsubgpos::*; | ^^^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `Class` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default + +error: aborting due to 1 previous error -warning: 1 warning emitted +Future incompatibility report: Future breakage diagnostic: +error: `Class` is ambiguous + --> $DIR/ambiguous-5.rs:12:23 + | +LL | struct MarkRecord(Class); + | ^^^^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `Class` could refer to the struct imported here + --> $DIR/ambiguous-5.rs:11:9 + | +LL | use super::*; + | ^^^^^^^^ + = help: consider adding an explicit import of `Class` to disambiguate +note: `Class` could also refer to the struct imported here + --> $DIR/ambiguous-5.rs:10:9 + | +LL | use super::gsubgpos::*; + | ^^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `Class` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-6.rs b/tests/ui/imports/ambiguous-6.rs index 955cdc3854f..1c6e3437716 100644 --- a/tests/ui/imports/ambiguous-6.rs +++ b/tests/ui/imports/ambiguous-6.rs @@ -1,11 +1,10 @@ -//@ check-pass //@ edition: 2021 // https://github.com/rust-lang/rust/issues/112713 pub fn foo() -> u32 { use sub::*; C - //~^ WARNING `C` is ambiguous + //~^ ERROR `C` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-6.stderr b/tests/ui/imports/ambiguous-6.stderr index d7871a0b8cb..d988126dbfb 100644 --- a/tests/ui/imports/ambiguous-6.stderr +++ b/tests/ui/imports/ambiguous-6.stderr @@ -1,5 +1,5 @@ -warning: `C` is ambiguous - --> $DIR/ambiguous-6.rs:7:5 +error: `C` is ambiguous + --> $DIR/ambiguous-6.rs:6:5 | LL | C | ^ ambiguous name @@ -8,18 +8,42 @@ LL | C = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `C` could refer to the constant imported here - --> $DIR/ambiguous-6.rs:16:13 + --> $DIR/ambiguous-6.rs:15:13 | LL | pub use mod1::*; | ^^^^^^^ = help: consider adding an explicit import of `C` to disambiguate note: `C` could also refer to the constant imported here - --> $DIR/ambiguous-6.rs:17:13 + --> $DIR/ambiguous-6.rs:16:13 | LL | pub use mod2::*; | ^^^^^^^ = help: consider adding an explicit import of `C` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default + +error: aborting due to 1 previous error -warning: 1 warning emitted +Future incompatibility report: Future breakage diagnostic: +error: `C` is ambiguous + --> $DIR/ambiguous-6.rs:6:5 + | +LL | C + | ^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `C` could refer to the constant imported here + --> $DIR/ambiguous-6.rs:15:13 + | +LL | pub use mod1::*; + | ^^^^^^^ + = help: consider adding an explicit import of `C` to disambiguate +note: `C` could also refer to the constant imported here + --> $DIR/ambiguous-6.rs:16:13 + | +LL | pub use mod2::*; + | ^^^^^^^ + = help: consider adding an explicit import of `C` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/ambiguous-9.rs b/tests/ui/imports/ambiguous-9.rs index 97321512df0..c10b1268060 100644 --- a/tests/ui/imports/ambiguous-9.rs +++ b/tests/ui/imports/ambiguous-9.rs @@ -1,4 +1,3 @@ -//@ check-pass // https://github.com/rust-lang/rust/pull/113099#issuecomment-1638206152 pub mod dsl { @@ -22,8 +21,8 @@ use prelude::*; fn main() { date_range(); - //~^ WARNING `date_range` is ambiguous + //~^ ERROR `date_range` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - //~| WARNING `date_range` is ambiguous + //~| ERROR `date_range` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-9.stderr b/tests/ui/imports/ambiguous-9.stderr index 6c7d79174da..1c4768da827 100644 --- a/tests/ui/imports/ambiguous-9.stderr +++ b/tests/ui/imports/ambiguous-9.stderr @@ -1,5 +1,5 @@ warning: ambiguous glob re-exports - --> $DIR/ambiguous-9.rs:8:13 + --> $DIR/ambiguous-9.rs:7:13 | LL | pub use self::range::*; | ^^^^^^^^^^^^^^ the name `date_range` in the value namespace is first re-exported here @@ -8,8 +8,8 @@ LL | use super::prelude::*; | = note: `#[warn(ambiguous_glob_reexports)]` on by default -warning: `date_range` is ambiguous - --> $DIR/ambiguous-9.rs:24:5 +error: `date_range` is ambiguous + --> $DIR/ambiguous-9.rs:23:5 | LL | date_range(); | ^^^^^^^^^^ ambiguous name @@ -18,29 +18,29 @@ LL | date_range(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `date_range` could refer to the function imported here - --> $DIR/ambiguous-9.rs:8:13 + --> $DIR/ambiguous-9.rs:7:13 | LL | pub use self::range::*; | ^^^^^^^^^^^^^^ = help: consider adding an explicit import of `date_range` to disambiguate note: `date_range` could also refer to the function imported here - --> $DIR/ambiguous-9.rs:9:9 + --> $DIR/ambiguous-9.rs:8:9 | LL | use super::prelude::*; | ^^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `date_range` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default warning: ambiguous glob re-exports - --> $DIR/ambiguous-9.rs:16:13 + --> $DIR/ambiguous-9.rs:15:13 | LL | pub use self::t::*; | ^^^^^^^^^^ the name `date_range` in the value namespace is first re-exported here LL | pub use super::dsl::*; | ------------- but the name `date_range` in the value namespace is also re-exported here -warning: `date_range` is ambiguous - --> $DIR/ambiguous-9.rs:24:5 +error: `date_range` is ambiguous + --> $DIR/ambiguous-9.rs:23:5 | LL | date_range(); | ^^^^^^^^^^ ambiguous name @@ -49,17 +49,65 @@ LL | date_range(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `date_range` could refer to the function imported here - --> $DIR/ambiguous-9.rs:20:5 + --> $DIR/ambiguous-9.rs:19:5 | LL | use dsl::*; | ^^^^^^ = help: consider adding an explicit import of `date_range` to disambiguate note: `date_range` could also refer to the function imported here - --> $DIR/ambiguous-9.rs:21:5 + --> $DIR/ambiguous-9.rs:20:5 | LL | use prelude::*; | ^^^^^^^^^^ = help: consider adding an explicit import of `date_range` to disambiguate -warning: 4 warnings emitted +error: aborting due to 2 previous errors; 2 warnings emitted + +Future incompatibility report: Future breakage diagnostic: +error: `date_range` is ambiguous + --> $DIR/ambiguous-9.rs:23:5 + | +LL | date_range(); + | ^^^^^^^^^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `date_range` could refer to the function imported here + --> $DIR/ambiguous-9.rs:7:13 + | +LL | pub use self::range::*; + | ^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `date_range` to disambiguate +note: `date_range` could also refer to the function imported here + --> $DIR/ambiguous-9.rs:8:9 + | +LL | use super::prelude::*; + | ^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `date_range` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default + +Future breakage diagnostic: +error: `date_range` is ambiguous + --> $DIR/ambiguous-9.rs:23:5 + | +LL | date_range(); + | ^^^^^^^^^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `date_range` could refer to the function imported here + --> $DIR/ambiguous-9.rs:19:5 + | +LL | use dsl::*; + | ^^^^^^ + = help: consider adding an explicit import of `date_range` to disambiguate +note: `date_range` could also refer to the function imported here + --> $DIR/ambiguous-9.rs:20:5 + | +LL | use prelude::*; + | ^^^^^^^^^^ + = help: consider adding an explicit import of `date_range` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default diff --git a/tests/ui/imports/duplicate.rs b/tests/ui/imports/duplicate.rs index 69ec82aafbd..0a652889ca8 100644 --- a/tests/ui/imports/duplicate.rs +++ b/tests/ui/imports/duplicate.rs @@ -34,7 +34,7 @@ fn main() { e::foo(); f::foo(); //~ ERROR `foo` is ambiguous g::foo(); - //~^ WARNING `foo` is ambiguous + //~^ ERROR `foo` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/duplicate.stderr b/tests/ui/imports/duplicate.stderr index f7dc7312b9d..ef987d07c04 100644 --- a/tests/ui/imports/duplicate.stderr +++ b/tests/ui/imports/duplicate.stderr @@ -68,7 +68,7 @@ LL | use self::m2::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate -warning: `foo` is ambiguous +error: `foo` is ambiguous --> $DIR/duplicate.rs:36:8 | LL | g::foo(); @@ -89,9 +89,33 @@ note: `foo` could also refer to the function imported here LL | pub use crate::b::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 5 previous errors Some errors have detailed explanations: E0252, E0659. For more information about an error, try `rustc --explain E0252`. +Future incompatibility report: Future breakage diagnostic: +error: `foo` is ambiguous + --> $DIR/duplicate.rs:36:8 + | +LL | g::foo(); + | ^^^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `foo` could refer to the function imported here + --> $DIR/duplicate.rs:24:13 + | +LL | pub use crate::a::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `foo` to disambiguate +note: `foo` could also refer to the function imported here + --> $DIR/duplicate.rs:25:13 + | +LL | pub use crate::b::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `foo` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default + diff --git a/tests/ui/imports/unresolved-seg-after-ambiguous.rs b/tests/ui/imports/unresolved-seg-after-ambiguous.rs index dcabc528a85..67366deabaa 100644 --- a/tests/ui/imports/unresolved-seg-after-ambiguous.rs +++ b/tests/ui/imports/unresolved-seg-after-ambiguous.rs @@ -18,7 +18,7 @@ mod a { use self::a::E::in_exist; //~^ ERROR: unresolved import `self::a::E` -//~| WARNING: `E` is ambiguous +//~| ERROR: `E` is ambiguous //~| WARNING: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! fn main() {} diff --git a/tests/ui/imports/unresolved-seg-after-ambiguous.stderr b/tests/ui/imports/unresolved-seg-after-ambiguous.stderr index 9e0efd4a75f..3b50ae32683 100644 --- a/tests/ui/imports/unresolved-seg-after-ambiguous.stderr +++ b/tests/ui/imports/unresolved-seg-after-ambiguous.stderr @@ -4,7 +4,7 @@ error[E0432]: unresolved import `self::a::E` LL | use self::a::E::in_exist; | ^ `E` is a struct, not a module -warning: `E` is ambiguous +error: `E` is ambiguous --> $DIR/unresolved-seg-after-ambiguous.rs:19:14 | LL | use self::a::E::in_exist; @@ -25,8 +25,32 @@ note: `E` could also refer to the struct imported here LL | pub use self::d::*; | ^^^^^^^^^^ = help: consider adding an explicit import of `E` to disambiguate - = note: `#[warn(ambiguous_glob_imports)]` on by default + = note: `#[deny(ambiguous_glob_imports)]` on by default -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0432`. +Future incompatibility report: Future breakage diagnostic: +error: `E` is ambiguous + --> $DIR/unresolved-seg-after-ambiguous.rs:19:14 + | +LL | use self::a::E::in_exist; + | ^ ambiguous name + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `E` could refer to the struct imported here + --> $DIR/unresolved-seg-after-ambiguous.rs:13:17 + | +LL | pub use self::c::*; + | ^^^^^^^^^^ + = help: consider adding an explicit import of `E` to disambiguate +note: `E` could also refer to the struct imported here + --> $DIR/unresolved-seg-after-ambiguous.rs:12:17 + | +LL | pub use self::d::*; + | ^^^^^^^^^^ + = help: consider adding an explicit import of `E` to disambiguate + = note: `#[deny(ambiguous_glob_imports)]` on by default + -- cgit 1.4.1-3-g733a5 From ecb397a3650d9a6001a2c3a7b51f395c1625727a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 25 Jun 2025 20:39:14 +0200 Subject: Reduce page size and number of DOM elements on clippy lints page --- util/gh-pages/index_template.html | 28 ++++++++++++---------------- util/gh-pages/style.css | 19 +++++++++++++++++-- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html index 865b9523c39..458f20d9f99 100644 --- a/util/gh-pages/index_template.html +++ b/util/gh-pages/index_template.html @@ -149,25 +149,21 @@ Otherwise, have a great day =^.^=
{# #} {# #} {# #}
{# #} diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css index 3cc7a919c23..046a2a5df7c 100644 --- a/util/gh-pages/style.css +++ b/util/gh-pages/style.css @@ -50,8 +50,23 @@ div.panel div.panel-body button.open { .panel-heading { cursor: pointer; } -.panel-title { display: flex; flex-wrap: wrap;} -.panel-title .label { display: inline-block; } +.lint-title { + cursor: pointer; + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + display: flex; + flex-wrap: wrap; + background: var(--theme-hover); + color: var(--fg); + border: 1px solid var(--theme-popup-border); + padding: 10px 15px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + gap: 4px; +} + +.lint-title .label { display: inline-block; } .panel-title-name { flex: 1; min-width: 400px;} .panel-title-name span { vertical-align: bottom; } -- cgit 1.4.1-3-g733a5 From 0c0a4d559263cb56e96825be356b540d380d13d4 Mon Sep 17 00:00:00 2001 From: Chengxu Bian Date: Wed, 25 Jun 2025 20:32:22 -0400 Subject: fix closure and refactor tooling code --- clippy_lints/src/floating_point_arithmetic.rs | 66 ++------------------- clippy_utils/src/hir_utils.rs | 82 ++++++++++++++++++++++++--- clippy_utils/src/lib.rs | 3 +- clippy_utils/src/visitors.rs | 10 ++-- tests/ui/floating_point_mul_add.fixed | 10 ++++ tests/ui/floating_point_mul_add.rs | 10 ++++ tests/ui/floating_point_mul_add.stderr | 18 ++---- 7 files changed, 112 insertions(+), 87 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 39b8e8c54fa..d5abaa547e8 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -2,13 +2,12 @@ use clippy_utils::consts::Constant::{F32, F64, Int}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, - numeric_literal, peel_blocks, sugg, sym, + eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, + is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym, }; use rustc_ast::ast; use rustc_errors::Applicability; -use rustc_hir::def::Res; -use rustc_hir::{BinOpKind, Expr, ExprKind, Node, PathSegment, UnOp}; +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; @@ -456,61 +455,6 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&' None } -// Check if any variable in an expression has an ambiguous type (could be f32 or f64) -fn has_ambiguous_float_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match &expr.kind { - ExprKind::Path(qpath) => { - if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id) { - if let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) { - // If the local has no type annotation, check if the initializer has ambiguous float literals - if local.ty.is_none() { - if let Some(init) = local.init { - return has_ambiguous_float_literal_in_expr(cx, init); - } - } - } - } - false - }, - _ => false, // only check path - } -} - -// Recursively check if an expression contains any unsuffixed float literals -fn has_ambiguous_float_literal_in_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match &expr.kind { - ExprKind::Lit(lit) => { - if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node { - return true; - } - false - }, - ExprKind::Binary(_, lhs, rhs) => { - has_ambiguous_float_literal_in_expr(cx, lhs) || has_ambiguous_float_literal_in_expr(cx, rhs) - }, - ExprKind::Unary(_, expr) => has_ambiguous_float_literal_in_expr(cx, expr), - ExprKind::If(_, then, else_) => { - has_ambiguous_float_literal_in_expr(cx, then) - || else_ - .as_ref() - .map_or(false, |else_expr| has_ambiguous_float_literal_in_expr(cx, else_expr)) - }, - ExprKind::Block(block, _) => block - .expr - .as_ref() - .map_or(false, |expr| has_ambiguous_float_literal_in_expr(cx, expr)), - ExprKind::MethodCall(_, receiver, args, _) => { - has_ambiguous_float_literal_in_expr(cx, receiver) - || args.iter().any(|arg| has_ambiguous_float_literal_in_expr(cx, arg)) - }, - ExprKind::Call(func, args) => { - has_ambiguous_float_literal_in_expr(cx, func) - || args.iter().any(|arg| has_ambiguous_float_literal_in_expr(cx, arg)) - }, - _ => false, - } -} - fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Binary( Spanned { @@ -548,7 +492,9 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { // Check if any variable in the expression has an ambiguous type (could be f32 or f64) // see: https://github.com/rust-lang/rust-clippy/issues/14897 - if has_ambiguous_float_type(cx, recv) { + if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _))) + && has_ambiguous_literal_in_expr(cx, recv) + { return; } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index c37231d0931..e249f87058d 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -2,6 +2,7 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; use crate::tokenize_with_text; +use rustc_ast::ast; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; use rustc_hir::MatchSource::TryDesugar; @@ -9,8 +10,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind, - Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, - TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, + Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; @@ -1004,8 +1005,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); } }, - ExprKind::Match(e, arms, s) => { - self.hash_expr(e); + ExprKind::Match(scrutinee, arms, _) => { + self.hash_expr(scrutinee); for arm in *arms { self.hash_pat(arm.pat); @@ -1014,8 +1015,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } self.hash_expr(arm.body); } - - s.hash(&mut self.s); }, ExprKind::MethodCall(path, receiver, args, _fn_span) => { self.hash_name(path.ident.name); @@ -1058,8 +1057,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Use(expr, _) => { self.hash_expr(expr); }, - ExprKind::Unary(lop, le) => { - std::mem::discriminant(lop).hash(&mut self.s); + ExprKind::Unary(l_op, le) => { + std::mem::discriminant(l_op).hash(&mut self.s); self.hash_expr(le); }, ExprKind::UnsafeBinderCast(kind, expr, ty) => { @@ -1394,3 +1393,70 @@ fn eq_span_tokens( } f(cx, left.into_range(), right.into_range(), pred) } + +/// Returns true if the expression contains ambiguous literals (unsuffixed float or int literals) +/// that could be interpreted as either f32/f64 or i32/i64 depending on context. +pub fn has_ambiguous_literal_in_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Path(ref qpath) => { + if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id) + && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) + && local.ty.is_none() + && let Some(init) = local.init + { + return has_ambiguous_literal_in_expr(cx, init); + } + false + }, + ExprKind::Lit(lit) => matches!( + lit.node, + ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) | ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) + ), + + ExprKind::Array(exprs) | ExprKind::Tup(exprs) => exprs.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Binary(_, lhs, rhs) => { + has_ambiguous_literal_in_expr(cx, lhs) || has_ambiguous_literal_in_expr(cx, rhs) + }, + + ExprKind::Unary(_, e) + | ExprKind::Cast(e, _) + | ExprKind::Type(e, _) + | ExprKind::DropTemps(e) + | ExprKind::AddrOf(_, _, e) + | ExprKind::Field(e, _) + | ExprKind::Index(e, _, _) + | ExprKind::Yield(e, _) => has_ambiguous_literal_in_expr(cx, e), + + ExprKind::MethodCall(_, receiver, args, _) | ExprKind::Call(receiver, args) => { + has_ambiguous_literal_in_expr(cx, receiver) || args.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + ExprKind::Closure(Closure { body, .. }) => { + let body = cx.tcx.hir_body(*body); + let closure_expr = crate::peel_blocks(body.value); + has_ambiguous_literal_in_expr(cx, closure_expr) + }, + + ExprKind::Block(blk, _) => blk.expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::If(cond, then_expr, else_expr) => { + has_ambiguous_literal_in_expr(cx, cond) + || has_ambiguous_literal_in_expr(cx, then_expr) + || else_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + ExprKind::Match(scrutinee, arms, _) => { + has_ambiguous_literal_in_expr(cx, scrutinee) + || arms.iter().any(|arm| has_ambiguous_literal_in_expr(cx, arm.body)) + }, + + ExprKind::Loop(body, ..) => body.expr.is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::Ret(opt_expr) | ExprKind::Break(_, opt_expr) => { + opt_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + _ => false, + } +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7fa52229fef..ade839cd441 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -77,7 +77,8 @@ pub mod visitors; pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, + HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr, + hash_stmt, is_bool, over, }; use core::mem; diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index fc6e30a9804..48f1c36117c 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -353,7 +353,7 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> ExprKind::Binary(_, lhs, rhs) if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, - ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (), + ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (), ExprKind::Index(base, _, _) if matches!( @@ -388,7 +388,8 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> | ExprKind::Repeat(..) | ExprKind::Struct(..) | ExprKind::Tup(_) - | ExprKind::Type(..) => (), + | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) => (), _ => { return ControlFlow::Break(()); @@ -676,10 +677,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>( helper(typeck, true, else_expr, f)?; } }, - ExprKind::Type(e, _) => { - helper(typeck, consume, e, f)?; - }, - ExprKind::UnsafeBinderCast(_, e, _) => { + ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => { helper(typeck, consume, e, f)?; }, diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index b07f3a6cc69..884bae00432 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed @@ -89,6 +89,16 @@ fn _issue14897() { let x = 2.4 + 1.2; let _ = 0.5 + x * 1.2; // should not suggest mul_add + let f = || 4.0; + let x = f(); + let _ = 0.5 + f() * 1.2; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 0.1; + let y = x; + let z = y; + let _ = 0.5 + z * 1.2; // should not suggest mul_add + let _ = 2.0f64.mul_add(x, 0.5); //~^ suboptimal_flops let _ = 2.0f64.mul_add(x, 0.5); diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 8af9c51f271..9ceb2ec9606 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs @@ -89,6 +89,16 @@ fn _issue14897() { let x = 2.4 + 1.2; let _ = 0.5 + x * 1.2; // should not suggest mul_add + let f = || 4.0; + let x = f(); + let _ = 0.5 + f() * 1.2; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 0.1; + let y = x; + let z = y; + let _ = 0.5 + z * 1.2; // should not suggest mul_add + let _ = 0.5 + 2.0 * x; //~^ suboptimal_flops let _ = 2.0 * x + 0.5; diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index 80d08c7de73..dad65ddf2ec 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -80,40 +80,34 @@ LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:85:13 - | -LL | let _ = 0.5 + x * 1.2; // should not suggest mul_add - | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(1.2, 0.5)` - -error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:96:13 + --> tests/ui/floating_point_mul_add.rs:102:13 | LL | let _ = 0.5 + 2.0 * x; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:98:13 + --> tests/ui/floating_point_mul_add.rs:104:13 | LL | let _ = 2.0 * x + 0.5; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:101:13 + --> tests/ui/floating_point_mul_add.rs:107:13 | LL | let _ = x + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:105:13 + --> tests/ui/floating_point_mul_add.rs:111:13 | LL | let _ = y * 2.0 + 0.5; | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)` error: multiply and add expressions can be calculated more efficiently and accurately - --> tests/ui/floating_point_mul_add.rs:107:13 + --> tests/ui/floating_point_mul_add.rs:113:13 | LL | let _ = 1.0 * 2.0 + 0.5; | ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)` -error: aborting due to 19 previous errors +error: aborting due to 18 previous errors -- cgit 1.4.1-3-g733a5 From 9b4231dcd9c691a07e1507c7824c6d339716c6ab Mon Sep 17 00:00:00 2001 From: Chengxu Bian Date: Wed, 25 Jun 2025 22:00:05 -0400 Subject: fix redudant closure broken fix --- clippy_lints/src/redundant_closure_call.rs | 22 +++++++++----------- tests/ui/redundant_closure_call_fixable.fixed | 14 ++++++++++++- tests/ui/redundant_closure_call_fixable.rs | 12 +++++++++++ tests/ui/redundant_closure_call_fixable.stderr | 28 ++++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 84597269a58..1c23fe998ad 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -5,9 +5,7 @@ use hir::Param; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; -use rustc_hir::{ - ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, intravisit as hir_visit, -}; +use rustc_hir::{ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, intravisit as hir_visit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; @@ -198,15 +196,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { hint = hint.asyncify(); } - let is_in_fn_call_arg = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) { - matches!(expr.kind, ExprKind::Call(_, _)) - } else { - false - }; - - // avoid clippy::double_parens - if !is_in_fn_call_arg { - hint = hint.maybe_paren(); + // If the closure body is a block with a single expression, suggest just the inner expression, + // not the block. Example: `(|| { Some(true) })()` should suggest + // `Some(true)` + if let ExprKind::Block(block, _) = body.kind + && block.stmts.is_empty() + && let Some(expr) = block.expr + { + hint = Sugg::hir_with_context(cx, expr, full_expr.span.ctxt(), "..", &mut applicability) + .maybe_paren(); } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability); diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index 099c118e64e..9f6643e8d52 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed @@ -60,7 +60,7 @@ fn issue9956() { //~^ redundant_closure_call // immediately calling only one closure, so we can't remove the other ones - let a = (|| || 123); + let a = || || 123; //~^ redundant_closure_call dbg!(a()()); @@ -144,3 +144,15 @@ fn issue_12358() { // different. make_closure!(x)(); } + +#[rustfmt::skip] +fn issue_9583() { + Some(true) == Some(true); + //~^ redundant_closure_call + Some(true) == Some(true); + //~^ redundant_closure_call + Some(if 1 > 2 {1} else {2}) == Some(2); + //~^ redundant_closure_call + Some( 1 > 2 ) == Some(true); + //~^ redundant_closure_call +} diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index da5dd7ef263..34f22878678 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -144,3 +144,15 @@ fn issue_12358() { // different. make_closure!(x)(); } + +#[rustfmt::skip] +fn issue_9583() { + (|| { Some(true) })() == Some(true); + //~^ redundant_closure_call + (|| Some(true))() == Some(true); + //~^ redundant_closure_call + (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); + //~^ redundant_closure_call + (|| { Some( 1 > 2 ) })() == Some(true); + //~^ redundant_closure_call +} diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index 2c35aafbe31..a5591cf7813 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -95,7 +95,7 @@ error: try not to call a closure in the expression where it is declared --> tests/ui/redundant_closure_call_fixable.rs:63:13 | LL | let a = (|| || || 123)(); - | ^^^^^^^^^^^^^^^^ help: try doing something like: `(|| || 123)` + | ^^^^^^^^^^^^^^^^ help: try doing something like: `|| || 123` error: try not to call a closure in the expression where it is declared --> tests/ui/redundant_closure_call_fixable.rs:68:13 @@ -145,5 +145,29 @@ error: try not to call a closure in the expression where it is declared LL | std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `13_i32 + 36_i32` -error: aborting due to 17 previous errors +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:150:5 + | +LL | (|| { Some(true) })() == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:152:5 + | +LL | (|| Some(true))() == Some(true); + | ^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:154:5 + | +LL | (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(if 1 > 2 {1} else {2})` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:156:5 + | +LL | (|| { Some( 1 > 2 ) })() == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some( 1 > 2 )` + +error: aborting due to 21 previous errors -- cgit 1.4.1-3-g733a5 From bb59c39fd2f18398d3d044525a0bf7557f9c4768 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 26 Jun 2025 09:35:11 +0200 Subject: Don't remove explicit cast to trait object pointer --- clippy_lints/src/casts/borrow_as_ptr.rs | 3 ++- tests/ui/borrow_as_ptr.fixed | 6 ++++++ tests/ui/borrow_as_ptr.rs | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index ad0a4f8cdf3..e3b125a8d5b 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -18,7 +18,8 @@ pub(super) fn check<'tcx>( cast_to: &'tcx Ty<'_>, msrv: Msrv, ) -> bool { - if matches!(cast_to.kind, TyKind::Ptr(_)) + if let TyKind::Ptr(target) = cast_to.kind + && !matches!(target.ty.kind, TyKind::TraitObject(..)) && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind && !is_lint_allowed(cx, BORROW_AS_PTR, expr.hir_id) { diff --git a/tests/ui/borrow_as_ptr.fixed b/tests/ui/borrow_as_ptr.fixed index 3ba2eea59f0..3f6e5245b87 100644 --- a/tests/ui/borrow_as_ptr.fixed +++ b/tests/ui/borrow_as_ptr.fixed @@ -47,3 +47,9 @@ fn implicit_cast() { // Do not lint references to temporaries core::ptr::eq(&0i32, &1i32); } + +fn issue_15141() { + let a = String::new(); + // Don't lint cast to dyn trait pointers + let b = &a as *const dyn std::any::Any; +} diff --git a/tests/ui/borrow_as_ptr.rs b/tests/ui/borrow_as_ptr.rs index 8cdd0512da5..20f4f40e001 100644 --- a/tests/ui/borrow_as_ptr.rs +++ b/tests/ui/borrow_as_ptr.rs @@ -47,3 +47,9 @@ fn implicit_cast() { // Do not lint references to temporaries core::ptr::eq(&0i32, &1i32); } + +fn issue_15141() { + let a = String::new(); + // Don't lint cast to dyn trait pointers + let b = &a as *const dyn std::any::Any; +} -- cgit 1.4.1-3-g733a5 From 4f3c17486eafadd4ccebdf5230b27a285c9a110d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 11 Mar 2025 12:08:45 +0000 Subject: Change const trait bound syntax from ~const to [const] --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- tests/ui/assign_ops.fixed | 2 +- tests/ui/assign_ops.rs | 2 +- tests/ui/trait_duplication_in_bounds.fixed | 4 ++-- tests/ui/trait_duplication_in_bounds.rs | 4 ++-- tests/ui/trait_duplication_in_bounds.stderr | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index e629012b187..328fe3d428b 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -436,7 +436,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> // FIXME(const_trait_impl, fee1-dead) revert to const destruct once it works again #[expect(unused)] fn is_ty_const_destruct_unused<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool { - // If this doesn't need drop at all, then don't select `~const Destruct`. + // If this doesn't need drop at all, then don't select `[const] Destruct`. if !ty.needs_drop(tcx, body.typing_env(tcx)) { return false; } diff --git a/tests/ui/assign_ops.fixed b/tests/ui/assign_ops.fixed index 3bc6885d7c3..99beea850a2 100644 --- a/tests/ui/assign_ops.fixed +++ b/tests/ui/assign_ops.fixed @@ -91,7 +91,7 @@ mod issue14871 { impl const NumberConstants for T where - T: Number + ~const core::ops::Add, + T: Number + [const] core::ops::Add, { fn constant(value: usize) -> Self { let mut res = Self::ZERO; diff --git a/tests/ui/assign_ops.rs b/tests/ui/assign_ops.rs index f1f8f9daff9..900d5ad38e0 100644 --- a/tests/ui/assign_ops.rs +++ b/tests/ui/assign_ops.rs @@ -91,7 +91,7 @@ mod issue14871 { impl const NumberConstants for T where - T: Number + ~const core::ops::Add, + T: Number + [const] core::ops::Add, { fn constant(value: usize) -> Self { let mut res = Self::ZERO; diff --git a/tests/ui/trait_duplication_in_bounds.fixed b/tests/ui/trait_duplication_in_bounds.fixed index 666ff78b218..cf52ecf2f03 100644 --- a/tests/ui/trait_duplication_in_bounds.fixed +++ b/tests/ui/trait_duplication_in_bounds.fixed @@ -169,9 +169,9 @@ where // #13476 #[const_trait] trait ConstTrait {} -const fn const_trait_bounds_good() {} +const fn const_trait_bounds_good() {} -const fn const_trait_bounds_bad() {} +const fn const_trait_bounds_bad() {} //~^ trait_duplication_in_bounds fn projections() diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index a1a86fe058e..955562f08dc 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -169,9 +169,9 @@ where // #13476 #[const_trait] trait ConstTrait {} -const fn const_trait_bounds_good() {} +const fn const_trait_bounds_good() {} -const fn const_trait_bounds_bad() {} +const fn const_trait_bounds_bad() {} //~^ trait_duplication_in_bounds fn projections() diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index d76b4e45848..ab31721ef51 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -61,8 +61,8 @@ LL | fn bad_trait_object(arg0: &(dyn Any + Send + Send)) { error: these bounds contain repeated elements --> tests/ui/trait_duplication_in_bounds.rs:174:36 | -LL | const fn const_trait_bounds_bad() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `~const ConstTrait` +LL | const fn const_trait_bounds_bad() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[const] ConstTrait` error: these where clauses contain repeated elements --> tests/ui/trait_duplication_in_bounds.rs:181:8 -- cgit 1.4.1-3-g733a5 From 664b143dac42362bebdd93ea1a864c21bac5dd4d Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 26 Jun 2025 09:18:43 -0700 Subject: doc_nested_refdefs: do not falsely report checkboxes as refdefs --- clippy_lints/src/doc/mod.rs | 4 +++- tests/ui/doc/doc_nested_refdef_list_item.fixed | 6 ++---- tests/ui/doc/doc_nested_refdef_list_item.rs | 2 -- tests/ui/doc/doc_nested_refdef_list_item.stderr | 26 +------------------------ 4 files changed, 6 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 1b6c48e53f5..49397938ca7 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1249,7 +1249,9 @@ fn looks_like_refdef(doc: &str, range: Range) -> Option> { b'[' => { start = Some(i + offset); }, - b']' if let Some(start) = start => { + b']' if let Some(start) = start + && doc.as_bytes().get(i + offset + 1) == Some(&b':') => + { return Some(start..i + offset + 1); }, _ => {}, diff --git a/tests/ui/doc/doc_nested_refdef_list_item.fixed b/tests/ui/doc/doc_nested_refdef_list_item.fixed index 065f4486e39..5c57c58fbc0 100644 --- a/tests/ui/doc/doc_nested_refdef_list_item.fixed +++ b/tests/ui/doc/doc_nested_refdef_list_item.fixed @@ -72,8 +72,6 @@ pub struct NotEmptyTight; /// ## Heading /// -/// - [x][] - Done -//~^ ERROR: link reference defined in list item -/// - [ ][] - Not Done -//~^ ERROR: link reference defined in list item +/// - [x] - Done +/// - [ ] - Not Done pub struct GithubCheckboxes; diff --git a/tests/ui/doc/doc_nested_refdef_list_item.rs b/tests/ui/doc/doc_nested_refdef_list_item.rs index c7eab50c8b3..06b6ba49e19 100644 --- a/tests/ui/doc/doc_nested_refdef_list_item.rs +++ b/tests/ui/doc/doc_nested_refdef_list_item.rs @@ -73,7 +73,5 @@ pub struct NotEmptyTight; /// ## Heading /// /// - [x] - Done -//~^ ERROR: link reference defined in list item /// - [ ] - Not Done -//~^ ERROR: link reference defined in list item pub struct GithubCheckboxes; diff --git a/tests/ui/doc/doc_nested_refdef_list_item.stderr b/tests/ui/doc/doc_nested_refdef_list_item.stderr index 5a815dabf4d..27314c7e968 100644 --- a/tests/ui/doc/doc_nested_refdef_list_item.stderr +++ b/tests/ui/doc/doc_nested_refdef_list_item.stderr @@ -144,29 +144,5 @@ help: for an intra-doc link, add `[]` between the label and the colon LL | /// - [link][]: def "title" | ++ -error: link reference defined in list item - --> tests/ui/doc/doc_nested_refdef_list_item.rs:75:7 - | -LL | /// - [x] - Done - | ^^^ - | - = help: link definitions are not shown in rendered documentation -help: for an intra-doc link, add `[]` between the label and the colon - | -LL | /// - [x][] - Done - | ++ - -error: link reference defined in list item - --> tests/ui/doc/doc_nested_refdef_list_item.rs:77:7 - | -LL | /// - [ ] - Not Done - | ^^^ - | - = help: link definitions are not shown in rendered documentation -help: for an intra-doc link, add `[]` between the label and the colon - | -LL | /// - [ ][] - Not Done - | ++ - -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 9117cb0223eaf72ab9015cee10549b2b774d8f74 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 26 Jun 2025 22:51:58 +0200 Subject: `zero_ptr`: lint in `const` context as well The lint was extra restrictive, and didn't suggest using `core::ptr::null` and `core::ptr::null_mut` in `const` contexts although they have been const-stabilized since Rust 1.24. --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/casts/zero_ptr.rs | 5 +++-- clippy_utils/src/msrvs.rs | 2 +- tests/ui/zero_ptr.fixed | 8 ++++++++ tests/ui/zero_ptr.rs | 8 ++++++++ tests/ui/zero_ptr.stderr | 8 +++++++- 8 files changed, 30 insertions(+), 5 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index e9b7f42a183..992ed2c6aaa 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -892,6 +892,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) * [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names) * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) +* [`zero_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr) ## `pass-by-value-size-limit` diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 841facdca06..555f54bcfb8 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -794,6 +794,7 @@ define_Conf! { unnested_or_patterns, unused_trait_names, use_self, + zero_ptr, )] msrv: Msrv = Msrv::default(), /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index daae9a8bb08..37accff5eaa 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -878,7 +878,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); - zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); + zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir, self.msrv); if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) { manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir); diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs index a34af6bc226..f4738e7b0d5 100644 --- a/clippy_lints/src/casts/zero_ptr.rs +++ b/clippy_lints/src/casts/zero_ptr.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; @@ -7,10 +8,10 @@ use rustc_lint::LateContext; use super::ZERO_PTR; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>, msrv: Msrv) { if let TyKind::Ptr(ref mut_ty) = to.kind && is_integer_literal(from, 0) - && !is_in_const_context(cx) + && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::PTR_NULL)) && let Some(std_or_core) = std_or_core(cx) { let (msg, sugg_fn) = match mut_ty.mutbl { diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 7a0bef1a9bb..24ed4c3a8be 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -74,7 +74,7 @@ msrv_aliases! { 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } - 1,24,0 { IS_ASCII_DIGIT } + 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } diff --git a/tests/ui/zero_ptr.fixed b/tests/ui/zero_ptr.fixed index f2375d57f3a..f9d9d2db176 100644 --- a/tests/ui/zero_ptr.fixed +++ b/tests/ui/zero_ptr.fixed @@ -16,3 +16,11 @@ fn main() { let z = 0; let _ = z as *const usize; // this is currently not caught } + +const fn in_const_context() { + #[clippy::msrv = "1.23"] + let _: *const usize = 0 as *const _; + #[clippy::msrv = "1.24"] + let _: *const usize = std::ptr::null(); + //~^ zero_ptr +} diff --git a/tests/ui/zero_ptr.rs b/tests/ui/zero_ptr.rs index ee01e426a43..41455fee5b5 100644 --- a/tests/ui/zero_ptr.rs +++ b/tests/ui/zero_ptr.rs @@ -16,3 +16,11 @@ fn main() { let z = 0; let _ = z as *const usize; // this is currently not caught } + +const fn in_const_context() { + #[clippy::msrv = "1.23"] + let _: *const usize = 0 as *const _; + #[clippy::msrv = "1.24"] + let _: *const usize = 0 as *const _; + //~^ zero_ptr +} diff --git a/tests/ui/zero_ptr.stderr b/tests/ui/zero_ptr.stderr index 8dc781f3625..81269de6c60 100644 --- a/tests/ui/zero_ptr.stderr +++ b/tests/ui/zero_ptr.stderr @@ -31,5 +31,11 @@ error: `0 as *mut _` detected LL | foo(0 as *const _, 0 as *mut _); | ^^^^^^^^^^^ help: try: `std::ptr::null_mut()` -error: aborting due to 5 previous errors +error: `0 as *const _` detected + --> tests/ui/zero_ptr.rs:24:27 + | +LL | let _: *const usize = 0 as *const _; + | ^^^^^^^^^^^^^ help: try: `std::ptr::null()` + +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 074ccaf3c0818c1c7162babe8e7874f1fa551e18 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 27 Jun 2025 12:20:57 +0200 Subject: Merge commit 'c5dbd1de07e0407b9687619a868384d6de06253f' into clippy-subtree-update --- .github/ISSUE_TEMPLATE/new_lint.yml | 4 +- .github/PULL_REQUEST_TEMPLATE.md | 4 + .github/workflows/feature_freeze.yml | 25 ++ CHANGELOG.md | 91 ++++++- Cargo.toml | 3 +- book/src/README.md | 4 + book/src/SUMMARY.md | 1 + book/src/development/adding_lints.md | 3 + book/src/development/feature_freeze.md | 55 ++++ book/src/lint_configuration.md | 24 +- clippy_config/Cargo.toml | 2 +- clippy_config/src/conf.rs | 25 +- clippy_dev/src/lint.rs | 4 +- clippy_dev/src/release.rs | 1 + clippy_dev/src/serve.rs | 2 +- clippy_dev/src/update_lints.rs | 291 ++++++++++++--------- clippy_dev/src/utils.rs | 15 -- clippy_lints/Cargo.toml | 3 +- clippy_lints/src/attrs/inline_always.rs | 4 +- clippy_lints/src/attrs/mod.rs | 2 +- clippy_lints/src/attrs/utils.rs | 12 +- clippy_lints/src/borrow_deref_ref.rs | 15 +- clippy_lints/src/casts/cast_sign_loss.rs | 2 +- clippy_lints/src/collapsible_if.rs | 127 ++++++--- clippy_lints/src/copies.rs | 18 +- clippy_lints/src/declare_clippy_lint.rs | 168 ------------ clippy_lints/src/declared_lints.rs | 4 +- clippy_lints/src/disallowed_macros.rs | 3 + clippy_lints/src/disallowed_methods.rs | 3 + clippy_lints/src/disallowed_types.rs | 3 + clippy_lints/src/doc/broken_link.rs | 83 ++++++ clippy_lints/src/doc/doc_suspicious_footnotes.rs | 6 +- clippy_lints/src/doc/mod.rs | 50 +++- clippy_lints/src/doc/needless_doctest_main.rs | 27 +- clippy_lints/src/empty_line_after.rs | 64 ++++- clippy_lints/src/eta_reduction.rs | 92 ++++--- clippy_lints/src/exhaustive_items.rs | 2 +- clippy_lints/src/floating_point_arithmetic.rs | 3 +- clippy_lints/src/functions/must_use.rs | 53 +++- clippy_lints/src/if_not_else.rs | 11 +- clippy_lints/src/inline_fn_without_body.rs | 10 +- clippy_lints/src/lib.rs | 126 +-------- clippy_lints/src/loops/same_item_push.rs | 15 +- clippy_lints/src/manual_let_else.rs | 1 - clippy_lints/src/matches/manual_ok_err.rs | 20 +- clippy_lints/src/matches/match_wild_enum.rs | 14 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/or_fun_call.rs | 11 +- clippy_lints/src/missing_inline.rs | 2 +- .../src/needless_borrows_for_generic_args.rs | 12 +- .../src/needless_parens_on_range_literals.rs | 1 - clippy_lints/src/needless_pass_by_value.rs | 6 +- clippy_lints/src/no_mangle_with_rust_abi.rs | 5 +- clippy_lints/src/non_copy_const.rs | 2 +- clippy_lints/src/operators/identity_op.rs | 76 +++++- .../src/operators/manual_is_multiple_of.rs | 66 +++++ clippy_lints/src/operators/mod.rs | 33 +++ clippy_lints/src/pass_by_ref_or_value.rs | 4 +- clippy_lints/src/question_mark.rs | 1 + clippy_lints/src/question_mark_used.rs | 1 - clippy_lints/src/read_zero_byte_vec.rs | 3 +- clippy_lints/src/return_self_not_must_use.rs | 5 +- clippy_lints/src/single_component_path_imports.rs | 29 +- clippy_lints/src/undocumented_unsafe_blocks.rs | 45 ++-- clippy_utils/Cargo.toml | 2 +- clippy_utils/README.md | 2 +- clippy_utils/src/consts.rs | 15 ++ clippy_utils/src/diagnostics.rs | 6 +- clippy_utils/src/lib.rs | 22 +- clippy_utils/src/msrvs.rs | 3 +- clippy_utils/src/qualify_min_const_fn.rs | 15 ++ clippy_utils/src/sugg.rs | 22 +- clippy_utils/src/sym.rs | 5 +- clippy_utils/src/ty/mod.rs | 24 +- declare_clippy_lint/Cargo.toml | 10 + declare_clippy_lint/src/lib.rs | 280 ++++++++++++++++++++ lintcheck/src/main.rs | 2 +- rust-toolchain.toml | 2 +- src/driver.rs | 8 +- src/main.rs | 2 +- tests/compile-test.rs | 6 +- tests/dogfood.rs | 1 + .../collapsible_if/collapsible_else_if.fixed | 50 ++++ .../ui-toml/collapsible_if/collapsible_else_if.rs | 55 ++++ .../collapsible_if/collapsible_else_if.stderr | 105 ++++++++ tests/ui/borrow_deref_ref.fixed | 47 ++++ tests/ui/borrow_deref_ref.rs | 47 ++++ tests/ui/borrow_deref_ref.stderr | 32 ++- tests/ui/borrow_interior_mutable_const.rs | 16 ++ tests/ui/box_default.fixed | 2 +- tests/ui/box_default.rs | 2 +- tests/ui/branches_sharing_code/shared_at_bottom.rs | 24 ++ .../branches_sharing_code/shared_at_bottom.stderr | 32 ++- tests/ui/collapsible_else_if.fixed | 18 ++ tests/ui/collapsible_else_if.rs | 18 ++ tests/ui/collapsible_if.fixed | 9 + tests/ui/collapsible_if.rs | 9 + tests/ui/doc/needless_doctest_main.rs | 112 +++++++- tests/ui/doc/needless_doctest_main.stderr | 36 +++ tests/ui/doc_broken_link.rs | 72 +++++ tests/ui/doc_broken_link.stderr | 29 ++ tests/ui/empty_line_after/outer_attribute.1.fixed | 9 + tests/ui/empty_line_after/outer_attribute.2.fixed | 9 + tests/ui/empty_line_after/outer_attribute.rs | 10 + tests/ui/empty_line_after/outer_attribute.stderr | 13 +- tests/ui/eta.fixed | 18 ++ tests/ui/eta.rs | 18 ++ tests/ui/exhaustive_items.fixed | 7 + tests/ui/exhaustive_items.rs | 7 + tests/ui/exhaustive_items.stderr | 10 +- tests/ui/identity_op.fixed | 46 ++++ tests/ui/identity_op.rs | 46 ++++ tests/ui/identity_op.stderr | 44 +++- tests/ui/infinite_iter.rs | 2 +- tests/ui/infinite_iter.stderr | 4 +- tests/ui/iter_kv_map.fixed | 20 +- tests/ui/iter_kv_map.rs | 20 +- tests/ui/iter_kv_map.stderr | 64 ++--- tests/ui/let_unit.fixed | 2 +- tests/ui/let_unit.rs | 2 +- tests/ui/let_unit.stderr | 2 +- tests/ui/manual_contains.fixed | 2 +- tests/ui/manual_contains.rs | 2 +- tests/ui/manual_find_fixable.fixed | 4 +- tests/ui/manual_find_fixable.rs | 4 +- tests/ui/manual_find_fixable.stderr | 4 +- tests/ui/manual_is_multiple_of.fixed | 25 ++ tests/ui/manual_is_multiple_of.rs | 25 ++ tests/ui/manual_is_multiple_of.stderr | 41 +++ tests/ui/manual_is_variant_and.fixed | 8 +- tests/ui/manual_is_variant_and.rs | 8 +- tests/ui/manual_is_variant_and.stderr | 12 +- tests/ui/manual_ok_err.fixed | 24 ++ tests/ui/manual_ok_err.rs | 36 +++ tests/ui/manual_ok_err.stderr | 32 ++- tests/ui/missing_const_for_fn/const_trait.fixed | 2 +- tests/ui/missing_const_for_fn/const_trait.rs | 2 +- tests/ui/missing_const_for_fn/could_be_const.fixed | 57 ++++ tests/ui/missing_const_for_fn/could_be_const.rs | 57 ++++ .../ui/missing_const_for_fn/could_be_const.stderr | 72 ++++- tests/ui/nonminimal_bool.stderr | 4 +- tests/ui/or_fun_call.fixed | 30 +++ tests/ui/or_fun_call.rs | 30 +++ tests/ui/or_fun_call.stderr | 96 ++++--- tests/ui/question_mark.fixed | 12 + tests/ui/question_mark.rs | 12 + tests/ui/unnecessary_os_str_debug_formatting.rs | 13 + .../ui/unnecessary_os_str_debug_formatting.stderr | 11 +- tests/ui/wildcard_enum_match_arm.fixed | 29 ++ tests/ui/wildcard_enum_match_arm.rs | 29 ++ tests/ui/wildcard_enum_match_arm.stderr | 14 +- tests/versioncheck.rs | 1 + triagebot.toml | 3 + util/versions.py | 6 +- 154 files changed, 3158 insertions(+), 843 deletions(-) create mode 100644 .github/workflows/feature_freeze.yml create mode 100644 book/src/development/feature_freeze.md delete mode 100644 clippy_lints/src/declare_clippy_lint.rs create mode 100644 clippy_lints/src/doc/broken_link.rs create mode 100644 clippy_lints/src/operators/manual_is_multiple_of.rs create mode 100644 declare_clippy_lint/Cargo.toml create mode 100644 declare_clippy_lint/src/lib.rs create mode 100644 tests/ui-toml/collapsible_if/collapsible_else_if.fixed create mode 100644 tests/ui-toml/collapsible_if/collapsible_else_if.rs create mode 100644 tests/ui-toml/collapsible_if/collapsible_else_if.stderr create mode 100644 tests/ui/doc/needless_doctest_main.stderr create mode 100644 tests/ui/doc_broken_link.rs create mode 100644 tests/ui/doc_broken_link.stderr create mode 100644 tests/ui/manual_is_multiple_of.fixed create mode 100644 tests/ui/manual_is_multiple_of.rs create mode 100644 tests/ui/manual_is_multiple_of.stderr diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml index b49493edce1..464740640e0 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.yml +++ b/.github/ISSUE_TEMPLATE/new_lint.yml @@ -1,5 +1,7 @@ name: New lint suggestion -description: Suggest a new Clippy lint. +description: | + Suggest a new Clippy lint (currently not accepting new lints) + Check out the Clippy book for more information about the feature freeze. labels: ["A-lint"] body: - type: markdown diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9e49f60892d..83bfd8e9c68 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -32,6 +32,10 @@ order to get feedback. Delete this line and everything above before opening your PR. +Note that we are currently not taking in new PRs that add new lints. We are in a +feature freeze. Check out the book for more information. If you open a +feature-adding pull request, its review will be delayed. + --- *Please write a short comment explaining your change (or "none" for internal only changes)* diff --git a/.github/workflows/feature_freeze.yml b/.github/workflows/feature_freeze.yml new file mode 100644 index 00000000000..a5f8d4bc145 --- /dev/null +++ b/.github/workflows/feature_freeze.yml @@ -0,0 +1,25 @@ +name: Feature freeze check + +on: + pull_request: + paths: + - 'clippy_lints/src/declared_lints.rs' + +jobs: + auto-comment: + runs-on: ubuntu-latest + + steps: + - name: Check PR Changes + id: pr-changes + run: echo "::set-output name=changes::${{ toJson(github.event.pull_request.changed_files) }}" + + - name: Create Comment + if: steps.pr-changes.outputs.changes != '[]' + run: | + # Use GitHub API to create a comment on the PR + PR_NUMBER=${{ github.event.pull_request.number }} + COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to August 1st and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team" + GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" + curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL -d "{\"body\":\"$COMMENT\"}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cfe89ad378..a92fbdc767b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,94 @@ document. ## Unreleased / Beta / In Rust Nightly -[1e5237f4...master](https://github.com/rust-lang/rust-clippy/compare/1e5237f4...master) +[03a5b6b9...master](https://github.com/rust-lang/rust-clippy/compare/03a5b6b9...master) + +## Rust 1.88 + +Current stable, released 2025-06-26 + +[View all 126 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-03-21T10%3A30%3A57Z..2025-05-01T08%3A03%3A26Z+base%3Amaster) + +### New Lints + +* Added [`swap_with_temporary`] to `complexity` [#14046](https://github.com/rust-lang/rust-clippy/pull/14046) +* Added [`redundant_test_prefix`] to `restriction` [#13710](https://github.com/rust-lang/rust-clippy/pull/13710) +* Added [`manual_dangling_ptr`] to `style` [#14107](https://github.com/rust-lang/rust-clippy/pull/14107) +* Added [`char_indices_as_byte_indices`] to `correctness` [#13435](https://github.com/rust-lang/rust-clippy/pull/13435) +* Added [`manual_abs_diff`] to `complexity` [#14482](https://github.com/rust-lang/rust-clippy/pull/14482) +* Added [`ignore_without_reason`] to `pedantic` [#13931](https://github.com/rust-lang/rust-clippy/pull/13931) + +### Moves and Deprecations + +* Moved [`uninlined_format_args`] to `style` (from `pedantic`) + [#14160](https://github.com/rust-lang/rust-clippy/pull/14160) +* [`match_on_vec_items`] deprecated in favor of [`indexing_slicing`] + [#14217](https://github.com/rust-lang/rust-clippy/pull/14217) +* Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`, + `transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc) + [#14703](https://github.com/rust-lang/rust-clippy/pull/14703) + +### Enhancements + +* Configuration renamed from `lint-inconsistent-struct-field-initializers` + to `check-inconsistent-struct-field-initializers` + [#14280](https://github.com/rust-lang/rust-clippy/pull/14280) +* Paths in `disallowed_*` configurations are now validated + [#14397](https://github.com/rust-lang/rust-clippy/pull/14397) +* [`borrow_as_ptr`] now lints implicit casts as well + [#14408](https://github.com/rust-lang/rust-clippy/pull/14408) +* [`iter_kv_map`] now recognizes references on maps + [#14596](https://github.com/rust-lang/rust-clippy/pull/14596) +* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used + as functions within same crate [#12971](https://github.com/rust-lang/rust-clippy/pull/12971) +* [`needless_lifetimes`] now checks for lifetime uses in closures + [#14608](https://github.com/rust-lang/rust-clippy/pull/14608) +* [`wildcard_imports`] now lints on `pub use` when `warn_on_all_wildcard_imports` is enabled + [#14182](https://github.com/rust-lang/rust-clippy/pull/14182) +* [`collapsible_if`] now recognizes the `let_chains` feature + [#14481](https://github.com/rust-lang/rust-clippy/pull/14481) +* [`match_single_binding`] now allows macros in scrutinee and patterns + [#14635](https://github.com/rust-lang/rust-clippy/pull/14635) +* [`needless_borrow`] does not contradict the compiler's + `dangerous_implicit_autorefs` lint even though the references + are not mandatory + [#14810](https://github.com/rust-lang/rust-clippy/pull/14810) + +### False Positive Fixes + +* [`double_ended_iterator_last`] and [`needless_collect`] fixed FP when iter has side effects + [#14490](https://github.com/rust-lang/rust-clippy/pull/14490) +* [`mut_from_ref`] fixed FP where lifetimes nested in types were not considered + [#14471](https://github.com/rust-lang/rust-clippy/pull/14471) +* [`redundant_clone`] fixed FP in overlapping lifetime + [#14237](https://github.com/rust-lang/rust-clippy/pull/14237) +* [`map_entry`] fixed FP where lint would trigger without insert calls present + [#14568](https://github.com/rust-lang/rust-clippy/pull/14568) +* [`iter_cloned_collect`] fixed FP with custom `From`/`IntoIterator` impl + [#14473](https://github.com/rust-lang/rust-clippy/pull/14473) +* [`shadow_unrelated`] fixed FP in destructuring assignments + [#14381](https://github.com/rust-lang/rust-clippy/pull/14381) +* [`redundant_clone`] fixed FP on enum cast + [#14395](https://github.com/rust-lang/rust-clippy/pull/14395) +* [`collapsible_if`] fixed FP on block stmt before expr + [#14730](https://github.com/rust-lang/rust-clippy/pull/14730) + +### ICE Fixes + +* [`missing_const_for_fn`] fix ICE with `-Z validate-mir` compilation option + [#14776](https://github.com/rust-lang/rust-clippy/pull/14776) + +### Documentation Improvements + +* [`missing_asserts_for_indexing`] improved documentation and examples + [#14108](https://github.com/rust-lang/rust-clippy/pull/14108) + +### Others + +* We're testing with edition 2024 now + [#14602](https://github.com/rust-lang/rust-clippy/pull/14602) +* Don't warn about unloaded crates in `clippy.toml` disallowed paths + [#14733](https://github.com/rust-lang/rust-clippy/pull/14733) ## Rust 1.87 @@ -5729,6 +5816,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_broken_link`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_broken_link [`doc_comment_double_space_linebreaks`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_comment_double_space_linebreaks [`doc_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg [`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation @@ -5967,6 +6055,7 @@ Released 2018-09-13 [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite [`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite +[`manual_is_multiple_of`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_multiple_of [`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two [`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else diff --git a/Cargo.toml b/Cargo.toml index 13cf82a062b..1278427b5a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.89" +version = "0.1.90" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -24,6 +24,7 @@ path = "src/driver.rs" clippy_config = { path = "clippy_config" } clippy_lints = { path = "clippy_lints" } clippy_utils = { path = "clippy_utils" } +declare_clippy_lint = { path = "declare_clippy_lint" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } clippy_lints_internal = { path = "clippy_lints_internal", optional = true } tempfile = { version = "3.20", optional = true } diff --git a/book/src/README.md b/book/src/README.md index 5d2c3972b06..db73b49ecc2 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -1,5 +1,9 @@ # Clippy +[### IMPORTANT NOTE FOR CONTRIBUTORS ================](development/feature_freeze.md) + +---- + [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license) A collection of lints to catch common mistakes and improve your diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 39fe7358ed8..b66c3481e49 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -13,6 +13,7 @@ - [GitLab CI](continuous_integration/gitlab.md) - [Travis CI](continuous_integration/travis.md) - [Development](development/README.md) + - [IMPORTANT: FEATURE FREEZE](development/feature_freeze.md) - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) - [Defining Lints](development/defining_lints.md) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 2b89e94cf8f..a42a2983744 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -1,5 +1,8 @@ # Adding a new lint +[### IMPORTANT NOTE FOR CONTRIBUTORS ================](feature_freeze.md) + + You are probably here because you want to add a new lint to Clippy. If this is the first time you're contributing to Clippy, this document guides you through creating an example lint from scratch. diff --git a/book/src/development/feature_freeze.md b/book/src/development/feature_freeze.md new file mode 100644 index 00000000000..260cb136cc0 --- /dev/null +++ b/book/src/development/feature_freeze.md @@ -0,0 +1,55 @@ +# IMPORTANT: FEATURE FREEZE + +This is a temporary notice. + +From the 26th of June until the 18th of September we will perform a feature freeze. Only bugfix PRs will be reviewed +except already open ones. Every feature-adding PR opened in between those dates will be moved into a +milestone to be reviewed separately at another time. + +We do this because of the long backlog of bugs that need to be addressed +in order to continue being the state-of-the-art linter that Clippy has become known for being. + +## For contributors + +If you are a contributor or are planning to become one, **please do not open a lint-adding PR**, we have lots of open +bugs of all levels of difficulty that you can address instead! + +We currently have about 800 lints, each one posing a maintainability challenge that needs to account to every possible +use case of the whole ecosystem. Bugs are natural in every software, but the Clippy team considers that Clippy needs a +refinement period. + +If you open a PR at this time, we will not review it but push it into a milestone until the refinement period ends, +adding additional load into our reviewing schedules. + +## I want to help, what can I do + +Thanks a lot to everyone who wants to help Clippy become better software in this feature freeze period! +If you'd like to help, making a bugfix, making sure that it works, and opening a PR is a great step! + +To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, go there and claim that +issue with `@rustbot claim`. + +As a general metric and always taking into account your skill and knowledge level, you can use this guide: + +- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level +debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that +improves a lot developer workflows! + +- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way. +Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs + +- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error +when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar +easy-to-happen occurrences. + +- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just" +identifying the root of a false positive and making an exception for those cases. + +Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a +trench coat. + +[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22 +[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug +[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20 +[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive +[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086 diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 7c850b4b023..e9b7f42a183 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -488,6 +488,13 @@ The maximum cognitive complexity a function can have ## `disallowed-macros` The list of disallowed macros, written as fully qualified paths. +**Fields:** +- `path` (required): the fully qualified path to the macro that should be disallowed +- `reason` (optional): explanation why this macro is disallowed +- `replacement` (optional): suggested alternative macro +- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + if the path doesn't exist, instead of emitting an error + **Default Value:** `[]` --- @@ -498,6 +505,13 @@ The list of disallowed macros, written as fully qualified paths. ## `disallowed-methods` The list of disallowed methods, written as fully qualified paths. +**Fields:** +- `path` (required): the fully qualified path to the method that should be disallowed +- `reason` (optional): explanation why this method is disallowed +- `replacement` (optional): suggested alternative method +- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + if the path doesn't exist, instead of emitting an error + **Default Value:** `[]` --- @@ -520,6 +534,13 @@ default configuration of Clippy. By default, any configuration will replace the ## `disallowed-types` The list of disallowed types, written as fully qualified paths. +**Fields:** +- `path` (required): the fully qualified path to the type that should be disallowed +- `reason` (optional): explanation why this type is disallowed +- `replacement` (optional): suggested alternative type +- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + if the path doesn't exist, instead of emitting an error + **Default Value:** `[]` --- @@ -651,13 +672,14 @@ The maximum size of the `Err`-variant in a `Result` returned from a function ## `lint-commented-code` -Whether collapsible `if` chains are linted if they contain comments inside the parts +Whether collapsible `if` and `else if` chains are linted if they contain comments inside the parts that would be collapsed. **Default Value:** `false` --- **Affected lints:** +* [`collapsible_else_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if) * [`collapsible_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if) diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 0606245f990..858366c8a5c 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.89" +version = "0.1.90" edition = "2024" publish = false diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 87158cec42b..841facdca06 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -575,10 +575,24 @@ define_Conf! { #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] cyclomatic_complexity_threshold: u64 = 25, /// The list of disallowed macros, written as fully qualified paths. + /// + /// **Fields:** + /// - `path` (required): the fully qualified path to the macro that should be disallowed + /// - `reason` (optional): explanation why this macro is disallowed + /// - `replacement` (optional): suggested alternative macro + /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + /// if the path doesn't exist, instead of emitting an error #[disallowed_paths_allow_replacements = true] #[lints(disallowed_macros)] disallowed_macros: Vec = Vec::new(), /// The list of disallowed methods, written as fully qualified paths. + /// + /// **Fields:** + /// - `path` (required): the fully qualified path to the method that should be disallowed + /// - `reason` (optional): explanation why this method is disallowed + /// - `replacement` (optional): suggested alternative method + /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + /// if the path doesn't exist, instead of emitting an error #[disallowed_paths_allow_replacements = true] #[lints(disallowed_methods)] disallowed_methods: Vec = Vec::new(), @@ -588,6 +602,13 @@ define_Conf! { #[lints(disallowed_names)] disallowed_names: Vec = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(), /// The list of disallowed types, written as fully qualified paths. + /// + /// **Fields:** + /// - `path` (required): the fully qualified path to the type that should be disallowed + /// - `reason` (optional): explanation why this type is disallowed + /// - `replacement` (optional): suggested alternative type + /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + /// if the path doesn't exist, instead of emitting an error #[disallowed_paths_allow_replacements = true] #[lints(disallowed_types)] disallowed_types: Vec = Vec::new(), @@ -641,9 +662,9 @@ define_Conf! { /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, - /// Whether collapsible `if` chains are linted if they contain comments inside the parts + /// Whether collapsible `if` and `else if` chains are linted if they contain comments inside the parts /// that would be collapsed. - #[lints(collapsible_if)] + #[lints(collapsible_else_if, collapsible_if)] lint_commented_code: bool = false, /// Whether to suggest reordering constructor fields when initializers are present. /// DEPRECATED CONFIGURATION: lint-inconsistent-struct-field-initializers diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index e0e036757d5..0d66f167a38 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -13,7 +13,7 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator if is_file { exit_if_err( - Command::new(env::var("CARGO").unwrap_or("cargo".into())) + Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) .args(["run", "--bin", "clippy-driver", "--"]) .args(["-L", "./target/debug"]) .args(["-Z", "no-codegen"]) @@ -26,7 +26,7 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator ); } else { exit_if_err( - Command::new(env::var("CARGO").unwrap_or("cargo".into())) + Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) .arg("build") .status(), ); diff --git a/clippy_dev/src/release.rs b/clippy_dev/src/release.rs index 62c1bee8185..15392dd1d29 100644 --- a/clippy_dev/src/release.rs +++ b/clippy_dev/src/release.rs @@ -5,6 +5,7 @@ static CARGO_TOML_FILES: &[&str] = &[ "clippy_config/Cargo.toml", "clippy_lints/Cargo.toml", "clippy_utils/Cargo.toml", + "declare_clippy_lint/Cargo.toml", "Cargo.toml", ]; diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index a2d1236629f..498ffeba9d6 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -28,7 +28,7 @@ pub fn run(port: u16, lint: Option) -> ! { .map(mtime); if times.iter().any(|&time| index_time < time) { - Command::new(env::var("CARGO").unwrap_or("cargo".into())) + Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) .arg("collect-metadata") .spawn() .unwrap() diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 3b827cc5603..5f6e874ffe2 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -2,11 +2,11 @@ use crate::utils::{ ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, expect_action, update_text_region_fn, }; use itertools::Itertools; -use rustc_lexer::{LiteralKind, TokenKind, tokenize}; use std::collections::HashSet; use std::fmt::Write; +use std::fs; use std::ops::Range; -use std::path::{Path, PathBuf}; +use std::path::{self, Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ @@ -37,123 +37,164 @@ pub fn generate_lint_files( deprecated: &[DeprecatedLint], renamed: &[RenamedLint], ) { - FileUpdater::default().update_files_checked( + let mut updater = FileUpdater::default(); + updater.update_file_checked( "cargo dev update_lints", update_mode, - &mut [ - ( - "README.md", - &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); - }), - ), - ( - "book/src/README.md", - &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); - }), - ), - ( - "CHANGELOG.md", - &mut update_text_region_fn( - "\n", - "", - |dst| { - for lint in lints - .iter() - .map(|l| &*l.name) - .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) - .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) - .sorted() - { - writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); - } - }, - ), - ), - ( - "clippy_lints/src/lib.rs", - &mut update_text_region_fn( - "// begin lints modules, do not remove this comment, it's used in `update_lints`\n", - "// end lints modules, do not remove this comment, it's used in `update_lints`", - |dst| { - for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() { - writeln!(dst, "mod {lint_mod};").unwrap(); - } - }, - ), - ), - ("clippy_lints/src/declared_lints.rs", &mut |_, src, dst| { - dst.push_str(GENERATED_FILE_COMMENT); - dst.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); - for (module_name, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() { - writeln!(dst, " crate::{module_name}::{lint_name}_INFO,").unwrap(); - } - dst.push_str("];\n"); - UpdateStatus::from_changed(src != dst) - }), - ("clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| { - let mut searcher = RustSearcher::new(src); - assert!( - searcher.find_token(Token::Ident("declare_with_version")) - && searcher.find_token(Token::Ident("declare_with_version")), - "error reading deprecated lints" - ); - dst.push_str(&src[..searcher.pos() as usize]); - dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); - for lint in deprecated { - write!( - dst, - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - lint.version, lint.name, lint.reason, - ) - .unwrap(); + "README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "book/src/README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "CHANGELOG.md", + &mut update_text_region_fn( + "\n", + "", + |dst| { + for lint in lints + .iter() + .map(|l| &*l.name) + .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) + .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) + .sorted() + { + writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); } - dst.push_str( - "]}\n\n\ + }, + ), + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "clippy_lints/src/deprecated_lints.rs", + &mut |_, src, dst| { + let mut searcher = RustSearcher::new(src); + assert!( + searcher.find_token(Token::Ident("declare_with_version")) + && searcher.find_token(Token::Ident("declare_with_version")), + "error reading deprecated lints" + ); + dst.push_str(&src[..searcher.pos() as usize]); + dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); + for lint in deprecated { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.name, lint.reason, + ) + .unwrap(); + } + dst.push_str( + "]}\n\n\ #[rustfmt::skip]\n\ declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ ", - ); - for lint in renamed { - write!( - dst, - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - lint.version, lint.old_name, lint.new_name, - ) - .unwrap(); + ); + for lint in renamed { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.old_name, lint.new_name, + ) + .unwrap(); + } + dst.push_str("]}\n"); + UpdateStatus::from_changed(src != dst) + }, + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "tests/ui/deprecated.rs", + &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + for lint in deprecated { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }, + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "tests/ui/rename.rs", + &mut move |_, src, dst| { + let mut seen_lints = HashSet::new(); + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); + for lint in renamed { + if seen_lints.insert(&lint.new_name) { + writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); } - dst.push_str("]}\n"); - UpdateStatus::from_changed(src != dst) - }), - ("tests/ui/deprecated.rs", &mut |_, src, dst| { - dst.push_str(GENERATED_FILE_COMMENT); - for lint in deprecated { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); + } + seen_lints.clear(); + for lint in renamed { + if seen_lints.insert(&lint.old_name) { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); } - dst.push_str("\nfn main() {}\n"); - UpdateStatus::from_changed(src != dst) - }), - ("tests/ui/rename.rs", &mut move |_, src, dst| { - let mut seen_lints = HashSet::new(); - dst.push_str(GENERATED_FILE_COMMENT); - dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in renamed { - if seen_lints.insert(&lint.new_name) { - writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }, + ); + for (crate_name, lints) in lints.iter().into_group_map_by(|&l| { + let Some(path::Component::Normal(name)) = l.path.components().next() else { + // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls` + panic!("internal error: can't read crate name from path `{}`", l.path.display()); + }; + name + }) { + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + Path::new(crate_name).join("src/lib.rs"), + &mut update_text_region_fn( + "// begin lints modules, do not remove this comment, it's used in `update_lints`\n", + "// end lints modules, do not remove this comment, it's used in `update_lints`", + |dst| { + for lint_mod in lints + .iter() + .filter(|l| !l.module.is_empty()) + .map(|l| l.module.split_once("::").map_or(&*l.module, |x| x.0)) + .sorted() + .dedup() + { + writeln!(dst, "mod {lint_mod};").unwrap(); } - } - seen_lints.clear(); - for lint in renamed { - if seen_lints.insert(&lint.old_name) { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + }, + ), + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + Path::new(crate_name).join("src/declared_lints.rs"), + &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n"); + for (module_path, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() { + if module_path.is_empty() { + writeln!(dst, " crate::{lint_name}_INFO,").unwrap(); + } else { + writeln!(dst, " crate::{module_path}::{lint_name}_INFO,").unwrap(); } } - dst.push_str("\nfn main() {}\n"); + dst.push_str("];\n"); UpdateStatus::from_changed(src != dst) - }), - ], - ); + }, + ); + } } fn round_to_fifty(count: usize) -> usize { @@ -187,13 +228,25 @@ pub struct RenamedLint { pub fn find_lint_decls() -> Vec { let mut lints = Vec::with_capacity(1000); let mut contents = String::new(); - for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { - parse_clippy_lint_decls( - file.path(), - File::open_read_to_cleared_string(file.path(), &mut contents), - &module, - &mut lints, - ); + for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { + let e = expect_action(e, ErrAction::Read, "."); + if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() { + continue; + } + let Ok(mut name) = e.file_name().into_string() else { + continue; + }; + if name.starts_with("clippy_lints") && name != "clippy_lints_internal" { + name.push_str("/src"); + for (file, module) in read_src_with_module(name.as_ref()) { + parse_clippy_lint_decls( + file.path(), + File::open_read_to_cleared_string(file.path(), &mut contents), + &module, + &mut lints, + ); + } + } } lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); lints @@ -205,7 +258,7 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator (Vec, Vec) { /// Removes the line splices and surrounding quotes from a string literal fn parse_str_lit(s: &str) -> String { - let (s, mode) = if let Some(s) = s.strip_prefix("r") { - (s.trim_matches('#'), rustc_literal_escaper::Mode::RawStr) - } else { - (s, rustc_literal_escaper::Mode::Str) - }; + let s = s.strip_prefix("r").unwrap_or(s).trim_matches('#'); let s = s .strip_prefix('"') .and_then(|s| s.strip_suffix('"')) .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); let mut res = String::with_capacity(s.len()); - rustc_literal_escaper::unescape_str(s, |range, ch| { + rustc_literal_escaper::unescape_str(s, &mut |_, ch| { if let Ok(ch) = ch { res.push(ch); } diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index c4808b7048b..89962a11034 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -383,21 +383,6 @@ impl FileUpdater { self.update_file_checked_inner(tool, mode, path.as_ref(), update); } - #[expect(clippy::type_complexity)] - pub fn update_files_checked( - &mut self, - tool: &str, - mode: UpdateMode, - files: &mut [( - impl AsRef, - &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, - )], - ) { - for (path, update) in files { - self.update_file_checked_inner(tool, mode, path.as_ref(), update); - } - } - pub fn update_file( &mut self, path: impl AsRef, diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 39e4e2e365e..c03cc99b581 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.89" +version = "0.1.90" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -13,6 +13,7 @@ arrayvec = { version = "0.7", default-features = false } cargo_metadata = "0.18" clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } +declare_clippy_lint = { path = "../declare_clippy_lint" } itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" diff --git a/clippy_lints/src/attrs/inline_always.rs b/clippy_lints/src/attrs/inline_always.rs index 58e51128a0d..b8f93ee5e2c 100644 --- a/clippy_lints/src/attrs/inline_always.rs +++ b/clippy_lints/src/attrs/inline_always.rs @@ -1,10 +1,10 @@ use super::INLINE_ALWAYS; use clippy_utils::diagnostics::span_lint; -use rustc_attr_data_structures::{find_attr, AttributeKind, InlineAttr}; +use rustc_attr_data_structures::{AttributeKind, InlineAttr, find_attr}; use rustc_hir::Attribute; use rustc_lint::LateContext; -use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_span::symbol::Symbol; pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { if span.from_expansion() { diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 9a124298041..91c2dc7f3dc 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -207,7 +207,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for usage of the `#[allow]` attribute and suggests replacing it with - /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) + /// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) /// /// This lint only warns outer attributes (`#[allow]`), as inner attributes /// (`#![allow]`) are usually used to enable or disable lints on a global scale. diff --git a/clippy_lints/src/attrs/utils.rs b/clippy_lints/src/attrs/utils.rs index a5ce2137bff..7b66f91f6c0 100644 --- a/clippy_lints/src/attrs/utils.rs +++ b/clippy_lints/src/attrs/utils.rs @@ -46,11 +46,13 @@ pub(super) fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> b } fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool { - block.stmts.first().map_or( - block - .expr - .as_ref() - .is_some_and(|e| is_relevant_expr(cx, typeck_results, e)), + block.stmts.first().map_or_else( + || { + block + .expr + .as_ref() + .is_some_and(|e| is_relevant_expr(cx, typeck_results, e)) + }, |stmt| match &stmt.kind { StmtKind::Let(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index 7cde007a9b6..70c9c45a60c 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -2,9 +2,9 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed, is_mutable}; +use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable}; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, ExprKind, UnOp}; +use rustc_hir::{BorrowKind, Expr, ExprKind, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; @@ -48,7 +48,7 @@ declare_clippy_lint! { declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) { if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, addrof_target) = e.kind && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..)) @@ -76,6 +76,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { && let e_ty = cx.typeck_results().expr_ty_adjusted(e) // check if the reference is coercing to a mutable reference && (!matches!(e_ty.kind(), ty::Ref(_, _, Mutability::Mut)) || is_mutable(cx, deref_target)) + // If the new borrow might be itself borrowed mutably and the original reference is not a temporary + // value, do not propose to use it directly. + && (is_expr_temporary_value(cx, deref_target) || !potentially_bound_to_mutable_ref(cx, e)) && let Some(deref_text) = deref_target.span.get_source_text(cx) { span_lint_and_then( @@ -110,3 +113,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { } } } + +/// Checks if `expr` is used as part of a `let` statement containing a `ref mut` binding. +fn potentially_bound_to_mutable_ref<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::LetStmt(let_stmt) + if let_stmt.pat.contains_explicit_ref_binding() == Some(Mutability::Mut)) +} diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 9a1ad8a7473..a70bd886191 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -168,7 +168,7 @@ fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<' // Rust's integer pow() functions take an unsigned exponent. let exponent_val = get_const_unsigned_int_eval(cx, exponent, None); - let exponent_is_even = exponent_val.map(|val| val % 2 == 0); + let exponent_is_even = exponent_val.map(|val| val.is_multiple_of(2)); match (base_sign, exponent_is_even) { // Non-negative bases always return non-negative results, ignoring overflow. diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 7f6ecea99fb..1854d86c53b 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,13 +1,16 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability}; +use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; +use clippy_utils::{span_contains_non_whitespace, tokenize_with_text}; use rustc_ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::Span; +use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -90,35 +93,74 @@ impl CollapsibleIf { } } - fn check_collapsible_else_if(cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) { - if !block_starts_with_comment(cx, else_block) - && let Some(else_) = expr_block(else_block) + fn check_collapsible_else_if(&self, cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) { + if let Some(else_) = expr_block(else_block) && cx.tcx.hir_attrs(else_.hir_id).is_empty() && !else_.span.from_expansion() - && let ExprKind::If(..) = else_.kind + && let ExprKind::If(else_if_cond, ..) = else_.kind + && !block_starts_with_significant_tokens(cx, else_block, else_, self.lint_commented_code) { - // Prevent "elseif" - // Check that the "else" is followed by whitespace - let up_to_else = then_span.between(else_block.span); - let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { - !c.is_whitespace() - } else { - false - }; - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( + span_lint_and_then( cx, COLLAPSIBLE_ELSE_IF, else_block.span, "this `else { if .. }` block can be collapsed", - "collapse nested if block", - format!( - "{}{}", - if requires_space { " " } else { "" }, - snippet_block_with_applicability(cx, else_.span, "..", Some(else_block.span), &mut applicability) - ), - applicability, + |diag| { + let up_to_else = then_span.between(else_block.span); + let else_before_if = else_.span.shrink_to_lo().with_hi(else_if_cond.span.lo() - BytePos(1)); + if self.lint_commented_code + && let Some(else_keyword_span) = + span_extract_keyword(cx.tcx.sess.source_map(), up_to_else, "else") + && let Some(else_if_keyword_span) = + span_extract_keyword(cx.tcx.sess.source_map(), else_before_if, "if") + { + let else_keyword_span = else_keyword_span.with_leading_whitespace(cx).into_span(); + let else_open_bracket = else_block.span.split_at(1).0.with_leading_whitespace(cx).into_span(); + let else_closing_bracket = { + let end = else_block.span.shrink_to_hi(); + end.with_lo(end.lo() - BytePos(1)) + .with_leading_whitespace(cx) + .into_span() + }; + let sugg = vec![ + // Remove the outer else block `else` + (else_keyword_span, String::new()), + // Replace the inner `if` by `else if` + (else_if_keyword_span, String::from("else if")), + // Remove the outer else block `{` + (else_open_bracket, String::new()), + // Remove the outer else block '}' + (else_closing_bracket, String::new()), + ]; + diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable); + return; + } + + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { + !c.is_whitespace() + } else { + false + }; + let mut applicability = Applicability::MachineApplicable; + diag.span_suggestion( + else_block.span, + "collapse nested if block", + format!( + "{}{}", + if requires_space { " " } else { "" }, + snippet_block_with_applicability( + cx, + else_.span, + "..", + Some(else_block.span), + &mut applicability + ) + ), + applicability, + ); + }, ); } } @@ -130,7 +172,7 @@ impl CollapsibleIf { && self.eligible_condition(cx, check_inner) && let ctxt = expr.span.ctxt() && inner.span.ctxt() == ctxt - && (self.lint_commented_code || !block_starts_with_comment(cx, then)) + && !block_starts_with_significant_tokens(cx, then, inner, self.lint_commented_code) { span_lint_and_then( cx, @@ -141,7 +183,7 @@ impl CollapsibleIf { let then_open_bracket = then.span.split_at(1).0.with_leading_whitespace(cx).into_span(); let then_closing_bracket = { let end = then.span.shrink_to_hi(); - end.with_lo(end.lo() - rustc_span::BytePos(1)) + end.with_lo(end.lo() - BytePos(1)) .with_leading_whitespace(cx) .into_span() }; @@ -179,7 +221,7 @@ impl LateLintPass<'_> for CollapsibleIf { if let Some(else_) = else_ && let ExprKind::Block(else_, None) = else_.kind { - Self::check_collapsible_else_if(cx, then.span, else_); + self.check_collapsible_else_if(cx, then.span, else_); } else if else_.is_none() && self.eligible_condition(cx, cond) && let ExprKind::Block(then, None) = then.kind @@ -190,12 +232,16 @@ impl LateLintPass<'_> for CollapsibleIf { } } -fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { - // We trim all opening braces and whitespaces and then check if the next string is a comment. - let trimmed_block_text = snippet_block(cx, block.span, "..", None) - .trim_start_matches(|c: char| c.is_whitespace() || c == '{') - .to_owned(); - trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") +// Check that nothing significant can be found but whitespaces between the initial `{` of `block` +// and the beginning of `stop_at`. +fn block_starts_with_significant_tokens( + cx: &LateContext<'_>, + block: &Block<'_>, + stop_at: &Expr<'_>, + lint_commented_code: bool, +) -> bool { + let span = block.span.split_at(1).1.until(stop_at.span); + span_contains_non_whitespace(cx, span, lint_commented_code) } /// If `block` is a block with either one expression or a statement containing an expression, @@ -226,3 +272,16 @@ fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { vec![] } } + +fn span_extract_keyword(sm: &SourceMap, span: Span, keyword: &str) -> Option { + let snippet = sm.span_to_snippet(span).ok()?; + tokenize_with_text(&snippet) + .filter(|(t, s, _)| matches!(t, TokenKind::Ident if *s == keyword)) + .map(|(_, _, inner)| { + span.split_at(u32::try_from(inner.start).unwrap()) + .1 + .split_at(u32::try_from(inner.end - inner.start).unwrap()) + .0 + }) + .next() +} diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 5ef726638a5..27918698cd6 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -11,7 +11,7 @@ use clippy_utils::{ use core::iter; use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind, intravisit}; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Node, Stmt, StmtKind, intravisit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; @@ -295,7 +295,7 @@ fn lint_branches_sharing_code<'tcx>( sugg, Applicability::Unspecified, ); - if !cx.typeck_results().expr_ty(expr).is_unit() { + if is_expr_parent_assignment(cx, expr) || !cx.typeck_results().expr_ty(expr).is_unit() { diag.note("the end suggestion probably needs some adjustments to use the expression result correctly"); } } @@ -660,3 +660,17 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { ); } } + +fn is_expr_parent_assignment(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let parent = cx.tcx.parent_hir_node(expr.hir_id); + if let Node::LetStmt(LetStmt { init: Some(e), .. }) + | Node::Expr(Expr { + kind: ExprKind::Assign(_, e, _), + .. + }) = parent + { + return e.hir_id == expr.hir_id; + } + + false +} diff --git a/clippy_lints/src/declare_clippy_lint.rs b/clippy_lints/src/declare_clippy_lint.rs deleted file mode 100644 index 9f82f876727..00000000000 --- a/clippy_lints/src/declare_clippy_lint.rs +++ /dev/null @@ -1,168 +0,0 @@ -#[macro_export] -#[allow(clippy::crate_in_macro_def)] -macro_rules! declare_clippy_lint { - (@ - $(#[doc = $lit:literal])* - pub $lint_name:ident, - $level:ident, - $lintcategory:expr, - $desc:literal, - $version_expr:expr, - $version_lit:literal - $(, $eval_always: literal)? - ) => { - rustc_session::declare_tool_lint! { - $(#[doc = $lit])* - #[clippy::version = $version_lit] - pub clippy::$lint_name, - $level, - $desc, - report_in_external_macro:true - $(, @eval_always = $eval_always)? - } - - pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo { - lint: &$lint_name, - category: $lintcategory, - explanation: concat!($($lit,"\n",)*), - location: concat!(file!(), "#L", line!()), - version: $version_expr - }; - }; - ( - $(#[doc = $lit:literal])* - #[clippy::version = $version:literal] - pub $lint_name:ident, - restriction, - $desc:literal - $(, @eval_always = $eval_always: literal)? - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Allow, crate::LintCategory::Restriction, $desc, - Some($version), $version - $(, $eval_always)? - } - }; - ( - $(#[doc = $lit:literal])* - #[clippy::version = $version:literal] - pub $lint_name:ident, - style, - $desc:literal - $(, @eval_always = $eval_always: literal)? - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Warn, crate::LintCategory::Style, $desc, - Some($version), $version - $(, $eval_always)? - } - }; - ( - $(#[doc = $lit:literal])* - #[clippy::version = $version:literal] - pub $lint_name:ident, - correctness, - $desc:literal - $(, @eval_always = $eval_always: literal)? - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Deny, crate::LintCategory::Correctness, $desc, - Some($version), $version - $(, $eval_always)? - - } - }; - ( - $(#[doc = $lit:literal])* - #[clippy::version = $version:literal] - pub $lint_name:ident, - perf, - $desc:literal - $(, @eval_always = $eval_always: literal)? - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Warn, crate::LintCategory::Perf, $desc, - Some($version), $version - $(, $eval_always)? - } - }; - ( - $(#[doc = $lit:literal])* - #[clippy::version = $version:literal] - pub $lint_name:ident, - complexity, - $desc:literal - $(, @eval_always = $eval_always: literal)? - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Warn, crate::LintCategory::Complexity, $desc, - Some($version), $version - $(, $eval_always)? - } - }; - ( - $(#[doc = $lit:literal])* - #[clippy::version = $version:literal] - pub $lint_name:ident, - suspicious, - $desc:literal - $(, @eval_always = $eval_always: literal)? - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc, - Some($version), $version - $(, $eval_always)? - } - }; - ( - $(#[doc = $lit:literal])* - #[clippy::version = $version:literal] - pub $lint_name:ident, - nursery, - $desc:literal - $(, @eval_always = $eval_always: literal)? - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Allow, crate::LintCategory::Nursery, $desc, - Some($version), $version - $(, $eval_always)? - } - }; - ( - $(#[doc = $lit:literal])* - #[clippy::version = $version:literal] - pub $lint_name:ident, - pedantic, - $desc:literal - $(, @eval_always = $eval_always: literal)? - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc, - Some($version), $version - $(, $eval_always)? - } - }; - ( - $(#[doc = $lit:literal])* - #[clippy::version = $version:literal] - pub $lint_name:ident, - cargo, - $desc:literal - $(, @eval_always = $eval_always: literal)? - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Allow, crate::LintCategory::Cargo, $desc, - Some($version), $version - $(, $eval_always)? - } - }; -} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 1e3907d9ede..c3f8e02b4c0 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -2,7 +2,7 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. -pub static LINTS: &[&crate::LintInfo] = &[ +pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::absolute_paths::ABSOLUTE_PATHS_INFO, crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, @@ -112,6 +112,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::disallowed_names::DISALLOWED_NAMES_INFO, crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_BROKEN_LINK_INFO, crate::doc::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS_INFO, crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO, crate::doc::DOC_LAZY_CONTINUATION_INFO, @@ -590,6 +591,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::operators::IMPOSSIBLE_COMPARISONS_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO, crate::operators::INTEGER_DIVISION_INFO, + crate::operators::MANUAL_IS_MULTIPLE_OF_INFO, crate::operators::MANUAL_MIDPOINT_INFO, crate::operators::MISREFACTORED_ASSIGN_OP_INFO, crate::operators::MODULO_ARITHMETIC_INFO, diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 9814d4fa84f..d55aeae98ed 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -40,6 +40,9 @@ declare_clippy_lint! { /// # When using an inline table, can add a `reason` for why the macro /// # is disallowed. /// { path = "serde::Serialize", reason = "no serializing" }, + /// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`, + /// # it will be silently ignored + /// { path = "std::invalid_macro", reason = "use alternative instead", allow-invalid = true } /// ] /// ``` /// ```no_run diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index fb970e17f38..8c067432cb4 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -34,6 +34,9 @@ declare_clippy_lint! { /// { path = "std::vec::Vec::leak", reason = "no leaking memory" }, /// # Can also add a `replacement` that will be offered as a suggestion. /// { path = "std::sync::Mutex::new", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex::new" }, + /// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`, + /// # it will be silently ignored + /// { path = "std::fs::InvalidPath", reason = "use alternative instead", allow-invalid = true }, /// ] /// ``` /// diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 7875cdd77e8..9a82327a0d7 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -35,6 +35,9 @@ declare_clippy_lint! { /// { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, /// # Can also add a `replacement` that will be offered as a suggestion. /// { path = "std::sync::Mutex", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex" }, + /// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`, + /// # it will be silently ignored + /// { path = "std::invalid::Type", reason = "use alternative instead", allow-invalid = true } /// ] /// ``` /// diff --git a/clippy_lints/src/doc/broken_link.rs b/clippy_lints/src/doc/broken_link.rs new file mode 100644 index 00000000000..a97b807e605 --- /dev/null +++ b/clippy_lints/src/doc/broken_link.rs @@ -0,0 +1,83 @@ +use clippy_utils::diagnostics::span_lint; +use pulldown_cmark::BrokenLink as PullDownBrokenLink; +use rustc_lint::LateContext; +use rustc_resolve::rustdoc::{DocFragment, source_span_for_markdown_range}; +use rustc_span::{BytePos, Pos, Span}; + +use super::DOC_BROKEN_LINK; + +/// Scan and report broken link on documents. +/// It ignores false positives detected by `pulldown_cmark`, and only +/// warns users when the broken link is consider a URL. +// NOTE: We don't check these other cases because +// rustdoc itself will check and warn about it: +// - When a link url is broken across multiple lines in the URL path part +// - When a link tag is missing the close parenthesis character at the end. +// - When a link has whitespace within the url link. +pub fn check(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) { + warn_if_broken_link(cx, bl, doc, fragments); +} + +fn warn_if_broken_link(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) { + if let Some(span) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) { + let mut len = 0; + + // grab raw link data + let (_, raw_link) = doc.split_at(bl.span.start); + + // strip off link text part + let raw_link = match raw_link.split_once(']') { + None => return, + Some((prefix, suffix)) => { + len += prefix.len() + 1; + suffix + }, + }; + + let raw_link = match raw_link.split_once('(') { + None => return, + Some((prefix, suffix)) => { + if !prefix.is_empty() { + // there is text between ']' and '(' chars, so it is not a valid link + return; + } + len += prefix.len() + 1; + suffix + }, + }; + + if raw_link.starts_with("(http") { + // reduce chances of false positive reports + // by limiting this checking only to http/https links. + return; + } + + for c in raw_link.chars() { + if c == ')' { + // it is a valid link + return; + } + + if c == '\n' { + report_broken_link(cx, span, len); + break; + } + + len += 1; + } + } +} + +fn report_broken_link(cx: &LateContext<'_>, frag_span: Span, offset: usize) { + let start = frag_span.lo(); + let end = start + BytePos::from_usize(offset); + + let span = Span::new(start, end, frag_span.ctxt(), frag_span.parent()); + + span_lint( + cx, + DOC_BROKEN_LINK, + span, + "possible broken doc link: broken across multiple lines", + ); +} diff --git a/clippy_lints/src/doc/doc_suspicious_footnotes.rs b/clippy_lints/src/doc/doc_suspicious_footnotes.rs index d3c39686976..3330cc5defd 100644 --- a/clippy_lints/src/doc/doc_suspicious_footnotes.rs +++ b/clippy_lints/src/doc/doc_suspicious_footnotes.rs @@ -49,11 +49,7 @@ pub fn check(cx: &LateContext<'_>, doc: &str, range: Range, fragments: &F .filter(|attr| attr.span().overlaps(this_fragment.span)) .rev() .find_map(|attr| { - Some(( - attr, - attr.doc_str_and_comment_kind()?, - attr.doc_resolution_scope()?, - )) + Some((attr, attr.doc_str_and_comment_kind()?, attr.doc_resolution_scope()?)) }) .unwrap(); let (to_add, terminator) = match (doc_attr_comment_kind, attr_style) { diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index d38588bb799..5ea55e102df 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -24,6 +24,7 @@ use rustc_span::edition::Edition; use std::ops::Range; use url::Url; +mod broken_link; mod doc_comment_double_space_linebreaks; mod doc_suspicious_footnotes; mod include_in_doc_without_cfg; @@ -292,6 +293,34 @@ declare_clippy_lint! { "possible typo for an intra-doc link" } +declare_clippy_lint! { + /// ### What it does + /// Checks the doc comments have unbroken links, mostly caused + /// by bad formatted links such as broken across multiple lines. + /// + /// ### Why is this bad? + /// Because documentation generated by rustdoc will be broken + /// since expected links won't be links and just text. + /// + /// ### Examples + /// This link is broken: + /// ```no_run + /// /// [example of a bad link](https:// + /// /// github.com/rust-lang/rust-clippy/) + /// pub fn do_something() {} + /// ``` + /// + /// It shouldn't be broken across multiple lines to work: + /// ```no_run + /// /// [example of a good link](https://github.com/rust-lang/rust-clippy/) + /// pub fn do_something() {} + /// ``` + #[clippy::version = "1.84.0"] + pub DOC_BROKEN_LINK, + pedantic, + "broken document link" +} + declare_clippy_lint! { /// ### What it does /// Checks for the doc comments of publicly visible @@ -656,6 +685,7 @@ impl Documentation { impl_lint_pass!(Documentation => [ DOC_LINK_CODE, DOC_LINK_WITH_QUOTES, + DOC_BROKEN_LINK, DOC_MARKDOWN, DOC_NESTED_REFDEFS, MISSING_SAFETY_DOC, @@ -786,9 +816,9 @@ struct DocHeaders { /// back in the various late lint pass methods if they need the final doc headers, like "Safety" or /// "Panics" sections. fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> Option { - /// We don't want the parser to choke on intra doc links. Since we don't - /// actually care about rendering them, just pretend that all broken links - /// point to a fake address. + // We don't want the parser to choke on intra doc links. Since we don't + // actually care about rendering them, just pretend that all broken links + // point to a fake address. #[expect(clippy::unnecessary_wraps)] // we're following a type signature fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> { Some(("fake".into(), "fake".into())) @@ -828,14 +858,12 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ return Some(DocHeaders::default()); } - let mut cb = fake_broken_link_callback; - check_for_code_clusters( cx, pulldown_cmark::Parser::new_with_broken_link_callback( &doc, main_body_opts() - Options::ENABLE_SMART_PUNCTUATION, - Some(&mut cb), + Some(&mut fake_broken_link_callback), ) .into_offset_iter(), &doc, @@ -845,9 +873,17 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ }, ); + // NOTE: check_doc uses it own cb function, + // to avoid causing duplicated diagnostics for the broken link checker. + let mut full_fake_broken_link_callback = |bl: BrokenLink<'_>| -> Option<(CowStr<'_>, CowStr<'_>)> { + broken_link::check(cx, &bl, &doc, &fragments); + Some(("fake".into(), "fake".into())) + }; + // disable smart punctuation to pick up ['link'] more easily let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION; - let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb)); + let parser = + pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut full_fake_broken_link_callback)); Some(check_doc( cx, diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs index 7ba11c20f45..74283d7ba86 100644 --- a/clippy_lints/src/doc/needless_doctest_main.rs +++ b/clippy_lints/src/doc/needless_doctest_main.rs @@ -71,6 +71,7 @@ pub fn check( if !ignore { get_test_spans(&item, *ident, &mut test_attr_spans); } + let is_async = matches!(sig.header.coroutine_kind, Some(CoroutineKind::Async { .. })); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, @@ -89,9 +90,14 @@ pub fn check( // Another function was found; this case is ignored for needless_doctest_main ItemKind::Fn(fn_) => { eligible = false; - if !ignore { - get_test_spans(&item, fn_.ident, &mut test_attr_spans); + if ignore { + // If ignore is active invalidating one lint, + // and we already found another function thus + // invalidating the other one, we have no + // business continuing. + return (false, test_attr_spans); } + get_test_spans(&item, fn_.ident, &mut test_attr_spans); }, // Tests with one of these items are ignored ItemKind::Static(..) @@ -104,7 +110,10 @@ pub fn check( }, Ok(None) => break, Err(e) => { - e.cancel(); + // See issue #15041. When calling `.cancel()` on the `Diag`, Clippy will unexpectedly panic + // when the `Diag` is unwinded. Meanwhile, we can just call `.emit()`, since the `DiagCtxt` + // is just a sink, nothing will be printed. + e.emit(); return (false, test_attr_spans); }, } @@ -119,6 +128,18 @@ pub fn check( let trailing_whitespace = text.len() - text.trim_end().len(); + // We currently only test for "fn main". Checking for the real + // entrypoint (with tcx.entry_fn(())) in each block would be unnecessarily + // expensive, as those are probably intended and relevant. Same goes for + // macros and other weird ways of declaring a main function. + // + // Also, as we only check for attribute names and don't do macro expansion, + // we can check only for #[test] + + if !((text.contains("main") && text.contains("fn")) || text.contains("#[test]")) { + return; + } + // Because of the global session, we need to create a new session in a different thread with // the edition we need. let text = text.to_owned(); diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs index 0c5f8bbf4ca..3bd74856165 100644 --- a/clippy_lints/src/empty_line_after.rs +++ b/clippy_lints/src/empty_line_after.rs @@ -10,7 +10,7 @@ use rustc_errors::{Applicability, Diag, SuggestionStyle}; use rustc_lexer::TokenKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw}; +use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw, sym}; declare_clippy_lint! { /// ### What it does @@ -129,10 +129,55 @@ struct Stop { kind: StopKind, first: usize, last: usize, + name: Option, } impl Stop { - fn convert_to_inner(&self) -> (Span, String) { + fn is_outer_attr_only(&self) -> bool { + let Some(name) = self.name else { + return false; + }; + // Check if the attribute only has effect when as an outer attribute + // The below attributes are collected from the builtin attributes of The Rust Reference + // https://doc.rust-lang.org/reference/attributes.html#r-attributes.builtin + // And the comments below are from compiler errors and warnings + matches!( + name, + // Cannot be used at crate level + sym::repr | sym::test | sym::derive | sym::automatically_derived | sym::path | sym::global_allocator | + // Only has an effect on macro definitions + sym::macro_export | + // Only be applied to trait definitions + sym::on_unimplemented | + // Only be placed on trait implementations + sym::do_not_recommend | + // Only has an effect on items + sym::ignore | sym::should_panic | sym::proc_macro | sym::proc_macro_derive | sym::proc_macro_attribute | + // Has no effect when applied to a module + sym::must_use | + // Should be applied to a foreign function or static + sym::link_name | sym::link_ordinal | sym::link_section | + // Should be applied to an `extern crate` item + sym::no_link | + // Should be applied to a free function, impl method or static + sym::export_name | sym::no_mangle | + // Should be applied to a `static` variable + sym::used | + // Should be applied to function or closure + sym::inline | + // Should be applied to a function definition + sym::cold | sym::target_feature | sym::track_caller | sym::instruction_set | + // Should be applied to a struct or enum + sym::non_exhaustive | + // Note: No any warning when it as an inner attribute, but it has no effect + sym::panic_handler + ) + } + + fn convert_to_inner(&self) -> Option<(Span, String)> { + if self.is_outer_attr_only() { + return None; + } let inner = match self.kind { // #![...] StopKind::Attr => InnerSpan::new(1, 1), @@ -140,7 +185,7 @@ impl Stop { // ^ ^ StopKind::Doc(_) => InnerSpan::new(2, 3), }; - (self.span.from_inner(inner), "!".into()) + Some((self.span.from_inner(inner), "!".into())) } fn comment_out(&self, cx: &EarlyContext<'_>, suggestions: &mut Vec<(Span, String)>) { @@ -177,6 +222,7 @@ impl Stop { }, first: file.lookup_line(file.relative_position(lo))?, last: file.lookup_line(file.relative_position(hi))?, + name: attr.name(), }) } } @@ -356,6 +402,12 @@ impl EmptyLineAfter { if let Some(parent) = self.items.iter().rev().nth(1) && (parent.kind == "module" || parent.kind == "crate") && parent.mod_items == Some(id) + && let suggestions = gaps + .iter() + .flat_map(|gap| gap.prev_chunk) + .filter_map(Stop::convert_to_inner) + .collect::>() + && !suggestions.is_empty() { let desc = if parent.kind == "module" { "parent module" @@ -367,10 +419,7 @@ impl EmptyLineAfter { StopKind::Attr => format!("if the attribute should apply to the {desc} use an inner attribute"), StopKind::Doc(_) => format!("if the comment should document the {desc} use an inner doc comment"), }, - gaps.iter() - .flat_map(|gap| gap.prev_chunk) - .map(Stop::convert_to_inner) - .collect(), + suggestions, Applicability::MaybeIncorrect, ); } @@ -425,6 +474,7 @@ impl EmptyLineAfter { first: line.line, // last doesn't need to be accurate here, we don't compare it with anything last: line.line, + name: None, }); } diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index b0077a9b05f..0288747d6f3 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; @@ -109,14 +109,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx { let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" }; // replace `|| vec![]` with `Vec::new` - span_lint_and_sugg( + span_lint_hir_and_then( cx, REDUNDANT_CLOSURE, + expr.hir_id, expr.span, "redundant closure", - "replace the closure with `Vec::new`", - format!("{vec_crate}::vec::Vec::new"), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + expr.span, + "replace the closure with `Vec::new`", + format!("{vec_crate}::vec::Vec::new"), + Applicability::MachineApplicable, + ); + }, ); } // skip `foo(|| macro!())` @@ -198,41 +204,48 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx // For now ignore all callee types which reference a type parameter. && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) { - span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { - if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).is_some_and(|l| { - // FIXME: Do we really need this `local_used_in` check? - // Isn't it checking something like... `callee(callee)`? - // If somehow this check is needed, add some test for it, - // 'cuz currently nothing changes after deleting this check. - local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) - }) { - match cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .err_ctxt() - .type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { - // Mutable closure is used after current expr; we cannot consume it. - Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), - Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { - snippet = format!("&{snippet}"); - }, - _ => (), + span_lint_hir_and_then( + cx, + REDUNDANT_CLOSURE, + expr.hir_id, + expr.span, + "redundant closure", + |diag| { + if let Some(mut snippet) = snippet_opt(cx, callee.span) { + if path_to_local(callee).is_some_and(|l| { + // FIXME: Do we really need this `local_used_in` check? + // Isn't it checking something like... `callee(callee)`? + // If somehow this check is needed, add some test for it, + // 'cuz currently nothing changes after deleting this check. + local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) + }) { + match cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { + // Mutable closure is used after current expr; we cannot consume it. + Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), + Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + snippet = format!("&{snippet}"); + }, + _ => (), + } } + diag.span_suggestion( + expr.span, + "replace the closure with the function itself", + snippet, + Applicability::MachineApplicable, + ); } - diag.span_suggestion( - expr.span, - "replace the closure with the function itself", - snippet, - Applicability::MachineApplicable, - ); - } - }); + }, + ); } }, ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { @@ -245,9 +258,10 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx Some(span) => format!("::{}", snippet_with_applicability(cx, span, "<..>", &mut app)), None => String::new(), }; - span_lint_and_then( + span_lint_hir_and_then( cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + expr.hir_id, expr.span, "redundant closure", |diag| { diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 1fb0e4d24d0..86d9038ec45 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -76,7 +76,7 @@ impl LateLintPass<'_> for ExhaustiveItems { "exported enums should not be exhaustive", [].as_slice(), ), - ItemKind::Struct(_, _, v) => ( + ItemKind::Struct(_, _, v) if v.fields().iter().all(|f| f.default.is_none()) => ( EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive", v.fields(), diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 3c7e83b0697..b3c9e860758 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -5,14 +5,13 @@ use clippy_utils::{ eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym, }; +use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; - -use rustc_ast::ast; use std::f32::consts as f32_consts; use std::f64::consts as f64_consts; use sugg::Sugg; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index c0c23e217fd..d959981a83c 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -14,9 +14,9 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{return_ty, trait_ref_of_method}; -use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_span::Symbol; use rustc_attr_data_structures::{AttributeKind, find_attr}; +use rustc_span::Symbol; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use core::ops::ControlFlow; @@ -34,7 +34,17 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some((attr_span, reason)) = attr { - check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig); + check_needless_must_use( + cx, + sig.decl, + item.owner_id, + item.span, + fn_header_span, + *attr_span, + *reason, + attrs, + sig, + ); } else if is_public && !is_proc_macro(attrs) && !find_attr!(attrs, AttributeKind::NoMangle(..)) { check_must_use_candidate( cx, @@ -54,9 +64,20 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir_attrs(item.hir_id()); - let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason)); + let attr = + find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason)); if let Some((attr_span, reason)) = attr { - check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig); + check_needless_must_use( + cx, + sig.decl, + item.owner_id, + item.span, + fn_header_span, + *attr_span, + *reason, + attrs, + sig, + ); } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id).is_none() { check_must_use_candidate( cx, @@ -77,9 +98,20 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir_attrs(item.hir_id()); - let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason)); + let attr = + find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason)); if let Some((attr_span, reason)) = attr { - check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig); + check_needless_must_use( + cx, + sig.decl, + item.owner_id, + item.span, + fn_header_span, + *attr_span, + *reason, + attrs, + sig, + ); } else if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir_body(eid); if attr.is_none() && is_public && !is_proc_macro(attrs) { @@ -121,12 +153,7 @@ fn check_needless_must_use( fn_header_span, "this unit-returning function has a `#[must_use]` attribute", |diag| { - diag.span_suggestion( - attr_span, - "remove the attribute", - "", - Applicability::MachineApplicable, - ); + diag.span_suggestion(attr_span, "remove the attribute", "", Applicability::MachineApplicable); }, ); } else { diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 45f9aa0a53e..ab7a965b367 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::consts::is_zero_integer_const; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_else_clause; use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet}; @@ -48,13 +48,6 @@ declare_clippy_lint! { declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]); -fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { - if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) { - return Constant::Int(0) == value; - } - false -} - impl LateLintPass<'_> for IfNotElse { fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { if let ExprKind::If(cond, cond_inner, Some(els)) = e.kind @@ -68,7 +61,7 @@ impl LateLintPass<'_> for IfNotElse { ), // Don't lint on `… != 0`, as these are likely to be bit tests. // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order. - ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_const(rhs, cx) => ( + ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => ( "unnecessary `!=` operation", "change to `==` and swap the blocks of the `if`/`else`", ), diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 617c006795b..ffe6ad14f63 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::DiagExt; -use rustc_attr_data_structures::{find_attr, AttributeKind}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -33,10 +33,10 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind && let Some(attr_span) = find_attr!(cx - .tcx - .hir_attrs(item.hir_id()), - AttributeKind::Inline(_, span) => *span - ) + .tcx + .hir_attrs(item.hir_id()), + AttributeKind::Inline(_, span) => *span + ) { span_lint_and_then( cx, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index be9142b17fe..96a6dee5885 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -59,10 +59,10 @@ extern crate smallvec; extern crate thin_vec; #[macro_use] -mod declare_clippy_lint; +extern crate clippy_utils; #[macro_use] -extern crate clippy_utils; +extern crate declare_clippy_lint; mod utils; @@ -411,108 +411,9 @@ mod zombie_processes; use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::{Lint, LintId}; +use rustc_lint::Lint; use utils::attr_collector::{AttrCollector, AttrStorage}; -#[derive(Default)] -struct RegistrationGroups { - all: Vec, - cargo: Vec, - complexity: Vec, - correctness: Vec, - nursery: Vec, - pedantic: Vec, - perf: Vec, - restriction: Vec, - style: Vec, - suspicious: Vec, -} - -impl RegistrationGroups { - #[rustfmt::skip] - fn register(self, store: &mut rustc_lint::LintStore) { - store.register_group(true, "clippy::all", Some("clippy_all"), self.all); - store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo); - store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity); - store.register_group(true, "clippy::correctness", Some("clippy_correctness"), self.correctness); - store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery); - store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic); - store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf); - store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction); - store.register_group(true, "clippy::style", Some("clippy_style"), self.style); - store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious); - } -} - -#[derive(Copy, Clone, Debug)] -pub(crate) enum LintCategory { - Cargo, - Complexity, - Correctness, - Nursery, - Pedantic, - Perf, - Restriction, - Style, - Suspicious, -} - -#[allow(clippy::enum_glob_use)] -use LintCategory::*; - -impl LintCategory { - fn is_all(self) -> bool { - matches!(self, Correctness | Suspicious | Style | Complexity | Perf) - } - - fn group(self, groups: &mut RegistrationGroups) -> &mut Vec { - match self { - Cargo => &mut groups.cargo, - Complexity => &mut groups.complexity, - Correctness => &mut groups.correctness, - Nursery => &mut groups.nursery, - Pedantic => &mut groups.pedantic, - Perf => &mut groups.perf, - Restriction => &mut groups.restriction, - Style => &mut groups.style, - Suspicious => &mut groups.suspicious, - } - } -} - -pub struct LintInfo { - /// Double reference to maintain pointer equality - pub lint: &'static &'static Lint, - category: LintCategory, - pub explanation: &'static str, - /// e.g. `clippy_lints/src/absolute_paths.rs#43` - pub location: &'static str, - pub version: Option<&'static str>, -} - -impl LintInfo { - /// Returns the lint name in lowercase without the `clippy::` prefix - #[allow(clippy::missing_panics_doc)] - pub fn name_lower(&self) -> String { - self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase() - } - - /// Returns the name of the lint's category in lowercase (`style`, `pedantic`) - pub fn category_str(&self) -> &'static str { - match self.category { - Cargo => "cargo", - Complexity => "complexity", - Correctness => "correctness", - Nursery => "nursery", - Pedantic => "pedantic", - Perf => "perf", - Restriction => "restriction", - Style => "style", - Suspicious => "suspicious", - } - } -} - pub fn explain(name: &str) -> i32 { let target = format!("clippy::{}", name.to_ascii_uppercase()); @@ -535,30 +436,11 @@ pub fn explain(name: &str) -> i32 { } } -fn register_categories(store: &mut rustc_lint::LintStore) { - let mut groups = RegistrationGroups::default(); - - for LintInfo { lint, category, .. } in declared_lints::LINTS { - if category.is_all() { - groups.all.push(LintId::of(lint)); - } - - category.group(&mut groups).push(LintId::of(lint)); - } - - let lints: Vec<&'static Lint> = declared_lints::LINTS.iter().map(|info| *info.lint).collect(); - - store.register_lints(&lints); - groups.register(store); -} - /// Register all lints and lint groups with the rustc lint store /// /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] -pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - register_categories(store); - +pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Conf) { for (old_name, new_name) in deprecated_lints::RENAMED { store.register_renamed(old_name, new_name); } diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 388034c39f5..e792edbe23e 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -163,15 +163,14 @@ impl<'tcx> Visitor<'tcx> for SameItemPushVisitor<'_, 'tcx> { StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(expr), _ => {}, } + } + // Current statement is a push ...check whether another + // push had been previously done + else if self.vec_push.is_none() { + self.vec_push = vec_push_option; } else { - // Current statement is a push ...check whether another - // push had been previously done - if self.vec_push.is_none() { - self.vec_push = vec_push_option; - } else { - // There are multiple pushes ... don't lint - self.multiple_pushes = true; - } + // There are multiple pushes ... don't lint + self.multiple_pushes = true; } } } diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 0b3bec714c0..9ff82cdcb66 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -13,7 +13,6 @@ use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; - use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; use std::slice; diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index 4959908dad6..edbb556fd97 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::option_arg_ty; +use clippy_utils::ty::{option_arg_ty, peel_mid_ty_refs_is_mutable}; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; -use rustc_ast::BindingMode; +use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; use rustc_hir::def::{DefKind, Res}; @@ -133,7 +133,21 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok Applicability::MachineApplicable }; let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren(); - let sugg = format!("{scrut}.{method}()"); + + let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee); + let (_, n_ref, mutability) = peel_mid_ty_refs_is_mutable(scrutinee_ty); + let prefix = if n_ref > 0 { + if mutability == Mutability::Mut { + ".as_mut()" + } else { + ".as_ref()" + } + } else { + "" + }; + + let sugg = format!("{scrut}{prefix}.{method}()"); + // If the expression being expanded is the `if …` part of an `else if …`, it must be blockified. let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) && let ExprKind::If(_, _, Some(else_part)) = parent_expr.kind diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 24b4a675800..70a03ff9376 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; @@ -116,11 +117,12 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let format_suggestion = |variant: &VariantDef| { format!( "{}{}{}{}", - if let Some(ident) = wildcard_ident { - format!("{} @ ", ident.name) - } else { - String::new() - }, + wildcard_ident.map_or(String::new(), |ident| { + ident + .span + .get_source_text(cx) + .map_or_else(|| format!("{} @ ", ident.name), |s| format!("{s} @ ")) + }), if let CommonPrefixSearcher::Path(path_prefix) = path_prefix { let mut s = String::new(); for seg in path_prefix { @@ -138,7 +140,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { Some(CtorKind::Fn) if variant.fields.len() == 1 => "(_)", Some(CtorKind::Fn) => "(..)", Some(CtorKind::Const) => "", - None => "{ .. }", + None => " { .. }", } ) }; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 347960e0003..f2dabdd3438 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4426,7 +4426,7 @@ declare_clippy_lint! { /// ```no_run /// use std::io::{BufReader, Read}; /// use std::fs::File; - /// let file = BufReader::new(std::fs::File::open("./bytes.txt").unwrap()); + /// let file = BufReader::new(File::open("./bytes.txt").unwrap()); /// file.bytes(); /// ``` #[clippy::version = "1.87.0"] diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 7bdd999bbba..2139466ce74 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -136,7 +136,7 @@ pub(super) fn check<'tcx>( fun_span: Option, ) -> bool { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 4] = [ + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 5] = [ (sym::BTreeEntry, false, &[sym::or_insert], "with"), (sym::HashMapEntry, false, &[sym::or_insert], "with"), ( @@ -145,16 +145,17 @@ pub(super) fn check<'tcx>( &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], "else", ), - (sym::Result, true, &[sym::or, sym::unwrap_or], "else"), + (sym::Option, false, &[sym::get_or_insert], "with"), + (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), ]; if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) && switch_to_lazy_eval(cx, arg) && !contains_return(arg) && let self_ty = cx.typeck_results().expr_ty(self_expr) - && let Some(&(_, fn_has_arguments, poss, suffix)) = - KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0)) - && poss.contains(&name) + && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES + .iter() + .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) { let ctxt = span.ctxt(); let mut app = Applicability::HasPlaceholders; diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index f835bbb7c56..25c95d23436 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use rustc_attr_data_structures::{find_attr, AttributeKind}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_hir as hir; use rustc_hir::Attribute; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index 8d2f8029112..17d251a7bbb 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -161,7 +161,7 @@ fn path_has_args(p: &QPath<'_>) -> bool { /// - `Copy` itself, or /// - the only use of a mutable reference, or /// - not a variable (created by a function call) -#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] fn needless_borrow_count<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, @@ -232,11 +232,11 @@ fn needless_borrow_count<'tcx>( let mut args_with_referent_ty = callee_args.to_vec(); let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| { - if let ExprKind::Field(base, _) = &referent.kind { - let base_ty = cx.typeck_results().expr_ty(base); - if drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) { - return false; - } + if let ExprKind::Field(base, _) = &referent.kind + && let base_ty = cx.typeck_results().expr_ty(base) + && drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) + { + return false; } let referent_ty = cx.typeck_results().expr_ty(referent); diff --git a/clippy_lints/src/needless_parens_on_range_literals.rs b/clippy_lints/src/needless_parens_on_range_literals.rs index 8a62106377c..021a11593f3 100644 --- a/clippy_lints/src/needless_parens_on_range_literals.rs +++ b/clippy_lints/src/needless_parens_on_range_literals.rs @@ -5,7 +5,6 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; - use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 9aede1dec93..c97ecce75b4 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -124,8 +124,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Note that we do not want to deal with qualified predicates here. match pred.kind().no_bound_vars() { Some(ty::ClauseKind::Trait(pred)) - if pred.def_id() != sized_trait && pred.def_id() != meta_sized_trait - => Some(pred), + if pred.def_id() != sized_trait && pred.def_id() != meta_sized_trait => + { + Some(pred) + }, _ => None, } }) diff --git a/clippy_lints/src/no_mangle_with_rust_abi.rs b/clippy_lints/src/no_mangle_with_rust_abi.rs index 0159c5d2ac1..dee8efeb291 100644 --- a/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_with_applicability}; use rustc_abi::ExternAbi; +use rustc_attr_data_structures::AttributeKind; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind}; +use rustc_hir::{Attribute, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::{BytePos, Pos}; -use rustc_attr_data_structures::AttributeKind; -use rustc_hir::Attribute; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index a27c6aa75e3..5f10e1968f1 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -617,7 +617,7 @@ impl<'tcx> NonCopyConst<'tcx> { // Then a type check. Note we only check the type here as the result // gets cached. - let ty = EarlyBinder::bind(typeck.expr_ty(src_expr)).instantiate(tcx, init_args); + let ty = typeck.expr_ty(src_expr); // Normalized as we need to check if this is an array later. let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index e1fd09549a4..3efbb896358 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -1,12 +1,13 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt, integer_const, is_zero_integer_const}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{clip, peel_hir_expr_refs, unsext}; +use clippy_utils::{ExprUseNode, clip, expr_use_ctxt, peel_hir_expr_refs, unsext}; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Span; +use rustc_span::{Span, kw}; use super::IDENTITY_OP; @@ -17,7 +18,7 @@ pub(crate) fn check<'tcx>( left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { - if !is_allowed(cx, op, left, right) { + if !is_allowed(cx, expr, op, left, right) { return; } @@ -165,14 +166,27 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>) Parens::Needed } -fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool { +fn is_allowed<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + cmp: BinOpKind, + left: &Expr<'tcx>, + right: &Expr<'tcx>, +) -> bool { + // Exclude case where the left or right side is associated function call returns a type which is + // `Self` that is not given explicitly, and the expression is not a let binding's init + // expression and the let binding has a type annotation, or a function's return value. + if (is_assoc_fn_without_type_instance(cx, left) || is_assoc_fn_without_type_instance(cx, right)) + && !is_expr_used_with_type_annotation(cx, expr) + { + return false; + } + // This lint applies to integers and their references cx.typeck_results().expr_ty(left).peel_refs().is_integral() && cx.typeck_results().expr_ty(right).peel_refs().is_integral() // `1 << 0` is a common pattern in bit manipulation code - && !(cmp == BinOpKind::Shl - && ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0)) - && ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1))) + && !(cmp == BinOpKind::Shl && is_zero_integer_const(cx, right) && integer_const(cx, left) == Some(1)) } fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { @@ -234,3 +248,47 @@ fn span_ineffective_operation( applicability, ); } + +fn is_expr_used_with_type_annotation<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + match expr_use_ctxt(cx, expr).use_node(cx) { + ExprUseNode::LetStmt(letstmt) => letstmt.ty.is_some(), + ExprUseNode::Return(_) => true, + _ => false, + } +} + +/// Check if the expression is an associated function without a type instance. +/// Example: +/// ``` +/// trait Def { +/// fn def() -> Self; +/// } +/// impl Def for usize { +/// fn def() -> Self { +/// 0 +/// } +/// } +/// fn test() { +/// let _ = 0usize + &Default::default(); +/// let _ = 0usize + &Def::def(); +/// } +/// ``` +fn is_assoc_fn_without_type_instance<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool { + if let ExprKind::Call(func, _) = peel_hir_expr_refs(expr).0.kind + && let ExprKind::Path(QPath::Resolved( + // If it's not None, don't need to go further. + None, + Path { + res: Res::Def(DefKind::AssocFn, def_id), + .. + }, + )) = func.kind + && let output_ty = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder().output() + && let ty::Param(ty::ParamTy { + name: kw::SelfUpper, .. + }) = output_ty.kind() + { + return true; + } + false +} diff --git a/clippy_lints/src/operators/manual_is_multiple_of.rs b/clippy_lints/src/operators/manual_is_multiple_of.rs new file mode 100644 index 00000000000..821178a4315 --- /dev/null +++ b/clippy_lints/src/operators/manual_is_multiple_of.rs @@ -0,0 +1,66 @@ +use clippy_utils::consts::is_zero_integer_const; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use rustc_ast::BinOpKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; + +use super::MANUAL_IS_MULTIPLE_OF; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'_>, + op: BinOpKind, + lhs: &'tcx Expr<'tcx>, + rhs: &'tcx Expr<'tcx>, + msrv: Msrv, +) { + if msrv.meets(cx, msrvs::UNSIGNED_IS_MULTIPLE_OF) + && let Some(operand) = uint_compare_to_zero(cx, op, lhs, rhs) + && let ExprKind::Binary(operand_op, operand_left, operand_right) = operand.kind + && operand_op.node == BinOpKind::Rem + { + let mut app = Applicability::MachineApplicable; + let divisor = Sugg::hir_with_applicability(cx, operand_right, "_", &mut app); + span_lint_and_sugg( + cx, + MANUAL_IS_MULTIPLE_OF, + expr.span, + "manual implementation of `.is_multiple_of()`", + "replace with", + format!( + "{}{}.is_multiple_of({divisor})", + if op == BinOpKind::Eq { "" } else { "!" }, + Sugg::hir_with_applicability(cx, operand_left, "_", &mut app).maybe_paren() + ), + app, + ); + } +} + +// If we have a `x == 0`, `x != 0` or `x > 0` (or the reverted ones), return the non-zero operand +fn uint_compare_to_zero<'tcx>( + cx: &LateContext<'tcx>, + op: BinOpKind, + lhs: &'tcx Expr<'tcx>, + rhs: &'tcx Expr<'tcx>, +) -> Option<&'tcx Expr<'tcx>> { + let operand = if matches!(lhs.kind, ExprKind::Binary(..)) + && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Gt) + && is_zero_integer_const(cx, rhs) + { + lhs + } else if matches!(rhs.kind, ExprKind::Binary(..)) + && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Lt) + && is_zero_integer_const(cx, lhs) + { + rhs + } else { + return None; + }; + + matches!(cx.typeck_results().expr_ty_adjusted(operand).kind(), ty::Uint(_)).then_some(operand) +} diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 2f4e8e99588..bdbbb3475cd 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -11,6 +11,7 @@ mod float_cmp; mod float_equality_without_abs; mod identity_op; mod integer_division; +mod manual_is_multiple_of; mod manual_midpoint; mod misrefactored_assign_op; mod modulo_arithmetic; @@ -830,12 +831,42 @@ declare_clippy_lint! { "manual implementation of `midpoint` which can overflow" } +declare_clippy_lint! { + /// ### What it does + /// Checks for manual implementation of `.is_multiple_of()` on + /// unsigned integer types. + /// + /// ### Why is this bad? + /// `a.is_multiple_of(b)` is a clearer way to check for divisibility + /// of `a` by `b`. This expression can never panic. + /// + /// ### Example + /// ```no_run + /// # let (a, b) = (3u64, 4u64); + /// if a % b == 0 { + /// println!("{a} is divisible by {b}"); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// # let (a, b) = (3u64, 4u64); + /// if a.is_multiple_of(b) { + /// println!("{a} is divisible by {b}"); + /// } + /// ``` + #[clippy::version = "1.89.0"] + pub MANUAL_IS_MULTIPLE_OF, + complexity, + "manual implementation of `.is_multiple_of()`" +} + pub struct Operators { arithmetic_context: numeric_arithmetic::Context, verbose_bit_mask_threshold: u64, modulo_arithmetic_allow_comparison_to_zero: bool, msrv: Msrv, } + impl Operators { pub fn new(conf: &'static Conf) -> Self { Self { @@ -874,6 +905,7 @@ impl_lint_pass!(Operators => [ NEEDLESS_BITWISE_BOOL, SELF_ASSIGNMENT, MANUAL_MIDPOINT, + MANUAL_IS_MULTIPLE_OF, ]); impl<'tcx> LateLintPass<'tcx> for Operators { @@ -891,6 +923,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { identity_op::check(cx, e, op.node, lhs, rhs); needless_bitwise_bool::check(cx, e, op.node, lhs, rhs); manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv); + manual_is_multiple_of::check(cx, e, op.node, lhs, rhs, self.msrv); } self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs); bit_mask::check(cx, e, op.node, lhs, rhs); diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index e18bdfb34ac..b8005dfd6f8 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -3,10 +3,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{is_self, is_self_ty}; -use rustc_attr_data_structures::{find_attr, AttributeKind, InlineAttr}; -use rustc_data_structures::fx::FxHashSet; use core::ops::ControlFlow; use rustc_abi::ExternAbi; +use rustc_attr_data_structures::{AttributeKind, InlineAttr, find_attr}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index c02e5e0621c..de12a25b03d 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -142,6 +142,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { && let Some(ret) = find_let_else_ret_expression(els) && let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, ret) && !span_contains_comment(cx.tcx.sess.source_map(), els.span) + && !span_contains_cfg(cx, els.span) { let mut applicability = Applicability::MaybeIncorrect; let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren(); diff --git a/clippy_lints/src/question_mark_used.rs b/clippy_lints/src/question_mark_used.rs index 96ea485d769..7bbbd0d25ac 100644 --- a/clippy_lints/src/question_mark_used.rs +++ b/clippy_lints/src/question_mark_used.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; - use clippy_utils::macros::span_is_local; use rustc_hir::{Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index 6b1dc864fb7..acd840401c6 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -3,11 +3,10 @@ use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::source::snippet; use clippy_utils::{get_enclosing_block, sym}; -use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr}; +use rustc_hir::{self as hir, Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 1b304dc5768..25929b853af 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_must_use_ty; use clippy_utils::{nth_arg, return_ty}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::{Span}; -use rustc_attr_data_structures::AttributeKind; -use rustc_attr_data_structures::find_attr; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 62939912304..38cf7e3822a 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -219,22 +219,21 @@ impl SingleComponentPathImports { } } } - } else { - // keep track of `use self::some_module` usages - if segments[0].ident.name == kw::SelfLower { - // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - imports_reused_with_self.push(segments[1].ident.name); - return; - } + } + // keep track of `use self::some_module` usages + else if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); + return; + } - // nested case such as `use self::{module1::Struct1, module2::Struct2}` - if let UseTreeKind::Nested { items, .. } = &use_tree.kind { - for tree in items { - let segments = &tree.0.prefix.segments; - if !segments.is_empty() { - imports_reused_with_self.push(segments[0].ident.name); - } + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested { items, .. } = &use_tree.kind { + for tree in items { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); } } } diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index a2938c86c76..92427473a8e 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -606,32 +606,31 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span let ctxt = span.ctxt(); if ctxt == SyntaxContext::root() { HasSafetyComment::Maybe - } else { - // From a macro expansion. Get the text from the start of the macro declaration to start of the - // unsafe block. - // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; } - // ^--------------------------------------------^ - if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) - && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo()) - && Arc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) - && let Some(src) = unsafe_line.sf.src.as_deref() - { - if macro_line.line < unsafe_line.line { - match text_has_safety_comment( - src, - &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } - } else { - HasSafetyComment::No + } + // From a macro expansion. Get the text from the start of the macro declaration to start of the + // unsafe block. + // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; } + // ^--------------------------------------------^ + else if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) + && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo()) + && Arc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + if macro_line.line < unsafe_line.line { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } } else { - // Problem getting source text. Pretend a comment was found. - HasSafetyComment::Maybe + HasSafetyComment::No } + } else { + // Problem getting source text. Pretend a comment was found. + HasSafetyComment::Maybe } } diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 615c0995e8b..73291aa8cdf 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.89" +version = "0.1.90" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 1aa16e3943c..649748d1534 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-06-12 +nightly-2025-06-26 ``` diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 1ec5d11384f..aaa071fd5c9 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -958,3 +958,18 @@ fn field_of_struct<'tcx>( None } } + +/// If `expr` evaluates to an integer constant, return its value. +pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_simple(expr) { + Some(value) + } else { + None + } +} + +/// Check if `expr` evaluates to an integer constant of 0. +#[inline] +pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + integer_const(cx, expr) == Some(0) +} diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index cd2098a8989..dc240dd067b 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -109,7 +109,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( }); } -/// Like `span_lint` but with a `note` section instead of a `help` message. +/// Like [`span_lint`] but with a `note` section instead of a `help` message. /// /// The `note` message is presented separately from the main lint message /// and is attached to a specific span: @@ -226,7 +226,7 @@ pub fn span_lint_and_note( }); } -/// Like `span_lint` but allows to add notes, help and suggestions using a closure. +/// Like [`span_lint`] but allows to add notes, help and suggestions using a closure. /// /// If you need to customize your lint output a lot, use this function. /// If you change the signature, remember to update the internal lint `CollapsibleCalls` diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 913589319fc..a8b33418c8c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -122,7 +122,7 @@ use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{InnerSpan, Span}; -use source::walk_span_to_context; +use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; @@ -1886,10 +1886,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => None, }; - did.is_some_and(|did| find_attr!( - cx.tcx.get_all_attrs(did), - AttributeKind::MustUse { ..} - )) + did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. })) } /// Checks if a function's body represents the identity function. Looks for bodies of the form: @@ -2713,7 +2710,7 @@ impl<'tcx> ExprUseNode<'tcx> { } /// Gets the context an expression's value is used in. -pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> { +pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> { let mut adjustments = [].as_slice(); let mut is_ty_unified = false; let mut moved_before_use = false; @@ -2790,6 +2787,19 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { }); } +/// Checks whether a given span has any significant token. A significant token is a non-whitespace +/// token, including comments unless `skip_comments` is set. +/// This is useful to determine if there are any actual code tokens in the span that are omitted in +/// the late pass, such as platform-specific code. +pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool { + matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)| + match token { + TokenKind::Whitespace => false, + TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments, + _ => true, + } + )) +} /// Returns all the comments a given span contains /// /// Comments are returned wrapped with their relevant delimiters diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index a5e66ad463b..7a0bef1a9bb 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -24,7 +24,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,88,0 { LET_CHAINS } - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } @@ -42,6 +42,7 @@ msrv_aliases! { 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } 1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN } + 1,61,0 { CONST_FN_TRAIT_BOUND } 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index e629012b187..be93f275fab 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -32,6 +32,21 @@ pub fn is_min_const_fn<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, msrv: Ms for local in &body.local_decls { check_ty(cx, local.ty, local.source_info.span, msrv)?; } + if !msrv.meets(cx, msrvs::CONST_FN_TRAIT_BOUND) + && let Some(sized_did) = cx.tcx.lang_items().sized_trait() + && let Some(meta_sized_did) = cx.tcx.lang_items().meta_sized_trait() + && cx.tcx.param_env(def_id).caller_bounds().iter().any(|bound| { + bound.as_trait_clause().is_some_and(|clause| { + let did = clause.def_id(); + did != sized_did && did != meta_sized_did + }) + }) + { + return Err(( + body.span, + "non-`Sized` trait clause before `const_fn_trait_bound` is stabilized".into(), + )); + } // impl trait is gone in MIR, so check the return type manually check_ty( cx, diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 6974e6512e2..7a24d07fa1d 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -494,7 +494,17 @@ impl Display for ParenHelper { /// operators have the same /// precedence. pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { - Sugg::MaybeParen(format!("{op}{}", expr.maybe_paren()).into()) + // If the `expr` starts with `op` already, do not add wrap it in + // parentheses. + let expr = if let Sugg::MaybeParen(ref sugg) = expr + && !has_enclosing_paren(sugg) + && sugg.starts_with(op) + { + expr + } else { + expr.maybe_paren() + }; + Sugg::MaybeParen(format!("{op}{expr}").into()) } /// Builds the string for ` ` adding parenthesis when necessary. @@ -1016,6 +1026,16 @@ mod test { let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "(1 + 1)".into(), "(1 + 1)".into()); assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_paren().to_string()); } + + #[test] + fn unop_parenthesize() { + let sugg = Sugg::NonParen("x".into()).mut_addr(); + assert_eq!("&mut x", sugg.to_string()); + let sugg = sugg.mut_addr(); + assert_eq!("&mut &mut x", sugg.to_string()); + assert_eq!("(&mut &mut x)", sugg.maybe_paren().to_string()); + } + #[test] fn not_op() { use ast::BinOpKind::{Add, And, Eq, Ge, Gt, Le, Lt, Ne, Or}; diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 3b58dba5628..8a8218c6976 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -46,7 +46,6 @@ generate! { DOUBLE_QUOTE: "\"", Deserialize, EarlyLintPass, - ErrorKind, IntoIter, Itertools, LF: "\n", @@ -65,7 +64,6 @@ generate! { RegexBuilder, RegexSet, Start, - Step, Symbol, SyntaxContext, TBD, @@ -158,7 +156,6 @@ generate! { from_ne_bytes, from_ptr, from_raw, - from_ref, from_str, from_str_radix, fs, @@ -166,6 +163,7 @@ generate! { futures_util, get, get_mut, + get_or_insert, get_or_insert_with, get_unchecked, get_unchecked_mut, @@ -216,7 +214,6 @@ generate! { max_by_key, max_value, maximum, - mem, min, min_by, min_by_key, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 782b079ce09..bffbcf073ab 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -6,6 +6,7 @@ use core::ops::ControlFlow; use itertools::Itertools; use rustc_abi::VariantIdx; use rustc_ast::ast::Mutability; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -20,8 +21,8 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, + GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -31,8 +32,6 @@ use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::iter; -use rustc_attr_data_structures::find_attr; -use rustc_attr_data_structures::AttributeKind; use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; @@ -328,14 +327,8 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // Returns whether the type has #[must_use] attribute pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { - ty::Adt(adt, _) => find_attr!( - cx.tcx.get_all_attrs(adt.did()), - AttributeKind::MustUse { ..} - ), - ty::Foreign(did) => find_attr!( - cx.tcx.get_all_attrs(*did), - AttributeKind::MustUse { ..} - ), + ty::Adt(adt, _) => find_attr!(cx.tcx.get_all_attrs(adt.did()), AttributeKind::MustUse { .. }), + ty::Foreign(did) => find_attr!(cx.tcx.get_all_attrs(*did), AttributeKind::MustUse { .. }), ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => { // for the Array case we don't need to care for the len == 0 case // because we don't want to lint functions returning empty arrays @@ -345,7 +338,10 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => { for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() { if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && find_attr!(cx.tcx.get_all_attrs(trait_predicate.trait_ref.def_id), AttributeKind::MustUse { ..}) + && find_attr!( + cx.tcx.get_all_attrs(trait_predicate.trait_ref.def_id), + AttributeKind::MustUse { .. } + ) { return true; } @@ -355,7 +351,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Dynamic(binder, _, _) => { for predicate in *binder { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() - && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { ..}) + && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { .. }) { return true; } diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml new file mode 100644 index 00000000000..bd6b4dfdee4 --- /dev/null +++ b/declare_clippy_lint/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "declare_clippy_lint" +version = "0.1.90" +edition = "2024" +repository = "https://github.com/rust-lang/rust-clippy" +license = "MIT OR Apache-2.0" + +[package.metadata.rust-analyzer] +# This crate uses #[feature(rustc_private)] +rustc_private = true diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs new file mode 100644 index 00000000000..f7d9c64bfbd --- /dev/null +++ b/declare_clippy_lint/src/lib.rs @@ -0,0 +1,280 @@ +#![feature(macro_metavar_expr_concat, rustc_private)] + +extern crate rustc_lint; + +use rustc_lint::{Lint, LintId, LintStore}; + +// Needed by `declare_clippy_lint!`. +pub extern crate rustc_session; + +#[derive(Default)] +pub struct LintListBuilder { + lints: Vec<&'static Lint>, + all: Vec, + cargo: Vec, + complexity: Vec, + correctness: Vec, + nursery: Vec, + pedantic: Vec, + perf: Vec, + restriction: Vec, + style: Vec, + suspicious: Vec, +} +impl LintListBuilder { + pub fn insert(&mut self, lints: &[&LintInfo]) { + #[allow(clippy::enum_glob_use)] + use LintCategory::*; + + self.lints.extend(lints.iter().map(|&x| x.lint)); + for &&LintInfo { lint, category, .. } in lints { + let (all, cat) = match category { + Complexity => (Some(&mut self.all), &mut self.complexity), + Correctness => (Some(&mut self.all), &mut self.correctness), + Perf => (Some(&mut self.all), &mut self.perf), + Style => (Some(&mut self.all), &mut self.style), + Suspicious => (Some(&mut self.all), &mut self.suspicious), + Cargo => (None, &mut self.cargo), + Nursery => (None, &mut self.nursery), + Pedantic => (None, &mut self.pedantic), + Restriction => (None, &mut self.restriction), + }; + if let Some(all) = all { + all.push(LintId::of(lint)); + } + cat.push(LintId::of(lint)); + } + } + + pub fn register(self, store: &mut LintStore) { + store.register_lints(&self.lints); + store.register_group(true, "clippy::all", Some("clippy_all"), self.all); + store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo); + store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity); + store.register_group( + true, + "clippy::correctness", + Some("clippy_correctness"), + self.correctness, + ); + store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery); + store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic); + store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf); + store.register_group( + true, + "clippy::restriction", + Some("clippy_restriction"), + self.restriction, + ); + store.register_group(true, "clippy::style", Some("clippy_style"), self.style); + store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious); + } +} + +#[derive(Copy, Clone, Debug)] +pub enum LintCategory { + Cargo, + Complexity, + Correctness, + Nursery, + Pedantic, + Perf, + Restriction, + Style, + Suspicious, +} +impl LintCategory { + #[must_use] + pub fn name(self) -> &'static str { + match self { + Self::Cargo => "cargo", + Self::Complexity => "complexity", + Self::Correctness => "correctness", + Self::Nursery => "nursery", + Self::Pedantic => "pedantic", + Self::Perf => "perf", + Self::Restriction => "restriction", + Self::Style => "style", + Self::Suspicious => "suspicious", + } + } +} + +pub struct LintInfo { + pub lint: &'static Lint, + pub category: LintCategory, + pub explanation: &'static str, + /// e.g. `clippy_lints/src/absolute_paths.rs#43` + pub location: &'static str, + pub version: &'static str, +} + +impl LintInfo { + /// Returns the lint name in lowercase without the `clippy::` prefix + #[must_use] + #[expect(clippy::missing_panics_doc)] + pub fn name_lower(&self) -> String { + self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase() + } +} + +#[macro_export] +macro_rules! declare_clippy_lint_inner { + ( + $(#[doc = $docs:literal])* + #[clippy::version = $version:literal] + $vis:vis $lint_name:ident, + $level:ident, + $category:ident, + $desc:literal + $(, @eval_always = $eval_always:literal)? + ) => { + $crate::rustc_session::declare_tool_lint! { + $(#[doc = $docs])* + #[clippy::version = $version] + $vis clippy::$lint_name, + $level, + $desc, + report_in_external_macro:true + $(, @eval_always = $eval_always)? + } + + pub(crate) static ${concat($lint_name, _INFO)}: &'static $crate::LintInfo = &$crate::LintInfo { + lint: $lint_name, + category: $crate::LintCategory::$category, + explanation: concat!($($docs,"\n",)*), + location: concat!(file!(), "#L", line!()), + version: $version, + }; + }; +} + +#[macro_export] +macro_rules! declare_clippy_lint { + ( + $(#[$($meta:tt)*])* + $vis:vis $lint_name:ident, + correctness, + $($rest:tt)* + ) => { + $crate::declare_clippy_lint_inner! { + $(#[$($meta)*])* + $vis $lint_name, + Deny, + Correctness, + $($rest)* + } + }; + ( + $(#[$($meta:tt)*])* + $vis:vis $lint_name:ident, + complexity, + $($rest:tt)* + ) => { + $crate::declare_clippy_lint_inner! { + $(#[$($meta)*])* + $vis $lint_name, + Warn, + Complexity, + $($rest)* + } + }; + ( + $(#[$($meta:tt)*])* + $vis:vis $lint_name:ident, + perf, + $($rest:tt)* + ) => { + $crate::declare_clippy_lint_inner! { + $(#[$($meta)*])* + $vis $lint_name, + Warn, + Perf, + $($rest)* + } + }; + ( + $(#[$($meta:tt)*])* + $vis:vis $lint_name:ident, + style, + $($rest:tt)* + ) => { + $crate::declare_clippy_lint_inner! { + $(#[$($meta)*])* + $vis $lint_name, + Warn, + Style, + $($rest)* + } + }; + ( + $(#[$($meta:tt)*])* + $vis:vis $lint_name:ident, + suspicious, + $($rest:tt)* + ) => { + $crate::declare_clippy_lint_inner! { + $(#[$($meta)*])* + $vis $lint_name, + Warn, + Suspicious, + $($rest)* + } + }; + ( + $(#[$($meta:tt)*])* + $vis:vis $lint_name:ident, + cargo, + $($rest:tt)* + ) => { + $crate::declare_clippy_lint_inner! { + $(#[$($meta)*])* + $vis $lint_name, + Allow, + Cargo, + $($rest)* + } + }; + ( + $(#[$($meta:tt)*])* + $vis:vis $lint_name:ident, + nursery, + $($rest:tt)* + ) => { + $crate::declare_clippy_lint_inner! { + $(#[$($meta)*])* + $vis $lint_name, + Allow, + Nursery, + $($rest)* + } + }; + ( + $(#[$($meta:tt)*])* + $vis:vis $lint_name:ident, + pedantic, + $($rest:tt)* + ) => { + $crate::declare_clippy_lint_inner! { + $(#[$($meta)*])* + $vis $lint_name, + Allow, + Pedantic, + $($rest)* + } + }; + ( + $(#[$($meta:tt)*])* + $vis:vis $lint_name:ident, + restriction, + $($rest:tt)* + ) => { + $crate::declare_clippy_lint_inner! { + $(#[$($meta)*])* + $vis $lint_name, + Allow, + Restriction, + $($rest)* + } + }; +} diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 84183831432..eb390eecbcc 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -45,7 +45,7 @@ use rayon::prelude::*; #[must_use] pub fn target_dir() -> String { - env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned()) + env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| "target".to_owned()) } fn lintcheck_sources() -> String { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3fc5a1224a8..124756a3600 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-06-12" +channel = "nightly-2025-06-26" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/driver.rs b/src/driver.rs index 426ba870f5f..c4076cbaa77 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -19,6 +19,7 @@ extern crate rustc_span; extern crate tikv_jemalloc_sys as jemalloc_sys; use clippy_utils::sym; +use declare_clippy_lint::LintListBuilder; use rustc_interface::interface; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; @@ -156,8 +157,13 @@ impl rustc_driver::Callbacks for ClippyCallbacks { (previous)(sess, lint_store); } + let mut list_builder = LintListBuilder::default(); + list_builder.insert(clippy_lints::declared_lints::LINTS); + list_builder.register(lint_store); + let conf = clippy_config::Conf::read(sess, &conf_path); - clippy_lints::register_lints(lint_store, conf); + clippy_lints::register_lint_passes(lint_store, conf); + #[cfg(feature = "internal")] clippy_lints_internal::register_lints(lint_store); })); diff --git a/src/main.rs b/src/main.rs index c9853e53f3b..3c2eec1f05b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,7 +107,7 @@ impl ClippyCmd { } fn into_std_cmd(self) -> Command { - let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into())); + let mut cmd = Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())); let clippy_args: String = self .clippy_args .iter() diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 99a01257a7b..cefe654fef6 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -7,9 +7,9 @@ use askama::filters::Safe; use cargo_metadata::Message; use cargo_metadata::diagnostic::{Applicability, Diagnostic}; use clippy_config::ClippyConfiguration; -use clippy_lints::LintInfo; use clippy_lints::declared_lints::LINTS; use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED}; +use declare_clippy_lint::LintInfo; use pulldown_cmark::{Options, Parser, html}; use serde::Deserialize; use test_utils::IS_RUSTC_TEST_SUITE; @@ -568,10 +568,10 @@ impl LintMetadata { Self { id: name, id_location: Some(lint.location), - group: lint.category_str(), + group: lint.category.name(), level: lint.lint.default_level.as_str(), docs, - version: lint.version.unwrap(), + version: lint.version, applicability, } } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 4ac2bd53285..389616801fc 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -40,6 +40,7 @@ fn dogfood() { "clippy_lints", "clippy_utils", "clippy_config", + "declare_clippy_lint", "lintcheck", "rustc_tools_util", ] { diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.fixed b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed new file mode 100644 index 00000000000..0dc0fc230c8 --- /dev/null +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed @@ -0,0 +1,50 @@ +#![allow(clippy::eq_op, clippy::nonminimal_bool)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let (x, y) = ("hello", "world"); + + if x == "hello" { + todo!() + } + // Comment must be kept + else if y == "world" { + println!("Hello world!"); + } + //~^^^^^^ collapsible_else_if + + if x == "hello" { + todo!() + } // Inner comment + else if y == "world" { + println!("Hello world!"); + } + //~^^^^^ collapsible_else_if + + if x == "hello" { + todo!() + } + /* Inner comment */ + else if y == "world" { + println!("Hello world!"); + } + //~^^^^^^ collapsible_else_if + + if x == "hello" { + todo!() + } /* Inner comment */ + else if y == "world" { + println!("Hello world!"); + } + //~^^^^^ collapsible_else_if + + if x == "hello" { + todo!() + } /* This should not be removed */ /* So does this */ + // Comment must be kept + else if y == "world" { + println!("Hello world!"); + } + //~^^^^^^ collapsible_else_if +} diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.rs b/tests/ui-toml/collapsible_if/collapsible_else_if.rs new file mode 100644 index 00000000000..8344c122f16 --- /dev/null +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.rs @@ -0,0 +1,55 @@ +#![allow(clippy::eq_op, clippy::nonminimal_bool)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let (x, y) = ("hello", "world"); + + if x == "hello" { + todo!() + } else { + // Comment must be kept + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^^ collapsible_else_if + + if x == "hello" { + todo!() + } else { // Inner comment + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^ collapsible_else_if + + if x == "hello" { + todo!() + } else { + /* Inner comment */ + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^^ collapsible_else_if + + if x == "hello" { + todo!() + } else { /* Inner comment */ + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^ collapsible_else_if + + if x == "hello" { + todo!() + } /* This should not be removed */ else /* So does this */ { + // Comment must be kept + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^^ collapsible_else_if +} diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.stderr b/tests/ui-toml/collapsible_if/collapsible_else_if.stderr new file mode 100644 index 00000000000..0ffe5f0a960 --- /dev/null +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.stderr @@ -0,0 +1,105 @@ +error: this `else { if .. }` block can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:10:12 + | +LL | } else { + | ____________^ +LL | | // Comment must be kept +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-else-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_else_if)]` +help: collapse nested if block + | +LL ~ } +LL | // Comment must be kept +LL ~ else if y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `else { if .. }` block can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:20:12 + | +LL | } else { // Inner comment + | ____________^ +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ } // Inner comment +LL ~ else if y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `else { if .. }` block can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:29:12 + | +LL | } else { + | ____________^ +LL | | /* Inner comment */ +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ } +LL | /* Inner comment */ +LL ~ else if y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `else { if .. }` block can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:39:12 + | +LL | } else { /* Inner comment */ + | ____________^ +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ } /* Inner comment */ +LL ~ else if y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `else { if .. }` block can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:48:64 + | +LL | } /* This should not be removed */ else /* So does this */ { + | ________________________________________________________________^ +LL | | // Comment must be kept +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ } /* This should not be removed */ /* So does this */ +LL | // Comment must be kept +LL ~ else if y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/borrow_deref_ref.fixed b/tests/ui/borrow_deref_ref.fixed index 765dd75fceb..6d06fcc3037 100644 --- a/tests/ui/borrow_deref_ref.fixed +++ b/tests/ui/borrow_deref_ref.fixed @@ -124,3 +124,50 @@ mod issue_11346 { //~^ borrow_deref_ref } } + +fn issue_14934() { + let x: &'static str = "x"; + let y = "y".to_string(); + { + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = &*x; // Do not lint + *x = &*y; + } + { + let mut x = x; + //~^ borrow_deref_ref + x = &*y; + } + { + #[expect(clippy::toplevel_ref_arg, clippy::needless_borrow)] + let ref x = x; + //~^ borrow_deref_ref + } + { + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = std::convert::identity(x); + //~^ borrow_deref_ref + *x = &*y; + } + { + #[derive(Clone)] + struct S(&'static str); + let s = S("foo"); + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = &*s.0; // Do not lint + *x = "bar"; + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = s.clone().0; + //~^ borrow_deref_ref + *x = "bar"; + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = &*std::convert::identity(&s).0; + *x = "bar"; + } + { + let y = &1; + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = { y }; + //~^ borrow_deref_ref + } +} diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs index 8ee66bfa881..b43f4c93bf2 100644 --- a/tests/ui/borrow_deref_ref.rs +++ b/tests/ui/borrow_deref_ref.rs @@ -124,3 +124,50 @@ mod issue_11346 { //~^ borrow_deref_ref } } + +fn issue_14934() { + let x: &'static str = "x"; + let y = "y".to_string(); + { + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = &*x; // Do not lint + *x = &*y; + } + { + let mut x = &*x; + //~^ borrow_deref_ref + x = &*y; + } + { + #[expect(clippy::toplevel_ref_arg, clippy::needless_borrow)] + let ref x = &*x; + //~^ borrow_deref_ref + } + { + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = &*std::convert::identity(x); + //~^ borrow_deref_ref + *x = &*y; + } + { + #[derive(Clone)] + struct S(&'static str); + let s = S("foo"); + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = &*s.0; // Do not lint + *x = "bar"; + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = &*s.clone().0; + //~^ borrow_deref_ref + *x = "bar"; + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = &*std::convert::identity(&s).0; + *x = "bar"; + } + { + let y = &1; + #[expect(clippy::toplevel_ref_arg)] + let ref mut x = { &*y }; + //~^ borrow_deref_ref + } +} diff --git a/tests/ui/borrow_deref_ref.stderr b/tests/ui/borrow_deref_ref.stderr index 3d55da25b9b..3a1f968b4be 100644 --- a/tests/ui/borrow_deref_ref.stderr +++ b/tests/ui/borrow_deref_ref.stderr @@ -25,5 +25,35 @@ error: deref on an immutable reference LL | (&*s).foo(); | ^^^^^ help: if you would like to reborrow, try removing `&*`: `s` -error: aborting due to 4 previous errors +error: deref on an immutable reference + --> tests/ui/borrow_deref_ref.rs:137:21 + | +LL | let mut x = &*x; + | ^^^ help: if you would like to reborrow, try removing `&*`: `x` + +error: deref on an immutable reference + --> tests/ui/borrow_deref_ref.rs:143:21 + | +LL | let ref x = &*x; + | ^^^ help: if you would like to reborrow, try removing `&*`: `x` + +error: deref on an immutable reference + --> tests/ui/borrow_deref_ref.rs:148:25 + | +LL | let ref mut x = &*std::convert::identity(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `std::convert::identity(x)` + +error: deref on an immutable reference + --> tests/ui/borrow_deref_ref.rs:160:25 + | +LL | let ref mut x = &*s.clone().0; + | ^^^^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `s.clone().0` + +error: deref on an immutable reference + --> tests/ui/borrow_deref_ref.rs:170:27 + | +LL | let ref mut x = { &*y }; + | ^^^ help: if you would like to reborrow, try removing `&*`: `y` + +error: aborting due to 9 previous errors diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index 0f439f78915..674450a73ad 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -218,4 +218,20 @@ fn main() { let _ = &S::VALUE.1; //~ borrow_interior_mutable_const let _ = &S::VALUE.2; } + { + pub struct Foo(pub Entry, pub T); + + pub struct Entry(pub Cell<[u32; N]>); + + impl Entry { + const INIT: Self = Self(Cell::new([42; N])); + } + + impl Foo { + pub fn make_foo(v: T) -> Self { + // Used to ICE due to incorrect instantiation. + Foo(Entry::INIT, v) + } + } + } } diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index 80000f5de4f..ed00494433b 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -126,7 +126,7 @@ fn issue_10381() { impl Bar for Foo {} fn maybe_get_bar(i: u32) -> Option> { - if i % 2 == 0 { + if i.is_multiple_of(2) { Some(Box::new(Foo::default())) } else { None diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index 4681016d7cd..801d92f5c29 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -126,7 +126,7 @@ fn issue_10381() { impl Bar for Foo {} fn maybe_get_bar(i: u32) -> Option> { - if i % 2 == 0 { + if i.is_multiple_of(2) { Some(Box::new(Foo::default())) } else { None diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs index 922d30443fc..fa322dc28a7 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -276,3 +276,27 @@ mod issue14873 { } } } + +fn issue15004() { + let a = 12u32; + let b = 13u32; + let mut c = 8u32; + + let mut result = if b > a { + c += 1; + 0 + } else { + c += 2; + 0 + //~^ branches_sharing_code + }; + + result = if b > a { + c += 1; + 1 + } else { + c += 2; + 1 + //~^ branches_sharing_code + }; +} diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_bottom.stderr index f437db8b733..1c470fb0da5 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_bottom.stderr @@ -172,5 +172,35 @@ LL ~ } LL + let y = 1; | -error: aborting due to 10 previous errors +error: all if blocks contain the same code at the end + --> tests/ui/branches_sharing_code/shared_at_bottom.rs:290:5 + | +LL | / 0 +LL | | +LL | | }; + | |_____^ + | + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements after the if + | +LL ~ } +LL ~ 0; + | + +error: all if blocks contain the same code at the end + --> tests/ui/branches_sharing_code/shared_at_bottom.rs:299:5 + | +LL | / 1 +LL | | +LL | | }; + | |_____^ + | + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements after the if + | +LL ~ } +LL ~ 1; + | + +error: aborting due to 12 previous errors diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index 9f530ad670a..fed75244c6f 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -86,3 +86,21 @@ fn issue_7318() { }else if false {} //~^^^ collapsible_else_if } + +fn issue14799() { + use std::ops::ControlFlow; + + let c: ControlFlow<_, ()> = ControlFlow::Break(Some(42)); + if let ControlFlow::Break(Some(_)) = c { + todo!(); + } else { + #[cfg(target_os = "freebsd")] + todo!(); + + if let ControlFlow::Break(None) = c { + todo!(); + } else { + todo!(); + } + } +} diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 2c646cd1d4d..e50e781fb69 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -102,3 +102,21 @@ fn issue_7318() { } //~^^^ collapsible_else_if } + +fn issue14799() { + use std::ops::ControlFlow; + + let c: ControlFlow<_, ()> = ControlFlow::Break(Some(42)); + if let ControlFlow::Break(Some(_)) = c { + todo!(); + } else { + #[cfg(target_os = "freebsd")] + todo!(); + + if let ControlFlow::Break(None) = c { + todo!(); + } else { + todo!(); + } + } +} diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index b553182a445..77bc791ea8e 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -154,3 +154,12 @@ fn issue14722() { None }; } + +fn issue14799() { + if true { + #[cfg(target_os = "freebsd")] + todo!(); + + if true {} + }; +} diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index f5998457ca6..d30df157d5e 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -164,3 +164,12 @@ fn issue14722() { None }; } + +fn issue14799() { + if true { + #[cfg(target_os = "freebsd")] + todo!(); + + if true {} + }; +} diff --git a/tests/ui/doc/needless_doctest_main.rs b/tests/ui/doc/needless_doctest_main.rs index 633a435ca5e..8c3217624d4 100644 --- a/tests/ui/doc/needless_doctest_main.rs +++ b/tests/ui/doc/needless_doctest_main.rs @@ -1,5 +1,3 @@ -//@ check-pass - #![warn(clippy::needless_doctest_main)] //! issue 10491: //! ```rust,no_test @@ -19,4 +17,114 @@ /// ``` fn foo() {} +#[rustfmt::skip] +/// Description +/// ```rust +/// fn main() { +//~^ error: needless `fn main` in doctest +/// let a = 0; +/// } +/// ``` +fn mulpipulpi() {} + +#[rustfmt::skip] +/// With a `#[no_main]` +/// ```rust +/// #[no_main] +/// fn a() { +/// let _ = 0; +/// } +/// ``` +fn pulpimulpi() {} + +// Without a `#[no_main]` attribute +/// ```rust +/// fn a() { +/// let _ = 0; +/// } +/// ``` +fn plumilupi() {} + +#[rustfmt::skip] +/// Additional function, shouldn't trigger +/// ```rust +/// fn additional_function() { +/// let _ = 0; +/// // Thus `fn main` is actually relevant! +/// } +/// fn main() { +/// let _ = 0; +/// } +/// ``` +fn mlupipupi() {} + +#[rustfmt::skip] +/// Additional function AFTER main, shouldn't trigger +/// ```rust +/// fn main() { +/// let _ = 0; +/// } +/// fn additional_function() { +/// let _ = 0; +/// // Thus `fn main` is actually relevant! +/// } +/// ``` +fn lumpimupli() {} + +#[rustfmt::skip] +/// Ignore code block, should not lint at all +/// ```rust, ignore +/// fn main() { +//~^ error: needless `fn main` in doctest +/// // Hi! +/// let _ = 0; +/// } +/// ``` +fn mpulpilumi() {} + +#[rustfmt::skip] +/// Spaces in weird positions (including an \u{A0} after `main`) +/// ```rust +/// fn main (){ +//~^ error: needless `fn main` in doctest +/// let _ = 0; +/// } +/// ``` +fn plumpiplupi() {} + +/// 4 Functions, this should not lint because there are several function +/// +/// ```rust +/// fn a() {let _ = 0; } +/// fn b() {let _ = 0; } +/// fn main() { let _ = 0; } +/// fn d() { let _ = 0; } +/// ``` +fn pulmipulmip() {} + +/// 3 Functions but main is first, should also not lint +/// +///```rust +/// fn main() { let _ = 0; } +/// fn b() { let _ = 0; } +/// fn c() { let _ = 0; } +/// ``` +fn pmuplimulip() {} + fn main() {} + +fn issue8244() -> Result<(), ()> { + //! ```compile_fail + //! fn test() -> Result< {} + //! ``` + Ok(()) +} + +/// # Examples +/// +/// ``` +/// use std::error::Error; +/// fn main() -> Result<(), Box/* > */ { +/// } +/// ``` +fn issue15041() {} diff --git a/tests/ui/doc/needless_doctest_main.stderr b/tests/ui/doc/needless_doctest_main.stderr new file mode 100644 index 00000000000..dd5474ccb85 --- /dev/null +++ b/tests/ui/doc/needless_doctest_main.stderr @@ -0,0 +1,36 @@ +error: needless `fn main` in doctest + --> tests/ui/doc/needless_doctest_main.rs:23:5 + | +LL | /// fn main() { + | _____^ +LL | | +LL | | /// let a = 0; +LL | | /// } + | |_____^ + | + = note: `-D clippy::needless-doctest-main` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]` + +error: needless `fn main` in doctest + --> tests/ui/doc/needless_doctest_main.rs:77:5 + | +LL | /// fn main() { + | _____^ +LL | | +LL | | /// // Hi! +LL | | /// let _ = 0; +LL | | /// } + | |_____^ + +error: needless `fn main` in doctest + --> tests/ui/doc/needless_doctest_main.rs:88:5 + | +LL | /// fn main (){ + | _____^ +LL | | +LL | | /// let _ = 0; +LL | | /// } + | |_____^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/doc_broken_link.rs b/tests/ui/doc_broken_link.rs new file mode 100644 index 00000000000..7d9c0ef13b3 --- /dev/null +++ b/tests/ui/doc_broken_link.rs @@ -0,0 +1,72 @@ +#![warn(clippy::doc_broken_link)] + +fn main() {} + +pub struct FakeType {} + +/// This might be considered a link false positive +/// and should be ignored by this lint rule: +/// Example of referencing some code with brackets [FakeType]. +pub fn doc_ignore_link_false_positive_1() {} + +/// This might be considered a link false positive +/// and should be ignored by this lint rule: +/// [`FakeType`]. Continue text after brackets, +/// then (something in +/// parenthesis). +pub fn doc_ignore_link_false_positive_2() {} + +/// Test valid link, whole link single line. +/// [doc valid link](https://test.fake/doc_valid_link) +pub fn doc_valid_link() {} + +/// Test valid link, whole link single line but it has special chars such as brackets and +/// parenthesis. [doc invalid link url invalid char](https://test.fake/doc_valid_link_url_invalid_char?foo[bar]=1&bar(foo)=2) +pub fn doc_valid_link_url_invalid_char() {} + +/// Test valid link, text tag broken across multiple lines. +/// [doc valid link broken +/// text](https://test.fake/doc_valid_link_broken_text) +pub fn doc_valid_link_broken_text() {} + +/// Test valid link, url tag broken across multiple lines, but +/// the whole url part in a single line. +/// [doc valid link broken url tag two lines first](https://test.fake/doc_valid_link_broken_url_tag_two_lines_first +/// ) +pub fn doc_valid_link_broken_url_tag_two_lines_first() {} + +/// Test valid link, url tag broken across multiple lines, but +/// the whole url part in a single line. +/// [doc valid link broken url tag two lines second]( +/// https://test.fake/doc_valid_link_broken_url_tag_two_lines_second) +pub fn doc_valid_link_broken_url_tag_two_lines_second() {} + +/// Test valid link, url tag broken across multiple lines, but +/// the whole url part in a single line, but the closing pharentesis +/// in a third line. +/// [doc valid link broken url tag three lines]( +/// https://test.fake/doc_valid_link_broken_url_tag_three_lines +/// ) +pub fn doc_valid_link_broken_url_tag_three_lines() {} + +/// Test invalid link, url part broken across multiple lines. +/// [doc invalid link broken url scheme part](https:// +/// test.fake/doc_invalid_link_broken_url_scheme_part) +//~^^ ERROR: possible broken doc link: broken across multiple lines +pub fn doc_invalid_link_broken_url_scheme_part() {} + +/// Test invalid link, url part broken across multiple lines. +/// [doc invalid link broken url host part](https://test +/// .fake/doc_invalid_link_broken_url_host_part) +//~^^ ERROR: possible broken doc link: broken across multiple lines +pub fn doc_invalid_link_broken_url_host_part() {} + +/// Test invalid link, for multiple urls in the same block of comment. +/// There is a [fist link - invalid](https://test +/// .fake) then it continues +//~^^ ERROR: possible broken doc link: broken across multiple lines +/// with a [second link - valid](https://test.fake/doc_valid_link) and another [third link - invalid](https://test +/// .fake). It ends with another +//~^^ ERROR: possible broken doc link: broken across multiple lines +/// line of comment. +pub fn doc_multiple_invalid_link_broken_url() {} diff --git a/tests/ui/doc_broken_link.stderr b/tests/ui/doc_broken_link.stderr new file mode 100644 index 00000000000..179ed97635e --- /dev/null +++ b/tests/ui/doc_broken_link.stderr @@ -0,0 +1,29 @@ +error: possible broken doc link: broken across multiple lines + --> tests/ui/doc_broken_link.rs:53:5 + | +LL | /// [doc invalid link broken url scheme part](https:// + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::doc-broken-link` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_broken_link)]` + +error: possible broken doc link: broken across multiple lines + --> tests/ui/doc_broken_link.rs:59:5 + | +LL | /// [doc invalid link broken url host part](https://test + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible broken doc link: broken across multiple lines + --> tests/ui/doc_broken_link.rs:65:16 + | +LL | /// There is a [fist link - invalid](https://test + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible broken doc link: broken across multiple lines + --> tests/ui/doc_broken_link.rs:68:80 + | +LL | /// with a [second link - valid](https://test.fake/doc_valid_link) and another [third link - invalid](https://test + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/empty_line_after/outer_attribute.1.fixed b/tests/ui/empty_line_after/outer_attribute.1.fixed index 36d80a2c95b..e36e3c2aea6 100644 --- a/tests/ui/empty_line_after/outer_attribute.1.fixed +++ b/tests/ui/empty_line_after/outer_attribute.1.fixed @@ -105,4 +105,13 @@ second line ")] pub struct Args; +mod issue_14980 { + //~v empty_line_after_outer_attr + #[repr(align(536870912))] + enum Aligned { + Zero = 0, + One = 1, + } +} + fn main() {} diff --git a/tests/ui/empty_line_after/outer_attribute.2.fixed b/tests/ui/empty_line_after/outer_attribute.2.fixed index 0e8e4129e85..b0908fc7214 100644 --- a/tests/ui/empty_line_after/outer_attribute.2.fixed +++ b/tests/ui/empty_line_after/outer_attribute.2.fixed @@ -108,4 +108,13 @@ second line ")] pub struct Args; +mod issue_14980 { + //~v empty_line_after_outer_attr + #[repr(align(536870912))] + enum Aligned { + Zero = 0, + One = 1, + } +} + fn main() {} diff --git a/tests/ui/empty_line_after/outer_attribute.rs b/tests/ui/empty_line_after/outer_attribute.rs index 1295088ac00..4ae113c68f5 100644 --- a/tests/ui/empty_line_after/outer_attribute.rs +++ b/tests/ui/empty_line_after/outer_attribute.rs @@ -116,4 +116,14 @@ second line ")] pub struct Args; +mod issue_14980 { + //~v empty_line_after_outer_attr + #[repr(align(536870912))] + + enum Aligned { + Zero = 0, + One = 1, + } +} + fn main() {} diff --git a/tests/ui/empty_line_after/outer_attribute.stderr b/tests/ui/empty_line_after/outer_attribute.stderr index 519ba6e6761..331bc7c8856 100644 --- a/tests/ui/empty_line_after/outer_attribute.stderr +++ b/tests/ui/empty_line_after/outer_attribute.stderr @@ -111,5 +111,16 @@ LL | pub fn isolated_comment() {} | = help: if the empty lines are unintentional, remove them -error: aborting due to 9 previous errors +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:121:5 + | +LL | / #[repr(align(536870912))] +LL | | + | |_^ +LL | enum Aligned { + | ------------ the attribute applies to this enum + | + = help: if the empty line is unintentional, remove it + +error: aborting due to 10 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 0ba631fda05..c93b83f53ec 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -543,3 +543,21 @@ mod issue_13073 { //~^ redundant_closure } } + +fn issue_14789() { + _ = Some(1u8).map( + #[expect(clippy::redundant_closure)] + |a| foo(a), + ); + + _ = Some("foo").map( + #[expect(clippy::redundant_closure_for_method_calls)] + |s| s.to_owned(), + ); + + let _: Vec = None.map_or_else( + #[expect(clippy::redundant_closure)] + || vec![], + std::convert::identity, + ); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 4d8b29d450c..273c8b21f4a 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -543,3 +543,21 @@ mod issue_13073 { //~^ redundant_closure } } + +fn issue_14789() { + _ = Some(1u8).map( + #[expect(clippy::redundant_closure)] + |a| foo(a), + ); + + _ = Some("foo").map( + #[expect(clippy::redundant_closure_for_method_calls)] + |s| s.to_owned(), + ); + + let _: Vec = None.map_or_else( + #[expect(clippy::redundant_closure)] + || vec![], + std::convert::identity, + ); +} diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed index 79c74aeefbd..3b2f33dbd2c 100644 --- a/tests/ui/exhaustive_items.fixed +++ b/tests/ui/exhaustive_items.fixed @@ -1,3 +1,4 @@ +#![feature(default_field_values)] #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] #![allow(unused)] @@ -90,3 +91,9 @@ pub mod structs { pub bar: String, } } + +pub mod issue14992 { + pub struct A { + pub a: isize = 42, + } +} diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs index 4e851f4c492..b0a6a717076 100644 --- a/tests/ui/exhaustive_items.rs +++ b/tests/ui/exhaustive_items.rs @@ -1,3 +1,4 @@ +#![feature(default_field_values)] #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] #![allow(unused)] @@ -87,3 +88,9 @@ pub mod structs { pub bar: String, } } + +pub mod issue14992 { + pub struct A { + pub a: isize = 42, + } +} diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index c92c8a9efaa..55928fa458d 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -1,5 +1,5 @@ error: exported enums should not be exhaustive - --> tests/ui/exhaustive_items.rs:9:5 + --> tests/ui/exhaustive_items.rs:10:5 | LL | / pub enum Exhaustive { LL | | @@ -11,7 +11,7 @@ LL | | } | |_____^ | note: the lint level is defined here - --> tests/ui/exhaustive_items.rs:1:9 + --> tests/ui/exhaustive_items.rs:2:9 | LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL ~ pub enum Exhaustive { | error: exported enums should not be exhaustive - --> tests/ui/exhaustive_items.rs:19:5 + --> tests/ui/exhaustive_items.rs:20:5 | LL | / pub enum ExhaustiveWithAttrs { LL | | @@ -40,7 +40,7 @@ LL ~ pub enum ExhaustiveWithAttrs { | error: exported structs should not be exhaustive - --> tests/ui/exhaustive_items.rs:55:5 + --> tests/ui/exhaustive_items.rs:56:5 | LL | / pub struct Exhaustive { LL | | @@ -50,7 +50,7 @@ LL | | } | |_____^ | note: the lint level is defined here - --> tests/ui/exhaustive_items.rs:1:35 + --> tests/ui/exhaustive_items.rs:2:35 | LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed index a1b55602998..4e14e1a5e33 100644 --- a/tests/ui/identity_op.fixed +++ b/tests/ui/identity_op.fixed @@ -312,3 +312,49 @@ fn issue_13470() { let _: u64 = 1u64 + ((x as i32 + y as i32) as u64); //~^ identity_op } + +fn issue_14932() { + let _ = 0usize + &Default::default(); // no error + + 0usize + &Default::default(); // no error + + ::default(); + //~^ identity_op + + let _ = usize::default(); + //~^ identity_op + + let _n: usize = Default::default(); + //~^ identity_op +} + +// Expr's type can be inferred by the function's return type +fn issue_14932_2() -> usize { + Default::default() + //~^ identity_op +} + +trait Def { + fn def() -> Self; +} + +impl Def for usize { + fn def() -> Self { + 0 + } +} + +fn issue_14932_3() { + let _ = 0usize + &Def::def(); // no error + + 0usize + &Def::def(); // no error + + ::def(); + //~^ identity_op + + let _ = usize::def(); + //~^ identity_op + + let _n: usize = Def::def(); + //~^ identity_op +} diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index f603e1078e4..ebbef5723ff 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -312,3 +312,49 @@ fn issue_13470() { let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64); //~^ identity_op } + +fn issue_14932() { + let _ = 0usize + &Default::default(); // no error + + 0usize + &Default::default(); // no error + + 0usize + &::default(); + //~^ identity_op + + let _ = 0usize + &usize::default(); + //~^ identity_op + + let _n: usize = 0usize + &Default::default(); + //~^ identity_op +} + +// Expr's type can be inferred by the function's return type +fn issue_14932_2() -> usize { + 0usize + &Default::default() + //~^ identity_op +} + +trait Def { + fn def() -> Self; +} + +impl Def for usize { + fn def() -> Self { + 0 + } +} + +fn issue_14932_3() { + let _ = 0usize + &Def::def(); // no error + + 0usize + &Def::def(); // no error + + 0usize + &::def(); + //~^ identity_op + + let _ = 0usize + &usize::def(); + //~^ identity_op + + let _n: usize = 0usize + &Def::def(); + //~^ identity_op +} diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 8f9c2b603c4..24fa5db08ce 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -379,5 +379,47 @@ error: this operation has no effect LL | let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x as i32 + y as i32) as u64)` -error: aborting due to 63 previous errors +error: this operation has no effect + --> tests/ui/identity_op.rs:321:5 + | +LL | 0usize + &::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `::default()` + +error: this operation has no effect + --> tests/ui/identity_op.rs:324:13 + | +LL | let _ = 0usize + &usize::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `usize::default()` + +error: this operation has no effect + --> tests/ui/identity_op.rs:327:21 + | +LL | let _n: usize = 0usize + &Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `Default::default()` + +error: this operation has no effect + --> tests/ui/identity_op.rs:333:5 + | +LL | 0usize + &Default::default() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `Default::default()` + +error: this operation has no effect + --> tests/ui/identity_op.rs:352:5 + | +LL | 0usize + &::def(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `::def()` + +error: this operation has no effect + --> tests/ui/identity_op.rs:355:13 + | +LL | let _ = 0usize + &usize::def(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `usize::def()` + +error: this operation has no effect + --> tests/ui/identity_op.rs:358:21 + | +LL | let _n: usize = 0usize + &Def::def(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `Def::def()` + +error: aborting due to 70 previous errors diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index 002a791a657..701a86534ba 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -38,7 +38,7 @@ fn infinite_iters() { //~^ infinite_iter // infinite iter - (0_u64..).filter(|x| x % 2 == 0).last(); + (0_u64..).filter(|x| x.is_multiple_of(2)).last(); //~^ infinite_iter // not an infinite, because ranges are double-ended diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index 47133a2ea62..b9e7c008f93 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -42,8 +42,8 @@ LL | (0_usize..).flat_map(|x| 0..x).product::(); error: infinite iteration detected --> tests/ui/infinite_iter.rs:41:5 | -LL | (0_u64..).filter(|x| x % 2 == 0).last(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | (0_u64..).filter(|x| x.is_multiple_of(2)).last(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected --> tests/ui/infinite_iter.rs:53:5 diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed index 874f749b33d..b18dda35887 100644 --- a/tests/ui/iter_kv_map.fixed +++ b/tests/ui/iter_kv_map.fixed @@ -30,15 +30,19 @@ fn main() { let _ = map.clone().values().collect::>(); //~^ iter_kv_map - let _ = map.keys().filter(|x| *x % 2 == 0).count(); + let _ = map.keys().filter(|x| x.is_multiple_of(2)).count(); //~^ iter_kv_map // Don't lint - let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count(); + let _ = map + .iter() + .filter(|(_, val)| val.is_multiple_of(2)) + .map(|(key, _)| key) + .count(); let _ = map.iter().map(get_key).collect::>(); // Linting the following could be an improvement to the lint - // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count(); + // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count(); // Lint let _ = map.keys().map(|key| key * 9).count(); @@ -84,15 +88,19 @@ fn main() { let _ = map.clone().values().collect::>(); //~^ iter_kv_map - let _ = map.keys().filter(|x| *x % 2 == 0).count(); + let _ = map.keys().filter(|x| x.is_multiple_of(2)).count(); //~^ iter_kv_map // Don't lint - let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count(); + let _ = map + .iter() + .filter(|(_, val)| val.is_multiple_of(2)) + .map(|(key, _)| key) + .count(); let _ = map.iter().map(get_key).collect::>(); // Linting the following could be an improvement to the lint - // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count(); + // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count(); // Lint let _ = map.keys().map(|key| key * 9).count(); diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs index f570e3c32cb..729e4e8a266 100644 --- a/tests/ui/iter_kv_map.rs +++ b/tests/ui/iter_kv_map.rs @@ -30,15 +30,19 @@ fn main() { let _ = map.clone().iter().map(|(_, val)| val).collect::>(); //~^ iter_kv_map - let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); + let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count(); //~^ iter_kv_map // Don't lint - let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count(); + let _ = map + .iter() + .filter(|(_, val)| val.is_multiple_of(2)) + .map(|(key, _)| key) + .count(); let _ = map.iter().map(get_key).collect::>(); // Linting the following could be an improvement to the lint - // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count(); + // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count(); // Lint let _ = map.iter().map(|(key, _value)| key * 9).count(); @@ -86,15 +90,19 @@ fn main() { let _ = map.clone().iter().map(|(_, val)| val).collect::>(); //~^ iter_kv_map - let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); + let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count(); //~^ iter_kv_map // Don't lint - let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count(); + let _ = map + .iter() + .filter(|(_, val)| val.is_multiple_of(2)) + .map(|(key, _)| key) + .count(); let _ = map.iter().map(get_key).collect::>(); // Linting the following could be an improvement to the lint - // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count(); + // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count(); // Lint let _ = map.iter().map(|(key, _value)| key * 9).count(); diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr index 31ee76c25b7..8f73541f503 100644 --- a/tests/ui/iter_kv_map.stderr +++ b/tests/ui/iter_kv_map.stderr @@ -52,29 +52,29 @@ LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); error: iterating on a map's keys --> tests/ui/iter_kv_map.rs:33:13 | -LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); +LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:44:13 + --> tests/ui/iter_kv_map.rs:48:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:46:13 + --> tests/ui/iter_kv_map.rs:50:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:50:13 + --> tests/ui/iter_kv_map.rs:54:13 | LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:54:13 + --> tests/ui/iter_kv_map.rs:58:13 | LL | let _ = map | _____________^ @@ -97,85 +97,85 @@ LL + }) | error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:65:13 + --> tests/ui/iter_kv_map.rs:69:13 | LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:70:13 + --> tests/ui/iter_kv_map.rs:74:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:72:13 + --> tests/ui/iter_kv_map.rs:76:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:74:13 + --> tests/ui/iter_kv_map.rs:78:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:77:13 + --> tests/ui/iter_kv_map.rs:81:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:79:13 + --> tests/ui/iter_kv_map.rs:83:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:82:13 + --> tests/ui/iter_kv_map.rs:86:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:84:13 + --> tests/ui/iter_kv_map.rs:88:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:87:13 + --> tests/ui/iter_kv_map.rs:91:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:89:13 + --> tests/ui/iter_kv_map.rs:93:13 | -LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); +LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:100:13 + --> tests/ui/iter_kv_map.rs:108:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:102:13 + --> tests/ui/iter_kv_map.rs:110:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:106:13 + --> tests/ui/iter_kv_map.rs:114:13 | LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:110:13 + --> tests/ui/iter_kv_map.rs:118:13 | LL | let _ = map | _____________^ @@ -198,73 +198,73 @@ LL + }) | error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:121:13 + --> tests/ui/iter_kv_map.rs:129:13 | LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:137:13 + --> tests/ui/iter_kv_map.rs:145:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:140:13 + --> tests/ui/iter_kv_map.rs:148:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:143:13 + --> tests/ui/iter_kv_map.rs:151:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:152:13 + --> tests/ui/iter_kv_map.rs:160:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:155:13 + --> tests/ui/iter_kv_map.rs:163:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:158:13 + --> tests/ui/iter_kv_map.rs:166:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:161:13 + --> tests/ui/iter_kv_map.rs:169:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:164:13 + --> tests/ui/iter_kv_map.rs:172:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:167:13 + --> tests/ui/iter_kv_map.rs:175:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:170:13 + --> tests/ui/iter_kv_map.rs:178:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:185:13 + --> tests/ui/iter_kv_map.rs:193:13 | LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()` diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 5e7a2ad37a8..304eacecd94 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -61,7 +61,7 @@ fn multiline_sugg() { //~^ let_unit_value .into_iter() .map(|i| i * 2) - .filter(|i| i % 2 == 0) + .filter(|i| i.is_multiple_of(2)) .map(|_| ()) .next() .unwrap(); diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index 7b06f694012..a02cb346ff9 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -61,7 +61,7 @@ fn multiline_sugg() { //~^ let_unit_value .into_iter() .map(|i| i * 2) - .filter(|i| i % 2 == 0) + .filter(|i| i.is_multiple_of(2)) .map(|_| ()) .next() .unwrap(); diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index d7d01d304ca..d743110c99d 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -25,7 +25,7 @@ LL ~ v LL + LL + .into_iter() LL + .map(|i| i * 2) -LL + .filter(|i| i % 2 == 0) +LL + .filter(|i| i.is_multiple_of(2)) LL + .map(|_| ()) LL + .next() LL + .unwrap(); diff --git a/tests/ui/manual_contains.fixed b/tests/ui/manual_contains.fixed index d26c948a781..18171f0b2b4 100644 --- a/tests/ui/manual_contains.fixed +++ b/tests/ui/manual_contains.fixed @@ -58,7 +58,7 @@ fn should_not_lint() { let vec: Vec = vec![1, 2, 3, 4, 5, 6]; let values = &vec[..]; - let _ = values.iter().any(|&v| v % 2 == 0); + let _ = values.iter().any(|&v| v.is_multiple_of(2)); let _ = values.iter().any(|&v| v * 2 == 6); let _ = values.iter().any(|&v| v == v); let _ = values.iter().any(|&v| 4 == 4); diff --git a/tests/ui/manual_contains.rs b/tests/ui/manual_contains.rs index fe67d2ee5d5..918f4d6b8dd 100644 --- a/tests/ui/manual_contains.rs +++ b/tests/ui/manual_contains.rs @@ -58,7 +58,7 @@ fn should_not_lint() { let vec: Vec = vec![1, 2, 3, 4, 5, 6]; let values = &vec[..]; - let _ = values.iter().any(|&v| v % 2 == 0); + let _ = values.iter().any(|&v| v.is_multiple_of(2)); let _ = values.iter().any(|&v| v * 2 == 6); let _ = values.iter().any(|&v| v == v); let _ = values.iter().any(|&v| 4 == 4); diff --git a/tests/ui/manual_find_fixable.fixed b/tests/ui/manual_find_fixable.fixed index 01b3ebacbeb..c69b0cb11e3 100644 --- a/tests/ui/manual_find_fixable.fixed +++ b/tests/ui/manual_find_fixable.fixed @@ -11,7 +11,7 @@ fn lookup(n: u32) -> Option { } fn with_pat(arr: Vec<(u32, u32)>) -> Option { - arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0) + arr.into_iter().map(|(a, _)| a).find(|&a| a.is_multiple_of(2)) } struct Data { @@ -63,7 +63,7 @@ fn with_side_effects(arr: Vec) -> Option { fn with_else(arr: Vec) -> Option { for el in arr { - if el % 2 == 0 { + if el.is_multiple_of(2) { return Some(el); } else { println!("{}", el); diff --git a/tests/ui/manual_find_fixable.rs b/tests/ui/manual_find_fixable.rs index ce62a4beba1..db7092f020c 100644 --- a/tests/ui/manual_find_fixable.rs +++ b/tests/ui/manual_find_fixable.rs @@ -19,7 +19,7 @@ fn lookup(n: u32) -> Option { fn with_pat(arr: Vec<(u32, u32)>) -> Option { for (a, _) in arr { //~^ manual_find - if a % 2 == 0 { + if a.is_multiple_of(2) { return Some(a); } } @@ -111,7 +111,7 @@ fn with_side_effects(arr: Vec) -> Option { fn with_else(arr: Vec) -> Option { for el in arr { - if el % 2 == 0 { + if el.is_multiple_of(2) { return Some(el); } else { println!("{}", el); diff --git a/tests/ui/manual_find_fixable.stderr b/tests/ui/manual_find_fixable.stderr index 020635d90bb..0c05c0d2c44 100644 --- a/tests/ui/manual_find_fixable.stderr +++ b/tests/ui/manual_find_fixable.stderr @@ -17,11 +17,11 @@ error: manual implementation of `Iterator::find` | LL | / for (a, _) in arr { LL | | -LL | | if a % 2 == 0 { +LL | | if a.is_multiple_of(2) { LL | | return Some(a); ... | LL | | None - | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)` + | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a.is_multiple_of(2))` error: manual implementation of `Iterator::find` --> tests/ui/manual_find_fixable.rs:34:5 diff --git a/tests/ui/manual_is_multiple_of.fixed b/tests/ui/manual_is_multiple_of.fixed new file mode 100644 index 00000000000..6735b99f298 --- /dev/null +++ b/tests/ui/manual_is_multiple_of.fixed @@ -0,0 +1,25 @@ +//@aux-build: proc_macros.rs +#![warn(clippy::manual_is_multiple_of)] + +fn main() {} + +#[clippy::msrv = "1.87"] +fn f(a: u64, b: u64) { + let _ = a.is_multiple_of(b); //~ manual_is_multiple_of + let _ = (a + 1).is_multiple_of(b + 1); //~ manual_is_multiple_of + let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of + let _ = !(a + 1).is_multiple_of(b + 1); //~ manual_is_multiple_of + + let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of + let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of + + proc_macros::external! { + let a: u64 = 23424; + let _ = a % 4096 == 0; + } +} + +#[clippy::msrv = "1.86"] +fn g(a: u64, b: u64) { + let _ = a % b == 0; +} diff --git a/tests/ui/manual_is_multiple_of.rs b/tests/ui/manual_is_multiple_of.rs new file mode 100644 index 00000000000..00b638e4fd9 --- /dev/null +++ b/tests/ui/manual_is_multiple_of.rs @@ -0,0 +1,25 @@ +//@aux-build: proc_macros.rs +#![warn(clippy::manual_is_multiple_of)] + +fn main() {} + +#[clippy::msrv = "1.87"] +fn f(a: u64, b: u64) { + let _ = a % b == 0; //~ manual_is_multiple_of + let _ = (a + 1) % (b + 1) == 0; //~ manual_is_multiple_of + let _ = a % b != 0; //~ manual_is_multiple_of + let _ = (a + 1) % (b + 1) != 0; //~ manual_is_multiple_of + + let _ = a % b > 0; //~ manual_is_multiple_of + let _ = 0 < a % b; //~ manual_is_multiple_of + + proc_macros::external! { + let a: u64 = 23424; + let _ = a % 4096 == 0; + } +} + +#[clippy::msrv = "1.86"] +fn g(a: u64, b: u64) { + let _ = a % b == 0; +} diff --git a/tests/ui/manual_is_multiple_of.stderr b/tests/ui/manual_is_multiple_of.stderr new file mode 100644 index 00000000000..0b1ae70c2a7 --- /dev/null +++ b/tests/ui/manual_is_multiple_of.stderr @@ -0,0 +1,41 @@ +error: manual implementation of `.is_multiple_of()` + --> tests/ui/manual_is_multiple_of.rs:8:13 + | +LL | let _ = a % b == 0; + | ^^^^^^^^^^ help: replace with: `a.is_multiple_of(b)` + | + = note: `-D clippy::manual-is-multiple-of` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_is_multiple_of)]` + +error: manual implementation of `.is_multiple_of()` + --> tests/ui/manual_is_multiple_of.rs:9:13 + | +LL | let _ = (a + 1) % (b + 1) == 0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(a + 1).is_multiple_of(b + 1)` + +error: manual implementation of `.is_multiple_of()` + --> tests/ui/manual_is_multiple_of.rs:10:13 + | +LL | let _ = a % b != 0; + | ^^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)` + +error: manual implementation of `.is_multiple_of()` + --> tests/ui/manual_is_multiple_of.rs:11:13 + | +LL | let _ = (a + 1) % (b + 1) != 0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `!(a + 1).is_multiple_of(b + 1)` + +error: manual implementation of `.is_multiple_of()` + --> tests/ui/manual_is_multiple_of.rs:13:13 + | +LL | let _ = a % b > 0; + | ^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)` + +error: manual implementation of `.is_multiple_of()` + --> tests/ui/manual_is_multiple_of.rs:14:13 + | +LL | let _ = 0 < a % b; + | ^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/manual_is_variant_and.fixed b/tests/ui/manual_is_variant_and.fixed index 18a72188ab5..6425f32c09c 100644 --- a/tests/ui/manual_is_variant_and.fixed +++ b/tests/ui/manual_is_variant_and.fixed @@ -77,7 +77,7 @@ fn option_methods() { let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint // Should not lint. - let _ = Foo::(0).map(|x| x % 2 == 0) == Some(true); + let _ = Foo::(0).map(|x| x.is_multiple_of(2)) == Some(true); let _ = Some(2).map(|x| x % 2 == 0) != foo(); let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true)); let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true); @@ -96,11 +96,11 @@ fn result_methods() { }); let _ = res.is_ok_and(|x| x > 1); - let _ = Ok::(2).is_ok_and(|x| x % 2 == 0); + let _ = Ok::(2).is_ok_and(|x| x.is_multiple_of(2)); //~^ manual_is_variant_and - let _ = !Ok::(2).is_ok_and(|x| x % 2 == 0); + let _ = !Ok::(2).is_ok_and(|x| x.is_multiple_of(2)); //~^ manual_is_variant_and - let _ = !Ok::(2).is_ok_and(|x| x % 2 == 0); + let _ = !Ok::(2).is_ok_and(|x| x.is_multiple_of(2)); //~^ manual_is_variant_and // won't fix because the return type of the closure is not `bool` diff --git a/tests/ui/manual_is_variant_and.rs b/tests/ui/manual_is_variant_and.rs index a92f7c04369..e069e97a04d 100644 --- a/tests/ui/manual_is_variant_and.rs +++ b/tests/ui/manual_is_variant_and.rs @@ -83,7 +83,7 @@ fn option_methods() { let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint // Should not lint. - let _ = Foo::(0).map(|x| x % 2 == 0) == Some(true); + let _ = Foo::(0).map(|x| x.is_multiple_of(2)) == Some(true); let _ = Some(2).map(|x| x % 2 == 0) != foo(); let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true)); let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true); @@ -105,11 +105,11 @@ fn result_methods() { //~^ manual_is_variant_and .unwrap_or_default(); - let _ = Ok::(2).map(|x| x % 2 == 0) == Ok(true); + let _ = Ok::(2).map(|x| x.is_multiple_of(2)) == Ok(true); //~^ manual_is_variant_and - let _ = Ok::(2).map(|x| x % 2 == 0) != Ok(true); + let _ = Ok::(2).map(|x| x.is_multiple_of(2)) != Ok(true); //~^ manual_is_variant_and - let _ = Ok::(2).map(|x| x % 2 == 0) != Ok(true); + let _ = Ok::(2).map(|x| x.is_multiple_of(2)) != Ok(true); //~^ manual_is_variant_and // won't fix because the return type of the closure is not `bool` diff --git a/tests/ui/manual_is_variant_and.stderr b/tests/ui/manual_is_variant_and.stderr index 1fb437a8bc7..f770319a268 100644 --- a/tests/ui/manual_is_variant_and.stderr +++ b/tests/ui/manual_is_variant_and.stderr @@ -105,20 +105,20 @@ LL | | .unwrap_or_default(); error: called `.map() == Ok()` --> tests/ui/manual_is_variant_and.rs:108:13 | -LL | let _ = Ok::(2).map(|x| x % 2 == 0) == Ok(true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::(2).is_ok_and(|x| x % 2 == 0)` +LL | let _ = Ok::(2).map(|x| x.is_multiple_of(2)) == Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::(2).is_ok_and(|x| x.is_multiple_of(2))` error: called `.map() != Ok()` --> tests/ui/manual_is_variant_and.rs:110:13 | -LL | let _ = Ok::(2).map(|x| x % 2 == 0) != Ok(true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::(2).is_ok_and(|x| x % 2 == 0)` +LL | let _ = Ok::(2).map(|x| x.is_multiple_of(2)) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::(2).is_ok_and(|x| x.is_multiple_of(2))` error: called `.map() != Ok()` --> tests/ui/manual_is_variant_and.rs:112:13 | -LL | let _ = Ok::(2).map(|x| x % 2 == 0) != Ok(true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::(2).is_ok_and(|x| x % 2 == 0)` +LL | let _ = Ok::(2).map(|x| x.is_multiple_of(2)) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::(2).is_ok_and(|x| x.is_multiple_of(2))` error: called `map().unwrap_or_default()` on a `Result` value --> tests/ui/manual_is_variant_and.rs:119:18 diff --git a/tests/ui/manual_ok_err.fixed b/tests/ui/manual_ok_err.fixed index e6f799aa58d..9b70ce0df43 100644 --- a/tests/ui/manual_ok_err.fixed +++ b/tests/ui/manual_ok_err.fixed @@ -103,3 +103,27 @@ fn issue14239() { }; //~^^^^^ manual_ok_err } + +mod issue15051 { + struct Container { + field: Result, + } + + #[allow(clippy::needless_borrow)] + fn with_addr_of(x: &Container) -> Option<&bool> { + (&x.field).as_ref().ok() + } + + fn from_fn(x: &Container) -> Option<&bool> { + let result_with_ref = || &x.field; + result_with_ref().as_ref().ok() + } + + fn result_with_ref_mut(x: &mut Container) -> &mut Result { + &mut x.field + } + + fn from_fn_mut(x: &mut Container) -> Option<&mut bool> { + result_with_ref_mut(x).as_mut().ok() + } +} diff --git a/tests/ui/manual_ok_err.rs b/tests/ui/manual_ok_err.rs index 972b2c41ee7..dee90463824 100644 --- a/tests/ui/manual_ok_err.rs +++ b/tests/ui/manual_ok_err.rs @@ -141,3 +141,39 @@ fn issue14239() { }; //~^^^^^ manual_ok_err } + +mod issue15051 { + struct Container { + field: Result, + } + + #[allow(clippy::needless_borrow)] + fn with_addr_of(x: &Container) -> Option<&bool> { + match &x.field { + //~^ manual_ok_err + Ok(panel) => Some(panel), + Err(_) => None, + } + } + + fn from_fn(x: &Container) -> Option<&bool> { + let result_with_ref = || &x.field; + match result_with_ref() { + //~^ manual_ok_err + Ok(panel) => Some(panel), + Err(_) => None, + } + } + + fn result_with_ref_mut(x: &mut Container) -> &mut Result { + &mut x.field + } + + fn from_fn_mut(x: &mut Container) -> Option<&mut bool> { + match result_with_ref_mut(x) { + //~^ manual_ok_err + Ok(panel) => Some(panel), + Err(_) => None, + } + } +} diff --git a/tests/ui/manual_ok_err.stderr b/tests/ui/manual_ok_err.stderr index 040e170f397..448fbffc050 100644 --- a/tests/ui/manual_ok_err.stderr +++ b/tests/ui/manual_ok_err.stderr @@ -111,5 +111,35 @@ LL + "1".parse::().ok() LL ~ }; | -error: aborting due to 9 previous errors +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:152:9 + | +LL | / match &x.field { +LL | | +LL | | Ok(panel) => Some(panel), +LL | | Err(_) => None, +LL | | } + | |_________^ help: replace with: `(&x.field).as_ref().ok()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:161:9 + | +LL | / match result_with_ref() { +LL | | +LL | | Ok(panel) => Some(panel), +LL | | Err(_) => None, +LL | | } + | |_________^ help: replace with: `result_with_ref().as_ref().ok()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:173:9 + | +LL | / match result_with_ref_mut(x) { +LL | | +LL | | Ok(panel) => Some(panel), +LL | | Err(_) => None, +LL | | } + | |_________^ help: replace with: `result_with_ref_mut(x).as_mut().ok()` + +error: aborting due to 12 previous errors diff --git a/tests/ui/missing_const_for_fn/const_trait.fixed b/tests/ui/missing_const_for_fn/const_trait.fixed index 7e0d4fccaae..f1d5579a723 100644 --- a/tests/ui/missing_const_for_fn/const_trait.fixed +++ b/tests/ui/missing_const_for_fn/const_trait.fixed @@ -25,7 +25,7 @@ const fn can_be_const() { 0u64.method(); } -// False negative, see FIXME comment in `clipy_utils::qualify_min_const` +// False negative, see FIXME comment in `clippy_utils::qualify_min_const_fn` fn could_be_const_but_does_not_trigger(t: T) where T: const ConstTrait, diff --git a/tests/ui/missing_const_for_fn/const_trait.rs b/tests/ui/missing_const_for_fn/const_trait.rs index 439da4622d7..d495759526d 100644 --- a/tests/ui/missing_const_for_fn/const_trait.rs +++ b/tests/ui/missing_const_for_fn/const_trait.rs @@ -25,7 +25,7 @@ fn can_be_const() { 0u64.method(); } -// False negative, see FIXME comment in `clipy_utils::qualify_min_const` +// False negative, see FIXME comment in `clippy_utils::qualify_min_const_fn` fn could_be_const_but_does_not_trigger(t: T) where T: const ConstTrait, diff --git a/tests/ui/missing_const_for_fn/could_be_const.fixed b/tests/ui/missing_const_for_fn/could_be_const.fixed index 65eb2d5938b..95bf63ed1df 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -221,3 +221,60 @@ const fn mut_add(x: &mut i32) { //~^ missing_const_for_fn *x += 1; } + +mod issue_15079 { + pub trait Trait {} + + pub struct Struct { + _t: Option, + } + + impl Struct { + #[clippy::msrv = "1.60"] + pub fn new_1_60() -> Self { + Self { _t: None } + } + + #[clippy::msrv = "1.61"] + pub const fn new_1_61() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + } + + pub struct S2 { + _t: Option, + } + + impl S2 { + #[clippy::msrv = "1.60"] + pub const fn new_1_60() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + + #[clippy::msrv = "1.61"] + pub const fn new_1_61() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + } + + pub struct S3 { + _t: Option<&'static T>, + } + + impl S3 { + #[clippy::msrv = "1.60"] + pub const fn new_1_60() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + + #[clippy::msrv = "1.61"] + pub const fn new_1_61() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + } +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 3690d2f799f..8290be67546 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -221,3 +221,60 @@ fn mut_add(x: &mut i32) { //~^ missing_const_for_fn *x += 1; } + +mod issue_15079 { + pub trait Trait {} + + pub struct Struct { + _t: Option, + } + + impl Struct { + #[clippy::msrv = "1.60"] + pub fn new_1_60() -> Self { + Self { _t: None } + } + + #[clippy::msrv = "1.61"] + pub fn new_1_61() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + } + + pub struct S2 { + _t: Option, + } + + impl S2 { + #[clippy::msrv = "1.60"] + pub fn new_1_60() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + + #[clippy::msrv = "1.61"] + pub fn new_1_61() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + } + + pub struct S3 { + _t: Option<&'static T>, + } + + impl S3 { + #[clippy::msrv = "1.60"] + pub fn new_1_60() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + + #[clippy::msrv = "1.61"] + pub fn new_1_61() -> Self { + //~^ missing_const_for_fn + Self { _t: None } + } + } +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 10e07d12f5a..17cbc431276 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -332,5 +332,75 @@ help: make the function `const` LL | const fn mut_add(x: &mut i32) { | +++++ -error: aborting due to 25 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:239:9 + | +LL | / pub fn new_1_61() -> Self { +LL | | +LL | | Self { _t: None } +LL | | } + | |_________^ + | +help: make the function `const` + | +LL | pub const fn new_1_61() -> Self { + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:251:9 + | +LL | / pub fn new_1_60() -> Self { +LL | | +LL | | Self { _t: None } +LL | | } + | |_________^ + | +help: make the function `const` + | +LL | pub const fn new_1_60() -> Self { + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:257:9 + | +LL | / pub fn new_1_61() -> Self { +LL | | +LL | | Self { _t: None } +LL | | } + | |_________^ + | +help: make the function `const` + | +LL | pub const fn new_1_61() -> Self { + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:269:9 + | +LL | / pub fn new_1_60() -> Self { +LL | | +LL | | Self { _t: None } +LL | | } + | |_________^ + | +help: make the function `const` + | +LL | pub const fn new_1_60() -> Self { + | +++++ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:275:9 + | +LL | / pub fn new_1_61() -> Self { +LL | | +LL | | Self { _t: None } +LL | | } + | |_________^ + | +help: make the function `const` + | +LL | pub const fn new_1_61() -> Self { + | +++++ + +error: aborting due to 30 previous errors diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index 0e3e4cf7988..ecb82a23da0 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -179,7 +179,7 @@ error: inequality checks against true can be replaced by a negation --> tests/ui/nonminimal_bool.rs:186:8 | LL | if !b != true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!(!b)` + | ^^^^^^^^^^ help: try simplifying it as shown: `!!b` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:189:8 @@ -209,7 +209,7 @@ error: inequality checks against true can be replaced by a negation --> tests/ui/nonminimal_bool.rs:193:8 | LL | if true != !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!(!b)` + | ^^^^^^^^^^ help: try simplifying it as shown: `!!b` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:196:8 diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index a1119d75c23..34f3e046841 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -5,6 +5,7 @@ clippy::uninlined_format_args, clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, + clippy::unnecessary_result_map_or_else, clippy::useless_vec )] @@ -409,4 +410,33 @@ fn fn_call_in_nested_expr() { //~^ or_fun_call } +mod result_map_or { + fn g() -> i32 { + 3 + } + + fn f(n: i32) -> i32 { + n + } + + fn test_map_or() { + let x: Result = Ok(4); + let _ = x.map_or_else(|_| g(), |v| v); + //~^ or_fun_call + let _ = x.map_or_else(|_| g(), f); + //~^ or_fun_call + let _ = x.map_or(0, f); + } +} + +fn test_option_get_or_insert() { + // assume that this is slow call + fn g() -> u8 { + 99 + } + let mut x = Some(42_u8); + let _ = x.get_or_insert_with(g); + //~^ or_fun_call +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index a7cd632bf16..dc57bd6060a 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -5,6 +5,7 @@ clippy::uninlined_format_args, clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, + clippy::unnecessary_result_map_or_else, clippy::useless_vec )] @@ -409,4 +410,33 @@ fn fn_call_in_nested_expr() { //~^ or_fun_call } +mod result_map_or { + fn g() -> i32 { + 3 + } + + fn f(n: i32) -> i32 { + n + } + + fn test_map_or() { + let x: Result = Ok(4); + let _ = x.map_or(g(), |v| v); + //~^ or_fun_call + let _ = x.map_or(g(), f); + //~^ or_fun_call + let _ = x.map_or(0, f); + } +} + +fn test_option_get_or_insert() { + // assume that this is slow call + fn g() -> u8 { + 99 + } + let mut x = Some(42_u8); + let _ = x.get_or_insert(g()); + //~^ or_fun_call +} + fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 35bda7e4d33..0f159fe8bff 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:52:22 + --> tests/ui/or_fun_call.rs:53:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` @@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make()); = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:56:14 + --> tests/ui/or_fun_call.rs:57:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` @@ -17,199 +17,199 @@ LL | with_new.unwrap_or(Vec::new()); = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:60:21 + --> tests/ui/or_fun_call.rs:61:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:64:14 + --> tests/ui/or_fun_call.rs:65:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:68:19 + --> tests/ui/or_fun_call.rs:69:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:72:24 + --> tests/ui/or_fun_call.rs:73:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:76:23 + --> tests/ui/or_fun_call.rs:77:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:80:18 + --> tests/ui/or_fun_call.rs:81:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:84:18 + --> tests/ui/or_fun_call.rs:85:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:88:14 + --> tests/ui/or_fun_call.rs:89:14 | LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:92:21 + --> tests/ui/or_fun_call.rs:93:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:96:19 + --> tests/ui/or_fun_call.rs:97:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:100:23 + --> tests/ui/or_fun_call.rs:101:23 | LL | map_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:104:21 + --> tests/ui/or_fun_call.rs:105:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:108:25 + --> tests/ui/or_fun_call.rs:109:25 | LL | btree_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:112:21 + --> tests/ui/or_fun_call.rs:113:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:117:17 + --> tests/ui/or_fun_call.rs:118:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:122:21 + --> tests/ui/or_fun_call.rs:123:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:125:21 + --> tests/ui/or_fun_call.rs:126:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:150:35 + --> tests/ui/or_fun_call.rs:151:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:193:18 + --> tests/ui/or_fun_call.rs:194:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:201:14 + --> tests/ui/or_fun_call.rs:202:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:204:14 + --> tests/ui/or_fun_call.rs:205:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:280:25 + --> tests/ui/or_fun_call.rs:281:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:282:25 + --> tests/ui/or_fun_call.rs:283:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:314:18 + --> tests/ui/or_fun_call.rs:315:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:318:28 + --> tests/ui/or_fun_call.rs:319:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:322:27 + --> tests/ui/or_fun_call.rs:323:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:326:22 + --> tests/ui/or_fun_call.rs:327:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:330:23 + --> tests/ui/or_fun_call.rs:331:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:334:25 + --> tests/ui/or_fun_call.rs:335:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:338:25 + --> tests/ui/or_fun_call.rs:339:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:380:17 + --> tests/ui/or_fun_call.rs:381:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:385:17 + --> tests/ui/or_fun_call.rs:386:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:390:17 + --> tests/ui/or_fun_call.rs:391:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -229,22 +229,40 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:396:17 + --> tests/ui/or_fun_call.rs:397:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:401:17 + --> tests/ui/or_fun_call.rs:402:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:408:21 + --> tests/ui/or_fun_call.rs:409:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` -error: aborting due to 38 previous errors +error: function call inside of `map_or` + --> tests/ui/or_fun_call.rs:424:19 + | +LL | let _ = x.map_or(g(), |v| v); + | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` + +error: function call inside of `map_or` + --> tests/ui/or_fun_call.rs:426:19 + | +LL | let _ = x.map_or(g(), f); + | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` + +error: function call inside of `get_or_insert` + --> tests/ui/or_fun_call.rs:438:15 + | +LL | let _ = x.get_or_insert(g()); + | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` + +error: aborting due to 41 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 60dc1c101b6..8d6f5fbadca 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -453,3 +453,15 @@ fn const_in_pattern(x: Option<(i32, i32)>) -> Option<()> { None } + +fn issue_13642(x: Option) -> Option<()> { + let Some(x) = x else { + #[cfg(false)] + panic!(); + + #[cfg(true)] + return None; + }; + + None +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 99d0122a98f..f13eee29c11 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -549,3 +549,15 @@ fn const_in_pattern(x: Option<(i32, i32)>) -> Option<()> { None } + +fn issue_13642(x: Option) -> Option<()> { + let Some(x) = x else { + #[cfg(false)] + panic!(); + + #[cfg(true)] + return None; + }; + + None +} diff --git a/tests/ui/unnecessary_os_str_debug_formatting.rs b/tests/ui/unnecessary_os_str_debug_formatting.rs index 6652efd9ae1..66590be3d05 100644 --- a/tests/ui/unnecessary_os_str_debug_formatting.rs +++ b/tests/ui/unnecessary_os_str_debug_formatting.rs @@ -21,3 +21,16 @@ fn main() { let _: String = format!("{:?}", os_str); //~ unnecessary_debug_formatting let _: String = format!("{:?}", os_string); //~ unnecessary_debug_formatting } + +#[clippy::msrv = "1.86"] +fn msrv_1_86() { + let os_str = OsStr::new("test"); + println!("{:?}", os_str); +} + +#[clippy::msrv = "1.87"] +fn msrv_1_87() { + let os_str = OsStr::new("test"); + println!("{:?}", os_str); + //~^ unnecessary_debug_formatting +} diff --git a/tests/ui/unnecessary_os_str_debug_formatting.stderr b/tests/ui/unnecessary_os_str_debug_formatting.stderr index 382e59b0461..f04d2d5bdc8 100644 --- a/tests/ui/unnecessary_os_str_debug_formatting.stderr +++ b/tests/ui/unnecessary_os_str_debug_formatting.stderr @@ -54,5 +54,14 @@ LL | let _: String = format!("{:?}", os_string); = help: use `Display` formatting and change this to `os_string.display()` = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed -error: aborting due to 6 previous errors +error: unnecessary `Debug` formatting in `println!` args + --> tests/ui/unnecessary_os_str_debug_formatting.rs:34:22 + | +LL | println!("{:?}", os_str); + | ^^^^^^ + | + = help: use `Display` formatting and change this to `os_str.display()` + = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed + +error: aborting due to 7 previous errors diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 141ff6eb2ac..5f738a254dc 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -90,6 +90,21 @@ fn main() { _ => {}, } + { + pub enum Enum { + A, + B, + C(u8), + D(u8, u8), + E { e: u8 }, + }; + match Enum::A { + Enum::A => (), + Enum::B | Enum::C(_) | Enum::D(..) | Enum::E { .. } => (), + //~^ wildcard_enum_match_arm + } + } + { #![allow(clippy::manual_non_exhaustive)] pub enum Enum { @@ -105,3 +120,17 @@ fn main() { } } } + +fn issue15091() { + enum Foo { + A, + B, + C, + } + + match Foo::A { + Foo::A => {}, + r#type @ Foo::B | r#type @ Foo::C => {}, + //~^ wildcard_enum_match_arm + } +} diff --git a/tests/ui/wildcard_enum_match_arm.rs b/tests/ui/wildcard_enum_match_arm.rs index a13684e9100..4bc4bfdcb79 100644 --- a/tests/ui/wildcard_enum_match_arm.rs +++ b/tests/ui/wildcard_enum_match_arm.rs @@ -90,6 +90,21 @@ fn main() { _ => {}, } + { + pub enum Enum { + A, + B, + C(u8), + D(u8, u8), + E { e: u8 }, + }; + match Enum::A { + Enum::A => (), + _ => (), + //~^ wildcard_enum_match_arm + } + } + { #![allow(clippy::manual_non_exhaustive)] pub enum Enum { @@ -105,3 +120,17 @@ fn main() { } } } + +fn issue15091() { + enum Foo { + A, + B, + C, + } + + match Foo::A { + Foo::A => {}, + r#type => {}, + //~^ wildcard_enum_match_arm + } +} diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index 088c6b7b284..d0929989494 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -37,8 +37,20 @@ LL | _ => {}, error: wildcard match will also match any future added variants --> tests/ui/wildcard_enum_match_arm.rs:103:13 | +LL | _ => (), + | ^ help: try: `Enum::B | Enum::C(_) | Enum::D(..) | Enum::E { .. }` + +error: wildcard match will also match any future added variants + --> tests/ui/wildcard_enum_match_arm.rs:118:13 + | LL | _ => (), | ^ help: try: `Enum::B | Enum::__Private` -error: aborting due to 6 previous errors +error: wildcard match will also match any future added variants + --> tests/ui/wildcard_enum_match_arm.rs:133:9 + | +LL | r#type => {}, + | ^^^^^^ help: try: `r#type @ Foo::B | r#type @ Foo::C` + +error: aborting due to 8 previous errors diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index f6fc2354ca0..b0179387b2b 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -27,6 +27,7 @@ fn consistent_clippy_crate_versions() { "clippy_config/Cargo.toml", "clippy_lints/Cargo.toml", "clippy_utils/Cargo.toml", + "declare_clippy_lint/Cargo.toml", ]; for path in paths { diff --git a/triagebot.toml b/triagebot.toml index 16557a4bebb..4f370758c00 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -17,6 +17,9 @@ allow-unauthenticated = [ [issue-links] +[mentions."clippy_lints/src/doc"] +cc = ["@notriddle"] + # Prevents mentions in commits to avoid users being spammed [no-mentions] diff --git a/util/versions.py b/util/versions.py index fee0d292df1..6e06d77a771 100755 --- a/util/versions.py +++ b/util/versions.py @@ -6,11 +6,11 @@ import os import sys def key(v): - if v == "master": - return sys.maxsize if v == "stable": - return sys.maxsize - 1 + return sys.maxsize if v == "beta": + return sys.maxsize - 1 + if v == "master": return sys.maxsize - 2 if v == "pre-1.29.0": return -1 -- cgit 1.4.1-3-g733a5 From 4a8eec34ee888852f3f2b909c575df0bedf9f61a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 27 Jun 2025 12:21:41 +0200 Subject: broken_links: Fix rustdoc API usage --- clippy_lints/src/doc/broken_link.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/doc/broken_link.rs b/clippy_lints/src/doc/broken_link.rs index a97b807e605..4af10510023 100644 --- a/clippy_lints/src/doc/broken_link.rs +++ b/clippy_lints/src/doc/broken_link.rs @@ -19,7 +19,7 @@ pub fn check(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragm } fn warn_if_broken_link(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) { - if let Some(span) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) { + if let Some((span, _)) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) { let mut len = 0; // grab raw link data -- cgit 1.4.1-3-g733a5 From 7e406e276a1778390c06ce40a9a4929e3d371776 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 24 Jun 2025 21:18:22 +0800 Subject: fix: `disallowed_script_idents` FP on identifiers with `_` --- clippy_lints/src/disallowed_script_idents.rs | 4 ++++ tests/ui/disallowed_script_idents.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs index d1a8590c59b..cf964d4b580 100644 --- a/clippy_lints/src/disallowed_script_idents.rs +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -89,6 +89,10 @@ impl EarlyLintPass for DisallowedScriptIdents { // Fast path for ascii-only idents. if !symbol_str.is_ascii() && let Some(script) = symbol_str.chars().find_map(|c| { + if c.is_ascii() { + return None; + } + c.script_extension() .iter() .find(|script| !self.whitelist.contains(script)) diff --git a/tests/ui/disallowed_script_idents.rs b/tests/ui/disallowed_script_idents.rs index 08fd1d9669e..dae380045ae 100644 --- a/tests/ui/disallowed_script_idents.rs +++ b/tests/ui/disallowed_script_idents.rs @@ -15,3 +15,17 @@ fn main() { let カウンタ = 10; //~^ disallowed_script_idents } + +fn issue15116() { + const ÄÖÜ: u8 = 0; + const _ÄÖÜ: u8 = 0; + const Ä_ÖÜ: u8 = 0; + const ÄÖ_Ü: u8 = 0; + const ÄÖÜ_: u8 = 0; + let äöüß = 1; + let _äöüß = 1; + let ä_öüß = 1; + let äö_üß = 1; + let äöü_ß = 1; + let äöüß_ = 1; +} -- cgit 1.4.1-3-g733a5 From 76b9fbf5b2641c77c1d6ae8b3d86ce087c99ab9e Mon Sep 17 00:00:00 2001 From: quaternic <57393910+quaternic@users.noreply.github.com> Date: Sun, 29 Jun 2025 06:53:07 +0300 Subject: apply suggestions for clippy::manual_is_multiple_of in libm-test --- library/compiler-builtins/libm-test/tests/z_extensive/run.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/compiler-builtins/libm-test/tests/z_extensive/run.rs b/library/compiler-builtins/libm-test/tests/z_extensive/run.rs index f2ba6a4a0e3..e04e00c6d74 100644 --- a/library/compiler-builtins/libm-test/tests/z_extensive/run.rs +++ b/library/compiler-builtins/libm-test/tests/z_extensive/run.rs @@ -197,15 +197,15 @@ impl Progress { fn update(&self, completed: u64, input: impl fmt::Debug) { // Infrequently update the progress bar. - if completed % 20_000 == 0 { + if completed.is_multiple_of(20_000) { self.pb.set_position(completed); } - if completed % 500_000 == 0 { + if completed.is_multiple_of(500_000) { self.pb.set_message(format!("input: {input:<24?}")); } - if !self.is_tty && completed % 5_000_000 == 0 { + if !self.is_tty && completed.is_multiple_of(5_000_000) { let len = self.pb.length().unwrap_or_default(); eprintln!( "[{elapsed:3?}s {percent:3.0}%] {name} \ -- cgit 1.4.1-3-g733a5 From ad7de948a871d7f11bcb62b4c301506de620085d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 29 Jun 2025 11:08:12 +0200 Subject: Consider deref'ed argument as non-temporary If there are more than one dereference (there is one corresponding matched with a borrow in any case), consider that the argument might point to a place expression, which is the safest choice. Also, use an appropriate number of dereferences in suggestions involving arguments using themselves multiple dereferences. --- clippy_lints/src/methods/swap_with_temporary.rs | 52 ++++++++++++++++++------- tests/ui/swap_with_temporary.fixed | 46 ++++++++++++++++++++++ tests/ui/swap_with_temporary.rs | 46 ++++++++++++++++++++++ tests/ui/swap_with_temporary.stderr | 38 +++++++++++++++++- 4 files changed, 166 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/methods/swap_with_temporary.rs b/clippy_lints/src/methods/swap_with_temporary.rs index de729fb343a..e378cbd6ae0 100644 --- a/clippy_lints/src/methods/swap_with_temporary.rs +++ b/clippy_lints/src/methods/swap_with_temporary.rs @@ -4,6 +4,7 @@ use rustc_ast::BorrowKind; use rustc_errors::{Applicability, Diag}; use rustc_hir::{Expr, ExprKind, Node, QPath}; use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::Adjust; use rustc_span::sym; use super::SWAP_WITH_TEMPORARY; @@ -11,12 +12,12 @@ use super::SWAP_WITH_TEMPORARY; const MSG_TEMPORARY: &str = "this expression returns a temporary value"; const MSG_TEMPORARY_REFMUT: &str = "this is a mutable reference to a temporary value"; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'_>, args: &'tcx [Expr<'_>]) { if let ExprKind::Path(QPath::Resolved(_, func_path)) = func.kind && let Some(func_def_id) = func_path.res.opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_swap, func_def_id) { - match (ArgKind::new(&args[0]), ArgKind::new(&args[1])) { + match (ArgKind::new(cx, &args[0]), ArgKind::new(cx, &args[1])) { (ArgKind::RefMutToTemp(left_temp), ArgKind::RefMutToTemp(right_temp)) => { emit_lint_useless(cx, expr, &args[0], &args[1], left_temp, right_temp); }, @@ -28,10 +29,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args } enum ArgKind<'tcx> { - // Mutable reference to a place, coming from a macro - RefMutToPlaceAsMacro(&'tcx Expr<'tcx>), - // Place behind a mutable reference - RefMutToPlace(&'tcx Expr<'tcx>), + // Mutable reference to a place, coming from a macro, and number of dereferences to use + RefMutToPlaceAsMacro(&'tcx Expr<'tcx>, usize), + // Place behind a mutable reference, and number of dereferences to use + RefMutToPlace(&'tcx Expr<'tcx>, usize), // Temporary value behind a mutable reference RefMutToTemp(&'tcx Expr<'tcx>), // Any other case @@ -39,13 +40,29 @@ enum ArgKind<'tcx> { } impl<'tcx> ArgKind<'tcx> { - fn new(arg: &'tcx Expr<'tcx>) -> Self { - if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind { - if target.is_syntactic_place_expr() { + /// Build a new `ArgKind` from `arg`. There must be no false positive when returning a + /// `ArgKind::RefMutToTemp` variant, as this may cause a spurious lint to be emitted. + fn new(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Self { + if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind + && let adjustments = cx.typeck_results().expr_adjustments(arg) + && adjustments + .first() + .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None))) + && adjustments + .last() + .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) + { + let extra_derefs = adjustments[1..adjustments.len() - 1] + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); + // If a deref is used, `arg` might be a place expression. For example, a mutex guard + // would dereference into the mutex content which is probably not temporary. + if target.is_syntactic_place_expr() || extra_derefs > 0 { if arg.span.from_expansion() { - ArgKind::RefMutToPlaceAsMacro(arg) + ArgKind::RefMutToPlaceAsMacro(arg, extra_derefs) } else { - ArgKind::RefMutToPlace(target) + ArgKind::RefMutToPlace(target, extra_derefs) } } else { ArgKind::RefMutToTemp(target) @@ -106,10 +123,15 @@ fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>, let mut applicability = Applicability::MachineApplicable; let ctxt = expr.span.ctxt(); let assign_target = match target { - ArgKind::Expr(target) | ArgKind::RefMutToPlaceAsMacro(target) => { - Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref() - }, - ArgKind::RefMutToPlace(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability), + ArgKind::Expr(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref(), + ArgKind::RefMutToPlaceAsMacro(arg, derefs) => (0..*derefs).fold( + Sugg::hir_with_context(cx, arg, ctxt, "_", &mut applicability).deref(), + |sugg, _| sugg.deref(), + ), + ArgKind::RefMutToPlace(target, derefs) => (0..*derefs).fold( + Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability), + |sugg, _| sugg.deref(), + ), ArgKind::RefMutToTemp(_) => unreachable!(), }; let assign_source = Sugg::hir_with_context(cx, temp, ctxt, "_", &mut applicability); diff --git a/tests/ui/swap_with_temporary.fixed b/tests/ui/swap_with_temporary.fixed index 4007d998ba0..4b4b0d4aebd 100644 --- a/tests/ui/swap_with_temporary.fixed +++ b/tests/ui/swap_with_temporary.fixed @@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { swap(&mut s.t, v.get_mut(0).unwrap()); swap(w.unwrap(), &mut s.t); } + +fn issue15166() { + use std::sync::Mutex; + + struct A { + thing: Mutex>, + } + + impl A { + fn a(&self) { + let mut new_vec = vec![42]; + // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries + swap(&mut new_vec, &mut self.thing.lock().unwrap()); + for v in new_vec { + // Do something with v + } + // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix + *self.thing.lock().unwrap() = vec![42]; + //~^ ERROR: swapping with a temporary value is inefficient + } + } +} + +fn multiple_deref() { + let mut v1 = &mut &mut &mut vec![42]; + ***v1 = vec![]; + //~^ ERROR: swapping with a temporary value is inefficient + + struct Wrapper(T); + impl std::ops::Deref for Wrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + use std::sync::Mutex; + let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42]))); + ***v1.lock().unwrap() = vec![]; + //~^ ERROR: swapping with a temporary value is inefficient +} diff --git a/tests/ui/swap_with_temporary.rs b/tests/ui/swap_with_temporary.rs index d403c086c0f..8e35e6144d9 100644 --- a/tests/ui/swap_with_temporary.rs +++ b/tests/ui/swap_with_temporary.rs @@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { swap(&mut s.t, v.get_mut(0).unwrap()); swap(w.unwrap(), &mut s.t); } + +fn issue15166() { + use std::sync::Mutex; + + struct A { + thing: Mutex>, + } + + impl A { + fn a(&self) { + let mut new_vec = vec![42]; + // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries + swap(&mut new_vec, &mut self.thing.lock().unwrap()); + for v in new_vec { + // Do something with v + } + // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix + swap(&mut vec![42], &mut self.thing.lock().unwrap()); + //~^ ERROR: swapping with a temporary value is inefficient + } + } +} + +fn multiple_deref() { + let mut v1 = &mut &mut &mut vec![42]; + swap(&mut ***v1, &mut vec![]); + //~^ ERROR: swapping with a temporary value is inefficient + + struct Wrapper(T); + impl std::ops::Deref for Wrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + use std::sync::Mutex; + let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42]))); + swap(&mut vec![], &mut v1.lock().unwrap()); + //~^ ERROR: swapping with a temporary value is inefficient +} diff --git a/tests/ui/swap_with_temporary.stderr b/tests/ui/swap_with_temporary.stderr index 59355771a96..5ca4fccd37a 100644 --- a/tests/ui/swap_with_temporary.stderr +++ b/tests/ui/swap_with_temporary.stderr @@ -96,5 +96,41 @@ note: this expression returns a temporary value LL | swap(mac!(refmut y), &mut func()); | ^^^^^^ -error: aborting due to 8 previous errors +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:92:13 + | +LL | swap(&mut vec![42], &mut self.thing.lock().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*self.thing.lock().unwrap() = vec![42]` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:92:23 + | +LL | swap(&mut vec![42], &mut self.thing.lock().unwrap()); + | ^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:100:5 + | +LL | swap(&mut ***v1, &mut vec![]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1 = vec![]` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:100:27 + | +LL | swap(&mut ***v1, &mut vec![]); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:118:5 + | +LL | swap(&mut vec![], &mut v1.lock().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1.lock().unwrap() = vec![]` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:118:15 + | +LL | swap(&mut vec![], &mut v1.lock().unwrap()); + | ^^^^^^ + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From ff3c9c15b7ae8a0ef7b4079b1c39ebb3a3bdbedb Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Sun, 29 Jun 2025 10:54:13 -0600 Subject: `missing_panics_doc`: Correctly distinguish maybe-const vs always-const contexts for missing_panics_doc changelog: [`missing_panics_doc`]: Allow unwrap() and expect()s in const-only contexts --- clippy_lints/src/doc/missing_headers.rs | 7 ++++-- tests/ui/missing_panics_doc.rs | 28 ++++++++++++++++++++++++ tests/ui/missing_panics_doc.stderr | 38 ++++++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 9ee32fced8c..3033ac0d0b0 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty}; +use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -99,13 +99,16 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { let mut panic_span = None; let typeck = cx.tcx.typeck_body(body_id); for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| { + if is_inside_always_const_context(cx.tcx, expr.hir_id) { + return ControlFlow::::Continue(()); + } + if let Some(macro_call) = root_macro_call_first_node(cx, expr) && (is_panic(cx, macro_call.def_id) || matches!( cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) )) - && !cx.tcx.hir_is_inside_const_context(expr.hir_id) && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) && panic_span.is_none() { diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index ffdae8504f7..d016e099e30 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -250,3 +250,31 @@ pub fn issue_12760() { } } } + +/// This needs documenting +pub fn unwrap_expect_etc_in_const() { + let a = const { std::num::NonZeroUsize::new(1).unwrap() }; + // This should still pass the lint even if it is guaranteed to panic at compile-time + let b = const { std::num::NonZeroUsize::new(0).unwrap() }; +} + +/// This needs documenting +pub const fn unwrap_expect_etc_in_const_fn_fails() { + //~^ missing_panics_doc + let a = std::num::NonZeroUsize::new(1).unwrap(); +} + +/// This needs documenting +pub const fn assert_in_const_fn_fails() { + //~^ missing_panics_doc + let x = 0; + if x == 0 { + panic!(); + } +} + +/// This needs documenting +pub const fn in_const_fn(n: usize) { + //~^ missing_panics_doc + assert!(N > n); +} diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index 7f0acf8de9b..85a00914427 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -180,5 +180,41 @@ note: first possible panic found here LL | *v.last().expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:262:1 + | +LL | pub const fn unwrap_expect_etc_in_const_fn_fails() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:264:13 + | +LL | let a = std::num::NonZeroUsize::new(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:268:1 + | +LL | pub const fn assert_in_const_fn_fails() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:272:9 + | +LL | panic!(); + | ^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:277:1 + | +LL | pub const fn in_const_fn(n: usize) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:279:5 + | +LL | assert!(N > n); + | ^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors -- cgit 1.4.1-3-g733a5 From 9cba70b2838a2c3d6fe9bbe9eb4a5c6a8f4dda92 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sat, 28 Jun 2025 22:12:01 +0800 Subject: fix: `cast_possible_truncation` should not suggest inside const context --- clippy_lints/src/casts/cast_possible_truncation.rs | 6 ++++-- tests/ui/cast_size.32bit.stderr | 10 +++++++++- tests/ui/cast_size.64bit.stderr | 10 +++++++++- tests/ui/cast_size.rs | 6 ++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index a2ecb5fb44a..2eebe849232 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; -use clippy_utils::{expr_or_init, sym}; +use clippy_utils::{expr_or_init, is_in_const_context, sym}; use rustc_abi::IntegerType; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; @@ -168,7 +168,9 @@ pub(super) fn check( span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, msg, |diag| { diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ..."); - if !cast_from.is_floating_point() { + // TODO: Remove the condition for const contexts when `try_from` and other commonly used methods + // become const fn. + if !is_in_const_context(cx) && !cast_from.is_floating_point() { offer_suggestion(cx, expr, cast_expr, cast_to_span, diag); } }); diff --git a/tests/ui/cast_size.32bit.stderr b/tests/ui/cast_size.32bit.stderr index cb1620e36a2..5811cb3607b 100644 --- a/tests/ui/cast_size.32bit.stderr +++ b/tests/ui/cast_size.32bit.stderr @@ -177,6 +177,14 @@ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bi LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: casting `usize` to `u16` may truncate the value + --> tests/ui/cast_size.rs:71:20 + | +LL | const N: u16 = M as u16; + | ^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... + error: literal out of range for `usize` --> tests/ui/cast_size.rs:63:5 | @@ -186,5 +194,5 @@ LL | 9_999_999_999_999_999usize as f64; = note: the literal `9_999_999_999_999_999usize` does not fit into the type `usize` whose range is `0..=4294967295` = note: `#[deny(overflowing_literals)]` on by default -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/cast_size.64bit.stderr b/tests/ui/cast_size.64bit.stderr index b6000a52abb..ba1419583ae 100644 --- a/tests/ui/cast_size.64bit.stderr +++ b/tests/ui/cast_size.64bit.stderr @@ -177,5 +177,13 @@ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bi LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors +error: casting `usize` to `u16` may truncate the value + --> tests/ui/cast_size.rs:71:20 + | +LL | const N: u16 = M as u16; + | ^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... + +error: aborting due to 19 previous errors diff --git a/tests/ui/cast_size.rs b/tests/ui/cast_size.rs index e5bef2a99d5..ecc58669419 100644 --- a/tests/ui/cast_size.rs +++ b/tests/ui/cast_size.rs @@ -65,3 +65,9 @@ fn main() { //~[32bit]^^ ERROR: literal out of range for `usize` // 999_999_999_999_999_999_999_999_999_999u128 as f128; } + +fn issue15163() { + const M: usize = 100; + const N: u16 = M as u16; + //~^ cast_possible_truncation +} -- cgit 1.4.1-3-g733a5 From 6709720e4c9208519874b50158150a8ce3c1afb1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 2 Jun 2025 08:59:29 +1000 Subject: Introduce `ByteSymbol`. It's like `Symbol` but for byte strings. The interner is now used for both `Symbol` and `ByteSymbol`. E.g. if you intern `"dog"` and `b"dog"` you'll get a `Symbol` and a `ByteSymbol` with the same index and the characters will only be stored once. The motivation for this is to eliminate the `Arc`s in `ast::LitKind`, to make `ast::LitKind` impl `Copy`, and to avoid the need to arena-allocate `ast::LitKind` in HIR. The latter change reduces peak memory by a non-trivial amount on literal-heavy benchmarks such as `deep-vector` and `tuple-stress`. `Encoder`, `Decoder`, `SpanEncoder`, and `SpanDecoder` all get some changes so that they can handle normal strings and byte strings. This change does slow down compilation of programs that use `include_bytes!` on large files, because the contents of those files are now interned (hashed). This makes `include_bytes!` more similar to `include_str!`, though `include_bytes!` contents still aren't escaped, and hashing is still much cheaper than escaping. --- clippy_lints/src/approx_const.rs | 2 +- clippy_lints/src/bool_assert_comparison.rs | 2 +- clippy_lints/src/casts/manual_dangling_ptr.rs | 2 +- clippy_lints/src/casts/unnecessary_cast.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 4 ++-- clippy_lints/src/large_include_file.rs | 2 +- clippy_lints/src/manual_ignore_case_cmp.rs | 8 ++++---- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/matches/match_like_matches.rs | 2 +- clippy_lints/src/matches/match_same_arms.rs | 8 +++++--- clippy_lints/src/methods/open_options.rs | 2 +- clippy_lints/src/missing_asserts_for_indexing.rs | 2 +- clippy_lints/src/utils/author.rs | 2 +- clippy_utils/src/consts.rs | 12 +++++++----- 14 files changed, 28 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 852e48cbcae..5ed4c82634a 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -74,7 +74,7 @@ impl ApproxConstant { } impl LateLintPass<'_> for ApproxConstant { - fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: &Lit, _negated: bool) { + fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: Lit, _negated: bool) { match lit.node { LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { FloatTy::F16 => self.check_known_consts(cx, lit.span, s, "f16"), diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 8f95e44bf85..581fe33ea0b 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -42,7 +42,7 @@ fn extract_bool_lit(e: &Expr<'_>) -> Option { }) = e.kind && !e.span.from_expansion() { - Some(*b) + Some(b) } else { None } diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index d9e88d6a401..92910cf8adf 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -46,7 +46,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool { match expr.kind { ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to), - ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to), + ExprKind::Lit(lit) => is_literal_aligned(cx, &lit, to), _ => false, } } diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 010f09d4c1d..c88a0539d70 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -243,7 +243,7 @@ fn lint_unnecessary_cast( ); } -fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { +fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option { match expr.kind { ExprKind::Lit(lit) => Some(lit), ExprKind::Unary(UnOp::Neg, e) => { diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 784214c29af..1507f1ed305 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -83,7 +83,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { } /// Check whether a passed literal has potential to cause fallback or not. - fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { + fn check_lit(&self, lit: Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { if !lit.span.in_external_macro(self.cx.sess().source_map()) && matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))) && matches!( @@ -210,7 +210,7 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { ExprKind::Lit(lit) => { let ty = self.cx.typeck_results().expr_ty(expr); - self.check_lit(lit, ty, expr.hir_id); + self.check_lit(*lit, ty, expr.hir_id); return; }, diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 621a2af1d32..8707612fbdd 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -57,7 +57,7 @@ impl LateLintPass<'_> for LargeIncludeFile { if let ExprKind::Lit(lit) = &expr.kind && let len = match &lit.node { // include_bytes - LitKind::ByteStr(bstr, _) => bstr.len(), + LitKind::ByteStr(bstr, _) => bstr.as_byte_str().len(), // include_str LitKind::Str(sym, _) => sym.as_str().len(), _ => return, diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index 57c03fbb2ed..f7d9ec1fae8 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -41,12 +41,12 @@ declare_clippy_lint! { declare_lint_pass!(ManualIgnoreCaseCmp => [MANUAL_IGNORE_CASE_CMP]); -enum MatchType<'a, 'b> { +enum MatchType<'a> { ToAscii(bool, Ty<'a>), - Literal(&'b LitKind), + Literal(LitKind), } -fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> { +fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Option<(Span, MatchType<'a>)> { if let MethodCall(path, expr, _, _) = kind { let is_lower = match path.ident.name { sym::to_ascii_lowercase => true, @@ -63,7 +63,7 @@ fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) - return Some((expr.span, ToAscii(is_lower, ty_raw))); } } else if let Lit(expr) = kind { - return Some((expr.span, Literal(&expr.node))); + return Some((expr.span, Literal(expr.node))); } None } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 9e911e61f19..6bf43a1c6d4 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -184,7 +184,7 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t .. }) = expr.kind { - constant_length(cx, pattern).is_some_and(|length| *n == length) + constant_length(cx, pattern).is_some_and(|length| n == length) } else { len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg)) } diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index f14b69d91ce..5816da5695e 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -159,7 +159,7 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option { node: LitKind::Bool(b), .. }) = exp.kind { - Some(*b) + Some(b) } else { None } diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index dbb29ee776b..ede68f30941 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -12,7 +12,7 @@ use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExp use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol}; use super::MATCH_SAME_ARMS; @@ -193,7 +193,7 @@ enum NormalizedPat<'a> { Or(&'a [Self]), Path(Option), LitStr(Symbol), - LitBytes(&'a [u8]), + LitBytes(ByteSymbol), LitInt(u128), LitBool(bool), Range(PatRange), @@ -332,7 +332,9 @@ impl<'a> NormalizedPat<'a> { // TODO: Handle negative integers. They're currently treated as a wild match. PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), - LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), + LitKind::ByteStr(byte_sym, _) | LitKind::CStr(byte_sym, _) => { + Self::LitBytes(byte_sym) + } LitKind::Byte(val) => Self::LitInt(val.into()), LitKind::Char(val) => Self::LitInt(val.into()), LitKind::Int(val, _) => Self::LitInt(val.get()), diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index fd368024177..9b5f138295c 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -76,7 +76,7 @@ fn get_open_options( .. } = span { - Argument::Set(*lit) + Argument::Set(lit) } else { // The function is called with a literal which is not a boolean literal. // This is theoretically possible, but not very likely. diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index c8e3462b24e..cf0c85990b1 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -104,7 +104,7 @@ fn len_comparison<'hir>( ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { macro_rules! int_lit_pat { ($id:ident) => { - ExprKind::Lit(&Spanned { + ExprKind::Lit(Spanned { node: LitKind::Int(Pu128($id), _), .. }) diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 3a08531cf1c..ac92ab5a245 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -324,7 +324,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } } - fn lit(&self, lit: &Binding<&Lit>) { + fn lit(&self, lit: &Binding) { let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index aaa071fd5c9..09299c869dc 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -4,8 +4,6 @@ //! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] -use std::sync::Arc; - use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; @@ -38,7 +36,7 @@ pub enum Constant<'tcx> { /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). - Binary(Arc<[u8]>), + Binary(Vec), /// A single `char` (e.g., `'a'`). Char(char), /// An integer's bit representation. @@ -306,7 +304,9 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => Constant::Binary(Arc::clone(s)), + LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => { + Constant::Binary(s.as_byte_str().to_vec()) + } LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n.get()), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { @@ -568,7 +568,9 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } else { match &lit.node { LitKind::Str(is, _) => Some(is.is_empty()), - LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => Some(s.is_empty()), + LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => { + Some(s.as_byte_str().is_empty()) + } _ => None, } } -- cgit 1.4.1-3-g733a5 From f3b3c096fdaf5f2041a0886a5f4375aa76c5c5e5 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Sun, 29 Jun 2025 02:14:43 +0500 Subject: Polishing changelog update doc --- book/src/development/infrastructure/backport.md | 2 +- .../development/infrastructure/changelog_update.md | 32 +++++++--------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/book/src/development/infrastructure/backport.md b/book/src/development/infrastructure/backport.md index 9526d8af1c9..47ea6a412c5 100644 --- a/book/src/development/infrastructure/backport.md +++ b/book/src/development/infrastructure/backport.md @@ -109,4 +109,4 @@ worth backporting this. When a PR is backported to Rust `beta`, label the PR with `beta-accepted`. This will then get picked up when [writing the changelog]. -[writing the changelog]: changelog_update.md#31-include-beta-accepted-prs +[writing the changelog]: changelog_update.md#4-include-beta-accepted-prs diff --git a/book/src/development/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md index eede6b78d92..c96ff228b01 100644 --- a/book/src/development/infrastructure/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md @@ -38,7 +38,7 @@ Usually you want to write the changelog of the **upcoming stable release**. Make sure though, that `beta` was already branched in the Rust repository. To find the commit hash, issue the following command when in a `rust-lang/rust` -checkout: +checkout (most of the time on the `upstream/beta` branch): ``` git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g" ``` @@ -48,16 +48,13 @@ git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into Once you've got the correct commit range, run ``` -util/fetch_prs_between.sh commit1 commit2 > changes.txt +util/fetch_prs_between.sh start_commit end_commit > changes.txt ``` -where `commit2` is the commit hash from the previous command and `commit1` -is the commit hash from the current CHANGELOG file. +where `end_commit` is the commit hash from the previous command and `start_commit` +is [the commit hash][beta_section] from the current CHANGELOG file. Open `changes.txt` file in your editor of choice. -When updating the changelog it's also a good idea to make sure that `commit1` is -already correct in the current changelog. - ### 3. Authoring the final changelog The above script should have dumped all the relevant PRs to the file you @@ -70,17 +67,7 @@ With the PRs filtered, you can start to take each PR and move the `changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it somewhat coherent. -The order should roughly be: - -1. New lints -2. Moves or deprecations of lints -3. Changes that expand what code existing lints cover -4. False positive fixes -5. ICE fixes -6. Documentation improvements -7. Others - -As section headers, we use: +The sections order should roughly be: ``` ### New Lints @@ -97,10 +84,10 @@ As section headers, we use: ### Others ``` -Please also be sure to update the Beta/Unreleased sections at the top with the -relevant commit ranges. +Please also be sure to update [the `Unreleased/Beta/In Rust Nightly` section][beta_section] at the top with the +relevant commits ranges and to add the `Rust ` section with release date and PR ranges. -#### 3.1 Include `beta-accepted` PRs +### 4. Include `beta-accepted` PRs Look for the [`beta-accepted`] label and make sure to also include the PRs with that label in the changelog. If you can, remove the `beta-accepted` labels @@ -109,7 +96,7 @@ that label in the changelog. If you can, remove the `beta-accepted` labels > _Note:_ Some of those PRs might even get backported to the previous `beta`. > Those have to be included in the changelog of the _previous_ release. -### 4. Update `clippy::version` attributes +### 5. Update `clippy::version` attributes Next, make sure to check that the `#[clippy::version]` attributes for the added lints contain the correct version. @@ -129,3 +116,4 @@ written for. If not, update the version to the changelog version. [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy [rust_stable_tools]: https://github.com/rust-lang/rust/releases [`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+ +[beta_section]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#unreleased--beta--in-rust-nightly -- cgit 1.4.1-3-g733a5 From a4d584e7a6d2bb4ce08b72faec0c35206dd6e4b7 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 14 Jun 2025 08:44:37 +0000 Subject: josh-sync: Replace `#xxxx`-style links in messages Often our short summaries will pick up a Bors "Auto merge of #xxxx ...` commit message. Replace these with something like `rust-lang/rust#1234` to avoid broken links when going between repositories. --- .../compiler-builtins/crates/josh-sync/Cargo.toml | 1 + .../compiler-builtins/crates/josh-sync/src/sync.rs | 30 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/library/compiler-builtins/crates/josh-sync/Cargo.toml b/library/compiler-builtins/crates/josh-sync/Cargo.toml index 1f3bb376d6d..8e2e891db54 100644 --- a/library/compiler-builtins/crates/josh-sync/Cargo.toml +++ b/library/compiler-builtins/crates/josh-sync/Cargo.toml @@ -5,3 +5,4 @@ publish = false [dependencies] directories = "6.0.0" +regex-lite = "0.1.6" diff --git a/library/compiler-builtins/crates/josh-sync/src/sync.rs b/library/compiler-builtins/crates/josh-sync/src/sync.rs index 003cf187d83..2d89d2d1cea 100644 --- a/library/compiler-builtins/crates/josh-sync/src/sync.rs +++ b/library/compiler-builtins/crates/josh-sync/src/sync.rs @@ -1,8 +1,11 @@ +use std::borrow::Cow; use std::net::{SocketAddr, TcpStream}; use std::process::{Command, Stdio, exit}; use std::time::Duration; use std::{env, fs, process, thread}; +use regex_lite::Regex; + const JOSH_PORT: u16 = 42042; const DEFAULT_PR_BRANCH: &str = "update-builtins"; @@ -77,6 +80,7 @@ impl GitSync { "--depth=1", ]); let new_summary = check_output(["git", "log", "-1", "--format=%h %s", &new_upstream_base]); + let new_summary = replace_references(&new_summary, &self.upstream_repo); // Update rust-version file. As a separate commit, since making it part of // the merge has confused the heck out of josh in the past. @@ -297,6 +301,13 @@ fn check_output_cfg(prog: &str, f: impl FnOnce(&mut Command) -> &mut Command) -> String::from_utf8(out.stdout.trim_ascii().to_vec()).expect("non-UTF8 output") } +/// Replace `#1234`-style issue/PR references with `repo#1234` to ensure links work across +/// repositories. +fn replace_references<'a>(s: &'a str, repo: &str) -> Cow<'a, str> { + let re = Regex::new(r"\B(?P#\d+)\b").unwrap(); + re.replace(s, &format!("{repo}$id")) +} + /// Create a wrapper that stops Josh on drop. pub struct Josh(process::Child); @@ -369,3 +380,22 @@ impl Drop for Josh { self.0.kill().expect("failed to SIGKILL josh-proxy"); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_replace() { + assert_eq!(replace_references("#1234", "r-l/rust"), "r-l/rust#1234"); + assert_eq!(replace_references("#1234x", "r-l/rust"), "#1234x"); + assert_eq!( + replace_references("merge #1234", "r-l/rust"), + "merge r-l/rust#1234" + ); + assert_eq!( + replace_references("foo/bar#1234", "r-l/rust"), + "foo/bar#1234" + ); + } +} -- cgit 1.4.1-3-g733a5 From a1e6ae9c3ead5ecc72667e02458734905ed930b5 Mon Sep 17 00:00:00 2001 From: Dario Heinisch Date: Mon, 30 Jun 2025 18:54:49 +0200 Subject: FIX: NegMultiply should preserve parenthesis when method is called changelog: [`neg_multiply`] does not remove parenthesis anymore if a method is being called on the affected expression --- clippy_lints/src/neg_multiply.rs | 40 ++++++++++++++++++++++++++++++++-------- tests/ui/neg_multiply.fixed | 12 ++++++++++++ tests/ui/neg_multiply.rs | 12 ++++++++++++ tests/ui/neg_multiply.stderr | 8 +++++++- 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 442280f9998..946114e1041 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -1,13 +1,13 @@ use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; +use clippy_utils::get_parent_expr; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -33,6 +33,19 @@ declare_clippy_lint! { declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); +fn is_in_parens_with_postfix(cx: &LateContext<'_>, mul_expr: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, mul_expr) { + let mult_snippet = snippet(cx, mul_expr.span, ""); + if has_enclosing_paren(&mult_snippet) + && let ExprKind::MethodCall(_, _, _, _) = parent.kind + { + return true; + } + } + + false +} + impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, left, right) = e.kind @@ -40,15 +53,15 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply { { match (&left.kind, &right.kind) { (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e, lit, left), _ => {}, } } } } -fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { +fn check_mul(cx: &LateContext<'_>, mul_expr: &Expr<'_>, lit: &Expr<'_>, exp: &Expr<'_>) { const F16_ONE: u16 = 1.0_f16.to_bits(); const F128_ONE: u128 = 1.0_f128.to_bits(); if let ExprKind::Lit(l) = lit.kind @@ -63,8 +76,19 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { && cx.typeck_results().expr_ty(exp).is_numeric() { let mut applicability = Applicability::MachineApplicable; - let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); - let suggestion = if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { + let (snip, from_macro) = snippet_with_context(cx, exp.span, mul_expr.span.ctxt(), "..", &mut applicability); + + let needs_parens_for_postfix = is_in_parens_with_postfix(cx, mul_expr); + + let suggestion = if needs_parens_for_postfix { + // Special case: when the multiplication is in parentheses followed by a method call + // we need to preserve the grouping but negate the inner expression. + // Consider this expression: `((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0)` + // We need to end up with: `(-(a.delta - 0.5).abs()).total_cmp(&1.0)` + // Otherwise, without the parentheses we would try to negate an Ordering: + // `-(a.delta - 0.5).abs().total_cmp(&1.0)` + format!("(-{snip})") + } else if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") @@ -72,7 +96,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { span_lint_and_sugg( cx, NEG_MULTIPLY, - span, + mul_expr.span, "this multiplication by -1 can be written more succinctly", "consider using", suggestion, diff --git a/tests/ui/neg_multiply.fixed b/tests/ui/neg_multiply.fixed index ff6e08300e2..93d314fea5f 100644 --- a/tests/ui/neg_multiply.fixed +++ b/tests/ui/neg_multiply.fixed @@ -82,3 +82,15 @@ fn float() { -1.0 * -1.0; // should be ok } + +struct Y { + delta: f64 +} + +fn nested() { + let a = Y {delta: 1.0}; + let b = Y {delta: 1.0}; + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); + //~^ neg_multiply + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); +} diff --git a/tests/ui/neg_multiply.rs b/tests/ui/neg_multiply.rs index b0f4e85c78e..5b60b57e7ef 100644 --- a/tests/ui/neg_multiply.rs +++ b/tests/ui/neg_multiply.rs @@ -82,3 +82,15 @@ fn float() { -1.0 * -1.0; // should be ok } + +struct Y { + delta: f64 +} + +fn nested() { + let a = Y {delta: 1.0}; + let b = Y {delta: 1.0}; + let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0); + //~^ neg_multiply + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); +} diff --git a/tests/ui/neg_multiply.stderr b/tests/ui/neg_multiply.stderr index 2ef7e32ce05..f4fb6d3ce54 100644 --- a/tests/ui/neg_multiply.stderr +++ b/tests/ui/neg_multiply.stderr @@ -97,5 +97,11 @@ error: this multiplication by -1 can be written more succinctly LL | (3.0_f32 as f64) * -1.0; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` -error: aborting due to 16 previous errors +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:93:13 + | +LL | let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-(a.delta - 0.5).abs())` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From c69905a995dad100185ef38fffcb1ec3079ec66a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 30 Jun 2025 19:45:09 +0200 Subject: Do not remove method call if type is adjusted Propose to replace `x.map_or(false, |y| y == z)` by `x == Some(z)` only if `x` is not adjusted. Otherwise, the type of `x` in the comparaison may not be the expected one, as it may be the product of an auto-deref. --- clippy_lints/src/methods/unnecessary_map_or.rs | 3 ++- tests/ui/unnecessary_map_or.fixed | 10 ++++++++++ tests/ui/unnecessary_map_or.rs | 10 ++++++++++ tests/ui/unnecessary_map_or.stderr | 26 +++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index b90748dd158..4a9007c607c 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -62,7 +62,8 @@ pub(super) fn check<'a>( let ext_def_span = def.span.until(map.span); - let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind + let (sugg, method, applicability) = if cx.typeck_results().expr_adjustments(recv).is_empty() + && let ExprKind::Closure(map_closure) = map.kind && let closure_body = cx.tcx.hir_body(map_closure.body) && let closure_body_value = closure_body.value.peel_blocks() && let ExprKind::Binary(op, l, r) = closure_body_value.kind diff --git a/tests/ui/unnecessary_map_or.fixed b/tests/ui/unnecessary_map_or.fixed index 3c724397284..3109c4af8e2 100644 --- a/tests/ui/unnecessary_map_or.fixed +++ b/tests/ui/unnecessary_map_or.fixed @@ -130,3 +130,13 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { //~^ unnecessary_map_or x && y } + +fn issue15180() { + let s = std::sync::Mutex::new(Some("foo")); + _ = s.lock().unwrap().is_some_and(|s| s == "foo"); + //~^ unnecessary_map_or + + let s = &&&&Some("foo"); + _ = s.is_some_and(|s| s == "foo"); + //~^ unnecessary_map_or +} diff --git a/tests/ui/unnecessary_map_or.rs b/tests/ui/unnecessary_map_or.rs index e734a27bada..52a55f9fc9e 100644 --- a/tests/ui/unnecessary_map_or.rs +++ b/tests/ui/unnecessary_map_or.rs @@ -134,3 +134,13 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { //~^ unnecessary_map_or x && y } + +fn issue15180() { + let s = std::sync::Mutex::new(Some("foo")); + _ = s.lock().unwrap().map_or(false, |s| s == "foo"); + //~^ unnecessary_map_or + + let s = &&&&Some("foo"); + _ = s.map_or(false, |s| s == "foo"); + //~^ unnecessary_map_or +} diff --git a/tests/ui/unnecessary_map_or.stderr b/tests/ui/unnecessary_map_or.stderr index 0f9466a6a6b..99e17e8b34b 100644 --- a/tests/ui/unnecessary_map_or.stderr +++ b/tests/ui/unnecessary_map_or.stderr @@ -326,5 +326,29 @@ LL - let y = b.map_or(true, |b| b == *s); LL + let y = b.is_none_or(|b| b == *s); | -error: aborting due to 26 previous errors +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:140:9 + | +LL | _ = s.lock().unwrap().map_or(false, |s| s == "foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - _ = s.lock().unwrap().map_or(false, |s| s == "foo"); +LL + _ = s.lock().unwrap().is_some_and(|s| s == "foo"); + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:144:9 + | +LL | _ = s.map_or(false, |s| s == "foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - _ = s.map_or(false, |s| s == "foo"); +LL + _ = s.is_some_and(|s| s == "foo"); + | + +error: aborting due to 28 previous errors -- cgit 1.4.1-3-g733a5 From 8ceb91e1c7f3d644ca26f45ae63dac2216f98a4e Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 30 Jun 2025 20:43:45 +0200 Subject: Do not remove `as` if it changes the type While `expr as T` can be removed as a statement if `expr` has no side-effect, the `as T` part alone cannot be removed if the type of `expr` would be ambiguous without the cast. --- clippy_lints/src/no_effect.rs | 6 ++++-- tests/ui/unnecessary_operation.fixed | 13 +++++++++++++ tests/ui/unnecessary_operation.rs | 13 +++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 02c48166131..72e6503e7e4 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::has_drop; +use clippy_utils::ty::{expr_type_is_certain, has_drop}; use clippy_utils::{ in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, }; @@ -340,11 +340,13 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option Some(v.iter().collect()), ExprKind::Repeat(inner, _) - | ExprKind::Cast(inner, _) | ExprKind::Type(inner, _) | ExprKind::Unary(_, inner) | ExprKind::Field(inner, _) | ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + ExprKind::Cast(inner, _) if expr_type_is_certain(cx, inner) => { + reduce_expression(cx, inner).or_else(|| Some(vec![inner])) + }, ExprKind::Struct(_, fields, ref base) => { if has_drop(cx, cx.typeck_results().expr_ty(expr)) { None diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index 645b56fe95e..ac9fa4de20a 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -144,3 +144,16 @@ const fn foo() { assert!([42, 55].len() > get_usize()); //~^ unnecessary_operation } + +fn issue15173() { + // No lint as `Box::new(None)` alone would be ambiguous + Box::new(None) as Box>; +} + +#[expect(clippy::redundant_closure_call)] +fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { + Box::new(move |value| { + (|_| handler.clone()())(value); + None + }) as Box Option>; +} diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index 97e90269c5c..a3e6c6288ad 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -150,3 +150,16 @@ const fn foo() { [42, 55][get_usize()]; //~^ unnecessary_operation } + +fn issue15173() { + // No lint as `Box::new(None)` alone would be ambiguous + Box::new(None) as Box>; +} + +#[expect(clippy::redundant_closure_call)] +fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { + Box::new(move |value| { + (|_| handler.clone()())(value); + None + }) as Box Option>; +} -- cgit 1.4.1-3-g733a5 From 9a7db566d7b7bb534c5dc3bcfbd2ddd51d99a8d5 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Tue, 1 Jul 2025 02:45:14 +0500 Subject: moved tests --- tests/ui/codegen/output-slot-init-vs-noninit.rs | 68 ++++++++++++++++++++++ tests/ui/codegen/remark-flag-functionality.rs | 28 +++++++++ tests/ui/codegen/shift-right-operand-mutation.rs | 12 ++++ tests/ui/codegen/sret-aliasing-rules.rs | 23 ++++++++ tests/ui/macros/macro-paren-span-diagnostic.rs | 21 +++++++ tests/ui/macros/macro-paren-span-diagnostic.stderr | 9 +++ tests/ui/optimization-remark.rs | 28 --------- tests/ui/out-pointer-aliasing.rs | 23 -------- tests/ui/output-slot-variants.rs | 68 ---------------------- tests/ui/over-constrained-vregs.rs | 12 ---- tests/ui/panic-while-printing.rs | 25 -------- tests/ui/panic_implementation-closures.rs | 10 ---- tests/ui/panics/panic-during-display-formatting.rs | 25 ++++++++ tests/ui/panics/panic-handler-closures.rs | 10 ++++ tests/ui/paren-span.rs | 21 ------- tests/ui/paren-span.stderr | 9 --- tests/ui/parser/ufcs-return-unused-parens.fixed | 17 ++++++ tests/ui/parser/ufcs-return-unused-parens.rs | 17 ++++++ tests/ui/parser/ufcs-return-unused-parens.stderr | 19 ++++++ tests/ui/partialeq_help.rs | 12 ---- tests/ui/partialeq_help.stderr | 35 ----------- tests/ui/path-lookahead.fixed | 17 ------ tests/ui/path-lookahead.rs | 17 ------ tests/ui/path-lookahead.stderr | 19 ------ .../ui/traits/partialeq-ref-mismatch-diagnostic.rs | 12 ++++ .../partialeq-ref-mismatch-diagnostic.stderr | 35 +++++++++++ 26 files changed, 296 insertions(+), 296 deletions(-) create mode 100644 tests/ui/codegen/output-slot-init-vs-noninit.rs create mode 100644 tests/ui/codegen/remark-flag-functionality.rs create mode 100644 tests/ui/codegen/shift-right-operand-mutation.rs create mode 100644 tests/ui/codegen/sret-aliasing-rules.rs create mode 100644 tests/ui/macros/macro-paren-span-diagnostic.rs create mode 100644 tests/ui/macros/macro-paren-span-diagnostic.stderr delete mode 100644 tests/ui/optimization-remark.rs delete mode 100644 tests/ui/out-pointer-aliasing.rs delete mode 100644 tests/ui/output-slot-variants.rs delete mode 100644 tests/ui/over-constrained-vregs.rs delete mode 100644 tests/ui/panic-while-printing.rs delete mode 100644 tests/ui/panic_implementation-closures.rs create mode 100644 tests/ui/panics/panic-during-display-formatting.rs create mode 100644 tests/ui/panics/panic-handler-closures.rs delete mode 100644 tests/ui/paren-span.rs delete mode 100644 tests/ui/paren-span.stderr create mode 100644 tests/ui/parser/ufcs-return-unused-parens.fixed create mode 100644 tests/ui/parser/ufcs-return-unused-parens.rs create mode 100644 tests/ui/parser/ufcs-return-unused-parens.stderr delete mode 100644 tests/ui/partialeq_help.rs delete mode 100644 tests/ui/partialeq_help.stderr delete mode 100644 tests/ui/path-lookahead.fixed delete mode 100644 tests/ui/path-lookahead.rs delete mode 100644 tests/ui/path-lookahead.stderr create mode 100644 tests/ui/traits/partialeq-ref-mismatch-diagnostic.rs create mode 100644 tests/ui/traits/partialeq-ref-mismatch-diagnostic.stderr diff --git a/tests/ui/codegen/output-slot-init-vs-noninit.rs b/tests/ui/codegen/output-slot-init-vs-noninit.rs new file mode 100644 index 00000000000..97757e74fc4 --- /dev/null +++ b/tests/ui/codegen/output-slot-init-vs-noninit.rs @@ -0,0 +1,68 @@ +//@ run-pass + +#![allow(dead_code)] +#![allow(unused_assignments)] +#![allow(unknown_lints)] + +#![allow(dead_assignment)] +#![allow(unused_variables)] + +struct A { a: isize, b: isize } +struct Abox { a: Box, b: Box } + +fn ret_int_i() -> isize { 10 } + +fn ret_ext_i() -> Box { Box::new(10) } + +fn ret_int_rec() -> A { A {a: 10, b: 10} } + +fn ret_ext_rec() -> Box { Box::new(A {a: 10, b: 10}) } + +fn ret_ext_mem() -> Abox { Abox {a: Box::new(10), b: Box::new(10) } } + +fn ret_ext_ext_mem() -> Box { Box::new(Abox{a: Box::new(10), b: Box::new(10) }) } + +pub fn main() { + let mut int_i: isize; + let mut ext_i: Box; + let mut int_rec: A; + let mut ext_rec: Box; + let mut ext_mem: Abox; + let mut ext_ext_mem: Box; + int_i = ret_int_i(); // initializing + + int_i = ret_int_i(); // non-initializing + + int_i = ret_int_i(); // non-initializing + + ext_i = ret_ext_i(); // initializing + + ext_i = ret_ext_i(); // non-initializing + + ext_i = ret_ext_i(); // non-initializing + + int_rec = ret_int_rec(); // initializing + + int_rec = ret_int_rec(); // non-initializing + + int_rec = ret_int_rec(); // non-initializing + + ext_rec = ret_ext_rec(); // initializing + + ext_rec = ret_ext_rec(); // non-initializing + + ext_rec = ret_ext_rec(); // non-initializing + + ext_mem = ret_ext_mem(); // initializing + + ext_mem = ret_ext_mem(); // non-initializing + + ext_mem = ret_ext_mem(); // non-initializing + + ext_ext_mem = ret_ext_ext_mem(); // initializing + + ext_ext_mem = ret_ext_ext_mem(); // non-initializing + + ext_ext_mem = ret_ext_ext_mem(); // non-initializing + +} diff --git a/tests/ui/codegen/remark-flag-functionality.rs b/tests/ui/codegen/remark-flag-functionality.rs new file mode 100644 index 00000000000..165fc63c007 --- /dev/null +++ b/tests/ui/codegen/remark-flag-functionality.rs @@ -0,0 +1,28 @@ +//@ build-pass +//@ ignore-pass +//@ revisions: all inline merge1 merge2 +//@ compile-flags: --crate-type=lib -Cdebuginfo=1 -Copt-level=2 +// +// Check that remarks can be enabled individually or with "all": +// +//@ [all] compile-flags: -Cremark=all +//@ [inline] compile-flags: -Cremark=inline +// +// Check that values of -Cremark flag are accumulated: +// +//@ [merge1] compile-flags: -Cremark=all -Cremark=giraffe +//@ [merge2] compile-flags: -Cremark=inline -Cremark=giraffe +//@ dont-check-compiler-stderr +//@ dont-require-annotations: NOTE + +#[no_mangle] +#[inline(never)] +pub fn f() { +} + +#[no_mangle] +pub fn g() { + f(); +} + +//~? NOTE inline (missed): 'f' not inlined into 'g' diff --git a/tests/ui/codegen/shift-right-operand-mutation.rs b/tests/ui/codegen/shift-right-operand-mutation.rs new file mode 100644 index 00000000000..016a667e937 --- /dev/null +++ b/tests/ui/codegen/shift-right-operand-mutation.rs @@ -0,0 +1,12 @@ +//@ run-pass + +#![allow(unused_must_use)] +// Regression test for issue #152. +pub fn main() { + let mut b: usize = 1_usize; + while b < std::mem::size_of::() { + 0_usize << b; + b <<= 1_usize; + println!("{}", b); + } +} diff --git a/tests/ui/codegen/sret-aliasing-rules.rs b/tests/ui/codegen/sret-aliasing-rules.rs new file mode 100644 index 00000000000..0dfaa19fadb --- /dev/null +++ b/tests/ui/codegen/sret-aliasing-rules.rs @@ -0,0 +1,23 @@ +//@ run-pass + +#[derive(Copy, Clone)] +pub struct Foo { + f1: isize, + _f2: isize, +} + +#[inline(never)] +pub fn foo(f: &mut Foo) -> Foo { + let ret = *f; + f.f1 = 0; + ret +} + +pub fn main() { + let mut f = Foo { + f1: 8, + _f2: 9, + }; + f = foo(&mut f); + assert_eq!(f.f1, 8); +} diff --git a/tests/ui/macros/macro-paren-span-diagnostic.rs b/tests/ui/macros/macro-paren-span-diagnostic.rs new file mode 100644 index 00000000000..c8cb63d5190 --- /dev/null +++ b/tests/ui/macros/macro-paren-span-diagnostic.rs @@ -0,0 +1,21 @@ +// Be smart about span of parenthesized expression in macro. + +macro_rules! paren { + ($e:expr) => (($e)) + // ^^^^ do not highlight here +} + +mod m { + pub struct S { + x: i32 + } + pub fn make() -> S { + S { x: 0 } + } +} + +fn main() { + let s = m::make(); + paren!(s.x); //~ ERROR field `x` of struct `S` is private + // ^^^ highlight here +} diff --git a/tests/ui/macros/macro-paren-span-diagnostic.stderr b/tests/ui/macros/macro-paren-span-diagnostic.stderr new file mode 100644 index 00000000000..da2f57033a4 --- /dev/null +++ b/tests/ui/macros/macro-paren-span-diagnostic.stderr @@ -0,0 +1,9 @@ +error[E0616]: field `x` of struct `S` is private + --> $DIR/paren-span.rs:19:14 + | +LL | paren!(s.x); + | ^ private field + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/optimization-remark.rs b/tests/ui/optimization-remark.rs deleted file mode 100644 index 165fc63c007..00000000000 --- a/tests/ui/optimization-remark.rs +++ /dev/null @@ -1,28 +0,0 @@ -//@ build-pass -//@ ignore-pass -//@ revisions: all inline merge1 merge2 -//@ compile-flags: --crate-type=lib -Cdebuginfo=1 -Copt-level=2 -// -// Check that remarks can be enabled individually or with "all": -// -//@ [all] compile-flags: -Cremark=all -//@ [inline] compile-flags: -Cremark=inline -// -// Check that values of -Cremark flag are accumulated: -// -//@ [merge1] compile-flags: -Cremark=all -Cremark=giraffe -//@ [merge2] compile-flags: -Cremark=inline -Cremark=giraffe -//@ dont-check-compiler-stderr -//@ dont-require-annotations: NOTE - -#[no_mangle] -#[inline(never)] -pub fn f() { -} - -#[no_mangle] -pub fn g() { - f(); -} - -//~? NOTE inline (missed): 'f' not inlined into 'g' diff --git a/tests/ui/out-pointer-aliasing.rs b/tests/ui/out-pointer-aliasing.rs deleted file mode 100644 index 0dfaa19fadb..00000000000 --- a/tests/ui/out-pointer-aliasing.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ run-pass - -#[derive(Copy, Clone)] -pub struct Foo { - f1: isize, - _f2: isize, -} - -#[inline(never)] -pub fn foo(f: &mut Foo) -> Foo { - let ret = *f; - f.f1 = 0; - ret -} - -pub fn main() { - let mut f = Foo { - f1: 8, - _f2: 9, - }; - f = foo(&mut f); - assert_eq!(f.f1, 8); -} diff --git a/tests/ui/output-slot-variants.rs b/tests/ui/output-slot-variants.rs deleted file mode 100644 index 97757e74fc4..00000000000 --- a/tests/ui/output-slot-variants.rs +++ /dev/null @@ -1,68 +0,0 @@ -//@ run-pass - -#![allow(dead_code)] -#![allow(unused_assignments)] -#![allow(unknown_lints)] - -#![allow(dead_assignment)] -#![allow(unused_variables)] - -struct A { a: isize, b: isize } -struct Abox { a: Box, b: Box } - -fn ret_int_i() -> isize { 10 } - -fn ret_ext_i() -> Box { Box::new(10) } - -fn ret_int_rec() -> A { A {a: 10, b: 10} } - -fn ret_ext_rec() -> Box { Box::new(A {a: 10, b: 10}) } - -fn ret_ext_mem() -> Abox { Abox {a: Box::new(10), b: Box::new(10) } } - -fn ret_ext_ext_mem() -> Box { Box::new(Abox{a: Box::new(10), b: Box::new(10) }) } - -pub fn main() { - let mut int_i: isize; - let mut ext_i: Box; - let mut int_rec: A; - let mut ext_rec: Box; - let mut ext_mem: Abox; - let mut ext_ext_mem: Box; - int_i = ret_int_i(); // initializing - - int_i = ret_int_i(); // non-initializing - - int_i = ret_int_i(); // non-initializing - - ext_i = ret_ext_i(); // initializing - - ext_i = ret_ext_i(); // non-initializing - - ext_i = ret_ext_i(); // non-initializing - - int_rec = ret_int_rec(); // initializing - - int_rec = ret_int_rec(); // non-initializing - - int_rec = ret_int_rec(); // non-initializing - - ext_rec = ret_ext_rec(); // initializing - - ext_rec = ret_ext_rec(); // non-initializing - - ext_rec = ret_ext_rec(); // non-initializing - - ext_mem = ret_ext_mem(); // initializing - - ext_mem = ret_ext_mem(); // non-initializing - - ext_mem = ret_ext_mem(); // non-initializing - - ext_ext_mem = ret_ext_ext_mem(); // initializing - - ext_ext_mem = ret_ext_ext_mem(); // non-initializing - - ext_ext_mem = ret_ext_ext_mem(); // non-initializing - -} diff --git a/tests/ui/over-constrained-vregs.rs b/tests/ui/over-constrained-vregs.rs deleted file mode 100644 index 016a667e937..00000000000 --- a/tests/ui/over-constrained-vregs.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ run-pass - -#![allow(unused_must_use)] -// Regression test for issue #152. -pub fn main() { - let mut b: usize = 1_usize; - while b < std::mem::size_of::() { - 0_usize << b; - b <<= 1_usize; - println!("{}", b); - } -} diff --git a/tests/ui/panic-while-printing.rs b/tests/ui/panic-while-printing.rs deleted file mode 100644 index 6505a69fef7..00000000000 --- a/tests/ui/panic-while-printing.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ run-pass -//@ needs-unwind - -#![feature(internal_output_capture)] - -use std::fmt; -use std::fmt::{Display, Formatter}; -use std::io::set_output_capture; -use std::sync::{Arc, Mutex}; - -pub struct A; - -impl Display for A { - fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { - panic!(); - } -} - -fn main() { - set_output_capture(Some(Arc::new(Mutex::new(Vec::new())))); - assert!(std::panic::catch_unwind(|| { - eprintln!("{}", A); - }) - .is_err()); -} diff --git a/tests/ui/panic_implementation-closures.rs b/tests/ui/panic_implementation-closures.rs deleted file mode 100644 index b161859bf9c..00000000000 --- a/tests/ui/panic_implementation-closures.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ build-pass (FIXME(62277): could be check-pass?) - -#![crate_type = "rlib"] -#![no_std] - -#[panic_handler] -pub fn panic_fmt(_: &::core::panic::PanicInfo) -> ! { - |x: u8| x; - loop {} -} diff --git a/tests/ui/panics/panic-during-display-formatting.rs b/tests/ui/panics/panic-during-display-formatting.rs new file mode 100644 index 00000000000..6505a69fef7 --- /dev/null +++ b/tests/ui/panics/panic-during-display-formatting.rs @@ -0,0 +1,25 @@ +//@ run-pass +//@ needs-unwind + +#![feature(internal_output_capture)] + +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::io::set_output_capture; +use std::sync::{Arc, Mutex}; + +pub struct A; + +impl Display for A { + fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { + panic!(); + } +} + +fn main() { + set_output_capture(Some(Arc::new(Mutex::new(Vec::new())))); + assert!(std::panic::catch_unwind(|| { + eprintln!("{}", A); + }) + .is_err()); +} diff --git a/tests/ui/panics/panic-handler-closures.rs b/tests/ui/panics/panic-handler-closures.rs new file mode 100644 index 00000000000..b161859bf9c --- /dev/null +++ b/tests/ui/panics/panic-handler-closures.rs @@ -0,0 +1,10 @@ +//@ build-pass (FIXME(62277): could be check-pass?) + +#![crate_type = "rlib"] +#![no_std] + +#[panic_handler] +pub fn panic_fmt(_: &::core::panic::PanicInfo) -> ! { + |x: u8| x; + loop {} +} diff --git a/tests/ui/paren-span.rs b/tests/ui/paren-span.rs deleted file mode 100644 index c8cb63d5190..00000000000 --- a/tests/ui/paren-span.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Be smart about span of parenthesized expression in macro. - -macro_rules! paren { - ($e:expr) => (($e)) - // ^^^^ do not highlight here -} - -mod m { - pub struct S { - x: i32 - } - pub fn make() -> S { - S { x: 0 } - } -} - -fn main() { - let s = m::make(); - paren!(s.x); //~ ERROR field `x` of struct `S` is private - // ^^^ highlight here -} diff --git a/tests/ui/paren-span.stderr b/tests/ui/paren-span.stderr deleted file mode 100644 index da2f57033a4..00000000000 --- a/tests/ui/paren-span.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0616]: field `x` of struct `S` is private - --> $DIR/paren-span.rs:19:14 - | -LL | paren!(s.x); - | ^ private field - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/parser/ufcs-return-unused-parens.fixed b/tests/ui/parser/ufcs-return-unused-parens.fixed new file mode 100644 index 00000000000..440b22edd7d --- /dev/null +++ b/tests/ui/parser/ufcs-return-unused-parens.fixed @@ -0,0 +1,17 @@ +//@ run-pass +//@ run-rustfix + +#![allow(dead_code)] +#![warn(unused_parens)] + +// Parser test for #37765 + +fn with_parens(arg: T) -> String { + return ::to_string(&arg); //~WARN unnecessary parentheses around `return` value +} + +fn no_parens(arg: T) -> String { + return ::to_string(&arg); +} + +fn main() {} diff --git a/tests/ui/parser/ufcs-return-unused-parens.rs b/tests/ui/parser/ufcs-return-unused-parens.rs new file mode 100644 index 00000000000..7eaacd6bba7 --- /dev/null +++ b/tests/ui/parser/ufcs-return-unused-parens.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ run-rustfix + +#![allow(dead_code)] +#![warn(unused_parens)] + +// Parser test for #37765 + +fn with_parens(arg: T) -> String { + return (::to_string(&arg)); //~WARN unnecessary parentheses around `return` value +} + +fn no_parens(arg: T) -> String { + return ::to_string(&arg); +} + +fn main() {} diff --git a/tests/ui/parser/ufcs-return-unused-parens.stderr b/tests/ui/parser/ufcs-return-unused-parens.stderr new file mode 100644 index 00000000000..2cc786fd947 --- /dev/null +++ b/tests/ui/parser/ufcs-return-unused-parens.stderr @@ -0,0 +1,19 @@ +warning: unnecessary parentheses around `return` value + --> $DIR/path-lookahead.rs:10:12 + | +LL | return (::to_string(&arg)); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/path-lookahead.rs:5:9 + | +LL | #![warn(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - return (::to_string(&arg)); +LL + return ::to_string(&arg); + | + +warning: 1 warning emitted + diff --git a/tests/ui/partialeq_help.rs b/tests/ui/partialeq_help.rs deleted file mode 100644 index 34b88b8a866..00000000000 --- a/tests/ui/partialeq_help.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn foo(a: &T, b: T) { - a == b; //~ ERROR E0277 -} - -fn foo2(a: &T, b: T) where { - a == b; //~ ERROR E0277 -} - -fn main() { - foo(&1, 1); - foo2(&1, 1); -} diff --git a/tests/ui/partialeq_help.stderr b/tests/ui/partialeq_help.stderr deleted file mode 100644 index f5de1308e87..00000000000 --- a/tests/ui/partialeq_help.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error[E0277]: can't compare `&T` with `T` - --> $DIR/partialeq_help.rs:2:7 - | -LL | a == b; - | ^^ no implementation for `&T == T` - | - = help: the trait `PartialEq` is not implemented for `&T` -help: consider dereferencing here - | -LL | *a == b; - | + -help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement - | -LL | fn foo(a: &T, b: T) where &T: PartialEq { - | ++++++++++++++++++++++ - -error[E0277]: can't compare `&T` with `T` - --> $DIR/partialeq_help.rs:6:7 - | -LL | a == b; - | ^^ no implementation for `&T == T` - | - = help: the trait `PartialEq` is not implemented for `&T` -help: consider dereferencing here - | -LL | *a == b; - | + -help: consider extending the `where` clause, but there might be an alternative better way to express this requirement - | -LL | fn foo2(a: &T, b: T) where &T: PartialEq { - | ++++++++++++++++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/path-lookahead.fixed b/tests/ui/path-lookahead.fixed deleted file mode 100644 index 440b22edd7d..00000000000 --- a/tests/ui/path-lookahead.fixed +++ /dev/null @@ -1,17 +0,0 @@ -//@ run-pass -//@ run-rustfix - -#![allow(dead_code)] -#![warn(unused_parens)] - -// Parser test for #37765 - -fn with_parens(arg: T) -> String { - return ::to_string(&arg); //~WARN unnecessary parentheses around `return` value -} - -fn no_parens(arg: T) -> String { - return ::to_string(&arg); -} - -fn main() {} diff --git a/tests/ui/path-lookahead.rs b/tests/ui/path-lookahead.rs deleted file mode 100644 index 7eaacd6bba7..00000000000 --- a/tests/ui/path-lookahead.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ run-pass -//@ run-rustfix - -#![allow(dead_code)] -#![warn(unused_parens)] - -// Parser test for #37765 - -fn with_parens(arg: T) -> String { - return (::to_string(&arg)); //~WARN unnecessary parentheses around `return` value -} - -fn no_parens(arg: T) -> String { - return ::to_string(&arg); -} - -fn main() {} diff --git a/tests/ui/path-lookahead.stderr b/tests/ui/path-lookahead.stderr deleted file mode 100644 index 2cc786fd947..00000000000 --- a/tests/ui/path-lookahead.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: unnecessary parentheses around `return` value - --> $DIR/path-lookahead.rs:10:12 - | -LL | return (::to_string(&arg)); - | ^ ^ - | -note: the lint level is defined here - --> $DIR/path-lookahead.rs:5:9 - | -LL | #![warn(unused_parens)] - | ^^^^^^^^^^^^^ -help: remove these parentheses - | -LL - return (::to_string(&arg)); -LL + return ::to_string(&arg); - | - -warning: 1 warning emitted - diff --git a/tests/ui/traits/partialeq-ref-mismatch-diagnostic.rs b/tests/ui/traits/partialeq-ref-mismatch-diagnostic.rs new file mode 100644 index 00000000000..34b88b8a866 --- /dev/null +++ b/tests/ui/traits/partialeq-ref-mismatch-diagnostic.rs @@ -0,0 +1,12 @@ +fn foo(a: &T, b: T) { + a == b; //~ ERROR E0277 +} + +fn foo2(a: &T, b: T) where { + a == b; //~ ERROR E0277 +} + +fn main() { + foo(&1, 1); + foo2(&1, 1); +} diff --git a/tests/ui/traits/partialeq-ref-mismatch-diagnostic.stderr b/tests/ui/traits/partialeq-ref-mismatch-diagnostic.stderr new file mode 100644 index 00000000000..f5de1308e87 --- /dev/null +++ b/tests/ui/traits/partialeq-ref-mismatch-diagnostic.stderr @@ -0,0 +1,35 @@ +error[E0277]: can't compare `&T` with `T` + --> $DIR/partialeq_help.rs:2:7 + | +LL | a == b; + | ^^ no implementation for `&T == T` + | + = help: the trait `PartialEq` is not implemented for `&T` +help: consider dereferencing here + | +LL | *a == b; + | + +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | fn foo(a: &T, b: T) where &T: PartialEq { + | ++++++++++++++++++++++ + +error[E0277]: can't compare `&T` with `T` + --> $DIR/partialeq_help.rs:6:7 + | +LL | a == b; + | ^^ no implementation for `&T == T` + | + = help: the trait `PartialEq` is not implemented for `&T` +help: consider dereferencing here + | +LL | *a == b; + | + +help: consider extending the `where` clause, but there might be an alternative better way to express this requirement + | +LL | fn foo2(a: &T, b: T) where &T: PartialEq { + | ++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. -- cgit 1.4.1-3-g733a5 From fd3161c20125252db57a5a14f8031338c431f626 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 30 Jun 2025 18:58:43 -0700 Subject: Mention as_chunks in the docs for chunks --- library/core/src/slice/mod.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 3a3f44c6b85..b472307871f 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1120,6 +1120,9 @@ impl [T] { /// `chunk_size` elements, and [`rchunks`] for the same iterator but starting at the end of the /// slice. /// + /// If your `chunk_size` is a constant, consider using [`as_chunks`] instead, which will + /// give references to arrays of exactly that length, rather than slices. + /// /// # Panics /// /// Panics if `chunk_size` is zero. @@ -1137,6 +1140,7 @@ impl [T] { /// /// [`chunks_exact`]: slice::chunks_exact /// [`rchunks`]: slice::rchunks + /// [`as_chunks`]: slice::as_chunks #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] @@ -1156,6 +1160,9 @@ impl [T] { /// exactly `chunk_size` elements, and [`rchunks_mut`] for the same iterator but starting at /// the end of the slice. /// + /// If your `chunk_size` is a constant, consider using [`as_chunks_mut`] instead, which will + /// give references to arrays of exactly that length, rather than slices. + /// /// # Panics /// /// Panics if `chunk_size` is zero. @@ -1177,6 +1184,7 @@ impl [T] { /// /// [`chunks_exact_mut`]: slice::chunks_exact_mut /// [`rchunks_mut`]: slice::rchunks_mut + /// [`as_chunks_mut`]: slice::as_chunks_mut #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] @@ -1199,6 +1207,9 @@ impl [T] { /// See [`chunks`] for a variant of this iterator that also returns the remainder as a smaller /// chunk, and [`rchunks_exact`] for the same iterator but starting at the end of the slice. /// + /// If your `chunk_size` is a constant, consider using [`as_chunks`] instead, which will + /// give references to arrays of exactly that length, rather than slices. + /// /// # Panics /// /// Panics if `chunk_size` is zero. @@ -1216,6 +1227,7 @@ impl [T] { /// /// [`chunks`]: slice::chunks /// [`rchunks_exact`]: slice::rchunks_exact + /// [`as_chunks`]: slice::chunks #[stable(feature = "chunks_exact", since = "1.31.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] @@ -1239,6 +1251,9 @@ impl [T] { /// smaller chunk, and [`rchunks_exact_mut`] for the same iterator but starting at the end of /// the slice. /// + /// If your `chunk_size` is a constant, consider using [`as_chunks_mut`] instead, which will + /// give references to arrays of exactly that length, rather than slices. + /// /// # Panics /// /// Panics if `chunk_size` is zero. @@ -1260,6 +1275,7 @@ impl [T] { /// /// [`chunks_mut`]: slice::chunks_mut /// [`rchunks_exact_mut`]: slice::rchunks_exact_mut + /// [`as_chunks_mut`]: slice::as_chunks_mut #[stable(feature = "chunks_exact", since = "1.31.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] @@ -1707,6 +1723,9 @@ impl [T] { /// `chunk_size` elements, and [`chunks`] for the same iterator but starting at the beginning /// of the slice. /// + /// If your `chunk_size` is a constant, consider using [`as_rchunks`] instead, which will + /// give references to arrays of exactly that length, rather than slices. + /// /// # Panics /// /// Panics if `chunk_size` is zero. @@ -1724,6 +1743,7 @@ impl [T] { /// /// [`rchunks_exact`]: slice::rchunks_exact /// [`chunks`]: slice::chunks + /// [`as_rchunks`]: slice::as_rchunks #[stable(feature = "rchunks", since = "1.31.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] @@ -1743,6 +1763,9 @@ impl [T] { /// exactly `chunk_size` elements, and [`chunks_mut`] for the same iterator but starting at the /// beginning of the slice. /// + /// If your `chunk_size` is a constant, consider using [`as_rchunks_mut`] instead, which will + /// give references to arrays of exactly that length, rather than slices. + /// /// # Panics /// /// Panics if `chunk_size` is zero. @@ -1764,6 +1787,7 @@ impl [T] { /// /// [`rchunks_exact_mut`]: slice::rchunks_exact_mut /// [`chunks_mut`]: slice::chunks_mut + /// [`as_rchunks_mut`]: slice::as_rchunks_mut #[stable(feature = "rchunks", since = "1.31.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] @@ -1787,6 +1811,9 @@ impl [T] { /// chunk, and [`chunks_exact`] for the same iterator but starting at the beginning of the /// slice. /// + /// If your `chunk_size` is a constant, consider using [`as_rchunks`] instead, which will + /// give references to arrays of exactly that length, rather than slices. + /// /// # Panics /// /// Panics if `chunk_size` is zero. @@ -1805,6 +1832,7 @@ impl [T] { /// [`chunks`]: slice::chunks /// [`rchunks`]: slice::rchunks /// [`chunks_exact`]: slice::chunks_exact + /// [`as_rchunks`]: slice::as_rchunks #[stable(feature = "rchunks", since = "1.31.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] @@ -1828,6 +1856,9 @@ impl [T] { /// smaller chunk, and [`chunks_exact_mut`] for the same iterator but starting at the beginning /// of the slice. /// + /// If your `chunk_size` is a constant, consider using [`as_rchunks_mut`] instead, which will + /// give references to arrays of exactly that length, rather than slices. + /// /// # Panics /// /// Panics if `chunk_size` is zero. @@ -1850,6 +1881,7 @@ impl [T] { /// [`chunks_mut`]: slice::chunks_mut /// [`rchunks_mut`]: slice::rchunks_mut /// [`chunks_exact_mut`]: slice::chunks_exact_mut + /// [`as_rchunks_mut`]: slice::as_rchunks_mut #[stable(feature = "rchunks", since = "1.31.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] -- cgit 1.4.1-3-g733a5 From 1fcd632be3b3e10f2fe3401f29af1250f496c52f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 30 Jun 2025 21:29:57 +0200 Subject: bump ui_test to 0.30.1 --- Cargo.toml | 2 +- tests/compile-test.rs | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8cbdcf45693..0f85d040f08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.29.2" +ui_test = "0.30.1" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index cefe654fef6..c9718161c4c 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -17,7 +17,8 @@ use ui_test::custom_flags::Flag; use ui_test::custom_flags::edition::Edition; use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; -use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter}; +use ui_test::status_emitter::StatusEmitter; +use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict}; use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; @@ -217,7 +218,7 @@ fn run_ui(cx: &TestContext) { vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } @@ -233,7 +234,7 @@ fn run_internal_tests(cx: &TestContext) { vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } @@ -257,7 +258,7 @@ fn run_ui_toml(cx: &TestContext) { .envs .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into()))); }, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } @@ -304,7 +305,7 @@ fn run_ui_cargo(cx: &TestContext) { .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path)) }, |_config, _file_contents| {}, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } -- cgit 1.4.1-3-g733a5 From e32c65d714a4c5698a032b24a8908c3ddc09b38c Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 30 Jun 2025 11:30:02 +0200 Subject: Activate feature freeze automated notice --- .github/workflows/feature_freeze.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/feature_freeze.yml b/.github/workflows/feature_freeze.yml index a5f8d4bc145..7ad58af77d4 100644 --- a/.github/workflows/feature_freeze.yml +++ b/.github/workflows/feature_freeze.yml @@ -1,7 +1,11 @@ name: Feature freeze check on: - pull_request: + pull_request_target: + types: + - opened + branches: + - master paths: - 'clippy_lints/src/declared_lints.rs' @@ -9,6 +13,12 @@ jobs: auto-comment: runs-on: ubuntu-latest + permissions: + pull-requests: write + + # Do not in any case add code that runs anything coming from the the content + # of the pull request, as malicious code would be able to access the private + # GitHub token. steps: - name: Check PR Changes id: pr-changes @@ -19,7 +29,7 @@ jobs: run: | # Use GitHub API to create a comment on the PR PR_NUMBER=${{ github.event.pull_request.number }} - COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to August 1st and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team" + COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to September 18 and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team\n\n@rustbot note Feature-freeze\n@rustbot blocked\n@rustbot label +A-lint\n" GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL -d "{\"body\":\"$COMMENT\"}" -- cgit 1.4.1-3-g733a5 From 7ca9f9363dbb42a845ea70dfb449253f08c83ce1 Mon Sep 17 00:00:00 2001 From: Tim Neumann Date: Tue, 1 Jul 2025 09:17:29 +0200 Subject: tests/codegen/enum/enum-match.rs: accept negative range attribute --- tests/codegen/enum/enum-match.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen/enum/enum-match.rs b/tests/codegen/enum/enum-match.rs index 6da6ad1f078..98635008d06 100644 --- a/tests/codegen/enum/enum-match.rs +++ b/tests/codegen/enum/enum-match.rs @@ -98,7 +98,7 @@ pub enum Enum2 { E, } -// CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match2(i8{{.+}}%0) +// CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, -?[0-9]+\))?}} i8 @match2(i8{{.+}}%0) // CHECK-NEXT: start: // CHECK-NEXT: %[[REL_VAR:.+]] = add i8 %0, 2 // CHECK-NEXT: %[[REL_VAR_WIDE:.+]] = zext i8 %[[REL_VAR]] to i64 -- cgit 1.4.1-3-g733a5 From 6c4221818e00828ecf54b395475477f49d0ea716 Mon Sep 17 00:00:00 2001 From: quaternic <57393910+quaternic@users.noreply.github.com> Date: Tue, 1 Jul 2025 11:07:48 +0300 Subject: libm: Improved integer utilities, implement shifts and bug fixes for i256 and u256 `i256` and `u256` - operators now use the same overflow convention as primitives - implement `<<` and `-` (previously just `>>` and `+`) - implement `Ord` correctly (the previous `PartialOrd` was broken) - correct `i256::SIGNED` to `true` The `Int`-trait is extended with `trailing_zeros`, `carrying_add`, and `borrowing_sub`. --- .../compiler-builtins/libm-test/benches/icount.rs | 18 ++- library/compiler-builtins/libm-test/tests/u256.rs | 46 ++++++- .../compiler-builtins/libm/src/math/support/big.rs | 133 ++++++++++++--------- .../libm/src/math/support/big/tests.rs | 63 +++++++++- .../libm/src/math/support/int_traits.rs | 23 +++- 5 files changed, 223 insertions(+), 60 deletions(-) diff --git a/library/compiler-builtins/libm-test/benches/icount.rs b/library/compiler-builtins/libm-test/benches/icount.rs index a0928a29f99..02ee13f804f 100644 --- a/library/compiler-builtins/libm-test/benches/icount.rs +++ b/library/compiler-builtins/libm-test/benches/icount.rs @@ -119,6 +119,22 @@ fn icount_bench_u256_add(cases: Vec<(u256, u256)>) { } } +#[library_benchmark] +#[bench::linspace(setup_u256_add())] +fn icount_bench_u256_sub(cases: Vec<(u256, u256)>) { + for (x, y) in cases.iter().copied() { + black_box(black_box(x) - black_box(y)); + } +} + +#[library_benchmark] +#[bench::linspace(setup_u256_shift())] +fn icount_bench_u256_shl(cases: Vec<(u256, u32)>) { + for (x, y) in cases.iter().copied() { + black_box(black_box(x) << black_box(y)); + } +} + #[library_benchmark] #[bench::linspace(setup_u256_shift())] fn icount_bench_u256_shr(cases: Vec<(u256, u32)>) { @@ -129,7 +145,7 @@ fn icount_bench_u256_shr(cases: Vec<(u256, u32)>) { library_benchmark_group!( name = icount_bench_u128_group; - benchmarks = icount_bench_u128_widen_mul, icount_bench_u256_add, icount_bench_u256_shr + benchmarks = icount_bench_u128_widen_mul, icount_bench_u256_add, icount_bench_u256_sub, icount_bench_u256_shl, icount_bench_u256_shr ); #[library_benchmark] diff --git a/library/compiler-builtins/libm-test/tests/u256.rs b/library/compiler-builtins/libm-test/tests/u256.rs index 8cbb3ad226f..d1c5cfbcc58 100644 --- a/library/compiler-builtins/libm-test/tests/u256.rs +++ b/library/compiler-builtins/libm-test/tests/u256.rs @@ -111,12 +111,54 @@ fn mp_u256_add() { let y = random_u256(&mut rng); assign_bigint(&mut bx, x); assign_bigint(&mut by, y); - let actual = x + y; + let actual = if u256::MAX - x >= y { + x + y + } else { + // otherwise (u256::MAX - x) < y, so the wrapped result is + // (x + y) - (u256::MAX + 1) == y - (u256::MAX - x) - 1 + y - (u256::MAX - x) - 1_u128.widen() + }; bx += &by; check_one(|| hexu(x), || Some(hexu(y)), actual, &mut bx); } } +#[test] +fn mp_u256_sub() { + let mut rng = ChaCha8Rng::from_seed(*SEED); + let mut bx = BigInt::new(); + let mut by = BigInt::new(); + + for _ in 0..bigint_fuzz_iteration_count() { + let x = random_u256(&mut rng); + let y = random_u256(&mut rng); + assign_bigint(&mut bx, x); + assign_bigint(&mut by, y); + + // since the operators (may) panic on overflow, + // we should test something that doesn't + let actual = if x >= y { x - y } else { y - x }; + bx -= &by; + bx.abs_mut(); + check_one(|| hexu(x), || Some(hexu(y)), actual, &mut bx); + } +} + +#[test] +fn mp_u256_shl() { + let mut rng = ChaCha8Rng::from_seed(*SEED); + let mut bx = BigInt::new(); + + for _ in 0..bigint_fuzz_iteration_count() { + let x = random_u256(&mut rng); + let shift: u32 = rng.random_range(0..256); + assign_bigint(&mut bx, x); + let actual = x << shift; + bx <<= shift; + check_one(|| hexu(x), || Some(shift.to_string()), actual, &mut bx); + } +} + #[test] fn mp_u256_shr() { let mut rng = ChaCha8Rng::from_seed(*SEED); @@ -124,7 +166,7 @@ fn mp_u256_shr() { for _ in 0..bigint_fuzz_iteration_count() { let x = random_u256(&mut rng); - let shift: u32 = rng.random_range(0..255); + let shift: u32 = rng.random_range(0..256); assign_bigint(&mut bx, x); let actual = x >> shift; bx >>= shift; diff --git a/library/compiler-builtins/libm/src/math/support/big.rs b/library/compiler-builtins/libm/src/math/support/big.rs index 8a52d86cc98..b7f12854249 100644 --- a/library/compiler-builtins/libm/src/math/support/big.rs +++ b/library/compiler-builtins/libm/src/math/support/big.rs @@ -11,10 +11,10 @@ const U128_LO_MASK: u128 = u64::MAX as u128; /// A 256-bit unsigned integer represented as two 128-bit native-endian limbs. #[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] pub struct u256 { - pub lo: u128, pub hi: u128, + pub lo: u128, } impl u256 { @@ -28,17 +28,17 @@ impl u256 { pub fn signed(self) -> i256 { i256 { lo: self.lo, - hi: self.hi, + hi: self.hi as i128, } } } /// A 256-bit signed integer represented as two 128-bit native-endian limbs. #[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] pub struct i256 { + pub hi: i128, pub lo: u128, - pub hi: u128, } impl i256 { @@ -47,7 +47,7 @@ impl i256 { pub fn unsigned(self) -> u256 { u256 { lo: self.lo, - hi: self.hi, + hi: self.hi as u128, } } } @@ -73,17 +73,17 @@ impl MinInt for i256 { type Unsigned = u256; - const SIGNED: bool = false; + const SIGNED: bool = true; const BITS: u32 = 256; const ZERO: Self = Self { lo: 0, hi: 0 }; const ONE: Self = Self { lo: 1, hi: 0 }; const MIN: Self = Self { - lo: 0, - hi: 1 << 127, + lo: u128::MIN, + hi: i128::MIN, }; const MAX: Self = Self { lo: u128::MAX, - hi: u128::MAX >> 1, + hi: i128::MAX, }; } @@ -109,60 +109,86 @@ macro_rules! impl_common { } } - impl ops::Shl for $ty { + impl ops::Add for $ty { type Output = Self; - fn shl(self, _rhs: u32) -> Self::Output { - unimplemented!("only used to meet trait bounds") + fn add(self, rhs: Self) -> Self::Output { + let (lo, carry) = self.lo.overflowing_add(rhs.lo); + let (hi, of) = Int::carrying_add(self.hi, rhs.hi, carry); + debug_assert!(!of, "attempt to add with overflow"); + Self { lo, hi } } } - }; -} -impl_common!(i256); -impl_common!(u256); + impl ops::Sub for $ty { + type Output = Self; -impl ops::Add for u256 { - type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + let (lo, borrow) = self.lo.overflowing_sub(rhs.lo); + let (hi, of) = Int::borrowing_sub(self.hi, rhs.hi, borrow); + debug_assert!(!of, "attempt to subtract with overflow"); + Self { lo, hi } + } + } - fn add(self, rhs: Self) -> Self::Output { - let (lo, carry) = self.lo.overflowing_add(rhs.lo); - let hi = self.hi.wrapping_add(carry as u128).wrapping_add(rhs.hi); + impl ops::Shl for $ty { + type Output = Self; - Self { lo, hi } - } -} + fn shl(mut self, rhs: u32) -> Self::Output { + debug_assert!(rhs < Self::BITS, "attempt to shift left with overflow"); -impl ops::Shr for u256 { - type Output = Self; + let half_bits = Self::BITS / 2; + let low_mask = half_bits - 1; + let s = rhs & low_mask; - fn shr(mut self, rhs: u32) -> Self::Output { - debug_assert!(rhs < Self::BITS, "attempted to shift right with overflow"); - if rhs >= Self::BITS { - return Self::ZERO; - } + let lo = self.lo; + let hi = self.hi; - if rhs == 0 { - return self; - } + self.lo = lo << s; - if rhs < 128 { - self.lo >>= rhs; - self.lo |= self.hi << (128 - rhs); - } else { - self.lo = self.hi >> (rhs - 128); + if rhs & half_bits == 0 { + self.hi = (lo >> (low_mask ^ s) >> 1) as _; + self.hi |= hi << s; + } else { + self.hi = self.lo as _; + self.lo = 0; + } + self + } } - if rhs < 128 { - self.hi >>= rhs; - } else { - self.hi = 0; - } + impl ops::Shr for $ty { + type Output = Self; - self - } + fn shr(mut self, rhs: u32) -> Self::Output { + debug_assert!(rhs < Self::BITS, "attempt to shift right with overflow"); + + let half_bits = Self::BITS / 2; + let low_mask = half_bits - 1; + let s = rhs & low_mask; + + let lo = self.lo; + let hi = self.hi; + + self.hi = hi >> s; + + #[allow(unused_comparisons)] + if rhs & half_bits == 0 { + self.lo = (hi << (low_mask ^ s) << 1) as _; + self.lo |= lo >> s; + } else { + self.lo = self.hi as _; + self.hi = if hi < 0 { !0 } else { 0 }; + } + self + } + } + }; } +impl_common!(i256); +impl_common!(u256); + impl HInt for u128 { type D = u256; @@ -200,7 +226,7 @@ impl HInt for u128 { } fn widen_hi(self) -> Self::D { - self.widen() << ::BITS + u256 { lo: 0, hi: self } } } @@ -208,11 +234,10 @@ impl HInt for i128 { type D = i256; fn widen(self) -> Self::D { - let mut ret = self.unsigned().zero_widen().signed(); - if self.is_negative() { - ret.hi = u128::MAX; + i256 { + lo: self as u128, + hi: if self < 0 { -1 } else { 0 }, } - ret } fn zero_widen(self) -> Self::D { @@ -228,7 +253,7 @@ impl HInt for i128 { } fn widen_hi(self) -> Self::D { - self.widen() << ::BITS + i256 { lo: 0, hi: self } } } @@ -252,6 +277,6 @@ impl DInt for i256 { } fn hi(self) -> Self::H { - self.hi as i128 + self.hi } } diff --git a/library/compiler-builtins/libm/src/math/support/big/tests.rs b/library/compiler-builtins/libm/src/math/support/big/tests.rs index d2010f0216e..d54706c7260 100644 --- a/library/compiler-builtins/libm/src/math/support/big/tests.rs +++ b/library/compiler-builtins/libm/src/math/support/big/tests.rs @@ -36,7 +36,7 @@ fn widen_i128() { (LOHI_SPLIT as i128).widen(), i256 { lo: LOHI_SPLIT, - hi: u128::MAX + hi: -1, } ); assert_eq!((-1i128).zero_widen().unsigned(), (u128::MAX).widen()); @@ -275,3 +275,64 @@ fn shr_u256_overflow() { assert_eq!(u256::MAX >> 257, u256::ZERO); assert_eq!(u256::MAX >> u32::MAX, u256::ZERO); } + +#[test] +fn u256_ord() { + let _1 = u256::ONE; + let _2 = _1 + _1; + for x in u8::MIN..u8::MAX { + let y = x + 1; + let wx = (x as u128).widen_hi(); + let wy = (y as u128).widen_hi(); + assert!([wx, wx + _1, wx + _2, wy, wy + _1, wy + _2].is_sorted()); + } +} +#[test] +fn i256_ord() { + let _1 = i256::ONE; + let _2 = _1 + _1; + for x in i8::MIN..i8::MAX { + let y = x + 1; + let wx = (x as i128).widen_hi(); + let wy = (y as i128).widen_hi(); + assert!([wx, wx + _1, wx + _2, wy - _2, wy - _1, wy].is_sorted()); + } +} + +#[test] +fn u256_shifts() { + let _1 = u256::ONE; + for k in 0..255 { + let x = _1 << k; + let x2 = _1 << (k + 1); + assert!(x < x2); + assert_eq!(x << 1, x2); + assert_eq!(x + x, x2); + assert_eq!(x >> k, _1); + assert_eq!(x2 >> (k + 1), _1); + } +} +#[test] +fn i256_shifts() { + let _1 = i256::ONE; + for k in 0..254 { + let x = _1 << k; + let x2 = _1 << (k + 1); + assert!(x < x2); + assert_eq!(x << 1, x2); + assert_eq!(x + x, x2); + assert_eq!(x >> k, _1); + assert_eq!(x2 >> (k + 1), _1); + } + + let min = _1 << 255; + assert_eq!(min, i256::MIN); + let mut x = min; + for k in 0..255 { + assert_eq!(x, min >> k); + let y = x >> 1; + assert_eq!(y + y, x); + assert!(x < y); + x = y; + } +} diff --git a/library/compiler-builtins/libm/src/math/support/int_traits.rs b/library/compiler-builtins/libm/src/math/support/int_traits.rs index 9b29e2f4561..9d8826dfed3 100644 --- a/library/compiler-builtins/libm/src/math/support/int_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/int_traits.rs @@ -37,8 +37,6 @@ pub trait Int: + fmt::Display + fmt::Binary + fmt::LowerHex - + PartialEq - + PartialOrd + ops::AddAssign + ops::SubAssign + ops::MulAssign @@ -102,7 +100,10 @@ pub trait Int: fn rotate_left(self, other: u32) -> Self; fn overflowing_add(self, other: Self) -> (Self, bool); fn overflowing_sub(self, other: Self) -> (Self, bool); + fn carrying_add(self, other: Self, carry: bool) -> (Self, bool); + fn borrowing_sub(self, other: Self, borrow: bool) -> (Self, bool); fn leading_zeros(self) -> u32; + fn trailing_zeros(self) -> u32; fn ilog2(self) -> u32; } @@ -168,12 +169,30 @@ macro_rules! int_impl_common { ::leading_zeros(self) } + fn trailing_zeros(self) -> u32 { + ::trailing_zeros(self) + } + fn ilog2(self) -> u32 { // On our older MSRV, this resolves to the trait method. Which won't actually work, // but this is only called behind other gates. #[allow(clippy::incompatible_msrv)] ::ilog2(self) } + + fn carrying_add(self, other: Self, carry: bool) -> (Self, bool) { + let (ab, of1) = self.overflowing_add(other); + let (abc, of2) = ab.overflowing_add(Self::from_bool(carry)); + // `of1 && of2` is possible with signed integers if a negative sum + // overflows to `MAX` and adding the carry overflows again back to `MIN` + (abc, of1 ^ of2) + } + + fn borrowing_sub(self, other: Self, borrow: bool) -> (Self, bool) { + let (ab, of1) = self.overflowing_sub(other); + let (abc, of2) = ab.overflowing_sub(Self::from_bool(borrow)); + (abc, of1 ^ of2) + } }; } -- cgit 1.4.1-3-g733a5 From 708ea873b11b7a099cbdd59c1d45f3743362554e Mon Sep 17 00:00:00 2001 From: Dario Heinisch Date: Tue, 1 Jul 2025 10:46:27 +0200 Subject: run cargo dev fmt --- tests/ui/neg_multiply.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/neg_multiply.rs b/tests/ui/neg_multiply.rs index 5b60b57e7ef..241a72c6d99 100644 --- a/tests/ui/neg_multiply.rs +++ b/tests/ui/neg_multiply.rs @@ -84,12 +84,12 @@ fn float() { } struct Y { - delta: f64 + delta: f64, } fn nested() { - let a = Y {delta: 1.0}; - let b = Y {delta: 1.0}; + let a = Y { delta: 1.0 }; + let b = Y { delta: 1.0 }; let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0); //~^ neg_multiply let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); -- cgit 1.4.1-3-g733a5 From e7fa5364c67dbd45a92432678878d406ad715630 Mon Sep 17 00:00:00 2001 From: Dario Heinisch Date: Tue, 1 Jul 2025 10:51:18 +0200 Subject: fix tests (run cargo uibless after fmt) --- tests/ui/neg_multiply.fixed | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/neg_multiply.fixed b/tests/ui/neg_multiply.fixed index 93d314fea5f..32d466e88fc 100644 --- a/tests/ui/neg_multiply.fixed +++ b/tests/ui/neg_multiply.fixed @@ -84,12 +84,12 @@ fn float() { } struct Y { - delta: f64 + delta: f64, } fn nested() { - let a = Y {delta: 1.0}; - let b = Y {delta: 1.0}; + let a = Y { delta: 1.0 }; + let b = Y { delta: 1.0 }; let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); //~^ neg_multiply let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); -- cgit 1.4.1-3-g733a5 From 5ff1a4164c98ca3815b103a6dfbf26a0d817cb69 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Jul 2025 13:27:49 +0200 Subject: use ui_test dependency builder for non-internal test dependencies --- Cargo.toml | 10 +------ clippy_test_deps/Cargo.toml | 17 ++++++++++++ clippy_test_deps/src/main.rs | 1 + tests/compile-test.rs | 65 +++++++++++++++++++------------------------- 4 files changed, 47 insertions(+), 46 deletions(-) create mode 100644 clippy_test_deps/Cargo.toml create mode 100644 clippy_test_deps/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 0f85d040f08..bdc299749d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.30.1" +ui_test = "0.30.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" @@ -45,14 +45,6 @@ itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } -# UI test dependencies -if_chain = "1.0" -quote = "1.0.25" -syn = { version = "2.0", features = ["full"] } -futures = "0.3" -parking_lot = "0.12" -tokio = { version = "1", features = ["io-util"] } - [build-dependencies] rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } diff --git a/clippy_test_deps/Cargo.toml b/clippy_test_deps/Cargo.toml new file mode 100644 index 00000000000..f41334f0ade --- /dev/null +++ b/clippy_test_deps/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "clippy_test_deps" +version = "0.1.0" +edition = "2021" + +# Add dependencies here to make them available in ui tests. + +[dependencies] +regex = "1.5.5" +serde = { version = "1.0.145", features = ["derive"] } +if_chain = "1.0" +quote = "1.0.25" +syn = { version = "2.0", features = ["full"] } +futures = "0.3" +parking_lot = "0.12" +tokio = { version = "1", features = ["io-util"] } +itertools = "0.12" diff --git a/clippy_test_deps/src/main.rs b/clippy_test_deps/src/main.rs new file mode 100644 index 00000000000..f328e4d9d04 --- /dev/null +++ b/clippy_test_deps/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c9718161c4c..aa786334711 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -16,6 +16,7 @@ use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; use ui_test::custom_flags::edition::Edition; use ui_test::custom_flags::rustfix::RustfixMode; +use ui_test::dependencies::DependencyBuilder; use ui_test::spanned::Spanned; use ui_test::status_emitter::StatusEmitter; use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict}; @@ -28,46 +29,26 @@ use std::path::{Path, PathBuf}; use std::sync::mpsc::{Sender, channel}; use std::{fs, iter, thread}; -// Test dependencies may need an `extern crate` here to ensure that they show up -// in the depinfo file (otherwise cargo thinks they are unused) -extern crate futures; -extern crate if_chain; -extern crate itertools; -extern crate parking_lot; -extern crate quote; -extern crate syn; -extern crate tokio; - mod test_utils; -/// All crates used in UI tests are listed here -static TEST_DEPENDENCIES: &[&str] = &[ - "clippy_config", - "clippy_lints", - "clippy_utils", - "futures", - "if_chain", - "itertools", - "parking_lot", - "quote", - "regex", - "serde_derive", - "serde", - "syn", - "tokio", -]; - -/// Produces a string with an `--extern` flag for all UI test crate -/// dependencies. +/// All crates used in internal UI tests are listed here. +/// We directly re-use these crates from their normal clippy builds, so we don't have them +/// in `clippy_test_devs`. That saves a lot of time but also means they don't work in a stage 1 +/// test in rustc bootstrap. +static INTERNAL_TEST_DEPENDENCIES: &[&str] = &["clippy_config", "clippy_lints", "clippy_utils"]; + +/// Produces a string with an `--extern` flag for all `INTERNAL_TEST_DEPENDENCIES`. /// /// The dependency files are located by parsing the depinfo file for this test /// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test /// dependencies must be added to Cargo.toml at the project root. Test /// dependencies that are not *directly* used by this test module require an /// `extern crate` declaration. -fn extern_flags() -> Vec { +fn internal_extern_flags() -> Vec { + let current_exe_path = env::current_exe().unwrap(); + let deps_path = current_exe_path.parent().unwrap(); let current_exe_depinfo = { - let mut path = env::current_exe().unwrap(); + let mut path = current_exe_path.clone(); path.set_extension("d"); fs::read_to_string(path).unwrap() }; @@ -89,7 +70,7 @@ fn extern_flags() -> Vec { Some((name, path_str)) }; if let Some((name, path)) = parse_name_path() - && TEST_DEPENDENCIES.contains(&name) + && INTERNAL_TEST_DEPENDENCIES.contains(&name) { // A dependency may be listed twice if it is available in sysroot, // and the sysroot dependencies are listed first. As of the writing, @@ -97,7 +78,7 @@ fn extern_flags() -> Vec { crates.insert(name, path); } } - let not_found: Vec<&str> = TEST_DEPENDENCIES + let not_found: Vec<&str> = INTERNAL_TEST_DEPENDENCIES .iter() .copied() .filter(|n| !crates.contains_key(n)) @@ -112,6 +93,7 @@ fn extern_flags() -> Vec { crates .into_iter() .map(|(name, path)| format!("--extern={name}={path}")) + .chain([format!("-Ldependency={}", deps_path.display())]) .collect() } @@ -120,7 +102,6 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); struct TestContext { args: Args, - extern_flags: Vec, diagnostic_collector: Option, collector_thread: Option>, } @@ -135,7 +116,6 @@ impl TestContext { .unzip(); Self { args, - extern_flags: extern_flags(), diagnostic_collector, collector_thread, } @@ -159,6 +139,15 @@ impl TestContext { }; let defaults = config.comment_defaults.base(); defaults.set_custom("edition", Edition("2024".into())); + defaults.set_custom( + "dependencies", + DependencyBuilder { + program: CommandBuilder::cargo(), + crate_manifest_path: Path::new("clippy_test_deps").join("Cargo.toml"), + build_std: None, + bless_lockfile: self.args.bless, + }, + ); defaults.exit_status = None.into(); if mandatory_annotations { defaults.require_annotations = Some(Spanned::dummy(true)).into(); @@ -183,12 +172,10 @@ impl TestContext { "-Zui-testing", "-Zdeduplicate-diagnostics=no", "-Dwarnings", - &format!("-Ldependency={}", deps_path.display()), ] .map(OsString::from), ); - config.program.args.extend(self.extern_flags.iter().map(OsString::from)); // Prevent rustc from creating `rustc-ice-*` files the console output is enough. config.program.envs.push(("RUSTC_ICE".into(), Some("0".into()))); @@ -228,6 +215,10 @@ fn run_internal_tests(cx: &TestContext) { return; } let mut config = cx.base_config("ui-internal", true); + config + .program + .args + .extend(internal_extern_flags().iter().map(OsString::from)); config.bless_command = Some("cargo uitest --features internal -- -- --bless".into()); ui_test::run_tests_generic( -- cgit 1.4.1-3-g733a5 From 9eb85122558fd7c3a8909939f9cbe4d0f15d73f3 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 29 Jun 2025 01:13:17 +0800 Subject: fix: `std-instead-of-core` FP when not all items come from the new crate --- clippy_lints/src/std_instead_of_core.rs | 109 ++++++++++++++++++-------- tests/ui/std_instead_of_core.fixed | 7 -- tests/ui/std_instead_of_core.rs | 7 -- tests/ui/std_instead_of_core.stderr | 30 +++---- tests/ui/std_instead_of_core_unfixable.rs | 18 +++++ tests/ui/std_instead_of_core_unfixable.stderr | 30 +++++++ 6 files changed, 139 insertions(+), 62 deletions(-) create mode 100644 tests/ui/std_instead_of_core_unfixable.rs create mode 100644 tests/ui/std_instead_of_core_unfixable.stderr diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 442b3250d86..cf70e883bd0 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::Msrv; use rustc_attr_data_structures::{StabilityLevel, StableSince}; use rustc_errors::Applicability; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Block, Body, HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; @@ -88,49 +88,52 @@ declare_clippy_lint! { } pub struct StdReexports { - lint_point: (Span, Option), + lint_points: Option<(Span, Vec)>, msrv: Msrv, } impl StdReexports { pub fn new(conf: &'static Conf) -> Self { Self { - lint_point: Default::default(), + lint_points: Option::default(), msrv: conf.msrv, } } - fn lint_if_finish(&mut self, cx: &LateContext<'_>, (span, item): (Span, Option)) { - if span.source_equal(self.lint_point.0) { - return; + fn lint_if_finish(&mut self, cx: &LateContext<'_>, krate: Span, lint_point: LintPoint) { + match &mut self.lint_points { + Some((prev_krate, prev_lints)) if prev_krate.overlaps(krate) => { + prev_lints.push(lint_point); + }, + _ => emit_lints(cx, self.lint_points.replace((krate, vec![lint_point]))), } - - if !self.lint_point.0.is_dummy() { - emit_lints(cx, &self.lint_point); - } - - self.lint_point = (span, item); } } impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); -type LintPoint = (&'static Lint, &'static str, &'static str); +#[derive(Debug)] +enum LintPoint { + Available(Span, &'static Lint, &'static str, &'static str), + Conflict, +} impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { - if let Res::Def(_, def_id) = path.res + if let Res::Def(def_kind, def_id) = path.res && let Some(first_segment) = get_first_segment(path) && is_stable(cx, def_id, self.msrv) && !path.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, &first_segment.ident) + && !matches!(def_kind, DefKind::Macro(_)) + && let Some(last_segment) = path.segments.last() { let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { sym::core => (STD_INSTEAD_OF_CORE, "std", "core"), sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"), _ => { - self.lint_if_finish(cx, (first_segment.ident.span, None)); + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; }, }, @@ -138,44 +141,84 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if cx.tcx.crate_name(def_id.krate) == sym::core { (ALLOC_INSTEAD_OF_CORE, "alloc", "core") } else { - self.lint_if_finish(cx, (first_segment.ident.span, None)); + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; } }, - _ => return, + _ => { + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); + return; + }, }; - self.lint_if_finish(cx, (first_segment.ident.span, Some((lint, used_mod, replace_with)))); + self.lint_if_finish( + cx, + first_segment.ident.span, + LintPoint::Available(last_segment.ident.span, lint, used_mod, replace_with), + ); } } fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &Block<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } fn check_body_post(&mut self, cx: &LateContext<'tcx>, _: &Body<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } } -fn emit_lints(cx: &LateContext<'_>, (span, item): &(Span, Option)) { - let Some((lint, used_mod, replace_with)) = item else { +fn emit_lints(cx: &LateContext<'_>, lint_points: Option<(Span, Vec)>) { + let Some((krate_span, lint_points)) = lint_points else { return; }; - span_lint_and_sugg( - cx, - lint, - *span, - format!("used import from `{used_mod}` instead of `{replace_with}`"), - format!("consider importing the item from `{replace_with}`"), - (*replace_with).to_string(), - Applicability::MachineApplicable, - ); + let mut lint: Option<(&'static Lint, &'static str, &'static str)> = None; + let mut has_conflict = false; + for lint_point in &lint_points { + match lint_point { + LintPoint::Available(_, l, used_mod, replace_with) + if lint.is_none_or(|(prev_l, ..)| l.name == prev_l.name) => + { + lint = Some((l, used_mod, replace_with)); + }, + _ => { + has_conflict = true; + break; + }, + } + } + + if !has_conflict && let Some((lint, used_mod, replace_with)) = lint { + span_lint_and_sugg( + cx, + lint, + krate_span, + format!("used import from `{used_mod}` instead of `{replace_with}`"), + format!("consider importing the item from `{replace_with}`"), + (*replace_with).to_string(), + Applicability::MachineApplicable, + ); + return; + } + + for lint_point in lint_points { + let LintPoint::Available(span, lint, used_mod, replace_with) = lint_point else { + continue; + }; + span_lint_and_help( + cx, + lint, + span, + format!("used import from `{used_mod}` instead of `{replace_with}`"), + None, + format!("consider importing the item from `{replace_with}`"), + ); + } } /// Returns the first named segment of a [`Path`]. diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index 1820ade422f..603ab0accb0 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed @@ -8,7 +8,6 @@ extern crate alloc; #[macro_use] extern crate proc_macro_derive; -#[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import use core::hash::Hasher; @@ -90,9 +89,3 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: core::net::IpAddr) {} //~^ std_instead_of_core - -#[warn(clippy::std_instead_of_core)] -#[rustfmt::skip] -fn issue14982() { - use std::{collections::HashMap, hash::Hash}; -} diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 32c49330981..b6d4abad9f8 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -8,7 +8,6 @@ extern crate alloc; #[macro_use] extern crate proc_macro_derive; -#[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import use std::hash::Hasher; @@ -90,9 +89,3 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: std::net::IpAddr) {} //~^ std_instead_of_core - -#[warn(clippy::std_instead_of_core)] -#[rustfmt::skip] -fn issue14982() { - use std::{collections::HashMap, hash::Hash}; -} diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 45d60d235ce..a5f8fbbe37c 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -1,5 +1,5 @@ error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:14:9 + --> tests/ui/std_instead_of_core.rs:13:9 | LL | use std::hash::Hasher; | ^^^ help: consider importing the item from `core`: `core` @@ -8,61 +8,61 @@ LL | use std::hash::Hasher; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:17:11 + --> tests/ui/std_instead_of_core.rs:16:11 | LL | use ::std::hash::Hash; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:23:9 + --> tests/ui/std_instead_of_core.rs:22:9 | LL | use std::fmt::{Debug, Result}; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:28:9 + --> tests/ui/std_instead_of_core.rs:27:9 | LL | use std::{ | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:35:15 + --> tests/ui/std_instead_of_core.rs:34:15 | LL | let ptr = std::ptr::null::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:37:21 + --> tests/ui/std_instead_of_core.rs:36:21 | LL | let ptr_mut = ::std::ptr::null_mut::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:41:16 + --> tests/ui/std_instead_of_core.rs:40:16 | LL | let cell = std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:43:27 + --> tests/ui/std_instead_of_core.rs:42:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:48:9 + --> tests/ui/std_instead_of_core.rs:47:9 | LL | use std::error::Error; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:52:9 + --> tests/ui/std_instead_of_core.rs:51:9 | LL | use std::iter::Iterator; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:59:9 + --> tests/ui/std_instead_of_core.rs:58:9 | LL | use std::vec; | ^^^ help: consider importing the item from `alloc`: `alloc` @@ -71,13 +71,13 @@ LL | use std::vec; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:61:9 + --> tests/ui/std_instead_of_core.rs:60:9 | LL | use std::vec::Vec; | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> tests/ui/std_instead_of_core.rs:67:9 + --> tests/ui/std_instead_of_core.rs:66:9 | LL | use alloc::slice::from_ref; | ^^^^^ help: consider importing the item from `core`: `core` @@ -86,13 +86,13 @@ LL | use alloc::slice::from_ref; = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:82:9 + --> tests/ui/std_instead_of_core.rs:81:9 | LL | std::intrinsics::copy(a, b, 1); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:91:17 + --> tests/ui/std_instead_of_core.rs:90:17 | LL | fn msrv_1_77(_: std::net::IpAddr) {} | ^^^ help: consider importing the item from `core`: `core` diff --git a/tests/ui/std_instead_of_core_unfixable.rs b/tests/ui/std_instead_of_core_unfixable.rs new file mode 100644 index 00000000000..957f472a454 --- /dev/null +++ b/tests/ui/std_instead_of_core_unfixable.rs @@ -0,0 +1,18 @@ +//@no-rustfix + +#![warn(clippy::std_instead_of_core)] +#![warn(clippy::std_instead_of_alloc)] +#![allow(unused_imports)] + +#[rustfmt::skip] +fn issue14982() { + use std::{collections::HashMap, hash::Hash}; + //~^ std_instead_of_core +} + +#[rustfmt::skip] +fn issue15143() { + use std::{error::Error, vec::Vec, fs::File}; + //~^ std_instead_of_core + //~| std_instead_of_alloc +} diff --git a/tests/ui/std_instead_of_core_unfixable.stderr b/tests/ui/std_instead_of_core_unfixable.stderr new file mode 100644 index 00000000000..0cdec56c992 --- /dev/null +++ b/tests/ui/std_instead_of_core_unfixable.stderr @@ -0,0 +1,30 @@ +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core_unfixable.rs:9:43 + | +LL | use std::{collections::HashMap, hash::Hash}; + | ^^^^ + | + = help: consider importing the item from `core` + = note: `-D clippy::std-instead-of-core` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core_unfixable.rs:15:22 + | +LL | use std::{error::Error, vec::Vec, fs::File}; + | ^^^^^ + | + = help: consider importing the item from `core` + +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:15:34 + | +LL | use std::{error::Error, vec::Vec, fs::File}; + | ^^^ + | + = help: consider importing the item from `alloc` + = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 4793fd1c46f3e091b2c81e20a2c1d59f049760a3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Jul 2025 15:15:44 +0200 Subject: Update clippy source code to make use of `TyCtxt::def_descr` instead of `ItemKind::descr` --- clippy_lints/src/undocumented_unsafe_blocks.rs | 10 ++++++++-- .../undocumented_unsafe_blocks.default.stderr | 2 +- .../undocumented_unsafe_blocks.disabled.stderr | 2 +- tests/ui/unnecessary_safety_comment.stderr | 4 ++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 92427473a8e..6cc4b589a72 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -256,7 +256,10 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { cx, UNNECESSARY_SAFETY_COMMENT, span, - format!("{} has unnecessary safety comment", item.kind.descr()), + format!( + "{} has unnecessary safety comment", + cx.tcx.def_descr(item.owner_id.to_def_id()), + ), |diag| { diag.span_help(help_span, "consider removing the safety comment"); }, @@ -274,7 +277,10 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { cx, UNNECESSARY_SAFETY_COMMENT, span, - format!("{} has unnecessary safety comment", item.kind.descr()), + format!( + "{} has unnecessary safety comment", + cx.tcx.def_descr(item.owner_id.to_def_id()), + ), |diag| { diag.span_help(help_span, "consider removing the safety comment"); }, diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index 8a2f201009a..bfc14be5421 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -240,7 +240,7 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: | = help: consider adding a safety comment on the preceding line -error: constant item has unnecessary safety comment +error: constant has unnecessary safety comment --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:508:5 | LL | const BIG_NUMBER: i32 = 1000000; diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index e9c5e5f9f11..20cdff5fcd1 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -240,7 +240,7 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: | = help: consider adding a safety comment on the preceding line -error: constant item has unnecessary safety comment +error: constant has unnecessary safety comment --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:508:5 | LL | const BIG_NUMBER: i32 = 1000000; diff --git a/tests/ui/unnecessary_safety_comment.stderr b/tests/ui/unnecessary_safety_comment.stderr index b56e8b35493..732e6767c17 100644 --- a/tests/ui/unnecessary_safety_comment.stderr +++ b/tests/ui/unnecessary_safety_comment.stderr @@ -1,4 +1,4 @@ -error: constant item has unnecessary safety comment +error: constant has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:6:5 | LL | const CONST: u32 = 0; @@ -12,7 +12,7 @@ LL | // SAFETY: = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` -error: static item has unnecessary safety comment +error: static has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:10:5 | LL | static STATIC: u32 = 0; -- cgit 1.4.1-3-g733a5 From eeaa63f39aadd452ae4dc1710820c8e9b4883ab2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 26 Jun 2025 02:01:38 +0000 Subject: Remove support for dyn* --- clippy_utils/src/qualify_min_const_fn.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 8f1ebb8ada6..942c71ac33b 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -175,10 +175,6 @@ fn check_rvalue<'tcx>( Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { Err((span, "casting pointers to ints is unstable in const fn".into())) }, - Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::DynStar, _), _, _) => { - // FIXME(dyn-star) - unimplemented!() - }, Rvalue::Cast(CastKind::Transmute, _, _) => Err(( span, "transmute can attempt to turn pointers into integers, so is unstable in const fn".into(), -- cgit 1.4.1-3-g733a5 From fc3d5e682781ec440dc21f3235207e3d4e230de3 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 21 May 2025 17:28:19 +0200 Subject: Remove unneeded `.as_bytes()` `&str` already implements `AsRef<[u8]>` --- clippy_dev/src/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index c1b6b370706..bd9e57c9f6d 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -223,7 +223,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if check { return Err(Error::CheckFailed); } - fs::write(path, new_text.as_bytes())?; + fs::write(path, new_text)?; } Ok(()) } -- cgit 1.4.1-3-g733a5 From be35d37d8b6c206576ff3cdcad956d921b47cc6b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 30 Jun 2025 16:53:21 -0500 Subject: Use the compiler to determine whether or not to enable `f16` and `f128` Currently we whether or not to build and test `f16` and `f128` support mostly based on the target triple. This isn't always accurate, however, since support also varies by backend and the backend version. Since recently, `rustc` is aware of this with the unstable config option `target_has_reliable_{f16,f128}`, which better represents when the types are actually expected to be available and usable. Switch our compiler-builtins and libm configuration to use this by probing `rustc` for the target's settings. A few small `cfg` fixes are needed with this. --- .../builtins-test-intrinsics/build.rs | 1 - .../builtins-test/benches/float_cmp.rs | 2 + library/compiler-builtins/builtins-test/build.rs | 1 - .../compiler-builtins/builtins-test/tests/conv.rs | 4 +- .../builtins-test/tests/div_rem.rs | 4 +- .../compiler-builtins/compiler-builtins/build.rs | 3 +- .../compiler-builtins/configure.rs | 81 ++++++++-------------- library/compiler-builtins/libm/configure.rs | 81 ++++++++-------------- 8 files changed, 66 insertions(+), 111 deletions(-) diff --git a/library/compiler-builtins/builtins-test-intrinsics/build.rs b/library/compiler-builtins/builtins-test-intrinsics/build.rs index 89b126ff2b2..b82581262f7 100644 --- a/library/compiler-builtins/builtins-test-intrinsics/build.rs +++ b/library/compiler-builtins/builtins-test-intrinsics/build.rs @@ -6,6 +6,5 @@ fn main() { println!("cargo::rerun-if-changed=../configure.rs"); let target = builtins_configure::Target::from_env(); - builtins_configure::configure_f16_f128(&target); builtins_configure::configure_aliases(&target); } diff --git a/library/compiler-builtins/builtins-test/benches/float_cmp.rs b/library/compiler-builtins/builtins-test/benches/float_cmp.rs index 87a89efb5a4..da29b5d3132 100644 --- a/library/compiler-builtins/builtins-test/benches/float_cmp.rs +++ b/library/compiler-builtins/builtins-test/benches/float_cmp.rs @@ -177,6 +177,7 @@ float_bench! { ], } +#[cfg(f128_enabled)] float_bench! { name: cmp_f128_gt, sig: (a: f128, b: f128) -> CmpResult, @@ -189,6 +190,7 @@ float_bench! { asm: [] } +#[cfg(f128_enabled)] float_bench! { name: cmp_f128_unord, sig: (a: f128, b: f128) -> CmpResult, diff --git a/library/compiler-builtins/builtins-test/build.rs b/library/compiler-builtins/builtins-test/build.rs index e8f4eb4dd22..5b2dcd12ef8 100644 --- a/library/compiler-builtins/builtins-test/build.rs +++ b/library/compiler-builtins/builtins-test/build.rs @@ -116,5 +116,4 @@ fn main() { } builtins_configure::configure_aliases(&target); - builtins_configure::configure_f16_f128(&target); } diff --git a/library/compiler-builtins/builtins-test/tests/conv.rs b/library/compiler-builtins/builtins-test/tests/conv.rs index 491915d9bb1..7d729364fae 100644 --- a/library/compiler-builtins/builtins-test/tests/conv.rs +++ b/library/compiler-builtins/builtins-test/tests/conv.rs @@ -118,7 +118,7 @@ mod i_to_f { i128, __floattidf; } - #[cfg(not(feature = "no-f16-f128"))] + #[cfg(f128_enabled)] #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] i_to_f! { f128, Quad, not(feature = "no-sys-f128-int-convert"), u32, __floatunsitf; @@ -129,7 +129,7 @@ mod i_to_f { i128, __floattitf; } - #[cfg(not(feature = "no-f16-f128"))] + #[cfg(f128_enabled)] #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] i_to_f! { f128, Quad, not(feature = "no-sys-f128-int-convert"), u32, __floatunsikf; diff --git a/library/compiler-builtins/builtins-test/tests/div_rem.rs b/library/compiler-builtins/builtins-test/tests/div_rem.rs index 5ae653cc90c..e8327f9b4b8 100644 --- a/library/compiler-builtins/builtins-test/tests/div_rem.rs +++ b/library/compiler-builtins/builtins-test/tests/div_rem.rs @@ -147,7 +147,7 @@ mod float_div { f64, __divdf3, Double, all(); } - #[cfg(not(feature = "no-f16-f128"))] + #[cfg(f128_enabled)] #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] float! { f128, __divtf3, Quad, @@ -156,7 +156,7 @@ mod float_div { not(any(feature = "no-sys-f128", all(target_arch = "aarch64", target_os = "linux"))); } - #[cfg(not(feature = "no-f16-f128"))] + #[cfg(f128_enabled)] #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] float! { f128, __divkf3, Quad, not(feature = "no-sys-f128"); diff --git a/library/compiler-builtins/compiler-builtins/build.rs b/library/compiler-builtins/compiler-builtins/build.rs index 018899faf1d..8f51c12b535 100644 --- a/library/compiler-builtins/compiler-builtins/build.rs +++ b/library/compiler-builtins/compiler-builtins/build.rs @@ -2,7 +2,7 @@ mod configure; use std::env; -use configure::{Target, configure_aliases, configure_f16_f128}; +use configure::{Target, configure_aliases}; fn main() { println!("cargo::rerun-if-changed=build.rs"); @@ -12,7 +12,6 @@ fn main() { let cwd = env::current_dir().unwrap(); configure_check_cfg(); - configure_f16_f128(&target); configure_aliases(&target); configure_libm(&target); diff --git a/library/compiler-builtins/compiler-builtins/configure.rs b/library/compiler-builtins/compiler-builtins/configure.rs index d825f35a9aa..a1e45080e71 100644 --- a/library/compiler-builtins/compiler-builtins/configure.rs +++ b/library/compiler-builtins/compiler-builtins/configure.rs @@ -1,6 +1,7 @@ // Configuration that is shared between `compiler_builtins` and `builtins_test`. -use std::env; +use std::process::{Command, Stdio}; +use std::{env, str}; #[derive(Debug)] #[allow(dead_code)] @@ -16,6 +17,8 @@ pub struct Target { pub pointer_width: u8, pub little_endian: bool, pub features: Vec, + pub reliable_f128: bool, + pub reliable_f16: bool, } impl Target { @@ -32,6 +35,19 @@ impl Target { .map(|s| s.to_lowercase().replace("_", "-")) .collect(); + // Query rustc for options that Cargo does not provide env for. The bootstrap hack is used + // to get consistent output regardless of channel (`f16`/`f128` config options are hidden + // on stable otherwise). + let mut cmd = Command::new(env::var("RUSTC").unwrap()); + cmd.args(["--print=cfg", "--target", &triple]) + .env("RUSTC_BOOTSTRAP", "1") + .stderr(Stdio::inherit()); + let out = cmd + .output() + .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}")); + assert!(out.status.success(), "failed to run `{cmd:?}`"); + let rustc_cfg = str::from_utf8(&out.stdout).unwrap(); + Self { triple, triple_split, @@ -51,6 +67,8 @@ impl Target { .split(",") .map(ToOwned::to_owned) .collect(), + reliable_f128: rustc_cfg.lines().any(|l| l == "target_has_reliable_f128"), + reliable_f16: rustc_cfg.lines().any(|l| l == "target_has_reliable_f16"), } } @@ -74,63 +92,24 @@ pub fn configure_aliases(target: &Target) { if target.triple_split[0] == "thumbv6m" || target.triple_split[0] == "thumbv8m.base" { println!("cargo:rustc-cfg=thumb_1") } -} - -/// Configure whether or not `f16` and `f128` support should be enabled. -pub fn configure_f16_f128(target: &Target) { - // Set whether or not `f16` and `f128` are supported at a basic level by LLVM. This only means - // that the backend will not crash when using these types and generates code that can be called - // without crashing (no infinite recursion). This does not mean that the platform doesn't have - // ABI or other bugs. - // - // We do this here rather than in `rust-lang/rust` because configuring via cargo features is - // not straightforward. - // - // Original source of this list: - // - let f16_enabled = match target.arch.as_str() { - // Unsupported - "arm64ec" => false, - // Selection failure - "s390x" => false, - // Infinite recursion - "csky" => false, - "hexagon" => false, - "powerpc" | "powerpc64" => false, - "sparc" | "sparc64" => false, - "wasm32" | "wasm64" => false, - // Most everything else works as of LLVM 19 - _ => true, - }; - let f128_enabled = match target.arch.as_str() { - // Unsupported (libcall is not supported) - "amdgpu" => false, - // Unsupported - "arm64ec" => false, - // FIXME(llvm20): fixed by - "mips64" | "mips64r6" => false, - // Selection failure - "nvptx64" => false, - // Selection failure - "powerpc64" if &target.os == "aix" => false, - // Selection failure - "sparc" => false, - // Most everything else works as of LLVM 19 - _ => true, - }; + /* Not all backends support `f16` and `f128` to the same level on all architectures, so we + * need to disable things if the compiler may crash. See configuration at: + * * https://github.com/rust-lang/rust/blob/c65dccabacdfd6c8a7f7439eba13422fdd89b91e/compiler/rustc_codegen_llvm/src/llvm_util.rs#L367-L432 + * * https://github.com/rust-lang/rustc_codegen_gcc/blob/4b5c44b14166083eef8d71f15f5ea1f53fc976a0/src/lib.rs#L496-L507 + * * https://github.com/rust-lang/rustc_codegen_cranelift/blob/c713ffab3c6e28ab4b4dd4e392330f786ea657ad/src/lib.rs#L196-L226 + */ - // If the feature is set, disable these types. - let disable_both = env::var_os("CARGO_FEATURE_NO_F16_F128").is_some(); + // If the feature is set, disable both of these types. + let no_f16_f128 = target.cargo_features.iter().any(|s| s == "no-f16-f128"); println!("cargo::rustc-check-cfg=cfg(f16_enabled)"); - println!("cargo::rustc-check-cfg=cfg(f128_enabled)"); - - if f16_enabled && !disable_both { + if target.reliable_f16 && !no_f16_f128 { println!("cargo::rustc-cfg=f16_enabled"); } - if f128_enabled && !disable_both { + println!("cargo::rustc-check-cfg=cfg(f128_enabled)"); + if target.reliable_f128 && !no_f16_f128 { println!("cargo::rustc-cfg=f128_enabled"); } } diff --git a/library/compiler-builtins/libm/configure.rs b/library/compiler-builtins/libm/configure.rs index 2a497c7b117..6562ecbe5db 100644 --- a/library/compiler-builtins/libm/configure.rs +++ b/library/compiler-builtins/libm/configure.rs @@ -1,7 +1,8 @@ // Configuration shared with both libm and libm-test -use std::env; use std::path::PathBuf; +use std::process::{Command, Stdio}; +use std::{env, str}; #[allow(dead_code)] pub struct Config { @@ -9,6 +10,7 @@ pub struct Config { pub out_dir: PathBuf, pub opt_level: String, pub cargo_features: Vec, + pub target_triple: String, pub target_arch: String, pub target_env: String, pub target_family: Option, @@ -16,10 +18,13 @@ pub struct Config { pub target_string: String, pub target_vendor: String, pub target_features: Vec, + pub reliable_f128: bool, + pub reliable_f16: bool, } impl Config { pub fn from_env() -> Self { + let target_triple = env::var("TARGET").unwrap(); let target_features = env::var("CARGO_CFG_TARGET_FEATURE") .map(|feats| feats.split(',').map(ToOwned::to_owned).collect()) .unwrap_or_default(); @@ -28,7 +33,21 @@ impl Config { .map(|s| s.to_lowercase().replace("_", "-")) .collect(); + // Query rustc for options that Cargo does not provide env for. The bootstrap hack is used + // to get consistent output regardless of channel (`f16`/`f128` config options are hidden + // on stable otherwise). + let mut cmd = Command::new(env::var("RUSTC").unwrap()); + cmd.args(["--print=cfg", "--target", &target_triple]) + .env("RUSTC_BOOTSTRAP", "1") + .stderr(Stdio::inherit()); + let out = cmd + .output() + .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}")); + assert!(out.status.success(), "failed to run `{cmd:?}`"); + let rustc_cfg = str::from_utf8(&out.stdout).unwrap(); + Self { + target_triple, manifest_dir: PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()), out_dir: PathBuf::from(env::var("OUT_DIR").unwrap()), opt_level: env::var("OPT_LEVEL").unwrap(), @@ -40,6 +59,8 @@ impl Config { target_string: env::var("TARGET").unwrap(), target_vendor: env::var("CARGO_CFG_TARGET_VENDOR").unwrap(), target_features, + reliable_f128: rustc_cfg.lines().any(|l| l == "target_has_reliable_f128"), + reliable_f16: rustc_cfg.lines().any(|l| l == "target_has_reliable_f16"), } } } @@ -128,62 +149,18 @@ fn emit_f16_f128_cfg(cfg: &Config) { return; } - // Set whether or not `f16` and `f128` are supported at a basic level by LLVM. This only means - // that the backend will not crash when using these types and generates code that can be called - // without crashing (no infinite recursion). This does not mean that the platform doesn't have - // ABI or other bugs. - // - // We do this here rather than in `rust-lang/rust` because configuring via cargo features is - // not straightforward. - // - // Original source of this list: - // - let f16_enabled = match cfg.target_arch.as_str() { - // Unsupported - "arm64ec" => false, - // Selection failure - "s390x" => false, - // Infinite recursion - // FIXME(llvm): loongarch fixed by - "csky" => false, - "hexagon" => false, - "loongarch64" => false, - "mips" | "mips64" | "mips32r6" | "mips64r6" => false, - "powerpc" | "powerpc64" => false, - "sparc" | "sparc64" => false, - "wasm32" | "wasm64" => false, - // Most everything else works as of LLVM 19 - _ => true, - }; - - let f128_enabled = match cfg.target_arch.as_str() { - // Unsupported (libcall is not supported) - "amdgpu" => false, - // Unsupported - "arm64ec" => false, - // Selection failure - "mips64" | "mips64r6" => false, - // Selection failure - "nvptx64" => false, - // Selection failure - "powerpc64" if &cfg.target_os == "aix" => false, - // Selection failure - "sparc" => false, - // Most everything else works as of LLVM 19 - _ => true, - }; - - // If the feature is set, disable these types. - let disable_both = env::var_os("CARGO_FEATURE_NO_F16_F128").is_some(); + /* See the compiler-builtins configure file for info about the meaning of these options */ - println!("cargo:rustc-check-cfg=cfg(f16_enabled)"); - println!("cargo:rustc-check-cfg=cfg(f128_enabled)"); + // If the feature is set, disable both of these types. + let no_f16_f128 = cfg.cargo_features.iter().any(|s| s == "no-f16-f128"); - if f16_enabled && !disable_both { + println!("cargo:rustc-check-cfg=cfg(f16_enabled)"); + if cfg.reliable_f16 && !no_f16_f128 { println!("cargo:rustc-cfg=f16_enabled"); } - if f128_enabled && !disable_both { + println!("cargo:rustc-check-cfg=cfg(f128_enabled)"); + if cfg.reliable_f128 && !no_f16_f128 { println!("cargo:rustc-cfg=f128_enabled"); } } -- cgit 1.4.1-3-g733a5 From 8bd580b80e3c31f9c2d4115768a3ac0b71372587 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 2 Jul 2025 00:57:59 +0200 Subject: Add `track_caller` attributes to trace origin of Clippy lints This allows the use of `-Z track-diagnostics` to see the origin of Clippy lints emission, as is already the case for lints coming from rustc. --- clippy_utils/src/diagnostics.rs | 7 +++++++ tests/ui/track-diagnostics-clippy.rs | 22 ++++++++++++++++++++++ tests/ui/track-diagnostics-clippy.stderr | 29 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 tests/ui/track-diagnostics-clippy.rs create mode 100644 tests/ui/track-diagnostics-clippy.stderr diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index dc240dd067b..8453165818b 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -98,6 +98,7 @@ fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) { /// 17 | std::mem::forget(seven); /// | ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` +#[track_caller] pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into, msg: impl Into) { #[expect(clippy::disallowed_methods)] cx.span_lint(lint, sp, |diag| { @@ -143,6 +144,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( cx: &T, lint: &'static Lint, @@ -203,6 +205,7 @@ pub fn span_lint_and_help( /// 10 | forget(&SomeStruct); /// | ^^^^^^^^^^^ /// ``` +#[track_caller] pub fn span_lint_and_note( cx: &T, lint: &'static Lint, @@ -244,6 +247,7 @@ pub fn span_lint_and_note( /// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. +#[track_caller] pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: M, f: F) where C: LintContext, @@ -286,6 +290,7 @@ where /// Instead, use this function and also pass the `HirId` of ``, which will let /// the compiler check lint level attributes at the place of the expression and /// the `#[allow]` will work. +#[track_caller] pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: impl Into) { #[expect(clippy::disallowed_methods)] cx.tcx.node_span_lint(lint, hir_id, sp, |diag| { @@ -321,6 +326,7 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s /// Instead, use this function and also pass the `HirId` of ``, which will let /// the compiler check lint level attributes at the place of the expression and /// the `#[allow]` will work. +#[track_caller] pub fn span_lint_hir_and_then( cx: &LateContext<'_>, lint: &'static Lint, @@ -374,6 +380,7 @@ pub fn span_lint_hir_and_then( /// = note: `-D fold-any` implied by `-D warnings` /// ``` #[cfg_attr(not(debug_assertions), expect(clippy::collapsible_span_lint_calls))] +#[track_caller] pub fn span_lint_and_sugg( cx: &T, lint: &'static Lint, diff --git a/tests/ui/track-diagnostics-clippy.rs b/tests/ui/track-diagnostics-clippy.rs new file mode 100644 index 00000000000..2e67fb65efc --- /dev/null +++ b/tests/ui/track-diagnostics-clippy.rs @@ -0,0 +1,22 @@ +//@compile-flags: -Z track-diagnostics +//@no-rustfix + +// Normalize the emitted location so this doesn't need +// updating everytime someone adds or removes a line. +//@normalize-stderr-test: ".rs:\d+:\d+" -> ".rs:LL:CC" + +#![warn(clippy::let_and_return, clippy::unnecessary_cast)] + +fn main() { + // Check the provenance of a lint sent through `LintContext::span_lint()` + let a = 3u32; + let b = a as u32; + //~^ unnecessary_cast + + // Check the provenance of a lint sent through `TyCtxt::node_span_lint()` + let c = { + let d = 42; + d + //~^ let_and_return + }; +} diff --git a/tests/ui/track-diagnostics-clippy.stderr b/tests/ui/track-diagnostics-clippy.stderr new file mode 100644 index 00000000000..f3aca685417 --- /dev/null +++ b/tests/ui/track-diagnostics-clippy.stderr @@ -0,0 +1,29 @@ +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> tests/ui/track-diagnostics-clippy.rs:LL:CC + | +LL | let b = a as u32; + | ^^^^^^^^ help: try: `a` +-Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs:LL:CC + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` + +error: returning the result of a `let` binding from a block + --> tests/ui/track-diagnostics-clippy.rs:LL:CC + | +LL | let d = 42; + | ----------- unnecessary `let` binding +LL | d + | ^ +-Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/returns.rs:LL:CC + | + = note: `-D clippy::let-and-return` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]` +help: return the expression directly + | +LL ~ +LL ~ 42 + | + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From fc076315d298c9cdef6baf770d9492f56e920c92 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 29 May 2025 16:38:06 +0200 Subject: Add Triagebot's `concern` capability to the Clippy repository This is a newly added triagebot capability, which allows registering and then resolving concerns with an issue or a pull request. The concerns are gathered by Triagebot in the issue/PR summary. Concerns are different from notes: when a concern is resolved, it is striked through in the issue/PR summary, and a link to the comment resolving it is also added, whereas a note can only be removed and then disappears from the summary. The `has-concerns` label, which must be created at the time of merging this change, will be automatically set on issues/PRs that have unresolved concerns, and cleared when all concerns are resolved. --- triagebot.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 16557a4bebb..a20e3ec2894 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -40,6 +40,10 @@ reviewed_label = "S-waiting-on-author" [autolabel."S-waiting-on-review"] new_pr = true +[concern] +# These labels are set when there are unresolved concerns, removed otherwise +labels = ["S-waiting-on-concerns"] + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ -- cgit 1.4.1-3-g733a5 From 8bc4add5796fa81ce53c1df6509867fb49a7582d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 3 Jul 2025 12:50:03 +0200 Subject: Do not lint intrinsics as empty loops --- clippy_lints/src/loops/empty_loop.rs | 15 +++++++++++++-- tests/ui/empty_loop_intrinsic.rs | 13 +++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/ui/empty_loop_intrinsic.rs diff --git a/clippy_lints/src/loops/empty_loop.rs b/clippy_lints/src/loops/empty_loop.rs index 823cf0f4322..e809987d75a 100644 --- a/clippy_lints/src/loops/empty_loop.rs +++ b/clippy_lints/src/loops/empty_loop.rs @@ -1,11 +1,22 @@ use super::EMPTY_LOOP; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_in_panic_handler, is_no_std_crate}; +use clippy_utils::{is_in_panic_handler, is_no_std_crate, sym}; -use rustc_hir::{Block, Expr}; +use rustc_hir::{Block, Expr, ItemKind, Node}; use rustc_lint::LateContext; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) { + let parent_hir_id = cx.tcx.parent_hir_id(expr.hir_id); + if let Node::Item(parent_node) = cx.tcx.hir_node(parent_hir_id) + && matches!(parent_node.kind, ItemKind::Fn { .. }) + && let attrs = cx.tcx.hir_attrs(parent_hir_id) + && attrs.iter().any(|attr| attr.has_name(sym::rustc_intrinsic)) + { + // Intrinsic functions are expanded into an empty loop when lowering the AST + // to simplify the job of later passes which might expect any function to have a body. + return; + } + if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx) { diff --git a/tests/ui/empty_loop_intrinsic.rs b/tests/ui/empty_loop_intrinsic.rs new file mode 100644 index 00000000000..a550e560965 --- /dev/null +++ b/tests/ui/empty_loop_intrinsic.rs @@ -0,0 +1,13 @@ +//@check-pass + +#![warn(clippy::empty_loop)] +#![feature(intrinsics)] +#![feature(rustc_attrs)] + +// From issue #15200 +#[rustc_intrinsic] +#[rustc_nounwind] +/// # Safety +pub const unsafe fn simd_insert(x: T, idx: u32, val: U) -> T; + +fn main() {} -- cgit 1.4.1-3-g733a5 From 7060377bfe52e08fe1f52b45dd67f665d2944c50 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Mon, 2 Jun 2025 23:40:01 -0600 Subject: refactor: Make -Ztrack-diagnostics emit like a note --- tests/ui/track-diagnostics-clippy.stderr | 4 ++-- tests/ui/track-diagnostics.rs | 1 + tests/ui/track-diagnostics.stderr | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/ui/track-diagnostics-clippy.stderr b/tests/ui/track-diagnostics-clippy.stderr index f3aca685417..9d6538112bf 100644 --- a/tests/ui/track-diagnostics-clippy.stderr +++ b/tests/ui/track-diagnostics-clippy.stderr @@ -3,8 +3,8 @@ error: casting to the same type is unnecessary (`u32` -> `u32`) | LL | let b = a as u32; | ^^^^^^^^ help: try: `a` --Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs:LL:CC | + = note: -Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs:LL:CC = note: `-D clippy::unnecessary-cast` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` @@ -15,8 +15,8 @@ LL | let d = 42; | ----------- unnecessary `let` binding LL | d | ^ --Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/returns.rs:LL:CC | + = note: -Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/returns.rs:LL:CC = note: `-D clippy::let-and-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]` help: return the expression directly diff --git a/tests/ui/track-diagnostics.rs b/tests/ui/track-diagnostics.rs index 723ea23e9a6..0fbde867390 100644 --- a/tests/ui/track-diagnostics.rs +++ b/tests/ui/track-diagnostics.rs @@ -8,5 +8,6 @@ struct A; struct B; const S: A = B; //~^ ERROR: mismatched types +//~| NOTE: created at fn main() {} diff --git a/tests/ui/track-diagnostics.stderr b/tests/ui/track-diagnostics.stderr index 83451fb658d..45262ba618f 100644 --- a/tests/ui/track-diagnostics.stderr +++ b/tests/ui/track-diagnostics.stderr @@ -3,7 +3,8 @@ error[E0308]: mismatched types | LL | const S: A = B; | ^ expected `A`, found `B` --Ztrack-diagnostics: created at compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs:LL:CC + | + = note: -Ztrack-diagnostics: created at compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs:LL:CC error: aborting due to 1 previous error -- cgit 1.4.1-3-g733a5 From a4f9d7a3f3de0096780e86b7a82a8474fc14fc59 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 2 Jul 2024 09:38:49 +0000 Subject: Replace kw_span by full span. --- clippy_utils/src/ast_utils/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index e65914b9b5e..e6396987cc6 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -886,13 +886,13 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool { ( Const { ty: lt, - kw_span: _, default: ld, + span: _, }, Const { ty: rt, - kw_span: _, default: rd, + span: _, }, ) => eq_ty(lt, rt) && both(ld.as_ref(), rd.as_ref(), eq_anon_const), _ => false, -- cgit 1.4.1-3-g733a5 From 25ab022f2951318e8ffdec2d8566c3a7d164e00d Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sun, 29 Jun 2025 15:37:28 +0800 Subject: move `tests/ui/resolve/suggest*` to `tests/ui/resolve/suggestions/` Signed-off-by: xizheyin --- .../auxiliary/suggest-constructor-cycle-error.rs | 12 -- tests/ui/resolve/suggest-builder-fn.rs | 64 -------- tests/ui/resolve/suggest-builder-fn.stderr | 51 ------ .../ui/resolve/suggest-constructor-cycle-error.rs | 10 -- .../resolve/suggest-constructor-cycle-error.stderr | 15 -- .../suggest-import-without-clobbering-attrs.fixed | 16 -- .../suggest-import-without-clobbering-attrs.rs | 15 -- .../suggest-import-without-clobbering-attrs.stderr | 14 -- tests/ui/resolve/suggest-path-for-tuple-struct.rs | 26 --- .../resolve/suggest-path-for-tuple-struct.stderr | 27 ---- .../suggest-path-instead-of-mod-dot-item.rs | 111 ------------- .../suggest-path-instead-of-mod-dot-item.stderr | 178 --------------------- .../auxiliary/suggest-constructor-cycle-error.rs | 12 ++ tests/ui/resolve/suggestions/suggest-builder-fn.rs | 64 ++++++++ .../resolve/suggestions/suggest-builder-fn.stderr | 51 ++++++ .../suggestions/suggest-constructor-cycle-error.rs | 10 ++ .../suggest-constructor-cycle-error.stderr | 15 ++ .../suggest-import-without-clobbering-attrs.fixed | 16 ++ .../suggest-import-without-clobbering-attrs.rs | 15 ++ .../suggest-import-without-clobbering-attrs.stderr | 14 ++ .../suggestions/suggest-path-for-tuple-struct.rs | 26 +++ .../suggest-path-for-tuple-struct.stderr | 27 ++++ .../suggest-path-instead-of-mod-dot-item.rs | 111 +++++++++++++ .../suggest-path-instead-of-mod-dot-item.stderr | 178 +++++++++++++++++++++ 24 files changed, 539 insertions(+), 539 deletions(-) delete mode 100644 tests/ui/resolve/auxiliary/suggest-constructor-cycle-error.rs delete mode 100644 tests/ui/resolve/suggest-builder-fn.rs delete mode 100644 tests/ui/resolve/suggest-builder-fn.stderr delete mode 100644 tests/ui/resolve/suggest-constructor-cycle-error.rs delete mode 100644 tests/ui/resolve/suggest-constructor-cycle-error.stderr delete mode 100644 tests/ui/resolve/suggest-import-without-clobbering-attrs.fixed delete mode 100644 tests/ui/resolve/suggest-import-without-clobbering-attrs.rs delete mode 100644 tests/ui/resolve/suggest-import-without-clobbering-attrs.stderr delete mode 100644 tests/ui/resolve/suggest-path-for-tuple-struct.rs delete mode 100644 tests/ui/resolve/suggest-path-for-tuple-struct.stderr delete mode 100644 tests/ui/resolve/suggest-path-instead-of-mod-dot-item.rs delete mode 100644 tests/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr create mode 100644 tests/ui/resolve/suggestions/auxiliary/suggest-constructor-cycle-error.rs create mode 100644 tests/ui/resolve/suggestions/suggest-builder-fn.rs create mode 100644 tests/ui/resolve/suggestions/suggest-builder-fn.stderr create mode 100644 tests/ui/resolve/suggestions/suggest-constructor-cycle-error.rs create mode 100644 tests/ui/resolve/suggestions/suggest-constructor-cycle-error.stderr create mode 100644 tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.fixed create mode 100644 tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.rs create mode 100644 tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.stderr create mode 100644 tests/ui/resolve/suggestions/suggest-path-for-tuple-struct.rs create mode 100644 tests/ui/resolve/suggestions/suggest-path-for-tuple-struct.stderr create mode 100644 tests/ui/resolve/suggestions/suggest-path-instead-of-mod-dot-item.rs create mode 100644 tests/ui/resolve/suggestions/suggest-path-instead-of-mod-dot-item.stderr diff --git a/tests/ui/resolve/auxiliary/suggest-constructor-cycle-error.rs b/tests/ui/resolve/auxiliary/suggest-constructor-cycle-error.rs deleted file mode 100644 index 8de68c38bc3..00000000000 --- a/tests/ui/resolve/auxiliary/suggest-constructor-cycle-error.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod m { - pub struct Uuid(()); - - impl Uuid { - pub fn encode_buffer() -> [u8; LENGTH] { - [] - } - } - const LENGTH: usize = 0; -} - -pub use m::Uuid; diff --git a/tests/ui/resolve/suggest-builder-fn.rs b/tests/ui/resolve/suggest-builder-fn.rs deleted file mode 100644 index 959675ef2c9..00000000000 --- a/tests/ui/resolve/suggest-builder-fn.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Tests that we suggest the right alternatives when -// a builder method cannot be resolved - -use std::net::TcpStream; - -trait SomeTrait {} - -struct Foo { - v : T -} - -impl Foo { - // Should not be suggested if constraint on the impl not met - fn new() -> Self { - Self { v: T::default() } - } -} - -struct Bar; - -impl Bar { - // Should be suggested - fn build() -> Self { - Self {} - } - - // Method with self can't be a builder. - // Should not be suggested - fn build_with_self(&self) -> Self { - Self {} - } -} - -mod SomeMod { - use crate::Bar; - - impl Bar { - // Public method. Should be suggested - pub fn build_public() -> Self { - Self {} - } - - // Private method. Should not be suggested - fn build_private() -> Self { - Self {} - } - } -} - -fn main() { - // `new` not found on `TcpStream` and `connect` should be suggested - let _stream = TcpStream::new(); - //~^ ERROR no function or associated item named `new` found - - // Although `new` is found on `>` it should not be - // suggested because `u8` does not meet the `T: SomeTrait` constraint - let _foo = Foo::::new(); - //~^ ERROR the function or associated item `new` exists for struct `Foo`, but its trait bounds were not satisfied - - // Should suggest only `::build()` and `SomeMod::::build_public()`. - // Other methods should not suggested because they are private or are not a builder - let _bar = Bar::new(); - //~^ ERROR no function or associated item named `new` found -} diff --git a/tests/ui/resolve/suggest-builder-fn.stderr b/tests/ui/resolve/suggest-builder-fn.stderr deleted file mode 100644 index 9c5eed35ccf..00000000000 --- a/tests/ui/resolve/suggest-builder-fn.stderr +++ /dev/null @@ -1,51 +0,0 @@ -error[E0599]: no function or associated item named `new` found for struct `TcpStream` in the current scope - --> $DIR/suggest-builder-fn.rs:52:29 - | -LL | let _stream = TcpStream::new(); - | ^^^ function or associated item not found in `TcpStream` - | -note: if you're trying to build a new `TcpStream` consider using one of the following associated functions: - TcpStream::connect - TcpStream::connect_timeout - --> $SRC_DIR/std/src/net/tcp.rs:LL:COL - -error[E0599]: the function or associated item `new` exists for struct `Foo`, but its trait bounds were not satisfied - --> $DIR/suggest-builder-fn.rs:57:27 - | -LL | struct Foo { - | ------------- function or associated item `new` not found for this struct -... -LL | let _foo = Foo::::new(); - | ^^^ function or associated item cannot be called on `Foo` due to unsatisfied trait bounds - | -note: trait bound `u8: SomeTrait` was not satisfied - --> $DIR/suggest-builder-fn.rs:12:9 - | -LL | impl Foo { - | ^^^^^^^^^ ------ - | | - | unsatisfied trait bound introduced here - -error[E0599]: no function or associated item named `new` found for struct `Bar` in the current scope - --> $DIR/suggest-builder-fn.rs:62:21 - | -LL | struct Bar; - | ---------- function or associated item `new` not found for this struct -... -LL | let _bar = Bar::new(); - | ^^^ function or associated item not found in `Bar` - | -note: if you're trying to build a new `Bar` consider using one of the following associated functions: - Bar::build - SomeMod::::build_public - --> $DIR/suggest-builder-fn.rs:23:5 - | -LL | fn build() -> Self { - | ^^^^^^^^^^^^^^^^^^ -... -LL | pub fn build_public() -> Self { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/resolve/suggest-constructor-cycle-error.rs b/tests/ui/resolve/suggest-constructor-cycle-error.rs deleted file mode 100644 index c23d6788eef..00000000000 --- a/tests/ui/resolve/suggest-constructor-cycle-error.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ aux-build:suggest-constructor-cycle-error.rs - -// Regression test for https://github.com/rust-lang/rust/issues/119625 - -extern crate suggest_constructor_cycle_error as a; - -const CONST_NAME: a::Uuid = a::Uuid(()); -//~^ ERROR: cannot initialize a tuple struct which contains private fields [E0423] - -fn main() {} diff --git a/tests/ui/resolve/suggest-constructor-cycle-error.stderr b/tests/ui/resolve/suggest-constructor-cycle-error.stderr deleted file mode 100644 index c6ec2465a43..00000000000 --- a/tests/ui/resolve/suggest-constructor-cycle-error.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0423]: cannot initialize a tuple struct which contains private fields - --> $DIR/suggest-constructor-cycle-error.rs:7:29 - | -LL | const CONST_NAME: a::Uuid = a::Uuid(()); - | ^^^^^^^ - | -note: constructor is not visible here due to private fields - --> $DIR/auxiliary/suggest-constructor-cycle-error.rs:2:21 - | -LL | pub struct Uuid(()); - | ^^ private field - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/resolve/suggest-import-without-clobbering-attrs.fixed b/tests/ui/resolve/suggest-import-without-clobbering-attrs.fixed deleted file mode 100644 index 607c9af4927..00000000000 --- a/tests/ui/resolve/suggest-import-without-clobbering-attrs.fixed +++ /dev/null @@ -1,16 +0,0 @@ -//@ run-rustfix -//@ compile-flags: -Aunused - -use y::z; -#[cfg(all())] -use y::Whatever; - -mod y { - pub(crate) fn z() {} - pub(crate) struct Whatever; -} - -fn main() { - z(); - //~^ ERROR cannot find function `z` in this scope -} diff --git a/tests/ui/resolve/suggest-import-without-clobbering-attrs.rs b/tests/ui/resolve/suggest-import-without-clobbering-attrs.rs deleted file mode 100644 index 6cc53fb1086..00000000000 --- a/tests/ui/resolve/suggest-import-without-clobbering-attrs.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix -//@ compile-flags: -Aunused - -#[cfg(all())] -use y::Whatever; - -mod y { - pub(crate) fn z() {} - pub(crate) struct Whatever; -} - -fn main() { - z(); - //~^ ERROR cannot find function `z` in this scope -} diff --git a/tests/ui/resolve/suggest-import-without-clobbering-attrs.stderr b/tests/ui/resolve/suggest-import-without-clobbering-attrs.stderr deleted file mode 100644 index de65d695dd2..00000000000 --- a/tests/ui/resolve/suggest-import-without-clobbering-attrs.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0425]: cannot find function `z` in this scope - --> $DIR/suggest-import-without-clobbering-attrs.rs:13:5 - | -LL | z(); - | ^ not found in this scope - | -help: consider importing this function - | -LL + use y::z; - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/resolve/suggest-path-for-tuple-struct.rs b/tests/ui/resolve/suggest-path-for-tuple-struct.rs deleted file mode 100644 index c8bc3e79fe2..00000000000 --- a/tests/ui/resolve/suggest-path-for-tuple-struct.rs +++ /dev/null @@ -1,26 +0,0 @@ -mod module { - pub struct SomeTupleStruct(u8); - pub struct SomeRegularStruct { - foo: u8 - } - - impl SomeTupleStruct { - pub fn new() -> Self { - Self(0) - } - } - impl SomeRegularStruct { - pub fn new() -> Self { - Self { foo: 0 } - } - } -} - -use module::{SomeTupleStruct, SomeRegularStruct}; - -fn main() { - let _ = SomeTupleStruct.new(); - //~^ ERROR expected value, found struct `SomeTupleStruct` - let _ = SomeRegularStruct.new(); - //~^ ERROR expected value, found struct `SomeRegularStruct` -} diff --git a/tests/ui/resolve/suggest-path-for-tuple-struct.stderr b/tests/ui/resolve/suggest-path-for-tuple-struct.stderr deleted file mode 100644 index 68a5b550978..00000000000 --- a/tests/ui/resolve/suggest-path-for-tuple-struct.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0423]: expected value, found struct `SomeTupleStruct` - --> $DIR/suggest-path-for-tuple-struct.rs:22:13 - | -LL | let _ = SomeTupleStruct.new(); - | ^^^^^^^^^^^^^^^ - | -help: use the path separator to refer to an item - | -LL - let _ = SomeTupleStruct.new(); -LL + let _ = SomeTupleStruct::new(); - | - -error[E0423]: expected value, found struct `SomeRegularStruct` - --> $DIR/suggest-path-for-tuple-struct.rs:24:13 - | -LL | let _ = SomeRegularStruct.new(); - | ^^^^^^^^^^^^^^^^^ - | -help: use the path separator to refer to an item - | -LL - let _ = SomeRegularStruct.new(); -LL + let _ = SomeRegularStruct::new(); - | - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.rs b/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.rs deleted file mode 100644 index d5d6b13d62c..00000000000 --- a/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Beginners write `mod.item` when they should write `mod::item`. -// This tests that we suggest the latter when we encounter the former. - -pub mod a { - pub const I: i32 = 1; - - pub fn f() -> i32 { 2 } - - pub mod b { - pub const J: i32 = 3; - - pub fn g() -> i32 { 4 } - } -} - -fn h1() -> i32 { - a.I - //~^ ERROR expected value, found module `a` - //~| HELP use the path separator -} - -fn h2() -> i32 { - a.g() - //~^ ERROR expected value, found module `a` - //~| HELP use the path separator -} - -fn h3() -> i32 { - a.b.J - //~^ ERROR expected value, found module `a` - //~| HELP use the path separator -} - -fn h4() -> i32 { - a::b.J - //~^ ERROR expected value, found module `a::b` - //~| HELP a constant with a similar name exists - //~| HELP use the path separator -} - -fn h5() { - a.b.f(); - //~^ ERROR expected value, found module `a` - //~| HELP use the path separator - let v = Vec::new(); - v.push(a::b); - //~^ ERROR expected value, found module `a::b` - //~| HELP a constant with a similar name exists -} - -fn h6() -> i32 { - a::b.f() - //~^ ERROR expected value, found module `a::b` - //~| HELP a constant with a similar name exists - //~| HELP use the path separator -} - -fn h7() { - a::b - //~^ ERROR expected value, found module `a::b` - //~| HELP a constant with a similar name exists -} - -fn h8() -> i32 { - a::b() - //~^ ERROR expected function, found module `a::b` - //~| HELP a constant with a similar name exists -} - -macro_rules! module { - () => { - a - //~^ ERROR expected value, found module `a` - //~| ERROR expected value, found module `a` - }; -} - -macro_rules! create { - (method) => { - a.f() - //~^ ERROR expected value, found module `a` - //~| HELP use the path separator - }; - (field) => { - a.f - //~^ ERROR expected value, found module `a` - //~| HELP use the path separator - }; -} - -fn h9() { - // - // Note that if the receiver is a macro call, we do not want to suggest to replace - // `.` with `::` as that would be a syntax error. - // Since the receiver is a module and not a type, we cannot suggest to surround - // it with angle brackets. - // - - module!().g::<()>(); // no `help` here! - - module!().g; // no `help` here! - - // - // Ensure that the suggestion is shown for expressions inside of macro definitions. - // - - let _ = create!(method); - let _ = create!(field); -} - -fn main() {} diff --git a/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr b/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr deleted file mode 100644 index 5db943cd10d..00000000000 --- a/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr +++ /dev/null @@ -1,178 +0,0 @@ -error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:17:5 - | -LL | a.I - | ^ - | -help: use the path separator to refer to an item - | -LL - a.I -LL + a::I - | - -error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:23:5 - | -LL | a.g() - | ^ - | -help: use the path separator to refer to an item - | -LL - a.g() -LL + a::g() - | - -error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:29:5 - | -LL | a.b.J - | ^ - | -help: use the path separator to refer to an item - | -LL - a.b.J -LL + a::b.J - | - -error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:35:5 - | -LL | pub const I: i32 = 1; - | --------------------- similarly named constant `I` defined here -... -LL | a::b.J - | ^^^^ - | -help: use the path separator to refer to an item - | -LL - a::b.J -LL + a::b::J - | -help: a constant with a similar name exists - | -LL - a::b.J -LL + a::I.J - | - -error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:42:5 - | -LL | a.b.f(); - | ^ - | -help: use the path separator to refer to an item - | -LL - a.b.f(); -LL + a::b.f(); - | - -error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:46:12 - | -LL | pub const I: i32 = 1; - | --------------------- similarly named constant `I` defined here -... -LL | v.push(a::b); - | ^^^- - | | - | help: a constant with a similar name exists: `I` - -error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:52:5 - | -LL | pub const I: i32 = 1; - | --------------------- similarly named constant `I` defined here -... -LL | a::b.f() - | ^^^^ - | -help: use the path separator to refer to an item - | -LL - a::b.f() -LL + a::b::f() - | -help: a constant with a similar name exists - | -LL - a::b.f() -LL + a::I.f() - | - -error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:59:5 - | -LL | pub const I: i32 = 1; - | --------------------- similarly named constant `I` defined here -... -LL | a::b - | ^^^- - | | - | help: a constant with a similar name exists: `I` - -error[E0423]: expected function, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:65:5 - | -LL | pub const I: i32 = 1; - | --------------------- similarly named constant `I` defined here -... -LL | a::b() - | ^^^- - | | - | help: a constant with a similar name exists: `I` - -error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:72:9 - | -LL | a - | ^ not a value -... -LL | module!().g::<()>(); // no `help` here! - | --------- in this macro invocation - | - = note: this error originates in the macro `module` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:72:9 - | -LL | a - | ^ not a value -... -LL | module!().g; // no `help` here! - | --------- in this macro invocation - | - = note: this error originates in the macro `module` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:80:9 - | -LL | a.f() - | ^ -... -LL | let _ = create!(method); - | --------------- in this macro invocation - | - = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) -help: use the path separator to refer to an item - | -LL - a.f() -LL + a::f() - | - -error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:85:9 - | -LL | a.f - | ^ -... -LL | let _ = create!(field); - | -------------- in this macro invocation - | - = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) -help: use the path separator to refer to an item - | -LL - a.f -LL + a::f - | - -error: aborting due to 13 previous errors - -For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/resolve/suggestions/auxiliary/suggest-constructor-cycle-error.rs b/tests/ui/resolve/suggestions/auxiliary/suggest-constructor-cycle-error.rs new file mode 100644 index 00000000000..8de68c38bc3 --- /dev/null +++ b/tests/ui/resolve/suggestions/auxiliary/suggest-constructor-cycle-error.rs @@ -0,0 +1,12 @@ +mod m { + pub struct Uuid(()); + + impl Uuid { + pub fn encode_buffer() -> [u8; LENGTH] { + [] + } + } + const LENGTH: usize = 0; +} + +pub use m::Uuid; diff --git a/tests/ui/resolve/suggestions/suggest-builder-fn.rs b/tests/ui/resolve/suggestions/suggest-builder-fn.rs new file mode 100644 index 00000000000..959675ef2c9 --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-builder-fn.rs @@ -0,0 +1,64 @@ +// Tests that we suggest the right alternatives when +// a builder method cannot be resolved + +use std::net::TcpStream; + +trait SomeTrait {} + +struct Foo { + v : T +} + +impl Foo { + // Should not be suggested if constraint on the impl not met + fn new() -> Self { + Self { v: T::default() } + } +} + +struct Bar; + +impl Bar { + // Should be suggested + fn build() -> Self { + Self {} + } + + // Method with self can't be a builder. + // Should not be suggested + fn build_with_self(&self) -> Self { + Self {} + } +} + +mod SomeMod { + use crate::Bar; + + impl Bar { + // Public method. Should be suggested + pub fn build_public() -> Self { + Self {} + } + + // Private method. Should not be suggested + fn build_private() -> Self { + Self {} + } + } +} + +fn main() { + // `new` not found on `TcpStream` and `connect` should be suggested + let _stream = TcpStream::new(); + //~^ ERROR no function or associated item named `new` found + + // Although `new` is found on `>` it should not be + // suggested because `u8` does not meet the `T: SomeTrait` constraint + let _foo = Foo::::new(); + //~^ ERROR the function or associated item `new` exists for struct `Foo`, but its trait bounds were not satisfied + + // Should suggest only `::build()` and `SomeMod::::build_public()`. + // Other methods should not suggested because they are private or are not a builder + let _bar = Bar::new(); + //~^ ERROR no function or associated item named `new` found +} diff --git a/tests/ui/resolve/suggestions/suggest-builder-fn.stderr b/tests/ui/resolve/suggestions/suggest-builder-fn.stderr new file mode 100644 index 00000000000..9c5eed35ccf --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-builder-fn.stderr @@ -0,0 +1,51 @@ +error[E0599]: no function or associated item named `new` found for struct `TcpStream` in the current scope + --> $DIR/suggest-builder-fn.rs:52:29 + | +LL | let _stream = TcpStream::new(); + | ^^^ function or associated item not found in `TcpStream` + | +note: if you're trying to build a new `TcpStream` consider using one of the following associated functions: + TcpStream::connect + TcpStream::connect_timeout + --> $SRC_DIR/std/src/net/tcp.rs:LL:COL + +error[E0599]: the function or associated item `new` exists for struct `Foo`, but its trait bounds were not satisfied + --> $DIR/suggest-builder-fn.rs:57:27 + | +LL | struct Foo { + | ------------- function or associated item `new` not found for this struct +... +LL | let _foo = Foo::::new(); + | ^^^ function or associated item cannot be called on `Foo` due to unsatisfied trait bounds + | +note: trait bound `u8: SomeTrait` was not satisfied + --> $DIR/suggest-builder-fn.rs:12:9 + | +LL | impl Foo { + | ^^^^^^^^^ ------ + | | + | unsatisfied trait bound introduced here + +error[E0599]: no function or associated item named `new` found for struct `Bar` in the current scope + --> $DIR/suggest-builder-fn.rs:62:21 + | +LL | struct Bar; + | ---------- function or associated item `new` not found for this struct +... +LL | let _bar = Bar::new(); + | ^^^ function or associated item not found in `Bar` + | +note: if you're trying to build a new `Bar` consider using one of the following associated functions: + Bar::build + SomeMod::::build_public + --> $DIR/suggest-builder-fn.rs:23:5 + | +LL | fn build() -> Self { + | ^^^^^^^^^^^^^^^^^^ +... +LL | pub fn build_public() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/resolve/suggestions/suggest-constructor-cycle-error.rs b/tests/ui/resolve/suggestions/suggest-constructor-cycle-error.rs new file mode 100644 index 00000000000..c23d6788eef --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-constructor-cycle-error.rs @@ -0,0 +1,10 @@ +//@ aux-build:suggest-constructor-cycle-error.rs + +// Regression test for https://github.com/rust-lang/rust/issues/119625 + +extern crate suggest_constructor_cycle_error as a; + +const CONST_NAME: a::Uuid = a::Uuid(()); +//~^ ERROR: cannot initialize a tuple struct which contains private fields [E0423] + +fn main() {} diff --git a/tests/ui/resolve/suggestions/suggest-constructor-cycle-error.stderr b/tests/ui/resolve/suggestions/suggest-constructor-cycle-error.stderr new file mode 100644 index 00000000000..c6ec2465a43 --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-constructor-cycle-error.stderr @@ -0,0 +1,15 @@ +error[E0423]: cannot initialize a tuple struct which contains private fields + --> $DIR/suggest-constructor-cycle-error.rs:7:29 + | +LL | const CONST_NAME: a::Uuid = a::Uuid(()); + | ^^^^^^^ + | +note: constructor is not visible here due to private fields + --> $DIR/auxiliary/suggest-constructor-cycle-error.rs:2:21 + | +LL | pub struct Uuid(()); + | ^^ private field + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.fixed b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.fixed new file mode 100644 index 00000000000..607c9af4927 --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.fixed @@ -0,0 +1,16 @@ +//@ run-rustfix +//@ compile-flags: -Aunused + +use y::z; +#[cfg(all())] +use y::Whatever; + +mod y { + pub(crate) fn z() {} + pub(crate) struct Whatever; +} + +fn main() { + z(); + //~^ ERROR cannot find function `z` in this scope +} diff --git a/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.rs b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.rs new file mode 100644 index 00000000000..6cc53fb1086 --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.rs @@ -0,0 +1,15 @@ +//@ run-rustfix +//@ compile-flags: -Aunused + +#[cfg(all())] +use y::Whatever; + +mod y { + pub(crate) fn z() {} + pub(crate) struct Whatever; +} + +fn main() { + z(); + //~^ ERROR cannot find function `z` in this scope +} diff --git a/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.stderr b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.stderr new file mode 100644 index 00000000000..de65d695dd2 --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-import-without-clobbering-attrs.stderr @@ -0,0 +1,14 @@ +error[E0425]: cannot find function `z` in this scope + --> $DIR/suggest-import-without-clobbering-attrs.rs:13:5 + | +LL | z(); + | ^ not found in this scope + | +help: consider importing this function + | +LL + use y::z; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/resolve/suggestions/suggest-path-for-tuple-struct.rs b/tests/ui/resolve/suggestions/suggest-path-for-tuple-struct.rs new file mode 100644 index 00000000000..c8bc3e79fe2 --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-path-for-tuple-struct.rs @@ -0,0 +1,26 @@ +mod module { + pub struct SomeTupleStruct(u8); + pub struct SomeRegularStruct { + foo: u8 + } + + impl SomeTupleStruct { + pub fn new() -> Self { + Self(0) + } + } + impl SomeRegularStruct { + pub fn new() -> Self { + Self { foo: 0 } + } + } +} + +use module::{SomeTupleStruct, SomeRegularStruct}; + +fn main() { + let _ = SomeTupleStruct.new(); + //~^ ERROR expected value, found struct `SomeTupleStruct` + let _ = SomeRegularStruct.new(); + //~^ ERROR expected value, found struct `SomeRegularStruct` +} diff --git a/tests/ui/resolve/suggestions/suggest-path-for-tuple-struct.stderr b/tests/ui/resolve/suggestions/suggest-path-for-tuple-struct.stderr new file mode 100644 index 00000000000..68a5b550978 --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-path-for-tuple-struct.stderr @@ -0,0 +1,27 @@ +error[E0423]: expected value, found struct `SomeTupleStruct` + --> $DIR/suggest-path-for-tuple-struct.rs:22:13 + | +LL | let _ = SomeTupleStruct.new(); + | ^^^^^^^^^^^^^^^ + | +help: use the path separator to refer to an item + | +LL - let _ = SomeTupleStruct.new(); +LL + let _ = SomeTupleStruct::new(); + | + +error[E0423]: expected value, found struct `SomeRegularStruct` + --> $DIR/suggest-path-for-tuple-struct.rs:24:13 + | +LL | let _ = SomeRegularStruct.new(); + | ^^^^^^^^^^^^^^^^^ + | +help: use the path separator to refer to an item + | +LL - let _ = SomeRegularStruct.new(); +LL + let _ = SomeRegularStruct::new(); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/resolve/suggestions/suggest-path-instead-of-mod-dot-item.rs b/tests/ui/resolve/suggestions/suggest-path-instead-of-mod-dot-item.rs new file mode 100644 index 00000000000..d5d6b13d62c --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-path-instead-of-mod-dot-item.rs @@ -0,0 +1,111 @@ +// Beginners write `mod.item` when they should write `mod::item`. +// This tests that we suggest the latter when we encounter the former. + +pub mod a { + pub const I: i32 = 1; + + pub fn f() -> i32 { 2 } + + pub mod b { + pub const J: i32 = 3; + + pub fn g() -> i32 { 4 } + } +} + +fn h1() -> i32 { + a.I + //~^ ERROR expected value, found module `a` + //~| HELP use the path separator +} + +fn h2() -> i32 { + a.g() + //~^ ERROR expected value, found module `a` + //~| HELP use the path separator +} + +fn h3() -> i32 { + a.b.J + //~^ ERROR expected value, found module `a` + //~| HELP use the path separator +} + +fn h4() -> i32 { + a::b.J + //~^ ERROR expected value, found module `a::b` + //~| HELP a constant with a similar name exists + //~| HELP use the path separator +} + +fn h5() { + a.b.f(); + //~^ ERROR expected value, found module `a` + //~| HELP use the path separator + let v = Vec::new(); + v.push(a::b); + //~^ ERROR expected value, found module `a::b` + //~| HELP a constant with a similar name exists +} + +fn h6() -> i32 { + a::b.f() + //~^ ERROR expected value, found module `a::b` + //~| HELP a constant with a similar name exists + //~| HELP use the path separator +} + +fn h7() { + a::b + //~^ ERROR expected value, found module `a::b` + //~| HELP a constant with a similar name exists +} + +fn h8() -> i32 { + a::b() + //~^ ERROR expected function, found module `a::b` + //~| HELP a constant with a similar name exists +} + +macro_rules! module { + () => { + a + //~^ ERROR expected value, found module `a` + //~| ERROR expected value, found module `a` + }; +} + +macro_rules! create { + (method) => { + a.f() + //~^ ERROR expected value, found module `a` + //~| HELP use the path separator + }; + (field) => { + a.f + //~^ ERROR expected value, found module `a` + //~| HELP use the path separator + }; +} + +fn h9() { + // + // Note that if the receiver is a macro call, we do not want to suggest to replace + // `.` with `::` as that would be a syntax error. + // Since the receiver is a module and not a type, we cannot suggest to surround + // it with angle brackets. + // + + module!().g::<()>(); // no `help` here! + + module!().g; // no `help` here! + + // + // Ensure that the suggestion is shown for expressions inside of macro definitions. + // + + let _ = create!(method); + let _ = create!(field); +} + +fn main() {} diff --git a/tests/ui/resolve/suggestions/suggest-path-instead-of-mod-dot-item.stderr b/tests/ui/resolve/suggestions/suggest-path-instead-of-mod-dot-item.stderr new file mode 100644 index 00000000000..5db943cd10d --- /dev/null +++ b/tests/ui/resolve/suggestions/suggest-path-instead-of-mod-dot-item.stderr @@ -0,0 +1,178 @@ +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:17:5 + | +LL | a.I + | ^ + | +help: use the path separator to refer to an item + | +LL - a.I +LL + a::I + | + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:23:5 + | +LL | a.g() + | ^ + | +help: use the path separator to refer to an item + | +LL - a.g() +LL + a::g() + | + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:29:5 + | +LL | a.b.J + | ^ + | +help: use the path separator to refer to an item + | +LL - a.b.J +LL + a::b.J + | + +error[E0423]: expected value, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:35:5 + | +LL | pub const I: i32 = 1; + | --------------------- similarly named constant `I` defined here +... +LL | a::b.J + | ^^^^ + | +help: use the path separator to refer to an item + | +LL - a::b.J +LL + a::b::J + | +help: a constant with a similar name exists + | +LL - a::b.J +LL + a::I.J + | + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:42:5 + | +LL | a.b.f(); + | ^ + | +help: use the path separator to refer to an item + | +LL - a.b.f(); +LL + a::b.f(); + | + +error[E0423]: expected value, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:46:12 + | +LL | pub const I: i32 = 1; + | --------------------- similarly named constant `I` defined here +... +LL | v.push(a::b); + | ^^^- + | | + | help: a constant with a similar name exists: `I` + +error[E0423]: expected value, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:52:5 + | +LL | pub const I: i32 = 1; + | --------------------- similarly named constant `I` defined here +... +LL | a::b.f() + | ^^^^ + | +help: use the path separator to refer to an item + | +LL - a::b.f() +LL + a::b::f() + | +help: a constant with a similar name exists + | +LL - a::b.f() +LL + a::I.f() + | + +error[E0423]: expected value, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:59:5 + | +LL | pub const I: i32 = 1; + | --------------------- similarly named constant `I` defined here +... +LL | a::b + | ^^^- + | | + | help: a constant with a similar name exists: `I` + +error[E0423]: expected function, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:65:5 + | +LL | pub const I: i32 = 1; + | --------------------- similarly named constant `I` defined here +... +LL | a::b() + | ^^^- + | | + | help: a constant with a similar name exists: `I` + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:72:9 + | +LL | a + | ^ not a value +... +LL | module!().g::<()>(); // no `help` here! + | --------- in this macro invocation + | + = note: this error originates in the macro `module` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:72:9 + | +LL | a + | ^ not a value +... +LL | module!().g; // no `help` here! + | --------- in this macro invocation + | + = note: this error originates in the macro `module` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:80:9 + | +LL | a.f() + | ^ +... +LL | let _ = create!(method); + | --------------- in this macro invocation + | + = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use the path separator to refer to an item + | +LL - a.f() +LL + a::f() + | + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:85:9 + | +LL | a.f + | ^ +... +LL | let _ = create!(field); + | -------------- in this macro invocation + | + = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use the path separator to refer to an item + | +LL - a.f +LL + a::f + | + +error: aborting due to 13 previous errors + +For more information about this error, try `rustc --explain E0423`. -- cgit 1.4.1-3-g733a5 From 000f038e9999ebdbaf3167c42dadc3db872a5d3f Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sun, 29 Jun 2025 15:38:09 +0800 Subject: Add ui test resolve/false-self-in-macro-issue-143134.rs Signed-off-by: xizheyin --- tests/ui/resolve/false-self-in-macro-issue-143134.rs | 10 ++++++++++ tests/ui/resolve/false-self-in-macro-issue-143134.stderr | 12 ++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/ui/resolve/false-self-in-macro-issue-143134.rs create mode 100644 tests/ui/resolve/false-self-in-macro-issue-143134.stderr diff --git a/tests/ui/resolve/false-self-in-macro-issue-143134.rs b/tests/ui/resolve/false-self-in-macro-issue-143134.rs new file mode 100644 index 00000000000..0983b8b3dc3 --- /dev/null +++ b/tests/ui/resolve/false-self-in-macro-issue-143134.rs @@ -0,0 +1,10 @@ +trait T { + fn f(self); + } + impl T for () { + fn f(self) { + let self = (); //~ ERROR expected unit struct, unit variant or constant, found local variable `self` + } +} + +fn main() {} diff --git a/tests/ui/resolve/false-self-in-macro-issue-143134.stderr b/tests/ui/resolve/false-self-in-macro-issue-143134.stderr new file mode 100644 index 00000000000..43e77e183ae --- /dev/null +++ b/tests/ui/resolve/false-self-in-macro-issue-143134.stderr @@ -0,0 +1,12 @@ +error[E0424]: expected unit struct, unit variant or constant, found local variable `self` + --> $DIR/false-self-in-macro-issue-143134.rs:6:13 + | +LL | / fn f(self) { +LL | | let self = (); + | | ^^^^ `self` value is a keyword and may not be bound to variables or shadowed +LL | | } + | |_____- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0424`. -- cgit 1.4.1-3-g733a5 From 236b392904d234d9ec2968c55ae20132405a555d Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sun, 29 Jun 2025 16:10:52 +0800 Subject: Return early when `self` resolve failure because of `let self = ...` Signed-off-by: xizheyin --- compiler/rustc_resolve/src/late/diagnostics.rs | 16 ++++++++++++---- tests/ui/error-codes/E0424.stderr | 2 -- tests/ui/resolve/false-self-in-macro-issue-143134.stderr | 7 ++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index e7b8c988cd4..0b78835326f 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1183,15 +1183,23 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { _ => "`self` value is a keyword only available in methods with a `self` parameter", }, ); + + // using `let self` is wrong even if we're not in an associated method or if we're in a macro expansion. + // So, we should return early if we're in a pattern, see issue #143134. + if matches!(source, PathSource::Pat) { + return true; + } + let is_assoc_fn = self.self_type_is_available(); let self_from_macro = "a `self` parameter, but a macro invocation can only \ access identifiers it receives from parameters"; - if let Some((fn_kind, span)) = &self.diag_metadata.current_function { + if let Some((fn_kind, fn_span)) = &self.diag_metadata.current_function { // The current function has a `self` parameter, but we were unable to resolve // a reference to `self`. This can only happen if the `self` identifier we - // are resolving came from a different hygiene context. + // are resolving came from a different hygiene context or a variable binding. + // But variable binding error is returned early above. if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) { - err.span_label(*span, format!("this function has {self_from_macro}")); + err.span_label(*fn_span, format!("this function has {self_from_macro}")); } else { let doesnt = if is_assoc_fn { let (span, sugg) = fn_kind @@ -1204,7 +1212,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // This avoids placing the suggestion into the visibility specifier. let span = fn_kind .ident() - .map_or(*span, |ident| span.with_lo(ident.span.hi())); + .map_or(*fn_span, |ident| fn_span.with_lo(ident.span.hi())); ( self.r .tcx diff --git a/tests/ui/error-codes/E0424.stderr b/tests/ui/error-codes/E0424.stderr index d02da3e4ecb..831a070bf6c 100644 --- a/tests/ui/error-codes/E0424.stderr +++ b/tests/ui/error-codes/E0424.stderr @@ -40,8 +40,6 @@ LL | fn qux(&self) { error[E0424]: expected unit struct, unit variant or constant, found module `self` --> $DIR/E0424.rs:20:9 | -LL | fn main () { - | ---- this function can't have a `self` parameter LL | let self = "self"; | ^^^^ `self` value is a keyword and may not be bound to variables or shadowed diff --git a/tests/ui/resolve/false-self-in-macro-issue-143134.stderr b/tests/ui/resolve/false-self-in-macro-issue-143134.stderr index 43e77e183ae..48c979575ea 100644 --- a/tests/ui/resolve/false-self-in-macro-issue-143134.stderr +++ b/tests/ui/resolve/false-self-in-macro-issue-143134.stderr @@ -1,11 +1,8 @@ error[E0424]: expected unit struct, unit variant or constant, found local variable `self` --> $DIR/false-self-in-macro-issue-143134.rs:6:13 | -LL | / fn f(self) { -LL | | let self = (); - | | ^^^^ `self` value is a keyword and may not be bound to variables or shadowed -LL | | } - | |_____- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters +LL | let self = (); + | ^^^^ `self` value is a keyword and may not be bound to variables or shadowed error: aborting due to 1 previous error -- cgit 1.4.1-3-g733a5 From 9d8e68d1d6a071696588c1f81e5f1d6df8709042 Mon Sep 17 00:00:00 2001 From: klensy Date: Fri, 4 Jul 2025 11:55:03 +0300 Subject: bump termize --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bdc299749d7..2b6139e7e7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ declare_clippy_lint = { path = "declare_clippy_lint" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } clippy_lints_internal = { path = "clippy_lints_internal", optional = true } tempfile = { version = "3.20", optional = true } -termize = "0.1" +termize = "0.2" color-print = "0.3.4" anstream = "0.6.18" -- cgit 1.4.1-3-g733a5 From 1a1b52acbf44f55f7458dd38b260479843fe3810 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 4 Jul 2025 11:33:36 +0000 Subject: clippy fix: indentation --- library/core/src/alloc/layout.rs | 4 ++-- library/core/src/cell.rs | 28 ++++++++++++++-------------- library/core/src/error.rs | 24 ++++++++++++------------ library/core/src/fmt/mod.rs | 14 +++++++------- library/core/src/marker.rs | 4 ++-- library/core/src/ops/drop.rs | 2 +- library/core/src/option.rs | 1 + library/core/src/pin.rs | 16 ++++++++-------- 8 files changed, 47 insertions(+), 46 deletions(-) diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 380f67f91f9..49275975f04 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -61,8 +61,8 @@ impl Layout { /// * `align` must be a power of two, /// /// * `size`, when rounded up to the nearest multiple of `align`, - /// must not overflow `isize` (i.e., the rounded value must be - /// less than or equal to `isize::MAX`). + /// must not overflow `isize` (i.e., the rounded value must be + /// less than or equal to `isize::MAX`). #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_const_stable(feature = "const_alloc_layout_size_align", since = "1.50.0")] #[inline] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 7ce03e3d831..2cd5c19bf4e 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1941,21 +1941,21 @@ impl fmt::Display for RefMut<'_, T> { /// The precise Rust aliasing rules are somewhat in flux, but the main points are not contentious: /// /// - If you create a safe reference with lifetime `'a` (either a `&T` or `&mut T` reference), then -/// you must not access the data in any way that contradicts that reference for the remainder of -/// `'a`. For example, this means that if you take the `*mut T` from an `UnsafeCell` and cast it -/// to an `&T`, then the data in `T` must remain immutable (modulo any `UnsafeCell` data found -/// within `T`, of course) until that reference's lifetime expires. Similarly, if you create a `&mut -/// T` reference that is released to safe code, then you must not access the data within the -/// `UnsafeCell` until that reference expires. +/// you must not access the data in any way that contradicts that reference for the remainder of +/// `'a`. For example, this means that if you take the `*mut T` from an `UnsafeCell` and cast it +/// to an `&T`, then the data in `T` must remain immutable (modulo any `UnsafeCell` data found +/// within `T`, of course) until that reference's lifetime expires. Similarly, if you create a +/// `&mut T` reference that is released to safe code, then you must not access the data within the +/// `UnsafeCell` until that reference expires. /// /// - For both `&T` without `UnsafeCell<_>` and `&mut T`, you must also not deallocate the data -/// until the reference expires. As a special exception, given an `&T`, any part of it that is -/// inside an `UnsafeCell<_>` may be deallocated during the lifetime of the reference, after the -/// last time the reference is used (dereferenced or reborrowed). Since you cannot deallocate a part -/// of what a reference points to, this means the memory an `&T` points to can be deallocated only if -/// *every part of it* (including padding) is inside an `UnsafeCell`. +/// until the reference expires. As a special exception, given an `&T`, any part of it that is +/// inside an `UnsafeCell<_>` may be deallocated during the lifetime of the reference, after the +/// last time the reference is used (dereferenced or reborrowed). Since you cannot deallocate a part +/// of what a reference points to, this means the memory an `&T` points to can be deallocated only if +/// *every part of it* (including padding) is inside an `UnsafeCell`. /// -/// However, whenever a `&UnsafeCell` is constructed or dereferenced, it must still point to +/// However, whenever a `&UnsafeCell` is constructed or dereferenced, it must still point to /// live memory and the compiler is allowed to insert spurious reads if it can prove that this /// memory has not yet been deallocated. /// @@ -1963,10 +1963,10 @@ impl fmt::Display for RefMut<'_, T> { /// for single-threaded code: /// /// 1. A `&T` reference can be released to safe code and there it can co-exist with other `&T` -/// references, but not with a `&mut T` +/// references, but not with a `&mut T` /// /// 2. A `&mut T` reference may be released to safe code provided neither other `&mut T` nor `&T` -/// co-exist with it. A `&mut T` must always be unique. +/// co-exist with it. A `&mut T` must always be unique. /// /// Note that whilst mutating the contents of an `&UnsafeCell` (even while other /// `&UnsafeCell` references alias the cell) is diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 7f5c6ac42bc..88e633c9eef 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -447,28 +447,28 @@ where /// separated by API boundaries: /// /// * Consumer - the consumer requests objects using a Request instance; eg a crate that offers -/// fancy `Error`/`Result` reporting to users wants to request a Backtrace from a given `dyn Error`. +/// fancy `Error`/`Result` reporting to users wants to request a Backtrace from a given `dyn Error`. /// /// * Producer - the producer provides objects when requested via Request; eg. a library with an -/// an `Error` implementation that automatically captures backtraces at the time instances are -/// created. +/// an `Error` implementation that automatically captures backtraces at the time instances are +/// created. /// /// The consumer only needs to know where to submit their request and are expected to handle the /// request not being fulfilled by the use of `Option` in the responses offered by the producer. /// /// * A Producer initializes the value of one of its fields of a specific type. (or is otherwise -/// prepared to generate a value requested). eg, `backtrace::Backtrace` or -/// `std::backtrace::Backtrace` +/// prepared to generate a value requested). eg, `backtrace::Backtrace` or +/// `std::backtrace::Backtrace` /// * A Consumer requests an object of a specific type (say `std::backtrace::Backtrace`). In the -/// case of a `dyn Error` trait object (the Producer), there are functions called `request_ref` and -/// `request_value` to simplify obtaining an `Option` for a given type. +/// case of a `dyn Error` trait object (the Producer), there are functions called `request_ref` and +/// `request_value` to simplify obtaining an `Option` for a given type. /// * The Producer, when requested, populates the given Request object which is given as a mutable -/// reference. +/// reference. /// * The Consumer extracts a value or reference to the requested type from the `Request` object -/// wrapped in an `Option`; in the case of `dyn Error` the aforementioned `request_ref` and ` -/// request_value` methods mean that `dyn Error` users don't have to deal with the `Request` type at -/// all (but `Error` implementors do). The `None` case of the `Option` suggests only that the -/// Producer cannot currently offer an instance of the requested type, not it can't or never will. +/// wrapped in an `Option`; in the case of `dyn Error` the aforementioned `request_ref` and ` +/// request_value` methods mean that `dyn Error` users don't have to deal with the `Request` type at +/// all (but `Error` implementors do). The `None` case of the `Option` suggests only that the +/// Producer cannot currently offer an instance of the requested type, not it can't or never will. /// /// # Examples /// diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index c593737af8a..223b50dda09 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -349,10 +349,10 @@ impl FormattingOptions { /// Sets or removes the sign (the `+` or the `-` flag). /// /// - `+`: This is intended for numeric types and indicates that the sign - /// should always be printed. By default only the negative sign of signed - /// values is printed, and the sign of positive or unsigned values is - /// omitted. This flag indicates that the correct sign (+ or -) should - /// always be printed. + /// should always be printed. By default only the negative sign of signed + /// values is printed, and the sign of positive or unsigned values is + /// omitted. This flag indicates that the correct sign (+ or -) should + /// always be printed. /// - `-`: Currently not used #[unstable(feature = "formatting_options", issue = "118117")] pub fn sign(&mut self, sign: Option) -> &mut Self { @@ -439,9 +439,9 @@ impl FormattingOptions { /// Sets or removes the precision. /// /// - For non-numeric types, this can be considered a “maximum width”. If - /// the resulting string is longer than this width, then it is truncated - /// down to this many characters and that truncated value is emitted with - /// proper fill, alignment and width if those parameters are set. + /// the resulting string is longer than this width, then it is truncated + /// down to this many characters and that truncated value is emitted with + /// proper fill, alignment and width if those parameters are set. /// - For integral types, this is ignored. /// - For floating-point types, this indicates how many digits after the /// decimal point should be printed. diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 67f95a02af3..0f9a1ff02d1 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -211,8 +211,8 @@ pub trait PointeeSized { /// - The type is sized. /// - The type outlives `'a`. /// - Structs `Foo<..., T1, ..., Tn, ...>` implement `Unsize>` -/// where any number of (type and const) parameters may be changed if all of these conditions -/// are met: +/// where any number of (type and const) parameters may be changed if all of these conditions +/// are met: /// - Only the last field of `Foo` has a type involving the parameters `T1`, ..., `Tn`. /// - All other parameters of the struct are equal. /// - `Field: Unsize>`, where `Field<...>` stands for the actual diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index 5d040804a8d..bbef7023207 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -11,7 +11,7 @@ /// This destructor consists of two components: /// - A call to `Drop::drop` for that value, if this special `Drop` trait is implemented for its type. /// - The automatically generated "drop glue" which recursively calls the destructors -/// of all the fields of this value. +/// of all the fields of this value. /// /// As Rust automatically calls the destructors of all contained fields, /// you don't have to implement `Drop` in most cases. But there are some cases where diff --git a/library/core/src/option.rs b/library/core/src/option.rs index f2a1e901188..f83a73fa566 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -125,6 +125,7 @@ //! `Option::::None` //! - `transmute::<_, [u8; size_of::()]>(Option::::None)` is sound and produces //! `[0u8; size_of::()]` +//! //! These cases are identified by the second column: //! //! | `T` | Transmuting between `[0u8; size_of::()]` and `Option::::None` sound? | diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 3ca6feb679b..14bf7ba9015 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -137,10 +137,10 @@ //! 2. An operation causes the value to depend on its own address not changing //! * e.g. calling [`poll`] for the first time on the produced [`Future`] //! 3. Further pieces of the safe interface of the type use internal [`unsafe`] operations which -//! assume that the address of the value is stable +//! assume that the address of the value is stable //! * e.g. subsequent calls to [`poll`] //! 4. Before the value is invalidated (e.g. deallocated), it is *dropped*, giving it a chance to -//! notify anything with pointers to itself that those pointers will be invalidated +//! notify anything with pointers to itself that those pointers will be invalidated //! * e.g. [`drop`]ping the [`Future`] [^pin-drop-future] //! //! There are two possible ways to ensure the invariants required for 2. and 3. above (which @@ -148,8 +148,8 @@ //! //! 1. Have the value detect when it is moved and update all the pointers that point to itself. //! 2. Guarantee that the address of the value does not change (and that memory is not re-used -//! for anything else) during the time that the pointers to it are expected to be valid to -//! dereference. +//! for anything else) during the time that the pointers to it are expected to be valid to +//! dereference. //! //! Since, as we discussed, Rust can move values without notifying them that they have moved, the //! first option is ruled out. @@ -160,11 +160,11 @@ //! be able to enforce this invariant in Rust: //! //! 1. Offer a wholly `unsafe` API to interact with the object, thus requiring every caller to -//! uphold the invariant themselves +//! uphold the invariant themselves //! 2. Store the value that must not be moved behind a carefully managed pointer internal to -//! the object +//! the object //! 3. Leverage the type system to encode and enforce this invariant by presenting a restricted -//! API surface to interact with *any* object that requires these invariants +//! API surface to interact with *any* object that requires these invariants //! //! The first option is quite obviously undesirable, as the [`unsafe`]ty of the interface will //! become viral throughout all code that interacts with the object. @@ -530,7 +530,7 @@ //! but it also implies that, //! //! 2. The memory location that stores the value must not get invalidated or otherwise repurposed -//! during the lifespan of the pinned value until its [`drop`] returns or panics +//! during the lifespan of the pinned value until its [`drop`] returns or panics //! //! This point is subtle but required for intrusive data structures to be implemented soundly. //! -- cgit 1.4.1-3-g733a5 From dd1a52f8b6ced8f49384859bbf379c9a05493f5c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 4 Jul 2025 13:57:38 +0200 Subject: Simplify clippy lints page further --- util/gh-pages/index_template.html | 16 ++++++++-------- util/gh-pages/script.js | 6 +++--- util/gh-pages/style.css | 7 +++---- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html index 458f20d9f99..6f380ec8fee 100644 --- a/util/gh-pages/index_template.html +++ b/util/gh-pages/index_template.html @@ -151,8 +151,8 @@ Otherwise, have a great day =^.^=

{# #}
{# #} - {{lint.id}} {#+ #} - {#+ #} + {{lint.id +}} + {#+ #} {# #} 📋 {# #} {# #} @@ -170,24 +170,24 @@ Otherwise, have a great day =^.^=
{{Self::markdown(lint.docs)}}
{# #}
{# Applicability #} -
{# #} - Applicability: {# #} +
{# #} + Applicability: {#+ #} {{ lint.applicability_str() }} {# #} (?) {# #}
{# Clippy version #} -
{# #} - {% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {# #} +
{# #} + {% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {#+ #} {{lint.version}} {# #}
{# Open related issues #} -
{# #} +
{# #} Related Issues {# #}
{# Jump to source #} {% if let Some(id_location) = lint.id_location %} -
{# #} +
{# #} View Source {# #}
{% endif %} diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index 285aa34e701..ee13f1c0cd8 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -554,10 +554,10 @@ function addListeners() { return; } - if (event.target.classList.contains("lint-anchor")) { - lintAnchor(event); - } else if (event.target.classList.contains("copy-to-clipboard")) { + if (event.target.classList.contains("copy-to-clipboard")) { copyToClipboard(event); + } else if (event.target.classList.contains("anchor")) { + lintAnchor(event); } }); diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css index 046a2a5df7c..022ea875200 100644 --- a/util/gh-pages/style.css +++ b/util/gh-pages/style.css @@ -69,7 +69,6 @@ div.panel div.panel-body button.open { .lint-title .label { display: inline-block; } .panel-title-name { flex: 1; min-width: 400px;} -.panel-title-name span { vertical-align: bottom; } .panel .panel-title-name .anchor { display: none; } .panel:hover .panel-title-name .anchor { display: inline;} @@ -162,7 +161,7 @@ div.panel div.panel-body button.open { display: flex; flex-flow: column; } - .lint-additional-info-item + .lint-additional-info-item { + .lint-additional-info-container > div + div { border-top: 1px solid var(--theme-popup-border); } } @@ -171,12 +170,12 @@ div.panel div.panel-body button.open { display: flex; flex-flow: row; } - .lint-additional-info-item + .lint-additional-info-item { + .lint-additional-info-container > div + div { border-left: 1px solid var(--theme-popup-border); } } -.lint-additional-info-item { +.lint-additional-info-container > div { display: inline-flex; min-width: 200px; flex-grow: 1; -- cgit 1.4.1-3-g733a5 From b4a0cd99950871b9e694d81a24f6e41827b3a4e5 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 3 Jul 2025 13:42:36 +0000 Subject: Remove names_imported_by_glob_use query. --- clippy_lints/src/wildcard_imports.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index d9dda6eadb2..22fd15d153a 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -130,7 +130,7 @@ impl LateLintPass<'_> for WildcardImports { } if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind && (self.warn_on_all || !self.check_exceptions(cx, item, use_path.segments)) - && let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id) + && let Some(used_imports) = cx.tcx.resolutions(()).glob_map.get(&item.owner_id.def_id) && !used_imports.is_empty() // Already handled by `unused_imports` && !used_imports.contains(&kw::Underscore) { -- cgit 1.4.1-3-g733a5 From 9c9ee348072ef5c8f29ccd8c0496d2269ba244df Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 3 Jul 2025 09:10:49 +0200 Subject: Port `#[non_exhaustive]` to the new attribute parsing infrastructure --- clippy_lints/src/exhaustive_items.rs | 6 ++++-- clippy_lints/src/manual_non_exhaustive.rs | 13 +++++++------ clippy_utils/src/attrs.rs | 9 +++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 86d9038ec45..7dda3e0fdb9 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -4,7 +4,9 @@ use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_attr_data_structures::AttributeKind; +use rustc_attr_data_structures::find_attr; + declare_clippy_lint! { /// ### What it does @@ -85,7 +87,7 @@ impl LateLintPass<'_> for ExhaustiveItems { }; if cx.effective_visibilities.is_exported(item.owner_id.def_id) && let attrs = cx.tcx.hir_attrs(item.hir_id()) - && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) + && !find_attr!(attrs, AttributeKind::NonExhaustive(..)) && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public()) { span_lint_and_then(cx, lint, item.span, msg, |diag| { diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 3562b1ff5cc..51696b38880 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -4,7 +4,6 @@ use clippy_utils::is_doc_hidden; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_indent; use itertools::Itertools; -use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -12,7 +11,9 @@ use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::Span; +use rustc_attr_data_structures::find_attr; +use rustc_attr_data_structures::AttributeKind; declare_clippy_lint! { /// ### What it does @@ -93,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { .then_some((v.def_id, v.span)) }); if let Ok((id, span)) = iter.exactly_one() - && !attr::contains_name(cx.tcx.hir_attrs(item.hir_id()), sym::non_exhaustive) + && !find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::NonExhaustive(..)) { self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); } @@ -113,10 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { item.span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - if let Some(non_exhaustive) = - attr::find_by_name(cx.tcx.hir_attrs(item.hir_id()), sym::non_exhaustive) + if let Some(non_exhaustive_span) = + find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::NonExhaustive(span) => *span) { - diag.span_note(non_exhaustive.span(), "the struct is already non-exhaustive"); + diag.span_note(non_exhaustive_span, "the struct is already non-exhaustive"); } else { let indent = snippet_indent(cx, item.span).unwrap_or_default(); diag.span_suggestion_verbose( diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 8a0ff5323c9..4c7a589e185 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -7,9 +7,10 @@ use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; use rustc_span::{Span, Symbol}; use std::str::FromStr; - +use rustc_attr_data_structures::find_attr; use crate::source::SpanRangeExt; use crate::{sym, tokenize_with_text}; +use rustc_attr_data_structures::AttributeKind; /// Deprecation status of attributes known by Clippy. pub enum DeprecationStatus { @@ -165,13 +166,13 @@ pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool { pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { adt.is_variant_list_non_exhaustive() - || tcx.has_attr(adt.did(), sym::non_exhaustive) + || find_attr!(tcx.get_all_attrs(adt.did()), AttributeKind::NonExhaustive(..)) || adt.variants().iter().any(|variant_def| { - variant_def.is_field_list_non_exhaustive() || tcx.has_attr(variant_def.def_id, sym::non_exhaustive) + variant_def.is_field_list_non_exhaustive() || find_attr!(tcx.get_all_attrs(variant_def.def_id), AttributeKind::NonExhaustive(..)) }) || adt .all_fields() - .any(|field_def| tcx.has_attr(field_def.did, sym::non_exhaustive)) + .any(|field_def| find_attr!(tcx.get_all_attrs(field_def.did), AttributeKind::NonExhaustive(..))) } /// Checks if the given span contains a `#[cfg(..)]` attribute -- cgit 1.4.1-3-g733a5 From 6ae56f4e965418d920da4981303cbdc43477cb39 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 4 Jul 2025 16:53:19 -0500 Subject: Remove the `let_chains` feature now that it is stable --- library/compiler-builtins/crates/libm-macros/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/compiler-builtins/crates/libm-macros/src/lib.rs b/library/compiler-builtins/crates/libm-macros/src/lib.rs index 482da974ca8..7efa1488f57 100644 --- a/library/compiler-builtins/crates/libm-macros/src/lib.rs +++ b/library/compiler-builtins/crates/libm-macros/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(let_chains)] - mod enums; mod parse; mod shared; -- cgit 1.4.1-3-g733a5 From e164811f5d69c00baf93694dfe203001b40ecdeb Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 4 Jul 2025 17:03:45 -0500 Subject: symcheck: Make `target` a positional argument This makes it more obvious what we intend to check rather than looking for `--target`. --- library/compiler-builtins/ci/run.sh | 27 +++++++--------- .../crates/symbol-check/src/main.rs | 37 ++++++++++++++-------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/library/compiler-builtins/ci/run.sh b/library/compiler-builtins/ci/run.sh index 27b9686eac6..8b7965bb205 100755 --- a/library/compiler-builtins/ci/run.sh +++ b/library/compiler-builtins/ci/run.sh @@ -54,29 +54,26 @@ symcheck=(cargo run -p symbol-check --release) [[ "$target" = "wasm"* ]] && symcheck+=(--features wasm) symcheck+=(-- build-and-check) -"${symcheck[@]}" -p compiler_builtins --target "$target" -"${symcheck[@]}" -p compiler_builtins --target "$target" --release -"${symcheck[@]}" -p compiler_builtins --target "$target" --features c -"${symcheck[@]}" -p compiler_builtins --target "$target" --features c --release -"${symcheck[@]}" -p compiler_builtins --target "$target" --features no-asm -"${symcheck[@]}" -p compiler_builtins --target "$target" --features no-asm --release -"${symcheck[@]}" -p compiler_builtins --target "$target" --features no-f16-f128 -"${symcheck[@]}" -p compiler_builtins --target "$target" --features no-f16-f128 --release +"${symcheck[@]}" "$target" -- -p compiler_builtins +"${symcheck[@]}" "$target" -- -p compiler_builtins --release +"${symcheck[@]}" "$target" -- -p compiler_builtins --features c +"${symcheck[@]}" "$target" -- -p compiler_builtins --features c --release +"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm +"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm --release +"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-f16-f128 +"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-f16-f128 --release run_intrinsics_test() { - args=( - --target "$target" --verbose \ - --manifest-path builtins-test-intrinsics/Cargo.toml - ) - args+=( "$@" ) + build_args=(--verbose --manifest-path builtins-test-intrinsics/Cargo.toml) + build_args+=("$@") # symcheck also checks the results of builtins-test-intrinsics - "${symcheck[@]}" "${args[@]}" + "${symcheck[@]}" "$target" -- "${build_args[@]}" # FIXME: we get access violations on Windows, our entrypoint may need to # be tweaked. if [ "${BUILD_ONLY:-}" != "1" ] && ! [[ "$target" = *"windows"* ]]; then - cargo run "${args[@]}" + cargo run --target "$target" "${build_args[@]}" fi } diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs index 843a943fb5f..f60d4f0d36f 100644 --- a/library/compiler-builtins/crates/symbol-check/src/main.rs +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -18,10 +18,12 @@ const CHECK_EXTENSIONS: &[Option<&str>] = &[Some("rlib"), Some("a"), Some("exe") const USAGE: &str = "Usage: - symbol-check build-and-check CARGO_ARGS ... + symbol-check build-and-check [TARGET] -- CARGO_BUILD_ARGS ... -Cargo will get invoked with `CARGO_ARGS` and all output +Cargo will get invoked with `CARGO_ARGS` and the specified target. All output `compiler_builtins*.rlib` files will be checked. + +If TARGET is not specified, the host target is used. "; fn main() { @@ -30,11 +32,13 @@ fn main() { let args_ref = args.iter().map(String::as_str).collect::>(); match &args_ref[1..] { - ["build-and-check", "--target", target, args @ ..] if !args.is_empty() => { - run_build_and_check(Some(target), args); + ["build-and-check", target, "--", args @ ..] if !args.is_empty() => { + check_cargo_args(args); + run_build_and_check(target, args); } - ["build-and-check", args @ ..] if !args.is_empty() => { - run_build_and_check(None, args); + ["build-and-check", "--", args @ ..] if !args.is_empty() => { + check_cargo_args(args); + run_build_and_check(&host_target(), args); } _ => { println!("{USAGE}"); @@ -43,7 +47,18 @@ fn main() { } } -fn run_build_and_check(target: Option<&str>, args: &[&str]) { +/// Make sure `--target` isn't passed to avoid confusion (since it should be proivded only once, +/// positionally). +fn check_cargo_args(args: &[&str]) { + for arg in args { + assert!( + !arg.contains("--target"), + "target must be passed positionally. {USAGE}" + ); + } +} + +fn run_build_and_check(target: &str, args: &[&str]) { let paths = exec_cargo_with_args(target, args); for path in paths { println!("Checking {}", path.display()); @@ -70,13 +85,7 @@ fn host_target() -> String { /// Run `cargo build` with the provided additional arguments, collecting the list of created /// libraries. -fn exec_cargo_with_args(target: Option<&str>, args: &[&str]) -> Vec { - let mut host = String::new(); - let target = target.unwrap_or_else(|| { - host = host_target(); - host.as_str() - }); - +fn exec_cargo_with_args(target: &str, args: &[&str]) -> Vec { let mut cmd = Command::new("cargo"); cmd.args(["build", "--target", target, "--message-format=json"]) .args(args) -- cgit 1.4.1-3-g733a5 From 7c2cc2ce40f962edeb7badc50172c5b6ff280f12 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Tue, 1 Jul 2025 02:46:47 +0500 Subject: cleaned up some tests --- tests/ui/codegen/output-slot-init-vs-noninit.rs | 54 +++++++++++++--------- tests/ui/codegen/remark-flag-functionality.rs | 14 +++--- tests/ui/codegen/shift-right-operand-mutation.rs | 21 ++++++--- tests/ui/codegen/sret-aliasing-rules.rs | 13 ++++-- tests/ui/macros/macro-paren-span-diagnostic.rs | 6 ++- tests/ui/macros/macro-paren-span-diagnostic.stderr | 2 +- tests/ui/panics/panic-during-display-formatting.rs | 12 +++-- tests/ui/panics/panic-handler-closures.rs | 6 ++- tests/ui/parser/ufcs-return-unused-parens.fixed | 9 ++-- tests/ui/parser/ufcs-return-unused-parens.rs | 9 ++-- tests/ui/parser/ufcs-return-unused-parens.stderr | 4 +- .../ui/traits/partialeq-ref-mismatch-diagnostic.rs | 4 +- .../partialeq-ref-mismatch-diagnostic.stderr | 8 ++-- 13 files changed, 101 insertions(+), 61 deletions(-) diff --git a/tests/ui/codegen/output-slot-init-vs-noninit.rs b/tests/ui/codegen/output-slot-init-vs-noninit.rs index 97757e74fc4..55586843740 100644 --- a/tests/ui/codegen/output-slot-init-vs-noninit.rs +++ b/tests/ui/codegen/output-slot-init-vs-noninit.rs @@ -1,26 +1,48 @@ +//! Check that output slots work correctly for both initializing and non-initializing assignments. +//! +//! Regression test for . + //@ run-pass #![allow(dead_code)] #![allow(unused_assignments)] #![allow(unknown_lints)] - #![allow(dead_assignment)] #![allow(unused_variables)] -struct A { a: isize, b: isize } -struct Abox { a: Box, b: Box } +struct A { + a: isize, + b: isize, +} -fn ret_int_i() -> isize { 10 } +struct Abox { + a: Box, + b: Box, +} -fn ret_ext_i() -> Box { Box::new(10) } +fn ret_int_i() -> isize { + 10 +} -fn ret_int_rec() -> A { A {a: 10, b: 10} } +fn ret_ext_i() -> Box { + Box::new(10) +} -fn ret_ext_rec() -> Box { Box::new(A {a: 10, b: 10}) } +fn ret_int_rec() -> A { + A { a: 10, b: 10 } +} -fn ret_ext_mem() -> Abox { Abox {a: Box::new(10), b: Box::new(10) } } +fn ret_ext_rec() -> Box { + Box::new(A { a: 10, b: 10 }) +} + +fn ret_ext_mem() -> Abox { + Abox { a: Box::new(10), b: Box::new(10) } +} -fn ret_ext_ext_mem() -> Box { Box::new(Abox{a: Box::new(10), b: Box::new(10) }) } +fn ret_ext_ext_mem() -> Box { + Box::new(Abox { a: Box::new(10), b: Box::new(10) }) +} pub fn main() { let mut int_i: isize; @@ -29,40 +51,28 @@ pub fn main() { let mut ext_rec: Box; let mut ext_mem: Abox; let mut ext_ext_mem: Box; - int_i = ret_int_i(); // initializing + int_i = ret_int_i(); // initializing int_i = ret_int_i(); // non-initializing - int_i = ret_int_i(); // non-initializing ext_i = ret_ext_i(); // initializing - ext_i = ret_ext_i(); // non-initializing - ext_i = ret_ext_i(); // non-initializing int_rec = ret_int_rec(); // initializing - int_rec = ret_int_rec(); // non-initializing - int_rec = ret_int_rec(); // non-initializing ext_rec = ret_ext_rec(); // initializing - ext_rec = ret_ext_rec(); // non-initializing - ext_rec = ret_ext_rec(); // non-initializing ext_mem = ret_ext_mem(); // initializing - ext_mem = ret_ext_mem(); // non-initializing - ext_mem = ret_ext_mem(); // non-initializing ext_ext_mem = ret_ext_ext_mem(); // initializing - ext_ext_mem = ret_ext_ext_mem(); // non-initializing - ext_ext_mem = ret_ext_ext_mem(); // non-initializing - } diff --git a/tests/ui/codegen/remark-flag-functionality.rs b/tests/ui/codegen/remark-flag-functionality.rs index 165fc63c007..797c55ba830 100644 --- a/tests/ui/codegen/remark-flag-functionality.rs +++ b/tests/ui/codegen/remark-flag-functionality.rs @@ -1,24 +1,26 @@ +//! Check that `-Cremark` flag correctly emits LLVM optimization remarks. +//! +//! Regression test for . + //@ build-pass //@ ignore-pass //@ revisions: all inline merge1 merge2 //@ compile-flags: --crate-type=lib -Cdebuginfo=1 -Copt-level=2 -// + // Check that remarks can be enabled individually or with "all": -// //@ [all] compile-flags: -Cremark=all //@ [inline] compile-flags: -Cremark=inline -// + // Check that values of -Cremark flag are accumulated: -// //@ [merge1] compile-flags: -Cremark=all -Cremark=giraffe //@ [merge2] compile-flags: -Cremark=inline -Cremark=giraffe + //@ dont-check-compiler-stderr //@ dont-require-annotations: NOTE #[no_mangle] #[inline(never)] -pub fn f() { -} +pub fn f() {} #[no_mangle] pub fn g() { diff --git a/tests/ui/codegen/shift-right-operand-mutation.rs b/tests/ui/codegen/shift-right-operand-mutation.rs index 016a667e937..b37a0baa6f8 100644 --- a/tests/ui/codegen/shift-right-operand-mutation.rs +++ b/tests/ui/codegen/shift-right-operand-mutation.rs @@ -1,12 +1,19 @@ +//! Ensure shift operations don't mutate their right operand. +//! +//! This test checks that expressions like `0 << b` don't accidentally +//! modify the variable `b` due to codegen issues with virtual registers. +//! +//! Regression test for . + //@ run-pass -#![allow(unused_must_use)] -// Regression test for issue #152. pub fn main() { - let mut b: usize = 1_usize; - while b < std::mem::size_of::() { - 0_usize << b; - b <<= 1_usize; - println!("{}", b); + let mut b: usize = 1; + while b < size_of::() { + // This shift operation should not mutate `b` + let _ = 0_usize << b; + b <<= 1; + std::hint::black_box(b); } + assert_eq!(size_of::(), b); } diff --git a/tests/ui/codegen/sret-aliasing-rules.rs b/tests/ui/codegen/sret-aliasing-rules.rs index 0dfaa19fadb..f35e722f764 100644 --- a/tests/ui/codegen/sret-aliasing-rules.rs +++ b/tests/ui/codegen/sret-aliasing-rules.rs @@ -1,3 +1,11 @@ +//! Check that functions with sret results don't violate aliasing rules. +//! +//! When `foo = func(&mut foo)` is called, the compiler must avoid creating +//! two mutable references to the same variable simultaneously (one for the +//! parameter and one for the hidden sret out-pointer). +//! +//! Regression test for . + //@ run-pass #[derive(Copy, Clone)] @@ -14,10 +22,7 @@ pub fn foo(f: &mut Foo) -> Foo { } pub fn main() { - let mut f = Foo { - f1: 8, - _f2: 9, - }; + let mut f = Foo { f1: 8, _f2: 9 }; f = foo(&mut f); assert_eq!(f.f1, 8); } diff --git a/tests/ui/macros/macro-paren-span-diagnostic.rs b/tests/ui/macros/macro-paren-span-diagnostic.rs index c8cb63d5190..cbcb0231e4e 100644 --- a/tests/ui/macros/macro-paren-span-diagnostic.rs +++ b/tests/ui/macros/macro-paren-span-diagnostic.rs @@ -1,5 +1,6 @@ -// Be smart about span of parenthesized expression in macro. +//! Check that error spans in parenthesized macro expressions point to the call site. +#[rustfmt::skip] macro_rules! paren { ($e:expr) => (($e)) // ^^^^ do not highlight here @@ -7,8 +8,9 @@ macro_rules! paren { mod m { pub struct S { - x: i32 + x: i32, } + pub fn make() -> S { S { x: 0 } } diff --git a/tests/ui/macros/macro-paren-span-diagnostic.stderr b/tests/ui/macros/macro-paren-span-diagnostic.stderr index da2f57033a4..ede6ff51c71 100644 --- a/tests/ui/macros/macro-paren-span-diagnostic.stderr +++ b/tests/ui/macros/macro-paren-span-diagnostic.stderr @@ -1,5 +1,5 @@ error[E0616]: field `x` of struct `S` is private - --> $DIR/paren-span.rs:19:14 + --> $DIR/macro-paren-span-diagnostic.rs:21:14 | LL | paren!(s.x); | ^ private field diff --git a/tests/ui/panics/panic-during-display-formatting.rs b/tests/ui/panics/panic-during-display-formatting.rs index 6505a69fef7..a307ee49385 100644 --- a/tests/ui/panics/panic-during-display-formatting.rs +++ b/tests/ui/panics/panic-during-display-formatting.rs @@ -1,3 +1,5 @@ +//! Check that panics in `Display::fmt` during printing are properly handled. + //@ run-pass //@ needs-unwind @@ -18,8 +20,10 @@ impl Display for A { fn main() { set_output_capture(Some(Arc::new(Mutex::new(Vec::new())))); - assert!(std::panic::catch_unwind(|| { - eprintln!("{}", A); - }) - .is_err()); + assert!( + std::panic::catch_unwind(|| { + eprintln!("{}", A); + }) + .is_err() + ); } diff --git a/tests/ui/panics/panic-handler-closures.rs b/tests/ui/panics/panic-handler-closures.rs index b161859bf9c..27fea925720 100644 --- a/tests/ui/panics/panic-handler-closures.rs +++ b/tests/ui/panics/panic-handler-closures.rs @@ -1,10 +1,12 @@ -//@ build-pass (FIXME(62277): could be check-pass?) +//! Check that closures can be used inside `#[panic_handler]` functions. + +//@ check-pass #![crate_type = "rlib"] #![no_std] #[panic_handler] -pub fn panic_fmt(_: &::core::panic::PanicInfo) -> ! { +pub fn panicfmt(_: &::core::panic::PanicInfo) -> ! { |x: u8| x; loop {} } diff --git a/tests/ui/parser/ufcs-return-unused-parens.fixed b/tests/ui/parser/ufcs-return-unused-parens.fixed index 440b22edd7d..811a853b769 100644 --- a/tests/ui/parser/ufcs-return-unused-parens.fixed +++ b/tests/ui/parser/ufcs-return-unused-parens.fixed @@ -1,13 +1,16 @@ +//! Check that UFCS syntax works correctly in return statements +//! without requiring workaround parentheses. +//! +//! Regression test for . + //@ run-pass //@ run-rustfix #![allow(dead_code)] #![warn(unused_parens)] -// Parser test for #37765 - fn with_parens(arg: T) -> String { - return ::to_string(&arg); //~WARN unnecessary parentheses around `return` value + return ::to_string(&arg); //~ WARN unnecessary parentheses around `return` value } fn no_parens(arg: T) -> String { diff --git a/tests/ui/parser/ufcs-return-unused-parens.rs b/tests/ui/parser/ufcs-return-unused-parens.rs index 7eaacd6bba7..6ea69ef9a26 100644 --- a/tests/ui/parser/ufcs-return-unused-parens.rs +++ b/tests/ui/parser/ufcs-return-unused-parens.rs @@ -1,13 +1,16 @@ +//! Check that UFCS syntax works correctly in return statements +//! without requiring workaround parentheses. +//! +//! Regression test for . + //@ run-pass //@ run-rustfix #![allow(dead_code)] #![warn(unused_parens)] -// Parser test for #37765 - fn with_parens(arg: T) -> String { - return (::to_string(&arg)); //~WARN unnecessary parentheses around `return` value + return (::to_string(&arg)); //~ WARN unnecessary parentheses around `return` value } fn no_parens(arg: T) -> String { diff --git a/tests/ui/parser/ufcs-return-unused-parens.stderr b/tests/ui/parser/ufcs-return-unused-parens.stderr index 2cc786fd947..6c09e98e7b1 100644 --- a/tests/ui/parser/ufcs-return-unused-parens.stderr +++ b/tests/ui/parser/ufcs-return-unused-parens.stderr @@ -1,11 +1,11 @@ warning: unnecessary parentheses around `return` value - --> $DIR/path-lookahead.rs:10:12 + --> $DIR/ufcs-return-unused-parens.rs:13:12 | LL | return (::to_string(&arg)); | ^ ^ | note: the lint level is defined here - --> $DIR/path-lookahead.rs:5:9 + --> $DIR/ufcs-return-unused-parens.rs:10:9 | LL | #![warn(unused_parens)] | ^^^^^^^^^^^^^ diff --git a/tests/ui/traits/partialeq-ref-mismatch-diagnostic.rs b/tests/ui/traits/partialeq-ref-mismatch-diagnostic.rs index 34b88b8a866..26ef8050b87 100644 --- a/tests/ui/traits/partialeq-ref-mismatch-diagnostic.rs +++ b/tests/ui/traits/partialeq-ref-mismatch-diagnostic.rs @@ -1,8 +1,10 @@ +//! Check diagnostic messages for `PartialEq` trait bound mismatches between `&T` and `T`. + fn foo(a: &T, b: T) { a == b; //~ ERROR E0277 } -fn foo2(a: &T, b: T) where { +fn foo2(a: &T, b: T) { a == b; //~ ERROR E0277 } diff --git a/tests/ui/traits/partialeq-ref-mismatch-diagnostic.stderr b/tests/ui/traits/partialeq-ref-mismatch-diagnostic.stderr index f5de1308e87..4cbd31656dc 100644 --- a/tests/ui/traits/partialeq-ref-mismatch-diagnostic.stderr +++ b/tests/ui/traits/partialeq-ref-mismatch-diagnostic.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `&T` with `T` - --> $DIR/partialeq_help.rs:2:7 + --> $DIR/partialeq-ref-mismatch-diagnostic.rs:4:7 | LL | a == b; | ^^ no implementation for `&T == T` @@ -15,7 +15,7 @@ LL | fn foo(a: &T, b: T) where &T: PartialEq { | ++++++++++++++++++++++ error[E0277]: can't compare `&T` with `T` - --> $DIR/partialeq_help.rs:6:7 + --> $DIR/partialeq-ref-mismatch-diagnostic.rs:8:7 | LL | a == b; | ^^ no implementation for `&T == T` @@ -25,10 +25,10 @@ help: consider dereferencing here | LL | *a == b; | + -help: consider extending the `where` clause, but there might be an alternative better way to express this requirement +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement | LL | fn foo2(a: &T, b: T) where &T: PartialEq { - | ++++++++++++++++ + | ++++++++++++++++++++++ error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 84060f608b375d77bed9845709d57441393ceed6 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 4 Jul 2025 18:10:27 -0500 Subject: symcheck: Improve diagnostics from spawned Cargo Rather than printing the entire JSON dump, use the rendered version. --- .../crates/symbol-check/src/main.rs | 29 ++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs index f60d4f0d36f..1312a717970 100644 --- a/library/compiler-builtins/crates/symbol-check/src/main.rs +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -87,9 +87,14 @@ fn host_target() -> String { /// libraries. fn exec_cargo_with_args(target: &str, args: &[&str]) -> Vec { let mut cmd = Command::new("cargo"); - cmd.args(["build", "--target", target, "--message-format=json"]) - .args(args) - .stdout(Stdio::piped()); + cmd.args([ + "build", + "--target", + target, + "--message-format=json-diagnostic-rendered-ansi", + ]) + .args(args) + .stdout(Stdio::piped()); println!("running: {cmd:?}"); let mut child = cmd.spawn().expect("failed to launch Cargo"); @@ -100,11 +105,21 @@ fn exec_cargo_with_args(target: &str, args: &[&str]) -> Vec { for line in reader.lines() { let line = line.expect("failed to read line"); - println!("{line}"); // tee to stdout - - // Select only steps that create files let j: Value = serde_json::from_str(&line).expect("failed to deserialize"); - if j["reason"] != "compiler-artifact" { + let reason = &j["reason"]; + + // Forward output that is meant to be user-facing + if reason == "compiler-message" { + println!("{}", j["message"]["rendered"].as_str().unwrap()); + } else if reason == "build-finished" { + println!("build finshed. success: {}", j["success"]); + } else if reason == "build-script-executed" { + let pretty = serde_json::to_string_pretty(&j).unwrap(); + println!("build script output: {pretty}",); + } + + // Only interested in the artifact list now + if reason != "compiler-artifact" { continue; } -- cgit 1.4.1-3-g733a5 From 19ab6461fbb1d9c05c3a6b6cdbaf9ffcd0f1c8d9 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 4 Jul 2025 19:30:31 -0500 Subject: Remove unused custom target JSON files 8521530f4938 ("Fix __divsi3 and __udivsi3 on thumbv6m targets") removed tests that use these `thumb*-linux` target files in favor of tests that use the `thumb*-none` targets, which are available via Rustup. The JSON files haven't been used since then and are outdated, so remove them. --- library/compiler-builtins/thumbv6m-linux-eabi.json | 28 ---------------------- .../compiler-builtins/thumbv7em-linux-eabi.json | 27 --------------------- .../compiler-builtins/thumbv7em-linux-eabihf.json | 28 ---------------------- library/compiler-builtins/thumbv7m-linux-eabi.json | 27 --------------------- 4 files changed, 110 deletions(-) delete mode 100644 library/compiler-builtins/thumbv6m-linux-eabi.json delete mode 100644 library/compiler-builtins/thumbv7em-linux-eabi.json delete mode 100644 library/compiler-builtins/thumbv7em-linux-eabihf.json delete mode 100644 library/compiler-builtins/thumbv7m-linux-eabi.json diff --git a/library/compiler-builtins/thumbv6m-linux-eabi.json b/library/compiler-builtins/thumbv6m-linux-eabi.json deleted file mode 100644 index ac736eae686..00000000000 --- a/library/compiler-builtins/thumbv6m-linux-eabi.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "abi-blacklist": [ - "stdcall", - "fastcall", - "vectorcall", - "win64", - "sysv64" - ], - "arch": "arm", - "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", - "env": "", - "executables": true, - "features": "+strict-align", - "linker": "arm-none-eabi-gcc", - "linker-flavor": "gcc", - "llvm-target": "thumbv6m-none-eabi", - "max-atomic-width": 0, - "os": "linux", - "panic-strategy": "abort", - "pre-link-args": { - "gcc": ["-nostartfiles"] - }, - "relocation-model": "static", - "target-endian": "little", - "target-pointer-width": "32", - "target-c-int-width": "32", - "vendor": "" -} diff --git a/library/compiler-builtins/thumbv7em-linux-eabi.json b/library/compiler-builtins/thumbv7em-linux-eabi.json deleted file mode 100644 index b6d4a6bda7b..00000000000 --- a/library/compiler-builtins/thumbv7em-linux-eabi.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "abi-blacklist": [ - "stdcall", - "fastcall", - "vectorcall", - "win64", - "sysv64" - ], - "arch": "arm", - "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", - "env": "", - "executables": true, - "linker": "arm-none-eabi-gcc", - "linker-flavor": "gcc", - "llvm-target": "thumbv7em-none-eabi", - "max-atomic-width": 32, - "os": "linux", - "panic-strategy": "abort", - "pre-link-args": { - "gcc": ["-nostartfiles"] - }, - "relocation-model": "static", - "target-endian": "little", - "target-pointer-width": "32", - "target-c-int-width": "32", - "vendor": "" -} diff --git a/library/compiler-builtins/thumbv7em-linux-eabihf.json b/library/compiler-builtins/thumbv7em-linux-eabihf.json deleted file mode 100644 index 81cfcd48d56..00000000000 --- a/library/compiler-builtins/thumbv7em-linux-eabihf.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "abi-blacklist": [ - "stdcall", - "fastcall", - "vectorcall", - "win64", - "sysv64" - ], - "arch": "arm", - "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", - "env": "", - "executables": true, - "features": "+vfp4,+d16,+fp-only-sp", - "linker": "arm-none-eabi-gcc", - "linker-flavor": "gcc", - "llvm-target": "thumbv7em-none-eabihf", - "max-atomic-width": 32, - "os": "linux", - "panic-strategy": "abort", - "pre-link-args": { - "gcc": ["-nostartfiles"] - }, - "relocation-model": "static", - "target-endian": "little", - "target-pointer-width": "32", - "target-c-int-width": "32", - "vendor": "" -} diff --git a/library/compiler-builtins/thumbv7m-linux-eabi.json b/library/compiler-builtins/thumbv7m-linux-eabi.json deleted file mode 100644 index abe037c5bef..00000000000 --- a/library/compiler-builtins/thumbv7m-linux-eabi.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "abi-blacklist": [ - "stdcall", - "fastcall", - "vectorcall", - "win64", - "sysv64" - ], - "arch": "arm", - "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", - "env": "", - "executables": true, - "linker": "arm-none-eabi-gcc", - "linker-flavor": "gcc", - "llvm-target": "thumbv7m-none-eabi", - "max-atomic-width": 32, - "os": "linux", - "panic-strategy": "abort", - "pre-link-args": { - "gcc": ["-nostartfiles"] - }, - "relocation-model": "static", - "target-endian": "little", - "target-pointer-width": "32", - "target-c-int-width": "32", - "vendor": "" -} -- cgit 1.4.1-3-g733a5 From 016bc61312b85fae0f0f53c56946239b712b4604 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 4 Jul 2025 19:42:18 -0500 Subject: Test building custom targets and resolve an issue probing `rustc` The `rustc` probe done in our build scripts needs to pass `--target` to get the correct configuration, which usually comes from the `TARGET` environment variable. However, for targets specified via a `target.json` file, `TARGET` gets set to the file name without an extension or path. `rustc` will check a search path to attempt to locate the file, but this is likely to fail since the directory where Cargo invokes build scripts (and hence where those scripts invoke `rustc`) might not have any relation to the JSON spec file. Resolve this for now by leaving `f16` and `f128` disabled if the `rustc` command fails. Result of the discussion at CARGO-14208 may eventually provide a better solution. A CI test is also added since custom JSON files are an edge case that could fail in other ways. I verified this fails without the fix here. The JSON file is the output for `thumbv7em-none-eabi`, just renamed so `rustc` doesn't identify it. --- .../compiler-builtins/.github/workflows/main.yaml | 20 +++++++++++++++++++ .../compiler-builtins/configure.rs | 13 +++++++++--- .../etc/thumbv7em-none-eabi-renamed.json | 23 ++++++++++++++++++++++ library/compiler-builtins/libm/configure.rs | 13 +++++++++--- 4 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 library/compiler-builtins/etc/thumbv7em-none-eabi-renamed.json diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index 95b0962b082..541c99c828d 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -195,6 +195,25 @@ jobs: run: ./ci/update-musl.sh - run: cargo clippy --workspace --all-targets + build-custom: + name: Build custom target + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: | + rustup update nightly --no-self-update + rustup default nightly + rustup component add rust-src + - uses: Swatinem/rust-cache@v2 + - run: | + # Ensure we can build with custom target.json files (these can interact + # poorly with build scripts) + cargo build -p compiler_builtins -p libm \ + --target etc/thumbv7em-none-eabi-renamed.json \ + -Zbuild-std=core + benchmarks: name: Benchmarks timeout-minutes: 20 @@ -331,6 +350,7 @@ jobs: success: needs: - benchmarks + - build-custom - clippy - extensive - miri diff --git a/library/compiler-builtins/compiler-builtins/configure.rs b/library/compiler-builtins/compiler-builtins/configure.rs index a1e45080e71..9721ddf090c 100644 --- a/library/compiler-builtins/compiler-builtins/configure.rs +++ b/library/compiler-builtins/compiler-builtins/configure.rs @@ -45,9 +45,16 @@ impl Target { let out = cmd .output() .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}")); - assert!(out.status.success(), "failed to run `{cmd:?}`"); let rustc_cfg = str::from_utf8(&out.stdout).unwrap(); + // If we couldn't query `rustc` (e.g. a custom JSON target was used), make the safe + // choice and leave `f16` and `f128` disabled. + let rustc_output_ok = out.status.success(); + let reliable_f128 = + rustc_output_ok && rustc_cfg.lines().any(|l| l == "target_has_reliable_f128"); + let reliable_f16 = + rustc_output_ok && rustc_cfg.lines().any(|l| l == "target_has_reliable_f16"); + Self { triple, triple_split, @@ -67,8 +74,8 @@ impl Target { .split(",") .map(ToOwned::to_owned) .collect(), - reliable_f128: rustc_cfg.lines().any(|l| l == "target_has_reliable_f128"), - reliable_f16: rustc_cfg.lines().any(|l| l == "target_has_reliable_f16"), + reliable_f128, + reliable_f16, } } diff --git a/library/compiler-builtins/etc/thumbv7em-none-eabi-renamed.json b/library/compiler-builtins/etc/thumbv7em-none-eabi-renamed.json new file mode 100644 index 00000000000..81273d44e49 --- /dev/null +++ b/library/compiler-builtins/etc/thumbv7em-none-eabi-renamed.json @@ -0,0 +1,23 @@ +{ + "abi": "eabi", + "arch": "arm", + "c-enum-min-bits": 8, + "crt-objects-fallback": "false", + "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + "emit-debug-gdb-scripts": false, + "frame-pointer": "always", + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "llvm-floatabi": "soft", + "llvm-target": "thumbv7em-none-eabi", + "max-atomic-width": 32, + "metadata": { + "description": "Bare ARMv7E-M", + "host_tools": false, + "std": false, + "tier": 2 + }, + "panic-strategy": "abort", + "relocation-model": "static", + "target-pointer-width": "32" +} diff --git a/library/compiler-builtins/libm/configure.rs b/library/compiler-builtins/libm/configure.rs index 6562ecbe5db..f9100d2d58b 100644 --- a/library/compiler-builtins/libm/configure.rs +++ b/library/compiler-builtins/libm/configure.rs @@ -43,9 +43,16 @@ impl Config { let out = cmd .output() .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}")); - assert!(out.status.success(), "failed to run `{cmd:?}`"); let rustc_cfg = str::from_utf8(&out.stdout).unwrap(); + // If we couldn't query `rustc` (e.g. a custom JSON target was used), make the safe + // choice and leave `f16` and `f128` disabled. + let rustc_output_ok = out.status.success(); + let reliable_f128 = + rustc_output_ok && rustc_cfg.lines().any(|l| l == "target_has_reliable_f128"); + let reliable_f16 = + rustc_output_ok && rustc_cfg.lines().any(|l| l == "target_has_reliable_f16"); + Self { target_triple, manifest_dir: PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()), @@ -59,8 +66,8 @@ impl Config { target_string: env::var("TARGET").unwrap(), target_vendor: env::var("CARGO_CFG_TARGET_VENDOR").unwrap(), target_features, - reliable_f128: rustc_cfg.lines().any(|l| l == "target_has_reliable_f128"), - reliable_f16: rustc_cfg.lines().any(|l| l == "target_has_reliable_f16"), + reliable_f128, + reliable_f16, } } } -- cgit 1.4.1-3-g733a5 From 50061f3b11f51d7a6e3acd8ce793a1f17f99b597 Mon Sep 17 00:00:00 2001 From: dianne Date: Fri, 4 Jul 2025 21:17:40 -0700 Subject: always check for mixed deref pattern and normal constructors This makes it work for box patterns and in rust-analyzer. --- compiler/rustc_pattern_analysis/src/checks.rs | 50 ++++++++++++++++ compiler/rustc_pattern_analysis/src/lib.rs | 10 ++++ compiler/rustc_pattern_analysis/src/rustc.rs | 67 +++++----------------- compiler/rustc_pattern_analysis/src/usefulness.rs | 5 +- .../rustc_pattern_analysis/tests/common/mod.rs | 9 +++ .../src/diagnostics/match_check/pat_analysis.rs | 8 +++ .../ui/pattern/box-pattern-constructor-mismatch.rs | 11 ++++ .../box-pattern-constructor-mismatch.stderr | 10 ++++ 8 files changed, 117 insertions(+), 53 deletions(-) create mode 100644 compiler/rustc_pattern_analysis/src/checks.rs create mode 100644 tests/ui/pattern/box-pattern-constructor-mismatch.rs create mode 100644 tests/ui/pattern/box-pattern-constructor-mismatch.stderr diff --git a/compiler/rustc_pattern_analysis/src/checks.rs b/compiler/rustc_pattern_analysis/src/checks.rs new file mode 100644 index 00000000000..88ccaa1e0e5 --- /dev/null +++ b/compiler/rustc_pattern_analysis/src/checks.rs @@ -0,0 +1,50 @@ +//! Contains checks that must be run to validate matches before performing usefulness analysis. + +use crate::constructor::Constructor::*; +use crate::pat_column::PatternColumn; +use crate::{MatchArm, PatCx}; + +/// Validate that deref patterns and normal constructors aren't used to match on the same place. +pub(crate) fn detect_mixed_deref_pat_ctors<'p, Cx: PatCx>( + cx: &Cx, + arms: &[MatchArm<'p, Cx>], +) -> Result<(), Cx::Error> { + let pat_column = PatternColumn::new(arms); + detect_mixed_deref_pat_ctors_inner(cx, &pat_column) +} + +fn detect_mixed_deref_pat_ctors_inner<'p, Cx: PatCx>( + cx: &Cx, + column: &PatternColumn<'p, Cx>, +) -> Result<(), Cx::Error> { + let Some(ty) = column.head_ty() else { + return Ok(()); + }; + + // Check for a mix of deref patterns and normal constructors. + let mut deref_pat = None; + let mut normal_pat = None; + for pat in column.iter() { + match pat.ctor() { + // The analysis can handle mixing deref patterns with wildcards and opaque patterns. + Wildcard | Opaque(_) => {} + DerefPattern(_) => deref_pat = Some(pat), + // Nothing else can be compared to deref patterns in `Constructor::is_covered_by`. + _ => normal_pat = Some(pat), + } + } + if let Some(deref_pat) = deref_pat + && let Some(normal_pat) = normal_pat + { + return Err(cx.report_mixed_deref_pat_ctors(deref_pat, normal_pat)); + } + + // Specialize and recurse into the patterns' fields. + let set = column.analyze_ctors(cx, &ty)?; + for ctor in set.present { + for specialized_column in column.specialize(cx, &ty, &ctor).iter() { + detect_mixed_deref_pat_ctors_inner(cx, specialized_column)?; + } + } + Ok(()) +} diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 2b85d7b26ce..129fd38725c 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -8,6 +8,7 @@ #![allow(unused_crate_dependencies)] // tidy-alphabetical-end +pub(crate) mod checks; pub mod constructor; #[cfg(feature = "rustc")] pub mod errors; @@ -107,6 +108,15 @@ pub trait PatCx: Sized + fmt::Debug { _gapped_with: &[&DeconstructedPat], ) { } + + /// The current implementation of deref patterns requires that they can't match on the same + /// place as a normal constructor. Since this isn't caught by type-checking, we check it in the + /// `PatCx` before running the analysis. This reports an error if the check fails. + fn report_mixed_deref_pat_ctors( + &self, + deref_pat: &DeconstructedPat, + normal_pat: &DeconstructedPat, + ) -> Self::Error; } /// The arm of a match expression. diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index e53cebc59ba..e9795126db6 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -1027,6 +1027,21 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { ); } } + + fn report_mixed_deref_pat_ctors( + &self, + deref_pat: &crate::pat::DeconstructedPat, + normal_pat: &crate::pat::DeconstructedPat, + ) -> Self::Error { + let deref_pattern_label = deref_pat.data().span; + let normal_constructor_label = normal_pat.data().span; + self.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors { + spans: vec![deref_pattern_label, normal_constructor_label], + smart_pointer_ty: deref_pat.ty().inner(), + deref_pattern_label, + normal_constructor_label, + }) + } } /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. @@ -1055,13 +1070,6 @@ pub fn analyze_match<'p, 'tcx>( ) -> Result, ErrorGuaranteed> { let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); - // The analysis doesn't support deref patterns mixed with normal constructors; error if present. - // FIXME(deref_patterns): This only needs to run when a deref pattern was found during lowering. - if tycx.tcx.features().deref_patterns() { - let pat_column = PatternColumn::new(arms); - detect_mixed_deref_pat_ctors(tycx, &pat_column)?; - } - let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee); let report = compute_match_usefulness( tycx, @@ -1080,48 +1088,3 @@ pub fn analyze_match<'p, 'tcx>( Ok(report) } - -// FIXME(deref_patterns): Currently it's the responsibility of the frontend (rustc or rust-analyzer) -// to ensure that deref patterns don't appear in the same column as normal constructors. Deref -// patterns aren't currently implemented in rust-analyzer, but should they be, the columnwise check -// here could be made generic and shared between frontends. -fn detect_mixed_deref_pat_ctors<'p, 'tcx>( - cx: &RustcPatCtxt<'p, 'tcx>, - column: &PatternColumn<'p, RustcPatCtxt<'p, 'tcx>>, -) -> Result<(), ErrorGuaranteed> { - let Some(&ty) = column.head_ty() else { - return Ok(()); - }; - - // Check for a mix of deref patterns and normal constructors. - let mut normal_ctor_span = None; - let mut deref_pat_span = None; - for pat in column.iter() { - match pat.ctor() { - // The analysis can handle mixing deref patterns with wildcards and opaque patterns. - Wildcard | Opaque(_) => {} - DerefPattern(_) => deref_pat_span = Some(pat.data().span), - // Nothing else can be compared to deref patterns in `Constructor::is_covered_by`. - _ => normal_ctor_span = Some(pat.data().span), - } - } - if let Some(normal_constructor_label) = normal_ctor_span - && let Some(deref_pattern_label) = deref_pat_span - { - return Err(cx.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors { - spans: vec![deref_pattern_label, normal_constructor_label], - smart_pointer_ty: ty.inner(), - deref_pattern_label, - normal_constructor_label, - })); - } - - // Specialize and recurse into the patterns' fields. - let set = column.analyze_ctors(cx, &ty)?; - for ctor in set.present { - for specialized_column in column.specialize(cx, &ty, &ctor).iter() { - detect_mixed_deref_pat_ctors(cx, specialized_column)?; - } - } - Ok(()) -} diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index c348cd508f9..fb94b4afebb 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -720,7 +720,7 @@ use tracing::{debug, instrument}; use self::PlaceValidity::*; use crate::constructor::{Constructor, ConstructorSet, IntRange}; use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat}; -use crate::{MatchArm, PatCx, PrivateUninhabitedField}; +use crate::{MatchArm, PatCx, PrivateUninhabitedField, checks}; #[cfg(not(feature = "rustc"))] pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { f() @@ -1836,6 +1836,9 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>( scrut_validity: PlaceValidity, complexity_limit: usize, ) -> Result, Cx::Error> { + // The analysis doesn't support deref patterns mixed with normal constructors; error if present. + checks::detect_mixed_deref_pat_ctors(tycx, arms)?; + let mut cx = UsefulnessCtxt { tycx, branch_usefulness: FxHashMap::default(), diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs index 8980b644f59..0b939ef7816 100644 --- a/compiler/rustc_pattern_analysis/tests/common/mod.rs +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -1,6 +1,7 @@ use rustc_pattern_analysis::constructor::{ Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility, }; +use rustc_pattern_analysis::pat::DeconstructedPat; use rustc_pattern_analysis::usefulness::{PlaceValidity, UsefulnessReport}; use rustc_pattern_analysis::{MatchArm, PatCx, PrivateUninhabitedField}; @@ -184,6 +185,14 @@ impl PatCx for Cx { fn complexity_exceeded(&self) -> Result<(), Self::Error> { Err(()) } + + fn report_mixed_deref_pat_ctors( + &self, + _deref_pat: &DeconstructedPat, + _normal_pat: &DeconstructedPat, + ) -> Self::Error { + panic!("`rustc_pattern_analysis::tests` currently doesn't test deref pattern errors") + } } /// Construct a single pattern; see `pats!()`. 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 7cf22c64d0f..2e408b1fac9 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 @@ -527,6 +527,14 @@ impl PatCx for MatchCheckCtx<'_> { fn complexity_exceeded(&self) -> Result<(), Self::Error> { Err(()) } + + fn report_mixed_deref_pat_ctors( + &self, + _deref_pat: &DeconstructedPat<'_>, + _normal_pat: &DeconstructedPat<'_>, + ) { + // FIXME(deref_patterns): This could report an error comparable to the one in rustc. + } } impl fmt::Debug for MatchCheckCtx<'_> { diff --git a/tests/ui/pattern/box-pattern-constructor-mismatch.rs b/tests/ui/pattern/box-pattern-constructor-mismatch.rs new file mode 100644 index 00000000000..8f0a19d7407 --- /dev/null +++ b/tests/ui/pattern/box-pattern-constructor-mismatch.rs @@ -0,0 +1,11 @@ +//! Test that `box _` patterns and `Box { .. }` patterns can't be used to match on the same place. +//! This is required for the current implementation of exhaustiveness analysis for deref patterns. + +#![feature(box_patterns)] + +fn main() { + match Box::new(0) { + box _ => {} //~ ERROR mix of deref patterns and normal constructors + Box { .. } => {} + } +} diff --git a/tests/ui/pattern/box-pattern-constructor-mismatch.stderr b/tests/ui/pattern/box-pattern-constructor-mismatch.stderr new file mode 100644 index 00000000000..489eefe0d21 --- /dev/null +++ b/tests/ui/pattern/box-pattern-constructor-mismatch.stderr @@ -0,0 +1,10 @@ +error: mix of deref patterns and normal constructors + --> $DIR/box-pattern-constructor-mismatch.rs:8:9 + | +LL | box _ => {} + | ^^^^^ matches on the result of dereferencing `Box` +LL | Box { .. } => {} + | ^^^^^^^^^^ matches directly on `Box` + +error: aborting due to 1 previous error + -- cgit 1.4.1-3-g733a5 From bb643159786c789c0b64f819d36a0979c63f9791 Mon Sep 17 00:00:00 2001 From: dianne Date: Fri, 4 Jul 2025 21:53:03 -0700 Subject: only check for mixed deref/normal constructors when needed --- compiler/rustc_mir_build/src/builder/scope.rs | 1 + compiler/rustc_mir_build/src/thir/pattern/check_match.rs | 1 + compiler/rustc_pattern_analysis/src/lib.rs | 5 +++++ compiler/rustc_pattern_analysis/src/rustc.rs | 16 ++++++++++++++++ compiler/rustc_pattern_analysis/src/usefulness.rs | 4 +++- 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 405d47c7c79..7fd3050012f 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -927,6 +927,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrut_span: rustc_span::Span::default(), refutable: true, known_valid_scrutinee: true, + internal_state: Default::default(), }; let valtree = match self.eval_unevaluated_mir_constant_to_valtree(constant) { 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 b7b160c738d..a49bfc1b8f4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -406,6 +406,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { scrut_span, refutable, known_valid_scrutinee, + internal_state: Default::default(), } } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 129fd38725c..66df35f9ee4 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -109,6 +109,11 @@ pub trait PatCx: Sized + fmt::Debug { ) { } + /// Check if we may need to perform additional deref-pattern-specific validation. + fn match_may_contain_deref_pats(&self) -> bool { + true + } + /// The current implementation of deref patterns requires that they can't match on the same /// place as a normal constructor. Since this isn't caught by type-checking, we check it in the /// `PatCx` before running the analysis. This reports an error if the check fails. diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index e9795126db6..ee72b676b38 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -1,3 +1,4 @@ +use std::cell::Cell; use std::fmt; use std::iter::once; @@ -99,6 +100,16 @@ pub struct RustcPatCtxt<'p, 'tcx: 'p> { /// Whether the data at the scrutinee is known to be valid. This is false if the scrutinee comes /// from a union field, a pointer deref, or a reference deref (pending opsem decisions). pub known_valid_scrutinee: bool, + pub internal_state: RustcPatCtxtState, +} + +/// Private fields of [`RustcPatCtxt`], separated out to permit record initialization syntax. +#[derive(Clone, Default)] +pub struct RustcPatCtxtState { + /// Has a deref pattern been lowered? This is initialized to `false` and is updated by + /// [`RustcPatCtxt::lower_pat`] in order to avoid performing deref-pattern-specific validation + /// for everything containing patterns. + has_lowered_deref_pat: Cell, } impl<'p, 'tcx: 'p> fmt::Debug for RustcPatCtxt<'p, 'tcx> { @@ -474,6 +485,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { fields = vec![self.lower_pat(subpattern).at_index(0)]; arity = 1; ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty)); + self.internal_state.has_lowered_deref_pat.set(true); } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { match ty.kind() { @@ -1028,6 +1040,10 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { } } + fn match_may_contain_deref_pats(&self) -> bool { + self.internal_state.has_lowered_deref_pat.get() + } + fn report_mixed_deref_pat_ctors( &self, deref_pat: &crate::pat::DeconstructedPat, diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index fb94b4afebb..b1c646e9884 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1837,7 +1837,9 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>( complexity_limit: usize, ) -> Result, Cx::Error> { // The analysis doesn't support deref patterns mixed with normal constructors; error if present. - checks::detect_mixed_deref_pat_ctors(tycx, arms)?; + if tycx.match_may_contain_deref_pats() { + checks::detect_mixed_deref_pat_ctors(tycx, arms)?; + } let mut cx = UsefulnessCtxt { tycx, -- cgit 1.4.1-3-g733a5 From c4d9c0f248f05542024ad55a52774ea84406db81 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 5 Jul 2025 01:46:49 -0500 Subject: tests: Use `cfg_target_has_reliable_f16_f128` `conv-bits-runtime-const` gates `f16` and `f128` tests behind `x86_64`, but this isn't always accurate. In particular, x86 `MinGW` has an ABI bug [1] which means things work when linked to our Rust math libraries but don't work with host libraries. RUST-143405 slightly adjusts which targets we provide `f16` and `f128` symbols for and effectively removes MinGW from that list, meaning host libraries start getting linked, meaning `f16` and `f128` tests start to fail. Account for this by changing the gates in one such test to `cfg(target_has_reliable_{f16,f128})` which is the way we should be gating all behavior related to the types going forward. `rustfmt` also seems to have formatted the macros which is fine. [1]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054 --- tests/ui/float/conv-bits-runtime-const.rs | 43 ++++++++++++++----------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/tests/ui/float/conv-bits-runtime-const.rs b/tests/ui/float/conv-bits-runtime-const.rs index 3046728fe66..1373001b74d 100644 --- a/tests/ui/float/conv-bits-runtime-const.rs +++ b/tests/ui/float/conv-bits-runtime-const.rs @@ -5,25 +5,24 @@ #![feature(f16)] #![feature(f128)] +#![feature(cfg_target_has_reliable_f16_f128)] #![allow(unused_macro_rules)] +// expect the unexpected (`target_has_reliable_*` are not "known" configs since they are unstable) +#![expect(unexpected_cfgs)] use std::hint::black_box; macro_rules! both_assert { - ($a:expr) => { - { - const _: () = assert!($a); - // `black_box` prevents promotion, and MIR opts are disabled above, so this is truly - // going through LLVM. - assert!(black_box($a)); - } - }; - ($a:expr, $b:expr) => { - { - const _: () = assert!($a == $b); - assert_eq!(black_box($a), black_box($b)); - } - }; + ($a:expr) => {{ + const _: () = assert!($a); + // `black_box` prevents promotion, and MIR opts are disabled above, so this is truly + // going through LLVM. + assert!(black_box($a)); + }}; + ($a:expr, $b:expr) => {{ + const _: () = assert!($a == $b); + assert_eq!(black_box($a), black_box($b)); + }}; } fn has_broken_floats() -> bool { @@ -31,8 +30,8 @@ fn has_broken_floats() -> bool { cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) } -#[cfg(target_arch = "x86_64")] -fn f16(){ +#[cfg(target_has_reliable_f16)] +fn f16() { both_assert!((1f16).to_bits(), 0x3c00); both_assert!(u16::from_be_bytes(1f16.to_be_bytes()), 0x3c00); both_assert!((12.5f16).to_bits(), 0x4a40); @@ -122,7 +121,7 @@ fn f64() { } } -#[cfg(target_arch = "x86_64")] +#[cfg(target_has_reliable_f128)] fn f128() { both_assert!((1f128).to_bits(), 0x3fff0000000000000000000000000000); both_assert!(u128::from_be_bytes(1f128.to_be_bytes()), 0x3fff0000000000000000000000000000); @@ -154,12 +153,10 @@ fn f128() { } fn main() { + #[cfg(target_has_reliable_f16)] + f16(); f32(); f64(); - - #[cfg(target_arch = "x86_64")] - { - f16(); - f128(); - } + #[cfg(target_has_reliable_f128)] + f128(); } -- cgit 1.4.1-3-g733a5 From 995eeeb54cda2f3c7884e14a82bbe1f279143388 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 5 Jul 2025 17:47:28 +0000 Subject: Don't call predicates_of on a dummy obligation cause's body id --- .../src/error_reporting/traits/ambiguity.rs | 33 +++++++++++++--------- .../ambiguity-in-dropck-err-reporting.rs | 18 ++++++++++++ .../ambiguity-in-dropck-err-reporting.stderr | 19 +++++++++++++ 3 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 tests/ui/traits/error-reporting/ambiguity-in-dropck-err-reporting.rs create mode 100644 tests/ui/traits/error-reporting/ambiguity-in-dropck-err-reporting.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 39f115ce0cd..a8ba1baf6b9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -4,7 +4,7 @@ use rustc_errors::{Applicability, Diag, E0283, E0284, E0790, MultiSpan, struct_s use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::intravisit::Visitor as _; use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; use rustc_infer::traits::util::elaborate; @@ -128,19 +128,26 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>( }, ); - let predicates = - tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx); - for (pred, span) in elaborate(tcx, predicates.into_iter()) { - let kind = pred.kind(); - if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder() - && param_env_candidate_may_apply(kind.rebind(trait_pred)) - { - if kind.rebind(trait_pred.trait_ref) - == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) + // If our `body_id` has been set (and isn't just from a dummy obligation cause), + // then try to look for a param-env clause that would apply. The way we compute + // this is somewhat manual, since we need the spans, so we elaborate this directly + // from `predicates_of` rather than actually looking at the param-env which + // otherwise would be more appropriate. + let body_id = obligation.cause.body_id; + if body_id != CRATE_DEF_ID { + let predicates = tcx.predicates_of(body_id.to_def_id()).instantiate_identity(tcx); + for (pred, span) in elaborate(tcx, predicates.into_iter()) { + let kind = pred.kind(); + if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder() + && param_env_candidate_may_apply(kind.rebind(trait_pred)) { - ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id()))) - } else { - ambiguities.push(CandidateSource::ParamEnv(span)) + if kind.rebind(trait_pred.trait_ref) + == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) + { + ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id()))) + } else { + ambiguities.push(CandidateSource::ParamEnv(span)) + } } } } diff --git a/tests/ui/traits/error-reporting/ambiguity-in-dropck-err-reporting.rs b/tests/ui/traits/error-reporting/ambiguity-in-dropck-err-reporting.rs new file mode 100644 index 00000000000..ad313823fe4 --- /dev/null +++ b/tests/ui/traits/error-reporting/ambiguity-in-dropck-err-reporting.rs @@ -0,0 +1,18 @@ +// Regression test for #143481, where we were calling `predicates_of` on +// a Crate HIR node because we were using a dummy obligation cause's body id +// without checking that it was meaningful first. + +trait Role { + type Inner; +} +struct HandshakeCallback(C); +impl Role for HandshakeCallback { + //~^ ERROR missing generics + type Inner = usize; +} +struct Handshake(R::Inner); +fn accept() -> Handshake> { + todo!() +} + +fn main() {} diff --git a/tests/ui/traits/error-reporting/ambiguity-in-dropck-err-reporting.stderr b/tests/ui/traits/error-reporting/ambiguity-in-dropck-err-reporting.stderr new file mode 100644 index 00000000000..17ace03e891 --- /dev/null +++ b/tests/ui/traits/error-reporting/ambiguity-in-dropck-err-reporting.stderr @@ -0,0 +1,19 @@ +error[E0107]: missing generics for struct `HandshakeCallback` + --> $DIR/ambiguity-in-dropck-err-reporting.rs:9:25 + | +LL | impl Role for HandshakeCallback { + | ^^^^^^^^^^^^^^^^^ expected 1 generic argument + | +note: struct defined here, with 1 generic parameter: `C` + --> $DIR/ambiguity-in-dropck-err-reporting.rs:8:8 + | +LL | struct HandshakeCallback(C); + | ^^^^^^^^^^^^^^^^^ - +help: add missing generic argument + | +LL | impl Role for HandshakeCallback { + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0107`. -- cgit 1.4.1-3-g733a5 From e0e881f8735cbd817a5e286eef232711020b21f4 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sun, 6 Jul 2025 02:09:54 +0200 Subject: Propagate accept-comment-above-attributes to statements Turns out that things like `#[attr]return unsafe { func(); }` didn't work because we weren't checking above attributes on statements, only on blocks! --- clippy_lints/src/undocumented_unsafe_blocks.rs | 20 +++++++++++++++++--- .../undocumented_unsafe_blocks.disabled.stderr | 10 +++++++++- .../undocumented_unsafe_blocks.rs | 11 +++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 92427473a8e..d4f8b1b4e30 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -143,7 +143,8 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if let Some(tail) = block.expr && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) && !tail.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) + && let HasSafetyComment::Yes(pos) = + stmt_has_safety_comment(cx, tail.span, tail.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) { span_lint_and_then( @@ -167,7 +168,8 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { }; if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) + && let HasSafetyComment::Yes(pos) = + stmt_has_safety_comment(cx, stmt.span, stmt.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { span_lint_and_then( @@ -534,7 +536,12 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] -fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment { +fn stmt_has_safety_comment( + cx: &LateContext<'_>, + span: Span, + hir_id: HirId, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { match span_from_macro_expansion_has_safety_comment(cx, span) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, @@ -549,6 +556,13 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H _ => return HasSafetyComment::Maybe, }; + // if span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attrib + // } + let mut span = span; + if accept_comment_above_attributes { + span = include_attrs_in_span(cx, hir_id, span); + } + let source_map = cx.sess().source_map(); if let Some(comment_start) = comment_start && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index e9c5e5f9f11..453bcf1ab33 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -450,5 +450,13 @@ help: consider removing the safety comment LL | // SAFETY: unnecessary_safety_comment triggers here | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 52 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:733:12 + | +LL | return unsafe { h() }; + | ^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 53 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index 91a02bc3d7c..a2d7c1b6c79 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -723,4 +723,15 @@ fn issue_13039() { _ = unsafe { foo() } } +fn rfl_issue15034() -> i32 { + unsafe fn h() -> i32 { + 1i32 + } + // This shouldn't lint with accept-comment-above-attributes! Thus fixing a false positive! + // SAFETY: My safety comment! + #[allow(clippy::unnecessary_cast)] + return unsafe { h() }; + //~[disabled]^ ERROR: unsafe block missing a safety comment +} + fn main() {} -- cgit 1.4.1-3-g733a5 From b49064e9df8c159828502167abcd80aafa5464fa Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sun, 6 Jul 2025 02:21:20 +0200 Subject: Not on vacation, again! --- triagebot.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index b7a8749fbaa..81718413c38 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -52,7 +52,6 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", - "blyxyas", ] [assign.owners] -- cgit 1.4.1-3-g733a5 From 8aad0e6ad9c0a31f458c62529ccbe1708c12b560 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 4 Jul 2025 12:42:33 +0200 Subject: Rewrite empty attribute lint Signed-off-by: Jonathan Brouwer --- clippy_lints/src/arbitrary_source_item_ordering.rs | 2 +- clippy_lints/src/attrs/repr_attributes.rs | 2 +- clippy_lints/src/default_union_representation.rs | 2 +- clippy_utils/src/lib.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index b9ae9afe851..8b6bfaebbe5 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -266,7 +266,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { .tcx .hir_attrs(item.hir_id()) .iter() - .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr(..)))) + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr{ .. }))) { // Do not lint items with a `#[repr]` attribute as their layout may be imposed by an external API. return; diff --git a/clippy_lints/src/attrs/repr_attributes.rs b/clippy_lints/src/attrs/repr_attributes.rs index 05d8a8c26d1..3e8808cec61 100644 --- a/clippy_lints/src/attrs/repr_attributes.rs +++ b/clippy_lints/src/attrs/repr_attributes.rs @@ -9,7 +9,7 @@ use clippy_utils::msrvs::{self, Msrv}; use super::REPR_PACKED_WITHOUT_ABI; pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], msrv: Msrv) { - if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr(r) => r) { + if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr { reprs, .. } => reprs) { let packed_span = reprs .iter() .find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..))) diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 615421f3a40..9bf2144e445 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -99,5 +99,5 @@ fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: ty::GenericArgsR fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); - find_attr!(attrs, AttributeKind::Repr(r) if r.iter().any(|(x, _)| *x == ReprAttr::ReprC)) + find_attr!(attrs, AttributeKind::Repr { reprs, .. } if reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC)) } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a8b33418c8c..c01f0ffaac9 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1761,7 +1761,7 @@ pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool { } pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr(..)) + find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr { .. }) } pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool { -- cgit 1.4.1-3-g733a5 From b63f920ccf6e148547aea4c0fd045c4536aac483 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 6 Jul 2025 17:30:09 +0000 Subject: More carefully consider span context when suggesting remove &mut --- .../src/error_reporting/traits/suggestions.rs | 13 ++++++++----- tests/ui/suggestions/suggest-remove-refs-6.rs | 12 ++++++++++++ tests/ui/suggestions/suggest-remove-refs-6.stderr | 22 ++++++++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 tests/ui/suggestions/suggest-remove-refs-6.rs create mode 100644 tests/ui/suggestions/suggest-remove-refs-6.stderr 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 362052e9fdb..1dc1588842f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1511,12 +1511,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { 'outer: loop { while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind { count += 1; - let span = if expr.span.eq_ctxt(borrowed.span) { - expr.span.until(borrowed.span) - } else { - expr.span.with_hi(expr.span.lo() + BytePos(1)) - }; + let span = + if let Some(borrowed_span) = borrowed.span.find_ancestor_inside(expr.span) { + expr.span.until(borrowed_span) + } else { + break 'outer; + }; + // Double check that the span we extracted actually corresponds to a borrow, + // rather than some macro garbage. match self.tcx.sess.source_map().span_to_snippet(span) { Ok(snippet) if snippet.starts_with("&") => {} _ => break 'outer, diff --git a/tests/ui/suggestions/suggest-remove-refs-6.rs b/tests/ui/suggestions/suggest-remove-refs-6.rs new file mode 100644 index 00000000000..0d06aed4806 --- /dev/null +++ b/tests/ui/suggestions/suggest-remove-refs-6.rs @@ -0,0 +1,12 @@ +// Regression test for #143523. + +trait Trait {} + +impl Trait for Vec {} + +fn foo(_: impl Trait) {} + +fn main() { + foo(&mut vec![1]); + //~^ ERROR the trait bound `&mut Vec<{integer}>: Trait` is not satisfied +} diff --git a/tests/ui/suggestions/suggest-remove-refs-6.stderr b/tests/ui/suggestions/suggest-remove-refs-6.stderr new file mode 100644 index 00000000000..bdc5a8a9049 --- /dev/null +++ b/tests/ui/suggestions/suggest-remove-refs-6.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `&mut Vec<{integer}>: Trait` is not satisfied + --> $DIR/suggest-remove-refs-6.rs:10:9 + | +LL | foo(&mut vec![1]); + | --- ^^^^^^^^^^^^ the trait `Trait` is not implemented for `&mut Vec<{integer}>` + | | + | required by a bound introduced by this call + | +note: required by a bound in `foo` + --> $DIR/suggest-remove-refs-6.rs:7:16 + | +LL | fn foo(_: impl Trait) {} + | ^^^^^ required by this bound in `foo` +help: consider removing the leading `&`-reference + | +LL - foo(&mut vec![1]); +LL + foo(vec![1]); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. -- cgit 1.4.1-3-g733a5 From 09cf02c8af6657ffdf0faba8afd2efa652d299c6 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sun, 6 Jul 2025 20:22:21 +0000 Subject: Simplify num formatting helpers --- library/core/src/num/fmt.rs | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/library/core/src/num/fmt.rs b/library/core/src/num/fmt.rs index ed61197157b..0e4b2844d81 100644 --- a/library/core/src/num/fmt.rs +++ b/library/core/src/num/fmt.rs @@ -22,19 +22,7 @@ impl<'a> Part<'a> { pub fn len(&self) -> usize { match *self { Part::Zero(nzeroes) => nzeroes, - Part::Num(v) => { - if v < 1_000 { - if v < 10 { - 1 - } else if v < 100 { - 2 - } else { - 3 - } - } else { - if v < 10_000 { 4 } else { 5 } - } - } + Part::Num(v) => v.checked_ilog10().unwrap_or_default() as usize + 1, Part::Copy(buf) => buf.len(), } } @@ -82,21 +70,14 @@ pub struct Formatted<'a> { impl<'a> Formatted<'a> { /// Returns the exact byte length of combined formatted result. pub fn len(&self) -> usize { - let mut len = self.sign.len(); - for part in self.parts { - len += part.len(); - } - len + self.sign.len() + self.parts.iter().map(|part| part.len()).sum::() } /// Writes all formatted parts into the supplied buffer. /// Returns the number of written bytes, or `None` if the buffer is not enough. /// (It may still leave partially written bytes in the buffer; do not rely on that.) pub fn write(&self, out: &mut [u8]) -> Option { - if out.len() < self.sign.len() { - return None; - } - out[..self.sign.len()].copy_from_slice(self.sign.as_bytes()); + out.get_mut(..self.sign.len())?.copy_from_slice(self.sign.as_bytes()); let mut written = self.sign.len(); for part in self.parts { -- cgit 1.4.1-3-g733a5 From d8ba94b0ae59b9e140f65bce27527c12357bfbf7 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 4 Jul 2025 00:11:38 +0200 Subject: Fix `manual_is_variant_and` condition generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When comparing `x.map(func) == Some(bool_lit)`, the value of `bool_lit` was ignored, despite the fact that its value should determine the value of the proposed expression. `func` can be either a closure or a path. For the latter, η-expansion will be used if needed to invert the result of the function call. --- clippy_lints/src/methods/manual_is_variant_and.rs | 168 +++++++++++++++++----- tests/ui/manual_is_variant_and.fixed | 112 ++++++++++++++- tests/ui/manual_is_variant_and.rs | 110 ++++++++++++++ tests/ui/manual_is_variant_and.stderr | 100 ++++++++++++- 4 files changed, 454 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/methods/manual_is_variant_and.rs b/clippy_lints/src/methods/manual_is_variant_and.rs index 4a61c223d2c..93325ca488e 100644 --- a/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,14 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, sym}; +use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{Span, Symbol}; use super::MANUAL_IS_VARIANT_AND; @@ -62,54 +64,154 @@ pub(super) fn check( ); } -fn emit_lint(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, method_span: Span, is_option: bool) { - if let Some(before_map_snippet) = snippet_opt(cx, parent.span.with_hi(method_span.lo())) - && let Some(after_map_snippet) = snippet_opt(cx, method_span.with_lo(method_span.lo() + BytePos(3))) - { - span_lint_and_sugg( - cx, - MANUAL_IS_VARIANT_AND, - parent.span, - format!( - "called `.map() {}= {}()`", - if op == BinOpKind::Eq { '=' } else { '!' }, - if is_option { "Some" } else { "Ok" }, - ), - "use", - if is_option && op == BinOpKind::Ne { - format!("{before_map_snippet}is_none_or{after_map_snippet}",) - } else { +#[derive(Clone, Copy, PartialEq)] +enum Flavor { + Option, + Result, +} + +impl Flavor { + const fn symbol(self) -> Symbol { + match self { + Self::Option => sym::Option, + Self::Result => sym::Result, + } + } + + const fn positive(self) -> Symbol { + match self { + Self::Option => sym::Some, + Self::Result => sym::Ok, + } + } +} + +#[derive(Clone, Copy, PartialEq)] +enum Op { + Eq, + Ne, +} + +impl std::fmt::Display for Op { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Eq => write!(f, "=="), + Self::Ne => write!(f, "!="), + } + } +} + +impl TryFrom for Op { + type Error = (); + fn try_from(op: BinOpKind) -> Result { + match op { + BinOpKind::Eq => Ok(Self::Eq), + BinOpKind::Ne => Ok(Self::Ne), + _ => Err(()), + } + } +} + +/// Represents the argument of the `.map()` function, as a closure or as a path +/// in case η-reduction is used. +enum MapFunc<'hir> { + Closure(&'hir Closure<'hir>), + Path(&'hir Expr<'hir>), +} + +impl<'hir> TryFrom<&'hir Expr<'hir>> for MapFunc<'hir> { + type Error = (); + + fn try_from(expr: &'hir Expr<'hir>) -> Result { + match expr.kind { + ExprKind::Closure(closure) => Ok(Self::Closure(closure)), + ExprKind::Path(_) => Ok(Self::Path(expr)), + _ => Err(()), + } + } +} + +impl<'hir> MapFunc<'hir> { + /// Build a suggestion suitable for use in a `.map()`-like function. η-expansion will be applied + /// as needed. + fn sugg(self, cx: &LateContext<'hir>, invert: bool, app: &mut Applicability) -> String { + match self { + Self::Closure(closure) => { + let body = Sugg::hir_with_applicability(cx, cx.tcx.hir_body(closure.body).value, "..", app); format!( - "{}{before_map_snippet}{}{after_map_snippet}", - if op == BinOpKind::Eq { "" } else { "!" }, - if is_option { "is_some_and" } else { "is_ok_and" }, + "{} {}", + snippet_with_applicability(cx, closure.fn_decl_span, "|..|", app), + if invert { !body } else { body } ) }, - Applicability::MachineApplicable, - ); + Self::Path(expr) => { + let path = snippet_with_applicability(cx, expr.span, "_", app); + if invert { + format!("|x| !{path}(x)") + } else { + path.to_string() + } + }, + } } } +fn emit_lint<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + op: Op, + flavor: Flavor, + in_some_or_ok: bool, + map_func: MapFunc<'tcx>, + recv: &Expr<'_>, +) { + let mut app = Applicability::MachineApplicable; + let recv = snippet_with_applicability(cx, recv.span, "_", &mut app); + + let (invert_expr, method, invert_body) = match (flavor, op) { + (Flavor::Option, Op::Eq) => (false, "is_some_and", !in_some_or_ok), + (Flavor::Option, Op::Ne) => (false, "is_none_or", in_some_or_ok), + (Flavor::Result, Op::Eq) => (false, "is_ok_and", !in_some_or_ok), + (Flavor::Result, Op::Ne) => (true, "is_ok_and", !in_some_or_ok), + }; + span_lint_and_sugg( + cx, + MANUAL_IS_VARIANT_AND, + span, + format!("called `.map() {op} {pos}()`", pos = flavor.positive(),), + "use", + format!( + "{inversion}{recv}.{method}({body})", + inversion = if invert_expr { "!" } else { "" }, + body = map_func.sugg(cx, invert_body, &mut app), + ), + app, + ); +} + pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { if let Some(parent_expr) = get_parent_expr(cx, expr) && let ExprKind::Binary(op, left, right) = parent_expr.kind - && matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) && op.span.eq_ctxt(expr.span) + && let Ok(op) = Op::try_from(op.node) { // Check `left` and `right` expression in any order, and for `Option` and `Result` for (expr1, expr2) in [(left, right), (right, left)] { - for item in [sym::Option, sym::Result] { - if let ExprKind::Call(call, ..) = expr1.kind + for flavor in [Flavor::Option, Flavor::Result] { + if let ExprKind::Call(call, [arg]) = expr1.kind + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Bool(bool_cst) = lit.node && let ExprKind::Path(QPath::Resolved(_, path)) = call.kind && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = path.res && let ty = cx.typeck_results().expr_ty(expr1) && let ty::Adt(adt, args) = ty.kind() - && cx.tcx.is_diagnostic_item(item, adt.did()) + && cx.tcx.is_diagnostic_item(flavor.symbol(), adt.did()) && args.type_at(0).is_bool() - && let ExprKind::MethodCall(_, recv, _, span) = expr2.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), item) + && let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), flavor.symbol()) + && let Ok(map_func) = MapFunc::try_from(map_expr) { - return emit_lint(cx, op.node, parent_expr, span, item == sym::Option); + return emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv); } } } diff --git a/tests/ui/manual_is_variant_and.fixed b/tests/ui/manual_is_variant_and.fixed index 6425f32c09c..65a9cfa6e64 100644 --- a/tests/ui/manual_is_variant_and.fixed +++ b/tests/ui/manual_is_variant_and.fixed @@ -61,7 +61,7 @@ fn option_methods() { let _ = Some(2).is_some_and(|x| x % 2 == 0); //~^ manual_is_variant_and - let _ = Some(2).is_none_or(|x| x % 2 == 0); + let _ = Some(2).is_none_or(|x| x % 2 != 0); //~^ manual_is_variant_and let _ = Some(2).is_some_and(|x| x % 2 == 0); //~^ manual_is_variant_and @@ -116,3 +116,113 @@ fn main() { option_methods(); result_methods(); } + +fn issue15202() { + let xs = [None, Some(b'_'), Some(b'1')]; + for x in xs { + let a1 = x.is_none_or(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_none_or(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_some_and(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_some_and(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + let xs = [Err("foo"), Ok(b'_'), Ok(b'1')]; + for x in xs { + let a1 = !x.is_ok_and(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = !x.is_ok_and(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_ok_and(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_ok_and(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } +} + +mod with_func { + fn iad(b: u8) -> bool { + b.is_ascii_digit() + } + + fn check_option(b: Option) { + let a1 = b.is_some_and(iad); + //~^ manual_is_variant_and + let a2 = b.is_some_and(iad); + assert_eq!(a1, a2); + + let a1 = b.is_some_and(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = b.is_some_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.is_none_or(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = b.is_none_or(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.is_none_or(iad); + //~^ manual_is_variant_and + let a2 = b.is_none_or(iad); + assert_eq!(a1, a2); + } + + fn check_result(b: Result) { + let a1 = b.is_ok_and(iad); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = b.is_ok_and(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = !b.is_ok_and(iad); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = !b.is_ok_and(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + } +} diff --git a/tests/ui/manual_is_variant_and.rs b/tests/ui/manual_is_variant_and.rs index e069e97a04d..85b45d654a7 100644 --- a/tests/ui/manual_is_variant_and.rs +++ b/tests/ui/manual_is_variant_and.rs @@ -125,3 +125,113 @@ fn main() { option_methods(); result_methods(); } + +fn issue15202() { + let xs = [None, Some(b'_'), Some(b'1')]; + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Some(true); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Some(false); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Some(true); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Some(false); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + let xs = [Err("foo"), Ok(b'_'), Ok(b'1')]; + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } +} + +mod with_func { + fn iad(b: u8) -> bool { + b.is_ascii_digit() + } + + fn check_option(b: Option) { + let a1 = b.map(iad) == Some(true); + //~^ manual_is_variant_and + let a2 = b.is_some_and(iad); + assert_eq!(a1, a2); + + let a1 = b.map(iad) == Some(false); + //~^ manual_is_variant_and + let a2 = b.is_some_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Some(true); + //~^ manual_is_variant_and + let a2 = b.is_none_or(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Some(false); + //~^ manual_is_variant_and + let a2 = b.is_none_or(iad); + assert_eq!(a1, a2); + } + + fn check_result(b: Result) { + let a1 = b.map(iad) == Ok(true); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = b.map(iad) == Ok(false); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Ok(true); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Ok(false); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + } +} diff --git a/tests/ui/manual_is_variant_and.stderr b/tests/ui/manual_is_variant_and.stderr index f770319a268..da36b5a07d2 100644 --- a/tests/ui/manual_is_variant_and.stderr +++ b/tests/ui/manual_is_variant_and.stderr @@ -54,7 +54,7 @@ error: called `.map() != Some()` --> tests/ui/manual_is_variant_and.rs:70:13 | LL | let _ = Some(2).map(|x| x % 2 == 0) != Some(true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 != 0)` error: called `.map() == Some()` --> tests/ui/manual_is_variant_and.rs:72:13 @@ -126,5 +126,101 @@ error: called `map().unwrap_or_default()` on a `Result` value LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)` -error: aborting due to 15 previous errors +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:132:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| !b.is_ascii_digit())` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:139:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| b.is_ascii_digit())` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:146:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| b.is_ascii_digit())` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:153:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| !b.is_ascii_digit())` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:161:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| b.is_ascii_digit())` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:168:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| !b.is_ascii_digit())` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:175:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| b.is_ascii_digit())` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:182:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| !b.is_ascii_digit())` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:195:18 + | +LL | let a1 = b.map(iad) == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(iad)` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:200:18 + | +LL | let a1 = b.map(iad) == Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(|x| !iad(x))` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:205:18 + | +LL | let a1 = b.map(iad) != Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(|x| !iad(x))` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:210:18 + | +LL | let a1 = b.map(iad) != Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(iad)` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:217:18 + | +LL | let a1 = b.map(iad) == Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(iad)` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:222:18 + | +LL | let a1 = b.map(iad) == Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(|x| !iad(x))` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:227:18 + | +LL | let a1 = b.map(iad) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(iad)` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:232:18 + | +LL | let a1 = b.map(iad) != Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(|x| !iad(x))` + +error: aborting due to 31 previous errors -- cgit 1.4.1-3-g733a5 From 491c8feaabff3c941a30d02c0c2a2622acf257e3 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sun, 6 Jul 2025 13:30:42 -0700 Subject: clippy: migrate BareFn -> FnPtr --- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/lifetimes.rs | 4 ++-- clippy_lints/src/types/type_complexity.rs | 2 +- clippy_utils/src/ast_utils/mod.rs | 2 +- clippy_utils/src/check_proc_macro.rs | 10 +++++----- clippy_utils/src/hir_utils.rs | 14 +++++++------- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 7463d7b5c3b..5099df3fa02 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -824,7 +824,7 @@ impl TyCoercionStability { TyKind::Slice(_) | TyKind::Array(..) | TyKind::Ptr(_) - | TyKind::BareFn(_) + | TyKind::FnPtr(_) | TyKind::Pat(..) | TyKind::Never | TyKind::Tup(_) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 8fe0c9d60f9..caf17c10484 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -13,7 +13,7 @@ use rustc_hir::intravisit::{ walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_unambig_ty, walk_where_predicate, }; use rustc_hir::{ - AmbigArg, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, + AmbigArg, BodyId, FnDecl, FnPtrTy, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeKind, LifetimeParamKind, Node, PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate, WherePredicateKind, lang_items, @@ -480,7 +480,7 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> { fn visit_ty(&mut self, ty: &'tcx Ty<'_, AmbigArg>) { match ty.kind { - TyKind::BareFn(&BareFnTy { decl, .. }) => { + TyKind::FnPtr(&FnPtrTy { decl, .. }) => { let mut sub_visitor = RefVisitor::new(self.cx); sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index 0704653385f..52c6fda8097 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -50,7 +50,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), // function types bring a lot of overhead - TyKind::BareFn(bare) if bare.abi == ExternAbi::Rust => (50 * self.nest, 1), + TyKind::FnPtr(fn_ptr) if fn_ptr.abi == ExternAbi::Rust => (50 * self.nest, 1), TyKind::TraitObject(param_bounds, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index e6396987cc6..42254ec8e92 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -838,7 +838,7 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool { (PinnedRef(ll, l), PinnedRef(rl, r)) => { both(ll.as_ref(), rl.as_ref(), |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty) }, - (BareFn(l), BareFn(r)) => { + (FnPtr(l), FnPtr(r)) => { l.safety == r.safety && eq_ext(&l.ext, &r.ext) && over(&l.generic_params, &r.generic_params, eq_generic_param) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 407e92d88fb..ce61fffe0de 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -372,17 +372,17 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1), TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1), - TyKind::BareFn(bare_fn) => ( - if bare_fn.safety.is_unsafe() { + TyKind::FnPtr(fn_ptr) => ( + if fn_ptr.safety.is_unsafe() { Pat::Str("unsafe") - } else if bare_fn.abi != ExternAbi::Rust { + } else if fn_ptr.abi != ExternAbi::Rust { Pat::Str("extern") } else { Pat::MultiStr(&["fn", "extern"]) }, - match bare_fn.decl.output { + match fn_ptr.decl.output { FnRetTy::DefaultReturn(_) => { - if let [.., ty] = bare_fn.decl.inputs { + if let [.., ty] = fn_ptr.decl.inputs { ty_search_pat(ty).1 } else { Pat::Str("(") diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index c37231d0931..0ca494f16e3 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1283,20 +1283,20 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, - TyKind::BareFn(bfn) => { - bfn.safety.hash(&mut self.s); - bfn.abi.hash(&mut self.s); - for arg in bfn.decl.inputs { + TyKind::FnPtr(fn_ptr) => { + fn_ptr.safety.hash(&mut self.s); + fn_ptr.abi.hash(&mut self.s); + for arg in fn_ptr.decl.inputs { self.hash_ty(arg); } - std::mem::discriminant(&bfn.decl.output).hash(&mut self.s); - match bfn.decl.output { + std::mem::discriminant(&fn_ptr.decl.output).hash(&mut self.s); + match fn_ptr.decl.output { FnRetTy::DefaultReturn(_) => {}, FnRetTy::Return(ty) => { self.hash_ty(ty); }, } - bfn.decl.c_variadic.hash(&mut self.s); + fn_ptr.decl.c_variadic.hash(&mut self.s); }, TyKind::Tup(ty_list) => { for ty in *ty_list { -- cgit 1.4.1-3-g733a5 From f4e2b5c7f542f441d8bbd0cf7e1fdd9d71952431 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Sun, 29 Jun 2025 12:11:51 +0200 Subject: compiler: Parse `p-` specs in datalayout string, allow definition of custom default data address space --- clippy_lints/src/casts/fn_to_numeric_cast.rs | 2 +- clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs | 2 +- clippy_lints/src/casts/utils.rs | 2 +- clippy_lints/src/enum_clike.rs | 2 +- clippy_utils/src/consts.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs index 105477093b5..55e27a05f3c 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, ty::FnDef(..) | ty::FnPtr(..) => { let mut applicability = Applicability::MaybeIncorrect; - if to_nbits >= cx.tcx.data_layout.pointer_size.bits() && !cast_to.is_usize() { + if to_nbits >= cx.tcx.data_layout.pointer_size().bits() && !cast_to.is_usize() { let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index 700b7d0d426..4da79205e20 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); - if to_nbits < cx.tcx.data_layout.pointer_size.bits() { + if to_nbits < cx.tcx.data_layout.pointer_size().bits() { span_lint_and_sugg( cx, FN_TO_NUMERIC_CAST_WITH_TRUNCATION, diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index 318a1646477..d846d78b9ee 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr}; /// integral type. pub(super) fn int_ty_to_nbits(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option { match ty.kind() { - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(tcx.data_layout.pointer_size.bits()), + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(tcx.data_layout.pointer_size().bits()), ty::Int(i) => i.bit_width(), ty::Uint(i) => i.bit_width(), _ => None, diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 098571a5351..c828fc57f76 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -35,7 +35,7 @@ declare_lint_pass!(UnportableVariant => [ENUM_CLIKE_UNPORTABLE_VARIANT]); impl<'tcx> LateLintPass<'tcx> for UnportableVariant { #[expect(clippy::cast_possible_wrap)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if cx.tcx.data_layout.pointer_size.bits() != 64 { + if cx.tcx.data_layout.pointer_size().bits() != 64 { return; } if let ItemKind::Enum(_, _, def) = &item.kind { diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 09299c869dc..ba0376e4d40 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -918,7 +918,7 @@ fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option Date: Mon, 16 Jun 2025 22:11:41 +0300 Subject: Port `#[export_stable]` to the new attribute system --- compiler/rustc_attr_data_structures/src/attributes.rs | 3 +++ .../rustc_attr_data_structures/src/encode_cross_crate.rs | 1 + compiler/rustc_attr_parsing/src/attributes/link_attrs.rs | 13 +++++++++++-- compiler/rustc_attr_parsing/src/context.rs | 3 ++- compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 4 +++- compiler/rustc_passes/src/check_export.rs | 7 ++++--- tests/ui/attributes/malformed-attrs.stderr | 15 +++++++++------ 8 files changed, 34 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 2cbb7270785..8c94bf79dce 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -248,6 +248,9 @@ pub enum AttributeKind { span: Span, }, + /// Represents `#[export_stable]`. + ExportStable, + /// Represents `#[ignore]` Ignore { span: Span, diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index a6ae49d2808..250cb1f1783 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -26,6 +26,7 @@ impl AttributeKind { Deprecation { .. } => Yes, DocComment { .. } => Yes, ExportName { .. } => Yes, + ExportStable => No, Ignore { .. } => No, Inline(..) => No, LinkName { .. } => Yes, diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index e298053ab76..935002be314 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,9 +1,11 @@ use rustc_attr_data_structures::AttributeKind; use rustc_attr_data_structures::AttributeKind::{LinkName, LinkSection}; use rustc_feature::{AttributeTemplate, template}; -use rustc_span::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::attributes::{ + AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, +}; use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics::NullOnLinkSection; @@ -57,3 +59,10 @@ impl SingleAttributeParser for LinkSectionParser { Some(LinkSection { name, span: cx.attr_span }) } } + +pub(crate) struct ExportStableParser; +impl NoArgsAttributeParser for ExportStableParser { + const PATH: &[Symbol] = &[sym::export_stable]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 939f4a6fde7..db66fd5da23 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -22,7 +22,7 @@ use crate::attributes::codegen_attrs::{ use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::link_attrs::{LinkNameParser, LinkSectionParser}; +use crate::attributes::link_attrs::{ExportStableParser, LinkNameParser, LinkSectionParser}; use crate::attributes::lint_helpers::{AsPtrParser, PassByValueParser, PubTransparentParser}; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::must_use::MustUseParser; @@ -145,6 +145,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 67b68e77d2b..47432a3b7ff 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -271,6 +271,7 @@ pub fn check_builtin_meta_item( if matches!( name, sym::inline + | sym::export_stable | sym::may_dangle | sym::rustc_as_ptr | sym::rustc_pub_transparent diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9e4e78c1db6..bf8dc06c7b7 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -204,6 +204,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcLayoutScalarValidRangeStart(_num, attr_span) | AttributeKind::RustcLayoutScalarValidRangeEnd(_num, attr_span), ) => self.check_rustc_layout_scalar_valid_range(*attr_span, span, target), + Attribute::Parsed(AttributeKind::ExportStable) => { + // handled in `check_export` + } Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -346,7 +349,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::cfg_attr | sym::cfg_trace | sym::cfg_attr_trace - | sym::export_stable // handled in `check_export` // need to be fixed | sym::cfi_encoding // FIXME(cfi_encoding) | sym::pointee // FIXME(derive_coerce_pointee) diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index f8f489d7d06..b1f4584c2a8 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -2,6 +2,7 @@ use std::iter; use std::ops::ControlFlow; use rustc_abi::ExternAbi; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -14,7 +15,7 @@ use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Visibility, }; use rustc_session::config::CrateType; -use rustc_span::{Span, sym}; +use rustc_span::Span; use crate::errors::UnexportableItem; @@ -44,7 +45,7 @@ impl<'tcx> ExportableItemCollector<'tcx> { } fn item_is_exportable(&self, def_id: LocalDefId) -> bool { - let has_attr = self.tcx.has_attr(def_id, sym::export_stable); + let has_attr = find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::ExportStable); if !self.in_exportable_mod && !has_attr { return false; } @@ -80,7 +81,7 @@ impl<'tcx> ExportableItemCollector<'tcx> { fn walk_item_with_mod(&mut self, item: &'tcx hir::Item<'tcx>) { let def_id = item.hir_id().owner.def_id; let old_exportable_mod = self.in_exportable_mod; - if self.tcx.get_attr(def_id, sym::export_stable).is_some() { + if find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::ExportStable) { self.in_exportable_mod = true; } let old_seen_exportable_in_mod = std::mem::replace(&mut self.seen_exportable_in_mod, false); diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 2f7bf50ead5..0b42284f90c 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -40,12 +40,6 @@ error: malformed `crate_name` attribute input LL | #[crate_name] | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` -error: malformed `export_stable` attribute input - --> $DIR/malformed-attrs.rs:81:1 - | -LL | #[export_stable = 1] - | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[export_stable]` - error: malformed `coverage` attribute input --> $DIR/malformed-attrs.rs:90:1 | @@ -481,6 +475,15 @@ LL | #[target_feature] | expected this to be a list | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]` +error[E0565]: malformed `export_stable` attribute input + --> $DIR/malformed-attrs.rs:81:1 + | +LL | #[export_stable = 1] + | ^^^^^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[export_stable]` + error[E0539]: malformed `link_name` attribute input --> $DIR/malformed-attrs.rs:86:1 | -- cgit 1.4.1-3-g733a5 From 99a9fe1b22edd82697a315e67392bfb35690152d Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 24 Jun 2025 01:00:30 +0300 Subject: Port `#[ffi_const]` to the new attribute system --- compiler/rustc_attr_data_structures/src/attributes.rs | 3 +++ .../rustc_attr_data_structures/src/encode_cross_crate.rs | 1 + compiler/rustc_attr_parsing/src/attributes/link_attrs.rs | 7 +++++++ compiler/rustc_attr_parsing/src/context.rs | 5 ++++- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 4 +++- compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 6 ++++-- tests/ui/attributes/malformed-attrs.stderr | 15 +++++++++------ 8 files changed, 32 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 8c94bf79dce..4f9ea8be314 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -251,6 +251,9 @@ pub enum AttributeKind { /// Represents `#[export_stable]`. ExportStable, + /// Represents `#[ffi_const]`. + FfiConst(Span), + /// Represents `#[ignore]` Ignore { span: Span, diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 250cb1f1783..e69a543d459 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -27,6 +27,7 @@ impl AttributeKind { DocComment { .. } => Yes, ExportName { .. } => Yes, ExportStable => No, + FfiConst(..) => No, Ignore { .. } => No, Inline(..) => No, LinkName { .. } => Yes, diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 935002be314..74283645a5a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -66,3 +66,10 @@ impl NoArgsAttributeParser for ExportStableParser { const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable; } + +pub(crate) struct FfiConstParser; +impl NoArgsAttributeParser for FfiConstParser { + const PATH: &[Symbol] = &[sym::ffi_const]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiConst; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index db66fd5da23..6544278573b 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -22,7 +22,9 @@ use crate::attributes::codegen_attrs::{ use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::link_attrs::{ExportStableParser, LinkNameParser, LinkSectionParser}; +use crate::attributes::link_attrs::{ + ExportStableParser, FfiConstParser, LinkNameParser, LinkSectionParser, +}; use crate::attributes::lint_helpers::{AsPtrParser, PassByValueParser, PubTransparentParser}; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::must_use::MustUseParser; @@ -146,6 +148,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index ff454427871..72bb9e8fac5 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -203,6 +203,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER, UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER, }, + AttributeKind::FfiConst(_) => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST + } _ => {} } } @@ -214,7 +217,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { match name { sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR, sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, - sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST, sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND, sym::rustc_reallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR, sym::rustc_deallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR, diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 47432a3b7ff..ff5bcdce97b 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -272,6 +272,7 @@ pub fn check_builtin_meta_item( name, sym::inline | sym::export_stable + | sym::ffi_const | sym::may_dangle | sym::rustc_as_ptr | sym::rustc_pub_transparent diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bf8dc06c7b7..8a59b07a4f3 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -207,6 +207,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::ExportStable) => { // handled in `check_export` } + &Attribute::Parsed(AttributeKind::FfiConst(attr_span)) => { + self.check_ffi_const(attr_span, target) + } Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -304,7 +307,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_has_incoherent_inherent_impls(attr, span, target) } [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target), - [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target), [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), [sym::link, ..] => self.check_link(hir_id, attr, span, target), [sym::macro_use, ..] | [sym::macro_escape, ..] => { @@ -1509,7 +1511,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.dcx().emit_err(errors::FfiPureInvalidTarget { attr_span }); return; } - if attrs.iter().any(|a| a.has_name(sym::ffi_const)) { + if find_attr!(attrs, AttributeKind::FfiConst(_)) { // `#[ffi_const]` functions cannot be `#[ffi_pure]` self.dcx().emit_err(errors::BothFfiConstAndPure { attr_span }); } diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 0b42284f90c..908b2ba6314 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -146,12 +146,6 @@ error: malformed `link_ordinal` attribute input LL | #[link_ordinal] | ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_ordinal(ordinal)]` -error: malformed `ffi_const` attribute input - --> $DIR/malformed-attrs.rs:171:5 - | -LL | #[unsafe(ffi_const = 1)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[ffi_const]` - error: malformed `linkage` attribute input --> $DIR/malformed-attrs.rs:173:5 | @@ -540,6 +534,15 @@ LL | #[rustc_layout_scalar_valid_range_end] | expected this to be a list | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]` +error[E0565]: malformed `ffi_const` attribute input + --> $DIR/malformed-attrs.rs:171:5 + | +LL | #[unsafe(ffi_const = 1)] + | ^^^^^^^^^^^^^^^^^^^---^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[ffi_const]` + error[E0565]: malformed `non_exhaustive` attribute input --> $DIR/malformed-attrs.rs:197:1 | -- cgit 1.4.1-3-g733a5 From 5d7771e50d1ed3f4174269c78f06691596e632d3 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 24 Jun 2025 01:10:12 +0300 Subject: Port `#[ffi_pure]` to the new attribute system --- compiler/rustc_attr_data_structures/src/attributes.rs | 3 +++ .../rustc_attr_data_structures/src/encode_cross_crate.rs | 1 + compiler/rustc_attr_parsing/src/attributes/link_attrs.rs | 7 +++++++ compiler/rustc_attr_parsing/src/context.rs | 3 ++- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 2 +- compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 4 +++- tests/ui/attributes/malformed-attrs.stderr | 15 +++++++++------ 8 files changed, 27 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 4f9ea8be314..a004a4ed4d7 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -254,6 +254,9 @@ pub enum AttributeKind { /// Represents `#[ffi_const]`. FfiConst(Span), + /// Represents `#[ffi_pure]`. + FfiPure(Span), + /// Represents `#[ignore]` Ignore { span: Span, diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index e69a543d459..5f0f7152f86 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -28,6 +28,7 @@ impl AttributeKind { ExportName { .. } => Yes, ExportStable => No, FfiConst(..) => No, + FfiPure(..) => No, Ignore { .. } => No, Inline(..) => No, LinkName { .. } => Yes, diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 74283645a5a..42d901041d5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -73,3 +73,10 @@ impl NoArgsAttributeParser for FfiConstParser { const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiConst; } + +pub(crate) struct FfiPureParser; +impl NoArgsAttributeParser for FfiPureParser { + const PATH: &[Symbol] = &[sym::ffi_pure]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6544278573b..f40d69c67a8 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -23,7 +23,7 @@ use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; use crate::attributes::link_attrs::{ - ExportStableParser, FfiConstParser, LinkNameParser, LinkSectionParser, + ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkSectionParser, }; use crate::attributes::lint_helpers::{AsPtrParser, PassByValueParser, PubTransparentParser}; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; @@ -149,6 +149,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 72bb9e8fac5..1791df67803 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -206,6 +206,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { AttributeKind::FfiConst(_) => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST } + AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, _ => {} } } @@ -216,7 +217,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { match name { sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR, - sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND, sym::rustc_reallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR, sym::rustc_deallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR, diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index ff5bcdce97b..2fc0eca1645 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -273,6 +273,7 @@ pub fn check_builtin_meta_item( sym::inline | sym::export_stable | sym::ffi_const + | sym::ffi_pure | sym::may_dangle | sym::rustc_as_ptr | sym::rustc_pub_transparent diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8a59b07a4f3..32e21c99f06 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -210,6 +210,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::FfiConst(attr_span)) => { self.check_ffi_const(attr_span, target) } + &Attribute::Parsed(AttributeKind::FfiPure(attr_span)) => { + self.check_ffi_pure(attr_span, attrs, target) + } Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -306,7 +309,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_has_incoherent_inherent_impls, ..] => { self.check_has_incoherent_inherent_impls(attr, span, target) } - [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target), [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), [sym::link, ..] => self.check_link(hir_id, attr, span, target), [sym::macro_use, ..] | [sym::macro_escape, ..] => { diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 908b2ba6314..5bcb0c4dc0a 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -134,12 +134,6 @@ error: malformed `fundamental` attribute input LL | #[fundamental()] | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[fundamental]` -error: malformed `ffi_pure` attribute input - --> $DIR/malformed-attrs.rs:165:5 - | -LL | #[unsafe(ffi_pure = 1)] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[ffi_pure]` - error: malformed `link_ordinal` attribute input --> $DIR/malformed-attrs.rs:167:5 | @@ -534,6 +528,15 @@ LL | #[rustc_layout_scalar_valid_range_end] | expected this to be a list | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]` +error[E0565]: malformed `ffi_pure` attribute input + --> $DIR/malformed-attrs.rs:165:5 + | +LL | #[unsafe(ffi_pure = 1)] + | ^^^^^^^^^^^^^^^^^^---^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[ffi_pure]` + error[E0565]: malformed `ffi_const` attribute input --> $DIR/malformed-attrs.rs:171:5 | -- cgit 1.4.1-3-g733a5 From 4f0b0b0f01ef3e5a2445993faf8367a4e17b8df0 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Fri, 4 Jul 2025 15:53:03 +0300 Subject: Port `#[rustc_std_internal_symbol]` to the new attribute system --- compiler/rustc_attr_data_structures/src/attributes.rs | 3 +++ .../rustc_attr_data_structures/src/encode_cross_crate.rs | 1 + compiler/rustc_attr_parsing/src/attributes/link_attrs.rs | 7 +++++++ compiler/rustc_attr_parsing/src/context.rs | 2 ++ compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 6 +++--- compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 12 +++++------- 7 files changed, 22 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index a004a4ed4d7..d3ab368bb20 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -335,6 +335,9 @@ pub enum AttributeKind { span: Span, }, + /// Represents `#[rustc_std_internal_symbol]`. + StdInternalSymbol(Span), + /// Represents `#[target_feature(enable = "...")]` TargetFeature(ThinVec<(Symbol, Span)>, Span), diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 5f0f7152f86..7b4885d86c5 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -51,6 +51,7 @@ impl AttributeKind { RustcObjectLifetimeDefault => No, SkipDuringMethodDispatch { .. } => No, Stability { .. } => Yes, + StdInternalSymbol(..) => No, TargetFeature(..) => No, TrackCaller(..) => Yes, Used { .. } => No, diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 42d901041d5..1a66eec870f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -80,3 +80,10 @@ impl NoArgsAttributeParser for FfiPureParser { const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; } + +pub(crate) struct StdInternalSymbolParser; +impl NoArgsAttributeParser for StdInternalSymbolParser { + const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f40d69c67a8..e6e1978b8db 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -24,6 +24,7 @@ use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; use crate::attributes::link_attrs::{ ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkSectionParser, + StdInternalSymbolParser, }; use crate::attributes::lint_helpers::{AsPtrParser, PassByValueParser, PubTransparentParser}; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; @@ -157,6 +158,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, // tidy-alphabetical-end ]; diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 1791df67803..85d01d4f938 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -207,6 +207,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST } AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, + AttributeKind::StdInternalSymbol(_) => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL + } _ => {} } } @@ -223,9 +226,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::rustc_allocator_zeroed => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } - sym::rustc_std_internal_symbol => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL - } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, sym::linkage => { if let Some(val) = attr.value_str() { diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 2fc0eca1645..11424ec3724 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -274,6 +274,7 @@ pub fn check_builtin_meta_item( | sym::export_stable | sym::ffi_const | sym::ffi_pure + | sym::rustc_std_internal_symbol | sym::may_dangle | sym::rustc_as_ptr | sym::rustc_pub_transparent diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 32e21c99f06..66166c53336 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -242,6 +242,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::PassByValue(attr_span)) => { self.check_pass_by_value(attr_span, span, target) } + &Attribute::Parsed(AttributeKind::StdInternalSymbol(attr_span)) => { + self.check_rustc_std_internal_symbol(attr_span, span, target) + } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { @@ -267,9 +270,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ), [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), - [sym::rustc_std_internal_symbol, ..] => { - self.check_rustc_std_internal_symbol(attr, span, target) - } [sym::rustc_no_implicit_autorefs, ..] => { self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } @@ -2220,13 +2220,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_rustc_std_internal_symbol(&self, attr: &Attribute, span: Span, target: Target) { + fn check_rustc_std_internal_symbol(&self, attr_span: Span, span: Span, target: Target) { match target { Target::Fn | Target::Static | Target::ForeignFn | Target::ForeignStatic => {} _ => { - self.tcx - .dcx() - .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span(), span }); + self.tcx.dcx().emit_err(errors::RustcStdInternalSymbol { attr_span, span }); } } } -- cgit 1.4.1-3-g733a5 From 2b93d2cca61ede7af6201dc7a2be7c6957ebfb77 Mon Sep 17 00:00:00 2001 From: Jared Davis Date: Mon, 7 Jul 2025 10:38:29 -0400 Subject: skip exit late lint pass on tests When using the `--test` or `--all-targets` flag, the exit lint should not fail on the main function. --- clippy_lints/src/exit.rs | 4 +++- tests/ui/exit4.rs | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/ui/exit4.rs diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index cc8e4d7d9e2..862dd6d6476 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_entrypoint_fn; use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -43,6 +43,8 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if cx.sess().is_test_crate() { return; } + if let ExprKind::Call(path_expr, [_]) = e.kind && let ExprKind::Path(ref path) = path_expr.kind && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() diff --git a/tests/ui/exit4.rs b/tests/ui/exit4.rs new file mode 100644 index 00000000000..d52cdbce0ea --- /dev/null +++ b/tests/ui/exit4.rs @@ -0,0 +1,8 @@ +//@ check-pass +//@compile-flags: --test + +#![warn(clippy::exit)] + +fn main() { + std::process::exit(0) +} \ No newline at end of file -- cgit 1.4.1-3-g733a5 From 1f36d4df9369b5665e936e812b587e1c6bfa0e28 Mon Sep 17 00:00:00 2001 From: Jared Davis Date: Mon, 7 Jul 2025 11:08:45 -0400 Subject: run `cargo dev fmt` on changes --- clippy_lints/src/exit.rs | 4 +++- tests/ui/exit4.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 862dd6d6476..e6f1b7d8954 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -43,7 +43,9 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if cx.sess().is_test_crate() { return; } + if cx.sess().is_test_crate() { + return; + } if let ExprKind::Call(path_expr, [_]) = e.kind && let ExprKind::Path(ref path) = path_expr.kind diff --git a/tests/ui/exit4.rs b/tests/ui/exit4.rs index d52cdbce0ea..821a26fd78b 100644 --- a/tests/ui/exit4.rs +++ b/tests/ui/exit4.rs @@ -5,4 +5,4 @@ fn main() { std::process::exit(0) -} \ No newline at end of file +} -- cgit 1.4.1-3-g733a5 From b4e68e212ece6896ea574c6345e14d4350c5c517 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Wed, 2 Jul 2025 20:33:28 +0300 Subject: Respect endianness correctly in CheckEnums test suite The endianness can change the test expectation for the enum check. This change is fixing the failing tests on big endian by changing the tests so that they behave the same as on little endian. --- tests/ui/mir/enum/convert_non_enum_break.rs | 20 --------------- tests/ui/mir/enum/convert_non_enum_niche_break.rs | 27 -------------------- tests/ui/mir/enum/convert_non_enum_niche_ok.rs | 29 ---------------------- tests/ui/mir/enum/convert_non_enum_ok.rs | 20 --------------- tests/ui/mir/enum/convert_non_integer_break.rs | 19 ++++++++++++++ .../ui/mir/enum/convert_non_integer_niche_break.rs | 27 ++++++++++++++++++++ tests/ui/mir/enum/convert_non_integer_niche_ok.rs | 29 ++++++++++++++++++++++ tests/ui/mir/enum/convert_non_integer_ok.rs | 19 ++++++++++++++ tests/ui/mir/enum/niche_option_tuple_break.rs | 11 ++++---- tests/ui/mir/enum/with_niche_int_break.rs | 4 +-- 10 files changed, 102 insertions(+), 103 deletions(-) delete mode 100644 tests/ui/mir/enum/convert_non_enum_break.rs delete mode 100644 tests/ui/mir/enum/convert_non_enum_niche_break.rs delete mode 100644 tests/ui/mir/enum/convert_non_enum_niche_ok.rs delete mode 100644 tests/ui/mir/enum/convert_non_enum_ok.rs create mode 100644 tests/ui/mir/enum/convert_non_integer_break.rs create mode 100644 tests/ui/mir/enum/convert_non_integer_niche_break.rs create mode 100644 tests/ui/mir/enum/convert_non_integer_niche_ok.rs create mode 100644 tests/ui/mir/enum/convert_non_integer_ok.rs diff --git a/tests/ui/mir/enum/convert_non_enum_break.rs b/tests/ui/mir/enum/convert_non_enum_break.rs deleted file mode 100644 index de062c39907..00000000000 --- a/tests/ui/mir/enum/convert_non_enum_break.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ run-fail -//@ compile-flags: -C debug-assertions -//@ error-pattern: trying to construct an enum from an invalid value 0x10000 - -#[allow(dead_code)] -#[repr(u32)] -enum Foo { - A, - B, -} - -#[allow(dead_code)] -struct Bar { - a: u16, - b: u16, -} - -fn main() { - let _val: Foo = unsafe { std::mem::transmute::<_, Foo>(Bar { a: 0, b: 1 }) }; -} diff --git a/tests/ui/mir/enum/convert_non_enum_niche_break.rs b/tests/ui/mir/enum/convert_non_enum_niche_break.rs deleted file mode 100644 index 9ff4849c5b1..00000000000 --- a/tests/ui/mir/enum/convert_non_enum_niche_break.rs +++ /dev/null @@ -1,27 +0,0 @@ -//@ run-fail -//@ compile-flags: -C debug-assertions -//@ error-pattern: trying to construct an enum from an invalid value 0x5 - -#[allow(dead_code)] -#[repr(u16)] -enum Mix { - A, - B(u16), -} - -#[allow(dead_code)] -enum Nested { - C(Mix), - D, - E, -} - -#[allow(dead_code)] -struct Bar { - a: u16, - b: u16, -} - -fn main() { - let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 5, b: 0 }) }; -} diff --git a/tests/ui/mir/enum/convert_non_enum_niche_ok.rs b/tests/ui/mir/enum/convert_non_enum_niche_ok.rs deleted file mode 100644 index 24027da5458..00000000000 --- a/tests/ui/mir/enum/convert_non_enum_niche_ok.rs +++ /dev/null @@ -1,29 +0,0 @@ -//@ run-pass -//@ compile-flags: -C debug-assertions - -#[allow(dead_code)] -#[repr(u16)] -enum Mix { - A, - B(u16), -} - -#[allow(dead_code)] -enum Nested { - C(Mix), - D, - E, -} - -#[allow(dead_code)] -struct Bar { - a: u16, - b: u16, -} - -fn main() { - let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 0, b: 0 }) }; - let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 1, b: 0 }) }; - let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 2, b: 0 }) }; - let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 3, b: 0 }) }; -} diff --git a/tests/ui/mir/enum/convert_non_enum_ok.rs b/tests/ui/mir/enum/convert_non_enum_ok.rs deleted file mode 100644 index 37fc64342ca..00000000000 --- a/tests/ui/mir/enum/convert_non_enum_ok.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ run-pass -//@ compile-flags: -C debug-assertions - -#[allow(dead_code)] -#[repr(u32)] -enum Foo { - A, - B, -} - -#[allow(dead_code)] -struct Bar { - a: u16, - b: u16, -} - -fn main() { - let _val: Foo = unsafe { std::mem::transmute::<_, Foo>(Bar { a: 0, b: 0 }) }; - let _val: Foo = unsafe { std::mem::transmute::<_, Foo>(Bar { a: 1, b: 0 }) }; -} diff --git a/tests/ui/mir/enum/convert_non_integer_break.rs b/tests/ui/mir/enum/convert_non_integer_break.rs new file mode 100644 index 00000000000..29795190bf6 --- /dev/null +++ b/tests/ui/mir/enum/convert_non_integer_break.rs @@ -0,0 +1,19 @@ +//@ run-fail +//@ compile-flags: -C debug-assertions +//@ error-pattern: trying to construct an enum from an invalid value + +#[allow(dead_code)] +#[repr(u32)] +enum Foo { + A, + B, +} + +#[allow(dead_code)] +struct Bar { + a: u32, +} + +fn main() { + let _val: Foo = unsafe { std::mem::transmute::<_, Foo>(Bar { a: 3 }) }; +} diff --git a/tests/ui/mir/enum/convert_non_integer_niche_break.rs b/tests/ui/mir/enum/convert_non_integer_niche_break.rs new file mode 100644 index 00000000000..9ff4849c5b1 --- /dev/null +++ b/tests/ui/mir/enum/convert_non_integer_niche_break.rs @@ -0,0 +1,27 @@ +//@ run-fail +//@ compile-flags: -C debug-assertions +//@ error-pattern: trying to construct an enum from an invalid value 0x5 + +#[allow(dead_code)] +#[repr(u16)] +enum Mix { + A, + B(u16), +} + +#[allow(dead_code)] +enum Nested { + C(Mix), + D, + E, +} + +#[allow(dead_code)] +struct Bar { + a: u16, + b: u16, +} + +fn main() { + let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 5, b: 0 }) }; +} diff --git a/tests/ui/mir/enum/convert_non_integer_niche_ok.rs b/tests/ui/mir/enum/convert_non_integer_niche_ok.rs new file mode 100644 index 00000000000..24027da5458 --- /dev/null +++ b/tests/ui/mir/enum/convert_non_integer_niche_ok.rs @@ -0,0 +1,29 @@ +//@ run-pass +//@ compile-flags: -C debug-assertions + +#[allow(dead_code)] +#[repr(u16)] +enum Mix { + A, + B(u16), +} + +#[allow(dead_code)] +enum Nested { + C(Mix), + D, + E, +} + +#[allow(dead_code)] +struct Bar { + a: u16, + b: u16, +} + +fn main() { + let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 0, b: 0 }) }; + let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 1, b: 0 }) }; + let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 2, b: 0 }) }; + let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 3, b: 0 }) }; +} diff --git a/tests/ui/mir/enum/convert_non_integer_ok.rs b/tests/ui/mir/enum/convert_non_integer_ok.rs new file mode 100644 index 00000000000..c9831531386 --- /dev/null +++ b/tests/ui/mir/enum/convert_non_integer_ok.rs @@ -0,0 +1,19 @@ +//@ run-pass +//@ compile-flags: -C debug-assertions + +#[allow(dead_code)] +#[repr(u32)] +enum Foo { + A, + B, +} + +#[allow(dead_code)] +struct Bar { + a: u32, +} + +fn main() { + let _val: Foo = unsafe { std::mem::transmute::<_, Foo>(Bar { a: 0 }) }; + let _val: Foo = unsafe { std::mem::transmute::<_, Foo>(Bar { a: 1 }) }; +} diff --git a/tests/ui/mir/enum/niche_option_tuple_break.rs b/tests/ui/mir/enum/niche_option_tuple_break.rs index 43eef3a4cc5..affdc4784a3 100644 --- a/tests/ui/mir/enum/niche_option_tuple_break.rs +++ b/tests/ui/mir/enum/niche_option_tuple_break.rs @@ -1,8 +1,9 @@ //@ run-fail //@ compile-flags: -C debug-assertions -//@ error-pattern: trying to construct an enum from an invalid value 0x3 +//@ error-pattern: trying to construct an enum from an invalid value #[allow(dead_code)] +#[repr(u32)] enum Foo { A, B, @@ -10,11 +11,11 @@ enum Foo { #[allow(dead_code)] struct Bar { - a: usize, - b: usize, + a: u32, + b: u32, } fn main() { - let _val: Option<(usize, Foo)> = - unsafe { std::mem::transmute::<_, Option<(usize, Foo)>>(Bar { a: 3, b: 3 }) }; + let _val: Option<(u32, Foo)> = + unsafe { std::mem::transmute::<_, Option<(u32, Foo)>>(Bar { a: 3, b: 3 }) }; } diff --git a/tests/ui/mir/enum/with_niche_int_break.rs b/tests/ui/mir/enum/with_niche_int_break.rs index 0ec60a33564..6a97eaa8f4f 100644 --- a/tests/ui/mir/enum/with_niche_int_break.rs +++ b/tests/ui/mir/enum/with_niche_int_break.rs @@ -1,6 +1,6 @@ //@ run-fail //@ compile-flags: -C debug-assertions -//@ error-pattern: trying to construct an enum from an invalid value 0x4 +//@ error-pattern: trying to construct an enum from an invalid value #[allow(dead_code)] #[repr(u16)] @@ -17,5 +17,5 @@ enum Nested { } fn main() { - let _val: Nested = unsafe { std::mem::transmute::(4) }; + let _val: Nested = unsafe { std::mem::transmute::(u32::MAX) }; } -- cgit 1.4.1-3-g733a5 From 429deed03a1aa6814ef0431c2662973cf4942462 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 7 Jul 2025 17:44:36 +0200 Subject: Port #[rustc_dummy] --- compiler/rustc_attr_data_structures/src/attributes.rs | 3 +++ .../src/encode_cross_crate.rs | 1 + compiler/rustc_attr_parsing/src/attributes/dummy.rs | 19 +++++++++++++++++++ compiler/rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 2 ++ compiler/rustc_passes/src/check_attr.rs | 3 ++- 6 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/dummy.rs diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 2cbb7270785..f9da7fa0b3a 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -240,6 +240,9 @@ pub enum AttributeKind { /// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html). DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol }, + /// Represents `#[rustc_dummy]`. + Dummy, + /// Represents [`#[export_name]`](https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute). ExportName { /// The name to export this item with. diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index a6ae49d2808..34eaa5eff14 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -25,6 +25,7 @@ impl AttributeKind { ConstStabilityIndirect => No, Deprecation { .. } => Yes, DocComment { .. } => Yes, + Dummy => No, ExportName { .. } => Yes, Ignore { .. } => No, Inline(..) => No, diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs new file mode 100644 index 00000000000..8a9b94fb1ff --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs @@ -0,0 +1,19 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Symbol, sym}; + +use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; + +pub(crate) struct DummyParser; +impl SingleAttributeParser for DummyParser { + const PATH: &[Symbol] = &[sym::rustc_dummy]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; + const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really + + fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser<'_>) -> Option { + Some(AttributeKind::Dummy) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index ba7572434df..c6ea15b2338 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -30,6 +30,7 @@ pub(crate) mod cfg; pub(crate) mod codegen_attrs; pub(crate) mod confusables; pub(crate) mod deprecation; +pub(crate) mod dummy; pub(crate) mod inline; pub(crate) mod link_attrs; pub(crate) mod lint_helpers; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 939f4a6fde7..6ebd01f10c6 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -21,6 +21,7 @@ use crate::attributes::codegen_attrs::{ }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; +use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; use crate::attributes::link_attrs::{LinkNameParser, LinkSectionParser}; use crate::attributes::lint_helpers::{AsPtrParser, PassByValueParser, PubTransparentParser}; @@ -127,6 +128,7 @@ attribute_parsers!( // tidy-alphabetical-start Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9e4e78c1db6..12986c75631 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -207,7 +207,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect - | AttributeKind::MacroTransparency(_), + | AttributeKind::MacroTransparency(_) + | AttributeKind::Dummy, ) => { /* do nothing */ } Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => { self.check_applied_to_fn_or_method(hir_id, *attr_span, span, target) -- cgit 1.4.1-3-g733a5 From 7ec8c89c1501881d3435556148cc27e4cefa43b8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 7 Jul 2025 10:42:08 -0700 Subject: Update intro blurb in `wasm32-wasip1` docs I was reading over this documentation in light of the effort to enlist more maintainers for Tier 2 targets and figured it was time for a refresh of this documentation now that historical renames/etc have all become a thing of the past. No new major changes to this documentation, mostly just wanted to update it and reflect the modern status quo for this target. --- .../rustc/src/platform-support/wasm32-wasip1.md | 48 +++++++++++----------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md index 4f065a554cf..1e7447698bc 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md @@ -4,37 +4,35 @@ The `wasm32-wasip1` target is a WebAssembly compilation target which assumes that the [WASIp1] (aka "WASI preview1") set of "syscalls" are available -for use in the standard library. Historically this target in the Rust compiler -was one of the first for WebAssembly where Rust and C code are explicitly -intended to interoperate as well. - -There's a bit of history to the target and current development which is also -worth explaining before going much further. Historically this target was -originally called `wasm32-wasi` in both rustc and Clang. This was first added -to Rust in 2019. In the intervening years leading up to 2024 the WASI standard -continued to be developed and was eventually "rebased" on top of the [Component -Model]. This was a large change to the WASI specification and was released as -0.2.0 ("WASIp2" colloquially) in January 2024. The previous target's name in -rustc, `wasm32-wasi`, was then renamed to `wasm32-wasip1`, to avoid -confusion with this new target to be added to rustc as `wasm32-wasip2`. -Some more context can be found in these MCPs: - -* [Rename wasm32-wasi target to wasm32-wasip1](https://github.com/rust-lang/compiler-team/issues/607) -* [Smooth the renaming transition of wasm32-wasi](https://github.com/rust-lang/compiler-team/issues/695) - -At this point the `wasm32-wasip1` target is intended for historical -compatibility with the first version of the WASI standard. As of now (January -2024) the 0.2.0 target of WASI ("WASIp2") is relatively new. The state of -WASI will likely change in few years after which point this documentation will -probably receive another update. - -[WASI Preview1]: https://github.com/WebAssembly/WASI/tree/main/legacy/preview1 +for use in the standard library. This target explicitly supports interop with +non-Rust code such as C and C++. + +The [WASIp1] set of syscalls is standard insofar as it was written down once by +a set of folks and has not changed since then. Additionally the [WASIp1] +syscalls have been adapted and adopted into a number of runtimes and embeddings. +It is not standard in the sense that there are no formal semantics for each +syscall and APIs are no longer receiving any maintenance (e.g. no new APIs, no +new documentation, etc). After [WASIp1] was originally developed in 2019 the +WASI standard effort has since been "rebased" on top of the [Component Model]. +This was a large change to the WASI specification and was released as 0.2.0 +("WASIp2" colloquially) in January 2024. Current standardization efforts are +focused on the Component Model-based definition of WASI. At this point the +`wasm32-wasip1` Rust target is intended for historical compatibility with +[WASIp1] set of syscalls. + +[WASIp1]: https://github.com/WebAssembly/WASI/tree/main/legacy/preview1 [Component Model]: https://github.com/webassembly/component-model Today the `wasm32-wasip1` target will generate core WebAssembly modules which will import functions from the `wasi_snapshot_preview1` module for OS-related functionality (e.g. printing). +> **Note**: Prior to March 2024 this target was known as `wasm32-wasi` with some +> historical context found in old MCPs: +> +> * [Rename wasm32-wasi target to wasm32-wasip1](https://github.com/rust-lang/compiler-team/issues/607) +> * [Smooth the renaming transition of wasm32-wasi](https://github.com/rust-lang/compiler-team/issues/695) + ## Target maintainers When this target was added to the compiler platform-specific documentation here -- cgit 1.4.1-3-g733a5 From 6254afa5f20ea9c6aa6e13555363d81f112c01cd Mon Sep 17 00:00:00 2001 From: Anne Stijns Date: Mon, 7 Jul 2025 20:22:56 +0200 Subject: Clarify the meaning of `AttributeOrder::KeepFirst` and `AttributeOrder::KeepLast` --- compiler/rustc_attr_parsing/src/attributes/mod.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index ba7572434df..d466d946cc0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -217,7 +217,14 @@ impl OnDuplicate { // them will be merged in another PR #[allow(unused)] pub(crate) enum AttributeOrder { - /// Duplicates after the first attribute will be an error. + /// Duplicates after the first attribute will be an error. I.e. only keep the lowest attribute. + /// + /// Attributes are processed from bottom to top, so this raises an error on all the attributes + /// further above the lowest one: + /// ``` + /// #[stable(since="1.0")] //~ WARNING duplicated attribute + /// #[stable(since="2.0")] + /// ``` /// /// This should be used where duplicates would be ignored, but carry extra /// meaning that could cause confusion. For example, `#[stable(since="1.0")] @@ -227,6 +234,13 @@ pub(crate) enum AttributeOrder { /// Duplicates preceding the last instance of the attribute will be a /// warning, with a note that this will be an error in the future. /// + /// Attributes are processed from bottom to top, so this raises a warning on all the attributes + /// below the higher one: + /// ``` + /// #[path="foo.rs"] + /// #[path="bar.rs"] //~ WARNING duplicated attribute + /// ``` + /// /// This is the same as `FutureWarnFollowing`, except the last attribute is /// the one that is "used". Ideally these can eventually migrate to /// `ErrorPreceding`. -- cgit 1.4.1-3-g733a5 From e2891c0fb9060f80e6aa24e0dc0a9c43b0861b8f Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 7 Jul 2025 14:54:50 -0400 Subject: configure.py: Write last key in each section The loop that writes the keys in each section of bootstrap.toml accumulates all the commented lines before a given key and emits them when it reaches the next key in the section. This ends up dropping lines accumulated for the last key --- src/bootstrap/configure.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index c077555b906..bec7a1c41b4 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -752,6 +752,10 @@ def write_uncommented(target, f): is_comment = True continue is_comment = is_comment and line.startswith("#") + # Write the last accumulated block + if len(block) > 0 and not is_comment: + for ln in block: + f.write(ln + "\n") return f -- cgit 1.4.1-3-g733a5 From c000a0136ccec78d3a7d6cf5ad0422eeb13bf9eb Mon Sep 17 00:00:00 2001 From: Ses Goe Date: Mon, 7 Jul 2025 15:08:38 -0400 Subject: fix: check against 'main' function name instead of entrypoint function update docs to be a bit more clear --- clippy_lints/src/exit.rs | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index e6f1b7d8954..0f63b36d5d2 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_entrypoint_fn; use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -7,7 +6,8 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Detects calls to the `exit()` function which terminates the program. + /// Detects calls to the `exit()` function that are not in the `main` function. Calls to `exit()` + /// immediately terminate the program. /// /// ### Why restrict this? /// `exit()` immediately terminates the program with no information other than an exit code. @@ -15,11 +15,24 @@ declare_clippy_lint! { /// /// Codebases may use this lint to require that all exits are performed either by panicking /// (which produces a message, a code location, and optionally a backtrace) - /// or by returning from `main()` (which is a single place to look). + /// or by calling `exit()` from `main()` (which is a single place to look). /// - /// ### Example + /// ### Good example /// ```no_run - /// std::process::exit(0) + /// fn main() { + /// std::process::exit(0); + /// } + /// ``` + /// + /// ### Bad example + /// ```no_run + /// fn main() { + /// other_function(); + /// } + /// + /// fn other_function() { + /// std::process::exit(0); + /// } /// ``` /// /// Use instead: @@ -36,7 +49,7 @@ declare_clippy_lint! { #[clippy::version = "1.41.0"] pub EXIT, restriction, - "detects `std::process::exit` calls" + "detects `std::process::exit` calls outside of `main`" } declare_lint_pass!(Exit => [EXIT]); @@ -52,10 +65,14 @@ impl<'tcx> LateLintPass<'tcx> for Exit { && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) && let parent = cx.tcx.hir_get_parent_item(e.hir_id) - && let OwnerNode::Item(Item{kind: ItemKind::Fn{ .. }, ..}) = cx.tcx.hir_owner_node(parent) - // If the next item up is a function we check if it is an entry point + && let OwnerNode::Item(Item{kind: ItemKind::Fn{ ident, .. }, ..}) = cx.tcx.hir_owner_node(parent) + // If the next item up is a function we check if it isn't named "main" // and only then emit a linter warning - && !is_entrypoint_fn(cx, parent.to_def_id()) + + // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before, + // in compilation contexts like --all-targets (which include --tests), you get false positives + // because in a test context, main is not the entrypoint function + && ident.name.as_str() != "main" { span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } -- cgit 1.4.1-3-g733a5 From 5853e6fa7e951a880dea88fc1076ef43827e6c32 Mon Sep 17 00:00:00 2001 From: Ses Goe Date: Mon, 7 Jul 2025 15:08:38 -0400 Subject: chore: add tests to check against the --test compile flag --- tests/ui/exit1_compile_flag_test.rs | 17 +++++++++++++++++ tests/ui/exit1_compile_flag_test.stderr | 11 +++++++++++ tests/ui/exit2_compile_flag_test.rs | 15 +++++++++++++++ tests/ui/exit2_compile_flag_test.stderr | 11 +++++++++++ tests/ui/exit3_compile_flag_test.rs | 11 +++++++++++ 5 files changed, 65 insertions(+) create mode 100644 tests/ui/exit1_compile_flag_test.rs create mode 100644 tests/ui/exit1_compile_flag_test.stderr create mode 100644 tests/ui/exit2_compile_flag_test.rs create mode 100644 tests/ui/exit2_compile_flag_test.stderr create mode 100644 tests/ui/exit3_compile_flag_test.rs diff --git a/tests/ui/exit1_compile_flag_test.rs b/tests/ui/exit1_compile_flag_test.rs new file mode 100644 index 00000000000..9f83ed32533 --- /dev/null +++ b/tests/ui/exit1_compile_flag_test.rs @@ -0,0 +1,17 @@ +//@compile-flags: --test +#![warn(clippy::exit)] + +fn not_main() { + if true { + std::process::exit(4); + //~^ exit + } +} + +fn main() { + if true { + std::process::exit(2); + }; + not_main(); + std::process::exit(1); +} diff --git a/tests/ui/exit1_compile_flag_test.stderr b/tests/ui/exit1_compile_flag_test.stderr new file mode 100644 index 00000000000..6e33c39f0d7 --- /dev/null +++ b/tests/ui/exit1_compile_flag_test.stderr @@ -0,0 +1,11 @@ +error: usage of `process::exit` + --> tests/ui/exit1_compile_flag_test.rs:6:9 + | +LL | std::process::exit(4); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::exit)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/exit2_compile_flag_test.rs b/tests/ui/exit2_compile_flag_test.rs new file mode 100644 index 00000000000..0b994ebc56c --- /dev/null +++ b/tests/ui/exit2_compile_flag_test.rs @@ -0,0 +1,15 @@ +//@compile-flags: --test +#![warn(clippy::exit)] + +fn also_not_main() { + std::process::exit(3); + //~^ exit +} + +fn main() { + if true { + std::process::exit(2); + }; + also_not_main(); + std::process::exit(1); +} diff --git a/tests/ui/exit2_compile_flag_test.stderr b/tests/ui/exit2_compile_flag_test.stderr new file mode 100644 index 00000000000..51eb26e9c2a --- /dev/null +++ b/tests/ui/exit2_compile_flag_test.stderr @@ -0,0 +1,11 @@ +error: usage of `process::exit` + --> tests/ui/exit2_compile_flag_test.rs:5:5 + | +LL | std::process::exit(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::exit)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/exit3_compile_flag_test.rs b/tests/ui/exit3_compile_flag_test.rs new file mode 100644 index 00000000000..f8131ead2da --- /dev/null +++ b/tests/ui/exit3_compile_flag_test.rs @@ -0,0 +1,11 @@ +//@ check-pass +//@compile-flags: --test + +#![warn(clippy::exit)] + +fn main() { + if true { + std::process::exit(2); + }; + std::process::exit(1); +} -- cgit 1.4.1-3-g733a5 From b6d21308672c9a0aa5c73beeb1d528aab3d347ed Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 7 Jul 2025 15:45:18 -0400 Subject: Add docstring --- src/bootstrap/configure.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index bec7a1c41b4..94e02f942dc 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -739,6 +739,10 @@ def configure_file(sections, top_level_keys, targets, config): def write_uncommented(target, f): + """Writes each block in 'target' that is not composed entirely of comments to 'f'. + + A block is a sequence of non-empty lines separated by empty lines. + """ block = [] is_comment = True -- cgit 1.4.1-3-g733a5 From 1c7fb0ff918f39621c0e1b27c89f6a9a6e8efc70 Mon Sep 17 00:00:00 2001 From: Jared Davis Date: Mon, 7 Jul 2025 15:59:38 -0400 Subject: do not completely suppress warning with test code --- clippy_lints/src/exit.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 0f63b36d5d2..248094a0b25 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -56,10 +56,6 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if cx.sess().is_test_crate() { - return; - } - if let ExprKind::Call(path_expr, [_]) = e.kind && let ExprKind::Path(ref path) = path_expr.kind && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() -- cgit 1.4.1-3-g733a5 From 9580d61dc52b08e2986eb4005f2cd991f112718e Mon Sep 17 00:00:00 2001 From: Jared Davis Date: Mon, 7 Jul 2025 16:05:03 -0400 Subject: remove no longer used import --- clippy_lints/src/exit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 248094a0b25..a43d788fc16 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; -- cgit 1.4.1-3-g733a5 From 81dd4e61ed8b63d3398a4bdee033edccc75d2994 Mon Sep 17 00:00:00 2001 From: Jared Davis Date: Mon, 7 Jul 2025 16:17:19 -0400 Subject: use preinterned symbol fixes https://github.com/rust-lang/rust-clippy/actions/runs/16126736130/job/45505355614?pr=15222#logs --- clippy_lints/src/exit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index a43d788fc16..487db69027a 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for Exit { // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before, // in compilation contexts like --all-targets (which include --tests), you get false positives // because in a test context, main is not the entrypoint function - && ident.name.as_str() != "main" + && ident.name != sym::main { span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } -- cgit 1.4.1-3-g733a5 From c3301503b97cd6b768a944661b81a92994e9db00 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Sat, 21 Dec 2024 20:25:43 +0000 Subject: Make `Default` const and add some `const Default` impls Full list of `impl const Default` types: - () - bool - char - Cell - std::ascii::Char - usize - u8 - u16 - u32 - u64 - u128 - i8 - i16 - i32 - i64 - i128 - f16 - f32 - f64 - f128 - std::marker::PhantomData - Option - std::iter::Empty - std::ptr::Alignment - &[T] - &mut [T] - &str - &mut str - String - Vec --- library/alloc/src/lib.rs | 2 + library/alloc/src/string.rs | 3 +- library/alloc/src/vec/mod.rs | 3 +- library/core/src/cell.rs | 12 +++-- library/core/src/default.rs | 5 +- library/core/src/iter/sources/empty.rs | 3 +- library/core/src/marker.rs | 3 +- library/core/src/option.rs | 3 +- library/core/src/ptr/alignment.rs | 3 +- library/core/src/slice/mod.rs | 6 ++- library/core/src/str/mod.rs | 6 ++- src/tools/clippy/tests/ui/or_fun_call.fixed | 4 +- src/tools/clippy/tests/ui/or_fun_call.rs | 2 +- src/tools/clippy/tests/ui/or_fun_call.stderr | 8 +-- tests/ui/consts/rustc-impl-const-stability.rs | 8 +-- tests/ui/consts/rustc-impl-const-stability.stderr | 6 +-- .../lint/dead-code/unused-struct-derive-default.rs | 4 +- tests/ui/specialization/const_trait_impl.rs | 8 +-- tests/ui/specialization/const_trait_impl.stderr | 60 +++++++++++----------- tests/ui/traits/const-traits/const-traits-alloc.rs | 9 ++++ tests/ui/traits/const-traits/const-traits-core.rs | 46 +++++++++++++++++ .../const_derives/derive-const-gate.rs | 5 +- .../const_derives/derive-const-gate.stderr | 21 +++++--- .../const_derives/derive-const-non-const-type.rs | 8 +-- .../derive-const-non-const-type.stderr | 18 +++---- .../const-traits/const_derives/derive-const-use.rs | 2 +- .../const_derives/derive-const-use.stderr | 57 +------------------- .../traits/const-traits/std-impl-gate.gated.stderr | 18 ------- tests/ui/traits/const-traits/std-impl-gate.rs | 7 +-- .../traits/const-traits/std-impl-gate.stock.stderr | 20 ++++++-- 30 files changed, 192 insertions(+), 168 deletions(-) create mode 100644 tests/ui/traits/const-traits/const-traits-alloc.rs create mode 100644 tests/ui/traits/const-traits/const-traits-core.rs delete mode 100644 tests/ui/traits/const-traits/std-impl-gate.gated.stderr diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 4290bb7a8a9..4ad65e678c3 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -107,8 +107,10 @@ #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(coerce_unsized)] +#![feature(const_default)] #![feature(const_eval_select)] #![feature(const_heap)] +#![feature(const_trait_impl)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 5f69f699867..84b82423202 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2611,7 +2611,8 @@ impl_eq! { Cow<'a, str>, &'b str } impl_eq! { Cow<'a, str>, String } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for String { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for String { /// Creates an empty `String`. #[inline] fn default() -> String { diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index c8341750f4d..9dac58bac4f 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3895,7 +3895,8 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for Vec { } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for Vec { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for Vec { /// Creates an empty `Vec`. /// /// The vector will not allocate until elements are pushed onto it. diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index dfed8a00b7d..f7ea1f37a39 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -333,7 +333,8 @@ impl Clone for Cell { } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for Cell { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for Cell { /// Creates a `Cell`, with the `Default` value for T. #[inline] fn default() -> Cell { @@ -1323,7 +1324,8 @@ impl Clone for RefCell { } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for RefCell { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for RefCell { /// Creates a `RefCell`, with the `Default` value for T. #[inline] fn default() -> RefCell { @@ -2330,7 +2332,8 @@ impl UnsafeCell { } #[stable(feature = "unsafe_cell_default", since = "1.10.0")] -impl Default for UnsafeCell { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for UnsafeCell { /// Creates an `UnsafeCell`, with the `Default` value for T. fn default() -> UnsafeCell { UnsafeCell::new(Default::default()) @@ -2434,7 +2437,8 @@ impl SyncUnsafeCell { } #[unstable(feature = "sync_unsafe_cell", issue = "95439")] -impl Default for SyncUnsafeCell { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for SyncUnsafeCell { /// Creates an `SyncUnsafeCell`, with the `Default` value for T. fn default() -> SyncUnsafeCell { SyncUnsafeCell::new(Default::default()) diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 0a15cedfb55..4d108b0c18e 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -103,6 +103,8 @@ use crate::ascii::Char as AsciiChar; /// ``` #[rustc_diagnostic_item = "Default"] #[stable(feature = "rust1", since = "1.0.0")] +#[const_trait] +#[rustc_const_unstable(feature = "const_default", issue = "67792")] pub trait Default: Sized { /// Returns the "default value" for a type. /// @@ -149,7 +151,8 @@ pub macro Default($item:item) { macro_rules! default_impl { ($t:ty, $v:expr, $doc:tt) => { #[stable(feature = "rust1", since = "1.0.0")] - impl Default for $t { + #[rustc_const_unstable(feature = "const_default", issue = "67792")] + impl const Default for $t { #[inline(always)] #[doc = $doc] fn default() -> $t { diff --git a/library/core/src/iter/sources/empty.rs b/library/core/src/iter/sources/empty.rs index 3c3acceded8..309962ab70c 100644 --- a/library/core/src/iter/sources/empty.rs +++ b/library/core/src/iter/sources/empty.rs @@ -81,7 +81,8 @@ impl Clone for Empty { // not #[derive] because that adds a Default bound on T, // which isn't necessary. #[stable(feature = "iter_empty", since = "1.2.0")] -impl Default for Empty { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for Empty { fn default() -> Empty { Empty(marker::PhantomData) } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index ccbd2b00cfd..2aeb0b0c31e 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -858,7 +858,8 @@ impl Clone for PhantomData { } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for PhantomData { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for PhantomData { fn default() -> Self { Self } diff --git a/library/core/src/option.rs b/library/core/src/option.rs index f2a1e901188..38eb2662b34 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2111,7 +2111,8 @@ where impl crate::clone::UseCloned for Option where T: crate::clone::UseCloned {} #[stable(feature = "rust1", since = "1.0.0")] -impl Default for Option { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for Option { /// Returns [`None`][Option::None]. /// /// # Examples diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 3e66e271f03..304cde05af9 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -230,7 +230,8 @@ impl hash::Hash for Alignment { /// Returns [`Alignment::MIN`], which is valid for any type. #[unstable(feature = "ptr_alignment_type", issue = "102070")] -impl Default for Alignment { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for Alignment { fn default() -> Alignment { Alignment::MIN } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index dc09ba8d788..479fe0f1554 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -5158,7 +5158,8 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for &[T] { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for &[T] { /// Creates an empty slice. fn default() -> Self { &[] @@ -5166,7 +5167,8 @@ impl Default for &[T] { } #[stable(feature = "mut_slice_default", since = "1.5.0")] -impl Default for &mut [T] { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for &mut [T] { /// Creates a mutable empty slice. fn default() -> Self { &mut [] diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index fe64132ff22..32a22988175 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -3072,7 +3072,8 @@ impl AsRef<[u8]> for str { } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for &str { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for &str { /// Creates an empty str #[inline] fn default() -> Self { @@ -3081,7 +3082,8 @@ impl Default for &str { } #[stable(feature = "default_mut_str", since = "1.28.0")] -impl Default for &mut str { +#[rustc_const_unstable(feature = "const_default", issue = "67792")] +impl const Default for &mut str { /// Creates an empty mutable str #[inline] fn default() -> Self { diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 34f3e046841..38822bdbb30 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -406,8 +406,8 @@ fn fn_call_in_nested_expr() { val: String::from("123"), }); - let _ = opt_foo.unwrap_or_else(|| Foo { val: String::default() }); - //~^ or_fun_call + // ok, `String::default()` is now `const` + let _ = opt_foo.unwrap_or(Foo { val: String::default() }); } mod result_map_or { diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index dc57bd6060a..7e8647f221c 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -406,8 +406,8 @@ fn fn_call_in_nested_expr() { val: String::from("123"), }); + // ok, `String::default()` is now `const` let _ = opt_foo.unwrap_or(Foo { val: String::default() }); - //~^ or_fun_call } mod result_map_or { diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 0f159fe8bff..0d88929e131 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -240,12 +240,6 @@ error: use of `unwrap_or` to construct default value LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:409:21 - | -LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` - error: function call inside of `map_or` --> tests/ui/or_fun_call.rs:424:19 | @@ -264,5 +258,5 @@ error: function call inside of `get_or_insert` LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` -error: aborting due to 41 previous errors +error: aborting due to 40 previous errors diff --git a/tests/ui/consts/rustc-impl-const-stability.rs b/tests/ui/consts/rustc-impl-const-stability.rs index 0df8482bec1..93a5e8e4f45 100644 --- a/tests/ui/consts/rustc-impl-const-stability.rs +++ b/tests/ui/consts/rustc-impl-const-stability.rs @@ -2,7 +2,7 @@ //@ known-bug: #110395 #![crate_type = "lib"] -#![feature(staged_api, const_trait_impl)] +#![feature(staged_api, const_trait_impl, const_default)] #![stable(feature = "foo", since = "1.0.0")] #[stable(feature = "potato", since = "1.27.0")] @@ -12,8 +12,8 @@ pub struct Data { #[stable(feature = "potato", since = "1.27.0")] #[rustc_const_unstable(feature = "data_foo", issue = "none")] -impl const Default for Data { - fn default() -> Data { - Data { _data: 42 } +impl const std::fmt::Debug for Data { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + Ok(()) } } diff --git a/tests/ui/consts/rustc-impl-const-stability.stderr b/tests/ui/consts/rustc-impl-const-stability.stderr index 19c6bb5907f..a3ef4031a13 100644 --- a/tests/ui/consts/rustc-impl-const-stability.stderr +++ b/tests/ui/consts/rustc-impl-const-stability.stderr @@ -1,8 +1,8 @@ -error: const `impl` for trait `Default` which is not marked with `#[const_trait]` +error: const `impl` for trait `Debug` which is not marked with `#[const_trait]` --> $DIR/rustc-impl-const-stability.rs:15:12 | -LL | impl const Default for Data { - | ^^^^^^^ this trait is not `const` +LL | impl const std::fmt::Debug for Data { + | ^^^^^^^^^^^^^^^ this trait is not `const` | = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` = note: adding a non-const method body in the future would be a breaking change diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.rs b/tests/ui/lint/dead-code/unused-struct-derive-default.rs index f20b7cb66ee..bfbdf57b0dc 100644 --- a/tests/ui/lint/dead-code/unused-struct-derive-default.rs +++ b/tests/ui/lint/dead-code/unused-struct-derive-default.rs @@ -1,4 +1,4 @@ -#![deny(dead_code)] +#![deny(dead_code)] //~ NOTE the lint level is defined here #[derive(Default)] struct T; //~ ERROR struct `T` is never constructed @@ -7,7 +7,7 @@ struct T; //~ ERROR struct `T` is never constructed struct Used; #[derive(Default)] -enum E { +enum E { //~ NOTE variant in this enum #[default] A, B, //~ ERROR variant `B` is never constructed diff --git a/tests/ui/specialization/const_trait_impl.rs b/tests/ui/specialization/const_trait_impl.rs index 2df92dfad3b..e917263d193 100644 --- a/tests/ui/specialization/const_trait_impl.rs +++ b/tests/ui/specialization/const_trait_impl.rs @@ -2,6 +2,8 @@ #![feature(const_trait_impl, min_specialization, rustc_attrs)] +use std::fmt::Debug; + #[rustc_specialization_trait] #[const_trait] pub unsafe trait Sup { @@ -31,19 +33,19 @@ pub trait A { fn a() -> u32; } -impl const A for T { +impl const A for T { default fn a() -> u32 { 2 } } -impl const A for T { +impl const A for T { default fn a() -> u32 { 3 } } -impl const A for T { +impl const A for T { fn a() -> u32 { T::foo() } diff --git a/tests/ui/specialization/const_trait_impl.stderr b/tests/ui/specialization/const_trait_impl.stderr index ea3ec16ac1e..b9c768812c8 100644 --- a/tests/ui/specialization/const_trait_impl.stderr +++ b/tests/ui/specialization/const_trait_impl.stderr @@ -1,58 +1,58 @@ error: `[const]` can only be applied to `#[const_trait]` traits - --> $DIR/const_trait_impl.rs:34:9 + --> $DIR/const_trait_impl.rs:36:9 | -LL | impl const A for T { - | ^^^^^^^ can't be applied to `Default` +LL | impl const A for T { + | ^^^^^^^ can't be applied to `Debug` | -note: `Default` can't be used with `[const]` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/default.rs:LL:COL +note: `Debug` can't be used with `[const]` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL error: `[const]` can only be applied to `#[const_trait]` traits - --> $DIR/const_trait_impl.rs:40:9 + --> $DIR/const_trait_impl.rs:42:9 | -LL | impl const A for T { - | ^^^^^^^ can't be applied to `Default` +LL | impl const A for T { + | ^^^^^^^ can't be applied to `Debug` | -note: `Default` can't be used with `[const]` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/default.rs:LL:COL +note: `Debug` can't be used with `[const]` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL error: `[const]` can only be applied to `#[const_trait]` traits - --> $DIR/const_trait_impl.rs:46:9 + --> $DIR/const_trait_impl.rs:48:9 | -LL | impl const A for T { - | ^^^^^^^ can't be applied to `Default` +LL | impl const A for T { + | ^^^^^^^ can't be applied to `Debug` | -note: `Default` can't be used with `[const]` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/default.rs:LL:COL +note: `Debug` can't be used with `[const]` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL error: `[const]` can only be applied to `#[const_trait]` traits - --> $DIR/const_trait_impl.rs:40:9 + --> $DIR/const_trait_impl.rs:42:9 | -LL | impl const A for T { - | ^^^^^^^ can't be applied to `Default` +LL | impl const A for T { + | ^^^^^^^ can't be applied to `Debug` | -note: `Default` can't be used with `[const]` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/default.rs:LL:COL +note: `Debug` can't be used with `[const]` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `[const]` can only be applied to `#[const_trait]` traits - --> $DIR/const_trait_impl.rs:34:9 + --> $DIR/const_trait_impl.rs:36:9 | -LL | impl const A for T { - | ^^^^^^^ can't be applied to `Default` +LL | impl const A for T { + | ^^^^^^^ can't be applied to `Debug` | -note: `Default` can't be used with `[const]` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/default.rs:LL:COL +note: `Debug` can't be used with `[const]` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `[const]` can only be applied to `#[const_trait]` traits - --> $DIR/const_trait_impl.rs:46:9 + --> $DIR/const_trait_impl.rs:48:9 | -LL | impl const A for T { - | ^^^^^^^ can't be applied to `Default` +LL | impl const A for T { + | ^^^^^^^ can't be applied to `Debug` | -note: `Default` can't be used with `[const]` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/default.rs:LL:COL +note: `Debug` can't be used with `[const]` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 6 previous errors diff --git a/tests/ui/traits/const-traits/const-traits-alloc.rs b/tests/ui/traits/const-traits/const-traits-alloc.rs new file mode 100644 index 00000000000..07725ef02f1 --- /dev/null +++ b/tests/ui/traits/const-traits/const-traits-alloc.rs @@ -0,0 +1,9 @@ +//@ run-pass +#![feature(const_trait_impl, const_default)] +#![allow(dead_code)] +// alloc::string +const STRING: String = Default::default(); +// alloc::vec +const VEC: Vec<()> = Default::default(); + +fn main() {} diff --git a/tests/ui/traits/const-traits/const-traits-core.rs b/tests/ui/traits/const-traits/const-traits-core.rs new file mode 100644 index 00000000000..6df53daae13 --- /dev/null +++ b/tests/ui/traits/const-traits/const-traits-core.rs @@ -0,0 +1,46 @@ +//@ run-pass +#![feature( + const_trait_impl, const_default, ptr_alignment_type, ascii_char, f16, f128, sync_unsafe_cell, +)] +#![allow(dead_code)] +// core::default +const UNIT: () = Default::default(); +const BOOL: bool = Default::default(); +const CHAR: char = Default::default(); +const ASCII_CHAR: std::ascii::Char = Default::default(); +const USIZE: usize = Default::default(); +const U8: u8 = Default::default(); +const U16: u16 = Default::default(); +const U32: u32 = Default::default(); +const U64: u64 = Default::default(); +const U128: u128 = Default::default(); +const I8: i8 = Default::default(); +const I16: i16 = Default::default(); +const I32: i32 = Default::default(); +const I64: i64 = Default::default(); +const I128: i128 = Default::default(); +const F16: f16 = Default::default(); +const F32: f32 = Default::default(); +const F64: f64 = Default::default(); +const F128: f128 = Default::default(); +// core::marker +const PHANTOM: std::marker::PhantomData<()> = Default::default(); +// core::option +const OPT: Option = Default::default(); +// core::iter::sources::empty +const EMPTY: std::iter::Empty<()> = Default::default(); +// core::ptr::alignment +const ALIGNMENT: std::ptr::Alignment = Default::default(); +// core::slice +const SLICE: &[()] = Default::default(); +const MUT_SLICE: &mut [()] = Default::default(); +// core::str +const STR: &str = Default::default(); +const MUT_STR: &mut str = Default::default(); +// core::cell +const CELL: std::cell::Cell<()> = Default::default(); +const REF_CELL: std::cell::RefCell<()> = Default::default(); +const UNSAFE_CELL: std::cell::UnsafeCell<()> = Default::default(); +const SYNC_UNSAFE_CELL: std::cell::SyncUnsafeCell<()> = Default::default(); + +fn main() {} diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-gate.rs b/tests/ui/traits/const-traits/const_derives/derive-const-gate.rs index a772d69c9e2..04fea1189ae 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-gate.rs +++ b/tests/ui/traits/const-traits/const_derives/derive-const-gate.rs @@ -1,5 +1,6 @@ -#[derive_const(Default)] //~ ERROR use of unstable library feature -//~^ ERROR const `impl` for trait `Default` which is not marked with `#[const_trait]` +#[derive_const(Debug)] //~ ERROR use of unstable library feature +//~^ ERROR const `impl` for trait `Debug` which is not marked with `#[const_trait]` +//~| ERROR cannot call non-const method pub struct S; fn main() {} diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr index 202210a2e65..5bde358001c 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr +++ b/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr @@ -1,21 +1,30 @@ error[E0658]: use of unstable library feature `derive_const` --> $DIR/derive-const-gate.rs:1:3 | -LL | #[derive_const(Default)] +LL | #[derive_const(Debug)] | ^^^^^^^^^^^^ | = help: add `#![feature(derive_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: const `impl` for trait `Default` which is not marked with `#[const_trait]` +error: const `impl` for trait `Debug` which is not marked with `#[const_trait]` --> $DIR/derive-const-gate.rs:1:16 | -LL | #[derive_const(Default)] - | ^^^^^^^ this trait is not `const` +LL | #[derive_const(Debug)] + | ^^^^^ this trait is not `const` | = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` = note: adding a non-const method body in the future would be a breaking change -error: aborting due to 2 previous errors +error[E0015]: cannot call non-const method `Formatter::<'_>::write_str` in constant functions + --> $DIR/derive-const-gate.rs:1:16 + | +LL | #[derive_const(Debug)] + | ^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0015, E0658. +For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.rs b/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.rs index 7bda7117a47..0bc25ce5f65 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.rs +++ b/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.rs @@ -3,11 +3,13 @@ pub struct A; -impl Default for A { - fn default() -> A { A } +impl std::fmt::Debug for A { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + panic!() + } } -#[derive_const(Default)] +#[derive_const(Debug)] pub struct S(A); fn main() {} diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.stderr index 27f4bcf46ef..c0bd360ebe5 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.stderr +++ b/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.stderr @@ -1,19 +1,17 @@ -error: const `impl` for trait `Default` which is not marked with `#[const_trait]` - --> $DIR/derive-const-non-const-type.rs:10:16 +error: const `impl` for trait `Debug` which is not marked with `#[const_trait]` + --> $DIR/derive-const-non-const-type.rs:12:16 | -LL | #[derive_const(Default)] - | ^^^^^^^ this trait is not `const` +LL | #[derive_const(Debug)] + | ^^^^^ this trait is not `const` | = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` = note: adding a non-const method body in the future would be a breaking change -error[E0015]: cannot call non-const associated function `::default` in constant functions - --> $DIR/derive-const-non-const-type.rs:11:14 +error[E0015]: cannot call non-const method `Formatter::<'_>::debug_tuple_field1_finish` in constant functions + --> $DIR/derive-const-non-const-type.rs:12:16 | -LL | #[derive_const(Default)] - | ------- in this derive macro expansion -LL | pub struct S(A); - | ^ +LL | #[derive_const(Debug)] + | ^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-use.rs b/tests/ui/traits/const-traits/const_derives/derive-const-use.rs index 1e447147213..1a3012de06f 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-use.rs +++ b/tests/ui/traits/const-traits/const_derives/derive-const-use.rs @@ -1,6 +1,6 @@ //@ known-bug: #110395 -#![feature(const_trait_impl, const_cmp, const_default_impls, derive_const)] +#![feature(const_trait_impl, const_default, const_cmp, derive_const)] pub struct A; diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr index ce61eb9a1ab..4ea11a0c7ed 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr +++ b/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr @@ -1,27 +1,3 @@ -error[E0635]: unknown feature `const_default_impls` - --> $DIR/derive-const-use.rs:3:41 - | -LL | #![feature(const_trait_impl, const_cmp, const_default_impls, derive_const)] - | ^^^^^^^^^^^^^^^^^^^ - -error: const `impl` for trait `Default` which is not marked with `#[const_trait]` - --> $DIR/derive-const-use.rs:7:12 - | -LL | impl const Default for A { - | ^^^^^^^ this trait is not `const` - | - = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` - = note: adding a non-const method body in the future would be a breaking change - -error: const `impl` for trait `Default` which is not marked with `#[const_trait]` - --> $DIR/derive-const-use.rs:15:16 - | -LL | #[derive_const(Default, PartialEq)] - | ^^^^^^^ this trait is not `const` - | - = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` - = note: adding a non-const method body in the future would be a breaking change - error[E0277]: the trait bound `(): [const] PartialEq` is not satisfied --> $DIR/derive-const-use.rs:16:14 | @@ -30,35 +6,6 @@ LL | #[derive_const(Default, PartialEq)] LL | pub struct S((), A); | ^^ -error[E0015]: cannot call non-const associated function `::default` in constants - --> $DIR/derive-const-use.rs:18:35 - | -LL | const _: () = assert!(S((), A) == S::default()); - | ^^^^^^^^^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error[E0015]: cannot call non-const associated function `<() as Default>::default` in constant functions - --> $DIR/derive-const-use.rs:16:14 - | -LL | #[derive_const(Default, PartialEq)] - | ------- in this derive macro expansion -LL | pub struct S((), A); - | ^^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - -error[E0015]: cannot call non-const associated function `::default` in constant functions - --> $DIR/derive-const-use.rs:16:18 - | -LL | #[derive_const(Default, PartialEq)] - | ------- in this derive macro expansion -LL | pub struct S((), A); - | ^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - -error: aborting due to 7 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0015, E0277, E0635. -For more information about an error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/std-impl-gate.gated.stderr b/tests/ui/traits/const-traits/std-impl-gate.gated.stderr deleted file mode 100644 index a78cf8ce61e..00000000000 --- a/tests/ui/traits/const-traits/std-impl-gate.gated.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0635]: unknown feature `const_default_impls` - --> $DIR/std-impl-gate.rs:6:46 - | -LL | #![cfg_attr(gated, feature(const_trait_impl, const_default_impls))] - | ^^^^^^^^^^^^^^^^^^^ - -error[E0015]: cannot call non-const associated function ` as Default>::default` in constant functions - --> $DIR/std-impl-gate.rs:13:5 - | -LL | Default::default() - | ^^^^^^^^^^^^^^^^^^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0015, E0635. -For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/const-traits/std-impl-gate.rs b/tests/ui/traits/const-traits/std-impl-gate.rs index 84091931997..d29bccf17c9 100644 --- a/tests/ui/traits/const-traits/std-impl-gate.rs +++ b/tests/ui/traits/const-traits/std-impl-gate.rs @@ -1,9 +1,9 @@ // This tests feature gates for const impls in the standard library. //@ revisions: stock gated -//@[gated] known-bug: #110395 +//@[gated] run-pass -#![cfg_attr(gated, feature(const_trait_impl, const_default_impls))] +#![cfg_attr(gated, feature(const_trait_impl, const_default))] fn non_const_context() -> Vec { Default::default() @@ -11,7 +11,8 @@ fn non_const_context() -> Vec { const fn const_context() -> Vec { Default::default() - //[stock]~^ ERROR cannot call non-const associated function + //[stock]~^ ERROR cannot call conditionally-const associated function + //[stock]~| ERROR `Default` is not yet stable as a const trait } fn main() { diff --git a/tests/ui/traits/const-traits/std-impl-gate.stock.stderr b/tests/ui/traits/const-traits/std-impl-gate.stock.stderr index 8728f652ef9..1fa71e41a98 100644 --- a/tests/ui/traits/const-traits/std-impl-gate.stock.stderr +++ b/tests/ui/traits/const-traits/std-impl-gate.stock.stderr @@ -1,11 +1,25 @@ -error[E0015]: cannot call non-const associated function ` as Default>::default` in constant functions +error[E0658]: cannot call conditionally-const associated function ` as Default>::default` in constant functions --> $DIR/std-impl-gate.rs:13:5 | LL | Default::default() | ^^^^^^^^^^^^^^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + = note: see issue #67792 for more information + = help: add `#![feature(const_trait_impl)]` 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 1 previous error +error: `Default` is not yet stable as a const trait + --> $DIR/std-impl-gate.rs:13:5 + | +LL | Default::default() + | ^^^^^^^^^^^^^^^^^^ + | +help: add `#![feature(const_default)]` to the crate attributes to enable + | +LL + #![feature(const_default)] + | + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0658`. -- cgit 1.4.1-3-g733a5 From d6f7aad00ee9fe8658722d634bfa8473352cfc95 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Sat, 21 Dec 2024 20:25:43 +0000 Subject: Make `Default` const and add some `const Default` impls Full list of `impl const Default` types: - () - bool - char - Cell - std::ascii::Char - usize - u8 - u16 - u32 - u64 - u128 - i8 - i16 - i32 - i64 - i128 - f16 - f32 - f64 - f128 - std::marker::PhantomData - Option - std::iter::Empty - std::ptr::Alignment - &[T] - &mut [T] - &str - &mut str - String - Vec --- tests/ui/or_fun_call.fixed | 4 ++-- tests/ui/or_fun_call.rs | 2 +- tests/ui/or_fun_call.stderr | 8 +------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 34f3e046841..38822bdbb30 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -406,8 +406,8 @@ fn fn_call_in_nested_expr() { val: String::from("123"), }); - let _ = opt_foo.unwrap_or_else(|| Foo { val: String::default() }); - //~^ or_fun_call + // ok, `String::default()` is now `const` + let _ = opt_foo.unwrap_or(Foo { val: String::default() }); } mod result_map_or { diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index dc57bd6060a..7e8647f221c 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -406,8 +406,8 @@ fn fn_call_in_nested_expr() { val: String::from("123"), }); + // ok, `String::default()` is now `const` let _ = opt_foo.unwrap_or(Foo { val: String::default() }); - //~^ or_fun_call } mod result_map_or { diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 0f159fe8bff..0d88929e131 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -240,12 +240,6 @@ error: use of `unwrap_or` to construct default value LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:409:21 - | -LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` - error: function call inside of `map_or` --> tests/ui/or_fun_call.rs:424:19 | @@ -264,5 +258,5 @@ error: function call inside of `get_or_insert` LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` -error: aborting due to 41 previous errors +error: aborting due to 40 previous errors -- cgit 1.4.1-3-g733a5 From 8f8099fb42f0b067cd9b6a82e704ce3cc0e63301 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Mon, 7 Jul 2025 23:07:32 +0000 Subject: Account for const stability in clippy when checking constness --- src/tools/clippy/clippy_utils/src/visitors.rs | 6 ++++-- src/tools/clippy/tests/ui/or_fun_call.fixed | 4 ++-- src/tools/clippy/tests/ui/or_fun_call.rs | 2 +- src/tools/clippy/tests/ui/or_fun_call.stderr | 8 +++++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index fc6e30a9804..615a0910dfd 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,5 +1,7 @@ +use crate::msrvs::Msrv; use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; +use crate::qualify_min_const_fn::is_stable_const_fn; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -343,13 +345,13 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> .cx .qpath_res(p, hir_id) .opt_def_id() - .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {}, + .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, ExprKind::MethodCall(..) if self .cx .typeck_results() .type_dependent_def_id(e.hir_id) - .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {}, + .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, ExprKind::Binary(_, lhs, rhs) if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 38822bdbb30..34f3e046841 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -406,8 +406,8 @@ fn fn_call_in_nested_expr() { val: String::from("123"), }); - // ok, `String::default()` is now `const` - let _ = opt_foo.unwrap_or(Foo { val: String::default() }); + let _ = opt_foo.unwrap_or_else(|| Foo { val: String::default() }); + //~^ or_fun_call } mod result_map_or { diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 7e8647f221c..dc57bd6060a 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -406,8 +406,8 @@ fn fn_call_in_nested_expr() { val: String::from("123"), }); - // ok, `String::default()` is now `const` let _ = opt_foo.unwrap_or(Foo { val: String::default() }); + //~^ or_fun_call } mod result_map_or { diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 0d88929e131..0f159fe8bff 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -240,6 +240,12 @@ error: use of `unwrap_or` to construct default value LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` +error: function call inside of `unwrap_or` + --> tests/ui/or_fun_call.rs:409:21 + | +LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` + error: function call inside of `map_or` --> tests/ui/or_fun_call.rs:424:19 | @@ -258,5 +264,5 @@ error: function call inside of `get_or_insert` LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` -error: aborting due to 40 previous errors +error: aborting due to 41 previous errors -- cgit 1.4.1-3-g733a5 From 5228e2846995ef24c743710877b6668fa0260ba6 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Mon, 7 Jul 2025 23:07:32 +0000 Subject: Account for const stability in clippy when checking constness --- clippy_utils/src/visitors.rs | 6 ++++-- tests/ui/or_fun_call.fixed | 4 ++-- tests/ui/or_fun_call.rs | 2 +- tests/ui/or_fun_call.stderr | 8 +++++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index fc6e30a9804..615a0910dfd 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,5 +1,7 @@ +use crate::msrvs::Msrv; use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; +use crate::qualify_min_const_fn::is_stable_const_fn; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -343,13 +345,13 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> .cx .qpath_res(p, hir_id) .opt_def_id() - .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {}, + .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, ExprKind::MethodCall(..) if self .cx .typeck_results() .type_dependent_def_id(e.hir_id) - .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {}, + .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, ExprKind::Binary(_, lhs, rhs) if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 38822bdbb30..34f3e046841 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -406,8 +406,8 @@ fn fn_call_in_nested_expr() { val: String::from("123"), }); - // ok, `String::default()` is now `const` - let _ = opt_foo.unwrap_or(Foo { val: String::default() }); + let _ = opt_foo.unwrap_or_else(|| Foo { val: String::default() }); + //~^ or_fun_call } mod result_map_or { diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 7e8647f221c..dc57bd6060a 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -406,8 +406,8 @@ fn fn_call_in_nested_expr() { val: String::from("123"), }); - // ok, `String::default()` is now `const` let _ = opt_foo.unwrap_or(Foo { val: String::default() }); + //~^ or_fun_call } mod result_map_or { diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 0d88929e131..0f159fe8bff 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -240,6 +240,12 @@ error: use of `unwrap_or` to construct default value LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` +error: function call inside of `unwrap_or` + --> tests/ui/or_fun_call.rs:409:21 + | +LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` + error: function call inside of `map_or` --> tests/ui/or_fun_call.rs:424:19 | @@ -258,5 +264,5 @@ error: function call inside of `get_or_insert` LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` -error: aborting due to 40 previous errors +error: aborting due to 41 previous errors -- cgit 1.4.1-3-g733a5 From 62951c2e07dea61ec20fd6f6e849400f4cd194c3 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Mon, 7 Jul 2025 18:22:28 -0600 Subject: fix: Remove newline from multiple crate versions note --- .../src/error_reporting/traits/fulfillment_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 28d572b303a..cd3e6c4bc54 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 @@ -1933,7 +1933,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::highlighted("multiple different versions".to_string()), StringPart::normal(" of crate `".to_string()), StringPart::highlighted(format!("{crate_name}")), - StringPart::normal("` in the dependency graph\n".to_string()), + StringPart::normal("` in the dependency graph".to_string()), ], ); if points_at_type { -- cgit 1.4.1-3-g733a5 From a58a423f9a37c614d56f86fbbfbebec414caaaf1 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Tue, 8 Jul 2025 03:16:46 +0200 Subject: Add target maintainer information for mips64-unknown-linux-muslabi64 The mips64-unknown-linux-muslabi64 target is currently rather broken, but I'm working on getting it fixed so that it can at least be used again. While I can't commit to maintaining the LLVM side of this target, I don't mind looking into any other MIPS or musl-related issues that arise with this target. Signed-off-by: Jens Reidel --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 2 +- .../mips64-unknown-linux-muslabi64.md | 49 ++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/doc/rustc/src/platform-support/mips64-unknown-linux-muslabi64.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 9fe4c218121..e1742631f63 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -83,6 +83,7 @@ - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md) - [m68k-unknown-none-elf](platform-support/m68k-unknown-none-elf.md) - [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md) + - [mips64-unknown-linux-muslabi64](platform-support/mips64-unknown-linux-muslabi64.md) - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md) - [mipsel-unknown-linux-gnu](platform-support/mipsel-unknown-linux-gnu.md) - [mips\*-mti-none-elf](platform-support/mips-mti-none-elf.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 285b1e519b7..ed01de4c1c3 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -333,7 +333,7 @@ target | std | host | notes `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc [`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux musl 1.2.3 `mips64-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 Linux, N64 ABI (kernel 4.4, glibc 2.23) -`mips64-unknown-linux-muslabi64` | ✓ | | MIPS64 Linux, N64 ABI, musl 1.2.3 +[`mips64-unknown-linux-muslabi64`](platform-support/mips64-unknown-linux-muslabi64.md) | ✓ | ✓ | MIPS64 Linux, N64 ABI, musl 1.2.3 `mips64el-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 (little endian) Linux, N64 ABI (kernel 4.4, glibc 2.23) `mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (little endian) Linux, N64 ABI, musl 1.2.3 `mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP) diff --git a/src/doc/rustc/src/platform-support/mips64-unknown-linux-muslabi64.md b/src/doc/rustc/src/platform-support/mips64-unknown-linux-muslabi64.md new file mode 100644 index 00000000000..60c0972bb3e --- /dev/null +++ b/src/doc/rustc/src/platform-support/mips64-unknown-linux-muslabi64.md @@ -0,0 +1,49 @@ +# mips64-unknown-linux-muslabi64 + +**Tier: 3** + +Target for 64-bit big endian MIPS Linux programs using musl libc and the N64 ABI. + +## Target maintainers + +[@Gelbpunkt](https://github.com/Gelbpunkt) + +## Requirements + +Building the target itself requires a 64-bit big endian MIPS compiler that is +supported by `cc-rs`. + +## Building the target + +The target can be built by enabling it for a `rustc` build. + +```toml +[build] +target = ["mips64-unknown-linux-muslabi64"] +``` + +Make sure your C compiler is included in `$PATH`, then add it to the +`bootstrap.toml`: + +```toml +[target.mips64-unknown-linux-muslabi64] +cc = "mips64-linux-musl-gcc" +cxx = "mips64-linux-musl-g++" +ar = "mips64-linux-musl-ar" +linker = "mips64-linux-musl-gcc" +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will first need to build Rust with the target enabled (see +"Building the target" above). + +## Cross-compilation + +This target can be cross-compiled from any host. + +## Testing + +This target can be tested as normal with `x.py` on a 64-bit big endian MIPS +host or via QEMU emulation. -- cgit 1.4.1-3-g733a5 From 717041232ae0ae0ae445a2a302510aab3375ea47 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 7 Jul 2025 00:56:29 +0000 Subject: Don't mark `#[target_feature]` safe fns as unsafe in rustdoc JSON. --- src/librustdoc/json/conversions.rs | 15 ++++++++++++++- tests/rustdoc-json/attrs/target_feature.rs | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index f51b35097f6..f76ff11590b 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -9,6 +9,7 @@ use rustc_ast::ast; use rustc_attr_data_structures::{self as attrs, DeprecatedSince}; use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; +use rustc_hir::{HeaderSafety, Safety}; use rustc_metadata::rendered_const; use rustc_middle::{bug, ty}; use rustc_span::{Pos, kw, sym}; @@ -383,10 +384,22 @@ impl FromClean for Union { impl FromClean for FunctionHeader { fn from_clean(header: &rustc_hir::FnHeader, renderer: &JsonRenderer<'_>) -> Self { + let is_unsafe = match header.safety { + HeaderSafety::SafeTargetFeatures => { + // The type system's internal implementation details consider + // safe functions with the `#[target_feature]` attribute to be analogous + // to unsafe functions: `header.is_unsafe()` returns `true` for them. + // For rustdoc, this isn't the right decision, so we explicitly return `false`. + // Context: https://github.com/rust-lang/rust/issues/142655 + false + } + HeaderSafety::Normal(Safety::Safe) => false, + HeaderSafety::Normal(Safety::Unsafe) => true, + }; FunctionHeader { is_async: header.is_async(), is_const: header.is_const(), - is_unsafe: header.is_unsafe(), + is_unsafe, abi: header.abi.into_json(renderer), } } diff --git a/tests/rustdoc-json/attrs/target_feature.rs b/tests/rustdoc-json/attrs/target_feature.rs index ee2b3235f72..80262d8e332 100644 --- a/tests/rustdoc-json/attrs/target_feature.rs +++ b/tests/rustdoc-json/attrs/target_feature.rs @@ -1,17 +1,40 @@ //@ only-x86_64 //@ is "$.index[?(@.name=='test1')].attrs" '["#[target_feature(enable=\"avx\")]"]' +//@ is "$.index[?(@.name=='test1')].inner.function.header.is_unsafe" false #[target_feature(enable = "avx")] pub fn test1() {} //@ is "$.index[?(@.name=='test2')].attrs" '["#[target_feature(enable=\"avx\", enable=\"avx2\")]"]' +//@ is "$.index[?(@.name=='test1')].inner.function.header.is_unsafe" false #[target_feature(enable = "avx,avx2")] pub fn test2() {} //@ is "$.index[?(@.name=='test3')].attrs" '["#[target_feature(enable=\"avx\", enable=\"avx2\")]"]' +//@ is "$.index[?(@.name=='test1')].inner.function.header.is_unsafe" false #[target_feature(enable = "avx", enable = "avx2")] pub fn test3() {} //@ is "$.index[?(@.name=='test4')].attrs" '["#[target_feature(enable=\"avx\", enable=\"avx2\", enable=\"avx512f\")]"]' +//@ is "$.index[?(@.name=='test1')].inner.function.header.is_unsafe" false #[target_feature(enable = "avx", enable = "avx2,avx512f")] pub fn test4() {} + +//@ is "$.index[?(@.name=='test_unsafe_fn')].attrs" '["#[target_feature(enable=\"avx\")]"]' +//@ is "$.index[?(@.name=='test_unsafe_fn')].inner.function.header.is_unsafe" true +#[target_feature(enable = "avx")] +pub unsafe fn test_unsafe_fn() {} + +pub struct Example; + +impl Example { + //@ is "$.index[?(@.name=='safe_assoc_fn')].attrs" '["#[target_feature(enable=\"avx\")]"]' + //@ is "$.index[?(@.name=='safe_assoc_fn')].inner.function.header.is_unsafe" false + #[target_feature(enable = "avx")] + pub fn safe_assoc_fn() {} + + //@ is "$.index[?(@.name=='unsafe_assoc_fn')].attrs" '["#[target_feature(enable=\"avx\")]"]' + //@ is "$.index[?(@.name=='unsafe_assoc_fn')].inner.function.header.is_unsafe" true + #[target_feature(enable = "avx")] + pub unsafe fn unsafe_assoc_fn() {} +} -- cgit 1.4.1-3-g733a5 From 8cf2c71243f8c093e7728370e6653aa940dbaf22 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 4 Jul 2025 23:16:41 -0700 Subject: Let `rvalue_creates_operand` return true for *all* `Rvalue::Aggregate`s Inspired by where I noticed that we were nearly at this point, plus the comments I was writing in 143410 that reminded me a type-dependent `true` is fine. This PR splits the `OperandRef::builder` logic out to a separate type, with the updates needed to handle SIMD as well. In doing so, that makes the existing `Aggregate` path in `codegen_rvalue_operand` capable of handing SIMD values just fine. As a result, we no longer need to do layout calculations for aggregate result types when running the analysis to determine which things can be SSA in codegen. --- compiler/rustc_codegen_ssa/src/mir/analyze.rs | 3 +- compiler/rustc_codegen_ssa/src/mir/operand.rs | 175 +++++++++++++++++--------- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 25 ++-- tests/codegen/enum/enum-aggregate.rs | 15 +-- tests/codegen/simd/aggregate-simd.rs | 106 ++++++++++++++++ tests/codegen/union-aggregate.rs | 23 ++++ 6 files changed, 261 insertions(+), 86 deletions(-) create mode 100644 tests/codegen/simd/aggregate-simd.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 99f35b79208..6d6465dd798 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -171,8 +171,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer if let Some(local) = place.as_local() { self.define(local, DefLocation::Assignment(location)); if self.locals[local] != LocalKind::Memory { - let decl_span = self.fx.mir.local_decls[local].source_info.span; - if !self.fx.rvalue_creates_operand(rvalue, decl_span) { + if !self.fx.rvalue_creates_operand(rvalue) { self.locals[local] = LocalKind::Memory; } } diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 2896dfd5463..e5b95e5ecc5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -565,118 +565,167 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } } } +} - /// Creates an incomplete operand containing the [`abi::Scalar`]s expected based - /// on the `layout` passed. This is for use with [`OperandRef::insert_field`] - /// later to set the necessary immediate(s), one-by-one converting all the `Right` to `Left`. - /// - /// Returns `None` for `layout`s which cannot be built this way. - pub(crate) fn builder( - layout: TyAndLayout<'tcx>, - ) -> Option>> { - // Uninhabited types are weird, because for example `Result` - // shows up as `FieldsShape::Primitive` and we need to be able to write - // a field into `(u32, !)`. We'll do that in an `alloca` instead. - if layout.uninhabited { - return None; - } +/// Each of these variants starts out as `Either::Right` when it's uninitialized, +/// then setting the field changes that to `Either::Left` with the backend value. +#[derive(Debug, Copy, Clone)] +enum OperandValueBuilder { + ZeroSized, + Immediate(Either), + Pair(Either, Either), + /// `repr(simd)` types need special handling because they each have a non-empty + /// array field (which uses [`OperandValue::Ref`]) despite the SIMD type itself + /// using [`OperandValue::Immediate`] which for any other kind of type would + /// mean that its one non-ZST field would also be [`OperandValue::Immediate`]. + Vector(Either), +} + +/// Allows building up an `OperandRef` by setting fields one at a time. +#[derive(Debug, Copy, Clone)] +pub(super) struct OperandRefBuilder<'tcx, V> { + val: OperandValueBuilder, + layout: TyAndLayout<'tcx>, +} +impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { + /// Creates an uninitialized builder for an instance of the `layout`. + /// + /// ICEs for [`BackendRepr::Memory`] types (other than ZSTs), which should + /// be built up inside a [`PlaceRef`] instead as they need an allocated place + /// into which to write the values of the fields. + pub(super) fn new(layout: TyAndLayout<'tcx>) -> Self { let val = match layout.backend_repr { - BackendRepr::Memory { .. } if layout.is_zst() => OperandValue::ZeroSized, - BackendRepr::Scalar(s) => OperandValue::Immediate(Either::Right(s)), - BackendRepr::ScalarPair(a, b) => OperandValue::Pair(Either::Right(a), Either::Right(b)), - BackendRepr::Memory { .. } | BackendRepr::SimdVector { .. } => return None, + BackendRepr::Memory { .. } if layout.is_zst() => OperandValueBuilder::ZeroSized, + BackendRepr::Scalar(s) => OperandValueBuilder::Immediate(Either::Right(s)), + BackendRepr::ScalarPair(a, b) => { + OperandValueBuilder::Pair(Either::Right(a), Either::Right(b)) + } + BackendRepr::SimdVector { .. } => OperandValueBuilder::Vector(Either::Right(())), + BackendRepr::Memory { .. } => { + bug!("Cannot use non-ZST Memory-ABI type in operand builder: {layout:?}"); + } }; - Some(OperandRef { val, layout }) + OperandRefBuilder { val, layout } } -} -impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Either> { - pub(crate) fn insert_field>( + pub(super) fn insert_field>( &mut self, bx: &mut Bx, - v: VariantIdx, - f: FieldIdx, - operand: OperandRef<'tcx, V>, + variant: VariantIdx, + field: FieldIdx, + field_operand: OperandRef<'tcx, V>, ) { - let (expect_zst, is_zero_offset) = if let abi::FieldsShape::Primitive = self.layout.fields { + if let OperandValue::ZeroSized = field_operand.val { + // A ZST never adds any state, so just ignore it. + // This special-casing is worth it because of things like + // `Result` where `Ok(never)` is legal to write, + // but the type shows as FieldShape::Primitive so we can't + // actually look at the layout for the field being set. + return; + } + + let is_zero_offset = if let abi::FieldsShape::Primitive = self.layout.fields { // The other branch looking at field layouts ICEs for primitives, // so we need to handle them separately. - // Multiple fields is possible for cases such as aggregating - // a thin pointer, where the second field is the unit. + // Because we handled ZSTs above (like the metadata in a thin pointer), + // the only possibility is that we're setting the one-and-only field. assert!(!self.layout.is_zst()); - assert_eq!(v, FIRST_VARIANT); - let first_field = f == FieldIdx::ZERO; - (!first_field, first_field) + assert_eq!(variant, FIRST_VARIANT); + assert_eq!(field, FieldIdx::ZERO); + true } else { - let variant_layout = self.layout.for_variant(bx.cx(), v); - let field_layout = variant_layout.field(bx.cx(), f.as_usize()); - let field_offset = variant_layout.fields.offset(f.as_usize()); - (field_layout.is_zst(), field_offset == Size::ZERO) + let variant_layout = self.layout.for_variant(bx.cx(), variant); + let field_offset = variant_layout.fields.offset(field.as_usize()); + field_offset == Size::ZERO }; let mut update = |tgt: &mut Either, src, from_scalar| { let to_scalar = tgt.unwrap_right(); + // We transmute here (rather than just `from_immediate`) because in + // `Result` the field of the `Ok` is an integer, + // but the corresponding scalar in the enum is a pointer. let imm = transmute_scalar(bx, src, from_scalar, to_scalar); *tgt = Either::Left(imm); }; - match (operand.val, operand.layout.backend_repr) { - (OperandValue::ZeroSized, _) if expect_zst => {} + match (field_operand.val, field_operand.layout.backend_repr) { + (OperandValue::ZeroSized, _) => unreachable!("Handled above"), (OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val { - OperandValue::Immediate(val @ Either::Right(_)) if is_zero_offset => { + OperandValueBuilder::Immediate(val @ Either::Right(_)) if is_zero_offset => { update(val, v, from_scalar); } - OperandValue::Pair(fst @ Either::Right(_), _) if is_zero_offset => { + OperandValueBuilder::Pair(fst @ Either::Right(_), _) if is_zero_offset => { update(fst, v, from_scalar); } - OperandValue::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { + OperandValueBuilder::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { update(snd, v, from_scalar); } - _ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"), + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } + }, + (OperandValue::Immediate(v), BackendRepr::SimdVector { .. }) => match &mut self.val { + OperandValueBuilder::Vector(val @ Either::Right(())) if is_zero_offset => { + *val = Either::Left(v); + } + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } }, (OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => { match &mut self.val { - OperandValue::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => { + OperandValueBuilder::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => { update(fst, a, from_sa); update(snd, b, from_sb); } - _ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"), + _ => bug!( + "Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}" + ), } } - _ => bug!("Unsupported operand {operand:?} inserting into {v:?}.{f:?} of {self:?}"), + (OperandValue::Ref(place), BackendRepr::Memory { .. }) => match &mut self.val { + OperandValueBuilder::Vector(val @ Either::Right(())) => { + let ibty = bx.cx().immediate_backend_type(self.layout); + let simd = bx.load_from_place(ibty, place); + *val = Either::Left(simd); + } + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } + }, + _ => bug!("Operand cannot be used with `insert_field`: {field_operand:?}"), } } /// Insert the immediate value `imm` for field `f` in the *type itself*, /// rather than into one of the variants. /// - /// Most things want [`OperandRef::insert_field`] instead, but this one is + /// Most things want [`Self::insert_field`] instead, but this one is /// necessary for writing things like enum tags that aren't in any variant. pub(super) fn insert_imm(&mut self, f: FieldIdx, imm: V) { let field_offset = self.layout.fields.offset(f.as_usize()); let is_zero_offset = field_offset == Size::ZERO; match &mut self.val { - OperandValue::Immediate(val @ Either::Right(_)) if is_zero_offset => { + OperandValueBuilder::Immediate(val @ Either::Right(_)) if is_zero_offset => { *val = Either::Left(imm); } - OperandValue::Pair(fst @ Either::Right(_), _) if is_zero_offset => { + OperandValueBuilder::Pair(fst @ Either::Right(_), _) if is_zero_offset => { *fst = Either::Left(imm); } - OperandValue::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { + OperandValueBuilder::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { *snd = Either::Left(imm); } _ => bug!("Tried to insert {imm:?} into field {f:?} of {self:?}"), } } - /// After having set all necessary fields, this converts the - /// `OperandValue>` (as obtained from [`OperandRef::builder`]) - /// to the normal `OperandValue`. + /// After having set all necessary fields, this converts the builder back + /// to the normal `OperandRef`. /// /// ICEs if any required fields were not set. - pub fn build(&self, cx: &impl CodegenMethods<'tcx, Value = V>) -> OperandRef<'tcx, V> { - let OperandRef { val, layout } = *self; + pub(super) fn build(&self, cx: &impl CodegenMethods<'tcx, Value = V>) -> OperandRef<'tcx, V> { + let OperandRefBuilder { val, layout } = *self; // For something like `Option::::None`, it's expected that the // payload scalar will not actually have been set, so this converts @@ -692,10 +741,22 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Either> { }; let val = match val { - OperandValue::ZeroSized => OperandValue::ZeroSized, - OperandValue::Immediate(v) => OperandValue::Immediate(unwrap(v)), - OperandValue::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)), - OperandValue::Ref(_) => bug!(), + OperandValueBuilder::ZeroSized => OperandValue::ZeroSized, + OperandValueBuilder::Immediate(v) => OperandValue::Immediate(unwrap(v)), + OperandValueBuilder::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)), + OperandValueBuilder::Vector(v) => match v { + Either::Left(v) => OperandValue::Immediate(v), + Either::Right(()) + if let BackendRepr::SimdVector { element, .. } = layout.backend_repr + && element.is_uninit_valid() => + { + let bty = cx.immediate_backend_type(layout); + OperandValue::Immediate(cx.const_undef(bty)) + } + Either::Right(()) => { + bug!("OperandRef::build called while fields are missing {self:?}") + } + }, }; OperandRef { val, layout } } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 43726e93252..2a7efdfe8bb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -4,10 +4,9 @@ use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_session::config::OptLevel; -use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; -use super::operand::{OperandRef, OperandValue}; +use super::operand::{OperandRef, OperandRefBuilder, OperandValue}; use super::place::{PlaceRef, codegen_tag_value}; use super::{FunctionCx, LocalRef}; use crate::common::{IntPredicate, TypeKind}; @@ -181,7 +180,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => { - assert!(self.rvalue_creates_operand(rvalue, DUMMY_SP)); + assert!(self.rvalue_creates_operand(rvalue)); let temp = self.codegen_rvalue_operand(bx, rvalue); temp.val.store(bx, dest); } @@ -354,10 +353,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx: &mut Bx, rvalue: &mir::Rvalue<'tcx>, ) -> OperandRef<'tcx, Bx::Value> { - assert!( - self.rvalue_creates_operand(rvalue, DUMMY_SP), - "cannot codegen {rvalue:?} to operand", - ); + assert!(self.rvalue_creates_operand(rvalue), "cannot codegen {rvalue:?} to operand",); match *rvalue { mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => { @@ -668,9 +664,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // `rvalue_creates_operand` has arranged that we only get here if // we can build the aggregate immediate from the field immediates. - let Some(mut builder) = OperandRef::builder(layout) else { - bug!("Cannot use type in operand builder: {layout:?}") - }; + let mut builder = OperandRefBuilder::new(layout); for (field_idx, field) in fields.iter_enumerated() { let op = self.codegen_operand(bx, field); let fi = active_field_index.unwrap_or(field_idx); @@ -980,7 +974,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// will not actually take the operand path because the result type is such /// that it always gets an `alloca`, but where it's not worth re-checking the /// layout in this code when the right thing will happen anyway. - pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool { + pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { match *rvalue { mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => { let operand_ty = operand.ty(self.mir, self.cx.tcx()); @@ -1025,18 +1019,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::NullaryOp(..) | mir::Rvalue::ThreadLocalRef(_) | mir::Rvalue::Use(..) | + mir::Rvalue::Aggregate(..) | // (*) mir::Rvalue::WrapUnsafeBinder(..) => // (*) true, // Arrays are always aggregates, so it's not worth checking anything here. // (If it's really `[(); N]` or `[T; 0]` and we use the place path, fine.) mir::Rvalue::Repeat(..) => false, - mir::Rvalue::Aggregate(..) => { - let ty = rvalue.ty(self.mir, self.cx.tcx()); - let ty = self.monomorphize(ty); - let layout = self.cx.spanned_layout_of(ty, span); - OperandRef::::builder(layout).is_some() - } - } + } // (*) this is only true if the type is suitable } diff --git a/tests/codegen/enum/enum-aggregate.rs b/tests/codegen/enum/enum-aggregate.rs index b6a9b8dd814..0161e5f3fa1 100644 --- a/tests/codegen/enum/enum-aggregate.rs +++ b/tests/codegen/enum/enum-aggregate.rs @@ -112,17 +112,14 @@ fn make_uninhabited_err_indirectly(n: Never) -> Result { #[no_mangle] fn make_fully_uninhabited_result(v: u32, n: Never) -> Result<(u32, Never), (Never, u32)> { - // We don't try to do this in SSA form since the whole type is uninhabited. + // Actually reaching this would be UB, so we don't actually build a result. // CHECK-LABEL: { i32, i32 } @make_fully_uninhabited_result(i32 %v) - // CHECK: %[[ALLOC_V:.+]] = alloca [4 x i8] - // CHECK: %[[RET:.+]] = alloca [8 x i8] - // CHECK: store i32 %v, ptr %[[ALLOC_V]] - // CHECK: %[[TEMP_V:.+]] = load i32, ptr %[[ALLOC_V]] - // CHECK: %[[INNER:.+]] = getelementptr inbounds i8, ptr %[[RET]] - // CHECK: store i32 %[[TEMP_V]], ptr %[[INNER]] - // CHECK: call void @llvm.trap() - // CHECK: unreachable + // CHECK-NEXT: start: + // CHECK-NEXT: call void @llvm.trap() + // CHECK-NEXT: call void @llvm.trap() + // CHECK-NEXT: call void @llvm.trap() + // CHECK-NEXT: unreachable Ok((v, n)) } diff --git a/tests/codegen/simd/aggregate-simd.rs b/tests/codegen/simd/aggregate-simd.rs new file mode 100644 index 00000000000..065e429a4c7 --- /dev/null +++ b/tests/codegen/simd/aggregate-simd.rs @@ -0,0 +1,106 @@ +//@ compile-flags: -C opt-level=3 -C no-prepopulate-passes +//@ only-64bit + +#![feature(core_intrinsics, repr_simd)] +#![no_std] +#![crate_type = "lib"] + +use core::intrinsics::simd::{simd_add, simd_extract}; + +#[repr(simd)] +#[derive(Clone, Copy)] +pub struct Simd([T; N]); + +#[repr(simd, packed)] +#[derive(Clone, Copy)] +pub struct PackedSimd([T; N]); + +#[repr(transparent)] +pub struct Transparent(T); + +// These tests don't actually care about the add/extract, but it ensures the +// aggregated temporaries are only used in potentially-SSA ways. + +#[no_mangle] +pub fn simd_aggregate_pot(x: [u32; 4], y: [u32; 4]) -> u32 { + // CHECK-LABEL: simd_aggregate_pot + // CHECK: %a = load <4 x i32>, ptr %x, align 4 + // CHECK: %b = load <4 x i32>, ptr %y, align 4 + // CHECK: add <4 x i32> %a, %b + + unsafe { + let a = Simd(x); + let b = Simd(y); + let c = simd_add(a, b); + simd_extract(c, 1) + } +} + +#[no_mangle] +pub fn simd_aggregate_npot(x: [u32; 7], y: [u32; 7]) -> u32 { + // CHECK-LABEL: simd_aggregate_npot + // CHECK: %a = load <7 x i32>, ptr %x, align 4 + // CHECK: %b = load <7 x i32>, ptr %y, align 4 + // CHECK: add <7 x i32> %a, %b + + unsafe { + let a = Simd(x); + let b = Simd(y); + let c = simd_add(a, b); + simd_extract(c, 1) + } +} + +#[no_mangle] +pub fn packed_simd_aggregate_pot(x: [u32; 4], y: [u32; 4]) -> u32 { + // CHECK-LABEL: packed_simd_aggregate_pot + // CHECK: %a = load <4 x i32>, ptr %x, align 4 + // CHECK: %b = load <4 x i32>, ptr %y, align 4 + // CHECK: add <4 x i32> %a, %b + + unsafe { + let a = PackedSimd(x); + let b = PackedSimd(y); + let c = simd_add(a, b); + simd_extract(c, 1) + } +} + +#[no_mangle] +pub fn packed_simd_aggregate_npot(x: [u32; 7], y: [u32; 7]) -> u32 { + // CHECK-LABEL: packed_simd_aggregate_npot + // CHECK: %b = alloca [28 x i8], align 4 + // CHECK: %a = alloca [28 x i8], align 4 + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %x, i64 28, i1 false) + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b, ptr align 4 %y, i64 28, i1 false) + // CHECK: %[[TEMPA:.+]] = load <7 x i32>, ptr %a, align 4 + // CHECK: %[[TEMPB:.+]] = load <7 x i32>, ptr %b, align 4 + // CHECK: add <7 x i32> %[[TEMPA]], %[[TEMPB]] + + unsafe { + let a = PackedSimd(x); + let b = PackedSimd(y); + let c = simd_add(a, b); + simd_extract(c, 1) + } +} + +#[no_mangle] +pub fn transparent_simd_aggregate(x: [u32; 4]) -> u32 { + // The transparent wrapper can just use the same SSA value as its field. + // No extra processing or spilling needed. + + // CHECK-LABEL: transparent_simd_aggregate + // CHECK-NOT: alloca + // CHECK: %[[RET:.+]] = alloca [4 x i8] + // CHECK-NOT: alloca + // CHECK: %a = load <4 x i32>, ptr %x, align 4 + // CHECK: %[[TEMP:.+]] = extractelement <4 x i32> %a, i32 1 + // CHECK: store i32 %[[TEMP]], ptr %[[RET]] + + unsafe { + let a = Simd(x); + let b = Transparent(a); + simd_extract(b.0, 1) + } +} diff --git a/tests/codegen/union-aggregate.rs b/tests/codegen/union-aggregate.rs index 3c6053379fa..aac66c5dcdd 100644 --- a/tests/codegen/union-aggregate.rs +++ b/tests/codegen/union-aggregate.rs @@ -4,6 +4,7 @@ #![crate_type = "lib"] #![feature(transparent_unions)] +#![feature(repr_simd)] #[repr(transparent)] union MU { @@ -83,3 +84,25 @@ fn make_mu_pair_uninit() -> MU<(u8, u32)> { // CHECK-NEXT: ret { i8, i32 } undef MU { uninit: () } } + +#[repr(simd)] +#[derive(Copy, Clone)] +struct I32X32([i32; 32]); + +#[no_mangle] +fn make_mu_simd(x: I32X32) -> MU { + // CHECK-LABEL: void @make_mu_simd(ptr{{.+}}%_0, ptr{{.+}}%x) + // CHECK-NEXT: start: + // CHECK-NEXT: %[[TEMP:.+]] = load <32 x i32>, ptr %x, + // CHECK-NEXT: store <32 x i32> %[[TEMP]], ptr %_0, + // CHECK-NEXT: ret void + MU { value: x } +} + +#[no_mangle] +fn make_mu_simd_uninit() -> MU { + // CHECK-LABEL: void @make_mu_simd_uninit(ptr{{.+}}%_0) + // CHECK-NEXT: start: + // CHECK-NEXT: ret void + MU { uninit: () } +} -- cgit 1.4.1-3-g733a5 From a4ea949356b8d40e7c588cccf30739fbb22cd45c Mon Sep 17 00:00:00 2001 From: Rémy Rakic Date: Mon, 17 Mar 2025 21:57:44 +0100 Subject: use LLD by default on x64 regardless of channel --- src/bootstrap/src/core/build_steps/compile.rs | 4 +--- src/bootstrap/src/core/config/toml/rust.rs | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 3e2bdc2d6b5..ca74771bb6e 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1369,9 +1369,7 @@ pub fn rustc_cargo_env( } // Enable rustc's env var for `rust-lld` when requested. - if builder.config.lld_enabled - && (builder.config.channel == "dev" || builder.config.channel == "nightly") - { + if builder.config.lld_enabled { cargo.env("CFG_USE_SELF_CONTAINED_LINKER", "1"); } diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index ac5eaea3bcb..0fae235bb93 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -619,7 +619,6 @@ impl Config { // build our internal lld and use it as the default linker, by setting the `rust.lld` config // to true by default: // - on the `x86_64-unknown-linux-gnu` target - // - on the `dev` and `nightly` channels // - when building our in-tree llvm (i.e. the target has not set an `llvm-config`), so that // we're also able to build the corresponding lld // - or when using an external llvm that's downloaded from CI, which also contains our prebuilt @@ -628,9 +627,7 @@ impl Config { // thus, disabled // - similarly, lld will not be built nor used by default when explicitly asked not to, e.g. // when the config sets `rust.lld = false` - if self.host_target.triple == "x86_64-unknown-linux-gnu" - && self.hosts == [self.host_target] - && (self.channel == "dev" || self.channel == "nightly") + if self.host_target.triple == "x86_64-unknown-linux-gnu" && self.hosts == [self.host_target] { let no_llvm_config = self .target_config -- cgit 1.4.1-3-g733a5 From aa527115432df082f3c9e4d2459acb7bb02ed5a6 Mon Sep 17 00:00:00 2001 From: Rémy Rakic Date: Tue, 18 Mar 2025 08:17:38 +0100 Subject: add post-dist test for checking that we use LLD And remove the previous beta/stable/nightly LLD tests. --- src/tools/opt-dist/src/tests.rs | 3 +++ .../run-make/rust-lld-by-default-beta-stable/main.rs | 1 - .../rust-lld-by-default-beta-stable/rmake.rs | 14 -------------- tests/run-make/rust-lld-by-default-nightly/main.rs | 5 ----- tests/run-make/rust-lld-by-default-nightly/rmake.rs | 19 ------------------- .../rust-lld-x86_64-unknown-linux-gnu-dist/main.rs | 5 +++++ .../rust-lld-x86_64-unknown-linux-gnu-dist/rmake.rs | 16 ++++++++++++++++ .../rust-lld-x86_64-unknown-linux-gnu/main.rs | 5 +++++ .../rust-lld-x86_64-unknown-linux-gnu/rmake.rs | 20 ++++++++++++++++++++ 9 files changed, 49 insertions(+), 39 deletions(-) delete mode 100644 tests/run-make/rust-lld-by-default-beta-stable/main.rs delete mode 100644 tests/run-make/rust-lld-by-default-beta-stable/rmake.rs delete mode 100644 tests/run-make/rust-lld-by-default-nightly/main.rs delete mode 100644 tests/run-make/rust-lld-by-default-nightly/rmake.rs create mode 100644 tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/main.rs create mode 100644 tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/rmake.rs create mode 100644 tests/run-make/rust-lld-x86_64-unknown-linux-gnu/main.rs create mode 100644 tests/run-make/rust-lld-x86_64-unknown-linux-gnu/rmake.rs diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs index 705a1750ae8..2d2aab86eda 100644 --- a/src/tools/opt-dist/src/tests.rs +++ b/src/tools/opt-dist/src/tests.rs @@ -106,7 +106,10 @@ llvm-config = "{llvm_config}" "tests/incremental", "tests/mir-opt", "tests/pretty", + // Make sure that we don't use too new GLIBC symbols on x64 "tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu", + // Make sure that we use LLD by default on x64 + "tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist", "tests/ui", "tests/crashes", ]; diff --git a/tests/run-make/rust-lld-by-default-beta-stable/main.rs b/tests/run-make/rust-lld-by-default-beta-stable/main.rs deleted file mode 100644 index f328e4d9d04..00000000000 --- a/tests/run-make/rust-lld-by-default-beta-stable/main.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() {} diff --git a/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs b/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs deleted file mode 100644 index 9a08991c4b8..00000000000 --- a/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Ensure that rust-lld is *not* used as the default linker on `x86_64-unknown-linux-gnu` on stable -// or beta. - -//@ ignore-nightly -//@ only-x86_64-unknown-linux-gnu - -use run_make_support::linker::assert_rustc_doesnt_use_lld; -use run_make_support::rustc; - -fn main() { - // A regular compilation should not use rust-lld by default. We'll check that by asking the - // linker to display its version number with a link-arg. - assert_rustc_doesnt_use_lld(rustc().input("main.rs")); -} diff --git a/tests/run-make/rust-lld-by-default-nightly/main.rs b/tests/run-make/rust-lld-by-default-nightly/main.rs deleted file mode 100644 index e9f655fc09e..00000000000 --- a/tests/run-make/rust-lld-by-default-nightly/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Test linking using `cc` with `rust-lld`, which is on by default on the x86_64-unknown-linux-gnu -// target. -// See https://github.com/rust-lang/compiler-team/issues/510 for more info - -fn main() {} diff --git a/tests/run-make/rust-lld-by-default-nightly/rmake.rs b/tests/run-make/rust-lld-by-default-nightly/rmake.rs deleted file mode 100644 index 3ff1e2770e6..00000000000 --- a/tests/run-make/rust-lld-by-default-nightly/rmake.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Ensure that rust-lld is used as the default linker on `x86_64-unknown-linux-gnu` on the nightly -// channel, and that it can also be turned off with a CLI flag. - -//@ needs-rust-lld -//@ ignore-beta -//@ ignore-stable -//@ only-x86_64-unknown-linux-gnu - -use run_make_support::linker::{assert_rustc_doesnt_use_lld, assert_rustc_uses_lld}; -use run_make_support::rustc; - -fn main() { - // A regular compilation should use rust-lld by default. We'll check that by asking the linker - // to display its version number with a link-arg. - assert_rustc_uses_lld(rustc().input("main.rs")); - - // But it can still be disabled by turning the linker feature off. - assert_rustc_doesnt_use_lld(rustc().arg("-Zlinker-features=-lld").input("main.rs")); -} diff --git a/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/main.rs b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/main.rs new file mode 100644 index 00000000000..e9f655fc09e --- /dev/null +++ b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/main.rs @@ -0,0 +1,5 @@ +// Test linking using `cc` with `rust-lld`, which is on by default on the x86_64-unknown-linux-gnu +// target. +// See https://github.com/rust-lang/compiler-team/issues/510 for more info + +fn main() {} diff --git a/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/rmake.rs b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/rmake.rs new file mode 100644 index 00000000000..c26f82b7d37 --- /dev/null +++ b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/rmake.rs @@ -0,0 +1,16 @@ +// Ensure that rust-lld is used as the default linker on `x86_64-unknown-linux-gnu` +// dist artifacts and that it can also be turned off with a CLI flag. + +//@ only-dist +//@ only-x86_64-unknown-linux-gnu + +use run_make_support::linker::{assert_rustc_doesnt_use_lld, assert_rustc_uses_lld}; +use run_make_support::rustc; + +fn main() { + // A regular compilation should use rust-lld by default. + assert_rustc_uses_lld(rustc().input("main.rs")); + + // But it can still be disabled by turning the linker feature off. + assert_rustc_doesnt_use_lld(rustc().arg("-Zlinker-features=-lld").input("main.rs")); +} diff --git a/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/main.rs b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/main.rs new file mode 100644 index 00000000000..e9f655fc09e --- /dev/null +++ b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/main.rs @@ -0,0 +1,5 @@ +// Test linking using `cc` with `rust-lld`, which is on by default on the x86_64-unknown-linux-gnu +// target. +// See https://github.com/rust-lang/compiler-team/issues/510 for more info + +fn main() {} diff --git a/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/rmake.rs b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/rmake.rs new file mode 100644 index 00000000000..e71a47f11e2 --- /dev/null +++ b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/rmake.rs @@ -0,0 +1,20 @@ +// Ensure that rust-lld is used as the default linker on `x86_64-unknown-linux-gnu` +// and that it can also be turned off with a CLI flag. +// +// This version of the test checks that LLD is used by default when LLD is enabled in the +// toolchain. There is a separate test that checks that LLD is used for dist artifacts +// unconditionally. + +//@ needs-rust-lld +//@ only-x86_64-unknown-linux-gnu + +use run_make_support::linker::{assert_rustc_doesnt_use_lld, assert_rustc_uses_lld}; +use run_make_support::rustc; + +fn main() { + // A regular compilation should use rust-lld by default. + assert_rustc_uses_lld(rustc().input("main.rs")); + + // But it can still be disabled by turning the linker feature off. + assert_rustc_doesnt_use_lld(rustc().arg("-Zlinker-features=-lld").input("main.rs")); +} -- cgit 1.4.1-3-g733a5 From 2e6d82c9c96348a2f7dafd1a3337287408fc9751 Mon Sep 17 00:00:00 2001 From: Rémy Rakic Date: Tue, 8 Jul 2025 09:01:25 +0000 Subject: stabilize `-Clinker-features=-lld` on x64 linux This stabilizes a subset of the `-Clinker-features` components on x64 linux: the lld opt-out. The opt-in is not stabilized, as interactions with other stable flags require more internal work, but are not needed for stabilizing using rust-lld by default. Similarly, since we only switch to rust-lld on x64 linux, the opt-out is only stabilized there. Other targets still require `-Zunstable-options` to use it. --- compiler/rustc_codegen_ssa/src/back/link.rs | 2 +- compiler/rustc_session/src/config.rs | 48 +++++++++++++++++++--- compiler/rustc_session/src/options.rs | 4 +- compiler/rustc_target/src/spec/mod.rs | 15 ++++++- tests/run-make/compressed-debuginfo-zstd/rmake.rs | 2 +- tests/run-make/rust-lld-custom-target/rmake.rs | 3 +- .../run-make/rust-lld-link-script-provide/rmake.rs | 2 +- .../rmake.rs | 2 +- .../rust-lld-x86_64-unknown-linux-gnu/rmake.rs | 2 +- tests/run-make/rust-lld/rmake.rs | 10 ++--- 10 files changed, 70 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 343cb0eeca9..34328687e3f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1379,7 +1379,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { } } - let features = sess.opts.unstable_opts.linker_features; + let features = sess.opts.cg.linker_features; // linker and linker flavor specified via command line have precedence over what the target // specification specifies diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 4627c2978fc..76969a0c409 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -402,7 +402,7 @@ impl LinkSelfContained { } } -/// The different values that `-Z linker-features` can take on the CLI: a list of individually +/// The different values that `-C linker-features` can take on the CLI: a list of individually /// enabled or disabled features used during linking. /// /// There is no need to enable or disable them in bulk. Each feature is fine-grained, and can be @@ -442,6 +442,39 @@ impl LinkerFeaturesCli { _ => None, } } + + /// When *not* using `-Z unstable-options` on the CLI, ensure only stable linker features are + /// used, for the given `TargetTuple`. Returns `Ok` if no unstable variants are used. + /// The caller should ensure that e.g. `nightly_options::is_unstable_enabled()` + /// returns false. + pub(crate) fn check_unstable_variants(&self, target_tuple: &TargetTuple) -> Result<(), String> { + // `-C linker-features=-lld` is only stable on x64 linux. + let has_minus_lld = self.disabled.is_lld_enabled(); + if has_minus_lld && target_tuple.tuple() != "x86_64-unknown-linux-gnu" { + return Err(format!( + "`-C linker-features=-lld` is unstable on the `{target_tuple}` \ + target. The `-Z unstable-options` flag must also be passed to use it on this target", + )); + } + + // Any `+lld` or non-lld feature used is unstable, and that's an error. + let unstable_enabled = self.enabled; + let unstable_disabled = self.disabled - LinkerFeatures::LLD; + if !unstable_enabled.union(unstable_disabled).is_empty() { + let unstable_features: Vec<_> = unstable_enabled + .iter() + .map(|f| format!("+{}", f.as_str().unwrap())) + .chain(unstable_disabled.iter().map(|f| format!("-{}", f.as_str().unwrap()))) + .collect(); + return Err(format!( + "`-C linker-features={}` is unstable, and also requires the \ + `-Z unstable-options` flag to be used", + unstable_features.join(","), + )); + } + + Ok(()) + } } /// Used with `-Z assert-incr-state`. @@ -2638,9 +2671,8 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } } - if !nightly_options::is_unstable_enabled(matches) - && cg.force_frame_pointers == FramePointer::NonLeaf - { + let unstable_options_enabled = nightly_options::is_unstable_enabled(matches); + if !unstable_options_enabled && cg.force_frame_pointers == FramePointer::NonLeaf { early_dcx.early_fatal( "`-Cforce-frame-pointers=non-leaf` or `always` also requires `-Zunstable-options` \ and a nightly compiler", @@ -2650,7 +2682,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M // For testing purposes, until we have more feedback about these options: ensure `-Z // unstable-options` is required when using the unstable `-C link-self-contained` and `-C // linker-flavor` options. - if !nightly_options::is_unstable_enabled(matches) { + if !unstable_options_enabled { let uses_unstable_self_contained_option = cg.link_self_contained.are_unstable_variants_set(); if uses_unstable_self_contained_option { @@ -2706,6 +2738,12 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let debuginfo = select_debuginfo(matches, &cg); let debuginfo_compression = unstable_opts.debuginfo_compression; + if !unstable_options_enabled { + if let Err(error) = cg.linker_features.check_unstable_variants(&target_triple) { + early_dcx.early_fatal(error); + } + } + let crate_name = matches.opt_str("crate-name"); let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref()); // Parse any `-l` flags, which link to native libraries. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index ecd82c0cc01..626262c8442 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2015,6 +2015,8 @@ options! { on a C toolchain or linker installed in the system"), linker: Option = (None, parse_opt_pathbuf, [UNTRACKED], "system linker to link outputs with"), + linker_features: LinkerFeaturesCli = (LinkerFeaturesCli::default(), parse_linker_features, [UNTRACKED], + "a comma-separated list of linker features to enable (+) or disable (-): `lld`"), linker_flavor: Option = (None, parse_linker_flavor, [UNTRACKED], "linker flavor"), linker_plugin_lto: LinkerPluginLto = (LinkerPluginLto::Disabled, @@ -2307,8 +2309,6 @@ options! { "link native libraries in the linker invocation (default: yes)"), link_only: bool = (false, parse_bool, [TRACKED], "link the `.rlink` file generated by `-Z no-link` (default: no)"), - linker_features: LinkerFeaturesCli = (LinkerFeaturesCli::default(), parse_linker_features, [UNTRACKED], - "a comma-separated list of linker features to enable (+) or disable (-): `lld`"), lint_llvm_ir: bool = (false, parse_bool, [TRACKED], "lint LLVM IR (default: no)"), lint_mir: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 5346b206a17..4bc0d88a910 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -725,7 +725,7 @@ impl ToJson for LinkSelfContainedComponents { } bitflags::bitflags! { - /// The `-Z linker-features` components that can individually be enabled or disabled. + /// The `-C linker-features` components that can individually be enabled or disabled. /// /// They are feature flags intended to be a more flexible mechanism than linker flavors, and /// also to prevent a combinatorial explosion of flavors whenever a new linker feature is @@ -756,7 +756,7 @@ bitflags::bitflags! { rustc_data_structures::external_bitflags_debug! { LinkerFeatures } impl LinkerFeatures { - /// Parses a single `-Z linker-features` well-known feature, not a set of flags. + /// Parses a single `-C linker-features` well-known feature, not a set of flags. pub fn from_str(s: &str) -> Option { Some(match s { "cc" => LinkerFeatures::CC, @@ -765,6 +765,17 @@ impl LinkerFeatures { }) } + /// Return the linker feature name, as would be passed on the CLI. + /// + /// Returns `None` if the bitflags aren't a singular component (but a mix of multiple flags). + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + LinkerFeatures::CC => "cc", + LinkerFeatures::LLD => "lld", + _ => return None, + }) + } + /// Returns whether the `lld` linker feature is enabled. pub fn is_lld_enabled(self) -> bool { self.contains(LinkerFeatures::LLD) diff --git a/tests/run-make/compressed-debuginfo-zstd/rmake.rs b/tests/run-make/compressed-debuginfo-zstd/rmake.rs index cd8cf223047..8d7e5c089da 100644 --- a/tests/run-make/compressed-debuginfo-zstd/rmake.rs +++ b/tests/run-make/compressed-debuginfo-zstd/rmake.rs @@ -26,7 +26,7 @@ fn prepare_and_check &mut Rustc>(to_find: &str, prepare run_in_tmpdir(|| { let mut rustc = Rustc::new(); rustc - .arg("-Zlinker-features=+lld") + .arg("-Clinker-features=+lld") .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") .arg("-Cdebuginfo=full") diff --git a/tests/run-make/rust-lld-custom-target/rmake.rs b/tests/run-make/rust-lld-custom-target/rmake.rs index e2b065a10b1..90ba424ffe9 100644 --- a/tests/run-make/rust-lld-custom-target/rmake.rs +++ b/tests/run-make/rust-lld-custom-target/rmake.rs @@ -23,7 +23,8 @@ fn main() { rustc() .crate_type("cdylib") .target("custom-target.json") - .arg("-Zlinker-features=-lld") + .arg("-Clinker-features=-lld") + .arg("-Zunstable-options") .input("lib.rs"), ); } diff --git a/tests/run-make/rust-lld-link-script-provide/rmake.rs b/tests/run-make/rust-lld-link-script-provide/rmake.rs index e78a411bc15..c637dff9038 100644 --- a/tests/run-make/rust-lld-link-script-provide/rmake.rs +++ b/tests/run-make/rust-lld-link-script-provide/rmake.rs @@ -10,7 +10,7 @@ use run_make_support::rustc; fn main() { rustc() .input("main.rs") - .arg("-Zlinker-features=+lld") + .arg("-Clinker-features=+lld") .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") .link_arg("-Tscript.t") diff --git a/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/rmake.rs b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/rmake.rs index c26f82b7d37..c315d36a39d 100644 --- a/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/rmake.rs +++ b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist/rmake.rs @@ -12,5 +12,5 @@ fn main() { assert_rustc_uses_lld(rustc().input("main.rs")); // But it can still be disabled by turning the linker feature off. - assert_rustc_doesnt_use_lld(rustc().arg("-Zlinker-features=-lld").input("main.rs")); + assert_rustc_doesnt_use_lld(rustc().arg("-Clinker-features=-lld").input("main.rs")); } diff --git a/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/rmake.rs b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/rmake.rs index e71a47f11e2..00415d27aaf 100644 --- a/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/rmake.rs +++ b/tests/run-make/rust-lld-x86_64-unknown-linux-gnu/rmake.rs @@ -16,5 +16,5 @@ fn main() { assert_rustc_uses_lld(rustc().input("main.rs")); // But it can still be disabled by turning the linker feature off. - assert_rustc_doesnt_use_lld(rustc().arg("-Zlinker-features=-lld").input("main.rs")); + assert_rustc_doesnt_use_lld(rustc().arg("-Clinker-features=-lld").input("main.rs")); } diff --git a/tests/run-make/rust-lld/rmake.rs b/tests/run-make/rust-lld/rmake.rs index 9470f5d0be1..2b914f19ac0 100644 --- a/tests/run-make/rust-lld/rmake.rs +++ b/tests/run-make/rust-lld/rmake.rs @@ -12,14 +12,14 @@ fn main() { // asking the linker to display its version number with a link-arg. assert_rustc_uses_lld( rustc() - .arg("-Zlinker-features=+lld") + .arg("-Clinker-features=+lld") .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") .input("main.rs"), ); // It should not be used when we explicitly opt out of lld. - assert_rustc_doesnt_use_lld(rustc().arg("-Zlinker-features=-lld").input("main.rs")); + assert_rustc_doesnt_use_lld(rustc().arg("-Clinker-features=-lld").input("main.rs")); // While we're here, also check that the last linker feature flag "wins" when passed multiple // times to rustc. @@ -27,9 +27,9 @@ fn main() { rustc() .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") - .arg("-Zlinker-features=-lld") - .arg("-Zlinker-features=+lld") - .arg("-Zlinker-features=-lld,+lld") + .arg("-Clinker-features=-lld") + .arg("-Clinker-features=+lld") + .arg("-Clinker-features=-lld,+lld") .input("main.rs"), ); } -- cgit 1.4.1-3-g733a5 From 3f194a86eeb4e8cf23556bc92946728f29c21da4 Mon Sep 17 00:00:00 2001 From: Rémy Rakic Date: Tue, 8 Jul 2025 09:10:39 +0000 Subject: stabilize `-Clink-self-contained=-linker` on x64 linux This stabilizes a subset of the `-Clink-self-contained` components on x64 linux: the rust-lld opt-out. The opt-in is not stabilized, as interactions with other stable flags require more internal work, but are not needed for stabilizing using rust-lld by default. Similarly, since we only switch to rust-lld on x64 linux, the opt-out is only stabilized there. Other targets still require `-Zunstable-options` to use it. --- compiler/rustc_session/src/config.rs | 51 ++++++++++++++++++++++++------------ tests/run-make/rust-lld/rmake.rs | 10 ++++--- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 76969a0c409..a91e2140fd4 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -370,12 +370,34 @@ impl LinkSelfContained { } /// To help checking CLI usage while some of the values are unstable: returns whether one of the - /// components was set individually. This would also require the `-Zunstable-options` flag, to - /// be allowed. - fn are_unstable_variants_set(&self) -> bool { - let any_component_set = - !self.enabled_components.is_empty() || !self.disabled_components.is_empty(); - self.explicitly_set.is_none() && any_component_set + /// unstable components was set individually, for the given `TargetTuple`. This would also + /// require the `-Zunstable-options` flag, to be allowed. + fn check_unstable_variants(&self, target_tuple: &TargetTuple) -> Result<(), String> { + if self.explicitly_set.is_some() { + return Ok(()); + } + + // `-C link-self-contained=-linker` is only stable on x64 linux. + let has_minus_linker = self.disabled_components.is_linker_enabled(); + if has_minus_linker && target_tuple.tuple() != "x86_64-unknown-linux-gnu" { + return Err(format!( + "`-C link-self-contained=-linker` is unstable on the `{target_tuple}` \ + target. The `-Z unstable-options` flag must also be passed to use it on this target", + )); + } + + // Any `+linker` or other component used is unstable, and that's an error. + let unstable_enabled = self.enabled_components; + let unstable_disabled = self.disabled_components - LinkSelfContainedComponents::LINKER; + if !unstable_enabled.union(unstable_disabled).is_empty() { + return Err(String::from( + "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` \ + are stable, the `-Z unstable-options` flag must also be passed to use \ + the unstable values", + )); + } + + Ok(()) } /// Returns whether the self-contained linker component was enabled on the CLI, using the @@ -2679,17 +2701,13 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M ) } - // For testing purposes, until we have more feedback about these options: ensure `-Z - // unstable-options` is required when using the unstable `-C link-self-contained` and `-C - // linker-flavor` options. + let target_triple = parse_target_triple(early_dcx, matches); + + // Ensure `-Z unstable-options` is required when using the unstable `-C link-self-contained` and + // `-C linker-flavor` options. if !unstable_options_enabled { - let uses_unstable_self_contained_option = - cg.link_self_contained.are_unstable_variants_set(); - if uses_unstable_self_contained_option { - early_dcx.early_fatal( - "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off` are stable, \ - the `-Z unstable-options` flag must also be passed to use the unstable values", - ); + if let Err(error) = cg.link_self_contained.check_unstable_variants(&target_triple) { + early_dcx.early_fatal(error); } if let Some(flavor) = cg.linker_flavor { @@ -2729,7 +2747,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let cg = cg; - let target_triple = parse_target_triple(early_dcx, matches); let opt_level = parse_opt_level(early_dcx, matches, &cg); // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) diff --git a/tests/run-make/rust-lld/rmake.rs b/tests/run-make/rust-lld/rmake.rs index 2b914f19ac0..932c2697ba0 100644 --- a/tests/run-make/rust-lld/rmake.rs +++ b/tests/run-make/rust-lld/rmake.rs @@ -1,5 +1,5 @@ -// Test linking using `cc` with `rust-lld`, using the unstable CLI described in MCP 510 -// see https://github.com/rust-lang/compiler-team/issues/510 for more info +// Test linking using `cc` with `rust-lld`, using the `-Clinker-features` and +// `-Clink-self-contained` CLI flags. //@ needs-rust-lld //@ ignore-s390x lld does not yet support s390x as target @@ -14,12 +14,14 @@ fn main() { rustc() .arg("-Clinker-features=+lld") .arg("-Clink-self-contained=+linker") - .arg("-Zunstable-options") + .arg("-Zunstable-options") // the opt-ins are unstable .input("main.rs"), ); // It should not be used when we explicitly opt out of lld. - assert_rustc_doesnt_use_lld(rustc().arg("-Clinker-features=-lld").input("main.rs")); + assert_rustc_doesnt_use_lld( + rustc().arg("-Clinker-features=-lld").arg("-Zunstable-options").input("main.rs"), + ); // While we're here, also check that the last linker feature flag "wins" when passed multiple // times to rustc. -- cgit 1.4.1-3-g733a5 From d6179022c1d86f6c3e63a64933dff2734b366935 Mon Sep 17 00:00:00 2001 From: Rémy Rakic Date: Tue, 8 Jul 2025 09:20:14 +0000 Subject: update bootstrap mcp510 handling beta and stage1 need to use different flags (-C vs -Z) to be able to use the old and new `linker-features` and `link-self-contained` flags --- src/bootstrap/src/core/build_steps/test.rs | 20 ++++++++++++++++---- src/bootstrap/src/core/builder/cargo.rs | 15 ++++++++++----- src/bootstrap/src/core/builder/mod.rs | 2 +- src/bootstrap/src/utils/helpers.rs | 25 +++++++++++++++++++------ 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index a5b7b22aba8..716bef3f38c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -262,7 +262,13 @@ impl Step for Cargotest { .args(builder.config.test_args()) .env("RUSTC", builder.rustc(compiler)) .env("RUSTDOC", builder.rustdoc(compiler)); - add_rustdoc_cargo_linker_args(&mut cmd, builder, compiler.host, LldThreads::No); + add_rustdoc_cargo_linker_args( + &mut cmd, + builder, + compiler.host, + LldThreads::No, + compiler.stage, + ); cmd.delay_failure().run(builder); } } @@ -857,7 +863,7 @@ impl Step for RustdocTheme { .env("CFG_RELEASE_CHANNEL", &builder.config.channel) .env("RUSTDOC_REAL", builder.rustdoc(self.compiler)) .env("RUSTC_BOOTSTRAP", "1"); - cmd.args(linker_args(builder, self.compiler.host, LldThreads::No)); + cmd.args(linker_args(builder, self.compiler.host, LldThreads::No, self.compiler.stage)); cmd.delay_failure().run(builder); } @@ -1033,7 +1039,13 @@ impl Step for RustdocGUI { cmd.env("RUSTDOC", builder.rustdoc(self.compiler)) .env("RUSTC", builder.rustc(self.compiler)); - add_rustdoc_cargo_linker_args(&mut cmd, builder, self.compiler.host, LldThreads::No); + add_rustdoc_cargo_linker_args( + &mut cmd, + builder, + self.compiler.host, + LldThreads::No, + self.compiler.stage, + ); for path in &builder.paths { if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) { @@ -1812,7 +1824,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } let mut hostflags = flags.clone(); - hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No)); + hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No, compiler.stage)); let mut targetflags = flags; diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index deb7106f185..065d7e45e0f 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -115,7 +115,7 @@ impl Cargo { // No need to configure the target linker for these command types. Kind::Clean | Kind::Check | Kind::Suggest | Kind::Format | Kind::Setup => {} _ => { - cargo.configure_linker(builder); + cargo.configure_linker(builder, mode); } } @@ -209,7 +209,7 @@ impl Cargo { // FIXME(onur-ozkan): Add coverage to make sure modifications to this function // doesn't cause cache invalidations (e.g., #130108). - fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo { + fn configure_linker(&mut self, builder: &Builder<'_>, mode: Mode) -> &mut Cargo { let target = self.target; let compiler = self.compiler; @@ -264,7 +264,12 @@ impl Cargo { } } - for arg in linker_args(builder, compiler.host, LldThreads::Yes) { + // We use the snapshot compiler when building host code (build scripts/proc macros) of + // `Mode::Std` tools, so we need to determine the current stage here to pass the proper + // linker args (e.g. -C vs -Z). + // This should stay synchronized with the [cargo] function. + let host_stage = if mode == Mode::Std { 0 } else { compiler.stage }; + for arg in linker_args(builder, compiler.host, LldThreads::Yes, host_stage) { self.hostflags.arg(&arg); } @@ -274,10 +279,10 @@ impl Cargo { } // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not // `linker_args` here. - for flag in linker_flags(builder, target, LldThreads::Yes) { + for flag in linker_flags(builder, target, LldThreads::Yes, compiler.stage) { self.rustflags.arg(&flag); } - for arg in linker_args(builder, target, LldThreads::Yes) { + for arg in linker_args(builder, target, LldThreads::Yes, compiler.stage) { self.rustdocflags.arg(&arg); } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index b96a988cde3..7464327fde9 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1599,7 +1599,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s cmd.arg("-Dwarnings"); } cmd.arg("-Znormalize-docs"); - cmd.args(linker_args(self, compiler.host, LldThreads::Yes)); + cmd.args(linker_args(self, compiler.host, LldThreads::Yes, compiler.stage)); cmd } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 3c5f612daa7..eb00ed566c2 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -404,8 +404,9 @@ pub fn linker_args( builder: &Builder<'_>, target: TargetSelection, lld_threads: LldThreads, + stage: u32, ) -> Vec { - let mut args = linker_flags(builder, target, lld_threads); + let mut args = linker_flags(builder, target, lld_threads, stage); if let Some(linker) = builder.linker(target) { args.push(format!("-Clinker={}", linker.display())); @@ -420,19 +421,30 @@ pub fn linker_flags( builder: &Builder<'_>, target: TargetSelection, lld_threads: LldThreads, + stage: u32, ) -> Vec { let mut args = vec![]; if !builder.is_lld_direct_linker(target) && builder.config.lld_mode.is_used() { match builder.config.lld_mode { LldMode::External => { - args.push("-Zlinker-features=+lld".to_string()); - // FIXME(kobzol): remove this flag once MCP510 gets stabilized + // cfg(bootstrap) - remove the stage 0 check after updating the bootstrap compiler: + // `-Clinker-features` has been stabilized. + if stage == 0 { + args.push("-Zlinker-features=+lld".to_string()); + } else { + args.push("-Clinker-features=+lld".to_string()); + } args.push("-Zunstable-options".to_string()); } LldMode::SelfContained => { - args.push("-Zlinker-features=+lld".to_string()); + // cfg(bootstrap) - remove the stage 0 check after updating the bootstrap compiler: + // `-Clinker-features` has been stabilized. + if stage == 0 { + args.push("-Zlinker-features=+lld".to_string()); + } else { + args.push("-Clinker-features=+lld".to_string()); + } args.push("-Clink-self-contained=+linker".to_string()); - // FIXME(kobzol): remove this flag once MCP510 gets stabilized args.push("-Zunstable-options".to_string()); } LldMode::Unused => unreachable!(), @@ -453,8 +465,9 @@ pub fn add_rustdoc_cargo_linker_args( builder: &Builder<'_>, target: TargetSelection, lld_threads: LldThreads, + stage: u32, ) { - let args = linker_args(builder, target, lld_threads); + let args = linker_args(builder, target, lld_threads, stage); let mut flags = cmd .get_envs() .find_map(|(k, v)| if k == OsStr::new("RUSTDOCFLAGS") { v } else { None }) -- cgit 1.4.1-3-g733a5 From 856c66256781ef3f9b0934b11abb2a6edcfb487f Mon Sep 17 00:00:00 2001 From: Rémy Rakic Date: Tue, 8 Jul 2025 09:02:14 +0000 Subject: expand `-Clinker-features` tests - ensure only `-Clinker-features=-lld` is stabilized, and only on x64 linux - test `-Clinker-features` unstable values --- tests/ui/linking/linker-features-lld-disallowed.rs | 19 ++++++++++++++++ ...eatures-lld-disallowed.unstable_positive.stderr | 2 ++ ...-lld-disallowed.unstable_target_negative.stderr | 2 ++ ...-lld-disallowed.unstable_target_positive.stderr | 2 ++ ...nker-features-malformed.invalid_modifier.stderr | 2 +- ...ker-features-malformed.invalid_separator.stderr | 2 +- .../linker-features-malformed.no_value.stderr | 2 +- tests/ui/linking/linker-features-malformed.rs | 26 +++++++++++----------- ...inker-features-malformed.unknown_boolean.stderr | 2 +- ...eatures-malformed.unknown_modifier_value.stderr | 2 +- .../linker-features-malformed.unknown_value.stderr | 2 +- tests/ui/linking/linker-features-unstable-cc.rs | 13 +++++++++++ .../ui/linking/linker-features-unstable-cc.stderr | 2 ++ 13 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 tests/ui/linking/linker-features-lld-disallowed.rs create mode 100644 tests/ui/linking/linker-features-lld-disallowed.unstable_positive.stderr create mode 100644 tests/ui/linking/linker-features-lld-disallowed.unstable_target_negative.stderr create mode 100644 tests/ui/linking/linker-features-lld-disallowed.unstable_target_positive.stderr create mode 100644 tests/ui/linking/linker-features-unstable-cc.rs create mode 100644 tests/ui/linking/linker-features-unstable-cc.stderr diff --git a/tests/ui/linking/linker-features-lld-disallowed.rs b/tests/ui/linking/linker-features-lld-disallowed.rs new file mode 100644 index 00000000000..9b8fa2b11e6 --- /dev/null +++ b/tests/ui/linking/linker-features-lld-disallowed.rs @@ -0,0 +1,19 @@ +// Check that only `-C linker-features=-lld` is stable on x64 linux. Any other value or target, +// needs `-Z unstable-options`. + +// ignore-tidy-linelength + +//@ revisions: unstable_target_positive unstable_target_negative unstable_positive +//@ [unstable_target_negative] compile-flags: --target=x86_64-unknown-linux-musl -C linker-features=-lld --crate-type=rlib +//@ [unstable_target_negative] needs-llvm-components: x86 +//@ [unstable_target_positive] compile-flags: --target=x86_64-unknown-linux-musl -C linker-features=+lld --crate-type=rlib +//@ [unstable_target_positive] needs-llvm-components: x86 +//@ [unstable_positive] compile-flags: --target=x86_64-unknown-linux-gnu -C linker-features=+lld --crate-type=rlib +//@ [unstable_positive] needs-llvm-components: x86 + + +#![feature(no_core)] +#![no_core] + +//[unstable_target_negative]~? ERROR `-C linker-features=-lld` is unstable on the `x86_64-unknown-linux-musl` target +//[unstable_target_positive,unstable_positive]~? ERROR `-C linker-features=+lld` is unstable, and also requires the `-Z unstable-options` diff --git a/tests/ui/linking/linker-features-lld-disallowed.unstable_positive.stderr b/tests/ui/linking/linker-features-lld-disallowed.unstable_positive.stderr new file mode 100644 index 00000000000..09e7e4975c4 --- /dev/null +++ b/tests/ui/linking/linker-features-lld-disallowed.unstable_positive.stderr @@ -0,0 +1,2 @@ +error: `-C linker-features=+lld` is unstable, and also requires the `-Z unstable-options` flag to be used + diff --git a/tests/ui/linking/linker-features-lld-disallowed.unstable_target_negative.stderr b/tests/ui/linking/linker-features-lld-disallowed.unstable_target_negative.stderr new file mode 100644 index 00000000000..205082b0726 --- /dev/null +++ b/tests/ui/linking/linker-features-lld-disallowed.unstable_target_negative.stderr @@ -0,0 +1,2 @@ +error: `-C linker-features=-lld` is unstable on the `x86_64-unknown-linux-musl` target. The `-Z unstable-options` flag must also be passed to use it on this target + diff --git a/tests/ui/linking/linker-features-lld-disallowed.unstable_target_positive.stderr b/tests/ui/linking/linker-features-lld-disallowed.unstable_target_positive.stderr new file mode 100644 index 00000000000..09e7e4975c4 --- /dev/null +++ b/tests/ui/linking/linker-features-lld-disallowed.unstable_target_positive.stderr @@ -0,0 +1,2 @@ +error: `-C linker-features=+lld` is unstable, and also requires the `-Z unstable-options` flag to be used + diff --git a/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr b/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr index 909b277089f..d9ed65ad3e2 100644 --- a/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr +++ b/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr @@ -1,2 +1,2 @@ -error: incorrect value `*lld` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected +error: incorrect value `*lld` for codegen option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected diff --git a/tests/ui/linking/linker-features-malformed.invalid_separator.stderr b/tests/ui/linking/linker-features-malformed.invalid_separator.stderr index 0f84898a774..e950d8f3e8f 100644 --- a/tests/ui/linking/linker-features-malformed.invalid_separator.stderr +++ b/tests/ui/linking/linker-features-malformed.invalid_separator.stderr @@ -1,2 +1,2 @@ -error: incorrect value `-lld@+lld` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected +error: incorrect value `-lld@+lld` for codegen option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected diff --git a/tests/ui/linking/linker-features-malformed.no_value.stderr b/tests/ui/linking/linker-features-malformed.no_value.stderr index e93a4e79bb1..e03d3b34bb1 100644 --- a/tests/ui/linking/linker-features-malformed.no_value.stderr +++ b/tests/ui/linking/linker-features-malformed.no_value.stderr @@ -1,2 +1,2 @@ -error: incorrect value `` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected +error: incorrect value `` for codegen option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected diff --git a/tests/ui/linking/linker-features-malformed.rs b/tests/ui/linking/linker-features-malformed.rs index 0bdcfa39920..627b8e68920 100644 --- a/tests/ui/linking/linker-features-malformed.rs +++ b/tests/ui/linking/linker-features-malformed.rs @@ -1,27 +1,27 @@ -//! Check that malformed `-Zlinker-features` flags are properly rejected. +//! Check that malformed `-Clinker-features` flags are properly rejected. //@ revisions: no_value -//@[no_value] compile-flags: -Zlinker-features= -//[no_value]~? ERROR incorrect value `` for unstable option `linker-features` +//@[no_value] compile-flags: -Clinker-features= +//[no_value]~? ERROR incorrect value `` for codegen option `linker-features` //@ revisions: invalid_modifier -//@[invalid_modifier] compile-flags: -Zlinker-features=*lld -//[invalid_modifier]~? ERROR incorrect value `*lld` for unstable option `linker-features` +//@[invalid_modifier] compile-flags: -Clinker-features=*lld +//[invalid_modifier]~? ERROR incorrect value `*lld` for codegen option `linker-features` //@ revisions: unknown_value -//@[unknown_value] compile-flags: -Zlinker-features=unknown -//[unknown_value]~? ERROR incorrect value `unknown` for unstable option `linker-features` +//@[unknown_value] compile-flags: -Clinker-features=unknown +//[unknown_value]~? ERROR incorrect value `unknown` for codegen option `linker-features` //@ revisions: unknown_modifier_value -//@[unknown_modifier_value] compile-flags: -Zlinker-features=-unknown -//[unknown_modifier_value]~? ERROR incorrect value `-unknown` for unstable option `linker-features` +//@[unknown_modifier_value] compile-flags: -Clinker-features=-unknown +//[unknown_modifier_value]~? ERROR incorrect value `-unknown` for codegen option `linker-features` //@ revisions: unknown_boolean -//@[unknown_boolean] compile-flags: -Zlinker-features=maybe -//[unknown_boolean]~? ERROR incorrect value `maybe` for unstable option `linker-features` +//@[unknown_boolean] compile-flags: -Clinker-features=maybe +//[unknown_boolean]~? ERROR incorrect value `maybe` for codegen option `linker-features` //@ revisions: invalid_separator -//@[invalid_separator] compile-flags: -Zlinker-features=-lld@+lld -//[invalid_separator]~? ERROR incorrect value `-lld@+lld` for unstable option `linker-features` +//@[invalid_separator] compile-flags: -Clinker-features=-lld@+lld +//[invalid_separator]~? ERROR incorrect value `-lld@+lld` for codegen option `linker-features` fn main() {} diff --git a/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr b/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr index 865738d0ccc..d82c2ea04b4 100644 --- a/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr +++ b/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr @@ -1,2 +1,2 @@ -error: incorrect value `maybe` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected +error: incorrect value `maybe` for codegen option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected diff --git a/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr b/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr index 03b9620ca26..59366e28e44 100644 --- a/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr +++ b/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr @@ -1,2 +1,2 @@ -error: incorrect value `-unknown` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected +error: incorrect value `-unknown` for codegen option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected diff --git a/tests/ui/linking/linker-features-malformed.unknown_value.stderr b/tests/ui/linking/linker-features-malformed.unknown_value.stderr index 566632a3df3..e8f6d5e637c 100644 --- a/tests/ui/linking/linker-features-malformed.unknown_value.stderr +++ b/tests/ui/linking/linker-features-malformed.unknown_value.stderr @@ -1,2 +1,2 @@ -error: incorrect value `unknown` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected +error: incorrect value `unknown` for codegen option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected diff --git a/tests/ui/linking/linker-features-unstable-cc.rs b/tests/ui/linking/linker-features-unstable-cc.rs new file mode 100644 index 00000000000..38103c81060 --- /dev/null +++ b/tests/ui/linking/linker-features-unstable-cc.rs @@ -0,0 +1,13 @@ +// Check that only `-C linker-features=-lld` is stable on x64 linux, and that other linker +// features require using `-Z unstable-options`. +// +// Note that, currently, only `lld` is parsed on the CLI, but that other linker features can exist +// internally (`cc`). +// +//@ compile-flags: --target=x86_64-unknown-linux-gnu -C linker-features=+cc --crate-type=rlib +//@ needs-llvm-components: x86 + +#![feature(no_core)] +#![no_core] + +//~? ERROR incorrect value `+cc` for codegen option `linker-features` diff --git a/tests/ui/linking/linker-features-unstable-cc.stderr b/tests/ui/linking/linker-features-unstable-cc.stderr new file mode 100644 index 00000000000..a69b4198160 --- /dev/null +++ b/tests/ui/linking/linker-features-unstable-cc.stderr @@ -0,0 +1,2 @@ +error: incorrect value `+cc` for codegen option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + -- cgit 1.4.1-3-g733a5 From 754d46e33a2a01fef9732b4bb7c2bb479f744195 Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Tue, 10 Jun 2025 20:15:36 +0200 Subject: Remove `extra_features` from `LlvmBitcodeLinker` It wasn't used anywhere. --- src/bootstrap/src/core/build_steps/compile.rs | 1 - src/bootstrap/src/core/build_steps/dist.rs | 3 +-- src/bootstrap/src/core/build_steps/tool.rs | 4 +--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 3e2bdc2d6b5..c84f451bbb2 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2061,7 +2061,6 @@ impl Step for Assemble { builder.ensure(crate::core::build_steps::tool::LlvmBitcodeLinker { compiler, target: target_compiler.host, - extra_features: vec![], }); let tool_exe = exe("llvm-bitcode-linker", target_compiler.host); builder.copy_link( diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 25b7e5a1b5d..f2aace76896 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2374,8 +2374,7 @@ impl Step for LlvmBitcodeLinker { builder.ensure(compile::Rustc::new(compiler, target)); - let llbc_linker = - builder.ensure(tool::LlvmBitcodeLinker { compiler, target, extra_features: vec![] }); + let llbc_linker = builder.ensure(tool::LlvmBitcodeLinker { compiler, target }); let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index b05b34b9b22..b906409aedd 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1016,7 +1016,6 @@ impl Step for RustAnalyzerProcMacroSrv { pub struct LlvmBitcodeLinker { pub compiler: Compiler, pub target: TargetSelection, - pub extra_features: Vec, } impl Step for LlvmBitcodeLinker { @@ -1033,7 +1032,6 @@ impl Step for LlvmBitcodeLinker { fn make_run(run: RunConfig<'_>) { run.builder.ensure(LlvmBitcodeLinker { compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), - extra_features: Vec::new(), target: run.target, }); } @@ -1050,7 +1048,7 @@ impl Step for LlvmBitcodeLinker { mode: Mode::ToolRustc, path: "src/tools/llvm-bitcode-linker", source_type: SourceType::InTree, - extra_features: self.extra_features, + extra_features: vec![], allow_features: "", cargo_args: Vec::new(), artifact_kind: ToolArtifactKind::Binary, -- cgit 1.4.1-3-g733a5 From c33f908f57cf1f3ea1a1261440062d2757909dc4 Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Wed, 11 Jun 2025 08:50:09 +0200 Subject: Remove sysroot copy from `LlvmBitcodeLinker` step That step should be responsible for building the tool, not performing side-effects. Also, only copy the tool to the `self-contained` directory, not to the `rustlib//bin` directory. --- src/bootstrap/src/core/build_steps/compile.rs | 9 ++++++++- src/bootstrap/src/core/build_steps/tool.rs | 21 ++------------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index c84f451bbb2..1d095cfc58e 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2062,10 +2062,17 @@ impl Step for Assemble { compiler, target: target_compiler.host, }); + + // Copy the llvm-bitcode-linker to the self-contained binary directory + let bindir_self_contained = builder + .sysroot(compiler) + .join(format!("lib/rustlib/{}/bin/self-contained", compiler.host)); let tool_exe = exe("llvm-bitcode-linker", target_compiler.host); + + t!(fs::create_dir_all(&bindir_self_contained)); builder.copy_link( &llvm_bitcode_linker.tool_path, - &libdir_bin.join(tool_exe), + &bindir_self_contained.join(tool_exe), FileType::Executable, ); } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index b906409aedd..8396099d380 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1041,7 +1041,7 @@ impl Step for LlvmBitcodeLinker { instrument(level = "debug", name = "LlvmBitcodeLinker::run", skip_all) )] fn run(self, builder: &Builder<'_>) -> ToolBuildResult { - let tool_result = builder.ensure(ToolBuild { + builder.ensure(ToolBuild { compiler: self.compiler, target: self.target, tool: "llvm-bitcode-linker", @@ -1052,24 +1052,7 @@ impl Step for LlvmBitcodeLinker { allow_features: "", cargo_args: Vec::new(), artifact_kind: ToolArtifactKind::Binary, - }); - - if tool_result.target_compiler.stage > 0 { - let bindir_self_contained = builder - .sysroot(tool_result.target_compiler) - .join(format!("lib/rustlib/{}/bin/self-contained", self.target.triple)); - t!(fs::create_dir_all(&bindir_self_contained)); - let bin_destination = bindir_self_contained - .join(exe("llvm-bitcode-linker", tool_result.target_compiler.host)); - builder.copy_link(&tool_result.tool_path, &bin_destination, FileType::Executable); - ToolBuildResult { - tool_path: bin_destination, - build_compiler: tool_result.build_compiler, - target_compiler: tool_result.target_compiler, - } - } else { - tool_result - } + }) } } -- cgit 1.4.1-3-g733a5 From fd3772200140b82324a7af8b4153c2a1a2ac1e89 Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Tue, 8 Jul 2025 11:47:57 +0200 Subject: Update llvm-bitcode-linker tests --- src/bootstrap/src/core/build_steps/compile.rs | 2 +- src/bootstrap/src/core/build_steps/dist.rs | 3 ++- src/bootstrap/src/core/build_steps/tool.rs | 12 +++++++++--- src/bootstrap/src/core/builder/tests.rs | 23 +++++++++++++++++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 1d095cfc58e..29300ff56f5 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2059,7 +2059,7 @@ impl Step for Assemble { trace!("llvm-bitcode-linker enabled, installing"); let llvm_bitcode_linker = builder.ensure(crate::core::build_steps::tool::LlvmBitcodeLinker { - compiler, + build_compiler: compiler, target: target_compiler.host, }); diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index f2aace76896..8b2d65ace50 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2374,7 +2374,8 @@ impl Step for LlvmBitcodeLinker { builder.ensure(compile::Rustc::new(compiler, target)); - let llbc_linker = builder.ensure(tool::LlvmBitcodeLinker { compiler, target }); + let llbc_linker = + builder.ensure(tool::LlvmBitcodeLinker { build_compiler: compiler, target }); let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 8396099d380..a7f6a2c7bae 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1014,7 +1014,7 @@ impl Step for RustAnalyzerProcMacroSrv { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct LlvmBitcodeLinker { - pub compiler: Compiler, + pub build_compiler: Compiler, pub target: TargetSelection, } @@ -1031,7 +1031,9 @@ impl Step for LlvmBitcodeLinker { fn make_run(run: RunConfig<'_>) { run.builder.ensure(LlvmBitcodeLinker { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), + build_compiler: run + .builder + .compiler(run.builder.top_stage, run.builder.config.host_target), target: run.target, }); } @@ -1042,7 +1044,7 @@ impl Step for LlvmBitcodeLinker { )] fn run(self, builder: &Builder<'_>) -> ToolBuildResult { builder.ensure(ToolBuild { - compiler: self.compiler, + compiler: self.build_compiler, target: self.target, tool: "llvm-bitcode-linker", mode: Mode::ToolRustc, @@ -1054,6 +1056,10 @@ impl Step for LlvmBitcodeLinker { artifact_kind: ToolArtifactKind::Binary, }) } + + fn metadata(&self) -> Option { + Some(StepMetadata::build("LlvmBitcodeLinker", self.target).built_by(self.build_compiler)) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1d5690a8197..131394f2a65 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -757,6 +757,27 @@ mod snapshot { "); } + #[test] + fn build_compiler_tools() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx + .config("build") + .stage(2) + .args(&["--set", "rust.llvm-bitcode-linker=true"]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> LlvmBitcodeLinker 2 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 2 -> LlvmBitcodeLinker 3 + [build] rustc 2 -> std 2 + [build] rustdoc 1 + " + ); + } + #[test] fn build_library_no_explicit_stage() { let ctx = TestCtx::new(); @@ -1040,6 +1061,7 @@ mod snapshot { [build] rustc 0 -> cargo-clippy 1 [build] rustc 0 -> miri 1 [build] rustc 0 -> cargo-miri 1 + [build] rustc 1 -> LlvmBitcodeLinker 2 "); } @@ -1230,6 +1252,7 @@ mod snapshot { [build] rustc 0 -> cargo-clippy 1 [build] rustc 0 -> miri 1 [build] rustc 0 -> cargo-miri 1 + [build] rustc 1 -> LlvmBitcodeLinker 2 "); } -- cgit 1.4.1-3-g733a5 From b14323aedcfef75bde97aff4f2562dcab8e9a3cd Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Tue, 8 Jul 2025 11:49:08 +0200 Subject: Also test `LldWrapper` and remove `llvm-config` override from tests --- src/bootstrap/src/core/build_steps/tool.rs | 7 +++++++ src/bootstrap/src/core/builder/tests.rs | 4 +++- src/bootstrap/src/utils/tests/mod.rs | 2 -- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index a7f6a2c7bae..5de1b472d79 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -910,6 +910,13 @@ impl Step for LldWrapper { tool_result } + + fn metadata(&self) -> Option { + Some( + StepMetadata::build("LldWrapper", self.target_compiler.host) + .built_by(self.build_compiler), + ) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 131394f2a65..b240d670b6b 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -764,13 +764,15 @@ mod snapshot { ctx .config("build") .stage(2) - .args(&["--set", "rust.llvm-bitcode-linker=true"]) + .args(&["--set", "rust.lld=true", "--set", "rust.llvm-bitcode-linker=true"]) .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 + [build] rustc 0 -> LldWrapper 1 [build] rustc 1 -> LlvmBitcodeLinker 2 [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 + [build] rustc 1 -> LldWrapper 2 [build] rustc 2 -> LlvmBitcodeLinker 3 [build] rustc 2 -> std 2 [build] rustdoc 1 diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 59c169b0f2b..5b568c1df5b 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -96,8 +96,6 @@ impl ConfigBuilder { // in-tree LLVM from sources. self.args.push("--set".to_string()); self.args.push("llvm.download-ci-llvm=false".to_string()); - self.args.push("--set".to_string()); - self.args.push(format!("target.'{}'.llvm-config=false", get_host_target())); // Do not mess with the local rustc checkout build directory self.args.push("--build-dir".to_string()); -- cgit 1.4.1-3-g733a5 From e32b1d712650bec425ea5b551473387cd9eea2ec Mon Sep 17 00:00:00 2001 From: Rémy Rakic Date: Tue, 8 Jul 2025 09:29:12 +0000 Subject: expand `-Clink-self-contained` tests - update existing tests for stabilization - ensure `-Clink-self-contained=-linker` is only stable on x64 linux - test invalid `-Clink-self-contained` components --- tests/ui/linking/link-self-contained-consistency.rs | 1 - .../linking/link-self-contained-linker-disallowed.rs | 18 ++++++++++++++++++ ...ontained-linker-disallowed.unstable_positive.stderr | 2 ++ ...d-linker-disallowed.unstable_target_negative.stderr | 2 ++ ...d-linker-disallowed.unstable_target_positive.stderr | 2 ++ .../linking/link-self-contained-unstable.crto.stderr | 2 ++ .../linking/link-self-contained-unstable.libc.stderr | 2 ++ .../linking/link-self-contained-unstable.mingw.stderr | 2 ++ tests/ui/linking/link-self-contained-unstable.rs | 13 +++++++++++++ .../link-self-contained-unstable.sanitizers.stderr | 2 ++ .../linking/link-self-contained-unstable.unwind.stderr | 2 ++ 11 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/ui/linking/link-self-contained-linker-disallowed.rs create mode 100644 tests/ui/linking/link-self-contained-linker-disallowed.unstable_positive.stderr create mode 100644 tests/ui/linking/link-self-contained-linker-disallowed.unstable_target_negative.stderr create mode 100644 tests/ui/linking/link-self-contained-linker-disallowed.unstable_target_positive.stderr create mode 100644 tests/ui/linking/link-self-contained-unstable.crto.stderr create mode 100644 tests/ui/linking/link-self-contained-unstable.libc.stderr create mode 100644 tests/ui/linking/link-self-contained-unstable.mingw.stderr create mode 100644 tests/ui/linking/link-self-contained-unstable.rs create mode 100644 tests/ui/linking/link-self-contained-unstable.sanitizers.stderr create mode 100644 tests/ui/linking/link-self-contained-unstable.unwind.stderr diff --git a/tests/ui/linking/link-self-contained-consistency.rs b/tests/ui/linking/link-self-contained-consistency.rs index 08227433891..e3944fc0360 100644 --- a/tests/ui/linking/link-self-contained-consistency.rs +++ b/tests/ui/linking/link-self-contained-consistency.rs @@ -1,7 +1,6 @@ // Checks that self-contained linking components cannot be both enabled and disabled at the same // time on the CLI. -//@ check-fail //@ revisions: one many //@ [one] compile-flags: -Clink-self-contained=-linker -Clink-self-contained=+linker -Zunstable-options //@ [many] compile-flags: -Clink-self-contained=+linker,+crto -Clink-self-contained=-linker,-crto -Zunstable-options diff --git a/tests/ui/linking/link-self-contained-linker-disallowed.rs b/tests/ui/linking/link-self-contained-linker-disallowed.rs new file mode 100644 index 00000000000..f076eb2017a --- /dev/null +++ b/tests/ui/linking/link-self-contained-linker-disallowed.rs @@ -0,0 +1,18 @@ +// Check that only `-C link-self-contained=-linker` is stable on x64 linux. Any other value or +// target, needs `-Z unstable-options`. + +// ignore-tidy-linelength + +//@ revisions: unstable_target_positive unstable_target_negative unstable_positive +//@ [unstable_target_negative] compile-flags: --target=x86_64-unknown-linux-musl -C link-self-contained=-linker --crate-type=rlib +//@ [unstable_target_negative] needs-llvm-components: x86 +//@ [unstable_target_positive] compile-flags: --target=x86_64-unknown-linux-musl -C link-self-contained=+linker --crate-type=rlib +//@ [unstable_target_positive] needs-llvm-components: x86 +//@ [unstable_positive] compile-flags: --target=x86_64-unknown-linux-gnu -C link-self-contained=+linker --crate-type=rlib +//@ [unstable_positive] needs-llvm-components: x86 + +#![feature(no_core)] +#![no_core] + +//[unstable_target_negative]~? ERROR `-C link-self-contained=-linker` is unstable on the `x86_64-unknown-linux-musl` target +//[unstable_target_positive,unstable_positive]~? ERROR only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` are stable diff --git a/tests/ui/linking/link-self-contained-linker-disallowed.unstable_positive.stderr b/tests/ui/linking/link-self-contained-linker-disallowed.unstable_positive.stderr new file mode 100644 index 00000000000..4eb0ff04b65 --- /dev/null +++ b/tests/ui/linking/link-self-contained-linker-disallowed.unstable_positive.stderr @@ -0,0 +1,2 @@ +error: only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` are stable, the `-Z unstable-options` flag must also be passed to use the unstable values + diff --git a/tests/ui/linking/link-self-contained-linker-disallowed.unstable_target_negative.stderr b/tests/ui/linking/link-self-contained-linker-disallowed.unstable_target_negative.stderr new file mode 100644 index 00000000000..8bf71941c44 --- /dev/null +++ b/tests/ui/linking/link-self-contained-linker-disallowed.unstable_target_negative.stderr @@ -0,0 +1,2 @@ +error: `-C link-self-contained=-linker` is unstable on the `x86_64-unknown-linux-musl` target. The `-Z unstable-options` flag must also be passed to use it on this target + diff --git a/tests/ui/linking/link-self-contained-linker-disallowed.unstable_target_positive.stderr b/tests/ui/linking/link-self-contained-linker-disallowed.unstable_target_positive.stderr new file mode 100644 index 00000000000..4eb0ff04b65 --- /dev/null +++ b/tests/ui/linking/link-self-contained-linker-disallowed.unstable_target_positive.stderr @@ -0,0 +1,2 @@ +error: only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` are stable, the `-Z unstable-options` flag must also be passed to use the unstable values + diff --git a/tests/ui/linking/link-self-contained-unstable.crto.stderr b/tests/ui/linking/link-self-contained-unstable.crto.stderr new file mode 100644 index 00000000000..4eb0ff04b65 --- /dev/null +++ b/tests/ui/linking/link-self-contained-unstable.crto.stderr @@ -0,0 +1,2 @@ +error: only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` are stable, the `-Z unstable-options` flag must also be passed to use the unstable values + diff --git a/tests/ui/linking/link-self-contained-unstable.libc.stderr b/tests/ui/linking/link-self-contained-unstable.libc.stderr new file mode 100644 index 00000000000..4eb0ff04b65 --- /dev/null +++ b/tests/ui/linking/link-self-contained-unstable.libc.stderr @@ -0,0 +1,2 @@ +error: only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` are stable, the `-Z unstable-options` flag must also be passed to use the unstable values + diff --git a/tests/ui/linking/link-self-contained-unstable.mingw.stderr b/tests/ui/linking/link-self-contained-unstable.mingw.stderr new file mode 100644 index 00000000000..4eb0ff04b65 --- /dev/null +++ b/tests/ui/linking/link-self-contained-unstable.mingw.stderr @@ -0,0 +1,2 @@ +error: only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` are stable, the `-Z unstable-options` flag must also be passed to use the unstable values + diff --git a/tests/ui/linking/link-self-contained-unstable.rs b/tests/ui/linking/link-self-contained-unstable.rs new file mode 100644 index 00000000000..10c895909d5 --- /dev/null +++ b/tests/ui/linking/link-self-contained-unstable.rs @@ -0,0 +1,13 @@ +// Checks that values for `-Clink-self-contained` other than the blanket enable/disable and +// `-linker` require `-Zunstable-options`. + +//@ revisions: crto libc unwind sanitizers mingw +//@ [crto] compile-flags: -Clink-self-contained=+crto +//@ [libc] compile-flags: -Clink-self-contained=-libc +//@ [unwind] compile-flags: -Clink-self-contained=+unwind +//@ [sanitizers] compile-flags: -Clink-self-contained=-sanitizers +//@ [mingw] compile-flags: -Clink-self-contained=+mingw + +fn main() {} + +//~? ERROR only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` are stable diff --git a/tests/ui/linking/link-self-contained-unstable.sanitizers.stderr b/tests/ui/linking/link-self-contained-unstable.sanitizers.stderr new file mode 100644 index 00000000000..4eb0ff04b65 --- /dev/null +++ b/tests/ui/linking/link-self-contained-unstable.sanitizers.stderr @@ -0,0 +1,2 @@ +error: only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` are stable, the `-Z unstable-options` flag must also be passed to use the unstable values + diff --git a/tests/ui/linking/link-self-contained-unstable.unwind.stderr b/tests/ui/linking/link-self-contained-unstable.unwind.stderr new file mode 100644 index 00000000000..4eb0ff04b65 --- /dev/null +++ b/tests/ui/linking/link-self-contained-unstable.unwind.stderr @@ -0,0 +1,2 @@ +error: only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` are stable, the `-Z unstable-options` flag must also be passed to use the unstable values + -- cgit 1.4.1-3-g733a5 From 889638a8b3591d71bdec38efbac27469f5019b49 Mon Sep 17 00:00:00 2001 From: Rémy Rakic Date: Tue, 8 Jul 2025 09:19:58 +0000 Subject: document new stable flags, with x64 linux implementation notes --- src/doc/rustc/src/codegen-options/index.md | 59 ++++++++++++++++++++-- .../src/compiler-flags/codegen-options.md | 8 +-- .../src/compiler-flags/linker-features.md | 35 ------------- 3 files changed, 59 insertions(+), 43 deletions(-) delete mode 100644 src/doc/unstable-book/src/compiler-flags/linker-features.md diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index bb109adf76f..07eafdf4c4c 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -235,15 +235,33 @@ coverage measurement. Its use is not recommended. ## link-self-contained -On `windows-gnu`, `linux-musl`, and `wasi` targets, this flag controls whether the -linker will use libraries and objects shipped with Rust instead of those in the system. -It takes one of the following values: +This flag controls whether the linker will use libraries and objects shipped with Rust instead of +those in the system. It also controls which binary is used for the linker itself. This allows +overriding cases when detection fails or the user wants to use shipped libraries. + +You can enable or disable the usage of any self-contained components using one of the following values: * no value: rustc will use heuristic to disable self-contained mode if system has necessary tools. * `y`, `yes`, `on`, `true`: use only libraries/objects shipped with Rust. * `n`, `no`, `off` or `false`: rely on the user or the linker to provide non-Rust libraries/objects. -This allows overriding cases when detection fails or user wants to use shipped libraries. +It is also possible to enable or disable specific self-contained components in a more granular way. +You can pass a comma-separated list of self-contained components, individually enabled +(`+component`) or disabled (`-component`). + +Currently, only the `linker` granular option is stabilized, and only on the `x86_64-unknown-linux-gnu` target: +- `linker`: toggle the usage of self-contained linker binaries (linker, dlltool, and their necessary libraries) + +Note that only the `-linker` opt-out is stable on the `x86_64-unknown-linux-gnu` target: `+linker` is +already the default on this target. + +#### Implementation notes + +On the `x86_64-unknown-linux-gnu` target, when using the default linker flavor (using `cc` as the +linker driver) and linker features (to try using `lld`), `rustc` will try to use the self-contained +linker by passing a `-B /path/to/sysroot/` link argument to the driver to find `rust-lld` in the +sysroot. For backwards-compatibility, and to limit name and `PATH` collisions, this is done using a +shim executable (the `lld-wrapper` tool) that forwards execution to the `rust-lld` executable itself. ## linker @@ -256,6 +274,39 @@ Note that on Unix-like targets (for example, `*-unknown-linux-gnu` or `*-unknown the C compiler (for example `cc` or `clang`) is used as the "linker" here, serving as a linker driver. It will invoke the actual linker with all the necessary flags to be able to link against the system libraries like libc. +## linker-features + +The `-Clinker-features` flag allows enabling or disabling specific features used during linking. + +These feature flags are a flexible extension mechanism that is complementary to linker flavors, +designed to avoid the combinatorial explosion of having to create a new set of flavors for each +linker feature we'd want to use. + +The flag accepts a comma-separated list of features, individually enabled (`+feature`) or disabled +(`-feature`). + +Currently only one is stable, and only on the `x86_64-unknown-linux-gnu` target: +- `lld`: to toggle trying to use the lld linker, either the system-installed binary, or the self-contained + `rust-lld` linker (via the [`-Clink-self-contained=+linker`](#link-self-contained) flag). + +For example, use: +- `-Clinker-features=+lld` to opt into using the `lld` linker, when possible (see the Implementation notes below) +- `-Clinker-features=-lld` to opt out instead, for targets where it is configured as the default linker + +Note that only the `-lld` opt-out is stable on the `x86_64-unknown-linux-gnu` target: `+lld` is +already the default on this target. + +#### Implementation notes + +On the `x86_64-unknown-linux-gnu` target, when using the default linker flavor (using `cc` as the +linker driver), `rustc` will try to use lld by passing a `-fuse-ld=lld` link argument to the driver. +`rustc` will also try to detect if that _causes_ an error during linking (for example, if GCC is too +old to understand the flag, and returns an error) and will then retry linking without this argument, +as a fallback. + +If the user _also_ passes a `-Clink-arg=-fuse-ld=$value`, both will be given to the linker +driver but the user's will be passed last, and would generally have priority over `rustc`'s. + ## linker-flavor This flag controls the linker flavor used by `rustc`. If a linker is given with diff --git a/src/doc/unstable-book/src/compiler-flags/codegen-options.md b/src/doc/unstable-book/src/compiler-flags/codegen-options.md index cc51554706d..f927e5c439c 100644 --- a/src/doc/unstable-book/src/compiler-flags/codegen-options.md +++ b/src/doc/unstable-book/src/compiler-flags/codegen-options.md @@ -51,10 +51,10 @@ instead of those in the system. The stable boolean values for this flag are coar - `mingw`: other MinGW libs and Windows import libs Out of the above self-contained linking components, `linker` is the only one currently implemented -(beyond parsing the CLI options). +(beyond parsing the CLI options) and stabilized. It refers to the LLD linker, built from the same LLVM revision used by rustc (named `rust-lld` to avoid naming conflicts), that is distributed via `rustup` with the compiler (and is used by default -for the wasm targets). One can also opt-in to use it by combining this flag with an appropriate -linker flavor: for example, `-Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker` will use the -toolchain's `rust-lld` as the linker. +for the wasm targets). One can also opt into using it by combining this flag with the appropriate +linker feature: for example, `-Clinker-features=+lld -Clink-self-contained=+linker` will use the +toolchain's `rust-lld` as the linker instead of the system's lld with `-Clinker-features=+lld` only. diff --git a/src/doc/unstable-book/src/compiler-flags/linker-features.md b/src/doc/unstable-book/src/compiler-flags/linker-features.md deleted file mode 100644 index 643fcf7c6d7..00000000000 --- a/src/doc/unstable-book/src/compiler-flags/linker-features.md +++ /dev/null @@ -1,35 +0,0 @@ -# `linker-features` - --------------------- - -The `-Zlinker-features` compiler flag allows enabling or disabling specific features used during -linking, and is intended to be stabilized under the codegen options as `-Clinker-features`. - -These feature flags are a flexible extension mechanism that is complementary to linker flavors, -designed to avoid the combinatorial explosion of having to create a new set of flavors for each -linker feature we'd want to use. - -For example, this design allows: -- default feature sets for principal flavors, or for specific targets. -- flavor-specific features: for example, clang offers automatic cross-linking with `--target`, which - gcc-style compilers don't support. The *flavor* is still a C/C++ compiler, and we don't want to - multiply the number of flavors for this use-case. Instead, we can have a single `+target` feature. -- umbrella features: for example, if clang accumulates more features in the future than just the - `+target` above. That could be modeled as `+clang`. -- niche features for resolving specific issues: for example, on Apple targets the linker flag - implementing the `as-needed` native link modifier (#99424) is only possible on sufficiently recent - linker versions. -- still allows for discovery and automation, for example via feature detection. This can be useful - in exotic environments/build systems. - -The flag accepts a comma-separated list of features, individually enabled (`+features`) or disabled -(`-features`), though currently only one is exposed on the CLI: -- `lld`: to toggle using the lld linker, either the system-installed binary, or the self-contained - `rust-lld` linker. - -As described above, this list is intended to grow in the future. - -One of the most common uses of this flag will be to toggle self-contained linking with `rust-lld` on -and off: `-Clinker-features=+lld -Clink-self-contained=+linker` will use the toolchain's `rust-lld` -as the linker. Inversely, `-Clinker-features=-lld` would opt out of that, if the current target had -self-contained linking enabled by default. -- cgit 1.4.1-3-g733a5 From 5059315e28fde405edffd7ad9eed3853cc412fad Mon Sep 17 00:00:00 2001 From: Rémy Rakic Date: Fri, 6 Jun 2025 12:17:55 +0000 Subject: remove `-znostart-stop-gc` workaround Now that `#[used(linker)]` is the default on ELF, we don't need to use the `-znostart-stop-gc` link-arg workaround to match bfd's behavior when using lld. --- compiler/rustc_codegen_ssa/src/back/link.rs | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 34328687e3f..b46773396fc 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3327,35 +3327,6 @@ fn add_lld_args( // this, `wasm-component-ld`, which is overridden if this option is passed. if !sess.target.is_like_wasm { cmd.cc_arg("-fuse-ld=lld"); - - // On ELF platforms like at least x64 linux, GNU ld and LLD have opposite defaults on some - // section garbage-collection features. For example, the somewhat popular `linkme` crate and - // its dependents rely in practice on this difference: when using lld, they need `-z - // nostart-stop-gc` to prevent encapsulation symbols and sections from being - // garbage-collected. - // - // More information about all this can be found in: - // - https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order - // - https://lld.llvm.org/ELF/start-stop-gc - // - // So when using lld, we restore, for now, the traditional behavior to help migration, but - // will remove it in the future. - // Since this only disables an optimization, it shouldn't create issues, but is in theory - // slightly suboptimal. However, it: - // - doesn't have any visible impact on our benchmarks - // - reduces the need to disable lld for the crates that depend on this - // - // Note that lld can detect some cases where this difference is relied on, and emits a - // dedicated error to add this link arg. We could make use of this error to emit an FCW. As - // of writing this, we don't do it, because lld is already enabled by default on nightly - // without this mitigation: no working project would see the FCW, so we do this to help - // stabilization. - // - // FIXME: emit an FCW if linking fails due its absence, and then remove this link-arg in the - // future. - if sess.target.llvm_target == "x86_64-unknown-linux-gnu" { - cmd.link_arg("-znostart-stop-gc"); - } } if !flavor.is_gnu() { -- cgit 1.4.1-3-g733a5 From 3ba8e330f9960a52b2c8ca10c8cba425514919f9 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 8 Jul 2025 07:34:51 -0400 Subject: Rewrite for clarity move common code to a helper function Co-Authored-By: Kobzol --- src/bootstrap/configure.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 94e02f942dc..86208b94261 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -744,22 +744,24 @@ def write_uncommented(target, f): A block is a sequence of non-empty lines separated by empty lines. """ block = [] - is_comment = True + + def flush(last): + # If the block is entiry made of comments, ignore it + entire_block_comments = all(ln.startswith("#") or ln == "" for ln in block) + if not entire_block_comments and len(block) > 0: + for line in block: + f.write(line + "\n") + # Required to output a newline before the start of a new section + if last: + f.write("\n") + block.clear() for line in target: block.append(line) if len(line) == 0: - if not is_comment: - for ln in block: - f.write(ln + "\n") - block = [] - is_comment = True - continue - is_comment = is_comment and line.startswith("#") - # Write the last accumulated block - if len(block) > 0 and not is_comment: - for ln in block: - f.write(ln + "\n") + flush(last=False) + + flush(last=True) return f -- cgit 1.4.1-3-g733a5 From 961bac0b197bf5ded0da8faf1395c96598923b20 Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Tue, 8 Jul 2025 15:06:27 +0200 Subject: Add cross-compilation tool test --- src/bootstrap/src/core/builder/tests.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index b240d670b6b..bbcb58fca14 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -780,6 +780,35 @@ mod snapshot { ); } + #[test] + fn build_compiler_tools_cross() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx + .config("build") + .stage(2) + .args(&["--set", "rust.lld=true", "--set", "rust.llvm-bitcode-linker=true"]) + .hosts(&[TEST_TRIPLE_1]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 0 -> LldWrapper 1 + [build] rustc 1 -> LlvmBitcodeLinker 2 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 1 -> LldWrapper 2 + [build] rustc 2 -> LlvmBitcodeLinker 3 + [build] rustc 1 -> std 1 + [build] rustc 2 -> std 2 + [build] llvm + [build] rustc 1 -> rustc 2 + [build] rustc 1 -> LldWrapper 2 + [build] rustc 2 -> LlvmBitcodeLinker 3 + [build] rustdoc 1 + " + ); + } + #[test] fn build_library_no_explicit_stage() { let ctx = TestCtx::new(); -- cgit 1.4.1-3-g733a5 From c4bf37d35824cf22dade0c1c4595186a29a8714d Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 2 Jul 2025 18:10:57 +0200 Subject: Always inline InterpCx::layout_of after perf regression --- compiler/rustc_const_eval/src/interpret/eval_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 068d6369f87..41fc8d47cd3 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -96,7 +96,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// This inherent method takes priority over the trait method with the same name in LayoutOf, /// and allows wrapping the actual [LayoutOf::layout_of] with a tracing span. /// See [LayoutOf::layout_of] for the original documentation. - #[inline] + #[inline(always)] pub fn layout_of( &self, ty: Ty<'tcx>, -- cgit 1.4.1-3-g733a5 From 07143afee43a4b516ca446e5e49570411043dfdb Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 5 Jul 2025 11:13:04 +0200 Subject: Replace TRACING_ENABLED with enter_trace_span() Hopefully this will make tracing calls be optimized out properly when tracing is disabled --- compiler/rustc_const_eval/src/interpret/machine.rs | 20 +++++++++++-------- compiler/rustc_const_eval/src/interpret/mod.rs | 1 + compiler/rustc_const_eval/src/interpret/util.rs | 23 +++++++++++----------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 844c19fea2d..33afadd94f5 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -18,8 +18,8 @@ use rustc_target::callconv::FnAbi; use super::{ AllocBytes, AllocId, AllocKind, AllocRange, Allocation, CTFE_ALLOC_SALT, ConstAllocation, - CtfeProvenance, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, MemoryKind, - Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, interp_ok, throw_unsup, + CtfeProvenance, EnteredTraceSpan, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, + MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, interp_ok, throw_unsup, }; /// Data returned by [`Machine::after_stack_pop`], and consumed by @@ -147,12 +147,6 @@ pub trait Machine<'tcx>: Sized { /// already been checked before. const ALL_CONSTS_ARE_PRECHECKED: bool = true; - /// Determines whether rustc_const_eval functions that make use of the [Machine] should make - /// tracing calls (to the `tracing` library). By default this is `false`, meaning the tracing - /// calls will supposedly be optimized out. This flag is set to `true` inside Miri, to allow - /// tracing the interpretation steps, among other things. - const TRACING_ENABLED: bool = false; - /// Whether memory accesses should be alignment-checked. fn enforce_alignment(ecx: &InterpCx<'tcx, Self>) -> bool; @@ -634,6 +628,16 @@ pub trait Machine<'tcx>: Sized { /// Compute the value passed to the constructors of the `AllocBytes` type for /// abstract machine allocations. fn get_default_alloc_params(&self) -> ::AllocParams; + + /// Allows enabling/disabling tracing calls from within `rustc_const_eval` at compile time, by + /// delegating the entering of [tracing::Span]s to implementors of the [Machine] trait. The + /// default implementation corresponds to tracing being disabled, meaning the tracing calls will + /// supposedly be optimized out completely. To enable tracing, override this trait method and + /// return `span.entered()`. Also see [crate::enter_trace_span]. + #[must_use] + fn enter_trace_span(_span: tracing::Span) -> impl EnteredTraceSpan { + () + } } /// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 8303f891f98..2fc372dd019 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -37,6 +37,7 @@ pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; use self::place::{MemPlace, Place}; pub use self::projection::{OffsetMode, Projectable}; pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation, StackPopInfo}; +pub use self::util::EnteredTraceSpan; pub(crate) use self::util::create_static_alloc; pub use self::validity::{CtfeValidationMode, RangeSet, RefTracking}; pub use self::visitor::ValueVisitor; diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 99add01f95c..eeab32fd79c 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -46,21 +46,20 @@ pub(crate) fn create_static_alloc<'tcx>( interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout)) } -/// This struct is needed to enforce `#[must_use]` on [tracing::span::EnteredSpan] -/// while wrapping them in an `Option`. -#[must_use] -pub enum MaybeEnteredSpan { - Some(tracing::span::EnteredSpan), - None, -} +/// A marker trait returned by [crate::interpret::Machine::enter_trace_span], identifying either a +/// real [tracing::span::EnteredSpan] in case tracing is enabled, or the dummy type `()` when +/// tracing is disabled. +pub trait EnteredTraceSpan {} +impl EnteredTraceSpan for () {} +impl EnteredTraceSpan for tracing::span::EnteredSpan {} +/// Shortand for calling [crate::interpret::Machine::enter_trace_span] on a [tracing::info_span]. +/// This is supposed to be compiled out when [crate::interpret::Machine::enter_trace_span] has the +/// default implementation (i.e. when it does not actually enter the span but instead returns `()`). +/// Note: the result of this macro **must be used** because the span is exited when it's dropped. #[macro_export] macro_rules! enter_trace_span { ($machine:ident, $($tt:tt)*) => { - if $machine::TRACING_ENABLED { - $crate::interpret::util::MaybeEnteredSpan::Some(tracing::info_span!($($tt)*).entered()) - } else { - $crate::interpret::util::MaybeEnteredSpan::None - } + $machine::enter_trace_span(tracing::info_span!($($tt)*)) } } -- cgit 1.4.1-3-g733a5 From 3cacaa7d0ebd8994f521fbcfc5277f8725f9348c Mon Sep 17 00:00:00 2001 From: Stypox Date: Sun, 6 Jul 2025 08:53:50 +0200 Subject: Add inline(always) to Machine::enter_trace_span --- compiler/rustc_const_eval/src/interpret/machine.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 33afadd94f5..64aa971d14e 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -635,6 +635,7 @@ pub trait Machine<'tcx>: Sized { /// supposedly be optimized out completely. To enable tracing, override this trait method and /// return `span.entered()`. Also see [crate::enter_trace_span]. #[must_use] + #[inline(always)] fn enter_trace_span(_span: tracing::Span) -> impl EnteredTraceSpan { () } -- cgit 1.4.1-3-g733a5 From e8c8330ba1a0fb7dcf8e74a8758569ab71261ef7 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sun, 6 Jul 2025 08:58:19 +0200 Subject: Make enter_trace_span take a closure for better optimization --- compiler/rustc_const_eval/src/interpret/machine.rs | 2 +- compiler/rustc_const_eval/src/interpret/util.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 64aa971d14e..d150ed69250 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -636,7 +636,7 @@ pub trait Machine<'tcx>: Sized { /// return `span.entered()`. Also see [crate::enter_trace_span]. #[must_use] #[inline(always)] - fn enter_trace_span(_span: tracing::Span) -> impl EnteredTraceSpan { + fn enter_trace_span(_span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan { () } } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index eeab32fd79c..72650d545c3 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -60,6 +60,6 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan {} #[macro_export] macro_rules! enter_trace_span { ($machine:ident, $($tt:tt)*) => { - $machine::enter_trace_span(tracing::info_span!($($tt)*)) + $machine::enter_trace_span(|| tracing::info_span!($($tt)*)) } } -- cgit 1.4.1-3-g733a5 From e5f7d4d783c1567ddc16e02091bae8bacfca1418 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 8 Jul 2025 15:37:01 +0200 Subject: Implement enter_trace_span() in MiriMachine --- src/tools/miri/src/machine.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 693b8916d89..35399dbf4cb 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1014,8 +1014,6 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { const PANIC_ON_ALLOC_FAIL: bool = false; - const TRACING_ENABLED: bool = cfg!(feature = "tracing"); - #[inline(always)] fn enforce_alignment(ecx: &MiriInterpCx<'tcx>) -> bool { ecx.machine.check_alignment != AlignmentCheck::None @@ -1827,6 +1825,16 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { #[cfg(not(target_os = "linux"))] MiriAllocParams::Global } + + fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan { + #[cfg(feature = "tracing")] + { span().entered() } + #[cfg(not(feature = "tracing"))] + { + let _ = span; // so we avoid the "unused variable" warning + () + } + } } /// Trait for callbacks handling asynchronous machine operations. -- cgit 1.4.1-3-g733a5 From 8d2d4eb89af09104bd1306073320d3e67ead5fe6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 6 Jul 2025 20:02:45 +0000 Subject: Instantiate auto trait before computing higher-ranked constituent types --- .../src/traits/select/confirmation.rs | 1 + .../rustc_trait_selection/src/traits/select/mod.rs | 38 +++++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 545531f927a..b5408ef3c51 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -403,6 +403,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); + let self_ty = self.infcx.enter_forall_and_leak_universe(self_ty); let types = self.constituent_types_for_ty(self_ty)?; let types = self.infcx.enter_forall_and_leak_universe(types); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c9930c69b32..308a0307afd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2330,9 +2330,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] fn constituent_types_for_ty( &self, - t: ty::Binder<'tcx, Ty<'tcx>>, + t: Ty<'tcx>, ) -> Result>>, SelectionError<'tcx>> { - Ok(match *t.skip_binder().kind() { + Ok(match *t.kind() { ty::Uint(_) | ty::Int(_) | ty::Bool @@ -2349,8 +2349,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // `assemble_candidates_from_auto_impls`. ty::Foreign(..) => ty::Binder::dummy(Vec::new()), - // FIXME(unsafe_binders): Squash the double binder for now, I guess. - ty::UnsafeBinder(_) => return Err(SelectionError::Unimplemented), + ty::UnsafeBinder(ty) => ty.map_bound(|ty| vec![ty]), // Treat this like `struct str([u8]);` ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]), @@ -2364,40 +2363,47 @@ impl<'tcx> SelectionContext<'_, 'tcx> { bug!("asked to assemble constituent types of unexpected type: {:?}", t); } - ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => t.rebind(vec![element_ty]), + ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => { + ty::Binder::dummy(vec![element_ty]) + } - ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => t.rebind(vec![ty]), + ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => ty::Binder::dummy(vec![ty]), ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - t.rebind(tys.iter().collect()) + ty::Binder::dummy(tys.iter().collect()) } ty::Closure(_, args) => { let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); - t.rebind(vec![ty]) + ty::Binder::dummy(vec![ty]) } ty::CoroutineClosure(_, args) => { let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); - t.rebind(vec![ty]) + ty::Binder::dummy(vec![ty]) } ty::Coroutine(_, args) => { let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); let witness = args.as_coroutine().witness(); - t.rebind([ty].into_iter().chain(iter::once(witness)).collect()) + ty::Binder::dummy([ty].into_iter().chain(iter::once(witness)).collect()) } - ty::CoroutineWitness(def_id, args) => { - rebind_coroutine_witness_types(self.infcx.tcx, def_id, args, t.bound_vars()) - } + ty::CoroutineWitness(def_id, args) => self + .infcx + .tcx + .coroutine_hidden_types(def_id) + .instantiate(self.infcx.tcx, args) + .map_bound(|witness| witness.types.to_vec()), // For `PhantomData`, we pass `T`. - ty::Adt(def, args) if def.is_phantom_data() => t.rebind(args.types().collect()), + ty::Adt(def, args) if def.is_phantom_data() => { + ty::Binder::dummy(args.types().collect()) + } ty::Adt(def, args) => { - t.rebind(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect()) + ty::Binder::dummy(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect()) } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { @@ -2408,7 +2414,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // which enforces a DAG between the functions requiring // the auto trait bounds in question. match self.tcx().type_of_opaque(def_id) { - Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]), + Ok(ty) => ty::Binder::dummy(vec![ty.instantiate(self.tcx(), args)]), Err(_) => { return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id)); } -- cgit 1.4.1-3-g733a5 From bbb409cc68e2588b72d95f77f2b551f5455a201d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 7 Jul 2025 22:21:08 +0000 Subject: Instantiate binder for Copy/Clone/Sized eagerly --- .../src/traits/select/candidate_assembly.rs | 180 ++++++++++++++--- .../src/traits/select/confirmation.rs | 26 ++- .../rustc_trait_selection/src/traits/select/mod.rs | 212 ++++++--------------- 3 files changed, 222 insertions(+), 196 deletions(-) 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 9af85f5e3f0..cc188a280aa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -19,7 +19,7 @@ use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; -use super::{BuiltinImplConditions, SelectionCandidateSet, SelectionContext, TraitObligationStack}; +use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; use crate::traits::util; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { @@ -75,8 +75,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidates_from_impls(obligation, &mut candidates); // For other types, we'll use the builtin rules. - let copy_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); + self.assemble_builtin_copy_clone_candidate( + obligation.predicate.self_ty().skip_binder(), + &mut candidates, + ); } Some(LangItem::DiscriminantKind) => { // `DiscriminantKind` is automatically implemented for every type. @@ -88,14 +90,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } Some(LangItem::Sized) => { self.assemble_builtin_sized_candidate( - obligation, + obligation.predicate.self_ty().skip_binder(), &mut candidates, SizedTraitKind::Sized, ); } Some(LangItem::MetaSized) => { self.assemble_builtin_sized_candidate( - obligation, + obligation.predicate.self_ty().skip_binder(), &mut candidates, SizedTraitKind::MetaSized, ); @@ -357,14 +359,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - let self_ty = obligation.self_ty().skip_binder(); - // gen constructs get lowered to a special kind of coroutine that - // should directly `impl FusedIterator`. - if let ty::Coroutine(did, ..) = self_ty.kind() - && self.tcx().coroutine_is_gen(*did) - { - debug!(?self_ty, ?obligation, "assemble_fused_iterator_candidates",); - + if self.coroutine_is_gen(obligation.self_ty().skip_binder()) { candidates.vec.push(BuiltinCandidate); } } @@ -1113,41 +1108,164 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - /// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself. + /// Assembles `Copy` and `Clone` candidates for built-in types with no libcore-defined + /// `Copy` or `Clone` impls. #[instrument(level = "debug", skip(self, candidates))] - fn assemble_builtin_sized_candidate( + fn assemble_builtin_copy_clone_candidate( &mut self, - obligation: &PolyTraitObligation<'tcx>, + self_ty: Ty<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - sizedness: SizedTraitKind, ) { - match self.sizedness_conditions(obligation, sizedness) { - BuiltinImplConditions::Where(_nested) => { - candidates.vec.push(SizedCandidate); + match *self_ty.kind() { + // These impls are built-in because we cannot express sufficiently + // generic impls in libcore. + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Tuple(..) + | ty::CoroutineWitness(..) + | ty::Pat(..) => { + candidates.vec.push(BuiltinCandidate); + } + + // Implementations provided in libcore. + ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) + | ty::Array(..) => {} + + // FIXME(unsafe_binder): Should we conditionally + // (i.e. universally) implement copy/clone? + ty::UnsafeBinder(_) => {} + + // Not `Sized`, which is a supertrait of `Copy`/`Clone`. + ty::Dynamic(..) | ty::Str | ty::Slice(..) | ty::Foreign(..) => {} + + // Not `Copy` or `Clone` by design. + ty::Ref(_, _, hir::Mutability::Mut) => {} + + ty::Coroutine(coroutine_def_id, args) => { + match self.tcx().coroutine_movability(coroutine_def_id) { + hir::Movability::Static => {} + hir::Movability::Movable => { + if self.tcx().features().coroutine_clone() { + let resolved_upvars = + self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); + let resolved_witness = + self.infcx.shallow_resolve(args.as_coroutine().witness()); + if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() { + // Not yet resolved. + candidates.ambiguous = true; + } else { + candidates.vec.push(BuiltinCandidate); + } + } + } + } } - BuiltinImplConditions::None => {} - BuiltinImplConditions::Ambiguous => { + + ty::Closure(_, args) => { + let resolved_upvars = + self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); + if resolved_upvars.is_ty_var() { + // Not yet resolved. + candidates.ambiguous = true; + } else { + candidates.vec.push(BuiltinCandidate); + } + } + + ty::CoroutineClosure(_, args) => { + let resolved_upvars = + self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); + if resolved_upvars.is_ty_var() { + // Not yet resolved. + candidates.ambiguous = true; + } else { + candidates.vec.push(BuiltinCandidate); + } + } + + // Fallback to whatever user-defined impls or param-env clauses exist in this case. + ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {} + + ty::Infer(ty::TyVar(_)) => { candidates.ambiguous = true; } + + // Only appears when assembling higher-ranked `for T: Clone`. + ty::Bound(..) => {} + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } } } - /// Assembles the trait which are built-in to the language itself: - /// e.g. `Copy` and `Clone`. + /// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself. #[instrument(level = "debug", skip(self, candidates))] - fn assemble_builtin_bound_candidates( + fn assemble_builtin_sized_candidate( &mut self, - conditions: BuiltinImplConditions<'tcx>, + self_ty: Ty<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, + sizedness: SizedTraitKind, ) { - match conditions { - BuiltinImplConditions::Where(_) => { - candidates.vec.push(BuiltinCandidate); + match *self_ty.kind() { + // Always sized. + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Never + | ty::Error(_) => { + candidates.vec.push(SizedCandidate); + } + + // Conditionally `Sized`. + ty::Tuple(..) | ty::Pat(..) | ty::Adt(..) | ty::UnsafeBinder(_) => { + candidates.vec.push(SizedCandidate); } - BuiltinImplConditions::None => {} - BuiltinImplConditions::Ambiguous => { + + // `MetaSized` but not `Sized`. + ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { + SizedTraitKind::Sized => {} + SizedTraitKind::MetaSized => { + candidates.vec.push(SizedCandidate); + } + }, + + // Not `MetaSized` or `Sized`. + ty::Foreign(..) => {} + + ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => {} + + ty::Infer(ty::TyVar(_)) => { candidates.ambiguous = true; } + + // Only appears when assembling higher-ranked `for T: Sized`. + ty::Bound(..) => {} + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index b5408ef3c51..ee8cef20279 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -21,7 +21,7 @@ use thin_vec::thin_vec; use tracing::{debug, instrument}; use super::SelectionCandidate::{self, *}; -use super::{BuiltinImplConditions, PredicateObligations, SelectionContext}; +use super::{PredicateObligations, SelectionContext}; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::{ @@ -257,16 +257,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligation, "confirm_builtin_candidate"); let tcx = self.tcx(); let trait_def = obligation.predicate.def_id(); - let conditions = match tcx.as_lang_item(trait_def) { - Some(LangItem::Sized) => self.sizedness_conditions(obligation, SizedTraitKind::Sized), + let self_ty = self.infcx.shallow_resolve( + self.infcx.enter_forall_and_leak_universe(obligation.predicate.self_ty()), + ); + let types = match tcx.as_lang_item(trait_def) { + Some(LangItem::Sized) => self.sizedness_conditions(self_ty, SizedTraitKind::Sized), Some(LangItem::MetaSized) => { - self.sizedness_conditions(obligation, SizedTraitKind::MetaSized) + self.sizedness_conditions(self_ty, SizedTraitKind::MetaSized) } Some(LangItem::PointeeSized) => { bug!("`PointeeSized` is removing during lowering"); } - Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(obligation), - Some(LangItem::FusedIterator) => self.fused_iterator_conditions(obligation), + Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(self_ty), + Some(LangItem::FusedIterator) => { + if self.coroutine_is_gen(self_ty) { + ty::Binder::dummy(vec![]) + } else { + unreachable!("tried to assemble `FusedIterator` for non-gen coroutine"); + } + } Some( LangItem::Destruct | LangItem::DiscriminantKind @@ -274,12 +283,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | LangItem::PointeeTrait | LangItem::Tuple | LangItem::Unpin, - ) => BuiltinImplConditions::Where(ty::Binder::dummy(vec![])), + ) => ty::Binder::dummy(vec![]), other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"), }; - let BuiltinImplConditions::Where(types) = conditions else { - bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation); - }; let types = self.infcx.enter_forall_and_leak_universe(types); let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 308a0307afd..2e65750db25 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -188,18 +188,6 @@ struct EvaluatedCandidate<'tcx> { evaluation: EvaluationResult, } -/// When does the builtin impl for `T: Trait` apply? -#[derive(Debug)] -enum BuiltinImplConditions<'tcx> { - /// The impl is conditional on `T1, T2, ...: Trait`. - Where(ty::Binder<'tcx, Vec>>), - /// There is no built-in impl. There may be some other - /// candidate (a where-clause or user-defined impl). - None, - /// It is unknown whether there is an impl. - Ambiguous, -} - impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { @@ -2104,14 +2092,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { impl<'tcx> SelectionContext<'_, 'tcx> { fn sizedness_conditions( &mut self, - obligation: &PolyTraitObligation<'tcx>, + self_ty: Ty<'tcx>, sizedness: SizedTraitKind, - ) -> BuiltinImplConditions<'tcx> { - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - + ) -> ty::Binder<'tcx, Vec>> { match self_ty.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) @@ -2129,59 +2112,44 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never - | ty::Error(_) => { - // safe for everything - Where(ty::Binder::dummy(Vec::new())) - } + | ty::Error(_) => ty::Binder::dummy(vec![]), ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { - SizedTraitKind::Sized => None, - SizedTraitKind::MetaSized => Where(ty::Binder::dummy(Vec::new())), + SizedTraitKind::Sized => unreachable!("tried to assemble `Sized` for unsized type"), + SizedTraitKind::MetaSized => ty::Binder::dummy(vec![]), }, - ty::Foreign(..) => None, + ty::Foreign(..) => unreachable!("tried to assemble `Sized` for unsized type"), - ty::Tuple(tys) => Where( - obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])), - ), + ty::Tuple(tys) => { + ty::Binder::dummy(tys.last().map_or_else(Vec::new, |&last| vec![last])) + } - ty::Pat(ty, _) => Where(obligation.predicate.rebind(vec![*ty])), + ty::Pat(ty, _) => ty::Binder::dummy(vec![*ty]), ty::Adt(def, args) => { if let Some(crit) = def.sizedness_constraint(self.tcx(), sizedness) { - // (*) binder moved here - Where(obligation.predicate.rebind(vec![crit.instantiate(self.tcx(), args)])) + ty::Binder::dummy(vec![crit.instantiate(self.tcx(), args)]) } else { - Where(ty::Binder::dummy(Vec::new())) + ty::Binder::dummy(vec![]) } } - // FIXME(unsafe_binders): This binder needs to be squashed - ty::UnsafeBinder(binder_ty) => Where(binder_ty.map_bound(|ty| vec![ty])), - - ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None, - ty::Infer(ty::TyVar(_)) => Ambiguous, - - // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. - ty::Bound(..) => None, + ty::UnsafeBinder(binder_ty) => binder_ty.map_bound(|ty| vec![ty]), - ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Bound(..) => { + bug!("asked to assemble `Sized` of unexpected type: {:?}", self_ty); } } } - fn copy_clone_conditions( - &mut self, - obligation: &PolyTraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - + fn copy_clone_conditions(&mut self, self_ty: Ty<'tcx>) -> ty::Binder<'tcx, Vec>> { match *self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => ty::Binder::dummy(vec![]), ty::Uint(_) | ty::Int(_) @@ -2193,127 +2161,78 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Never | ty::Ref(_, _, hir::Mutability::Not) | ty::Array(..) => { - // Implementations provided in libcore - None + unreachable!("tried to assemble `Sized` for type with libcore-provided impl") } // FIXME(unsafe_binder): Should we conditionally // (i.e. universally) implement copy/clone? - ty::UnsafeBinder(_) => None, - - ty::Dynamic(..) - | ty::Str - | ty::Slice(..) - | ty::Foreign(..) - | ty::Ref(_, _, hir::Mutability::Mut) => None, + ty::UnsafeBinder(_) => unreachable!("tried to assemble `Sized` for unsafe binder"), ty::Tuple(tys) => { // (*) binder moved here - Where(obligation.predicate.rebind(tys.iter().collect())) + ty::Binder::dummy(tys.iter().collect()) } ty::Pat(ty, _) => { // (*) binder moved here - Where(obligation.predicate.rebind(vec![ty])) + ty::Binder::dummy(vec![ty]) } ty::Coroutine(coroutine_def_id, args) => { match self.tcx().coroutine_movability(coroutine_def_id) { - hir::Movability::Static => None, + hir::Movability::Static => { + unreachable!("tried to assemble `Sized` for static coroutine") + } hir::Movability::Movable => { if self.tcx().features().coroutine_clone() { - let resolved_upvars = - self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); - let resolved_witness = - self.infcx.shallow_resolve(args.as_coroutine().witness()); - if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() { - // Not yet resolved. - Ambiguous - } else { - let all = args - .as_coroutine() + ty::Binder::dummy( + args.as_coroutine() .upvar_tys() .iter() .chain([args.as_coroutine().witness()]) - .collect::>(); - Where(obligation.predicate.rebind(all)) - } + .collect::>(), + ) } else { - None + unreachable!( + "tried to assemble `Sized` for coroutine without enabled feature" + ) } } } } - ty::CoroutineWitness(def_id, args) => { - let hidden_types = rebind_coroutine_witness_types( - self.infcx.tcx, - def_id, - args, - obligation.predicate.bound_vars(), - ); - Where(hidden_types) - } + ty::CoroutineWitness(def_id, args) => self + .infcx + .tcx + .coroutine_hidden_types(def_id) + .instantiate(self.infcx.tcx, args) + .map_bound(|witness| witness.types.to_vec()), - ty::Closure(_, args) => { - // (*) binder moved here - let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); - if let ty::Infer(ty::TyVar(_)) = ty.kind() { - // Not yet resolved. - Ambiguous - } else { - Where(obligation.predicate.rebind(args.as_closure().upvar_tys().to_vec())) - } - } + ty::Closure(_, args) => ty::Binder::dummy(args.as_closure().upvar_tys().to_vec()), ty::CoroutineClosure(_, args) => { - // (*) binder moved here - let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); - if let ty::Infer(ty::TyVar(_)) = ty.kind() { - // Not yet resolved. - Ambiguous - } else { - Where( - obligation - .predicate - .rebind(args.as_coroutine_closure().upvar_tys().to_vec()), - ) - } - } - - ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => { - // Fallback to whatever user-defined impls exist in this case. - None + ty::Binder::dummy(args.as_coroutine_closure().upvar_tys().to_vec()) } - ty::Infer(ty::TyVar(_)) => { - // Unbound type variable. Might or might not have - // applicable impls and so forth, depending on what - // those type variables wind up being bound to. - Ambiguous - } - - // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. - ty::Bound(..) => None, - - ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + ty::Foreign(..) + | ty::Str + | ty::Slice(_) + | ty::Dynamic(..) + | ty::Adt(..) + | ty::Alias(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Ref(_, _, ty::Mutability::Mut) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); } } } - fn fused_iterator_conditions( - &mut self, - obligation: &PolyTraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - if let ty::Coroutine(did, ..) = *self_ty.kind() - && self.tcx().coroutine_is_gen(did) - { - BuiltinImplConditions::Where(ty::Binder::dummy(Vec::new())) - } else { - BuiltinImplConditions::None - } + fn coroutine_is_gen(&mut self, self_ty: Ty<'tcx>) -> bool { + matches!(*self_ty.kind(), ty::Coroutine(did, ..) + if self.tcx().coroutine_is_gen(did)) } /// For default impls, we need to break apart a type into its @@ -2886,23 +2805,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } -fn rebind_coroutine_witness_types<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - args: ty::GenericArgsRef<'tcx>, - bound_vars: &'tcx ty::List, -) -> ty::Binder<'tcx, Vec>> { - let bound_coroutine_types = tcx.coroutine_hidden_types(def_id).skip_binder(); - let shifted_coroutine_types = - tcx.shift_bound_var_indices(bound_vars.len(), bound_coroutine_types.skip_binder()); - ty::Binder::bind_with_vars( - ty::EarlyBinder::bind(shifted_coroutine_types.types.to_vec()).instantiate(tcx, args), - tcx.mk_bound_variable_kinds_from_iter( - bound_vars.iter().chain(bound_coroutine_types.bound_vars()), - ), - ) -} - impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { TraitObligationStackList::with(self) -- cgit 1.4.1-3-g733a5 From fab9c64e2d98af65820a660f345106cc586b0006 Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Tue, 8 Jul 2025 19:03:33 +0200 Subject: Add triagebot stdarch mention ping --- triagebot.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 4e3dff219f1..6b989102f12 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -931,6 +931,15 @@ instead. """ cc = ["@tgross35"] +[mentions."library/stdarch"] +message = """ +`stdarch` is developed in its own repository. If possible, consider \ +making this change to \ +[rust-lang/stdarch](https://github.com/rust-lang/stdarch) \ +instead. +""" +cc = ["@Amanieu", "@folkertdev", "@sayantn"] + [mentions."library/core/src/intrinsics/simd.rs"] message = """ Some changes occurred to the platform-builtins intrinsics. Make sure the -- cgit 1.4.1-3-g733a5 From 66bc2340876746365870a61b3ee3f769713c7525 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 3 Jul 2025 13:21:35 -0500 Subject: tidy: refactor --extra-checks parsing --- src/tools/tidy/src/ext_tool_checks.rs | 125 ++++++++++++++++++++++++++++++---- 1 file changed, 111 insertions(+), 14 deletions(-) diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs index d2da63a9703..c552cf8e400 100644 --- a/src/tools/tidy/src/ext_tool_checks.rs +++ b/src/tools/tidy/src/ext_tool_checks.rs @@ -20,6 +20,7 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::process::Command; +use std::str::FromStr; use std::{fmt, fs, io}; const MIN_PY_REV: (u32, u32) = (3, 9); @@ -61,25 +62,38 @@ fn check_impl( // Split comma-separated args up let lint_args = match extra_checks { - Some(s) => s.strip_prefix("--extra-checks=").unwrap().split(',').collect(), + Some(s) => s + .strip_prefix("--extra-checks=") + .unwrap() + .split(',') + .map(|s| { + if s == "spellcheck:fix" { + eprintln!("warning: `spellcheck:fix` is no longer valid, use `--extra=check=spellcheck --bless`"); + } + (ExtraCheckArg::from_str(s), s) + }) + .filter_map(|(res, src)| match res { + Ok(x) => Some(x), + Err(err) => { + eprintln!("warning: bad extra check argument {src:?}: {err:?}"); + None + } + }) + .collect(), None => vec![], }; - if lint_args.contains(&"spellcheck:fix") { - return Err(Error::Generic( - "`spellcheck:fix` is no longer valid, use `--extra=check=spellcheck --bless`" - .to_string(), - )); + macro_rules! extra_check { + ($lang:ident, $kind:ident) => { + lint_args.iter().any(|arg| arg.matches(ExtraCheckLang::$lang, ExtraCheckKind::$kind)) + }; } - let python_all = lint_args.contains(&"py"); - let python_lint = lint_args.contains(&"py:lint") || python_all; - let python_fmt = lint_args.contains(&"py:fmt") || python_all; - let shell_all = lint_args.contains(&"shell"); - let shell_lint = lint_args.contains(&"shell:lint") || shell_all; - let cpp_all = lint_args.contains(&"cpp"); - let cpp_fmt = lint_args.contains(&"cpp:fmt") || cpp_all; - let spellcheck = lint_args.contains(&"spellcheck"); + let python_lint = extra_check!(Py, Lint); + let python_fmt = extra_check!(Py, Fmt); + let shell_lint = extra_check!(Shell, Lint); + let cpp_fmt = extra_check!(Cpp, Fmt); + let spellcheck = extra_check!(Spellcheck, None); let mut py_path = None; @@ -638,3 +652,86 @@ impl From for Error { Self::Io(value) } } + +#[derive(Debug)] +enum ExtraCheckParseError { + #[allow(dead_code, reason = "shown through Debug")] + UnknownKind(String), + #[allow(dead_code)] + UnknownLang(String), + /// Too many `:` + TooManyParts, + /// Tried to parse the empty string + Empty, +} + +struct ExtraCheckArg { + lang: ExtraCheckLang, + /// None = run all extra checks for the given lang + kind: Option, +} + +impl ExtraCheckArg { + fn matches(&self, lang: ExtraCheckLang, kind: ExtraCheckKind) -> bool { + self.lang == lang && self.kind.map(|k| k == kind).unwrap_or(true) + } +} + +impl FromStr for ExtraCheckArg { + type Err = ExtraCheckParseError; + + fn from_str(s: &str) -> Result { + let mut parts = s.split(':'); + let Some(first) = parts.next() else { + return Err(ExtraCheckParseError::Empty); + }; + let second = parts.next(); + if parts.next().is_some() { + return Err(ExtraCheckParseError::TooManyParts); + } + Ok(Self { lang: first.parse()?, kind: second.map(|s| s.parse()).transpose()? }) + } +} + +#[derive(PartialEq, Copy, Clone)] +enum ExtraCheckLang { + Py, + Shell, + Cpp, + Spellcheck, +} + +impl FromStr for ExtraCheckLang { + type Err = ExtraCheckParseError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "py" => Self::Py, + "shell" => Self::Shell, + "cpp" => Self::Cpp, + "spellcheck" => Self::Spellcheck, + _ => return Err(ExtraCheckParseError::UnknownLang(s.to_string())), + }) + } +} + +#[derive(PartialEq, Copy, Clone)] +enum ExtraCheckKind { + Lint, + Fmt, + /// Never parsed, but used as a placeholder for + /// langs that never have a specific kind. + None, +} + +impl FromStr for ExtraCheckKind { + type Err = ExtraCheckParseError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "lint" => Self::Lint, + "fmt" => Self::Fmt, + _ => return Err(ExtraCheckParseError::UnknownKind(s.to_string())), + }) + } +} -- cgit 1.4.1-3-g733a5 From 7c8a6d978bb47827eeef15448ca95f82af32c381 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 8 Jul 2025 14:18:07 -0400 Subject: Spelling --- src/bootstrap/configure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 86208b94261..b05a5cc8b81 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -746,7 +746,7 @@ def write_uncommented(target, f): block = [] def flush(last): - # If the block is entiry made of comments, ignore it + # If the block is entirely made of comments, ignore it entire_block_comments = all(ln.startswith("#") or ln == "" for ln in block) if not entire_block_comments and len(block) > 0: for line in block: -- cgit 1.4.1-3-g733a5 From 9eb180541ac58409b1b2c82ad86e4078bcbb741b Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 3 Jul 2025 13:37:20 -0500 Subject: tidy: factor out change detection logic and make it more robust now does proper parsing of git's output and falls back to assuming all files are modified if `git` doesn't work. accepts a closure so extensions can be checked. --- src/tools/tidy/src/lib.rs | 26 ++++++++++++++++++++++++++ src/tools/tidy/src/rustdoc_json.rs | 20 ++++---------------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 237737f0f16..90111079ecd 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -124,6 +124,32 @@ pub fn git_diff>(base_commit: &str, extra_arg: S) -> Option bool) -> bool { + match crate::git_diff(&base_commit, "--name-status") { + Some(output) => { + let modified_files = output.lines().filter_map(|ln| { + let (status, name) = ln + .trim_end() + .split_once('\t') + .expect("bad format from `git diff --name-status`"); + if status == "M" { Some(name) } else { None } + }); + for modified_file in modified_files { + if pred(modified_file) { + return true; + } + } + false + } + None => { + eprintln!("warning: failed to run `git diff` to check for changes"); + eprintln!("warning: assuming all files are modified"); + true + } + } +} + pub mod alphabetical; pub mod bins; pub mod debug_artifacts; diff --git a/src/tools/tidy/src/rustdoc_json.rs b/src/tools/tidy/src/rustdoc_json.rs index dfbb35d69f1..da6f51c2a22 100644 --- a/src/tools/tidy/src/rustdoc_json.rs +++ b/src/tools/tidy/src/rustdoc_json.rs @@ -14,22 +14,10 @@ pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) { }; // First we check that `src/rustdoc-json-types` was modified. - match crate::git_diff(&base_commit, "--name-status") { - Some(output) => { - if !output - .lines() - .any(|line| line.starts_with("M") && line.contains(RUSTDOC_JSON_TYPES)) - { - // `rustdoc-json-types` was not modified so nothing more to check here. - println!("`rustdoc-json-types` was not modified."); - return; - } - } - None => { - *bad = true; - eprintln!("error: failed to run `git diff` in rustdoc_json check"); - return; - } + if !crate::files_modified(base_commit, |p| p == RUSTDOC_JSON_TYPES) { + // `rustdoc-json-types` was not modified so nothing more to check here. + println!("`rustdoc-json-types` was not modified."); + return; } // Then we check that if `FORMAT_VERSION` was updated, the `Latest feature:` was also updated. match crate::git_diff(&base_commit, src_path.join("rustdoc-json-types")) { -- cgit 1.4.1-3-g733a5 From 4a6f6977f98026a9359a3b168b6d50045b1c07a8 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 3 Jul 2025 14:06:32 -0500 Subject: tidy: update files_modified to take CiInfo --- src/tools/tidy/src/lib.rs | 8 ++++++-- src/tools/tidy/src/rustdoc_json.rs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 90111079ecd..062cfe7b597 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -124,8 +124,12 @@ pub fn git_diff>(base_commit: &str, extra_arg: S) -> Option bool) -> bool { +/// Returns true if any modified file matches the predicate, if we are in CI, or if unable to list modified files. +pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool { + let Some(base_commit) = &ci_info.base_commit else { + eprintln!("No base commit, assuming all files are modified"); + return true; + }; match crate::git_diff(&base_commit, "--name-status") { Some(output) => { let modified_files = output.lines().filter_map(|ln| { diff --git a/src/tools/tidy/src/rustdoc_json.rs b/src/tools/tidy/src/rustdoc_json.rs index da6f51c2a22..19b8e79ec33 100644 --- a/src/tools/tidy/src/rustdoc_json.rs +++ b/src/tools/tidy/src/rustdoc_json.rs @@ -14,7 +14,7 @@ pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) { }; // First we check that `src/rustdoc-json-types` was modified. - if !crate::files_modified(base_commit, |p| p == RUSTDOC_JSON_TYPES) { + if !crate::files_modified(ci_info, |p| p == RUSTDOC_JSON_TYPES) { // `rustdoc-json-types` was not modified so nothing more to check here. println!("`rustdoc-json-types` was not modified."); return; -- cgit 1.4.1-3-g733a5 From 64456e07b8e8550376d5136600fb0c59178df6a0 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 3 Jul 2025 14:27:47 -0500 Subject: tidy: add `auto:` prefix to --extra-checks syntax currently this just uses a very simple extension-based heirustic. --- src/bootstrap/src/core/config/flags.rs | 5 ++- src/etc/completions/x.fish | 2 +- src/etc/completions/x.ps1 | 2 +- src/etc/completions/x.py.fish | 2 +- src/etc/completions/x.py.ps1 | 2 +- src/etc/completions/x.py.zsh | 2 +- src/etc/completions/x.zsh | 2 +- src/tools/tidy/src/ext_tool_checks.rs | 60 +++++++++++++++++++++++++++------- src/tools/tidy/src/main.rs | 10 +++++- 9 files changed, 67 insertions(+), 20 deletions(-) diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index bfc06f90d4f..19e0d3413bb 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -383,7 +383,10 @@ pub enum Subcommand { bless: bool, #[arg(long)] /// comma-separated list of other files types to check (accepts py, py:lint, - /// py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix) + /// py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck) + /// + /// Any argument can be prefixed with "auto:" to only run if + /// relevant files are modified (eg. "auto:py"). extra_checks: Option, #[arg(long)] /// rerun tests even if the inputs are unchanged diff --git a/src/etc/completions/x.fish b/src/etc/completions/x.fish index 69bd525a312..3009dd85e93 100644 --- a/src/etc/completions/x.fish +++ b/src/etc/completions/x.fish @@ -293,7 +293,7 @@ complete -c x -n "__fish_x_using_subcommand doc" -l skip-std-check-if-no-downloa complete -c x -n "__fish_x_using_subcommand doc" -s h -l help -d 'Print help (see more with \'--help\')' complete -c x -n "__fish_x_using_subcommand test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r complete -c x -n "__fish_x_using_subcommand test" -l compiletest-rustc-args -d 'extra options to pass the compiler when running compiletest tests' -r -complete -c x -n "__fish_x_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix)' -r +complete -c x -n "__fish_x_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)' -r complete -c x -n "__fish_x_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x -n "__fish_x_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x -n "__fish_x_using_subcommand test" -l run -d 'whether to execute run-* tests' -r diff --git a/src/etc/completions/x.ps1 b/src/etc/completions/x.ps1 index 7b142ba97ce..8002ec31592 100644 --- a/src/etc/completions/x.ps1 +++ b/src/etc/completions/x.ps1 @@ -339,7 +339,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { 'x;test' { [CompletionResult]::new('--test-args', '--test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)') [CompletionResult]::new('--compiletest-rustc-args', '--compiletest-rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running compiletest tests') - [CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix)') + [CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)') [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index e982cde634f..4766a5ee0c7 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -293,7 +293,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand doc" -l skip-std-check-if-no-d complete -c x.py -n "__fish_x.py_using_subcommand doc" -s h -l help -d 'Print help (see more with \'--help\')' complete -c x.py -n "__fish_x.py_using_subcommand test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l compiletest-rustc-args -d 'extra options to pass the compiler when running compiletest tests' -r -complete -c x.py -n "__fish_x.py_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix)' -r +complete -c x.py -n "__fish_x.py_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l run -d 'whether to execute run-* tests' -r diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index f4b26fac0bc..1aff3c433b4 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -339,7 +339,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { 'x.py;test' { [CompletionResult]::new('--test-args', '--test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)') [CompletionResult]::new('--compiletest-rustc-args', '--compiletest-rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running compiletest tests') - [CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix)') + [CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)') [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index 8fc4f052252..77f995c1704 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -338,7 +338,7 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS:_default' \ '*--compiletest-rustc-args=[extra options to pass the compiler when running compiletest tests]:ARGS:_default' \ -'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, shell\:lint, cpp, cpp\:fmt, spellcheck, spellcheck\:fix)]:EXTRA_CHECKS:_default' \ +'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, shell\:lint, cpp, cpp\:fmt, spellcheck)]:EXTRA_CHECKS:_default' \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ diff --git a/src/etc/completions/x.zsh b/src/etc/completions/x.zsh index c495e8318ba..6c6d7b3f49f 100644 --- a/src/etc/completions/x.zsh +++ b/src/etc/completions/x.zsh @@ -338,7 +338,7 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS:_default' \ '*--compiletest-rustc-args=[extra options to pass the compiler when running compiletest tests]:ARGS:_default' \ -'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, shell\:lint, cpp, cpp\:fmt, spellcheck, spellcheck\:fix)]:EXTRA_CHECKS:_default' \ +'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, shell\:lint, cpp, cpp\:fmt, spellcheck)]:EXTRA_CHECKS:_default' \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs index c552cf8e400..be2f585a6d8 100644 --- a/src/tools/tidy/src/ext_tool_checks.rs +++ b/src/tools/tidy/src/ext_tool_checks.rs @@ -23,6 +23,8 @@ use std::process::Command; use std::str::FromStr; use std::{fmt, fs, io}; +use crate::CiInfo; + const MIN_PY_REV: (u32, u32) = (3, 9); const MIN_PY_REV_STR: &str = "≥3.9"; @@ -37,15 +39,18 @@ const RUFF_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "ruff.toml const RUFF_CACHE_PATH: &[&str] = &["cache", "ruff_cache"]; const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements.txt"]; +const SPELLCHECK_DIRS: &[&str] = &["compiler", "library", "src/bootstrap", "src/librustdoc"]; + pub fn check( root_path: &Path, outdir: &Path, + ci_info: &CiInfo, bless: bool, extra_checks: Option<&str>, pos_args: &[String], bad: &mut bool, ) { - if let Err(e) = check_impl(root_path, outdir, bless, extra_checks, pos_args) { + if let Err(e) = check_impl(root_path, outdir, ci_info, bless, extra_checks, pos_args) { tidy_error!(bad, "{e}"); } } @@ -53,6 +58,7 @@ pub fn check( fn check_impl( root_path: &Path, outdir: &Path, + ci_info: &CiInfo, bless: bool, extra_checks: Option<&str>, pos_args: &[String], @@ -73,7 +79,13 @@ fn check_impl( (ExtraCheckArg::from_str(s), s) }) .filter_map(|(res, src)| match res { - Ok(x) => Some(x), + Ok(arg) => { + if arg.is_inactive_auto(ci_info) { + None + } else { + Some(arg) + } + } Err(err) => { eprintln!("warning: bad extra check argument {src:?}: {err:?}"); None @@ -249,14 +261,9 @@ fn check_impl( if spellcheck { let config_path = root_path.join("typos.toml"); // sync target files with .github/workflows/spellcheck.yml - let mut args = vec![ - "-c", - config_path.as_os_str().to_str().unwrap(), - "./compiler", - "./library", - "./src/bootstrap", - "./src/librustdoc", - ]; + let mut args = vec!["-c", config_path.as_os_str().to_str().unwrap()]; + + args.extend_from_slice(SPELLCHECK_DIRS); if bless { eprintln!("spellcheck files and fix"); @@ -663,9 +670,12 @@ enum ExtraCheckParseError { TooManyParts, /// Tried to parse the empty string Empty, + /// `auto` specified without lang part. + AutoRequiresLang, } struct ExtraCheckArg { + auto: bool, lang: ExtraCheckLang, /// None = run all extra checks for the given lang kind: Option, @@ -675,21 +685,47 @@ impl ExtraCheckArg { fn matches(&self, lang: ExtraCheckLang, kind: ExtraCheckKind) -> bool { self.lang == lang && self.kind.map(|k| k == kind).unwrap_or(true) } + + /// Returns `true` if this is an auto arg and the relevant files are not modified. + fn is_inactive_auto(&self, ci_info: &CiInfo) -> bool { + if !self.auto { + return false; + } + let ext = match self.lang { + ExtraCheckLang::Py => ".py", + ExtraCheckLang::Cpp => ".cpp", + ExtraCheckLang::Shell => ".sh", + ExtraCheckLang::Spellcheck => { + return !crate::files_modified(ci_info, |s| { + SPELLCHECK_DIRS.iter().any(|dir| Path::new(s).starts_with(dir)) + }); + } + }; + !crate::files_modified(ci_info, |s| s.ends_with(ext)) + } } impl FromStr for ExtraCheckArg { type Err = ExtraCheckParseError; fn from_str(s: &str) -> Result { + let mut auto = false; let mut parts = s.split(':'); - let Some(first) = parts.next() else { + let Some(mut first) = parts.next() else { return Err(ExtraCheckParseError::Empty); }; + if first == "auto" { + let Some(part) = parts.next() else { + return Err(ExtraCheckParseError::AutoRequiresLang); + }; + auto = true; + first = part; + } let second = parts.next(); if parts.next().is_some() { return Err(ExtraCheckParseError::TooManyParts); } - Ok(Self { lang: first.parse()?, kind: second.map(|s| s.parse()).transpose()? }) + Ok(Self { auto, lang: first.parse()?, kind: second.map(|s| s.parse()).transpose()? }) } } diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index ef6ff5c9277..1eb5485f2b8 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -173,7 +173,15 @@ fn main() { }; check!(unstable_book, &src_path, collected); - check!(ext_tool_checks, &root_path, &output_directory, bless, extra_checks, pos_args); + check!( + ext_tool_checks, + &root_path, + &output_directory, + &ci_info, + bless, + extra_checks, + pos_args + ); }); if bad.load(Ordering::Relaxed) { -- cgit 1.4.1-3-g733a5 From 6b349a41da18b0960c7a2e7bb2d8444b56f32054 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 3 Jul 2025 14:42:42 -0500 Subject: tidy: warn when --extra-checks is passed an invalid lang:kind combo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Beránek --- src/tools/tidy/src/ext_tool_checks.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs index be2f585a6d8..baab46752a5 100644 --- a/src/tools/tidy/src/ext_tool_checks.rs +++ b/src/tools/tidy/src/ext_tool_checks.rs @@ -39,6 +39,7 @@ const RUFF_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "ruff.toml const RUFF_CACHE_PATH: &[&str] = &["cache", "ruff_cache"]; const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements.txt"]; +// this must be kept in sync with with .github/workflows/spellcheck.yml const SPELLCHECK_DIRS: &[&str] = &["compiler", "library", "src/bootstrap", "src/librustdoc"]; pub fn check( @@ -74,7 +75,7 @@ fn check_impl( .split(',') .map(|s| { if s == "spellcheck:fix" { - eprintln!("warning: `spellcheck:fix` is no longer valid, use `--extra=check=spellcheck --bless`"); + eprintln!("warning: `spellcheck:fix` is no longer valid, use `--extra-checks=spellcheck --bless`"); } (ExtraCheckArg::from_str(s), s) }) @@ -87,6 +88,7 @@ fn check_impl( } } Err(err) => { + // only warn because before bad extra checks would be silently ignored. eprintln!("warning: bad extra check argument {src:?}: {err:?}"); None } @@ -260,7 +262,6 @@ fn check_impl( if spellcheck { let config_path = root_path.join("typos.toml"); - // sync target files with .github/workflows/spellcheck.yml let mut args = vec!["-c", config_path.as_os_str().to_str().unwrap()]; args.extend_from_slice(SPELLCHECK_DIRS); @@ -666,6 +667,7 @@ enum ExtraCheckParseError { UnknownKind(String), #[allow(dead_code)] UnknownLang(String), + UnsupportedKindForLang, /// Too many `:` TooManyParts, /// Tried to parse the empty string @@ -703,6 +705,21 @@ impl ExtraCheckArg { }; !crate::files_modified(ci_info, |s| s.ends_with(ext)) } + + fn has_supported_kind(&self) -> bool { + let Some(kind) = self.kind else { + // "run all extra checks" mode is supported for all languages. + return true; + }; + use ExtraCheckKind::*; + let supported_kinds: &[_] = match self.lang { + ExtraCheckLang::Py => &[Fmt, Lint], + ExtraCheckLang::Cpp => &[Fmt], + ExtraCheckLang::Shell => &[Lint], + ExtraCheckLang::Spellcheck => &[], + }; + supported_kinds.contains(&kind) + } } impl FromStr for ExtraCheckArg { @@ -725,7 +742,12 @@ impl FromStr for ExtraCheckArg { if parts.next().is_some() { return Err(ExtraCheckParseError::TooManyParts); } - Ok(Self { auto, lang: first.parse()?, kind: second.map(|s| s.parse()).transpose()? }) + let arg = Self { auto, lang: first.parse()?, kind: second.map(|s| s.parse()).transpose()? }; + if !arg.has_supported_kind() { + return Err(ExtraCheckParseError::UnsupportedKindForLang); + } + + Ok(arg) } } -- cgit 1.4.1-3-g733a5 From 9aafc98244c0a8e0ec83203e7d17e02fcdfa0371 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 8 Jul 2025 12:32:33 -0500 Subject: tidy: assume all files are modified in CI --- src/tools/tidy/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 062cfe7b597..77855392b4d 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -126,6 +126,10 @@ pub fn git_diff>(base_commit: &str, extra_arg: S) -> Option bool) -> bool { + if CiEnv::is_ci() { + // assume everything is modified on CI because we really don't want false positives there. + return true; + } let Some(base_commit) = &ci_info.base_commit else { eprintln!("No base commit, assuming all files are modified"); return true; -- cgit 1.4.1-3-g733a5 From 1f80fd0f23405bd5d6061684eb2259dc92116f81 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 8 Jul 2025 12:36:15 -0500 Subject: bootstrap: add change tracker entry for new --extra-checks=auto: feature --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 424f211c7d4..29591edc9cc 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -461,4 +461,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "`download-rustc` has been temporarily disabled for the library profile due to implementation bugs (see #142505).", }, + ChangeInfo { + change_id: 143398, + severity: ChangeSeverity::Info, + summary: "The --extra-checks flag now supports prefixing any check with `auto:` to only run it if relevant files are modified", + }, ]; -- cgit 1.4.1-3-g733a5 From c9ff1609d4f7ccd001d9a1487e644831d0b06947 Mon Sep 17 00:00:00 2001 From: Moulins Date: Tue, 8 Jul 2025 21:10:40 +0200 Subject: docs: document trait upcasting rules in `Unsize` trait --- library/core/src/marker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 2aeb0b0c31e..f092916eb8c 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -210,6 +210,11 @@ pub trait PointeeSized { /// - `Trait` is dyn-compatible[^1]. /// - The type is sized. /// - The type outlives `'a`. +/// - Trait objects `dyn TraitA + AutoA... + 'a` implement `Unsize` +/// if all of these conditions are met: +/// - `TraitB` is a supertrait of `TraitA`. +/// - `AutoB...` is a subset of `AutoA...`. +/// - `'a` outlives `'b`. /// - Structs `Foo<..., T1, ..., Tn, ...>` implement `Unsize>` /// where any number of (type and const) parameters may be changed if all of these conditions /// are met: -- cgit 1.4.1-3-g733a5 From e391578abb374d533162ada0b8a5b03e43ef5bc0 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Wed, 9 Jul 2025 01:40:47 +0300 Subject: Use "Innermost" & "Outermost" terminology for `AttributeOrder` --- .../src/attributes/codegen_attrs.rs | 4 +-- .../src/attributes/deprecation.rs | 2 +- .../rustc_attr_parsing/src/attributes/dummy.rs | 2 +- .../rustc_attr_parsing/src/attributes/inline.rs | 4 +-- .../src/attributes/link_attrs.rs | 4 +-- compiler/rustc_attr_parsing/src/attributes/mod.rs | 39 ++++++++-------------- .../rustc_attr_parsing/src/attributes/must_use.rs | 2 +- compiler/rustc_attr_parsing/src/attributes/path.rs | 2 +- .../src/attributes/rustc_internal.rs | 6 ++-- .../src/attributes/test_attrs.rs | 2 +- .../rustc_attr_parsing/src/attributes/traits.rs | 2 +- .../src/attributes/transparency.rs | 2 +- 12 files changed, 29 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index cb3956d46a0..fdec09edaa1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -15,7 +15,7 @@ pub(crate) struct OptimizeParser; impl SingleAttributeParser for OptimizeParser { const PATH: &[Symbol] = &[sym::optimize]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none"); @@ -56,7 +56,7 @@ pub(crate) struct ExportNameParser; impl SingleAttributeParser for ExportNameParser { const PATH: &[rustc_span::Symbol] = &[sym::export_name]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 702ad66f578..08cf1ab5d19 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -36,7 +36,7 @@ fn get( impl SingleAttributeParser for DeprecationParser { const PATH: &[Symbol] = &[sym::deprecated]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!( Word, diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs index 8a9b94fb1ff..e5e1c3bb6b6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/dummy.rs +++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs @@ -9,7 +9,7 @@ use crate::parser::ArgParser; pub(crate) struct DummyParser; impl SingleAttributeParser for DummyParser { const PATH: &[Symbol] = &[sym::rustc_dummy]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index 11844f4cd95..fe812175218 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -16,7 +16,7 @@ pub(crate) struct InlineParser; impl SingleAttributeParser for InlineParser { const PATH: &'static [Symbol] = &[sym::inline]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(Word, List: "always|never"); @@ -57,7 +57,7 @@ pub(crate) struct RustcForceInlineParser; impl SingleAttributeParser for RustcForceInlineParser { const PATH: &'static [Symbol] = &[sym::rustc_force_inline]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason"); diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 1a66eec870f..23a8e96482d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -14,7 +14,7 @@ pub(crate) struct LinkNameParser; impl SingleAttributeParser for LinkNameParser { const PATH: &[Symbol] = &[sym::link_name]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); @@ -36,7 +36,7 @@ pub(crate) struct LinkSectionParser; impl SingleAttributeParser for LinkSectionParser { const PATH: &[Symbol] = &[sym::link_section]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 0225b141336..68c716d1a99 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -140,7 +140,7 @@ impl, S: Stage> AttributeParser for Single if let Some(pa) = T::convert(cx, args) { match T::ATTRIBUTE_ORDER { // keep the first and report immediately. ignore this attribute - AttributeOrder::KeepFirst => { + AttributeOrder::KeepInnermost => { if let Some((_, unused)) = group.1 { T::ON_DUPLICATE.exec::(cx, cx.attr_span, unused); return; @@ -148,7 +148,7 @@ impl, S: Stage> AttributeParser for Single } // keep the new one and warn about the previous, // then replace - AttributeOrder::KeepLast => { + AttributeOrder::KeepOutermost => { if let Some((_, used)) = group.1 { T::ON_DUPLICATE.exec::(cx, used, cx.attr_span); } @@ -165,9 +165,6 @@ impl, S: Stage> AttributeParser for Single } } -// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing -// them will be merged in another PR -#[allow(unused)] pub(crate) enum OnDuplicate { /// Give a default warning Warn, @@ -213,39 +210,29 @@ impl OnDuplicate { } } } -// -// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing -// them will be merged in another PR -#[allow(unused)] + pub(crate) enum AttributeOrder { - /// Duplicates after the first attribute will be an error. I.e. only keep the lowest attribute. + /// Duplicates after the innermost instance of the attribute will be an error/warning. + /// Only keep the lowest attribute. /// - /// Attributes are processed from bottom to top, so this raises an error on all the attributes + /// Attributes are processed from bottom to top, so this raises a warning/error on all the attributes /// further above the lowest one: /// ``` /// #[stable(since="1.0")] //~ WARNING duplicated attribute /// #[stable(since="2.0")] /// ``` - /// - /// This should be used where duplicates would be ignored, but carry extra - /// meaning that could cause confusion. For example, `#[stable(since="1.0")] - /// #[stable(since="2.0")]`, which version should be used for `stable`? - KeepFirst, + KeepInnermost, - /// Duplicates preceding the last instance of the attribute will be a - /// warning, with a note that this will be an error in the future. + /// Duplicates before the outermost instance of the attribute will be an error/warning. + /// Only keep the highest attribute. /// - /// Attributes are processed from bottom to top, so this raises a warning on all the attributes - /// below the higher one: + /// Attributes are processed from bottom to top, so this raises a warning/error on all the attributes + /// below the highest one: /// ``` /// #[path="foo.rs"] /// #[path="bar.rs"] //~ WARNING duplicated attribute /// ``` - /// - /// This is the same as `FutureWarnFollowing`, except the last attribute is - /// the one that is "used". Ideally these can eventually migrate to - /// `ErrorPreceding`. - KeepLast, + KeepOutermost, } /// An even simpler version of [`SingleAttributeParser`]: @@ -271,7 +258,7 @@ impl, S: Stage> Default for WithoutArgs { impl, S: Stage> SingleAttributeParser for WithoutArgs { const PATH: &[Symbol] = T::PATH; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = T::ON_DUPLICATE; const TEMPLATE: AttributeTemplate = template!(Word); diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index b5eb85f68b4..e0a3e675509 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -12,7 +12,7 @@ pub(crate) struct MustUseParser; impl SingleAttributeParser for MustUseParser { const PATH: &[Symbol] = &[sym::must_use]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason"); diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs index 0dfbc9a9aa8..febb1b45a18 100644 --- a/compiler/rustc_attr_parsing/src/attributes/path.rs +++ b/compiler/rustc_attr_parsing/src/attributes/path.rs @@ -10,7 +10,7 @@ pub(crate) struct PathParser; impl SingleAttributeParser for PathParser { const PATH: &[Symbol] = &[sym::path]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "file"); diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index e6b6a6fe3c9..ec821cb11ce 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -11,7 +11,7 @@ pub(crate) struct RustcLayoutScalarValidRangeStart; impl SingleAttributeParser for RustcLayoutScalarValidRangeStart { const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_start]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(List: "start"); @@ -25,7 +25,7 @@ pub(crate) struct RustcLayoutScalarValidRangeEnd; impl SingleAttributeParser for RustcLayoutScalarValidRangeEnd { const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_end]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(List: "end"); @@ -62,7 +62,7 @@ pub(crate) struct RustcObjectLifetimeDefaultParser; impl SingleAttributeParser for RustcObjectLifetimeDefaultParser { const PATH: &[rustc_span::Symbol] = &[sym::rustc_object_lifetime_default]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(Word); diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index cea3ee52ff4..ee81f64860f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -11,7 +11,7 @@ pub(crate) struct IgnoreParser; impl SingleAttributeParser for IgnoreParser { const PATH: &[Symbol] = &[sym::ignore]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason"); diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index 83a98c53c7f..c29dbf4d1e9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -12,7 +12,7 @@ pub(crate) struct SkipDuringMethodDispatchParser; impl SingleAttributeParser for SkipDuringMethodDispatchParser { const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(List: "array, boxed_slice"); diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index ce5ceb9139a..c9fdc57cc06 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -14,7 +14,7 @@ pub(crate) struct TransparencyParser; #[allow(rustc::diagnostic_outside_of_impl)] impl SingleAttributeParser for TransparencyParser { const PATH: &[Symbol] = &[sym::rustc_macro_transparency]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Custom(|cx, used, unused| { cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); }); -- cgit 1.4.1-3-g733a5 From c7ef03aeb7c33825e3d020c9e1791171e71cd445 Mon Sep 17 00:00:00 2001 From: Amogh Shivaram Date: Tue, 8 Jul 2025 18:15:05 -0500 Subject: Apply effects to otherwise edge in dataflow analysis --- .../rustc_mir_dataflow/src/drop_flag_effects.rs | 28 ++++-- .../rustc_mir_dataflow/src/framework/direction.rs | 11 ++- compiler/rustc_mir_dataflow/src/framework/mod.rs | 1 + .../rustc_mir_dataflow/src/impls/initialized.rs | 88 ++++++++++++----- .../rustc_mir_transform/src/elaborate_drops.rs | 2 + .../rustc_mir_transform/src/remove_uninit_drops.rs | 1 + .../otherwise_drops.result_ok.ElaborateDrops.diff | 108 +++++++++++++++++++++ tests/mir-opt/otherwise_drops.rs | 13 +++ ...ifetimes.foo.ScalarReplacementOfAggregates.diff | 11 +-- 9 files changed, 219 insertions(+), 44 deletions(-) create mode 100644 tests/mir-opt/otherwise_drops.result_ok.ElaborateDrops.diff create mode 100644 tests/mir-opt/otherwise_drops.rs diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs index 496a342cf4c..c9c7fddae5a 100644 --- a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs +++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs @@ -1,5 +1,6 @@ use rustc_abi::VariantIdx; use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind}; +use smallvec::SmallVec; use tracing::debug; use super::move_paths::{InitKind, LookupResult, MoveData, MovePathIndex}; @@ -155,15 +156,28 @@ where } } -/// Calls `handle_inactive_variant` for each descendant move path of `enum_place` that contains a -/// `Downcast` to a variant besides the `active_variant`. -/// -/// NOTE: If there are no move paths corresponding to an inactive variant, -/// `handle_inactive_variant` will not be called for that variant. +/// Indicates which variants are inactive at a `SwitchInt` edge by listing their `VariantIdx`s or +/// specifying the single active variant's `VariantIdx`. +pub(crate) enum InactiveVariants { + Inactives(SmallVec<[VariantIdx; 4]>), + Active(VariantIdx), +} + +impl InactiveVariants { + fn contains(&self, variant_idx: VariantIdx) -> bool { + match self { + InactiveVariants::Inactives(inactives) => inactives.contains(&variant_idx), + InactiveVariants::Active(active) => variant_idx != *active, + } + } +} + +/// Calls `handle_inactive_variant` for each child move path of `enum_place` corresponding to an +/// inactive variant at a particular `SwitchInt` edge. pub(crate) fn on_all_inactive_variants<'tcx>( move_data: &MoveData<'tcx>, enum_place: mir::Place<'tcx>, - active_variant: VariantIdx, + inactive_variants: &InactiveVariants, mut handle_inactive_variant: impl FnMut(MovePathIndex), ) { let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else { @@ -182,7 +196,7 @@ pub(crate) fn on_all_inactive_variants<'tcx>( unreachable!(); }; - if variant_idx != active_variant { + if inactive_variants.contains(variant_idx) { on_all_children_bits(move_data, variant_mpi, |mpi| handle_inactive_variant(mpi)); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index e955e38ad10..cb647476db8 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -112,12 +112,13 @@ impl Direction for Backward { propagate(pred, &tmp); } - mir::TerminatorKind::SwitchInt { targets: _, ref discr } => { + mir::TerminatorKind::SwitchInt { ref targets, ref discr } => { if let Some(mut data) = analysis.get_switch_int_data(block, discr) { let mut tmp = analysis.bottom_value(body); for &value in &body.basic_blocks.switch_sources()[&(block, pred)] { tmp.clone_from(exit_state); - analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value); + analysis + .apply_switch_int_edge_effect(&mut data, &mut tmp, value, targets); propagate(pred, &tmp); } } else { @@ -290,20 +291,20 @@ impl Direction for Forward { for (value, target) in targets.iter() { tmp.clone_from(exit_state); let value = SwitchTargetValue::Normal(value); - analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value); + analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value, targets); propagate(target, &tmp); } // Once we get to the final, "otherwise" branch, there is no need to preserve // `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save // a clone of the dataflow state. - let otherwise = targets.otherwise(); analysis.apply_switch_int_edge_effect( &mut data, exit_state, SwitchTargetValue::Otherwise, + targets, ); - propagate(otherwise, exit_state); + propagate(targets.otherwise(), exit_state); } else { for target in targets.all_targets() { propagate(*target, exit_state); diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 9cadec100b5..b6a56036019 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -224,6 +224,7 @@ pub trait Analysis<'tcx> { _data: &mut Self::SwitchIntData, _state: &mut Self::Domain, _value: SwitchTargetValue, + _targets: &mir::SwitchTargets, ) { unreachable!(); } diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 18165b0b9bd..085757f0fb6 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -9,9 +9,10 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::util::Discr; use rustc_middle::ty::{self, TyCtxt}; +use smallvec::SmallVec; use tracing::{debug, instrument}; -use crate::drop_flag_effects::DropFlagState; +use crate::drop_flag_effects::{DropFlagState, InactiveVariants}; use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; use crate::{ Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry, @@ -26,6 +27,12 @@ pub struct MaybePlacesSwitchIntData<'tcx> { } impl<'tcx> MaybePlacesSwitchIntData<'tcx> { + /// Creates a `SmallVec` mapping each target in `targets` to its `VariantIdx`. + fn variants(&mut self, targets: &mir::SwitchTargets) -> SmallVec<[VariantIdx; 4]> { + self.index = 0; + targets.all_values().iter().map(|value| self.next_discr(value.get())).collect() + } + // The discriminant order in the `SwitchInt` targets should match the order yielded by // `AdtDef::discriminants`. We rely on this to match each discriminant in the targets to its // corresponding variant in linear time. @@ -131,12 +138,26 @@ pub struct MaybeInitializedPlaces<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, + exclude_inactive_in_otherwise: bool, skip_unreachable_unwind: bool, } impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self { - MaybeInitializedPlaces { tcx, body, move_data, skip_unreachable_unwind: false } + MaybeInitializedPlaces { + tcx, + body, + move_data, + exclude_inactive_in_otherwise: false, + skip_unreachable_unwind: false, + } + } + + /// Ensures definitely inactive variants are excluded from the set of initialized places for + /// blocks reached through an `otherwise` edge. + pub fn exclude_inactive_in_otherwise(mut self) -> Self { + self.exclude_inactive_in_otherwise = true; + self } pub fn skipping_unreachable_unwind(mut self) -> Self { @@ -208,6 +229,7 @@ pub struct MaybeUninitializedPlaces<'a, 'tcx> { move_data: &'a MoveData<'tcx>, mark_inactive_variants_as_uninit: bool, + include_inactive_in_otherwise: bool, skip_unreachable_unwind: DenseBitSet, } @@ -218,6 +240,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { body, move_data, mark_inactive_variants_as_uninit: false, + include_inactive_in_otherwise: false, skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()), } } @@ -232,6 +255,13 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { self } + /// Ensures definitely inactive variants are included in the set of uninitialized places for + /// blocks reached through an `otherwise` edge. + pub fn include_inactive_in_otherwise(mut self) -> Self { + self.include_inactive_in_otherwise = true; + self + } + pub fn skipping_unreachable_unwind( mut self, unreachable_unwind: DenseBitSet, @@ -431,17 +461,24 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { data: &mut Self::SwitchIntData, state: &mut Self::Domain, value: SwitchTargetValue, + targets: &mir::SwitchTargets, ) { - if let SwitchTargetValue::Normal(value) = value { - // Kill all move paths that correspond to variants we know to be inactive along this - // particular outgoing edge of a `SwitchInt`. - drop_flag_effects::on_all_inactive_variants( - self.move_data, - data.enum_place, - data.next_discr(value), - |mpi| state.kill(mpi), - ); - } + let inactive_variants = match value { + SwitchTargetValue::Normal(value) => InactiveVariants::Active(data.next_discr(value)), + SwitchTargetValue::Otherwise if self.exclude_inactive_in_otherwise => { + InactiveVariants::Inactives(data.variants(targets)) + } + _ => return, + }; + + // Kill all move paths that correspond to variants we know to be inactive along this + // particular outgoing edge of a `SwitchInt`. + drop_flag_effects::on_all_inactive_variants( + self.move_data, + data.enum_place, + &inactive_variants, + |mpi| state.kill(mpi), + ); } } @@ -544,17 +581,24 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { data: &mut Self::SwitchIntData, state: &mut Self::Domain, value: SwitchTargetValue, + targets: &mir::SwitchTargets, ) { - if let SwitchTargetValue::Normal(value) = value { - // Mark all move paths that correspond to variants other than this one as maybe - // uninitialized (in reality, they are *definitely* uninitialized). - drop_flag_effects::on_all_inactive_variants( - self.move_data, - data.enum_place, - data.next_discr(value), - |mpi| state.gen_(mpi), - ); - } + let inactive_variants = match value { + SwitchTargetValue::Normal(value) => InactiveVariants::Active(data.next_discr(value)), + SwitchTargetValue::Otherwise if self.include_inactive_in_otherwise => { + InactiveVariants::Inactives(data.variants(targets)) + } + _ => return, + }; + + // Mark all move paths that correspond to variants other than this one as maybe + // uninitialized (in reality, they are *definitely* uninitialized). + drop_flag_effects::on_all_inactive_variants( + self.move_data, + data.enum_place, + &inactive_variants, + |mpi| state.gen_(mpi), + ); } } diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 42c8cb0b906..b4fa2be1d00 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -62,12 +62,14 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { let env = MoveDataTypingEnv { move_data, typing_env }; let mut inits = MaybeInitializedPlaces::new(tcx, body, &env.move_data) + .exclude_inactive_in_otherwise() .skipping_unreachable_unwind() .iterate_to_fixpoint(tcx, body, Some("elaborate_drops")) .into_results_cursor(body); let dead_unwinds = compute_dead_unwinds(body, &mut inits); let uninits = MaybeUninitializedPlaces::new(tcx, body, &env.move_data) + .include_inactive_in_otherwise() .mark_inactive_variants_as_uninit() .skipping_unreachable_unwind(dead_unwinds) .iterate_to_fixpoint(tcx, body, Some("elaborate_drops")) diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index 9044a88295c..25d6fa1bab9 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -22,6 +22,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, typing_env)); let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) + .exclude_inactive_in_otherwise() .iterate_to_fixpoint(tcx, body, Some("remove_uninit_drops")) .into_results_cursor(body); diff --git a/tests/mir-opt/otherwise_drops.result_ok.ElaborateDrops.diff b/tests/mir-opt/otherwise_drops.result_ok.ElaborateDrops.diff new file mode 100644 index 00000000000..9bd4db723d4 --- /dev/null +++ b/tests/mir-opt/otherwise_drops.result_ok.ElaborateDrops.diff @@ -0,0 +1,108 @@ +- // MIR for `result_ok` before ElaborateDrops ++ // MIR for `result_ok` after ElaborateDrops + + fn result_ok(_1: Result) -> Option { + debug result => _1; + let mut _0: std::option::Option; + let mut _2: isize; + let _3: std::string::String; + let mut _4: std::string::String; ++ let mut _5: bool; ++ let mut _6: isize; ++ let mut _7: isize; + scope 1 { + debug s => _3; + } + + bb0: { ++ _5 = const false; ++ _5 = const true; + PlaceMention(_1); + _2 = discriminant(_1); + switchInt(move _2) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + _0 = Option::::None; + goto -> bb5; + } + + bb2: { + StorageLive(_3); + _3 = move ((_1 as Ok).0: std::string::String); + StorageLive(_4); + _4 = move _3; + _0 = Option::::Some(move _4); +- drop(_4) -> [return: bb3, unwind: bb7]; ++ goto -> bb3; + } + + bb3: { + StorageDead(_4); +- drop(_3) -> [return: bb4, unwind: bb8]; ++ goto -> bb4; + } + + bb4: { + StorageDead(_3); + goto -> bb5; + } + + bb5: { +- drop(_1) -> [return: bb6, unwind: bb9]; ++ goto -> bb16; + } + + bb6: { + return; + } + + bb7 (cleanup): { +- drop(_3) -> [return: bb8, unwind terminate(cleanup)]; ++ goto -> bb8; + } + + bb8 (cleanup): { +- drop(_1) -> [return: bb9, unwind terminate(cleanup)]; ++ goto -> bb9; + } + + bb9 (cleanup): { + resume; ++ } ++ ++ bb10: { ++ goto -> bb6; ++ } ++ ++ bb11 (cleanup): { ++ goto -> bb9; ++ } ++ ++ bb12 (cleanup): { ++ goto -> bb9; ++ } ++ ++ bb13: { ++ goto -> bb10; ++ } ++ ++ bb14: { ++ goto -> bb10; ++ } ++ ++ bb15 (cleanup): { ++ goto -> bb9; ++ } ++ ++ bb16: { ++ _6 = discriminant(_1); ++ switchInt(move _6) -> [0: bb13, otherwise: bb14]; ++ } ++ ++ bb17 (cleanup): { ++ _7 = discriminant(_1); ++ switchInt(move _7) -> [0: bb11, otherwise: bb15]; + } + } + diff --git a/tests/mir-opt/otherwise_drops.rs b/tests/mir-opt/otherwise_drops.rs new file mode 100644 index 00000000000..c4bc05b593c --- /dev/null +++ b/tests/mir-opt/otherwise_drops.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -C panic=abort +//@ test-mir-pass: ElaborateDrops + +// Ensures there are no drops for the wildcard match arm. + +// EMIT_MIR otherwise_drops.result_ok.ElaborateDrops.diff +fn result_ok(result: Result) -> Option { + // CHECK-NOT: drop + match result { + Ok(s) => Some(s), + _ => None, + } +} diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff index e2d3c6c41b8..0d5fcf9ef14 100644 --- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff +++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff @@ -96,7 +96,6 @@ bb2: { StorageLive(_8); - _28 = const false; _8 = move ((_5 as Ok).0: std::boxed::Box); StorageLive(_9); StorageLive(_10); @@ -186,7 +185,7 @@ bb9: { StorageDead(_6); _29 = discriminant(_5); - switchInt(move _29) -> [0: bb11, otherwise: bb13]; + switchInt(move _29) -> [0: bb10, otherwise: bb11]; } bb10: { @@ -200,14 +199,6 @@ } bb11: { - switchInt(copy _28) -> [0: bb10, otherwise: bb12]; - } - - bb12: { - drop(((_5 as Ok).0: std::boxed::Box)) -> [return: bb10, unwind unreachable]; - } - - bb13: { drop(_5) -> [return: bb10, unwind unreachable]; } } -- cgit 1.4.1-3-g733a5 From ba4235bc855fedcf33b3b10496efaae55170ed4e Mon Sep 17 00:00:00 2001 From: Dillon Amburgey Date: Tue, 8 Jul 2025 18:45:36 -0500 Subject: fix: correct typo in attr_parsing_previously_accepted message key --- compiler/rustc_attr_parsing/messages.ftl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 8a709ea5d20..bec3a1e8a59 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -146,12 +146,12 @@ attr_parsing_unused_duplicate = unused attribute .suggestion = remove this attribute .note = attribute also specified here - .warn = {-passes_previously_accepted} + .warn = {-attr_parsing_previously_accepted} attr_parsing_unused_multiple = multiple `{$name}` attributes .suggestion = remove this attribute .note = attribute also specified here --attr_parsing_perviously_accepted = +-attr_parsing_previously_accepted = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -- cgit 1.4.1-3-g733a5 From 3c7bf9fe01aacd7205c755cb898c94de68fcd1fd Mon Sep 17 00:00:00 2001 From: Nico Lehmann Date: Tue, 8 Jul 2025 18:07:14 -0700 Subject: Expose nested bodies in rustc_borrowck::consumers --- compiler/rustc_borrowck/src/consumers.rs | 81 +++++++++++++++------- compiler/rustc_borrowck/src/lib.rs | 43 +++++------- compiler/rustc_borrowck/src/nll.rs | 6 +- compiler/rustc_borrowck/src/root_cx.rs | 13 +++- .../ui-fulldeps/auxiliary/obtain-borrowck-input.rs | 4 ++ tests/ui-fulldeps/obtain-borrowck.rs | 20 +++--- tests/ui-fulldeps/obtain-borrowck.run.stdout | 2 + 7 files changed, 107 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 1f087b09234..1c4ff5a6779 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -1,7 +1,9 @@ //! This file provides API for compiler consumers. +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; use rustc_index::IndexVec; +use rustc_middle::bug; use rustc_middle::mir::{Body, Promoted}; use rustc_middle::ty::TyCtxt; @@ -17,7 +19,39 @@ pub use super::polonius::legacy::{ pub use super::region_infer::RegionInferenceContext; use crate::{BorrowCheckRootCtxt, do_mir_borrowck}; -/// Options determining the output behavior of [`get_body_with_borrowck_facts`]. +/// Struct used during mir borrowck to collect bodies with facts for a typeck root and all +/// its nested bodies. +pub(crate) struct BorrowckConsumer<'tcx> { + options: ConsumerOptions, + bodies: FxHashMap>, +} + +impl<'tcx> BorrowckConsumer<'tcx> { + pub(crate) fn new(options: ConsumerOptions) -> Self { + Self { options, bodies: Default::default() } + } + + pub(crate) fn insert_body(&mut self, def_id: LocalDefId, body: BodyWithBorrowckFacts<'tcx>) { + if self.bodies.insert(def_id, body).is_some() { + bug!("unexpected previous body for {def_id:?}"); + } + } + + /// Should the Polonius input facts be computed? + pub(crate) fn polonius_input(&self) -> bool { + matches!( + self.options, + ConsumerOptions::PoloniusInputFacts | ConsumerOptions::PoloniusOutputFacts + ) + } + + /// Should we run Polonius and collect the output facts? + pub(crate) fn polonius_output(&self) -> bool { + matches!(self.options, ConsumerOptions::PoloniusOutputFacts) + } +} + +/// Options determining the output behavior of [`get_bodies_with_borrowck_facts`]. /// /// If executing under `-Z polonius` the choice here has no effect, and everything as if /// [`PoloniusOutputFacts`](ConsumerOptions::PoloniusOutputFacts) had been selected @@ -43,17 +77,6 @@ pub enum ConsumerOptions { PoloniusOutputFacts, } -impl ConsumerOptions { - /// Should the Polonius input facts be computed? - pub(crate) fn polonius_input(&self) -> bool { - matches!(self, Self::PoloniusInputFacts | Self::PoloniusOutputFacts) - } - /// Should we run Polonius and collect the output facts? - pub(crate) fn polonius_output(&self) -> bool { - matches!(self, Self::PoloniusOutputFacts) - } -} - /// A `Body` with information computed by the borrow checker. This struct is /// intended to be consumed by compiler consumers. /// @@ -82,25 +105,35 @@ pub struct BodyWithBorrowckFacts<'tcx> { pub output_facts: Option>, } -/// This function computes borrowck facts for the given body. The [`ConsumerOptions`] -/// determine which facts are returned. This function makes a copy of the body because -/// it needs to regenerate the region identifiers. It should never be invoked during a -/// typical compilation session due to the unnecessary overhead of returning -/// [`BodyWithBorrowckFacts`]. +/// This function computes borrowck facts for the given def id and all its nested bodies. +/// It must be called with a typeck root which will then borrowck all nested bodies as well. +/// The [`ConsumerOptions`] determine which facts are returned. This function makes a copy +/// of the bodies because it needs to regenerate the region identifiers. It should never be +/// invoked during a typical compilation session due to the unnecessary overhead of +/// returning [`BodyWithBorrowckFacts`]. /// /// Note: -/// * This function will panic if the required body was already stolen. This +/// * This function will panic if the required bodies were already stolen. This /// can, for example, happen when requesting a body of a `const` function /// because they are evaluated during typechecking. The panic can be avoided /// by overriding the `mir_borrowck` query. You can find a complete example -/// that shows how to do this at `tests/run-make/obtain-borrowck/`. +/// that shows how to do this at `tests/ui-fulldeps/obtain-borrowck.rs`. /// /// * Polonius is highly unstable, so expect regular changes in its signature or other details. -pub fn get_body_with_borrowck_facts( +pub fn get_bodies_with_borrowck_facts( tcx: TyCtxt<'_>, - def_id: LocalDefId, + root_def_id: LocalDefId, options: ConsumerOptions, -) -> BodyWithBorrowckFacts<'_> { - let mut root_cx = BorrowCheckRootCtxt::new(tcx, def_id); - *do_mir_borrowck(&mut root_cx, def_id, Some(options)).1.unwrap() +) -> FxHashMap> { + let mut root_cx = + BorrowCheckRootCtxt::new(tcx, root_def_id, Some(BorrowckConsumer::new(options))); + + // See comment in `rustc_borrowck::mir_borrowck` + let nested_bodies = tcx.nested_bodies_within(root_def_id); + for def_id in nested_bodies { + root_cx.get_or_insert_nested(def_id); + } + + do_mir_borrowck(&mut root_cx, root_def_id); + root_cx.consumer.unwrap().bodies } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 82b300dcb17..321b18c9b78 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -51,7 +51,7 @@ use smallvec::SmallVec; use tracing::{debug, instrument}; use crate::borrow_set::{BorrowData, BorrowSet}; -use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions}; +use crate::consumers::BodyWithBorrowckFacts; use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows}; use crate::diagnostics::{ AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName, @@ -124,7 +124,7 @@ fn mir_borrowck( let opaque_types = ConcreteOpaqueTypes(Default::default()); Ok(tcx.arena.alloc(opaque_types)) } else { - let mut root_cx = BorrowCheckRootCtxt::new(tcx, def); + let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None); // We need to manually borrowck all nested bodies from the HIR as // we do not generate MIR for dead code. Not doing so causes us to // never check closures in dead code. @@ -134,7 +134,7 @@ fn mir_borrowck( } let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } = - do_mir_borrowck(&mut root_cx, def, None).0; + do_mir_borrowck(&mut root_cx, def); debug_assert!(closure_requirements.is_none()); debug_assert!(used_mut_upvars.is_empty()); root_cx.finalize() @@ -289,17 +289,12 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { /// Perform the actual borrow checking. /// -/// Use `consumer_options: None` for the default behavior of returning -/// [`PropagatedBorrowCheckResults`] only. Otherwise, return [`BodyWithBorrowckFacts`] -/// according to the given [`ConsumerOptions`]. -/// /// For nested bodies this should only be called through `root_cx.get_or_insert_nested`. #[instrument(skip(root_cx), level = "debug")] fn do_mir_borrowck<'tcx>( root_cx: &mut BorrowCheckRootCtxt<'tcx>, def: LocalDefId, - consumer_options: Option, -) -> (PropagatedBorrowCheckResults<'tcx>, Option>>) { +) -> PropagatedBorrowCheckResults<'tcx> { let tcx = root_cx.tcx; let infcx = BorrowckInferCtxt::new(tcx, def); let (input_body, promoted) = tcx.mir_promoted(def); @@ -343,7 +338,6 @@ fn do_mir_borrowck<'tcx>( &location_table, &move_data, &borrow_set, - consumer_options, ); // Dump MIR results into a file, if that is enabled. This lets us @@ -483,23 +477,24 @@ fn do_mir_borrowck<'tcx>( used_mut_upvars: mbcx.used_mut_upvars, }; - let body_with_facts = if consumer_options.is_some() { - Some(Box::new(BodyWithBorrowckFacts { - body: body_owned, - promoted, - borrow_set, - region_inference_context: regioncx, - location_table: polonius_input.as_ref().map(|_| location_table), - input_facts: polonius_input, - output_facts: polonius_output, - })) - } else { - None - }; + if let Some(consumer) = &mut root_cx.consumer { + consumer.insert_body( + def, + BodyWithBorrowckFacts { + body: body_owned, + promoted, + borrow_set, + region_inference_context: regioncx, + location_table: polonius_input.as_ref().map(|_| location_table), + input_facts: polonius_input, + output_facts: polonius_output, + }, + ); + } debug!("do_mir_borrowck: result = {:#?}", result); - (result, body_with_facts) + result } fn get_flow_results<'a, 'tcx>( diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index af450507296..41f67e78930 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -18,7 +18,6 @@ use rustc_span::sym; use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; -use crate::consumers::ConsumerOptions; use crate::diagnostics::RegionErrors; use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints; use crate::polonius::PoloniusDiagnosticsContext; @@ -83,12 +82,11 @@ pub(crate) fn compute_regions<'tcx>( location_table: &PoloniusLocationTable, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, - consumer_options: Option, ) -> NllOutput<'tcx> { let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled(); - let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default() + let polonius_input = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_input()) || is_polonius_legacy_enabled; - let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default() + let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output()) || is_polonius_legacy_enabled; let mut polonius_facts = (polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default()); diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index 66b526fa02a..9b1d12aede5 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -6,6 +6,7 @@ use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::ErrorGuaranteed; use smallvec::SmallVec; +use crate::consumers::BorrowckConsumer; use crate::{ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults}; /// The shared context used by both the root as well as all its nested @@ -16,16 +17,24 @@ pub(super) struct BorrowCheckRootCtxt<'tcx> { concrete_opaque_types: ConcreteOpaqueTypes<'tcx>, nested_bodies: FxHashMap>, tainted_by_errors: Option, + /// This should be `None` during normal compilation. See [`crate::consumers`] for more + /// information on how this is used. + pub(crate) consumer: Option>, } impl<'tcx> BorrowCheckRootCtxt<'tcx> { - pub(super) fn new(tcx: TyCtxt<'tcx>, root_def_id: LocalDefId) -> BorrowCheckRootCtxt<'tcx> { + pub(super) fn new( + tcx: TyCtxt<'tcx>, + root_def_id: LocalDefId, + consumer: Option>, + ) -> BorrowCheckRootCtxt<'tcx> { BorrowCheckRootCtxt { tcx, root_def_id, concrete_opaque_types: Default::default(), nested_bodies: Default::default(), tainted_by_errors: None, + consumer, } } @@ -71,7 +80,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { self.root_def_id.to_def_id() ); if !self.nested_bodies.contains_key(&def_id) { - let result = super::do_mir_borrowck(self, def_id, None).0; + let result = super::do_mir_borrowck(self, def_id); if let Some(prev) = self.nested_bodies.insert(def_id, result) { bug!("unexpected previous nested body: {prev:?}"); } diff --git a/tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs b/tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs index 7213e06792a..9cfc901eabe 100644 --- a/tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs +++ b/tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs @@ -28,6 +28,10 @@ const fn foo() -> usize { 1 } +fn with_nested_body(opt: Option) -> Option { + opt.map(|x| x + 1) +} + fn main() { let bar: [Bar; foo()] = [Bar::new()]; assert_eq!(bar[0].provided(), foo()); diff --git a/tests/ui-fulldeps/obtain-borrowck.rs b/tests/ui-fulldeps/obtain-borrowck.rs index 84f6970c83a..08213fd7588 100644 --- a/tests/ui-fulldeps/obtain-borrowck.rs +++ b/tests/ui-fulldeps/obtain-borrowck.rs @@ -9,16 +9,17 @@ //! This program implements a rustc driver that retrieves MIR bodies with //! borrowck information. This cannot be done in a straightforward way because -//! `get_body_with_borrowck_facts`–the function for retrieving a MIR body with -//! borrowck facts–can panic if the body is stolen before it is invoked. +//! `get_bodies_with_borrowck_facts`–the function for retrieving MIR bodies with +//! borrowck facts–can panic if the bodies are stolen before it is invoked. //! Therefore, the driver overrides `mir_borrowck` query (this is done in the -//! `config` callback), which retrieves the body that is about to be borrow -//! checked and stores it in a thread local `MIR_BODIES`. Then, `after_analysis` +//! `config` callback), which retrieves the bodies that are about to be borrow +//! checked and stores them in a thread local `MIR_BODIES`. Then, `after_analysis` //! callback triggers borrow checking of all MIR bodies by retrieving //! `optimized_mir` and pulls out the MIR bodies with the borrowck information //! from the thread local storage. extern crate rustc_borrowck; +extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_hir; extern crate rustc_interface; @@ -30,6 +31,7 @@ use std::collections::HashMap; use std::thread_local; use rustc_borrowck::consumers::{self, BodyWithBorrowckFacts, ConsumerOptions}; +use rustc_data_structures::fx::FxHashMap; use rustc_driver::Compilation; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; @@ -129,13 +131,15 @@ thread_local! { fn mir_borrowck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ProvidedValue<'tcx> { let opts = ConsumerOptions::PoloniusInputFacts; - let body_with_facts = consumers::get_body_with_borrowck_facts(tcx, def_id, opts); + let bodies_with_facts = consumers::get_bodies_with_borrowck_facts(tcx, def_id, opts); // SAFETY: The reader casts the 'static lifetime to 'tcx before using it. - let body_with_facts: BodyWithBorrowckFacts<'static> = - unsafe { std::mem::transmute(body_with_facts) }; + let bodies_with_facts: FxHashMap> = + unsafe { std::mem::transmute(bodies_with_facts) }; MIR_BODIES.with(|state| { let mut map = state.borrow_mut(); - assert!(map.insert(def_id, body_with_facts).is_none()); + for (def_id, body_with_facts) in bodies_with_facts { + assert!(map.insert(def_id, body_with_facts).is_none()); + } }); let mut providers = Providers::default(); rustc_borrowck::provide(&mut providers); diff --git a/tests/ui-fulldeps/obtain-borrowck.run.stdout b/tests/ui-fulldeps/obtain-borrowck.run.stdout index e011622e6b2..09d3e50f42d 100644 --- a/tests/ui-fulldeps/obtain-borrowck.run.stdout +++ b/tests/ui-fulldeps/obtain-borrowck.run.stdout @@ -3,6 +3,8 @@ Bodies retrieved for: ::foo ::main ::main::{constant#0} +::with_nested_body +::with_nested_body::{closure#0} ::{impl#0}::new ::{impl#1}::provided ::{impl#1}::required -- cgit 1.4.1-3-g733a5 From 663733584561320e07623205f2c2dfcf43c375c1 Mon Sep 17 00:00:00 2001 From: B I Mohammed Abbas Date: Wed, 9 Jul 2025 09:54:08 +0530 Subject: Fix VxWorks build errors --- library/std/src/sys/fs/unix.rs | 1 - library/std/src/sys/pal/unix/thread.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index dc278274f00..b310db2dac4 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1491,7 +1491,6 @@ impl File { target_os = "redox", target_os = "espidf", target_os = "horizon", - target_os = "vxworks", target_os = "nuttx", )))] let to_timespec = |time: Option| match time { diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 53f0d1eeda5..e4f5520d8a3 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -222,7 +222,7 @@ impl Thread { #[cfg(target_os = "vxworks")] pub fn set_name(name: &CStr) { - let mut name = truncate_cstr::<{ libc::VX_TASK_RENAME_LENGTH - 1 }>(name); + let mut name = truncate_cstr::<{ (libc::VX_TASK_RENAME_LENGTH - 1) as usize }>(name); let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; debug_assert_eq!(res, libc::OK); } -- cgit 1.4.1-3-g733a5 From 15940b8beae3d6b9e06c239c81dd2d094f1ca9c0 Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Wed, 9 Jul 2025 07:40:01 +0100 Subject: Add a new maintainer to the wasm32-wasip1 target --- src/doc/rustc/src/platform-support/wasm32-wasip1.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md index 1e7447698bc..a8a9e550581 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md @@ -42,6 +42,7 @@ said since when this document was last updated those interested in maintaining this target are: [@alexcrichton](https://github.com/alexcrichton) +[@loganek](https://github.com/loganek) ## Requirements -- cgit 1.4.1-3-g733a5 From 43bc40f113e0e12e44b8aea3968ff6b9d7eb4582 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Tue, 8 Jul 2025 14:47:00 +0000 Subject: improve lint doc text --- compiler/rustc_lint/src/types.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index e41bc8f852e..fc9d795cb23 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -59,8 +59,7 @@ declare_lint! { } declare_lint! { - /// The `overflowing_literals` lint detects literal out of range for its - /// type. + /// The `overflowing_literals` lint detects literals out of range for their type. /// /// ### Example /// @@ -72,9 +71,9 @@ declare_lint! { /// /// ### Explanation /// - /// It is usually a mistake to use a literal that overflows the type where - /// it is used. Either use a literal that is within range, or change the - /// type to be within the range of the literal. + /// It is usually a mistake to use a literal that overflows its type + /// Change either the literal or its type such that the literal is + /// within the range of its type. OVERFLOWING_LITERALS, Deny, "literal out of range for its type" -- cgit 1.4.1-3-g733a5 From 8d58d5e800c0a52aef5194055c3e19e1bf345211 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 9 Jul 2025 20:02:36 +0800 Subject: Remove `run-make-support` CHANGELOG Hopelessly outdated, and this support library is purely internal. --- src/tools/run-make-support/CHANGELOG.md | 83 --------------------------------- 1 file changed, 83 deletions(-) delete mode 100644 src/tools/run-make-support/CHANGELOG.md diff --git a/src/tools/run-make-support/CHANGELOG.md b/src/tools/run-make-support/CHANGELOG.md deleted file mode 100644 index c1b7b618a92..00000000000 --- a/src/tools/run-make-support/CHANGELOG.md +++ /dev/null @@ -1,83 +0,0 @@ -# Changelog - -All notable changes to the `run_make_support` library should be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the support -library should adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) even if it's -not intended for public consumption (it's moreso to help internally, to help test writers track -changes to the support library). - -This support library will probably never reach 1.0. Please bump the minor version in `Cargo.toml` if -you make any breaking changes or other significant changes, or bump the patch version for bug fixes. - -## [0.2.0] - 2024-06-11 - -### Added - -- Added `fs_wrapper` module which provides panic-on-fail helpers for their respective `std::fs` - counterparts, the motivation is to: - - Reduce littering `.unwrap()` or `.expect()` everywhere for fs operations - - Help the test writer avoid forgetting to check fs results (even though enforced by - `-Dunused_must_use`) - - Provide better panic messages by default -- Added `path()` helper which creates a `Path` relative to `cwd()` (but is less noisy). - -### Changed - -- Marked many functions with `#[must_use]`, and rmake.rs are now compiled with `-Dunused_must_use`. - -## [0.1.0] - 2024-06-09 - -### Changed - -- Use *drop bombs* to enforce that commands are executed; a command invocation will panic if it is - constructed but never executed. Execution methods `Command::{run, run_fail}` will defuse the drop - bomb. -- Added `Command` helpers that forward to `std::process::Command` counterparts. - -### Removed - -- The `env_var` method which was incorrectly named and is `env_clear` underneath and is a footgun - from `impl_common_helpers`. For example, removing `TMPDIR` on Unix and `TMP`/`TEMP` breaks - `std::env::temp_dir` and wrecks anything using that, such as rustc's codgen. -- Removed `Deref`/`DerefMut` for `run_make_support::Command` -> `std::process::Command` because it - causes a method chain like `htmldocck().arg().run()` to fail, because `arg()` resolves to - `std::process::Command` which also returns a `&mut std::process::Command`, causing the `run()` to - be not found. - -## [0.0.0] - 2024-06-09 - -Consider this version to contain all changes made to the support library before we started to track -changes in this changelog. - -### Added - -- Custom command wrappers around `std::process::Command` (`run_make_support::Command`) and custom - wrapper around `std::process::Output` (`CompletedProcess`) to make it more convenient to work with - commands and their output, and help avoid forgetting to check for exit status. - - `Command`: `set_stdin`, `run`, `run_fail`. - - `CompletedProcess`: `std{err,out}_utf8`, `status`, `assert_std{err,out}_{equals, contains, - not_contains}`, `assert_exit_code`. -- `impl_common_helpers` macro to avoid repeating adding common convenience methods, including: - - Environment manipulation methods: `env`, `env_remove` - - Command argument providers: `arg`, `args` - - Common invocation inspection (of the command invocation up until `inspect` is called): - `inspect` - - Execution methods: `run` (for commands expected to succeed execution, exit status `0`) and - `run_fail` (for commands expected to fail execution, exit status non-zero). -- Command wrappers around: `rustc`, `clang`, `cc`, `rustc`, `rustdoc`, `llvm-readobj`. -- Thin helpers to construct `python` and `htmldocck` commands. -- `run` and `run_fail` (like `Command::{run, run_fail}`) for running binaries, which sets suitable - env vars (like `LD_LIB_PATH` or equivalent, `TARGET_RPATH_ENV`, `PATH` on Windows). -- Pseudo command `diff` which has similar functionality as the cli util but not the same API. -- Convenience panic-on-fail helpers `env_var`, `env_var_os`, `cwd` for their `std::env` conterparts. -- Convenience panic-on-fail helpers for reading respective env vars: `target`, `source_root`. -- Platform check helpers: `is_windows`, `is_msvc`, `cygpath_windows`, `uname`. -- fs helpers: `copy_dir_all`. -- `recursive_diff` helper. -- Generic `assert_not_contains` helper. -- Scoped run-with-teardown helper `run_in_tmpdir` which is designed to run commands in a temporary - directory that is cleared when closure returns. -- Helpers for constructing the name of binaries and libraries: `rust_lib_name`, `static_lib_name`, - `bin_name`, `dynamic_lib_name`. -- Re-export libraries: `gimli`, `object`, `regex`, `wasmparsmer`. -- cgit 1.4.1-3-g733a5 From ed520af279100728fd35df40cbf6353838cb7529 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 9 Jul 2025 20:05:13 +0800 Subject: Don't attempt to version `run-make-support` Purely internal test support library. --- Cargo.lock | 2 +- src/tools/run-make-support/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6d823c5b5a5..271a2f7962c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3214,7 +3214,7 @@ dependencies = [ [[package]] name = "run_make_support" -version = "0.2.0" +version = "0.0.0" dependencies = [ "bstr", "build_helper", diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 3226f467ba4..95aedf7d080 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "run_make_support" -version = "0.2.0" +version = "0.0.0" edition = "2021" [dependencies] -- cgit 1.4.1-3-g733a5 From e70493d06c1e4fbc56380efc8d179bd845b20061 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 9 Jul 2025 20:07:09 +0800 Subject: Bump `run-make-support` to Edition 2024 --- src/tools/run-make-support/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 95aedf7d080..d01224eb56d 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "run_make_support" version = "0.0.0" -edition = "2021" +edition = "2024" [dependencies] bstr = "1.12" -- cgit 1.4.1-3-g733a5 From fae15466aadd77e0369a893c7b962f7afbb86ff2 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 9 Jul 2025 20:10:32 +0800 Subject: Sort and document `run-make-support` dependencies --- src/tools/run-make-support/Cargo.toml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index d01224eb56d..a4e7534137d 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -4,15 +4,24 @@ version = "0.0.0" edition = "2024" [dependencies] + +# These dependencies are either used to implement part of support library +# functionality, or re-exported to test recipe programs via the support library, +# or both. + +# tidy-alphabetical-start bstr = "1.12" +gimli = "0.32" +libc = "0.2" object = "0.37" +regex = "1.11" +serde_json = "1.0" similar = "2.7" wasmparser = { version = "0.219", default-features = false, features = ["std"] } -regex = "1.11" -gimli = "0.32" +# tidy-alphabetical-end + +# Shared with bootstrap and compiletest build_helper = { path = "../../build_helper" } -serde_json = "1.0" -libc = "0.2" [lib] crate-type = ["lib", "dylib"] -- cgit 1.4.1-3-g733a5 From 200f132367f2aada7aff45689e53448a69cd7ce3 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 9 Jul 2025 20:22:58 +0800 Subject: Massage `lib.rs` so it can be rustfmt'd Even if the formatting isn't strictly "better", it at least allows rustfmtting it automatically. --- src/tools/run-make-support/src/lib.rs | 108 +++++++++++++--------------------- 1 file changed, 42 insertions(+), 66 deletions(-) diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 67d8c351a59..69054396229 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -3,9 +3,6 @@ //! notably is built via cargo: this means that if your test wants some non-trivial utility, such //! as `object` or `wasmparser`, they can be re-exported and be made available through this library. -// We want to control use declaration ordering and spacing (and preserve use group comments), so -// skip rustfmt on this file. -#![cfg_attr(rustfmt, rustfmt::skip)] #![warn(unreachable_pub)] mod command; @@ -22,8 +19,8 @@ pub mod path_helpers; pub mod run; pub mod scoped_run; pub mod string; -pub mod targets; pub mod symbols; +pub mod targets; // Internally we call our fs-related support module as `fs`, but re-export its content as `rfs` // to tests to avoid colliding with commonly used `use std::fs;`. @@ -36,77 +33,56 @@ pub mod rfs { } // Re-exports of third-party library crates. -// tidy-alphabetical-start -pub use bstr; -pub use gimli; -pub use libc; -pub use object; -pub use regex; -pub use serde_json; -pub use similar; -pub use wasmparser; -// tidy-alphabetical-end +pub use {bstr, gimli, libc, object, regex, serde_json, similar, wasmparser}; -// Re-exports of external dependencies. -pub use external_deps::{ - cargo, c_build, c_cxx_compiler, clang, htmldocck, llvm, python, rustc, rustdoc +// Helpers for building names of output artifacts that are potentially target-specific. +pub use crate::artifact_names::{ + bin_name, dynamic_lib_extension, dynamic_lib_name, msvc_import_dynamic_lib_name, rust_lib_name, + static_lib_name, }; - -// These rely on external dependencies. -pub use c_cxx_compiler::{Cc, Gcc, cc, cxx, extra_c_flags, extra_cxx_flags, gcc}; -pub use c_build::{ +pub use crate::assertion_helpers::{ + assert_contains, assert_contains_regex, assert_count_is, assert_dirs_are_equal, assert_equals, + assert_not_contains, assert_not_contains_regex, +}; +// `diff` is implemented in terms of the [similar] library. +// +// [similar]: https://github.com/mitsuhiko/similar +pub use crate::diff::{Diff, diff}; +// Panic-on-fail [`std::env::var`] and [`std::env::var_os`] wrappers. +pub use crate::env::{env_var, env_var_os, set_current_dir}; +pub use crate::external_deps::c_build::{ build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_cxx, build_native_static_lib_optimized, }; -pub use cargo::cargo; -pub use clang::{clang, Clang}; -pub use htmldocck::htmldocck; -pub use llvm::{ - llvm_ar, llvm_bcanalyzer, llvm_dis, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objcopy, - llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr, LlvmBcanalyzer, LlvmDis, LlvmDwarfdump, - LlvmFilecheck, LlvmNm, LlvmObjcopy, LlvmObjdump, LlvmProfdata, LlvmReadobj, -}; -pub use python::python_command; -pub use rustc::{bare_rustc, rustc, rustc_path, Rustc}; -pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc}; - -/// [`diff`][mod@diff] is implemented in terms of the [similar] library. -/// -/// [similar]: https://github.com/mitsuhiko/similar -pub use diff::{diff, Diff}; - -/// Panic-on-fail [`std::env::var`] and [`std::env::var_os`] wrappers. -pub use env::{env_var, env_var_os, set_current_dir}; - -/// Convenience helpers for running binaries and other commands. -pub use run::{cmd, run, run_fail, run_with_args}; - -/// Helpers for checking target information. -pub use targets::{ - apple_os, is_aix, is_darwin, is_msvc, is_windows, is_windows_gnu, is_windows_msvc, is_win7, llvm_components_contain, - target, uname, +// Re-exports of external dependencies. +pub use crate::external_deps::c_cxx_compiler::{ + Cc, Gcc, cc, cxx, extra_c_flags, extra_cxx_flags, gcc, }; - -/// Helpers for building names of output artifacts that are potentially target-specific. -pub use artifact_names::{ - bin_name, dynamic_lib_extension, dynamic_lib_name, msvc_import_dynamic_lib_name, rust_lib_name, - static_lib_name, +pub use crate::external_deps::cargo::cargo; +pub use crate::external_deps::clang::{Clang, clang}; +pub use crate::external_deps::htmldocck::htmldocck; +pub use crate::external_deps::llvm::{ + self, LlvmAr, LlvmBcanalyzer, LlvmDis, LlvmDwarfdump, LlvmFilecheck, LlvmNm, LlvmObjcopy, + LlvmObjdump, LlvmProfdata, LlvmReadobj, llvm_ar, llvm_bcanalyzer, llvm_dis, llvm_dwarfdump, + llvm_filecheck, llvm_nm, llvm_objcopy, llvm_objdump, llvm_profdata, llvm_readobj, }; - -/// Path-related helpers. -pub use path_helpers::{ +pub use crate::external_deps::python::python_command; +pub use crate::external_deps::rustc::{self, Rustc, bare_rustc, rustc, rustc_path}; +pub use crate::external_deps::rustdoc::{Rustdoc, bare_rustdoc, rustdoc}; +// Path-related helpers. +pub use crate::path_helpers::{ build_root, cwd, filename_contains, filename_not_in_denylist, has_extension, has_prefix, has_suffix, not_contains, path, shallow_find_directories, shallow_find_files, source_root, }; - -/// Helpers for scoped test execution where certain properties are attempted to be maintained. -pub use scoped_run::{run_in_tmpdir, test_while_readonly}; - -pub use assertion_helpers::{ - assert_contains, assert_contains_regex, assert_count_is, assert_dirs_are_equal, assert_equals, - assert_not_contains, assert_not_contains_regex, -}; - -pub use string::{ +// Convenience helpers for running binaries and other commands. +pub use crate::run::{cmd, run, run_fail, run_with_args}; +// Helpers for scoped test execution where certain properties are attempted to be maintained. +pub use crate::scoped_run::{run_in_tmpdir, test_while_readonly}; +pub use crate::string::{ count_regex_matches_in_files_with_extension, invalid_utf8_contains, invalid_utf8_not_contains, }; +// Helpers for checking target information. +pub use crate::targets::{ + apple_os, is_aix, is_darwin, is_msvc, is_win7, is_windows, is_windows_gnu, is_windows_msvc, + llvm_components_contain, target, uname, +}; -- cgit 1.4.1-3-g733a5 From 5339629b11901c2c1fbcb54fdb49afd4ed0b98ba Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 9 Jul 2025 18:30:32 +0200 Subject: Auto-add `S-waiting-on-author` when the PR is/switches to draft state --- triagebot.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 4e3dff219f1..006ed96cce9 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -535,6 +535,9 @@ trigger_files = [ [autolabel."S-waiting-on-review"] new_pr = true +[autolabel."S-waiting-on-author"] +new_draft = true + [autolabel."needs-triage"] new_issue = true exclude_labels = [ -- cgit 1.4.1-3-g733a5 From 486ffda9dcd0d4ef0a09d81e6ce5f241e77526a1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 12 Mar 2025 10:26:37 +0000 Subject: Add opaque TypeId handles for CTFE --- compiler/rustc_codegen_cranelift/src/constant.rs | 13 ++++ compiler/rustc_codegen_gcc/src/common.rs | 10 ++- compiler/rustc_codegen_llvm/src/common.rs | 18 +++-- compiler/rustc_codegen_llvm/src/consts.rs | 5 +- compiler/rustc_codegen_ssa/src/mir/operand.rs | 2 +- compiler/rustc_const_eval/messages.ftl | 2 + compiler/rustc_const_eval/src/errors.rs | 6 +- .../rustc_const_eval/src/interpret/intrinsics.rs | 79 +++++++++++++++++++- compiler/rustc_const_eval/src/interpret/memory.rs | 30 +++++++- .../rustc_const_eval/src/interpret/projection.rs | 6 +- .../rustc_const_eval/src/interpret/validity.rs | 64 +++++++++-------- compiler/rustc_hir/src/lang_items.rs | 2 + compiler/rustc_hir_analysis/src/check/intrinsic.rs | 9 ++- compiler/rustc_middle/src/mir/interpret/error.rs | 2 + compiler/rustc_middle/src/mir/interpret/mod.rs | 33 +++++++-- compiler/rustc_middle/src/mir/pretty.rs | 1 + compiler/rustc_middle/src/ty/print/pretty.rs | 1 + compiler/rustc_monomorphize/src/collector.rs | 1 + compiler/rustc_passes/src/reachable.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/stable_mir/src/mir/alloc.rs | 3 + .../stable_mir/src/unstable/convert/stable/mir.rs | 3 + library/core/Cargo.toml | 2 - library/core/src/any.rs | 84 +++++++++++++++------- library/core/src/intrinsics/mod.rs | 15 +++- library/coretests/tests/any.rs | 8 --- library/std/Cargo.toml | 2 - library/sysroot/Cargo.toml | 1 - tests/codegen/error-provide.rs | 6 +- tests/ui/const-generics/issues/issue-90318.rs | 3 +- tests/ui/const-generics/issues/issue-90318.stderr | 25 +------ tests/ui/consts/const_cmp_type_id.rs | 3 +- tests/ui/consts/const_cmp_type_id.stderr | 20 +++--- tests/ui/consts/const_transmute_type_id.rs | 11 +++ tests/ui/consts/const_transmute_type_id.stderr | 12 ++++ tests/ui/consts/const_transmute_type_id2.rs | 14 ++++ tests/ui/consts/const_transmute_type_id2.stderr | 15 ++++ tests/ui/consts/const_transmute_type_id3.rs | 16 +++++ tests/ui/consts/const_transmute_type_id3.stderr | 15 ++++ tests/ui/consts/const_transmute_type_id4.rs | 16 +++++ tests/ui/consts/const_transmute_type_id4.stderr | 15 ++++ tests/ui/consts/issue-73976-monomorphic.rs | 2 +- tests/ui/consts/issue-73976-monomorphic.stderr | 9 --- 43 files changed, 438 insertions(+), 148 deletions(-) create mode 100644 tests/ui/consts/const_transmute_type_id.rs create mode 100644 tests/ui/consts/const_transmute_type_id.stderr create mode 100644 tests/ui/consts/const_transmute_type_id2.rs create mode 100644 tests/ui/consts/const_transmute_type_id2.stderr create mode 100644 tests/ui/consts/const_transmute_type_id3.rs create mode 100644 tests/ui/consts/const_transmute_type_id3.stderr create mode 100644 tests/ui/consts/const_transmute_type_id4.rs create mode 100644 tests/ui/consts/const_transmute_type_id4.stderr delete mode 100644 tests/ui/consts/issue-73976-monomorphic.stderr diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index ed06423b260..85adf0f3716 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -175,6 +175,13 @@ pub(crate) fn codegen_const_value<'tcx>( fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } + GlobalAlloc::TypeId { .. } => { + return CValue::const_val( + fx, + layout, + ScalarInt::try_from_target_usize(offset.bytes(), fx.tcx).unwrap(), + ); + } GlobalAlloc::Static(def_id) => { assert!(fx.tcx.is_static(def_id)); let data_id = data_id_for_static( @@ -360,6 +367,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant GlobalAlloc::Memory(alloc) => alloc, GlobalAlloc::Function { .. } | GlobalAlloc::Static(_) + | GlobalAlloc::TypeId { .. } | GlobalAlloc::VTable(..) => { unreachable!() } @@ -471,6 +479,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant .principal() .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), ), + GlobalAlloc::TypeId { .. } => { + // Nothing to do, the bytes/offset of this pointer have already been written together with all other bytes, + // so we just need to drop this provenance. + continue; + } GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 32713eb56c6..28848ca6184 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -1,7 +1,6 @@ use gccjit::{LValue, RValue, ToRValue, Type}; -use rustc_abi as abi; -use rustc_abi::HasDataLayout; use rustc_abi::Primitive::Pointer; +use rustc_abi::{self as abi, HasDataLayout}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, }; @@ -282,6 +281,13 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { let init = self.const_data_from_alloc(alloc); self.static_addr_of(init, alloc.inner().align, None) } + GlobalAlloc::TypeId { .. } => { + let val = self.const_usize(offset.bytes()); + // This is still a variable of pointer type, even though we only use the provenance + // of that pointer in CTFE and Miri. But to make LLVM's type system happy, + // we need an int-to-ptr cast here (it doesn't matter at all which provenance that picks). + return self.context.new_cast(None, val, ty); + } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); self.get_static(def_id).get_address(None) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 92f38565eef..b9b5c776d86 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -3,9 +3,8 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; -use rustc_abi as abi; -use rustc_abi::HasDataLayout; use rustc_abi::Primitive::Pointer; +use rustc_abi::{self as abi, HasDataLayout as _}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; @@ -284,7 +283,8 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { self.const_bitcast(llval, llty) }; } else { - let init = const_alloc_to_llvm(self, alloc, /*static*/ false); + let init = + const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); let alloc = alloc.inner(); let value = match alloc.mutability { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), @@ -316,15 +316,19 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { }), ))) .unwrap_memory(); - let init = const_alloc_to_llvm(self, alloc, /*static*/ false); - let value = self.static_addr_of_impl(init, alloc.inner().align, None); - value + let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + self.static_addr_of_impl(init, alloc.inner().align, None) } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); assert!(!self.tcx.is_thread_local_static(def_id)); self.get_static(def_id) } + GlobalAlloc::TypeId { .. } => { + // Drop the provenance, the offset contains the bytes of the hash + let llval = self.const_usize(offset.bytes()); + return unsafe { llvm::LLVMConstIntToPtr(llval, llty) }; + } }; let base_addr_space = global_alloc.address_space(self); let llval = unsafe { @@ -346,7 +350,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { } fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value { - const_alloc_to_llvm(self, alloc, /*static*/ false) + const_alloc_to_llvm(self, alloc.inner(), /*static*/ false) } fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 28f5282c6b0..21524fd2eb8 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -27,10 +27,9 @@ use crate::{base, debuginfo}; pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, - alloc: ConstAllocation<'_>, + alloc: &Allocation, is_static: bool, ) -> &'ll Value { - let alloc = alloc.inner(); // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be // producing empty LLVM allocations as they're just adding noise to binaries and forcing less @@ -141,7 +140,7 @@ fn codegen_static_initializer<'ll, 'tcx>( def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { let alloc = cx.tcx.eval_static_initializer(def_id)?; - Ok((const_alloc_to_llvm(cx, alloc, /*static*/ true), alloc)) + Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc)) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 2896dfd5463..a639803ea60 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -186,7 +186,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { offset: Size, ) -> Self { let alloc_align = alloc.inner().align; - assert!(alloc_align >= layout.align.abi); + assert!(alloc_align >= layout.align.abi, "{alloc_align:?} < {:?}", layout.align.abi); let read_scalar = |start, size, s: abi::Scalar, ty| { match alloc.0.read_scalar( diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 22a1894ee72..b767ca9a3c2 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -78,6 +78,8 @@ const_eval_dealloc_kind_mismatch = const_eval_deref_function_pointer = accessing {$allocation} which contains a function +const_eval_deref_typeid_pointer = + accessing {$allocation} which contains a `TypeId` const_eval_deref_vtable_pointer = accessing {$allocation} which contains a vtable const_eval_division_by_zero = diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 9133a5fc8ef..49cd7138748 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -475,6 +475,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { WriteToReadOnly(_) => const_eval_write_to_read_only, DerefFunctionPointer(_) => const_eval_deref_function_pointer, DerefVTablePointer(_) => const_eval_deref_vtable_pointer, + DerefTypeIdPointer(_) => const_eval_deref_typeid_pointer, InvalidBool(_) => const_eval_invalid_bool, InvalidChar(_) => const_eval_invalid_char, InvalidTag(_) => const_eval_invalid_tag, @@ -588,7 +589,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { diag.arg("has", has.bytes()); diag.arg("msg", format!("{msg:?}")); } - WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => { + WriteToReadOnly(alloc) + | DerefFunctionPointer(alloc) + | DerefVTablePointer(alloc) + | DerefTypeIdPointer(alloc) => { diag.arg("allocation", alloc); } InvalidBool(b) => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 378ed6d0e10..ee5382af0b2 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -4,8 +4,10 @@ use std::assert_matches::assert_matches; -use rustc_abi::Size; +use rustc_abi::{FieldIdx, Size}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_ast::Mutability; +use rustc_middle::mir::interpret::{AllocId, AllocInit, alloc_range}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; @@ -29,6 +31,37 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll tcx.mk_const_alloc(alloc) } +pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId { + let size = Size::from_bytes(16); + let align = tcx.data_layout.pointer_align(); + let mut alloc = Allocation::new(size, *align, AllocInit::Uninit, ()); + let ptr_size = tcx.data_layout.pointer_size(); + let type_id_hash = tcx.type_id_hash(ty).as_u128(); + alloc + .write_scalar( + &tcx, + alloc_range(Size::ZERO, Size::from_bytes(16)), + Scalar::from_u128(type_id_hash), + ) + .unwrap(); + + // Give the first pointer-size bytes provenance that knows about the type id + + let alloc_id = tcx.reserve_and_set_type_id_alloc(ty); + let offset = alloc + .read_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), false) + .unwrap() + .to_target_usize(&tcx) + .unwrap(); + let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset)); + let val = Scalar::from_pointer(ptr, &tcx); + alloc.write_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), val).unwrap(); + + alloc.mutability = Mutability::Not; + + tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc)) +} + impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns `true` if emulation happened. /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own @@ -63,10 +96,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::type_id => { let tp_ty = instance.args.type_at(0); ensure_monomorphic_enough(tcx, tp_ty)?; - let val = ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()); + let alloc_id = alloc_type_id(tcx, tp_ty); + let val = ConstValue::Indirect { alloc_id, offset: Size::ZERO }; let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; self.copy_op(&val, dest)?; } + sym::type_id_eq => { + // Both operands are `TypeId`, which is a newtype around an array of pointers. + // Project until we have the array elements. + let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?; + let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?; + + let mut a_fields = self.project_array_fields(&a_fields)?; + let mut b_fields = self.project_array_fields(&b_fields)?; + + let (_idx, a) = a_fields + .next(self)? + .expect("we know the layout of TypeId has at least 2 array elements"); + let a = self.deref_pointer(&a)?; + let (a, offset_a) = self.get_ptr_type_id(a.ptr())?; + + let (_idx, b) = b_fields + .next(self)? + .expect("we know the layout of TypeId has at least 2 array elements"); + let b = self.deref_pointer(&b)?; + let (b, offset_b) = self.get_ptr_type_id(b.ptr())?; + + let provenance_matches = a == b; + + let mut eq_id = offset_a == offset_b; + + while let Some((_, a)) = a_fields.next(self)? { + let (_, b) = b_fields.next(self)?.unwrap(); + + let a = self.read_target_usize(&a)?; + let b = self.read_target_usize(&b)?; + eq_id &= a == b; + } + + if !eq_id && provenance_matches { + throw_ub_format!( + "type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents" + ) + } + + self.write_scalar(Scalar::from_bool(provenance_matches), dest)?; + } sym::variant_count => { let tp_ty = instance.args.type_at(0); let ty = match tp_ty.kind() { diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 524023b8104..bb301600135 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -15,9 +15,9 @@ use std::{fmt, ptr}; use rustc_abi::{Align, HasDataLayout, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_middle::bug; use rustc_middle::mir::display_allocation; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::{bug, throw_ub_format}; use tracing::{debug, instrument, trace}; use super::{ @@ -346,6 +346,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind = "vtable", ) } + Some(GlobalAlloc::TypeId { .. }) => { + err_ub_custom!( + fluent::const_eval_invalid_dealloc, + alloc_id = alloc_id, + kind = "typeid", + ) + } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { err_ub_custom!( fluent::const_eval_invalid_dealloc, @@ -615,6 +622,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)), Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)), + Some(GlobalAlloc::TypeId { .. }) => throw_ub!(DerefTypeIdPointer(id)), None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); @@ -896,7 +904,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env); let mutbl = global_alloc.mutability(*self.tcx, self.typing_env); let kind = match global_alloc { - GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData, + GlobalAlloc::TypeId { .. } + | GlobalAlloc::Static { .. } + | GlobalAlloc::Memory { .. } => AllocKind::LiveData, GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"), GlobalAlloc::VTable { .. } => AllocKind::VTable, }; @@ -936,6 +946,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + /// Takes a pointer that is the first chunk of a `TypeId` and return the type that its + /// provenance refers to, as well as the segment of the hash that this pointer covers. + pub fn get_ptr_type_id( + &self, + ptr: Pointer>, + ) -> InterpResult<'tcx, (Ty<'tcx>, Size)> { + let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?; + let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else { + throw_ub_format!("type_id_eq: `TypeId` provenance is not a type id") + }; + interp_ok((ty, offset)) + } + pub fn get_ptr_fn( &self, ptr: Pointer>, @@ -1197,6 +1220,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> { Some(GlobalAlloc::VTable(ty, dyn_ty)) => { write!(fmt, " (vtable: impl {dyn_ty} for {ty})")?; } + Some(GlobalAlloc::TypeId { ty }) => { + write!(fmt, " (typeid for {ty})")?; + } Some(GlobalAlloc::Static(did)) => { write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?; } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 306697d4ec9..f72c4418081 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -296,7 +296,11 @@ where base: &'a P, ) -> InterpResult<'tcx, ArrayIterator<'a, 'tcx, M::Provenance, P>> { let abi::FieldsShape::Array { stride, .. } = base.layout().fields else { - span_bug!(self.cur_span(), "project_array_fields: expected an array layout"); + span_bug!( + self.cur_span(), + "project_array_fields: expected an array layout, got {:#?}", + base.layout() + ); }; let len = base.len(self)?; let field_layout = base.layout().field(self, 0); diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index fc4d13af8c4..fc44490c96d 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -571,40 +571,42 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { let alloc_actual_mutbl = global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env); - if let GlobalAlloc::Static(did) = global_alloc { - let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { - bug!() - }; - // Special handling for pointers to statics (irrespective of their type). - assert!(!self.ecx.tcx.is_thread_local_static(did)); - assert!(self.ecx.tcx.is_static(did)); - // Mode-specific checks - match ctfe_mode { - CtfeValidationMode::Static { .. } - | CtfeValidationMode::Promoted { .. } => { - // We skip recursively checking other statics. These statics must be sound by - // themselves, and the only way to get broken statics here is by using - // unsafe code. - // The reasons we don't check other statics is twofold. For one, in all - // sound cases, the static was already validated on its own, and second, we - // trigger cycle errors if we try to compute the value of the other static - // and that static refers back to us (potentially through a promoted). - // This could miss some UB, but that's fine. - // We still walk nested allocations, as they are fundamentally part of this validation run. - // This means we will also recurse into nested statics of *other* - // statics, even though we do not recurse into other statics directly. - // That's somewhat inconsistent but harmless. - skip_recursive_check = !nested; - } - CtfeValidationMode::Const { .. } => { - // If this is mutable memory or an `extern static`, there's no point in checking it -- we'd - // just get errors trying to read the value. - if alloc_actual_mutbl.is_mut() || self.ecx.tcx.is_foreign_item(did) - { - skip_recursive_check = true; + match global_alloc { + GlobalAlloc::Static(did) => { + let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { + bug!() + }; + assert!(!self.ecx.tcx.is_thread_local_static(did)); + assert!(self.ecx.tcx.is_static(did)); + match ctfe_mode { + CtfeValidationMode::Static { .. } + | CtfeValidationMode::Promoted { .. } => { + // We skip recursively checking other statics. These statics must be sound by + // themselves, and the only way to get broken statics here is by using + // unsafe code. + // The reasons we don't check other statics is twofold. For one, in all + // sound cases, the static was already validated on its own, and second, we + // trigger cycle errors if we try to compute the value of the other static + // and that static refers back to us (potentially through a promoted). + // This could miss some UB, but that's fine. + // We still walk nested allocations, as they are fundamentally part of this validation run. + // This means we will also recurse into nested statics of *other* + // statics, even though we do not recurse into other statics directly. + // That's somewhat inconsistent but harmless. + skip_recursive_check = !nested; + } + CtfeValidationMode::Const { .. } => { + // If this is mutable memory or an `extern static`, there's no point in checking it -- we'd + // just get errors trying to read the value. + if alloc_actual_mutbl.is_mut() + || self.ecx.tcx.is_foreign_item(did) + { + skip_recursive_check = true; + } } } } + _ => (), } // If this allocation has size zero, there is no actual mutability here. diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c11db63ba11..6347f1bfa71 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -274,6 +274,8 @@ language_item_table! { PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; + TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None; + // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays. // diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cebf7d1b532..e3532ade32f 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -93,6 +93,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::three_way_compare | sym::discriminant_value | sym::type_id + | sym::type_id_eq | sym::select_unpredictable | sym::cold_path | sym::ptr_guaranteed_cmp @@ -220,7 +221,13 @@ pub(crate) fn check_intrinsic_type( sym::needs_drop => (1, 0, vec![], tcx.types.bool), sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), - sym::type_id => (1, 0, vec![], tcx.types.u128), + sym::type_id => { + (1, 0, vec![], tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity()) + } + sym::type_id_eq => { + let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity(); + (0, 0, vec![type_id, type_id], tcx.types.bool) + } sym::offset => (2, 0, vec![param(0), param(1)], param(0)), sym::arith_offset => ( 1, diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 8acb8fa9f80..71e0c943fbb 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -392,6 +392,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { DerefFunctionPointer(AllocId), /// Trying to access the data behind a vtable pointer. DerefVTablePointer(AllocId), + /// Trying to access the actual type id. + DerefTypeIdPointer(AllocId), /// Using a non-boolean `u8` as bool. InvalidBool(u8), /// Using a non-character `u32` as character. diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 0b2645013ba..bed99a4ff2a 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -103,6 +103,7 @@ enum AllocDiscriminant { Fn, VTable, Static, + Type, } pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( @@ -127,6 +128,11 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( ty.encode(encoder); poly_trait_ref.encode(encoder); } + GlobalAlloc::TypeId { ty } => { + trace!("encoding {alloc_id:?} with {ty:#?}"); + AllocDiscriminant::Type.encode(encoder); + ty.encode(encoder); + } GlobalAlloc::Static(did) => { assert!(!tcx.is_thread_local_static(did)); // References to statics doesn't need to know about their allocations, @@ -228,6 +234,12 @@ impl<'s> AllocDecodingSession<'s> { trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}"); decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref, CTFE_ALLOC_SALT) } + AllocDiscriminant::Type => { + trace!("creating typeid alloc ID"); + let ty = Decodable::decode(decoder); + trace!("decoded typid: {ty:?}"); + decoder.interner().reserve_and_set_type_id_alloc(ty) + } AllocDiscriminant::Static => { trace!("creating extern static alloc ID"); let did = >::decode(decoder); @@ -258,6 +270,9 @@ pub enum GlobalAlloc<'tcx> { Static(DefId), /// The alloc ID points to memory. Memory(ConstAllocation<'tcx>), + /// The first pointer-sized segment of a type id. On 64 bit systems, the 128 bit type id + /// is split into two segments, on 32 bit systems there are 4 segments, and so on. + TypeId { ty: Ty<'tcx> }, } impl<'tcx> GlobalAlloc<'tcx> { @@ -296,9 +311,10 @@ impl<'tcx> GlobalAlloc<'tcx> { pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace { match self { GlobalAlloc::Function { .. } => cx.data_layout().instruction_address_space, - GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => { - AddressSpace::ZERO - } + GlobalAlloc::TypeId { .. } + | GlobalAlloc::Static(..) + | GlobalAlloc::Memory(..) + | GlobalAlloc::VTable(..) => AddressSpace::ZERO, } } @@ -334,7 +350,7 @@ impl<'tcx> GlobalAlloc<'tcx> { } } GlobalAlloc::Memory(alloc) => alloc.inner().mutability, - GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => { + GlobalAlloc::TypeId { .. } | GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => { // These are immutable. Mutability::Not } @@ -380,8 +396,10 @@ impl<'tcx> GlobalAlloc<'tcx> { GlobalAlloc::Function { .. } => (Size::ZERO, Align::ONE), GlobalAlloc::VTable(..) => { // No data to be accessed here. But vtables are pointer-aligned. - return (Size::ZERO, tcx.data_layout.pointer_align().abi); + (Size::ZERO, tcx.data_layout.pointer_align().abi) } + // Fake allocation, there's nothing to access here + GlobalAlloc::TypeId { .. } => (Size::ZERO, Align::ONE), } } } @@ -487,6 +505,11 @@ impl<'tcx> TyCtxt<'tcx> { self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, dyn_ty), salt) } + /// Generates an [AllocId] for a [core::any::TypeId]. Will get deduplicated. + pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::TypeId { ty }, 0) + } + /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical /// `Allocation` with a different `AllocId`. /// Statics with identical content will still point to the same `Allocation`, i.e., diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e9f3fb6ac8d..8e403dfddae 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1621,6 +1621,7 @@ pub fn write_allocations<'tcx>( Some(GlobalAlloc::VTable(ty, dyn_ty)) => { write!(w, " (vtable: impl {dyn_ty} for {ty})")? } + Some(GlobalAlloc::TypeId { ty }) => write!(w, " (typeid for {ty})")?, Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { write!(w, " (static: {}", tcx.def_path_str(did))?; if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 4e078847815..7c48a4b0885 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1773,6 +1773,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } Some(GlobalAlloc::Function { .. }) => p!(""), Some(GlobalAlloc::VTable(..)) => p!(""), + Some(GlobalAlloc::TypeId { .. }) => p!(""), None => p!(""), } return Ok(()); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 131064f9832..91c8e64ce9a 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1219,6 +1219,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt )); collect_alloc(tcx, alloc_id, output) } + GlobalAlloc::TypeId { .. } => {} } } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 7e15267a953..b49e8118fe3 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -325,6 +325,7 @@ impl<'tcx> ReachableContext<'tcx> { self.visit(args); } } + GlobalAlloc::TypeId { ty, .. } => self.visit(ty), GlobalAlloc::Memory(alloc) => self.propagate_from_alloc(alloc), } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 09f01d8704e..4df91cc3429 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2194,6 +2194,7 @@ symbols! { type_changing_struct_update, type_const, type_id, + type_id_eq, type_ir, type_ir_infer_ctxt_like, type_ir_inherent, diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/stable_mir/src/mir/alloc.rs index 0d45e59885c..9a94551f3ec 100644 --- a/compiler/stable_mir/src/mir/alloc.rs +++ b/compiler/stable_mir/src/mir/alloc.rs @@ -23,6 +23,9 @@ pub enum GlobalAlloc { Static(StaticDef), /// The alloc ID points to memory. Memory(Allocation), + /// The first pointer-sized segment of a type id. On 64 bit systems, the 128 bit type id + /// is split into two segments, on 32 bit systems there are 4 segments, and so on. + TypeId { ty: Ty }, } impl From for GlobalAlloc { diff --git a/compiler/stable_mir/src/unstable/convert/stable/mir.rs b/compiler/stable_mir/src/unstable/convert/stable/mir.rs index f6f4706e40b..ad39fc37600 100644 --- a/compiler/stable_mir/src/unstable/convert/stable/mir.rs +++ b/compiler/stable_mir/src/unstable/convert/stable/mir.rs @@ -864,6 +864,9 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> { mir::interpret::GlobalAlloc::Memory(alloc) => { GlobalAlloc::Memory(alloc.stable(tables, cx)) } + mir::interpret::GlobalAlloc::TypeId { ty } => { + GlobalAlloc::TypeId { ty: ty.stable(tables, cx) } + } } } } diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index f88661ee001..3e34e03a61e 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -23,8 +23,6 @@ optimize_for_size = [] # Make `RefCell` store additional debugging information, which is printed out when # a borrow error occurs debug_refcell = [] -# Make `TypeId` store a reference to the name of the type, so that it can print that name. -debug_typeid = [] [lints.rust.unexpected_cfgs] level = "warn" diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 01dce114592..39cdf6efda0 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -707,19 +707,52 @@ impl dyn Any + Send + Sync { /// ``` #[derive(Clone, Copy, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] +#[lang = "type_id"] pub struct TypeId { - // We avoid using `u128` because that imposes higher alignment requirements on many platforms. - // See issue #115620 for more information. - t: (u64, u64), - #[cfg(feature = "debug_typeid")] - name: &'static str, + /// This needs to be an array of pointers, since there is provenance + /// in the first array field. This provenance knows exactly which type + /// the TypeId actually is, allowing CTFE and miri to operate based off it. + /// At runtime all the pointers in the array contain bits of the hash, making + /// the entire `TypeId` actually just be a `u128` hash of the type. + pub(crate) data: [*const (); 16 / size_of::<*const ()>()], } +// SAFETY: the raw pointer is always an integer #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for TypeId { +unsafe impl Send for TypeId {} +// SAFETY: the raw pointer is always an integer +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for TypeId {} + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_type_id", issue = "77125")] +impl const PartialEq for TypeId { #[inline] fn eq(&self, other: &Self) -> bool { - self.t == other.t + #[cfg(miri)] + return crate::intrinsics::type_id_eq(*self, *other); + #[cfg(not(miri))] + { + let this = self; + crate::intrinsics::const_eval_select!( + @capture { this: &TypeId, other: &TypeId } -> bool: + if const { + crate::intrinsics::type_id_eq(*this, *other) + } else { + // Ideally we would just invoke `type_id_eq` unconditionally here, + // but since we do not MIR inline intrinsics, because backends + // may want to override them (and miri does!), MIR opts do not + // clean up this call sufficiently for LLVM to turn repeated calls + // of `TypeId` comparisons against one specific `TypeId` into + // a lookup table. + // SAFETY: We know that at runtime none of the bits have provenance and all bits + // are initialized. So we can just convert the whole thing to a `u128` and compare that. + unsafe { + crate::mem::transmute::<_, u128>(*this) == crate::mem::transmute::<_, u128>(*other) + } + } + ) + } } } @@ -742,19 +775,19 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub const fn of() -> TypeId { - let t: u128 = const { intrinsics::type_id::() }; - let t1 = (t >> 64) as u64; - let t2 = t as u64; - - TypeId { - t: (t1, t2), - #[cfg(feature = "debug_typeid")] - name: type_name::(), - } + const { intrinsics::type_id::() } } fn as_u128(self) -> u128 { - u128::from(self.t.0) << 64 | u128::from(self.t.1) + let mut bytes = [0; 16]; + + // This is a provenance-stripping memcpy. + for (i, chunk) in self.data.iter().copied().enumerate() { + let chunk = chunk.expose_provenance().to_ne_bytes(); + let start = i * chunk.len(); + bytes[start..(start + chunk.len())].copy_from_slice(&chunk); + } + u128::from_ne_bytes(bytes) } } @@ -774,22 +807,19 @@ impl hash::Hash for TypeId { // - It is correct to do so -- only hashing a subset of `self` is still // compatible with an `Eq` implementation that considers the entire // value, as ours does. - self.t.1.hash(state); + let data = + // SAFETY: The `offset` stays in-bounds, it just moves the pointer to the 2nd half of the `TypeId`. + // Only the first ptr-sized chunk ever has provenance, so that second half is always + // fine to read at integer type. + unsafe { crate::ptr::read_unaligned(self.data.as_ptr().cast::().offset(1)) }; + data.hash(state); } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for TypeId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - #[cfg(feature = "debug_typeid")] - { - write!(f, "TypeId({:#034x} = {})", self.as_u128(), self.name)?; - } - #[cfg(not(feature = "debug_typeid"))] - { - write!(f, "TypeId({:#034x})", self.as_u128())?; - } - Ok(()) + write!(f, "TypeId({:#034x})", self.as_u128()) } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 791d10eda6d..f5dcfeebed8 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2724,7 +2724,20 @@ pub const fn type_name() -> &'static str; #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] -pub const fn type_id() -> u128; +pub const fn type_id() -> crate::any::TypeId; + +/// Tests (at compile-time) if two [`crate::any::TypeId`] instances identify the +/// same type. This is necessary because at const-eval time the actual discriminating +/// data is opaque and cannot be inspected directly. +/// +/// The stabilized version of this intrinsic is the [PartialEq] impl for [`core::any::TypeId`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_do_not_const_check] +pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool { + a.data == b.data +} /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// diff --git a/library/coretests/tests/any.rs b/library/coretests/tests/any.rs index 117ef004238..25002617d0b 100644 --- a/library/coretests/tests/any.rs +++ b/library/coretests/tests/any.rs @@ -118,14 +118,6 @@ fn any_unsized() { is_any::<[i32]>(); } -#[cfg(feature = "debug_typeid")] -#[test] -fn debug_typeid_includes_name() { - let type_id = TypeId::of::<[usize; 2]>(); - let debug_str = format!("{type_id:?}"); - assert!(debug_str.ends_with("= [usize; 2])"), "{debug_str:?} did not match"); -} - #[test] fn distinct_type_names() { // https://github.com/rust-lang/rust/issues/84666 diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 3a75d316871..62ece4b6961 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -113,8 +113,6 @@ optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] # Make `RefCell` store additional debugging information, which is printed out when # a borrow error occurs debug_refcell = ["core/debug_refcell"] -# Make `TypeId` store a reference to the name of the type, so that it can print that name. -debug_typeid = ["core/debug_typeid"] # Enable std_detect default features for stdarch/crates/std_detect: diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 3adc0224971..c4937c36d4c 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -22,7 +22,6 @@ compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"] compiler-builtins-no-f16-f128 = ["std/compiler-builtins-no-f16-f128"] compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"] debug_refcell = ["std/debug_refcell"] -debug_typeid = ["std/debug_typeid"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] optimize_for_size = ["std/optimize_for_size"] diff --git a/tests/codegen/error-provide.rs b/tests/codegen/error-provide.rs index 25a66078fd4..7f091e34359 100644 --- a/tests/codegen/error-provide.rs +++ b/tests/codegen/error-provide.rs @@ -37,9 +37,9 @@ impl std::error::Error for MyError { // and eliminate redundant ones, rather than compare one-by-one. // CHECK-NEXT: start: - // CHECK-NEXT: %[[SCRUTINEE:[^ ]+]] = load i64, ptr - // CHECK-NEXT: switch i64 %[[SCRUTINEE]], label %{{.*}} [ - // CHECK-COUNT-3: i64 {{.*}}, label %{{.*}} + // CHECK-NEXT: %[[SCRUTINEE:[^ ]+]] = load i128, ptr + // CHECK-NEXT: switch i128 %[[SCRUTINEE]], label %{{.*}} [ + // CHECK-COUNT-3: i128 {{.*}}, label %{{.*}} // CHECK-NEXT: ] request .provide_ref::(&self.backtrace1) diff --git a/tests/ui/const-generics/issues/issue-90318.rs b/tests/ui/const-generics/issues/issue-90318.rs index 317ddad49cd..dfba90a5575 100644 --- a/tests/ui/const-generics/issues/issue-90318.rs +++ b/tests/ui/const-generics/issues/issue-90318.rs @@ -1,5 +1,6 @@ #![feature(const_type_id)] #![feature(generic_const_exprs)] +#![feature(const_trait_impl)] #![feature(core_intrinsics)] #![allow(incomplete_features)] @@ -13,7 +14,6 @@ fn consume(_val: T) where If<{ TypeId::of::() != TypeId::of::<()>() }>: True, //~^ ERROR overly complex generic constant - //~| ERROR: cannot call { } @@ -21,7 +21,6 @@ fn test() where If<{ TypeId::of::() != TypeId::of::<()>() }>: True, //~^ ERROR overly complex generic constant - //~| ERROR: cannot call { } diff --git a/tests/ui/const-generics/issues/issue-90318.stderr b/tests/ui/const-generics/issues/issue-90318.stderr index 9c7cb5ceb58..7031230db91 100644 --- a/tests/ui/const-generics/issues/issue-90318.stderr +++ b/tests/ui/const-generics/issues/issue-90318.stderr @@ -1,5 +1,5 @@ error: overly complex generic constant - --> $DIR/issue-90318.rs:14:8 + --> $DIR/issue-90318.rs:15:8 | LL | If<{ TypeId::of::() != TypeId::of::<()>() }>: True, | ^^-----------------^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,26 +20,5 @@ LL | If<{ TypeId::of::() != TypeId::of::<()>() }>: True, = help: consider moving this anonymous constant into a `const` function = note: this operation may be supported in the future -error[E0015]: cannot call non-const operator in constants - --> $DIR/issue-90318.rs:14:10 - | -LL | If<{ TypeId::of::() != TypeId::of::<()>() }>: True, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: impl defined here, but it is not `const` - --> $SRC_DIR/core/src/any.rs:LL:COL - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error[E0015]: cannot call non-const operator in constants - --> $DIR/issue-90318.rs:22:10 - | -LL | If<{ TypeId::of::() != TypeId::of::<()>() }>: True, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: impl defined here, but it is not `const` - --> $SRC_DIR/core/src/any.rs:LL:COL - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/const_cmp_type_id.rs b/tests/ui/consts/const_cmp_type_id.rs index dca0615083a..def615bd92b 100644 --- a/tests/ui/consts/const_cmp_type_id.rs +++ b/tests/ui/consts/const_cmp_type_id.rs @@ -6,10 +6,9 @@ use std::any::TypeId; fn main() { const { assert!(TypeId::of::() == TypeId::of::()); - //~^ ERROR the trait bound `TypeId: const PartialEq` is not satisfied assert!(TypeId::of::<()>() != TypeId::of::()); - //~^ ERROR the trait bound `TypeId: const PartialEq` is not satisfied let _a = TypeId::of::() < TypeId::of::(); + //~^ ERROR: cannot call non-const operator in constants // can't assert `_a` because it is not deterministic // FIXME(const_trait_impl) make it pass } diff --git a/tests/ui/consts/const_cmp_type_id.stderr b/tests/ui/consts/const_cmp_type_id.stderr index a8242a200ef..540eec5098b 100644 --- a/tests/ui/consts/const_cmp_type_id.stderr +++ b/tests/ui/consts/const_cmp_type_id.stderr @@ -1,15 +1,13 @@ -error[E0277]: the trait bound `TypeId: const PartialEq` is not satisfied - --> $DIR/const_cmp_type_id.rs:8:17 +error[E0015]: cannot call non-const operator in constants + --> $DIR/const_cmp_type_id.rs:10:18 | -LL | assert!(TypeId::of::() == TypeId::of::()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `TypeId: const PartialEq` is not satisfied - --> $DIR/const_cmp_type_id.rs:10:17 +LL | let _a = TypeId::of::() < TypeId::of::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | assert!(TypeId::of::<()>() != TypeId::of::()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: impl defined here, but it is not `const` + --> $SRC_DIR/core/src/any.rs:LL:COL + = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/const_transmute_type_id.rs b/tests/ui/consts/const_transmute_type_id.rs new file mode 100644 index 00000000000..56ead6a622b --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id.rs @@ -0,0 +1,11 @@ +#![feature(const_type_id, const_trait_impl)] + +use std::any::TypeId; + +const _: () = { + let id = TypeId::of::(); + let id: u8 = unsafe { (&raw const id).cast::().read() }; + //~^ ERROR: unable to turn pointer into integer +}; + +fn main() {} diff --git a/tests/ui/consts/const_transmute_type_id.stderr b/tests/ui/consts/const_transmute_type_id.stderr new file mode 100644 index 00000000000..85bd4ea2736 --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id.stderr @@ -0,0 +1,12 @@ +error[E0080]: unable to turn pointer into integer + --> $DIR/const_transmute_type_id.rs:7:27 + | +LL | let id: u8 = unsafe { (&raw const id).cast::().read() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const_transmute_type_id2.rs b/tests/ui/consts/const_transmute_type_id2.rs new file mode 100644 index 00000000000..e29cf8171ac --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id2.rs @@ -0,0 +1,14 @@ +//@ normalize-stderr: "0x(ff)+" -> "" + +#![feature(const_type_id, const_trait_impl)] + +use std::any::TypeId; + +const _: () = { + let a: TypeId = unsafe { std::mem::transmute(u128::MAX) }; + let b: TypeId = unsafe { std::mem::transmute(u128::MAX) }; + assert!(a == b); + //~^ ERROR: pointer must point to some allocation +}; + +fn main() {} diff --git a/tests/ui/consts/const_transmute_type_id2.stderr b/tests/ui/consts/const_transmute_type_id2.stderr new file mode 100644 index 00000000000..5646eb1257d --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id2.stderr @@ -0,0 +1,15 @@ +error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got [noalloc] which is a dangling pointer (it has no provenance) + --> $DIR/const_transmute_type_id2.rs:10:13 + | +LL | assert!(a == b); + | ^^^^^^ evaluation of `_` failed inside this call + | +note: inside `::eq` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `::eq::compiletime` + --> $SRC_DIR/core/src/any.rs:LL:COL + = note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `crate::intrinsics::const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const_transmute_type_id3.rs b/tests/ui/consts/const_transmute_type_id3.rs new file mode 100644 index 00000000000..a870d6e7e80 --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id3.rs @@ -0,0 +1,16 @@ +#![feature(const_type_id, const_trait_impl)] + +use std::any::TypeId; + +const _: () = { + let a = TypeId::of::<()>(); + let mut b = TypeId::of::<()>(); + unsafe { + let ptr = &mut b as *mut TypeId as *mut usize; + std::ptr::write(ptr.offset(1), 999); + } + assert!(a == b); + //~^ ERROR: one of the TypeId arguments is invalid, the hash does not match the type it represents +}; + +fn main() {} diff --git a/tests/ui/consts/const_transmute_type_id3.stderr b/tests/ui/consts/const_transmute_type_id3.stderr new file mode 100644 index 00000000000..8cfdcfebaa4 --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id3.stderr @@ -0,0 +1,15 @@ +error[E0080]: type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents + --> $DIR/const_transmute_type_id3.rs:12:13 + | +LL | assert!(a == b); + | ^^^^^^ evaluation of `_` failed inside this call + | +note: inside `::eq` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `::eq::compiletime` + --> $SRC_DIR/core/src/any.rs:LL:COL + = note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `crate::intrinsics::const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const_transmute_type_id4.rs b/tests/ui/consts/const_transmute_type_id4.rs new file mode 100644 index 00000000000..bc71f961a51 --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id4.rs @@ -0,0 +1,16 @@ +#![feature(const_type_id, const_trait_impl)] + +use std::any::TypeId; + +const _: () = { + let a = TypeId::of::<()>(); + let mut b = TypeId::of::<()>(); + unsafe { + let ptr = &mut b as *mut TypeId as *mut *const (); + std::ptr::write(ptr.offset(0), main as fn() as *const ()); + } + assert!(a == b); + //~^ ERROR: type_id_eq: `TypeId` provenance is not a type id +}; + +fn main() {} diff --git a/tests/ui/consts/const_transmute_type_id4.stderr b/tests/ui/consts/const_transmute_type_id4.stderr new file mode 100644 index 00000000000..b418a79d7f0 --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id4.stderr @@ -0,0 +1,15 @@ +error[E0080]: type_id_eq: `TypeId` provenance is not a type id + --> $DIR/const_transmute_type_id4.rs:12:13 + | +LL | assert!(a == b); + | ^^^^^^ evaluation of `_` failed inside this call + | +note: inside `::eq` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `::eq::compiletime` + --> $SRC_DIR/core/src/any.rs:LL:COL + = note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `crate::intrinsics::const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/issue-73976-monomorphic.rs b/tests/ui/consts/issue-73976-monomorphic.rs index 561c1976051..3bfdb397afb 100644 --- a/tests/ui/consts/issue-73976-monomorphic.rs +++ b/tests/ui/consts/issue-73976-monomorphic.rs @@ -1,4 +1,4 @@ -//@ known-bug: #110395 +//@ check-pass // // This test is complement to the test in issue-73976-polymorphic.rs. // In that test we ensure that polymorphic use of type_id and type_name in patterns diff --git a/tests/ui/consts/issue-73976-monomorphic.stderr b/tests/ui/consts/issue-73976-monomorphic.stderr deleted file mode 100644 index 367d5be09da..00000000000 --- a/tests/ui/consts/issue-73976-monomorphic.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0277]: the trait bound `TypeId: [const] PartialEq` is not satisfied - --> $DIR/issue-73976-monomorphic.rs:21:5 - | -LL | GetTypeId::::VALUE == GetTypeId::::VALUE - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. -- cgit 1.4.1-3-g733a5 From 51084719cddaf35bbe82c3d9bab444fdb498c936 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 27 Jun 2025 15:14:21 +0000 Subject: Avoid creating a helper allocation for creating new `TypeId` values in the interpreter --- .../rustc_const_eval/src/interpret/intrinsics.rs | 67 ++++++++++------------ 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index ee5382af0b2..1eba1f2f03c 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -6,8 +6,6 @@ use std::assert_matches::assert_matches; use rustc_abi::{FieldIdx, Size}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_ast::Mutability; -use rustc_middle::mir::interpret::{AllocId, AllocInit, alloc_range}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; @@ -30,39 +28,35 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes(), ()); tcx.mk_const_alloc(alloc) } - -pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId { - let size = Size::from_bytes(16); - let align = tcx.data_layout.pointer_align(); - let mut alloc = Allocation::new(size, *align, AllocInit::Uninit, ()); - let ptr_size = tcx.data_layout.pointer_size(); - let type_id_hash = tcx.type_id_hash(ty).as_u128(); - alloc - .write_scalar( - &tcx, - alloc_range(Size::ZERO, Size::from_bytes(16)), - Scalar::from_u128(type_id_hash), - ) - .unwrap(); - - // Give the first pointer-size bytes provenance that knows about the type id - - let alloc_id = tcx.reserve_and_set_type_id_alloc(ty); - let offset = alloc - .read_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), false) - .unwrap() - .to_target_usize(&tcx) - .unwrap(); - let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset)); - let val = Scalar::from_pointer(ptr, &tcx); - alloc.write_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), val).unwrap(); - - alloc.mutability = Mutability::Not; - - tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc)) -} - impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { + /// Generates a value of `TypeId` for `ty` in-place. + pub(crate) fn write_type_id( + &mut self, + ty: Ty<'tcx>, + dest: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ()> { + let tcx = self.tcx; + let type_id_hash = tcx.type_id_hash(ty).as_u128(); + let op = self.const_val_to_op( + ConstValue::Scalar(Scalar::from_u128(type_id_hash)), + tcx.types.u128, + None, + )?; + self.copy_op_allow_transmute(&op, dest)?; + + // Give the first pointer-size bytes provenance that knows about the type id. + // Here we rely on `TypeId` being a newtype around an array of pointers, so we + // first project to its only field and then the first array element. + let alloc_id = tcx.reserve_and_set_type_id_alloc(ty); + let first = self.project_field(dest, FieldIdx::ZERO)?; + let first = self.project_index(&first, 0)?; + let offset = self.read_scalar(&first)?.to_target_usize(&tcx)?; + let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset)); + let ptr = self.global_root_pointer(ptr)?; + let val = Scalar::from_pointer(ptr, &tcx); + self.write_scalar(val, &first) + } + /// Returns `true` if emulation happened. /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own /// intrinsic handling. @@ -96,10 +90,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::type_id => { let tp_ty = instance.args.type_at(0); ensure_monomorphic_enough(tcx, tp_ty)?; - let alloc_id = alloc_type_id(tcx, tp_ty); - let val = ConstValue::Indirect { alloc_id, offset: Size::ZERO }; - let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; - self.copy_op(&val, dest)?; + self.write_type_id(tp_ty, dest)?; } sym::type_id_eq => { // Both operands are `TypeId`, which is a newtype around an array of pointers. -- cgit 1.4.1-3-g733a5 From 65dc474d1ce75071dacd76480807abdf0beed830 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Wed, 9 Jul 2025 12:56:06 -0700 Subject: Update LLVM submodule --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 9b1bf4cf041..99f0e053168 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 9b1bf4cf041c1c1fe62cf03891ac90431615e780 +Subproject commit 99f0e0531688a822a753cc585b7408b069cb6822 -- cgit 1.4.1-3-g733a5 From 7e2527d3ab306f30d17d381257683bb0418f8b9e Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 9 Jul 2025 14:58:44 -0500 Subject: triagebot.toml: ping lolbinarycat if tidy extra checks were modified --- triagebot.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 4e3dff219f1..e3099289181 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1080,6 +1080,10 @@ cc = ["@jieyouxu"] message = "The list of allowed third-party dependencies may have been modified! You must ensure that any new dependencies have compatible licenses before merging." cc = ["@davidtwco", "@wesleywiser"] +[mentions."src/tools/tidy/src/ext_tool_checks.rs"] +message = "`tidy` extra checks were modified." +cc = ["@lolbinarycat"] + [mentions."src/bootstrap/src/core/config"] message = """ This PR modifies `src/bootstrap/src/core/config`. -- cgit 1.4.1-3-g733a5 From b286bc3141936114edcc7d124b2488e63ef4eb6f Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Wed, 9 Jul 2025 23:18:32 +0200 Subject: fix aux-build failures --- tests/ui/imports/ambiguous-1.rs | 5 ++++- tests/ui/imports/ambiguous-1.stderr | 32 +++++++++++++++++++----------- tests/ui/imports/ambiguous-2.rs | 3 +-- tests/ui/imports/ambiguous-4-extern.rs | 5 ++++- tests/ui/imports/ambiguous-4-extern.stderr | 32 +++++++++++++++++++----------- tests/ui/imports/ambiguous-4.rs | 3 +-- 6 files changed, 50 insertions(+), 30 deletions(-) diff --git a/tests/ui/imports/ambiguous-1.rs b/tests/ui/imports/ambiguous-1.rs index dca26b17466..31f39eee62b 100644 --- a/tests/ui/imports/ambiguous-1.rs +++ b/tests/ui/imports/ambiguous-1.rs @@ -1,5 +1,8 @@ +//@ check-pass // https://github.com/rust-lang/rust/pull/112743#issuecomment-1601986883 +#![warn(ambiguous_glob_imports)] + macro_rules! m { () => { pub fn id() {} @@ -24,6 +27,6 @@ pub use openssl::*; fn main() { id(); - //~^ ERROR `id` is ambiguous + //~^ WARNING `id` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-1.stderr b/tests/ui/imports/ambiguous-1.stderr index c4b7fc41932..04ff3a36c74 100644 --- a/tests/ui/imports/ambiguous-1.stderr +++ b/tests/ui/imports/ambiguous-1.stderr @@ -1,5 +1,5 @@ warning: ambiguous glob re-exports - --> $DIR/ambiguous-1.rs:10:13 + --> $DIR/ambiguous-1.rs:13:13 | LL | pub use self::evp::*; | ^^^^^^^^^^^^ the name `id` in the value namespace is first re-exported here @@ -9,8 +9,8 @@ LL | pub use self::handwritten::*; | = note: `#[warn(ambiguous_glob_reexports)]` on by default -error: `id` is ambiguous - --> $DIR/ambiguous-1.rs:26:5 +warning: `id` is ambiguous + --> $DIR/ambiguous-1.rs:29:5 | LL | id(); | ^^ ambiguous name @@ -19,24 +19,28 @@ LL | id(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function imported here - --> $DIR/ambiguous-1.rs:10:13 + --> $DIR/ambiguous-1.rs:13:13 | LL | pub use self::evp::*; | ^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate note: `id` could also refer to the function imported here - --> $DIR/ambiguous-1.rs:12:13 + --> $DIR/ambiguous-1.rs:15:13 | LL | pub use self::handwritten::*; | ^^^^^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate - = note: `#[deny(ambiguous_glob_imports)]` on by default +note: the lint level is defined here + --> $DIR/ambiguous-1.rs:4:9 + | +LL | #![warn(ambiguous_glob_imports)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error; 1 warning emitted +warning: 2 warnings emitted Future incompatibility report: Future breakage diagnostic: -error: `id` is ambiguous - --> $DIR/ambiguous-1.rs:26:5 +warning: `id` is ambiguous + --> $DIR/ambiguous-1.rs:29:5 | LL | id(); | ^^ ambiguous name @@ -45,16 +49,20 @@ LL | id(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function imported here - --> $DIR/ambiguous-1.rs:10:13 + --> $DIR/ambiguous-1.rs:13:13 | LL | pub use self::evp::*; | ^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate note: `id` could also refer to the function imported here - --> $DIR/ambiguous-1.rs:12:13 + --> $DIR/ambiguous-1.rs:15:13 | LL | pub use self::handwritten::*; | ^^^^^^^^^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate - = note: `#[deny(ambiguous_glob_imports)]` on by default +note: the lint level is defined here + --> $DIR/ambiguous-1.rs:4:9 + | +LL | #![warn(ambiguous_glob_imports)] + | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/imports/ambiguous-2.rs b/tests/ui/imports/ambiguous-2.rs index cffdcb9d922..087431485ad 100644 --- a/tests/ui/imports/ambiguous-2.rs +++ b/tests/ui/imports/ambiguous-2.rs @@ -1,5 +1,4 @@ -//@ ignore-test -// ^^ don't know how to change this test +//@ check-pass //@ aux-build: ../ambiguous-1.rs // https://github.com/rust-lang/rust/pull/113099#issuecomment-1633574396 diff --git a/tests/ui/imports/ambiguous-4-extern.rs b/tests/ui/imports/ambiguous-4-extern.rs index b70d0484801..bc856af852d 100644 --- a/tests/ui/imports/ambiguous-4-extern.rs +++ b/tests/ui/imports/ambiguous-4-extern.rs @@ -1,5 +1,8 @@ +//@ check-pass // https://github.com/rust-lang/rust/pull/112743#issuecomment-1601986883 +#![warn(ambiguous_glob_imports)] + macro_rules! m { () => { pub fn id() {} @@ -20,6 +23,6 @@ mod handwritten { fn main() { id(); - //~^ ERROR `id` is ambiguous + //~^ WARNING `id` is ambiguous //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/imports/ambiguous-4-extern.stderr b/tests/ui/imports/ambiguous-4-extern.stderr index 3e79c455c42..a9427ac0350 100644 --- a/tests/ui/imports/ambiguous-4-extern.stderr +++ b/tests/ui/imports/ambiguous-4-extern.stderr @@ -1,5 +1,5 @@ warning: ambiguous glob re-exports - --> $DIR/ambiguous-4-extern.rs:9:9 + --> $DIR/ambiguous-4-extern.rs:12:9 | LL | pub use evp::*; | ^^^^^^ the name `id` in the value namespace is first re-exported here @@ -8,8 +8,8 @@ LL | pub use handwritten::*; | = note: `#[warn(ambiguous_glob_reexports)]` on by default -error: `id` is ambiguous - --> $DIR/ambiguous-4-extern.rs:22:5 +warning: `id` is ambiguous + --> $DIR/ambiguous-4-extern.rs:25:5 | LL | id(); | ^^ ambiguous name @@ -18,24 +18,28 @@ LL | id(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function imported here - --> $DIR/ambiguous-4-extern.rs:9:9 + --> $DIR/ambiguous-4-extern.rs:12:9 | LL | pub use evp::*; | ^^^^^^ = help: consider adding an explicit import of `id` to disambiguate note: `id` could also refer to the function imported here - --> $DIR/ambiguous-4-extern.rs:10:9 + --> $DIR/ambiguous-4-extern.rs:13:9 | LL | pub use handwritten::*; | ^^^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate - = note: `#[deny(ambiguous_glob_imports)]` on by default +note: the lint level is defined here + --> $DIR/ambiguous-4-extern.rs:4:9 + | +LL | #![warn(ambiguous_glob_imports)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error; 1 warning emitted +warning: 2 warnings emitted Future incompatibility report: Future breakage diagnostic: -error: `id` is ambiguous - --> $DIR/ambiguous-4-extern.rs:22:5 +warning: `id` is ambiguous + --> $DIR/ambiguous-4-extern.rs:25:5 | LL | id(); | ^^ ambiguous name @@ -44,16 +48,20 @@ LL | id(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function imported here - --> $DIR/ambiguous-4-extern.rs:9:9 + --> $DIR/ambiguous-4-extern.rs:12:9 | LL | pub use evp::*; | ^^^^^^ = help: consider adding an explicit import of `id` to disambiguate note: `id` could also refer to the function imported here - --> $DIR/ambiguous-4-extern.rs:10:9 + --> $DIR/ambiguous-4-extern.rs:13:9 | LL | pub use handwritten::*; | ^^^^^^^^^^^^^^ = help: consider adding an explicit import of `id` to disambiguate - = note: `#[deny(ambiguous_glob_imports)]` on by default +note: the lint level is defined here + --> $DIR/ambiguous-4-extern.rs:4:9 + | +LL | #![warn(ambiguous_glob_imports)] + | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/imports/ambiguous-4.rs b/tests/ui/imports/ambiguous-4.rs index e367c120adf..3a9a14bfb52 100644 --- a/tests/ui/imports/ambiguous-4.rs +++ b/tests/ui/imports/ambiguous-4.rs @@ -1,5 +1,4 @@ -//@ ignore-test -// ^^ don't know how to change this test +//@ check-pass //@ aux-build: ../ambiguous-4-extern.rs extern crate ambiguous_4_extern; -- cgit 1.4.1-3-g733a5 From e9e31b1a932314e086b246fabb94623dda8fbafa Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Tue, 8 Jul 2025 23:52:20 +0200 Subject: MacroData in ResolverArenas + split macro_map into extern_macro_map and local_macro_map. --- compiler/rustc_resolve/src/build_reduced_graph.rs | 36 ++++++++++++----------- compiler/rustc_resolve/src/def_collector.rs | 2 +- compiler/rustc_resolve/src/diagnostics.rs | 9 ++++-- compiler/rustc_resolve/src/lib.rs | 24 +++++++++++---- compiler/rustc_resolve/src/macros.rs | 8 ++--- 5 files changed, 50 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index f775cac149e..ef122deba4e 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -162,28 +162,30 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - pub(crate) fn get_macro(&mut self, res: Res) -> Option<&MacroData> { + pub(crate) fn get_macro(&self, res: Res) -> Option<&'ra MacroData> { match res { Res::Def(DefKind::Macro(..), def_id) => Some(self.get_macro_by_def_id(def_id)), - Res::NonMacroAttr(_) => Some(&self.non_macro_attr), + Res::NonMacroAttr(_) => Some(self.non_macro_attr), _ => None, } } - pub(crate) fn get_macro_by_def_id(&mut self, def_id: DefId) -> &MacroData { - if self.macro_map.contains_key(&def_id) { - return &self.macro_map[&def_id]; - } - - let loaded_macro = self.cstore().load_macro_untracked(def_id, self.tcx); - let macro_data = match loaded_macro { - LoadedMacro::MacroDef { def, ident, attrs, span, edition } => { - self.compile_macro(&def, ident, &attrs, span, ast::DUMMY_NODE_ID, edition) - } - LoadedMacro::ProcMacro(ext) => MacroData::new(Arc::new(ext)), - }; + pub(crate) fn get_macro_by_def_id(&self, def_id: DefId) -> &'ra MacroData { + // Local macros are always compiled. + match def_id.as_local() { + Some(local_def_id) => self.local_macro_map[&local_def_id], + None => *self.extern_macro_map.borrow_mut().entry(def_id).or_insert_with(|| { + let loaded_macro = self.cstore().load_macro_untracked(def_id, self.tcx); + let macro_data = match loaded_macro { + LoadedMacro::MacroDef { def, ident, attrs, span, edition } => { + self.compile_macro(&def, ident, &attrs, span, ast::DUMMY_NODE_ID, edition) + } + LoadedMacro::ProcMacro(ext) => MacroData::new(Arc::new(ext)), + }; - self.macro_map.entry(def_id).or_insert(macro_data) + self.arenas.alloc_macro(macro_data) + }), + } } pub(crate) fn build_reduced_graph( @@ -1203,7 +1205,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) { if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); - let nrules = self.r.macro_map[&def_id.to_def_id()].nrules; + let nrules = self.r.local_macro_map[&def_id].nrules; self.r.unused_macro_rules.insert(node_id, DenseBitSet::new_filled(nrules)); } } @@ -1222,7 +1224,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { Some((macro_kind, ident, span)) => { let res = Res::Def(DefKind::Macro(macro_kind), def_id.to_def_id()); let macro_data = MacroData::new(self.r.dummy_ext(macro_kind)); - self.r.macro_map.insert(def_id.to_def_id(), macro_data); + self.r.new_local_macro(def_id, macro_data); self.r.proc_macro_stubs.insert(def_id); (res, ident, span, false) } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 1e345b11c14..e2caf632dd2 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -165,7 +165,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { self.create_def(i.id, i.kind.ident().map(|ident| ident.name), def_kind, i.span); if let Some(macro_data) = opt_macro_data { - self.resolver.macro_map.insert(def_id.to_def_id(), macro_data); + self.resolver.new_local_macro(def_id, macro_data); } self.with_parent(def_id, |this| { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index c99bc747fd2..c4ff5770e76 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1669,9 +1669,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut all_attrs: UnordMap> = UnordMap::default(); // We're collecting these in a hashmap, and handle ordering the output further down. #[allow(rustc::potential_query_instability)] - for (def_id, data) in &self.macro_map { + for (def_id, data) in self + .local_macro_map + .iter() + .map(|(local_id, data)| (local_id.to_def_id(), data)) + .chain(self.extern_macro_map.borrow().iter().map(|(id, d)| (*id, d))) + { for helper_attr in &data.ext.helper_attrs { - let item_name = self.tcx.item_name(*def_id); + let item_name = self.tcx.item_name(def_id); all_attrs.entry(*helper_attr).or_default().push(item_name); if helper_attr == &ident.name { derives.push(item_name); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index f8ca20c568f..f6c56c9a46a 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1128,10 +1128,12 @@ pub struct Resolver<'ra, 'tcx> { builtin_macros: FxHashMap, registered_tools: &'tcx RegisteredTools, macro_use_prelude: FxIndexMap>, - macro_map: FxHashMap, + local_macro_map: FxHashMap, + /// Lazily populated cache of macros loaded from external crates. + extern_macro_map: RefCell>, dummy_ext_bang: Arc, dummy_ext_derive: Arc, - non_macro_attr: MacroData, + non_macro_attr: &'ra MacroData, local_macro_def_scopes: FxHashMap>, ast_transform_scopes: FxHashMap>, unused_macros: FxIndexMap, @@ -1241,6 +1243,7 @@ pub struct ResolverArenas<'ra> { imports: TypedArena>, name_resolutions: TypedArena>>, ast_paths: TypedArena, + macros: TypedArena, dropless: DroplessArena, } @@ -1298,6 +1301,9 @@ impl<'ra> ResolverArenas<'ra> { fn alloc_ast_paths(&'ra self, paths: &[ast::Path]) -> &'ra [ast::Path] { self.ast_paths.alloc_from_iter(paths.iter().cloned()) } + fn alloc_macro(&'ra self, macro_data: MacroData) -> &'ra MacroData { + self.macros.alloc(macro_data) + } fn alloc_pattern_spans(&'ra self, spans: impl Iterator) -> &'ra [Span] { self.dropless.alloc_from_iter(spans) } @@ -1540,10 +1546,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { builtin_macros: Default::default(), registered_tools, macro_use_prelude: Default::default(), - macro_map: FxHashMap::default(), + local_macro_map: Default::default(), + extern_macro_map: Default::default(), dummy_ext_bang: Arc::new(SyntaxExtension::dummy_bang(edition)), dummy_ext_derive: Arc::new(SyntaxExtension::dummy_derive(edition)), - non_macro_attr: MacroData::new(Arc::new(SyntaxExtension::non_macro_attr(edition))), + non_macro_attr: arenas + .alloc_macro(MacroData::new(Arc::new(SyntaxExtension::non_macro_attr(edition)))), invocation_parent_scopes: Default::default(), output_macro_rules_scopes: Default::default(), macro_rules_scopes: Default::default(), @@ -1616,6 +1624,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) } + fn new_local_macro(&mut self, def_id: LocalDefId, macro_data: MacroData) -> &'ra MacroData { + let mac = self.arenas.alloc_macro(macro_data); + self.local_macro_map.insert(def_id, mac); + mac + } + fn next_node_id(&mut self) -> NodeId { let start = self.next_node_id; let next = start.as_u32().checked_add(1).expect("input too large; ran out of NodeIds"); @@ -1734,7 +1748,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { f(self, MacroNS); } - fn is_builtin_macro(&mut self, res: Res) -> bool { + fn is_builtin_macro(&self, res: Res) -> bool { self.get_macro(res).is_some_and(|macro_data| macro_data.ext.builtin_name.is_some()) } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index acbefe53422..c45d18900e2 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -354,8 +354,8 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { if unused_arms.is_empty() { continue; } - let def_id = self.local_def_id(node_id).to_def_id(); - let m = &self.macro_map[&def_id]; + let def_id = self.local_def_id(node_id); + let m = &self.local_macro_map[&def_id]; let SyntaxExtensionKind::LegacyBang(ref ext) = m.ext.kind else { continue; }; @@ -1132,7 +1132,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - pub(crate) fn check_reserved_macro_name(&mut self, ident: Ident, res: Res) { + pub(crate) fn check_reserved_macro_name(&self, ident: Ident, res: Res) { // Reserve some names that are not quite covered by the general check // performed on `Resolver::builtin_attrs`. if ident.name == sym::cfg || ident.name == sym::cfg_attr { @@ -1148,7 +1148,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// /// Possibly replace its expander to a pre-defined one for built-in macros. pub(crate) fn compile_macro( - &mut self, + &self, macro_def: &ast::MacroDef, ident: Ident, attrs: &[rustc_hir::Attribute], -- cgit 1.4.1-3-g733a5 From e56678378bd0ef967074ed6b07fc9ea7d9ce4070 Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Wed, 9 Jul 2025 17:49:05 +0200 Subject: Use reference for MacroRulesScopeRef instead of Interned. --- compiler/rustc_resolve/src/lib.rs | 2 +- compiler/rustc_resolve/src/macros.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index f6c56c9a46a..6e4c5ef1909 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1290,7 +1290,7 @@ impl<'ra> ResolverArenas<'ra> { self.name_resolutions.alloc(Default::default()) } fn alloc_macro_rules_scope(&'ra self, scope: MacroRulesScope<'ra>) -> MacroRulesScopeRef<'ra> { - Interned::new_unchecked(self.dropless.alloc(Cell::new(scope))) + self.dropless.alloc(Cell::new(scope)) } fn alloc_macro_rules_binding( &'ra self, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index c45d18900e2..89bbe8dad98 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -9,7 +9,6 @@ use rustc_ast::expand::StrippedCfgItem; use rustc_ast::{self as ast, Crate, NodeId, attr}; use rustc_ast_pretty::pprust; use rustc_attr_data_structures::StabilityLevel; -use rustc_data_structures::intern::Interned; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; use rustc_expand::base::{ Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, @@ -80,7 +79,7 @@ pub(crate) enum MacroRulesScope<'ra> { /// This helps to avoid uncontrollable growth of `macro_rules!` scope chains, /// which usually grow linearly with the number of macro invocations /// in a module (including derives) and hurt performance. -pub(crate) type MacroRulesScopeRef<'ra> = Interned<'ra, Cell>>; +pub(crate) type MacroRulesScopeRef<'ra> = &'ra Cell>; /// Macro namespace is separated into two sub-namespaces, one for bang macros and /// one for attribute-like macros (attributes, derives). -- cgit 1.4.1-3-g733a5 From e7ef31d651be6aa8809933fabb793b98300b970f Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 21 Jun 2025 01:01:20 -0400 Subject: mbe: Refactor diagnostics for invalid metavar expression syntax Give a more user-friendly diagnostic about the following: * Trailing tokens within braces, e.g. `${foo() extra}` * Missing parentheses, e.g. `${foo}` * Incorrect number of arguments, with a hint about correct usage. --- compiler/rustc_expand/messages.ftl | 21 +++ compiler/rustc_expand/src/errors.rs | 34 ++++ compiler/rustc_expand/src/mbe/metavar_expr.rs | 84 ++++++++-- .../ui/macros/metavar-expressions/syntax-errors.rs | 31 ++-- .../metavar-expressions/syntax-errors.stderr | 186 ++++++++++----------- 5 files changed, 234 insertions(+), 122 deletions(-) diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 3fc0fa06191..7b8c1e076a0 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -133,6 +133,27 @@ expand_module_multiple_candidates = expand_must_repeat_once = this must repeat at least once +expand_mve_extra_tokens = + unexpected trailing tokens + .label = for this metavariable expression + .range = the `{$name}` metavariable expression takes between {$min_or_exact_args} and {$max_args} arguments + .exact = the `{$name}` metavariable expression takes {$min_or_exact_args -> + [zero] no arguments + [one] a single argument + *[other] {$min_or_exact_args} arguments + } + .suggestion = try removing {$extra_count -> + [one] this token + *[other] these tokens + } + +expand_mve_missing_paren = + expected `(` + .label = for this this metavariable expression + .unexpected = unexpected token + .note = metavariable expressions use function-like parentheses syntax + .suggestion = try adding parentheses + expand_mve_unrecognized_var = variable `{$key}` is not recognized in meta-variable expression diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index fdbc65aff68..b56a2b043e7 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -496,6 +496,40 @@ pub(crate) use metavar_exprs::*; mod metavar_exprs { use super::*; + #[derive(Diagnostic, Default)] + #[diag(expand_mve_extra_tokens)] + pub(crate) struct MveExtraTokens { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, + #[label] + pub ident_span: Span, + pub extra_count: usize, + + // The rest is only used for specific diagnostics and can be default if neither + // `note` is `Some`. + #[note(expand_exact)] + pub exact_args_note: Option<()>, + #[note(expand_range)] + pub range_args_note: Option<()>, + pub min_or_exact_args: usize, + pub max_args: usize, + pub name: String, + } + + #[derive(Diagnostic)] + #[note] + #[diag(expand_mve_missing_paren)] + pub(crate) struct MveMissingParen { + #[primary_span] + #[label] + pub ident_span: Span, + #[label(expand_unexpected)] + pub unexpected_span: Option, + #[suggestion(code = "( /* ... */ )", applicability = "has-placeholders")] + pub insert_span: Option, + } + #[derive(Diagnostic)] #[diag(expand_mve_unrecognized_var)] pub(crate) struct MveUnrecognizedVar { diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index ffd3548019a..8f15b5794c7 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -7,6 +7,8 @@ use rustc_macros::{Decodable, Encodable}; use rustc_session::parse::ParseSess; use rustc_span::{Ident, Span, Symbol}; +use crate::errors; + pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers"; pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal"; @@ -40,11 +42,32 @@ impl MetaVarExpr { ) -> PResult<'psess, MetaVarExpr> { let mut iter = input.iter(); let ident = parse_ident(&mut iter, psess, outer_span)?; - let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = iter.next() else { - let msg = "meta-variable expression parameter must be wrapped in parentheses"; - return Err(psess.dcx().struct_span_err(ident.span, msg)); + let next = iter.next(); + let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = next else { + // No `()`; wrong or no delimiters. Point at a problematic span or a place to + // add parens if it makes sense. + let (unexpected_span, insert_span) = match next { + Some(TokenTree::Delimited(..)) => (None, None), + Some(tt) => (Some(tt.span()), None), + None => (None, Some(ident.span.shrink_to_hi())), + }; + let err = + errors::MveMissingParen { ident_span: ident.span, unexpected_span, insert_span }; + return Err(psess.dcx().create_err(err)); }; - check_trailing_token(&mut iter, psess)?; + + // Ensure there are no trailing tokens in the braces, e.g. `${foo() extra}` + if iter.peek().is_some() { + let span = iter_span(&iter).expect("checked is_some above"); + let err = errors::MveExtraTokens { + span, + ident_span: ident.span, + extra_count: iter.count(), + ..Default::default() + }; + return Err(psess.dcx().create_err(err)); + } + let mut iter = args.iter(); let rslt = match ident.as_str() { "concat" => parse_concat(&mut iter, psess, outer_span, ident.span)?, @@ -67,7 +90,7 @@ impl MetaVarExpr { return Err(err); } }; - check_trailing_token(&mut iter, psess)?; + check_trailing_tokens(&mut iter, psess, ident)?; Ok(rslt) } @@ -87,20 +110,51 @@ impl MetaVarExpr { } } -// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}` -fn check_trailing_token<'psess>( +/// Checks if there are any remaining tokens (for example, `${ignore($valid, extra)}`) and create +/// a diag with the correct arg count if so. +fn check_trailing_tokens<'psess>( iter: &mut TokenStreamIter<'_>, psess: &'psess ParseSess, + ident: Ident, ) -> PResult<'psess, ()> { - if let Some(tt) = iter.next() { - let mut diag = psess - .dcx() - .struct_span_err(tt.span(), format!("unexpected token: {}", pprust::tt_to_string(tt))); - diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens"); - Err(diag) - } else { - Ok(()) + if iter.peek().is_none() { + // All tokens consumed, as expected + return Ok(()); } + + // `None` for max indicates the arg count must be exact, `Some` indicates a range is accepted. + let (min_or_exact_args, max_args) = match ident.as_str() { + "concat" => panic!("concat takes unlimited tokens but didn't eat them all"), + "ignore" => (1, None), + // 1 or 2 args + "count" => (1, Some(2)), + // 0 or 1 arg + "index" => (0, Some(1)), + "len" => (0, Some(1)), + other => unreachable!("unknown MVEs should be rejected earlier (got `{other}`)"), + }; + + let err = errors::MveExtraTokens { + span: iter_span(iter).expect("checked is_none above"), + ident_span: ident.span, + extra_count: iter.count(), + + exact_args_note: if max_args.is_some() { None } else { Some(()) }, + range_args_note: if max_args.is_some() { Some(()) } else { None }, + min_or_exact_args, + max_args: max_args.unwrap_or_default(), + name: ident.to_string(), + }; + Err(psess.dcx().create_err(err)) +} + +/// Returns a span encompassing all tokens in the iterator if there is at least one item. +fn iter_span(iter: &TokenStreamIter<'_>) -> Option { + let mut iter = iter.clone(); // cloning is cheap + let first_sp = iter.next()?.span(); + let last_sp = iter.last().map(TokenTree::span).unwrap_or(first_sp); + let span = first_sp.with_hi(last_sp.hi()); + Some(span) } /// Indicates what is placed in a `concat` parameter. For example, literals diff --git a/tests/ui/macros/metavar-expressions/syntax-errors.rs b/tests/ui/macros/metavar-expressions/syntax-errors.rs index 8fc76a74baa..a6eebdfa674 100644 --- a/tests/ui/macros/metavar-expressions/syntax-errors.rs +++ b/tests/ui/macros/metavar-expressions/syntax-errors.rs @@ -30,7 +30,7 @@ macro_rules! metavar_with_literal_suffix { macro_rules! mve_without_parens { ( $( $i:ident ),* ) => { ${ count } }; - //~^ ERROR meta-variable expression parameter must be wrapped in parentheses + //~^ ERROR expected `(` } #[rustfmt::skip] @@ -45,9 +45,14 @@ macro_rules! open_brackets_with_lit { //~^ ERROR expected identifier } +macro_rules! mvs_missing_paren { + ( $( $i:ident ),* ) => { ${ count $i ($i) } }; + //~^ ERROR expected `(` +} + macro_rules! mve_wrong_delim { ( $( $i:ident ),* ) => { ${ count{i} } }; - //~^ ERROR meta-variable expression parameter must be wrapped in parentheses + //~^ ERROR expected `(` } macro_rules! invalid_metavar { @@ -64,28 +69,30 @@ macro_rules! open_brackets_with_group { macro_rules! extra_garbage_after_metavar { ( $( $i:ident ),* ) => { ${count() a b c} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens ${count($i a b c)} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens ${count($i, 1 a b c)} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens ${count($i) a b c} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens ${ignore($i) a b c} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens ${ignore($i a b c)} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens ${index() a b c} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens ${index(1 a b c)} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens ${index() a b c} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens ${index(1 a b c)} - //~^ ERROR unexpected token: a + //~^ ERROR unexpected trailing tokens + ${index(1, a b c)} + //~^ ERROR unexpected trailing tokens }; } diff --git a/tests/ui/macros/metavar-expressions/syntax-errors.stderr b/tests/ui/macros/metavar-expressions/syntax-errors.stderr index 20d2358facc..f3c08f05e2a 100644 --- a/tests/ui/macros/metavar-expressions/syntax-errors.stderr +++ b/tests/ui/macros/metavar-expressions/syntax-errors.stderr @@ -40,164 +40,160 @@ error: only unsuffixes integer literals are supported in meta-variable expressio LL | ( $( $i:ident ),* ) => { ${ index(1u32) } }; | ^^^^^ -error: meta-variable expression parameter must be wrapped in parentheses +error: expected `(` --> $DIR/syntax-errors.rs:32:33 | LL | ( $( $i:ident ),* ) => { ${ count } }; - | ^^^^^ + | ^^^^^- help: try adding parentheses: `( /* ... */ )` + | | + | for this this metavariable expression + | + = note: metavariable expressions use function-like parentheses syntax -error: meta-variable expression parameter must be wrapped in parentheses +error: expected `(` --> $DIR/syntax-errors.rs:49:33 | +LL | ( $( $i:ident ),* ) => { ${ count $i ($i) } }; + | ^^^^^ - unexpected token + | | + | for this this metavariable expression + | + = note: metavariable expressions use function-like parentheses syntax + +error: expected `(` + --> $DIR/syntax-errors.rs:54:33 + | LL | ( $( $i:ident ),* ) => { ${ count{i} } }; - | ^^^^^ + | ^^^^^ for this this metavariable expression + | + = note: metavariable expressions use function-like parentheses syntax error: expected identifier, found `123` - --> $DIR/syntax-errors.rs:54:23 + --> $DIR/syntax-errors.rs:59:23 | LL | () => { ${ignore($123)} } | ^^^ help: try removing `123` -error: unexpected token: a - --> $DIR/syntax-errors.rs:66:19 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:71:19 | LL | ${count() a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:66:19 - | -LL | ${count() a b c} - | ^ + | ----- ^^^^^ help: try removing these tokens + | | + | for this metavariable expression -error: unexpected token: a - --> $DIR/syntax-errors.rs:68:20 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:73:20 | LL | ${count($i a b c)} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:68:20 + | ----- ^^^^^ help: try removing these tokens + | | + | for this metavariable expression | -LL | ${count($i a b c)} - | ^ + = note: the `count` metavariable expression takes between 1 and 2 arguments -error: unexpected token: a - --> $DIR/syntax-errors.rs:70:23 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:75:23 | LL | ${count($i, 1 a b c)} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:70:23 + | ----- ^^^^^ help: try removing these tokens + | | + | for this metavariable expression | -LL | ${count($i, 1 a b c)} - | ^ + = note: the `count` metavariable expression takes between 1 and 2 arguments -error: unexpected token: a - --> $DIR/syntax-errors.rs:72:21 - | -LL | ${count($i) a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:72:21 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:77:21 | LL | ${count($i) a b c} - | ^ + | ----- ^^^^^ help: try removing these tokens + | | + | for this metavariable expression -error: unexpected token: a - --> $DIR/syntax-errors.rs:75:22 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:80:22 | LL | ${ignore($i) a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:75:22 - | -LL | ${ignore($i) a b c} - | ^ + | ------ ^^^^^ help: try removing these tokens + | | + | for this metavariable expression -error: unexpected token: a - --> $DIR/syntax-errors.rs:77:21 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:82:21 | LL | ${ignore($i a b c)} - | ^ + | ------ ^^^^^ help: try removing these tokens + | | + | for this metavariable expression | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:77:21 - | -LL | ${ignore($i a b c)} - | ^ + = note: the `ignore` metavariable expression takes a single argument -error: unexpected token: a - --> $DIR/syntax-errors.rs:80:19 - | -LL | ${index() a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:80:19 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:85:19 | LL | ${index() a b c} - | ^ + | ----- ^^^^^ help: try removing these tokens + | | + | for this metavariable expression -error: unexpected token: a - --> $DIR/syntax-errors.rs:82:19 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:87:19 | LL | ${index(1 a b c)} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:82:19 + | ----- ^^^^^ help: try removing these tokens + | | + | for this metavariable expression | -LL | ${index(1 a b c)} - | ^ + = note: the `index` metavariable expression takes between 0 and 1 arguments -error: unexpected token: a - --> $DIR/syntax-errors.rs:85:19 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:90:19 | LL | ${index() a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:85:19 - | -LL | ${index() a b c} - | ^ + | ----- ^^^^^ help: try removing these tokens + | | + | for this metavariable expression -error: unexpected token: a - --> $DIR/syntax-errors.rs:87:19 +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:92:19 | LL | ${index(1 a b c)} - | ^ + | ----- ^^^^^ help: try removing these tokens + | | + | for this metavariable expression | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:87:19 + = note: the `index` metavariable expression takes between 0 and 1 arguments + +error: unexpected trailing tokens + --> $DIR/syntax-errors.rs:94:18 | -LL | ${index(1 a b c)} - | ^ +LL | ${index(1, a b c)} + | ----- ^^^^^^^ help: try removing these tokens + | | + | for this metavariable expression + | + = note: the `index` metavariable expression takes between 0 and 1 arguments error: meta-variable expression depth must be a literal - --> $DIR/syntax-errors.rs:94:33 + --> $DIR/syntax-errors.rs:101:33 | LL | ( $( $i:ident ),* ) => { ${ index(IDX) } }; | ^^^^^ error: meta-variables within meta-variable expressions must be referenced using a dollar sign - --> $DIR/syntax-errors.rs:100:11 + --> $DIR/syntax-errors.rs:107:11 | LL | ${count(foo)} | ^^^^^ error: meta-variables within meta-variable expressions must be referenced using a dollar sign - --> $DIR/syntax-errors.rs:107:11 + --> $DIR/syntax-errors.rs:114:11 | LL | ${ignore(bar)} | ^^^^^^ error: unrecognized meta-variable expression - --> $DIR/syntax-errors.rs:113:33 + --> $DIR/syntax-errors.rs:120:33 | LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; | ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and len @@ -215,10 +211,10 @@ LL | () => { ${ "hi" } }; | ^^^^ help: try removing `"hi"` error: expected identifier or string literal - --> $DIR/syntax-errors.rs:60:33 + --> $DIR/syntax-errors.rs:65:33 | LL | ( $( $i:ident ),* ) => { ${ {} } }; | ^^ -error: aborting due to 25 previous errors +error: aborting due to 27 previous errors -- cgit 1.4.1-3-g733a5 From 87e981996fe47826a8f3a69e3d3cc73ec68dbf8d Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 21 Jun 2025 02:59:14 -0400 Subject: mbe: Refactor the diagnostic for unrecognized metavariable expressions Change to a structural diagnostic, update the valid list, and move the valid list to a note. --- compiler/rustc_expand/messages.ftl | 5 +++++ compiler/rustc_expand/src/errors.rs | 10 ++++++++++ compiler/rustc_expand/src/mbe/metavar_expr.rs | 14 +++++--------- tests/ui/macros/metavar-expressions/syntax-errors.rs | 2 +- tests/ui/macros/metavar-expressions/syntax-errors.stderr | 6 ++++-- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 7b8c1e076a0..89d6e62834d 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -154,6 +154,11 @@ expand_mve_missing_paren = .note = metavariable expressions use function-like parentheses syntax .suggestion = try adding parentheses +expand_mve_unrecognized_expr = + unrecognized metavariable expression + .label = not a valid metavariable expression + .note = valid metavariable expressions are {$valid_expr_list} + expand_mve_unrecognized_var = variable `{$key}` is not recognized in meta-variable expression diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index b56a2b043e7..3ac5d213053 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -530,6 +530,16 @@ mod metavar_exprs { pub insert_span: Option, } + #[derive(Diagnostic)] + #[note] + #[diag(expand_mve_unrecognized_expr)] + pub(crate) struct MveUnrecognizedExpr { + #[primary_span] + #[label] + pub span: Span, + pub valid_expr_list: &'static str, + } + #[derive(Diagnostic)] #[diag(expand_mve_unrecognized_var)] pub(crate) struct MveUnrecognizedVar { diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 8f15b5794c7..d2b275ad20a 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -79,15 +79,11 @@ impl MetaVarExpr { "index" => MetaVarExpr::Index(parse_depth(&mut iter, psess, ident.span)?), "len" => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?), _ => { - let err_msg = "unrecognized meta-variable expression"; - let mut err = psess.dcx().struct_span_err(ident.span, err_msg); - err.span_suggestion( - ident.span, - "supported expressions are count, ignore, index and len", - "", - Applicability::MachineApplicable, - ); - return Err(err); + let err = errors::MveUnrecognizedExpr { + span: ident.span, + valid_expr_list: "`count`, `ignore`, `index`, `len`, and `concat`", + }; + return Err(psess.dcx().create_err(err)); } }; check_trailing_tokens(&mut iter, psess, ident)?; diff --git a/tests/ui/macros/metavar-expressions/syntax-errors.rs b/tests/ui/macros/metavar-expressions/syntax-errors.rs index a6eebdfa674..585ea4d5979 100644 --- a/tests/ui/macros/metavar-expressions/syntax-errors.rs +++ b/tests/ui/macros/metavar-expressions/syntax-errors.rs @@ -118,7 +118,7 @@ macro_rules! unknown_ignore_ident { macro_rules! unknown_metavar { ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; - //~^ ERROR unrecognized meta-variable expression + //~^ ERROR unrecognized metavariable expression } fn main() {} diff --git a/tests/ui/macros/metavar-expressions/syntax-errors.stderr b/tests/ui/macros/metavar-expressions/syntax-errors.stderr index f3c08f05e2a..bf1c7673a6c 100644 --- a/tests/ui/macros/metavar-expressions/syntax-errors.stderr +++ b/tests/ui/macros/metavar-expressions/syntax-errors.stderr @@ -192,11 +192,13 @@ error: meta-variables within meta-variable expressions must be referenced using LL | ${ignore(bar)} | ^^^^^^ -error: unrecognized meta-variable expression +error: unrecognized metavariable expression --> $DIR/syntax-errors.rs:120:33 | LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; - | ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and len + | ^^^^^^^^^^^^^^ not a valid metavariable expression + | + = note: valid metavariable expressions are `count`, `ignore`, `index`, `len`, and `concat` error: expected identifier or string literal --> $DIR/syntax-errors.rs:38:14 -- cgit 1.4.1-3-g733a5 From 93db9e7ee01d61cb97b4f7b3d61903477910cae2 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 29 Jun 2025 11:40:59 +0800 Subject: Remove uncessary parens in closure body with unused lint --- compiler/rustc_errors/src/emitter.rs | 2 +- compiler/rustc_lint/src/unused.rs | 10 +++- compiler/rustc_parse/src/parser/item.rs | 2 +- compiler/rustc_resolve/src/late/diagnostics.rs | 3 +- library/core/src/unicode/unicode_data.rs | 2 +- src/tools/clippy/clippy_lints/src/unused_async.rs | 2 +- src/tools/clippy/clippy_utils/src/ty/mod.rs | 4 +- src/tools/compiletest/src/runtest.rs | 6 +++ src/tools/test-float-parse/src/lib.rs | 2 +- .../unicode-table-generator/src/range_search.rs | 2 +- .../async-await/issues/issue-54752-async-block.rs | 1 - .../issues/issue-54752-async-block.stderr | 15 ------ .../ui/lint/unused/closure-body-issue-136741.fixed | 36 +++++++++++++ tests/ui/lint/unused/closure-body-issue-136741.rs | 38 +++++++++++++ .../lint/unused/closure-body-issue-136741.stderr | 62 ++++++++++++++++++++++ 15 files changed, 160 insertions(+), 27 deletions(-) delete mode 100644 tests/ui/async-await/issues/issue-54752-async-block.stderr create mode 100644 tests/ui/lint/unused/closure-body-issue-136741.fixed create mode 100644 tests/ui/lint/unused/closure-body-issue-136741.rs create mode 100644 tests/ui/lint/unused/closure-body-issue-136741.stderr diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 2f398cea926..510f37f37e2 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -3526,7 +3526,7 @@ pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool { // All the chars that differ in capitalization are confusable (above): let confusable = iter::zip(found.chars(), suggested.chars()) .filter(|(f, s)| f != s) - .all(|(f, s)| (ascii_confusables.contains(&f) || ascii_confusables.contains(&s))); + .all(|(f, s)| ascii_confusables.contains(&f) || ascii_confusables.contains(&s)); confusable && found.to_lowercase() == suggested.to_lowercase() // FIXME: We sometimes suggest the same thing we already have, which is a // bug, but be defensive against that here. diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index d3942a1c816..a9eb1739f7f 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1,7 +1,7 @@ use std::iter; use rustc_ast::util::{classify, parser}; -use rustc_ast::{self as ast, ExprKind, HasAttrs as _, StmtKind}; +use rustc_ast::{self as ast, ExprKind, FnRetTy, HasAttrs as _, StmtKind}; use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{MultiSpan, pluralize}; @@ -599,6 +599,7 @@ enum UnusedDelimsCtx { AnonConst, MatchArmExpr, IndexExpr, + ClosureBody, } impl From for &'static str { @@ -620,6 +621,7 @@ impl From for &'static str { UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression", UnusedDelimsCtx::MatchArmExpr => "match arm expression", UnusedDelimsCtx::IndexExpr => "index expression", + UnusedDelimsCtx::ClosureBody => "closure body", } } } @@ -919,6 +921,11 @@ trait UnusedDelimLint { let (args_to_check, ctx) = match *call_or_other { Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg), MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg), + Closure(ref closure) + if matches!(closure.fn_decl.output, FnRetTy::Default(_)) => + { + (&[closure.body.clone()][..], UnusedDelimsCtx::ClosureBody) + } // actual catch-all arm _ => { return; @@ -1508,6 +1515,7 @@ impl UnusedDelimLint for UnusedBraces { && (ctx != UnusedDelimsCtx::AnonConst || (matches!(expr.kind, ast::ExprKind::Lit(_)) && !expr.span.from_expansion())) + && ctx != UnusedDelimsCtx::ClosureBody && !cx.sess().source_map().is_multiline(value.span) && value.attrs.is_empty() && !value.span.from_expansion() diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9ed7124a11c..d6cc98d505c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2206,7 +2206,7 @@ impl<'a> Parser<'a> { if self.look_ahead(1, |t| *t == token::Bang) && self.look_ahead(2, |t| t.is_ident()) { return IsMacroRulesItem::Yes { has_bang: true }; - } else if self.look_ahead(1, |t| (t.is_ident())) { + } else if self.look_ahead(1, |t| t.is_ident()) { // macro_rules foo self.dcx().emit_err(errors::MacroRulesMissingBang { span: macro_rules_span, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index e015eb7a636..8114021510e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -328,8 +328,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id); let mod_prefix = - mod_prefix.map_or_else(String::new, |res| (format!("{} ", res.descr()))); - + mod_prefix.map_or_else(String::new, |res| format!("{} ", res.descr())); (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None) }; diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 25b9c6e0e0e..b57234bbee9 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -82,7 +82,7 @@ unsafe fn skip_search( let needle = needle as u32; let last_idx = - match short_offset_runs.binary_search_by_key(&(needle << 11), |header| (header.0 << 11)) { + match short_offset_runs.binary_search_by_key(&(needle << 11), |header| header.0 << 11) { Ok(idx) => idx + 1, Err(idx) => idx, }; diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index 8ceaa3dc58e..e67afc7f5a8 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -169,7 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { let iter = self .unused_async_fns .iter() - .filter(|UnusedAsyncFn { def_id, .. }| (!self.async_fns_as_value.contains(def_id))); + .filter(|UnusedAsyncFn { def_id, .. }| !self.async_fns_as_value.contains(def_id)); for fun in iter { span_lint_hir_and_then( diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index bffbcf073ab..fe208c032f4 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -889,7 +889,7 @@ impl AdtVariantInfo { .enumerate() .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst)))) .collect::>(); - fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size))); + fields_size.sort_by(|(_, a_size), (_, b_size)| a_size.cmp(b_size)); Self { ind: i, @@ -898,7 +898,7 @@ impl AdtVariantInfo { } }) .collect::>(); - variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); + variants_size.sort_by(|a, b| b.size.cmp(&a.size)); variants_size } } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 3e879e0e4bb..933a32392bd 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1777,6 +1777,12 @@ impl<'test> TestCx<'test> { // Allow tests to use internal features. rustc.args(&["-A", "internal_features"]); + // Allow tests to have unused parens and braces. + // Add #![deny(unused_parens, unused_braces)] to the test file if you want to + // test that these lints are working. + rustc.args(&["-A", "unused_parens"]); + rustc.args(&["-A", "unused_braces"]); + if self.props.force_host { self.maybe_add_external_args(&mut rustc, &self.config.host_rustcflags); if !is_rustdoc { diff --git a/src/tools/test-float-parse/src/lib.rs b/src/tools/test-float-parse/src/lib.rs index 0bd4878f9a6..1321a3c3354 100644 --- a/src/tools/test-float-parse/src/lib.rs +++ b/src/tools/test-float-parse/src/lib.rs @@ -340,7 +340,7 @@ fn launch_tests(tests: &mut [TestInfo], cfg: &Config) -> Duration { for test in tests.iter_mut() { test.progress = Some(ui::Progress::new(test, &mut all_progress_bars)); ui::set_panic_hook(&all_progress_bars); - ((test.launch)(test, cfg)); + (test.launch)(test, cfg); } start.elapsed() diff --git a/src/tools/unicode-table-generator/src/range_search.rs b/src/tools/unicode-table-generator/src/range_search.rs index 02f9cf16d4d..4d1dd9b423b 100644 --- a/src/tools/unicode-table-generator/src/range_search.rs +++ b/src/tools/unicode-table-generator/src/range_search.rs @@ -80,7 +80,7 @@ unsafe fn skip_search( let needle = needle as u32; let last_idx = - match short_offset_runs.binary_search_by_key(&(needle << 11), |header| (header.0 << 11)) { + match short_offset_runs.binary_search_by_key(&(needle << 11), |header| header.0 << 11) { Ok(idx) => idx + 1, Err(idx) => idx, }; diff --git a/tests/ui/async-await/issues/issue-54752-async-block.rs b/tests/ui/async-await/issues/issue-54752-async-block.rs index 452b6794bee..164c1885da1 100644 --- a/tests/ui/async-await/issues/issue-54752-async-block.rs +++ b/tests/ui/async-await/issues/issue-54752-async-block.rs @@ -4,4 +4,3 @@ //@ pp-exact fn main() { let _a = (async { }); } -//~^ WARNING unnecessary parentheses around assigned value diff --git a/tests/ui/async-await/issues/issue-54752-async-block.stderr b/tests/ui/async-await/issues/issue-54752-async-block.stderr deleted file mode 100644 index 8cc849dd985..00000000000 --- a/tests/ui/async-await/issues/issue-54752-async-block.stderr +++ /dev/null @@ -1,15 +0,0 @@ -warning: unnecessary parentheses around assigned value - --> $DIR/issue-54752-async-block.rs:6:22 - | -LL | fn main() { let _a = (async { }); } - | ^ ^ - | - = note: `#[warn(unused_parens)]` on by default -help: remove these parentheses - | -LL - fn main() { let _a = (async { }); } -LL + fn main() { let _a = async { }; } - | - -warning: 1 warning emitted - diff --git a/tests/ui/lint/unused/closure-body-issue-136741.fixed b/tests/ui/lint/unused/closure-body-issue-136741.fixed new file mode 100644 index 00000000000..2ded52544b9 --- /dev/null +++ b/tests/ui/lint/unused/closure-body-issue-136741.fixed @@ -0,0 +1,36 @@ +//@ run-rustfix +// ignore-tidy-linelength +#![deny(unused_parens)] +#![deny(unused_braces)] + +fn long_expr_that_does_not_require_braces_long_expr_that_does_not_require_braces_long_expr_that_does_not_require_braces() +{} + +fn func(f: impl FnOnce()) { + f() +} + +pub fn main() { + let _closure = |x: i32, y: i32| { x * (x + (y * 2)) }; + let _ = || 0 == 0; //~ ERROR unnecessary parentheses around closure body + let _ = (0..).find(|n| n % 2 == 0); //~ ERROR unnecessary parentheses around closure body + let _ = (0..).find(|n| {n % 2 == 0}); + + // multiple lines of code will not lint with braces + let _ = (0..).find(|n| { + n % 2 == 0 + }); + + // multiple lines of code will lint with parentheses + let _ = (0..).find(|n| n % 2 == 0); + + let _ = || { + _ = 0; + 0 == 0 //~ ERROR unnecessary parentheses around block return value + }; + + // long expressions will not lint with braces + func(|| { + long_expr_that_does_not_require_braces_long_expr_that_does_not_require_braces_long_expr_that_does_not_require_braces() + }) +} diff --git a/tests/ui/lint/unused/closure-body-issue-136741.rs b/tests/ui/lint/unused/closure-body-issue-136741.rs new file mode 100644 index 00000000000..4eac981ec2e --- /dev/null +++ b/tests/ui/lint/unused/closure-body-issue-136741.rs @@ -0,0 +1,38 @@ +//@ run-rustfix +// ignore-tidy-linelength +#![deny(unused_parens)] +#![deny(unused_braces)] + +fn long_expr_that_does_not_require_braces_long_expr_that_does_not_require_braces_long_expr_that_does_not_require_braces() +{} + +fn func(f: impl FnOnce()) { + f() +} + +pub fn main() { + let _closure = |x: i32, y: i32| { x * (x + (y * 2)) }; + let _ = || (0 == 0); //~ ERROR unnecessary parentheses around closure body + let _ = (0..).find(|n| (n % 2 == 0)); //~ ERROR unnecessary parentheses around closure body + let _ = (0..).find(|n| {n % 2 == 0}); + + // multiple lines of code will not lint with braces + let _ = (0..).find(|n| { + n % 2 == 0 + }); + + // multiple lines of code will lint with parentheses + let _ = (0..).find(|n| ( //~ ERROR unnecessary parentheses around closure body + n % 2 == 0 + )); + + let _ = || { + _ = 0; + (0 == 0) //~ ERROR unnecessary parentheses around block return value + }; + + // long expressions will not lint with braces + func(|| { + long_expr_that_does_not_require_braces_long_expr_that_does_not_require_braces_long_expr_that_does_not_require_braces() + }) +} diff --git a/tests/ui/lint/unused/closure-body-issue-136741.stderr b/tests/ui/lint/unused/closure-body-issue-136741.stderr new file mode 100644 index 00000000000..2ea872c08c7 --- /dev/null +++ b/tests/ui/lint/unused/closure-body-issue-136741.stderr @@ -0,0 +1,62 @@ +error: unnecessary parentheses around closure body + --> $DIR/closure-body-issue-136741.rs:15:16 + | +LL | let _ = || (0 == 0); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/closure-body-issue-136741.rs:3:9 + | +LL | #![deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - let _ = || (0 == 0); +LL + let _ = || 0 == 0; + | + +error: unnecessary parentheses around closure body + --> $DIR/closure-body-issue-136741.rs:16:28 + | +LL | let _ = (0..).find(|n| (n % 2 == 0)); + | ^ ^ + | +help: remove these parentheses + | +LL - let _ = (0..).find(|n| (n % 2 == 0)); +LL + let _ = (0..).find(|n| n % 2 == 0); + | + +error: unnecessary parentheses around closure body + --> $DIR/closure-body-issue-136741.rs:25:28 + | +LL | let _ = (0..).find(|n| ( + | _____________________________^ +LL | | n % 2 == 0 + | | ________^__________^ + | ||________| + | | +LL | | )); + | |_____^ + | +help: remove these parentheses + | +LL - let _ = (0..).find(|n| ( +LL - n % 2 == 0 +LL + let _ = (0..).find(|n| n % 2 == 0); + | + +error: unnecessary parentheses around block return value + --> $DIR/closure-body-issue-136741.rs:31:9 + | +LL | (0 == 0) + | ^ ^ + | +help: remove these parentheses + | +LL - (0 == 0) +LL + 0 == 0 + | + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 0a64bfd6858606fae0b112673d4a28b2d6f149f8 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 29 Jun 2025 11:40:59 +0800 Subject: Remove uncessary parens in closure body with unused lint --- clippy_lints/src/unused_async.rs | 2 +- clippy_utils/src/ty/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 8ceaa3dc58e..e67afc7f5a8 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -169,7 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { let iter = self .unused_async_fns .iter() - .filter(|UnusedAsyncFn { def_id, .. }| (!self.async_fns_as_value.contains(def_id))); + .filter(|UnusedAsyncFn { def_id, .. }| !self.async_fns_as_value.contains(def_id)); for fun in iter { span_lint_hir_and_then( diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index bffbcf073ab..fe208c032f4 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -889,7 +889,7 @@ impl AdtVariantInfo { .enumerate() .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst)))) .collect::>(); - fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size))); + fields_size.sort_by(|(_, a_size), (_, b_size)| a_size.cmp(b_size)); Self { ind: i, @@ -898,7 +898,7 @@ impl AdtVariantInfo { } }) .collect::>(); - variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); + variants_size.sort_by(|a, b| b.size.cmp(&a.size)); variants_size } } -- cgit 1.4.1-3-g733a5 From bc2001f15867b2e1496f8973a7eafb704e6fce2f Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Thu, 3 Jul 2025 15:18:57 -0400 Subject: Refactor nan tests --- library/coretests/tests/floats/f128.rs | 14 -------------- library/coretests/tests/floats/f16.rs | 14 -------------- library/coretests/tests/floats/f32.rs | 14 -------------- library/coretests/tests/floats/f64.rs | 14 -------------- library/coretests/tests/floats/mod.rs | 23 +++++++++++++++++++++++ 5 files changed, 23 insertions(+), 56 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index cf78e8796a0..38df09a91c1 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -55,20 +55,6 @@ fn test_num_f128() { // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_nan() { - let nan: f128 = f128::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert!(!nan.is_normal()); - assert_eq!(Fp::Nan, nan.classify()); - // Ensure the quiet bit is set. - assert!(nan.to_bits() & (1 << (f128::MANTISSA_DIGITS - 2)) != 0); -} - #[test] fn test_infinity() { let inf: f128 = f128::INFINITY; diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index 9e91b654304..f6749d796cc 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -51,20 +51,6 @@ fn test_num_f16() { // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_nan() { - let nan: f16 = f16::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert!(!nan.is_normal()); - assert_eq!(Fp::Nan, nan.classify()); - // Ensure the quiet bit is set. - assert!(nan.to_bits() & (1 << (f16::MANTISSA_DIGITS - 2)) != 0); -} - #[test] fn test_infinity() { let inf: f16 = f16::INFINITY; diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index d2724d12e39..f5d5723fea4 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -35,20 +35,6 @@ fn test_num_f32() { super::test_num(10f32, 2f32); } -#[test] -fn test_nan() { - let nan: f32 = f32::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(!nan.is_normal()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert_eq!(Fp::Nan, nan.classify()); - // Ensure the quiet bit is set. - assert!(nan.to_bits() & (1 << (f32::MANTISSA_DIGITS - 2)) != 0); -} - #[test] fn test_infinity() { let inf: f32 = f32::INFINITY; diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index b2b2393a527..34af87c241e 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -30,20 +30,6 @@ fn test_num_f64() { super::test_num(10f64, 2f64); } -#[test] -fn test_nan() { - let nan: f64 = f64::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(!nan.is_normal()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert_eq!(Fp::Nan, nan.classify()); - // Ensure the quiet bit is set. - assert!(nan.to_bits() & (1 << (f64::MANTISSA_DIGITS - 2)) != 0); -} - #[test] fn test_infinity() { let inf: f64 = f64::INFINITY; diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 6b4f586fa9b..36743a7d6df 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::num::FpCategory as Fp; use std::ops::{Add, Div, Mul, Rem, Sub}; /// Set the default tolerance for float comparison based on the type. @@ -187,6 +188,8 @@ macro_rules! float_test { mod const_ { #[allow(unused)] use super::Approx; + #[allow(unused)] + use std::num::FpCategory as Fp; // Shadow the runtime versions of the macro with const-compatible versions. #[allow(unused)] use $crate::floats::{ @@ -250,6 +253,26 @@ mod f16; mod f32; mod f64; +float_test! { + name: nan, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + let nan: Float = Float::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(!nan.is_normal()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert!(matches!(nan.classify(), Fp::Nan)); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (Float::MANTISSA_DIGITS - 2)) != 0); + } +} + float_test! { name: min, attrs: { -- cgit 1.4.1-3-g733a5 From e726c643e8319ee5eb2a8be45eb65f7600be2d5d Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 10 Jul 2025 04:58:14 +0000 Subject: Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index a9394819541..0af6b8c6fc7 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -688ea65df6a47866d0f72a00f1e18b47a7edf83b +32cd9114712a24010b0583624dc52ac302194128 -- cgit 1.4.1-3-g733a5 From 4dc954f88256314c4c03a4cb43eee838bc3762eb Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 10 Jul 2025 13:50:02 +0800 Subject: remove unnecessary parens in rust-analyzer --- src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d258c5d8191..37f83f6dee6 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 @@ -25,7 +25,7 @@ impl flags::Scip { eprintln!("Generating SCIP start..."); let now = Instant::now(); - let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}")); + let no_progress = &|s| eprintln!("rust-analyzer: Loading {s}"); let root = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize(); -- cgit 1.4.1-3-g733a5 From 87a41210d4ec4f43a1e015dfea44ef3f686c9872 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 9 Jul 2025 20:29:51 +0800 Subject: Only provide `is_windows_msvc` to gate on windows-msvc And not both `is_windows_msvc` and `is_msvc`. --- src/tools/run-make-support/src/artifact_names.rs | 6 +++--- .../run-make-support/src/external_deps/c_build.rs | 23 +++++++++++----------- .../src/external_deps/c_cxx_compiler/cc.rs | 6 +++--- .../src/external_deps/c_cxx_compiler/extras.rs | 6 +++--- .../run-make-support/src/external_deps/rustc.rs | 4 ++-- src/tools/run-make-support/src/lib.rs | 2 +- src/tools/run-make-support/src/linker.rs | 4 ++-- src/tools/run-make-support/src/targets.rs | 12 +++-------- 8 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/tools/run-make-support/src/artifact_names.rs b/src/tools/run-make-support/src/artifact_names.rs index b0d588d3550..a889b30e145 100644 --- a/src/tools/run-make-support/src/artifact_names.rs +++ b/src/tools/run-make-support/src/artifact_names.rs @@ -2,7 +2,7 @@ //! libraries which are target-dependent. use crate::target; -use crate::targets::is_msvc; +use crate::targets::is_windows_msvc; /// Construct the static library name based on the target. #[track_caller] @@ -10,7 +10,7 @@ use crate::targets::is_msvc; pub fn static_lib_name(name: &str) -> String { assert!(!name.contains(char::is_whitespace), "static library name cannot contain whitespace"); - if is_msvc() { format!("{name}.lib") } else { format!("lib{name}.a") } + if is_windows_msvc() { format!("{name}.lib") } else { format!("lib{name}.a") } } /// Construct the dynamic library name based on the target. @@ -45,7 +45,7 @@ pub fn dynamic_lib_extension() -> &'static str { #[track_caller] #[must_use] pub fn msvc_import_dynamic_lib_name(name: &str) -> String { - assert!(is_msvc(), "this function is exclusive to MSVC"); + assert!(is_windows_msvc(), "this function is exclusive to MSVC"); assert!(!name.contains(char::is_whitespace), "import library name cannot contain whitespace"); format!("{name}.dll.lib") diff --git a/src/tools/run-make-support/src/external_deps/c_build.rs b/src/tools/run-make-support/src/external_deps/c_build.rs index 9dd30713f95..ecbf5ba8fe0 100644 --- a/src/tools/run-make-support/src/external_deps/c_build.rs +++ b/src/tools/run-make-support/src/external_deps/c_build.rs @@ -4,7 +4,7 @@ use crate::artifact_names::{dynamic_lib_name, static_lib_name}; use crate::external_deps::c_cxx_compiler::{cc, cxx}; use crate::external_deps::llvm::llvm_ar; use crate::path_helpers::path; -use crate::targets::{is_darwin, is_msvc, is_windows}; +use crate::targets::{is_darwin, is_windows, is_windows_msvc}; // FIXME(Oneirical): These native build functions should take a Path-based generic. @@ -24,12 +24,12 @@ pub fn build_native_static_lib_optimized(lib_name: &str) -> PathBuf { #[track_caller] fn build_native_static_lib_internal(lib_name: &str, optimzed: bool) -> PathBuf { - let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; + let obj_file = if is_windows_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; let src = format!("{lib_name}.c"); let lib_path = static_lib_name(lib_name); let mut cc = cc(); - if !is_msvc() { + if !is_windows_msvc() { cc.arg("-v"); } if optimzed { @@ -37,7 +37,7 @@ fn build_native_static_lib_internal(lib_name: &str, optimzed: bool) -> PathBuf { } cc.arg("-c").out_exe(&obj_file).input(src).optimize().run(); - let obj_file = if is_msvc() { + let obj_file = if is_windows_msvc() { PathBuf::from(format!("{lib_name}.obj")) } else { PathBuf::from(format!("{lib_name}.o")) @@ -50,16 +50,17 @@ fn build_native_static_lib_internal(lib_name: &str, optimzed: bool) -> PathBuf { /// [`std::env::consts::DLL_PREFIX`] and [`std::env::consts::DLL_EXTENSION`]. #[track_caller] pub fn build_native_dynamic_lib(lib_name: &str) -> PathBuf { - let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; + let obj_file = if is_windows_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; let src = format!("{lib_name}.c"); let lib_path = dynamic_lib_name(lib_name); - if is_msvc() { + if is_windows_msvc() { cc().arg("-c").out_exe(&obj_file).input(src).run(); } else { cc().arg("-v").arg("-c").out_exe(&obj_file).input(src).run(); }; - let obj_file = if is_msvc() { format!("{lib_name}.obj") } else { format!("{lib_name}.o") }; - if is_msvc() { + let obj_file = + if is_windows_msvc() { format!("{lib_name}.obj") } else { format!("{lib_name}.o") }; + if is_windows_msvc() { let out_arg = format!("-out:{lib_path}"); cc().input(&obj_file).args(&["-link", "-dll", &out_arg]).run(); } else if is_darwin() { @@ -79,15 +80,15 @@ pub fn build_native_dynamic_lib(lib_name: &str) -> PathBuf { /// Built from a C++ file. #[track_caller] pub fn build_native_static_lib_cxx(lib_name: &str) -> PathBuf { - let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; + let obj_file = if is_windows_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; let src = format!("{lib_name}.cpp"); let lib_path = static_lib_name(lib_name); - if is_msvc() { + if is_windows_msvc() { cxx().arg("-EHs").arg("-c").out_exe(&obj_file).input(src).run(); } else { cxx().arg("-c").out_exe(&obj_file).input(src).run(); }; - let obj_file = if is_msvc() { + let obj_file = if is_windows_msvc() { PathBuf::from(format!("{lib_name}.obj")) } else { PathBuf::from(format!("{lib_name}.o")) diff --git a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs index 0e6d6ea6075..31469e669e1 100644 --- a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs +++ b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs @@ -1,7 +1,7 @@ use std::path::Path; use crate::command::Command; -use crate::{env_var, is_msvc}; +use crate::{env_var, is_windows_msvc}; /// Construct a new platform-specific C compiler invocation. /// @@ -82,7 +82,7 @@ impl Cc { pub fn out_exe(&mut self, name: &str) -> &mut Self { let mut path = std::path::PathBuf::from(name); - if is_msvc() { + if is_windows_msvc() { path.set_extension("exe"); let fe_path = path.clone(); path.set_extension(""); @@ -108,7 +108,7 @@ impl Cc { /// Optimize the output. /// Equivalent to `-O3` for GNU-compatible linkers or `-O2` for MSVC linkers. pub fn optimize(&mut self) -> &mut Self { - if is_msvc() { + if is_windows_msvc() { self.cmd.arg("-O2"); } else { self.cmd.arg("-O3"); diff --git a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs index c0317633873..ac7392641c0 100644 --- a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs +++ b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs @@ -1,9 +1,9 @@ -use crate::{is_msvc, is_win7, is_windows, uname}; +use crate::{is_win7, is_windows, is_windows_msvc, uname}; /// `EXTRACFLAGS` pub fn extra_c_flags() -> Vec<&'static str> { if is_windows() { - if is_msvc() { + if is_windows_msvc() { let mut libs = vec!["ws2_32.lib", "userenv.lib", "bcrypt.lib", "ntdll.lib", "synchronization.lib"]; if is_win7() { @@ -29,7 +29,7 @@ pub fn extra_c_flags() -> Vec<&'static str> { /// `EXTRACXXFLAGS` pub fn extra_cxx_flags() -> Vec<&'static str> { if is_windows() { - if is_msvc() { vec![] } else { vec!["-lstdc++"] } + if is_windows_msvc() { vec![] } else { vec!["-lstdc++"] } } else { match &uname()[..] { "Darwin" => vec!["-lc++"], diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 72a1e062a38..1ea549ca7ea 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -6,7 +6,7 @@ use crate::command::Command; use crate::env::env_var; use crate::path_helpers::cwd; use crate::util::set_host_compiler_dylib_path; -use crate::{is_aix, is_darwin, is_msvc, is_windows, target, uname}; +use crate::{is_aix, is_darwin, is_windows, is_windows_msvc, target, uname}; /// Construct a new `rustc` invocation. This will automatically set the library /// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this. @@ -377,7 +377,7 @@ impl Rustc { // So we end up with the following hack: we link use static:-bundle to only // link the parts of libstdc++ that we actually use, which doesn't include // the dependency on the pthreads DLL. - if !is_msvc() { + if !is_windows_msvc() { self.cmd.arg("-lstatic:-bundle=stdc++"); }; } else if is_darwin() { diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 69054396229..29cd6c4ad15 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -83,6 +83,6 @@ pub use crate::string::{ }; // Helpers for checking target information. pub use crate::targets::{ - apple_os, is_aix, is_darwin, is_msvc, is_win7, is_windows, is_windows_gnu, is_windows_msvc, + apple_os, is_aix, is_darwin, is_win7, is_windows, is_windows_gnu, is_windows_msvc, llvm_components_contain, target, uname, }; diff --git a/src/tools/run-make-support/src/linker.rs b/src/tools/run-make-support/src/linker.rs index 89093cf0113..b2893ad88fe 100644 --- a/src/tools/run-make-support/src/linker.rs +++ b/src/tools/run-make-support/src/linker.rs @@ -1,6 +1,6 @@ use regex::Regex; -use crate::{Rustc, is_msvc}; +use crate::{Rustc, is_windows_msvc}; /// Asserts that `rustc` uses LLD for linking when executed. pub fn assert_rustc_uses_lld(rustc: &mut Rustc) { @@ -22,7 +22,7 @@ pub fn assert_rustc_doesnt_use_lld(rustc: &mut Rustc) { fn get_stderr_with_linker_messages(rustc: &mut Rustc) -> String { // lld-link is used if msvc, otherwise a gnu-compatible lld is used. - let linker_version_flag = if is_msvc() { "--version" } else { "-Wl,-v" }; + let linker_version_flag = if is_windows_msvc() { "--version" } else { "-Wl,-v" }; let output = rustc.arg("-Wlinker-messages").link_arg(linker_version_flag).run(); output.stderr_utf8() diff --git a/src/tools/run-make-support/src/targets.rs b/src/tools/run-make-support/src/targets.rs index 1ab2e2ab2be..b20e12561fb 100644 --- a/src/tools/run-make-support/src/targets.rs +++ b/src/tools/run-make-support/src/targets.rs @@ -16,10 +16,10 @@ pub fn is_windows() -> bool { target().contains("windows") } -/// Check if target uses msvc. +/// Check if target is windows-msvc. #[must_use] -pub fn is_msvc() -> bool { - target().contains("msvc") +pub fn is_windows_msvc() -> bool { + target().ends_with("windows-msvc") } /// Check if target is windows-gnu. @@ -28,12 +28,6 @@ pub fn is_windows_gnu() -> bool { target().ends_with("windows-gnu") } -/// Check if target is windows-msvc. -#[must_use] -pub fn is_windows_msvc() -> bool { - target().ends_with("windows-msvc") -} - /// Check if target is win7. #[must_use] pub fn is_win7() -> bool { -- cgit 1.4.1-3-g733a5 From 3f4f23af617171e2e03e867a61b952d56166143b Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 9 Jul 2025 20:29:33 +0800 Subject: Use `is_windows_msvc` instead of `is_msvc` in run-make tests --- tests/run-make/archive-duplicate-names/rmake.rs | 8 ++++---- tests/run-make/c-link-to-rust-dylib/rmake.rs | 6 ++++-- tests/run-make/c-unwind-abi-catch-lib-panic/rmake.rs | 6 +++--- tests/run-make/cdylib-dylib-linkage/rmake.rs | 4 ++-- tests/run-make/cdylib/rmake.rs | 4 ++-- tests/run-make/compiler-rt-works-on-mingw/rmake.rs | 2 +- tests/run-make/link-args-order/rmake.rs | 4 ++-- tests/run-make/link-dedup/rmake.rs | 4 ++-- tests/run-make/native-link-modifier-bundle/rmake.rs | 4 ++-- tests/run-make/native-link-modifier-whole-archive/rmake.rs | 6 +++--- tests/run-make/pointer-auth-link-with-c/rmake.rs | 6 +++--- tests/run-make/print-native-static-libs/rmake.rs | 6 +++--- tests/run-make/raw-dylib-alt-calling-convention/rmake.rs | 6 ++++-- tests/run-make/raw-dylib-import-name-type/rmake.rs | 4 ++-- tests/run-make/raw-dylib-inline-cross-dylib/rmake.rs | 4 ++-- tests/run-make/raw-dylib-link-ordinal/rmake.rs | 4 ++-- tests/run-make/raw-dylib-stdcall-ordinal/rmake.rs | 4 ++-- tests/run-make/rlib-format-packed-bundled-libs/rmake.rs | 6 +++--- tests/run-make/split-debuginfo/rmake.rs | 6 +++--- tests/run-make/static-dylib-by-default/rmake.rs | 6 +++--- tests/run-make/staticlib-dylib-linkage/rmake.rs | 4 ++-- tests/run-make/symbol-visibility/rmake.rs | 6 +++--- 22 files changed, 57 insertions(+), 53 deletions(-) diff --git a/tests/run-make/archive-duplicate-names/rmake.rs b/tests/run-make/archive-duplicate-names/rmake.rs index 62a35566199..0c352b3ae1b 100644 --- a/tests/run-make/archive-duplicate-names/rmake.rs +++ b/tests/run-make/archive-duplicate-names/rmake.rs @@ -6,7 +6,7 @@ //@ ignore-cross-compile // Reason: the compiled binary is executed -use run_make_support::{cc, is_msvc, llvm_ar, rfs, run, rustc}; +use run_make_support::{cc, is_windows_msvc, llvm_ar, rfs, run, rustc}; fn main() { rfs::create_dir("a"); @@ -15,7 +15,7 @@ fn main() { compile_obj_force_foo("b", "bar"); let mut ar = llvm_ar(); ar.obj_to_ar().arg("libfoo.a"); - if is_msvc() { + if is_windows_msvc() { ar.arg("a/foo.obj").arg("b/foo.obj").run(); } else { ar.arg("a/foo.o").arg("b/foo.o").run(); @@ -27,9 +27,9 @@ fn main() { #[track_caller] pub fn compile_obj_force_foo(dir: &str, lib_name: &str) { - let obj_file = if is_msvc() { format!("{dir}/foo") } else { format!("{dir}/foo.o") }; + let obj_file = if is_windows_msvc() { format!("{dir}/foo") } else { format!("{dir}/foo.o") }; let src = format!("{lib_name}.c"); - if is_msvc() { + if is_windows_msvc() { cc().arg("-c").out_exe(&obj_file).input(src).run(); } else { cc().arg("-v").arg("-c").out_exe(&obj_file).input(src).run(); diff --git a/tests/run-make/c-link-to-rust-dylib/rmake.rs b/tests/run-make/c-link-to-rust-dylib/rmake.rs index 3a48af8a366..ece0c81d2da 100644 --- a/tests/run-make/c-link-to-rust-dylib/rmake.rs +++ b/tests/run-make/c-link-to-rust-dylib/rmake.rs @@ -3,12 +3,14 @@ //@ ignore-cross-compile -use run_make_support::{cc, cwd, dynamic_lib_extension, is_msvc, rfs, run, run_fail, rustc}; +use run_make_support::{ + cc, cwd, dynamic_lib_extension, is_windows_msvc, rfs, run, run_fail, rustc, +}; fn main() { rustc().input("foo.rs").run(); - if is_msvc() { + if is_windows_msvc() { let lib = "foo.dll.lib"; cc().input("bar.c").arg(lib).out_exe("bar").run(); diff --git a/tests/run-make/c-unwind-abi-catch-lib-panic/rmake.rs b/tests/run-make/c-unwind-abi-catch-lib-panic/rmake.rs index 62e1748b6fb..ba5fe589acb 100644 --- a/tests/run-make/c-unwind-abi-catch-lib-panic/rmake.rs +++ b/tests/run-make/c-unwind-abi-catch-lib-panic/rmake.rs @@ -8,11 +8,11 @@ //@ needs-unwind // Reason: this test exercises unwinding a panic -use run_make_support::{cc, is_msvc, llvm_ar, run, rustc, static_lib_name}; +use run_make_support::{cc, is_windows_msvc, llvm_ar, run, rustc, static_lib_name}; fn main() { // Compile `add.c` into an object file. - if is_msvc() { + if is_windows_msvc() { cc().arg("-c").out_exe("add").input("add.c").run(); } else { cc().arg("-v").arg("-c").out_exe("add.o").input("add.c").run(); @@ -24,7 +24,7 @@ fn main() { rustc().emit("obj").input("panic.rs").run(); // Now, create an archive using these two objects. - if is_msvc() { + if is_windows_msvc() { llvm_ar().obj_to_ar().args(&[&static_lib_name("add"), "add.obj", "panic.o"]).run(); } else { llvm_ar().obj_to_ar().args(&[&static_lib_name("add"), "add.o", "panic.o"]).run(); diff --git a/tests/run-make/cdylib-dylib-linkage/rmake.rs b/tests/run-make/cdylib-dylib-linkage/rmake.rs index a8fd8e2d168..3c145d9f99c 100644 --- a/tests/run-make/cdylib-dylib-linkage/rmake.rs +++ b/tests/run-make/cdylib-dylib-linkage/rmake.rs @@ -9,7 +9,7 @@ use run_make_support::{ bin_name, cc, dynamic_lib_extension, dynamic_lib_name, filename_contains, has_extension, - has_prefix, has_suffix, is_msvc, msvc_import_dynamic_lib_name, path, run, rustc, + has_prefix, has_suffix, is_windows_msvc, msvc_import_dynamic_lib_name, path, run, rustc, shallow_find_files, target, }; @@ -19,7 +19,7 @@ fn main() { let sysroot = rustc().print("sysroot").run().stdout_utf8(); let sysroot = sysroot.trim(); let target_sysroot = path(sysroot).join("lib/rustlib").join(target()).join("lib"); - if is_msvc() { + if is_windows_msvc() { let mut libs = shallow_find_files(&target_sysroot, |path| { has_prefix(path, "libstd-") && has_suffix(path, ".dll.lib") }); diff --git a/tests/run-make/cdylib/rmake.rs b/tests/run-make/cdylib/rmake.rs index 21b83d1b1a9..21fd8b486c4 100644 --- a/tests/run-make/cdylib/rmake.rs +++ b/tests/run-make/cdylib/rmake.rs @@ -10,13 +10,13 @@ //@ ignore-cross-compile -use run_make_support::{cc, cwd, dynamic_lib_name, is_msvc, rfs, run, rustc}; +use run_make_support::{cc, cwd, dynamic_lib_name, is_windows_msvc, rfs, run, rustc}; fn main() { rustc().input("bar.rs").run(); rustc().input("foo.rs").run(); - if is_msvc() { + if is_windows_msvc() { cc().input("foo.c").arg("foo.dll.lib").out_exe("foo").run(); } else { cc().input("foo.c").arg("-lfoo").library_search_path(cwd()).output("foo").run(); diff --git a/tests/run-make/compiler-rt-works-on-mingw/rmake.rs b/tests/run-make/compiler-rt-works-on-mingw/rmake.rs index f1b41f96312..b15e56db1bb 100644 --- a/tests/run-make/compiler-rt-works-on-mingw/rmake.rs +++ b/tests/run-make/compiler-rt-works-on-mingw/rmake.rs @@ -5,7 +5,7 @@ //@ only-windows-gnu -use run_make_support::{cxx, is_msvc, llvm_ar, run, rustc, static_lib_name}; +use run_make_support::{cxx, llvm_ar, run, rustc, static_lib_name}; fn main() { cxx().input("foo.cpp").arg("-c").out_exe("foo.o").run(); diff --git a/tests/run-make/link-args-order/rmake.rs b/tests/run-make/link-args-order/rmake.rs index 7a67c12f74c..e3c730bfec5 100644 --- a/tests/run-make/link-args-order/rmake.rs +++ b/tests/run-make/link-args-order/rmake.rs @@ -6,10 +6,10 @@ // checks that linker arguments remain intact and in the order they were originally passed in. // See https://github.com/rust-lang/rust/pull/70665 -use run_make_support::{is_msvc, rustc}; +use run_make_support::{is_windows_msvc, rustc}; fn main() { - let linker = if is_msvc() { "msvc" } else { "ld" }; + let linker = if is_windows_msvc() { "msvc" } else { "ld" }; rustc() .input("empty.rs") diff --git a/tests/run-make/link-dedup/rmake.rs b/tests/run-make/link-dedup/rmake.rs index 874e6e0083b..84fdf87e9c7 100644 --- a/tests/run-make/link-dedup/rmake.rs +++ b/tests/run-make/link-dedup/rmake.rs @@ -9,7 +9,7 @@ use std::fmt::Write; -use run_make_support::{is_msvc, rustc, target}; +use run_make_support::{is_windows_msvc, rustc, target}; fn main() { rustc().input("depa.rs").run(); @@ -32,7 +32,7 @@ fn main() { fn needle_from_libs(libs: &[&str]) -> String { let mut needle = String::new(); for lib in libs { - if is_msvc() { + if is_windows_msvc() { needle.write_fmt(format_args!(r#""{lib}.lib" "#)).unwrap(); } else if target().contains("wasm") { needle.write_fmt(format_args!(r#""-l" "{lib}" "#)).unwrap(); diff --git a/tests/run-make/native-link-modifier-bundle/rmake.rs b/tests/run-make/native-link-modifier-bundle/rmake.rs index 058b66b15f1..64ee7733498 100644 --- a/tests/run-make/native-link-modifier-bundle/rmake.rs +++ b/tests/run-make/native-link-modifier-bundle/rmake.rs @@ -20,7 +20,7 @@ // Reason: cross-compilation fails to export native symbols use run_make_support::{ - build_native_static_lib, dynamic_lib_name, is_msvc, llvm_nm, rust_lib_name, rustc, + build_native_static_lib, dynamic_lib_name, is_windows_msvc, llvm_nm, rust_lib_name, rustc, static_lib_name, }; @@ -60,7 +60,7 @@ fn main() { .assert_stdout_contains_regex("U _*native_func"); // This part of the test does not function on Windows MSVC - no symbols are printed. - if !is_msvc() { + if !is_windows_msvc() { // Build a cdylib, `native-staticlib` will not appear on the linker line because it was // bundled previously. The cdylib will contain the `native_func` symbol in the end. rustc() diff --git a/tests/run-make/native-link-modifier-whole-archive/rmake.rs b/tests/run-make/native-link-modifier-whole-archive/rmake.rs index b8b814043e5..90b0203e278 100644 --- a/tests/run-make/native-link-modifier-whole-archive/rmake.rs +++ b/tests/run-make/native-link-modifier-whole-archive/rmake.rs @@ -10,11 +10,11 @@ // Reason: compiling C++ code does not work well when cross-compiling // plus, the compiled binary is executed -use run_make_support::{cxx, is_msvc, llvm_ar, run, run_with_args, rustc, static_lib_name}; +use run_make_support::{cxx, is_windows_msvc, llvm_ar, run, run_with_args, rustc, static_lib_name}; fn main() { let mut cxx = cxx(); - if is_msvc() { + if is_windows_msvc() { cxx.arg("-EHs"); } cxx.input("c_static_lib_with_constructor.cpp") @@ -24,7 +24,7 @@ fn main() { let mut llvm_ar = llvm_ar(); llvm_ar.obj_to_ar(); - if is_msvc() { + if is_windows_msvc() { llvm_ar .output_input( static_lib_name("c_static_lib_with_constructor"), diff --git a/tests/run-make/pointer-auth-link-with-c/rmake.rs b/tests/run-make/pointer-auth-link-with-c/rmake.rs index 7b6dff10eae..a4d7454e575 100644 --- a/tests/run-make/pointer-auth-link-with-c/rmake.rs +++ b/tests/run-make/pointer-auth-link-with-c/rmake.rs @@ -9,7 +9,7 @@ //@ ignore-cross-compile // Reason: the compiled binary is executed -use run_make_support::{build_native_static_lib, cc, is_msvc, llvm_ar, run, rustc}; +use run_make_support::{build_native_static_lib, cc, is_windows_msvc, llvm_ar, run, rustc}; fn main() { build_native_static_lib("test"); @@ -21,7 +21,7 @@ fn main() { .input("test.c") .arg("-mbranch-protection=bti+pac-ret+leaf") .run(); - let obj_file = if is_msvc() { "test.obj" } else { "test" }; + let obj_file = if is_windows_msvc() { "test.obj" } else { "test" }; llvm_ar().obj_to_ar().output_input("libtest.a", &obj_file).run(); rustc().arg("-Zbranch-protection=bti,pac-ret,leaf").input("test.rs").run(); run("test"); @@ -33,7 +33,7 @@ fn main() { // .input("test.c") // .arg("-mbranch-protection=bti+pac-ret+pc+leaf") // .run(); - // let obj_file = if is_msvc() { "test.obj" } else { "test" }; + // let obj_file = if is_windows_msvc() { "test.obj" } else { "test" }; // llvm_ar().obj_to_ar().output_input("libtest.a", &obj_file).run(); // rustc().arg("-Zbranch-protection=bti,pac-ret,pc,leaf").input("test.rs").run(); // run("test"); diff --git a/tests/run-make/print-native-static-libs/rmake.rs b/tests/run-make/print-native-static-libs/rmake.rs index a51ac934c72..4502c874cae 100644 --- a/tests/run-make/print-native-static-libs/rmake.rs +++ b/tests/run-make/print-native-static-libs/rmake.rs @@ -12,7 +12,7 @@ //@ ignore-cross-compile //@ ignore-wasm -use run_make_support::{is_msvc, rustc}; +use run_make_support::{is_windows_msvc, rustc}; fn main() { // build supporting crate @@ -41,9 +41,9 @@ fn main() { ($lib:literal in $args:ident) => {{ let lib = format!( "{}{}{}", - if !is_msvc() { "-l" } else { "" }, + if !is_windows_msvc() { "-l" } else { "" }, $lib, - if !is_msvc() { "" } else { ".lib" }, + if !is_windows_msvc() { "" } else { ".lib" }, ); let found = $args.contains(&&*lib); assert!(found, "unable to find lib `{}` in those linker args: {:?}", lib, $args); diff --git a/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs b/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs index 1a1622f2754..0843c6beae8 100644 --- a/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs +++ b/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs @@ -9,7 +9,9 @@ //@ only-x86 //@ only-windows -use run_make_support::{build_native_dynamic_lib, diff, is_msvc, run, run_with_args, rustc}; +use run_make_support::{ + build_native_dynamic_lib, diff, is_windows_msvc, run, run_with_args, rustc, +}; fn main() { rustc() @@ -21,7 +23,7 @@ fn main() { build_native_dynamic_lib("extern"); let out = run("driver").stdout_utf8(); diff().expected_file("output.txt").actual_text("actual", out).normalize(r#"\r"#, "").run(); - if is_msvc() { + if is_windows_msvc() { let out_msvc = run_with_args("driver", &["true"]).stdout_utf8(); diff() .expected_file("output.msvc.txt") diff --git a/tests/run-make/raw-dylib-import-name-type/rmake.rs b/tests/run-make/raw-dylib-import-name-type/rmake.rs index 13a2c99150e..71f255ab39f 100644 --- a/tests/run-make/raw-dylib-import-name-type/rmake.rs +++ b/tests/run-make/raw-dylib-import-name-type/rmake.rs @@ -11,14 +11,14 @@ //@ only-windows // Reason: this test specifically exercises a 32bit Windows calling convention. -use run_make_support::{cc, diff, is_msvc, run, rustc}; +use run_make_support::{cc, diff, is_windows_msvc, run, rustc}; // NOTE: build_native_dynamic lib is not used, as the special `def` files // must be passed to the CC compiler. fn main() { rustc().crate_type("bin").input("driver.rs").run(); - if is_msvc() { + if is_windows_msvc() { cc().arg("-c").out_exe("extern").input("extern.c").run(); cc().input("extern.obj") .arg("extern.msvc.def") diff --git a/tests/run-make/raw-dylib-inline-cross-dylib/rmake.rs b/tests/run-make/raw-dylib-inline-cross-dylib/rmake.rs index 6e3b31a0cdb..a167e8198a7 100644 --- a/tests/run-make/raw-dylib-inline-cross-dylib/rmake.rs +++ b/tests/run-make/raw-dylib-inline-cross-dylib/rmake.rs @@ -7,7 +7,7 @@ //@ only-windows -use run_make_support::{cc, diff, is_msvc, llvm_objdump, run, rustc}; +use run_make_support::{cc, diff, is_windows_msvc, llvm_objdump, run, rustc}; fn main() { rustc() @@ -31,7 +31,7 @@ fn main() { .assert_stdout_not_contains("inline_library_function") // Make sure we do find an import to the functions we expect to be imported. .assert_stdout_contains("library_function"); - if is_msvc() { + if is_windows_msvc() { cc().arg("-c").out_exe("extern_1").input("extern_1.c").run(); cc().arg("-c").out_exe("extern_2").input("extern_2.c").run(); cc().input("extern_1.obj") diff --git a/tests/run-make/raw-dylib-link-ordinal/rmake.rs b/tests/run-make/raw-dylib-link-ordinal/rmake.rs index b52181ae3f9..43274b9765b 100644 --- a/tests/run-make/raw-dylib-link-ordinal/rmake.rs +++ b/tests/run-make/raw-dylib-link-ordinal/rmake.rs @@ -11,7 +11,7 @@ //@ only-windows -use run_make_support::{cc, diff, is_msvc, run, rustc}; +use run_make_support::{cc, diff, is_windows_msvc, run, rustc}; // NOTE: build_native_dynamic lib is not used, as the special `def` files // must be passed to the CC compiler. @@ -19,7 +19,7 @@ use run_make_support::{cc, diff, is_msvc, run, rustc}; fn main() { rustc().crate_type("lib").crate_name("raw_dylib_test").input("lib.rs").run(); rustc().crate_type("bin").input("driver.rs").run(); - if is_msvc() { + if is_windows_msvc() { cc().arg("-c").out_exe("exporter").input("exporter.c").run(); cc().input("exporter.obj") .arg("exporter.def") diff --git a/tests/run-make/raw-dylib-stdcall-ordinal/rmake.rs b/tests/run-make/raw-dylib-stdcall-ordinal/rmake.rs index 320ea1520d8..f898cc0f8c8 100644 --- a/tests/run-make/raw-dylib-stdcall-ordinal/rmake.rs +++ b/tests/run-make/raw-dylib-stdcall-ordinal/rmake.rs @@ -10,7 +10,7 @@ //@ only-windows // Reason: this test specifically exercises a 32bit Windows calling convention. -use run_make_support::{cc, diff, is_msvc, run, rustc}; +use run_make_support::{cc, diff, is_windows_msvc, run, rustc}; // NOTE: build_native_dynamic lib is not used, as the special `def` files // must be passed to the CC compiler. @@ -18,7 +18,7 @@ use run_make_support::{cc, diff, is_msvc, run, rustc}; fn main() { rustc().crate_type("lib").crate_name("raw_dylib_test").input("lib.rs").run(); rustc().crate_type("bin").input("driver.rs").run(); - if is_msvc() { + if is_windows_msvc() { cc().arg("-c").out_exe("exporter").input("exporter.c").run(); cc().input("exporter.obj") .arg("exporter-msvc.def") diff --git a/tests/run-make/rlib-format-packed-bundled-libs/rmake.rs b/tests/run-make/rlib-format-packed-bundled-libs/rmake.rs index ff0438a6b72..f0929a3ee85 100644 --- a/tests/run-make/rlib-format-packed-bundled-libs/rmake.rs +++ b/tests/run-make/rlib-format-packed-bundled-libs/rmake.rs @@ -8,8 +8,8 @@ // Reason: cross-compilation fails to export native symbols use run_make_support::{ - bin_name, build_native_static_lib, cwd, filename_contains, is_msvc, llvm_ar, llvm_nm, rfs, - rust_lib_name, rustc, shallow_find_files, + bin_name, build_native_static_lib, cwd, filename_contains, is_windows_msvc, llvm_ar, llvm_nm, + rfs, rust_lib_name, rustc, shallow_find_files, }; fn main() { @@ -74,7 +74,7 @@ fn main() { .assert_stdout_contains_regex("native_dep_1.*native_dep_2.*native_dep_3"); // The binary "main" will not contain any symbols on MSVC. - if !is_msvc() { + if !is_windows_msvc() { llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*native_f1"); llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*native_f2"); llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*native_f3"); diff --git a/tests/run-make/split-debuginfo/rmake.rs b/tests/run-make/split-debuginfo/rmake.rs index 530a5d119f1..e8de5aed172 100644 --- a/tests/run-make/split-debuginfo/rmake.rs +++ b/tests/run-make/split-debuginfo/rmake.rs @@ -61,8 +61,8 @@ use std::collections::BTreeSet; use run_make_support::rustc::Rustc; use run_make_support::{ - cwd, has_extension, is_darwin, is_msvc, is_windows, llvm_dwarfdump, run_in_tmpdir, rustc, - shallow_find_directories, shallow_find_files, uname, + cwd, has_extension, is_darwin, is_windows, is_windows_msvc, llvm_dwarfdump, run_in_tmpdir, + rustc, shallow_find_directories, shallow_find_files, uname, }; /// `-C debuginfo`. See . @@ -1296,7 +1296,7 @@ fn main() { // identify which combination isn't exercised with a 6-layers nested for loop iterating through // each of the cli flag enum variants. - if is_msvc() { + if is_windows_msvc() { // FIXME: the windows-msvc test coverage is sparse at best. windows_msvc_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::Unspecified); diff --git a/tests/run-make/static-dylib-by-default/rmake.rs b/tests/run-make/static-dylib-by-default/rmake.rs index 133210c74e7..b1160c63297 100644 --- a/tests/run-make/static-dylib-by-default/rmake.rs +++ b/tests/run-make/static-dylib-by-default/rmake.rs @@ -9,7 +9,7 @@ // Reason: the compiled binary is executed use run_make_support::{ - cc, cwd, dynamic_lib_name, extra_c_flags, has_extension, is_msvc, rfs, run, rustc, + cc, cwd, dynamic_lib_name, extra_c_flags, has_extension, is_windows_msvc, rfs, run, rustc, shallow_find_files, }; @@ -22,13 +22,13 @@ fn main() { // bar.dll.exp // export library for the dylib // msvc's underlying link.exe requires the import library for the dynamic library as input. // That is why the library is bar.dll.lib, not bar.dll. - let library = if is_msvc() { "bar.dll.lib" } else { &dynamic_lib_name("bar") }; + let library = if is_windows_msvc() { "bar.dll.lib" } else { &dynamic_lib_name("bar") }; cc().input("main.c").out_exe("main").arg(library).args(extra_c_flags()).run(); for rlib in shallow_find_files(cwd(), |path| has_extension(path, "rlib")) { rfs::remove_file(rlib); } rfs::remove_file(dynamic_lib_name("foo")); - if is_msvc() { + if is_windows_msvc() { rfs::remove_file("foo.dll.lib"); } run("main"); diff --git a/tests/run-make/staticlib-dylib-linkage/rmake.rs b/tests/run-make/staticlib-dylib-linkage/rmake.rs index 8dd1ac0ffbd..9582ca19831 100644 --- a/tests/run-make/staticlib-dylib-linkage/rmake.rs +++ b/tests/run-make/staticlib-dylib-linkage/rmake.rs @@ -9,7 +9,7 @@ //@ ignore-wasm // Reason: WASM does not support dynamic libraries -use run_make_support::{cc, is_msvc, regex, run, rustc, static_lib_name}; +use run_make_support::{cc, is_windows_msvc, regex, run, rustc, static_lib_name}; fn main() { rustc().arg("-Cprefer-dynamic").input("bar.rs").run(); @@ -27,7 +27,7 @@ fn main() { let (_, native_link_args) = libs.split_once("note: native-static-libs: ").unwrap(); // divide the command-line arguments in a vec let mut native_link_args = native_link_args.split(' ').collect::>(); - if is_msvc() { + if is_windows_msvc() { // For MSVC pass the arguments on to the linker. native_link_args.insert(0, "-link"); } diff --git a/tests/run-make/symbol-visibility/rmake.rs b/tests/run-make/symbol-visibility/rmake.rs index 49c8e87707b..e3d276d6da8 100644 --- a/tests/run-make/symbol-visibility/rmake.rs +++ b/tests/run-make/symbol-visibility/rmake.rs @@ -9,7 +9,7 @@ // See https://github.com/rust-lang/rust/issues/37530 use run_make_support::object::read::Object; -use run_make_support::{bin_name, dynamic_lib_name, is_msvc, object, regex, rfs, rustc}; +use run_make_support::{bin_name, dynamic_lib_name, is_windows_msvc, object, regex, rfs, rustc}; fn main() { let cdylib_name = dynamic_lib_name("a_cdylib"); @@ -64,7 +64,7 @@ fn main() { ); // FIXME(nbdd0121): This is broken in MinGW, see https://github.com/rust-lang/rust/pull/95604#issuecomment-1101564032 - if is_msvc() { + if is_windows_msvc() { // Check that an executable does not export any dynamic symbols symbols_check(&exe_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), false); symbols_check( @@ -130,7 +130,7 @@ fn main() { ); // FIXME(nbdd0121): This is broken in MinGW, see https://github.com/rust-lang/rust/pull/95604#issuecomment-1101564032 - if is_msvc() { + if is_windows_msvc() { // Check that an executable does not export any dynamic symbols symbols_check(&exe_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), false); symbols_check( -- cgit 1.4.1-3-g733a5 From b9af6670d882c4d5f71f760c4efaceec2c849030 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 10 Jul 2025 20:01:15 +0200 Subject: Bump nightly version -> 2025-07-10 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 649748d1534..645b644d9f4 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-06-26 +nightly-2025-07-10 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 124756a3600..f46e079db3f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-06-26" +channel = "nightly-2025-07-10" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" -- cgit 1.4.1-3-g733a5 From 69fca569eb0bd4b777fbb970477e561efa62a6dd Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 10 Jul 2025 20:25:57 +0200 Subject: Update Cargo.lock --- Cargo.lock | 80 +++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 271a2f7962c..549818eecb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -569,22 +569,16 @@ dependencies = [ "color-print", "declare_clippy_lint", "filetime", - "futures", - "if_chain", "itertools", - "parking_lot", "pulldown-cmark", - "quote", "regex", "rustc_tools_util 0.4.2", "serde", "serde_json", - "syn 2.0.104", "tempfile", - "termize 0.1.1", - "tokio", + "termize", "toml 0.7.8", - "ui_test", + "ui_test 0.30.2", "walkdir", ] @@ -642,6 +636,21 @@ dependencies = [ "rustc-semver", ] +[[package]] +name = "clippy_test_deps" +version = "0.1.0" +dependencies = [ + "futures", + "if_chain", + "itertools", + "parking_lot", + "quote", + "regex", + "serde", + "syn 2.0.104", + "tokio", +] + [[package]] name = "clippy_utils" version = "0.1.90" @@ -2409,7 +2418,7 @@ dependencies = [ "smallvec", "tempfile", "tikv-jemalloc-sys", - "ui_test", + "ui_test 0.29.2", ] [[package]] @@ -3773,7 +3782,7 @@ dependencies = [ "serde", "serde_json", "termcolor", - "termize 0.2.0", + "termize", "tracing", "windows 0.61.3", ] @@ -4536,7 +4545,7 @@ dependencies = [ "rustc_serialize", "rustc_span", "rustc_target", - "termize 0.2.0", + "termize", "tracing", "windows 0.61.3", ] @@ -5067,6 +5076,17 @@ dependencies = [ "color-eyre", ] +[[package]] +name = "spanned" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4b0c055fde758f086eb4a6e73410247df8a3837fd606d2caeeaf72aa566d" +dependencies = [ + "anyhow", + "bstr", + "color-eyre", +] + [[package]] name = "spdx-expression" version = "0.5.2" @@ -5305,16 +5325,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "termize" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "termize" version = "0.2.0" @@ -5726,7 +5736,33 @@ dependencies = [ "rustfix", "serde", "serde_json", - "spanned", + "spanned 0.3.0", +] + +[[package]] +name = "ui_test" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b56a6897cc4bb6f8daf1939b0b39cd9645856997f46f4d0b3e3cb7122dfe9251" +dependencies = [ + "annotate-snippets 0.11.5", + "anyhow", + "bstr", + "cargo-platform", + "cargo_metadata 0.18.1", + "color-eyre", + "colored", + "comma", + "crossbeam-channel", + "indicatif", + "levenshtein", + "prettydiff", + "regex", + "rustc_version", + "rustfix", + "serde", + "serde_json", + "spanned 0.4.1", ] [[package]] -- cgit 1.4.1-3-g733a5 From c629a450d650f478bde8fffca92a0da7b0701316 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 10 Jul 2025 20:35:35 +0200 Subject: Add clippy_test_deps to workspace member of repo --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 6d3425f4115..9890027d098 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "src/tools/cargotest", "src/tools/clippy", "src/tools/clippy/clippy_dev", + "src/tools/clippy/clippy_test_deps", "src/tools/collect-license-metadata", "src/tools/compiletest", "src/tools/coverage-dump", -- cgit 1.4.1-3-g733a5 From 5e940f581008dbc9b84aa1a8672a3c0a940da7be Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 10 Jul 2025 05:06:47 +0000 Subject: fmt --- src/tools/miri/src/machine.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 35399dbf4cb..7c4d394525b 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1828,7 +1828,9 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan { #[cfg(feature = "tracing")] - { span().entered() } + { + span().entered() + } #[cfg(not(feature = "tracing"))] { let _ = span; // so we avoid the "unused variable" warning -- cgit 1.4.1-3-g733a5 From 17b240e04f8d14e5563b6afb2ed3b842f87d1c78 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 10 Jul 2025 08:33:05 +0200 Subject: silence clippy --- src/tools/miri/src/machine.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7c4d394525b..9c077e5b7b6 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1832,6 +1832,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { span().entered() } #[cfg(not(feature = "tracing"))] + #[expect(clippy::unused_unit)] { let _ = span; // so we avoid the "unused variable" warning () -- cgit 1.4.1-3-g733a5