rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.02k stars 12.68k forks source link

Passing an `ExprKind::Assign` to a function should specify that rust does not have named arguments #115192

Open Centri3 opened 1 year ago

Centri3 commented 1 year ago

Code

fn a(x: u32, y: u32) {}

fn b() {
    let x = 1;
    let y = 1;
    a(x = x, y = y);
}

fn c() {
    let x_2 = 1;
    let y_2 = 1;
    a(x = x_2, y = y_2);
}

Current output

error[E0425]: cannot find value `x` in this scope
  --> a/bb/src/main.rs:12:7
   |
1  | fn a(x: u32, y: u32) {}
   | -------------------- similarly named function `a` defined here
...
12 |     a(x = x_2, y = y_2);
   |       ^ help: a function with a similar name exists: `a`

error[E0425]: cannot find value `y` in this scope
  --> a/bb/src/main.rs:12:16
   |
1  | fn a(x: u32, y: u32) {}
   | -------------------- similarly named function `a` defined here
...
12 |     a(x = x_2, y = y_2);
   |                ^ help: a function with a similar name exists: `a`

error[E0308]: arguments to this function are incorrect
 --> a/bb/src/main.rs:6:5
  |
6 |     a(x = x, y = y);
  |     ^ -----  ----- expected `u32`, found `()`
  |       |
  |       expected `u32`, found `()`
  |
note: function defined here
 --> a/bb/src/main.rs:1:4
  |
1 | fn a(x: u32, y: u32) {}
  |    ^ ------  ------

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0308, E0425.
For more information about an error, try `rustc --explain E0308`.

Desired output

error[E0425]: cannot find value `x` in this scope
  --> a/bb/src/main.rs:12:7
   |
1  | fn a(x: u32, y: u32) {}
   | -------------------- similarly named function `a` defined here
...
12 |     a(x = x_2, y = y_2);
   |       ^ help: a function with a similar name exists: `a`
note: Rust does not support named arguments; try
(suggestion like `a(x_2, y_2);` here)

error[E0425]: cannot find value `y` in this scope
  --> a/bb/src/main.rs:12:16
   |
1  | fn a(x: u32, y: u32) {}
   | -------------------- similarly named function `a` defined here
...
12 |     a(x = x_2, y = y_2);
   |                ^ help: a function with a similar name exists: `a`

error[E0308]: arguments to this function are incorrect
 --> a/bb/src/main.rs:6:5
  |
6 |     a(x = x, y = y);
  |     ^ -----  ----- expected `u32`, found `()`
  |       |
  |       expected `u32`, found `()`
  |
note: function defined here
 --> a/bb/src/main.rs:1:4
  |
1 | fn a(x: u32, y: u32) {}
  |    ^ ------  ------
note: Rust does not support named arguments; try
(suggestion like `a(x, y);` here)

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0308, E0425.
For more information about an error, try `rustc --explain E0308`.

Rationale and extra context

For somebody new to rust, it may not be immediately clear why this doesn't work, or why it's (). We'd ideally guide them in the right direction if lhs' name is the same as that parameter.

