Skip to content

Commit c4c9415

Browse files
author
mejrs
committed
Wrapper suggestions
1 parent 57ee5cf commit c4c9415

File tree

9 files changed

+316
-67
lines changed

9 files changed

+316
-67
lines changed

compiler/rustc_hir_analysis/src/check/method/suggest.rs

Lines changed: 146 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! found or is otherwise invalid.
33
44
use crate::check::FnCtxt;
5+
use rustc_ast::ast::Mutability;
56
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
67
use rustc_errors::{
78
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@@ -30,7 +31,7 @@ use rustc_trait_selection::traits::{
3031
use std::cmp::Ordering;
3132
use std::iter;
3233

33-
use super::probe::{IsSuggestion, Mode, ProbeScope};
34+
use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
3435
use super::{CandidateSource, MethodError, NoMatchData};
3536

3637
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -983,7 +984,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
983984
self.check_for_field_method(&mut err, source, span, actual, item_name);
984985
}
985986

986-
self.check_for_unwrap_self(&mut err, source, span, actual, item_name);
987+
self.check_for_inner_self(&mut err, source, span, actual, item_name);
987988

988989
bound_spans.sort();
989990
bound_spans.dedup();
@@ -1395,7 +1396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13951396
}
13961397
}
13971398

1398-
fn check_for_unwrap_self(
1399+
fn check_for_inner_self(
13991400
&self,
14001401
err: &mut Diagnostic,
14011402
source: SelfSource<'tcx>,
@@ -1408,81 +1409,159 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14081409
let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
14091410

14101411
let ty::Adt(kind, substs) = actual.kind() else { return; };
1411-
if !kind.is_enum() {
1412-
return;
1413-
}
1412+
match kind.adt_kind() {
1413+
ty::AdtKind::Enum => {
1414+
let matching_variants: Vec<_> = kind
1415+
.variants()
1416+
.iter()
1417+
.flat_map(|variant| {
1418+
let [field] = &variant.fields[..] else { return None; };
1419+
let field_ty = field.ty(tcx, substs);
1420+
1421+
// Skip `_`, since that'll just lead to ambiguity.
1422+
if self.resolve_vars_if_possible(field_ty).is_ty_var() {
1423+
return None;
1424+
}
14141425

1415-
let matching_variants: Vec<_> = kind
1416-
.variants()
1417-
.iter()
1418-
.flat_map(|variant| {
1419-
let [field] = &variant.fields[..] else { return None; };
1420-
let field_ty = field.ty(tcx, substs);
1426+
self.lookup_probe(
1427+
span,
1428+
item_name,
1429+
field_ty,
1430+
call_expr,
1431+
ProbeScope::AllTraits,
1432+
)
1433+
.ok()
1434+
.map(|pick| (variant, field, pick))
1435+
})
1436+
.collect();
1437+
1438+
let ret_ty_matches = |diagnostic_item| {
1439+
if let Some(ret_ty) = self
1440+
.ret_coercion
1441+
.as_ref()
1442+
.map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
1443+
&& let ty::Adt(kind, _) = ret_ty.kind()
1444+
&& tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
1445+
{
1446+
true
1447+
} else {
1448+
false
1449+
}
1450+
};
14211451

1422-
// Skip `_`, since that'll just lead to ambiguity.
1423-
if self.resolve_vars_if_possible(field_ty).is_ty_var() {
1424-
return None;
1452+
match &matching_variants[..] {
1453+
[(_, field, pick)] => {
1454+
let self_ty = field.ty(tcx, substs);
1455+
err.span_note(
1456+
tcx.def_span(pick.item.def_id),
1457+
&format!("the method `{item_name}` exists on the type `{self_ty}`"),
1458+
);
1459+
let (article, kind, variant, question) =
1460+
if tcx.is_diagnostic_item(sym::Result, kind.did()) {
1461+
("a", "Result", "Err", ret_ty_matches(sym::Result))
1462+
} else if tcx.is_diagnostic_item(sym::Option, kind.did()) {
1463+
("an", "Option", "None", ret_ty_matches(sym::Option))
1464+
} else {
1465+
return;
1466+
};
1467+
if question {
1468+
err.span_suggestion_verbose(
1469+
expr.span.shrink_to_hi(),
1470+
format!(
1471+
"use the `?` operator to extract the `{self_ty}` value, propagating \
1472+
{article} `{kind}::{variant}` value to the caller"
1473+
),
1474+
"?",
1475+
Applicability::MachineApplicable,
1476+
);
1477+
} else {
1478+
err.span_suggestion_verbose(
1479+
expr.span.shrink_to_hi(),
1480+
format!(
1481+
"consider using `{kind}::expect` to unwrap the `{self_ty}` value, \
1482+
panicking if the value is {article} `{kind}::{variant}`"
1483+
),
1484+
".expect(\"REASON\")",
1485+
Applicability::HasPlaceholders,
1486+
);
1487+
}
1488+
}
1489+
// FIXME(compiler-errors): Support suggestions for other matching enum variants
1490+
_ => {}
14251491
}
1426-
1427-
self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits)
1428-
.ok()
1429-
.map(|pick| (variant, field, pick))
1430-
})
1431-
.collect();
1432-
1433-
let ret_ty_matches = |diagnostic_item| {
1434-
if let Some(ret_ty) = self
1435-
.ret_coercion
1436-
.as_ref()
1437-
.map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
1438-
&& let ty::Adt(kind, _) = ret_ty.kind()
1439-
&& tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
1440-
{
1441-
true
1442-
} else {
1443-
false
14441492
}
1445-
};
1446-
1447-
match &matching_variants[..] {
1448-
[(_, field, pick)] => {
1449-
let self_ty = field.ty(tcx, substs);
1450-
err.span_note(
1451-
tcx.def_span(pick.item.def_id),
1452-
&format!("the method `{item_name}` exists on the type `{self_ty}`"),
1453-
);
1454-
let (article, kind, variant, question) =
1455-
if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) {
1456-
("a", "Result", "Err", ret_ty_matches(sym::Result))
1457-
} else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) {
1458-
("an", "Option", "None", ret_ty_matches(sym::Option))
1459-
} else {
1460-
return;
1461-
};
1462-
if question {
1493+
// Target wrapper types - types that wrap or pretend to wrap another type,
1494+
// perhaps this inner type is meant to be called?
1495+
ty::AdtKind::Struct | ty::AdtKind::Union => {
1496+
let [first] = ***substs else { return; };
1497+
let ty::GenericArgKind::Type(ty) = first.unpack() else { return; };
1498+
let Ok(pick) = self.lookup_probe(
1499+
span,
1500+
item_name,
1501+
ty,
1502+
call_expr,
1503+
ProbeScope::AllTraits,
1504+
) else { return; };
1505+
1506+
let name = self.ty_to_value_string(actual);
1507+
let inner_id = kind.did();
1508+
1509+
if tcx.is_diagnostic_item(sym::LocalKey, inner_id) {
1510+
err.help("use `with` or `try_with` to access the contents of threadlocals");
1511+
} else if Some(kind.did()) == tcx.lang_items().maybe_uninit() {
1512+
err.help(format!(
1513+
"if this `{name}` has been initialized, \
1514+
use one of the `assume_init` methods to access the inner value"
1515+
));
1516+
} else if tcx.is_diagnostic_item(sym::RefCell, inner_id) {
1517+
match pick.autoref_or_ptr_adjustment {
1518+
Some(AutorefOrPtrAdjustment::Autoref {
1519+
mutbl: Mutability::Not, ..
1520+
}) => {
1521+
err.span_suggestion_verbose(
1522+
expr.span.shrink_to_hi(),
1523+
format!(
1524+
"use `.borrow()` to borrow the {ty}, \
1525+
panicking if any outstanding mutable borrows exist."
1526+
),
1527+
".borrow()",
1528+
Applicability::MaybeIncorrect,
1529+
);
1530+
}
1531+
Some(AutorefOrPtrAdjustment::Autoref {
1532+
mutbl: Mutability::Mut, ..
1533+
}) => {
1534+
err.span_suggestion_verbose(
1535+
expr.span.shrink_to_hi(),
1536+
format!(
1537+
"use `.borrow_mut()` to mutably borrow the {ty}, \
1538+
panicking if any outstanding borrows exist."
1539+
),
1540+
".borrow_mut()",
1541+
Applicability::MaybeIncorrect,
1542+
);
1543+
}
1544+
_ => return,
1545+
}
1546+
} else if tcx.is_diagnostic_item(sym::Mutex, inner_id) {
14631547
err.span_suggestion_verbose(
14641548
expr.span.shrink_to_hi(),
14651549
format!(
1466-
"use the `?` operator to extract the `{self_ty}` value, propagating \
1467-
{article} `{kind}::{variant}` value to the caller"
1550+
"use `.lock()` to borrow the {ty}, \
1551+
blocking the current thread until it can be acquired"
14681552
),
1469-
"?",
1470-
Applicability::MachineApplicable,
1553+
".lock().unwrap()",
1554+
Applicability::MaybeIncorrect,
14711555
);
14721556
} else {
1473-
err.span_suggestion_verbose(
1474-
expr.span.shrink_to_hi(),
1475-
format!(
1476-
"consider using `{kind}::expect` to unwrap the `{self_ty}` value, \
1477-
panicking if the value is {article} `{kind}::{variant}`"
1478-
),
1479-
".expect(\"REASON\")",
1480-
Applicability::HasPlaceholders,
1481-
);
1482-
}
1557+
return;
1558+
};
1559+
1560+
err.span_note(
1561+
tcx.def_span(pick.item.def_id),
1562+
&format!("the method `{item_name}` exists on the type `{ty}`"),
1563+
);
14831564
}
1484-
// FIXME(compiler-errors): Support suggestions for other matching enum variants
1485-
_ => {}
14861565
}
14871566
}
14881567

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ symbols! {
224224
Left,
225225
LinkedList,
226226
LintPass,
227+
LocalKey,
227228
Mutex,
228229
MutexGuard,
229230
N,
@@ -266,6 +267,7 @@ symbols! {
266267
Rc,
267268
Ready,
268269
Receiver,
270+
RefCell,
269271
Relaxed,
270272
Release,
271273
Result,

library/core/src/cell.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ impl<T, const N: usize> Cell<[T; N]> {
614614
/// A mutable memory location with dynamically checked borrow rules
615615
///
616616
/// See the [module-level documentation](self) for more.
617+
#[rustc_diagnostic_item = "RefCell"]
617618
#[stable(feature = "rust1", since = "1.0.0")]
618619
pub struct RefCell<T: ?Sized> {
619620
borrow: Cell<BorrowFlag>,

library/std/src/thread/local.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ use crate::fmt;
9595
/// [loader lock]: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
9696
/// [`JoinHandle::join`]: crate::thread::JoinHandle::join
9797
/// [`with`]: LocalKey::with
98+
#[rustc_diagnostic_item = "LocalKey"]
9899
#[stable(feature = "rust1", since = "1.0.0")]
99100
pub struct LocalKey<T: 'static> {
100101
// This outer `LocalKey<T>` type is what's going to be stored in statics,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// compile-flags: --edition=2021
2+
// run-rustfix
3+
4+
pub struct Struct<T> {
5+
pub p: T,
6+
}
7+
8+
impl<T> Struct<T> {
9+
pub fn method(&self) {}
10+
11+
pub fn some_mutable_method(&mut self) {}
12+
}
13+
14+
fn main() {
15+
let other_item = std::cell::RefCell::new(Struct { p: 42_u32 });
16+
17+
other_item.borrow().method();
18+
//~^ ERROR no method named `method` found for struct `RefCell` in the current scope [E0599]
19+
//~| HELP use `.borrow()` to borrow the Struct<u32>, panicking if any outstanding mutable borrows exist.
20+
21+
other_item.borrow_mut().some_mutable_method();
22+
//~^ ERROR no method named `some_mutable_method` found for struct `RefCell` in the current scope [E0599]
23+
//~| HELP use `.borrow_mut()` to mutably borrow the Struct<u32>, panicking if any outstanding borrows exist.
24+
25+
let another_item = std::sync::Mutex::new(Struct { p: 42_u32 });
26+
27+
another_item.lock().unwrap().method();
28+
//~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599]
29+
//~| HELP use `.lock()` to borrow the Struct<u32>, blocking the current thread until it can be acquired
30+
}

src/test/ui/suggestions/inner_type.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// compile-flags: --edition=2021
2+
// run-rustfix
3+
4+
pub struct Struct<T> {
5+
pub p: T,
6+
}
7+
8+
impl<T> Struct<T> {
9+
pub fn method(&self) {}
10+
11+
pub fn some_mutable_method(&mut self) {}
12+
}
13+
14+
fn main() {
15+
let other_item = std::cell::RefCell::new(Struct { p: 42_u32 });
16+
17+
other_item.method();
18+
//~^ ERROR no method named `method` found for struct `RefCell` in the current scope [E0599]
19+
//~| HELP use `.borrow()` to borrow the Struct<u32>, panicking if any outstanding mutable borrows exist.
20+
21+
other_item.some_mutable_method();
22+
//~^ ERROR no method named `some_mutable_method` found for struct `RefCell` in the current scope [E0599]
23+
//~| HELP use `.borrow_mut()` to mutably borrow the Struct<u32>, panicking if any outstanding borrows exist.
24+
25+
let another_item = std::sync::Mutex::new(Struct { p: 42_u32 });
26+
27+
another_item.method();
28+
//~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599]
29+
//~| HELP use `.lock()` to borrow the Struct<u32>, blocking the current thread until it can be acquired
30+
}

0 commit comments

Comments
 (0)