Skip to content

Commit

Permalink
Split refining_impl_trait lint into _reachable, _internal variants
Browse files Browse the repository at this point in the history
  • Loading branch information
tmandry committed Feb 28, 2024
1 parent fc3800f commit 68a8617
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 29 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ hir_analysis_rpitit_refined = impl trait in impl method signature does not match
.label = return type from trait method defined here
.unmatched_bound_label = this bound is stronger than that defined on the trait
.note = add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
.feedback_note = we are soliciting feedback, see issue #12345 <https://TODO> for more information
hir_analysis_self_in_impl_self =
`Self` is not valid in the self type of an impl block
Expand Down
33 changes: 17 additions & 16 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::{outlives::env::OutlivesEnvironment, TyCtxtInferExt};
use rustc_lint_defs::builtin::REFINING_IMPL_TRAIT;
use rustc_lint_defs::builtin::{REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE};
use rustc_middle::traits::{ObligationCause, Reveal};
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperVisitable, TypeVisitable, TypeVisitor,
Expand All @@ -24,26 +24,23 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
if !tcx.impl_method_has_trait_impl_trait_tys(impl_m.def_id) {
return;
}

// unreachable traits don't have any library guarantees, there's no need to do this check.
if trait_m
let is_internal = trait_m
.container_id(tcx)
.as_local()
.is_some_and(|trait_def_id| !tcx.effective_visibilities(()).is_reachable(trait_def_id))
{
return;
}
// If a type in the trait ref is private, then there's also no reason to do this check.
|| impl_trait_ref.args.iter().any(|arg| {
if let Some(ty) = arg.as_type()
&& let Some(self_visibility) = type_visibility(tcx, ty)
{
return !self_visibility.is_public();
}
false
});

// If a type in the trait ref is private, then there's also no reason to do this check.
let impl_def_id = impl_m.container_id(tcx);
for arg in impl_trait_ref.args {
if let Some(ty) = arg.as_type()
&& let Some(self_visibility) = type_visibility(tcx, ty)
&& !self_visibility.is_public()
{
return;
}
}