I think we only need to do this for the two above cases (where lhs doesn't exist or it does, but the type is wrong)

If the correct type happens to be (), this could probably be a clippy lint.

Other cases

No response

Anything else?

No response

Rajveer100 commented 1 year ago

@Centri3 Here's some brief info about me, having a conversation the first time.

How do we target the rustc command for the compiler with [options] for specifying [file name] rather than the one installed in the system's bin? Once that's done I can test changes to be reflected and move forward.

[PS: Issue was resolved via zulip]

Rajveer100 commented 1 year ago

@rustbot claim

chenyukang commented 1 year ago

@Rajveer100 welcome! I used this for run rustc compiled:

rustup toolchain link dev ./build/aarch64-apple-darwin/stage1/
RUST_BACKTRACE=1 rustc +dev {{FILE}}

If you use justfile, this is my frequently used commands for rustc, FYI. https://gist.github.com/chenyukang/1483cbbf75a4bd5ae2930415329cb682

Rajveer100 commented 1 year ago

@chenyukang Thanks for the info! Looks pretty good!

Rajveer100 commented 1 year ago

Backtrace for the issue:

BACKTRACE ``` thread 'rustc' panicked at compiler/rustc_errors/src/lib.rs:1722:30: aborting due to `-Z treat-err-as-bug=1` stack backtrace: 0: 0x102b59f00 - ::fmt::h891d11f801b0b8cc 1: 0x102ba15ac - core::fmt::write::h43d59a9705b0b236 2: 0x102b3004c - std::io::Write::write_fmt::h3a39072d3fb9e1fc 3: 0x102b59d5c - std::sys_common::backtrace::print::ha10492ec9e7c9f44 4: 0x102b5c7a4 - std::panicking::panic_hook_with_disk_dump::{{closure}}::h61bd497779c5cf01 5: 0x102b5c4d4 - std::panicking::panic_hook_with_disk_dump::hffce69d4126ca800 6: 0x10cba1558 - rustc_driver_impl[6bf06c0529d77aec]::install_ice_hook::{closure#0} 7: 0x102b5cd8c - std::panicking::rust_panic_with_hook::h5b4f2ed5717f7e1a 8: 0x102b64e34 - std::panicking::begin_panic_handler::{{closure}}::h1ea0688d3d67fe05 9: 0x102b64da0 - std::sys_common::backtrace::__rust_end_short_backtrace::h312eebcfb7b785ab 10: 0x102b5c8e4 - _rust_begin_unwind 11: 0x102bc6858 - core::panicking::panic_fmt::he61e13a935a81a23 12: 0x110d48900 - ::panic_if_treat_err_as_bug 13: 0x110d48754 - ::emit_diagnostic::{closure#2} 14: 0x10cd464a0 - >>::with::::{closure#0}, ()> 15: 0x10ccbfd28 - rustc_interface[76adf9fc96937673]::callbacks::track_diagnostic 16: 0x110d48150 - ::emit_diagnostic 17: 0x110d47440 - ::emit_diagnostic 18: 0x110d447c0 - ::diagnostic_builder_emit_producing_guarantee 19: 0x10fa13064 - ::report_errors 20: 0x10f9c17e8 - ::time::<(), ::resolve_crate::{closure#0}> 21: 0x10fa28e98 - ::resolve_crate 22: 0x10ccd6f74 - rustc_interface[76adf9fc96937673]::passes::resolver_for_lowering 23: 0x11001121c - rustc_query_impl[526b8106c55b4013]::plumbing::__rust_begin_short_backtrace::> 24: 0x110023054 - >::call_once 25: 0x11005feb0 - >>::with::>, false, false, false>, rustc_query_impl[526b8106c55b4013]::plumbing::QueryCtxt>::{closure#0}, rustc_middle[41350a9f045af874]::query::erase::Erased<[u8; 8usize]>>::{closure#0}, rustc_middle[41350a9f045af874]::query::erase::Erased<[u8; 8usize]>> 26: 0x1100dd5b4 - rustc_query_system[2894895f175f0716]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[526b8106c55b4013]::plumbing::QueryCtxt, false> 27: 0x10ff8e648 - rustc_query_impl[526b8106c55b4013]::query_impl::resolver_for_lowering::get_query_non_incr::__rust_end_short_backtrace 28: 0x10cb99674 - >>::with::::enter)>>::{closure#0}, &rustc_data_structures[8a8161a4c491a4ab]::steal::Steal<(rustc_middle[41350a9f045af874]::ty::ResolverAstLowering, alloc[1d3802ade63a7721]::rc::Rc)>>::{closure#0}, &rustc_data_structures[8a8161a4c491a4ab]::steal::Steal<(rustc_middle[41350a9f045af874]::ty::ResolverAstLowering, alloc[1d3802ade63a7721]::rc::Rc)>> 29: 0x10cb99220 - ::enter::)>> 30: 0x10cb855e0 - ::enter::, rustc_span[8d2250cb752c397d]::ErrorGuaranteed>> 31: 0x10cba8ee0 - rustc_span[8d2250cb752c397d]::set_source_map::, rustc_interface[76adf9fc96937673]::interface::run_compiler, rustc_driver_impl[6bf06c0529d77aec]::run_compiler::{closure#1}>::{closure#0}::{closure#0}> 32: 0x10cbf4484 - >::set::, rustc_driver_impl[6bf06c0529d77aec]::run_compiler::{closure#1}>::{closure#0}, core[eec49cfb8bff2685]::result::Result<(), rustc_span[8d2250cb752c397d]::ErrorGuaranteed>> 33: 0x10cba9d48 - std[c2e8ad8b68e0c3f7]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[6bf06c0529d77aec]::run_compiler::{closure#1}>::{closure#0}, core[eec49cfb8bff2685]::result::Result<(), rustc_span[8d2250cb752c397d]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[eec49cfb8bff2685]::result::Result<(), rustc_span[8d2250cb752c397d]::ErrorGuaranteed>> 34: 0x10cbf1cac - ::spawn_unchecked_, rustc_driver_impl[6bf06c0529d77aec]::run_compiler::{closure#1}>::{closure#0}, core[eec49cfb8bff2685]::result::Result<(), rustc_span[8d2250cb752c397d]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[eec49cfb8bff2685]::result::Result<(), rustc_span[8d2250cb752c397d]::ErrorGuaranteed>>::{closure#1}::{closure#0}> as core[eec49cfb8bff2685]::ops::function::FnOnce<()>>::call_once 35: 0x10cb95508 - std[c2e8ad8b68e0c3f7]::panicking::try::, core[eec49cfb8bff2685]::panic::unwind_safe::AssertUnwindSafe<::spawn_unchecked_, rustc_driver_impl[6bf06c0529d77aec]::run_compiler::{closure#1}>::{closure#0}, core[eec49cfb8bff2685]::result::Result<(), rustc_span[8d2250cb752c397d]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[eec49cfb8bff2685]::result::Result<(), rustc_span[8d2250cb752c397d]::ErrorGuaranteed>>::{closure#1}::{closure#0}>> 36: 0x10cbb067c - <::spawn_unchecked_, rustc_driver_impl[6bf06c0529d77aec]::run_compiler::{closure#1}>::{closure#0}, core[eec49cfb8bff2685]::result::Result<(), rustc_span[8d2250cb752c397d]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[eec49cfb8bff2685]::result::Result<(), rustc_span[8d2250cb752c397d]::ErrorGuaranteed>>::{closure#1} as core[eec49cfb8bff2685]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} 37: 0x102b363e4 - as core::ops::function::FnOnce>::call_once::hc87e32152f3be35e 38: 0x102b1af0c - std::sys::unix::thread::Thread::new::thread_start::h32f12e8dd186b38f 39: 0x18dddffa8 - __pthread_joiner_wake rustc version: 1.74.0-dev platform: aarch64-apple-darwin query stack during panic: #0 [resolver_for_lowering] getting the resolver for lowering end of query stack ```
estebank commented 1 year ago

Be aware that this can't be handled in the parser because this is syntactically supported.

Rajveer100 commented 10 months ago

@estebank The issue seems pretty clear to me, we just want to improve the diagnostics when passing named arguments to a function instead of just saying () which is a unit type.

Could you let me know the target files/dir which handles such logic so I can accordingly work on it?

Also, as an improvement, we could also consider named arguments for this form <name_of_argument>: <argument> too, as certain languages use this rather than =.

Let me know what you think.

PS: Just noticed, the : part already has an issue mentioned with an ongoing PR.

estebank commented 10 months ago

@Rajveer100 this will actually be slightly difficult to handle (due to the case I mentioned in my previous comment). What you'd have to do is modify the parser to detect this (effectively, track that you've started to parse a function or method call, that you're directly parsing an expression that's meant to be an argument), keep that data around in a side-channel, some ParseSess diagnostic field with the span of these, and then during resolve and typeck you need to look at that information to silence the errors that happen now and emit a single error about named parameters not being supported. I am not able to provide pointers for all of these at the moment, but you can start looking at the parse_expr_fn_call and parse_dot_or_call_expr methods for the parser part of it, use -Ztrack-diagnostics to have all diagnostics produce a note pointing which file the error is being created at and -Z=treat-err-as-bug=1 to have the compiler ICE when it encounters the first error (you can change the 1 to be any number and it will skip that many before ICEing) to get a backtrace of where the error is being emitted.

