Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

False positive and highly misleading suggestions for the non-local-definitions lint #124396

Closed
weiznich opened this issue Apr 26, 2024 · 12 comments · Fixed by #125089
Closed

False positive and highly misleading suggestions for the non-local-definitions lint #124396

weiznich opened this issue Apr 26, 2024 · 12 comments · Fixed by #125089
Assignees
Labels
C-bug Category: This is a bug. L-non_local_definitions Lint: non_local_definitions P-high High priority regression-from-stable-to-beta Performance or correctness regression from stable to beta. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Milestone

Comments

@weiznich
Copy link
Contributor

weiznich commented Apr 26, 2024

I tried this code: https://github.com/diesel-rs/diesel/blob/974814c6792c534736498a0bd7abf95e21f8bf7b/diesel_derives/tests/associations.rs#L37

I expected to see this happen: I get an error message that explains that this macro expands to a trait impl and therefore it triggers the lint as I (the user) placed the macro in a function scope.

Instead, this happened: I get a rather generic error that points to the macro (ok so far) and claims that this might be an issue with the dependency version (diesel in this case) I'm using.

Example error message:

error: non-local `impl` definition, they should be avoided as they go against expectation
  --> diesel_derives/tests/associations.rs:37:5
   |
37 |     joinable!(posts -> users(user_id));
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: move this `impl` block outside the of the current constant `_` and up 2 bodies
   = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl`
   = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type
   = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue <https://github.com/rust-lang/rust/issues/120363>
   = note: the macro `$crate::joinable_inner` may come from an old version of the `diesel` crate, try updating your dependency with `cargo update -p diesel`
   = note: this error originates in the macro `$crate::joinable_inner` which comes from the expansion of the macro `joinable` (in Nightly builds, run with -Z macro-backtrace for more info)

Especially the suggestion = note: the macro allow_tables_to_appear_in_same_query may come from an old version of the diesel crate, try updating your dependency with cargo update -p diesel` is highly problematic in this context as that never could resolve the error given above. The wording might result in cases where the user will believe that this is an upstream diesel issue, rather than an issue with their local code.

Instead I propose that the lint detects weather the impl macro is placed in an local scope or not and adjusts the error message based on that.

The other interesting observation about that specific code example is that the lint is only triggered by the macro rules macro there and not by any of the proc macros above/below the linked line. In fact the macro also only generates impls that refer only to local types defined by the table! macro above, so arguably the lint is even wrong at all in this case as it does not add a non local impl.

(code for joinable!)

Relevant for #120363

Meta

rustc --version:

rustc 1.79.0-nightly (3a36386dc 2024-04-25)
@weiznich weiznich added the C-bug Category: This is a bug. label Apr 26, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Apr 26, 2024
@Urgau
Copy link
Member

Urgau commented Apr 26, 2024

I think I have reduced the issue to this MCVE:

#![allow(dead_code, non_camel_case_types)]

trait JoinTo {}

fn simple_belongs_to() {
    mod posts {
        pub struct table {}
    }
    
    impl JoinTo for posts::table {}
}

playground

@ChayimFriedman2
Copy link
Contributor

A gap in the implementation of #122747? The check probably doesn't account for this case:

if self_ty_has_local_parent {
return;
}

@Urgau
Copy link
Member

Urgau commented Apr 26, 2024

The RFC says that either the Trait or the Type should at the same nesting as the impl block, in the implementation we look at the parent item to see if they are equal.

In the MCVE:

  • the impl block parent is fn simple_belongs_to
  • JoinTo trait parent is the crate root
  • and posts::table parent is mod posts

None of those are equal, so we emit the lint.

However, this is were things get interesting, rust-analyzer actually also consider "all ancestor of the current block"; which means that the impl block would be correctly taken into consideration, because in r-a terms block are not modules, just expressions; so making modules transparent would fix the issue in a way that is compatible with r-a.

edit: to be ignored

Except that it would fall apart quickly for r-a as not all parent blocks are not taken into consideration. EDIT: I was wrong, I was missing a import.

trait JoinTo {
    fn gg(&self) {}
}

fn simple_belongs_to() {
    mod posts {
        pub mod posts {
            pub struct table;
        }
        
        fn hh() {
            use crate::JoinTo; // EDIT: actually `rustc` doesn't compile without this import
                               // and adding it makes r-a also resolve this 🤔 
            let _a = posts::table.gg(); // --this is NOT resolved by r-a--
        }
    }
    
    impl JoinTo for posts::posts::table {}

    let _a = posts::posts::table.gg(); // but here it is resolved by r-a
}