let impl_m_args = ty::GenericArgs::identity_for_item(tcx, impl_m.def_id);
let trait_m_to_impl_m_args = impl_m_args.rebase_onto(tcx, impl_def_id, impl_trait_ref.args);
let bound_trait_m_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_m_to_impl_m_args);
Expand Down Expand Up @@ -86,6 +83,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
trait_m.def_id,
impl_m.def_id,
None,
is_internal,
);
return;
};
Expand All @@ -105,6 +103,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
trait_m.def_id,
impl_m.def_id,
None,
is_internal,
);
return;
}
Expand Down Expand Up @@ -195,6 +194,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
trait_m.def_id,
impl_m.def_id,
Some(span),
is_internal,
);
return;
}
Expand Down Expand Up @@ -235,6 +235,7 @@ fn report_mismatched_rpitit_signature<'tcx>(
trait_m_def_id: DefId,
impl_m_def_id: DefId,
unmatched_bound: Option<Span>,
is_internal: bool,
) {
let mapping = std::iter::zip(
tcx.fn_sig(trait_m_def_id).skip_binder().bound_vars(),
Expand Down Expand Up @@ -287,7 +288,7 @@ fn report_mismatched_rpitit_signature<'tcx>(

let span = unmatched_bound.unwrap_or(span);
tcx.emit_node_span_lint(
REFINING_IMPL_TRAIT,
if is_internal { REFINING_IMPL_TRAIT_INTERNAL } else { REFINING_IMPL_TRAIT_REACHABLE },
tcx.local_def_id_to_hir_id(impl_m_def_id.expect_local()),
span,
crate::errors::ReturnPositionImplTraitInTraitRefined {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,7 @@ pub struct UnusedAssociatedTypeBounds {
#[derive(LintDiagnostic)]
#[diag(hir_analysis_rpitit_refined)]
#[note]
#[note(hir_analysis_feedback_note)]
pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> {
#[suggestion(applicability = "maybe-incorrect", code = "{pre}{return_ty}{post}")]
pub impl_return_span: Span,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,12 @@ fn register_builtins(store: &mut LintStore) {
// MACRO_USE_EXTERN_CRATE
);

add_lint_group!(
"refining_impl_trait",
REFINING_IMPL_TRAIT_REACHABLE,
REFINING_IMPL_TRAIT_INTERNAL
);

// Register renamed and removed lints.
store.register_renamed("single_use_lifetime", "single_use_lifetimes");
store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths");
Expand Down
64 changes: 56 additions & 8 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ declare_lint_pass! {
PROC_MACRO_BACK_COMPAT,
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
REFINING_IMPL_TRAIT,
REFINING_IMPL_TRAIT_INTERNAL,
REFINING_IMPL_TRAIT_REACHABLE,
RENAMED_AND_REMOVED_LINTS,
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
Expand Down Expand Up @@ -4320,7 +4321,9 @@ declare_lint! {

declare_lint! {
/// The `refining_impl_trait` lint detects usages of return-position impl
/// traits in trait signatures which are refined by implementations.
/// traits in trait signatures which are refined by implementations, meaning
/// the implementation adds information about the return type that is not
/// present in the trait.
///
/// ### Example
///
Expand Down Expand Up @@ -4350,13 +4353,58 @@ declare_lint! {
///
/// ### Explanation
///
/// Return-position impl trait in traits (RPITITs) desugar to associated types,
/// and callers of methods for types where the implementation is known are
/// Callers of methods for types where the implementation is known are
/// able to observe the types written in the impl signature. This may be
/// intended behavior, but may also pose a semver hazard for authors of libraries
/// who do not wish to make stronger guarantees about the types than what is
/// written in the trait signature.
pub REFINING_IMPL_TRAIT,
/// intended behavior, but may also lead to implementation details being
/// revealed unintentionally. In particular, it may pose a semver hazard
/// for authors of libraries who do not wish to make stronger guarantees
/// about the types than what is written in the trait signature.
pub REFINING_IMPL_TRAIT_REACHABLE,
Warn,
"impl trait in impl method signature does not match trait method signature",
}

declare_lint! {
/// The `refining_impl_trait` lint detects usages of return-position impl
/// traits in trait signatures which are refined by implementations, meaning
/// the implementation adds information about the return type that is not
/// present in the trait.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(refining_impl_trait)]
///
/// use std::fmt::Display;
///
/// pub trait AsDisplay {
/// fn as_display(&self) -> impl Display;
/// }
///
/// impl<'s> AsDisplay for &'s str {
/// fn as_display(&self) -> Self {
/// *self
/// }
/// }
///
/// fn main() {
/// // users can observe that the return type of
/// // `<&str as AsDisplay>::as_display()` is `&str`.
/// let x: &str = "".as_display();
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Callers of methods for types where the implementation is known are
/// able to observe the types written in the impl signature. This may be
/// intended behavior, but may also lead to implementation details being
/// revealed unintentionally. In particular, it may pose a semver hazard
/// for authors of libraries who do not wish to make stronger guarantees
/// about the types than what is written in the trait signature.
pub REFINING_IMPL_TRAIT_INTERNAL,
Warn,
"impl trait in impl method signature does not match trait method signature",
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ LL | fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
= note: we are soliciting feedback, see issue #12345 <https://TODO> for more information
note: the lint level is defined here
--> $DIR/async-example-desugared-boxed.rs:13:12
|
LL | #[warn(refining_impl_trait)]
| ^^^^^^^^^^^^^^^^^^^
= note: `#[warn(refining_impl_trait_reachable)]` implied by `#[warn(refining_impl_trait)]`
help: replace the return type so that it matches the trait
|
LL | fn foo(&self) -> impl Future<Output = i32> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ LL | fn foo(&self) -> MyFuture {
| ^^^^^^^^
|
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
= note: we are soliciting feedback, see issue #12345 <https://TODO> for more information
note: the lint level is defined here
--> $DIR/async-example-desugared-manual.rs:21:12
|
LL | #[warn(refining_impl_trait)]
| ^^^^^^^^^^^^^^^^^^^
= note: `#[warn(refining_impl_trait_reachable)]` implied by `#[warn(refining_impl_trait)]`
help: replace the return type so that it matches the trait
|
LL | fn foo(&self) -> impl Future<Output = i32> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,22 @@ LL | #![feature(return_type_notation)]
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted
warning: impl trait in impl method signature does not match trait method signature
--> $DIR/normalizing-self-auto-trait-issue-109924.rs:15:5
|
LL | async fn bar(&self);
| -------------------- return type from trait method defined here
...
LL | async fn bar(&self) {}
| ^^^^^^^^^^^^^^^^^^^ this bound is stronger than that defined on the trait
|
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
= note: we are soliciting feedback, see issue #12345 <https://TODO> for more information
= note: `#[warn(refining_impl_trait_internal)]` on by default
help: replace the return type so that it matches the trait
|
LL | () {}
| ~~

warning: 2 warnings emitted

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ LL | fn iter(&self) -> impl 'a + Iterator<Item = I::Item<'a>> {
| ^^ this bound is stronger than that defined on the trait
|
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
= note: `#[warn(refining_impl_trait)]` on by default
= note: we are soliciting feedback, see issue #12345 <https://TODO> for more information
= note: `#[warn(refining_impl_trait_reachable)]` on by default
help: replace the return type so that it matches the trait
|
LL | fn iter(&self) -> impl Iterator<Item = <Self as Iterable>::Item<'_>> + '_ {
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/impl-trait/in-trait/foreign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ impl Foo for Local {

struct LocalIgnoreRefining;
impl Foo for LocalIgnoreRefining {
#[deny(refining_impl_trait)]
#[warn(refining_impl_trait)]
fn bar(self) -> Arc<String> {
//~^ WARN impl method signature does not match trait method signature
Arc::new(String::new())
}
}
Expand Down
21 changes: 21 additions & 0 deletions tests/ui/impl-trait/in-trait/foreign.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
warning: impl trait in impl method signature does not match trait method signature
--> $DIR/foreign.rs:23:21
|
LL | fn bar(self) -> Arc<String> {
| ^^^^^^^^^^^
|
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
= note: we are soliciting feedback, see issue #12345 <https://TODO> for more information
note: the lint level is defined here
--> $DIR/foreign.rs:22:12
|
LL | #[warn(refining_impl_trait)]
| ^^^^^^^^^^^^^^^^^^^
= note: `#[warn(refining_impl_trait_internal)]` implied by `#[warn(refining_impl_trait)]`
help: replace the return type so that it matches the trait
|
LL | fn bar(self) -> impl Deref<Target = impl Sized> {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

warning: 1 warning emitted

3 changes: 3 additions & 0 deletions tests/ui/impl-trait/in-trait/refine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ impl Foo for C {
struct Private;
impl Foo for Private {
fn bar() -> () {}
//~^ ERROR impl method signature does not match trait method signature
}

pub trait Arg<A> {
fn bar() -> impl Sized;
}
impl Arg<Private> for A {
fn bar() -> () {}
//~^ ERROR impl method signature does not match trait method signature
}

pub trait Late {
Expand All @@ -52,6 +54,7 @@ mod unreachable {
struct E;
impl UnreachablePub for E {
fn bar() {}
//~^ ERROR impl method signature does not match trait method signature
}
}

Expand Down
Loading

0 comments on commit 68a8617

Please sign in to comment.