Rajveer100 commented 10 months ago
$ rustc debug.rs -Ztrack-diagnostics -Ztreat-err-as-bug=1
error[E0425]: cannot find value `x` in this scope
  --> debug.rs:12:7
   |
1  | fn a(x: u32, y: u32) {}
   | -------------------- similarly named function `a` defined here
...
12 |     a(x = x_2, y = y_2);
   |       ^ help: a function with a similar name exists: `a`
-Ztrack-diagnostics: created at compiler/rustc_resolve/src/late/diagnostics.rs:428:39
Backtrace

``` thread 'rustc' panicked at compiler/rustc_errors/src/lib.rs:1738:30: aborting due to `-Z treat-err-as-bug=1` stack backtrace: 0: 0x101b90268 - ::fmt::h53b029f62d91b096 1: 0x101bdfd0c - core::fmt::write::h94df89c52ce405f9 2: 0x101b8699c - std::io::Write::write_fmt::h9dd2d04f17136633 3: 0x101b900a4 - std::sys_common::backtrace::print::h72c8d72ac493e523 4: 0x101b92aa4 - std::panicking::default_hook::{{closure}}::h3a7f8c26027c2c06 5: 0x101b927ec - std::panicking::default_hook::h86c6cebb1e85e2e8 6: 0x10ac50ac0 - as core[e17b1bfd351ee445]::ops::function::Fn<(&dyn for<'a, 'b> core[e17b1bfd351ee445]::ops::function::Fn<(&'a core[e17b1bfd351ee445]::panic::panic_info::PanicInfo<'b>,), Output = ()> + core[e17b1bfd351ee445]::marker::Send + core[e17b1bfd351ee445]::marker::Sync, &core[e17b1bfd351ee445]::panic::panic_info::PanicInfo)>>::call 7: 0x101b9311c - std::panicking::rust_panic_with_hook::h507f48f3262f9677 8: 0x101b92ec0 - std::panicking::begin_panic_handler::{{closure}}::hdafcf7d3ee3805fb 9: 0x101b906d0 - std::sys_common::backtrace::__rust_end_short_backtrace::hed5651d8ca22bdf3 10: 0x101b92c80 - _rust_begin_unwind 11: 0x101c09f64 - core::panicking::panic_fmt::h5987b735963c557e 12: 0x10acc564c - ::panic_if_treat_err_as_bug 13: 0x10acc4bc0 - ::emit_diagnostic::{closure#2} 14: 0x10b348690 - rustc_interface[fdf73c8bbfbab0ed]::callbacks::track_diagnostic 15: 0x10acc4624 - ::emit_diagnostic 16: 0x10acc3444 - ::emit_diagnostic 17: 0x10acf64ec - ::diagnostic_builder_emit_producing_guarantee 18: 0x10c00c7e0 - ::report_errors 19: 0x10c0cb33c - ::time::<(), ::resolve_crate::{closure#0}> 20: 0x10b35b1b4 - rustc_interface[fdf73c8bbfbab0ed]::passes::resolver_for_lowering 21: 0x10bded3c4 - rustc_query_impl[e8e4fe076f1e159c]::plumbing::__rust_begin_short_backtrace::> 22: 0x10bec2490 - >::call_once 23: 0x10bd0c5ac - rustc_query_system[f767e0e86444d493]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[e8e4fe076f1e159c]::plumbing::QueryCtxt, false> 24: 0x10bf21000 - rustc_query_impl[e8e4fe076f1e159c]::query_impl::resolver_for_lowering::get_query_non_incr::__rust_end_short_backtrace 25: 0x10ac64acc - ::enter::)>> 26: 0x10ac0e4e8 - ::enter::, rustc_span[a04965e70f1136e6]::ErrorGuaranteed>> 27: 0x10ac57990 - rustc_span[a04965e70f1136e6]::set_source_map::, rustc_interface[fdf73c8bbfbab0ed]::interface::run_compiler, rustc_driver_impl[c1f29ae1717eb959]::run_compiler::{closure#1}>::{closure#0}::{closure#0}> 28: 0x10ac59210 - rustc_span[a04965e70f1136e6]::create_session_globals_then::, rustc_interface[fdf73c8bbfbab0ed]::util::run_in_thread_pool_with_globals, rustc_driver_impl[c1f29ae1717eb959]::run_compiler::{closure#1}>::{closure#0}, core[e17b1bfd351ee445]::result::Result<(), rustc_span[a04965e70f1136e6]::ErrorGuaranteed>>::{closure#0}> 29: 0x10ac32698 - std[9107502e9e20a56b]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[c1f29ae1717eb959]::run_compiler::{closure#1}>::{closure#0}, core[e17b1bfd351ee445]::result::Result<(), rustc_span[a04965e70f1136e6]::ErrorGuaranteed>>::{closure#0}, core[e17b1bfd351ee445]::result::Result<(), rustc_span[a04965e70f1136e6]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[e17b1bfd351ee445]::result::Result<(), rustc_span[a04965e70f1136e6]::ErrorGuaranteed>> 30: 0x10ac36884 - <::spawn_unchecked_, rustc_driver_impl[c1f29ae1717eb959]::run_compiler::{closure#1}>::{closure#0}, core[e17b1bfd351ee445]::result::Result<(), rustc_span[a04965e70f1136e6]::ErrorGuaranteed>>::{closure#0}, core[e17b1bfd351ee445]::result::Result<(), rustc_span[a04965e70f1136e6]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[e17b1bfd351ee445]::result::Result<(), rustc_span[a04965e70f1136e6]::ErrorGuaranteed>>::{closure#1} as core[e17b1bfd351ee445]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} 31: 0x101b9c14c - std::sys::unix::thread::Thread::new::thread_start::h839aee321f78783f 32: 0x1810f2034 - __pthread_joiner_wake error: the compiler unexpectedly panicked. this is a bug. note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md note: please attach the file at `.../rust/build/rustc-ice-2023-12-27T11_17_46-11589.txt` to your bug report note: compiler flags: -Z track-diagnostics -Z treat-err-as-bug=1 query stack during panic: #0 [resolver_for_lowering] getting the resolver for lowering end of query stack ```