Therefore strictly speaking I don't think is a false positive.

@Urgau
Copy link
Member

Urgau commented Apr 26, 2024

Thanks to @Veykril, I now better understand r-a internals. I have confirmed with him that making modules transparent when checking if the impl def is at the same nesting as the Type or Trait would be fine for r-a1.

Implementation wise making modules transparent would be very similar to the const _: () = { ... } exception that we already have. In the sense that we would consider all parent modules of the Type/Trait to be transparent, no neighbours.

I tried finding examples where type inference would make the impl non-local but couldn't find any, that doesn't mean there aren't, just that if there are, I was not able to find any.

Whenever we should do this is beyond me. cc @joshtriplett

Footnotes

  1. Internally, when doing type/method resolutions, r-a considers, among other things, all ancestors of the current block, in which modules are basically transparent.

@joshtriplett
Copy link
Member

I don't see any obvious ways to get non-local impls out of this either. Assuming nobody finds any, this exception (treating a local module's contents as local) seems reasonable and consistent with what we're trying to do here.

I also don't see any obvious ways in which this closes off future avenues for adding ways to reach inside/outside of a module-inside-function. We've talked about adding a way for a nested module to reference the containing fn's scope, but that wouldn't create non-local behavior.

I'm nominating this for lang. If anyone in lang can see a way to get a non-local impl out of a module-inside-fn like this, please speak up. Otherwise, I think it's reasonable to fix this as proposed.

@joshtriplett joshtriplett added T-lang Relevant to the language team, which will review and decide on the PR/issue. I-lang-nominated Nominated for discussion during a lang team meeting. labels Apr 26, 2024
@glandium
Copy link
Contributor

There's another false positive:

struct A;

fn foo() {
    struct B;
    
    impl From<B> for A {
        fn from(b: B) -> A {
            todo!()
        }
    }
}

The error shown is:

warning: non-local `impl` definition, they should be avoided as they go against expectation
  --> src/lib.rs:6:5
   |
6  | /     impl From<B> for A {
7  | |         fn from(b: B) -> A {
8  | |             todo!()
9  | |         }
10 | |     }
   | |_____^
   |
   = help: move this `impl` block outside the of the current function `foo`
   = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl`
   = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type
   = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue <https://github.com/rust-lang/rust/issues/120363>
   = note: `#[warn(non_local_definitions)]` on by default

Should I file a new issue?

@weiznich
Copy link
Contributor Author

Thanks you all for your swift response. It would be great to see that resolved before it hits stable. The lint seems now to be enabled on the beta branch as well.

Also I want to highlight the other part of the issue (misleading message) again:

Especially the suggestion = note: the macro allow_tables_to_appear_in_same_query may come from an old version of the diesel crate, try updating your dependency with cargo update -p diesel is highly problematic in this context as that never could resolve the error given above. The wording might result in cases where the user will believe that this is an upstream diesel issue, rather than an issue with their local code.

I feel that this should detect whether an update could even resolve this issue. This is not the case if the user put the macro call inside a function on it's own, which I feel is something that should be easy to detect while emitting the lint. (If someone points to the right location I even can have a look at that myself.)

bors added a commit to rust-lang-ci/rust that referenced this issue May 1, 2024
… r=lcnr

Consider inner modules to be local in the `non_local_definitions` lint