Rajveer100 commented 10 months ago

What you'd have to do is modify the parser to detect this (effectively, track that you've started to parse a function or method call, that you're directly parsing an expression that's meant to be an argument), keep that data around in a side-channel, some ParseSess diagnostic field with the span of these, and then during resolve and typeck you need to look at that information to silence the errors that happen now and emit a single error about named parameters not being supported.

You mean to say we need to add an additional field in the Parser struct with ParseSess as datatype, which we can use to compare the last and current argument, where if we find such matching =, emit the new diagnostic?

Probably this place:

#[derive(Clone)]
pub struct Parser<'a> {
    pub sess: &'a ParseSess,
    /// The current token.
    pub token: Token,
    /// The spacing for the current token.
    pub token_spacing: Spacing,
    /// The previous token.
    pub prev_token: Token,
    pub capture_cfg: bool,
    restrictions: Restrictions,
    expected_tokens: Vec<TokenType>,
    token_cursor: TokenCursor,
...
estebank commented 10 months ago

Yeah, you could add it to Parser, but we try to avoid adding that kind of "global" state as much as possible. Normally we'd add a field to the AST, but because this is exclusively for error recover (so we don't want to add any performance penalty when not hit) and this will be very uncommon, it seems like the best option available to us.

Rajveer100 commented 9 months ago

Could you describe how we would store the data using ParseSess?

I see this, do we need an additional field here as well and what type would it have, probably bool?:

pub(crate) struct ParseSess {
    parse_sess: RawParseSess,
    ignore_path_set: Lrc<IgnorePathSet>,
    can_reset_errors: Lrc<AtomicBool>,
}
Rajveer100 commented 5 months ago

@estebank Under parse_expr_fn_call:

let open_paren = self.token.span;

Is this essentially the tokenizer's eat method that we use in parsing for getting rid of the first open ( in the function definition?

Which token would be checked here for the function parameter, so I can store it, also once that's done, where is the resolve and diagnostic done do I can retrieve my side-channel data and throw a single error?

estebank commented 5 months ago

Is this essentially the tokenizer's eat method that we use in parsing for getting rid of the first open ( in the function definition?

No, parse_paren_comma_seq which gets called within parse_expr_paren_seq takes care of the opening and closing parens.

I think this can be done entirely in rustc_resolve, though. We already emit a suggestion to introduce a let binding, that we can modify to look to see if the parent of the assign expression is an fn or method call, and if so instead of suggesting let, suggest deleting the name and = signs with an explanation that we don't have named arguments.

Rajveer100 commented 5 months ago

Yeah, I have found the locations for this, for the parent part, do we do something like this:

if let Some(Expr { _: ExprKind::MethodCall(_, ..), .. }) = self.parent <--?? {

Also, the name field for this issue would be x = x where we then strip to just x and throw a span suggestion?