This PR implements the [proposed fix](rust-lang#124396 (comment)) for rust-lang#124396, that is to consider inner modules to be local in the `non_local_definitions` lint.

This PR is voluntarily kept as minimal as possible so it can be backported easily.

T-lang [nomination](rust-lang#124396 (comment)) will need to be removed before this can be merged.

Fixes *(nearly, needs backport)* rust-lang#124396
@Urgau
Copy link
Member

Urgau commented May 1, 2024

Updating the labels so we track this issue as a regression.

@rustbot labels -needs-triage +regression-from-stable-to-beta

@rustbot rustbot added regression-from-stable-to-beta Performance or correctness regression from stable to beta. I-prioritize Issue: Indicates that prioritization has been requested for this issue. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels May 1, 2024
@traviscross
Copy link
Contributor

traviscross commented May 1, 2024

@rustbot labels -I-lang-nominated

We discussed this in the lang meeting today. Our consensus was in favor of adopting the modification to the RFC 3373 rules discussed here, to make "modules transparent when checking if the impl def is at the same nesting as the" type or trait.

I.e., this code is not a "sneaky inner impl" and should not lint:

trait Trait {}
fn foo() {
    mod m {
        pub struct Ty {}
    }
    impl Trait for m::Ty {}
}

This is good to go on the lang side, and we leave it as usual to the compiler team to decide on the appropriate follow-up actions.

@rustbot rustbot removed the I-lang-nominated Nominated for discussion during a lang team meeting. label May 1, 2024
@apiraino
Copy link
Contributor

apiraino commented May 2, 2024

WG-prioritization assigning priority (Zulip discussion).

Since this looks like a diag regression so maybe confusing but not critical.

Fixed by #124539 (beta-nominated)

@rustbot label -I-prioritize +P-medium

@rustbot rustbot added I-compiler-nominated Nominated for discussion during a compiler team meeting. P-medium Medium priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels May 2, 2024
@apiraino apiraino removed the I-compiler-nominated Nominated for discussion during a compiler team meeting. label May 2, 2024
github-actions bot pushed a commit to rust-lang/miri that referenced this issue May 3, 2024
Consider inner modules to be local in the `non_local_definitions` lint

This PR implements the [proposed fix](rust-lang/rust#124396 (comment)) for #124396, that is to consider inner modules to be local in the `non_local_definitions` lint.

This PR is voluntarily kept as minimal as possible so it can be backported easily.

T-lang [nomination](rust-lang/rust#124396 (comment)) will need to be removed before this can be merged.

Fixes *(nearly, needs backport)* rust-lang/rust#124396
@wesleywiser
Copy link
Member

I think since this lint is new in 1.79 this is a higher priority than a normal diagnostic regression. Retagging as P-high.

@rustbot label -P-medium +P-high

@rustbot rustbot added P-high High priority and removed P-medium Medium priority labels May 9, 2024
@cuviper cuviper added this to the 1.79.0 milestone May 9, 2024
@Urgau
Copy link
Member

Urgau commented May 10, 2024

The fixed has landed in 1.80 (#124539) and now in 1.79 (#124947) as well.

@jieyouxu jieyouxu added the L-non_local_definitions Lint: non_local_definitions label May 13, 2024
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue May 28, 2024
…=estebank

Improve diagnostic output the `non_local_definitions` lint

This PR improves (or at least tries to improve) the diagnostic output the `non_local_definitions` lint, by simplifying the wording, by adding a "sort of" explanation of bounds interaction that leak the impl...

This PR is best reviewed commit by commit and is voluntarily made a bit vague as to have a starting point to improve on.

Related to https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/non_local_defs.20wording.20improvements

Fixes rust-lang#125068
Fixes rust-lang#124396
cc `@workingjubilee`
r? `@estebank`
workingjubilee added a commit to workingjubilee/rustc that referenced this issue May 28, 2024
…=estebank

Improve diagnostic output the `non_local_definitions` lint

This PR improves (or at least tries to improve) the diagnostic output the `non_local_definitions` lint, by simplifying the wording, by adding a "sort of" explanation of bounds interaction that leak the impl...

This PR is best reviewed commit by commit and is voluntarily made a bit vague as to have a starting point to improve on.

Related to https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/non_local_defs.20wording.20improvements

Fixes rust-lang#125068
Fixes rust-lang#124396
cc ``@workingjubilee``
r? ``@estebank``
@bors bors closed this as completed in 8e89f83 May 28, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this issue May 28, 2024
Rollup merge of rust-lang#125089 - Urgau:non_local_def-suggestions, r=estebank

Improve diagnostic output the `non_local_definitions` lint

This PR improves (or at least tries to improve) the diagnostic output the `non_local_definitions` lint, by simplifying the wording, by adding a "sort of" explanation of bounds interaction that leak the impl...

This PR is best reviewed commit by commit and is voluntarily made a bit vague as to have a starting point to improve on.

Related to https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/non_local_defs.20wording.20improvements

Fixes rust-lang#125068
Fixes rust-lang#124396
cc ```@workingjubilee```
r? ```@estebank```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. L-non_local_definitions Lint: non_local_definitions P-high High priority regression-from-stable-to-beta Performance or correctness regression from stable to beta. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.