]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/method/prelude2021.rs
b7e41525bde47156a28cc55031a5d3237369618e
[rust.git] / compiler / rustc_typeck / src / check / method / prelude2021.rs
1 use rustc_ast::Mutability;
2 use rustc_errors::Applicability;
3 use rustc_hir as hir;
4 use rustc_middle::ty::Ty;
5 use rustc_session::lint::builtin::FUTURE_PRELUDE_COLLISION;
6 use rustc_span::symbol::{sym, Ident};
7 use rustc_span::Span;
8
9 use crate::check::{
10     method::probe::{self, Pick},
11     FnCtxt,
12 };
13
14 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15     pub(super) fn lint_dot_call_from_2018(
16         &self,
17         self_ty: Ty<'tcx>,
18         segment: &hir::PathSegment<'_>,
19         span: Span,
20         call_expr: &'tcx hir::Expr<'tcx>,
21         self_expr: &'tcx hir::Expr<'tcx>,
22         pick: &Pick<'tcx>,
23         args: &'tcx [hir::Expr<'tcx>],
24     ) {
25         debug!(
26             "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
27             segment.ident, self_ty, call_expr, self_expr
28         );
29
30         // Rust 2021 and later is already using the new prelude
31         if span.rust_2021() {
32             return;
33         }
34
35         // These are the method names that were added to prelude in Rust 2021
36         if !matches!(segment.ident.name, sym::try_into) {
37             return;
38         }
39
40         // No need to lint if method came from std/core, as that will now be in the prelude
41         if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
42             return;
43         }
44
45         self.tcx.struct_span_lint_hir(
46             FUTURE_PRELUDE_COLLISION,
47             call_expr.hir_id,
48             call_expr.span,
49             |lint| {
50                 let sp = call_expr.span;
51                 let trait_name = self.tcx.def_path_str(pick.item.container.id());
52
53                 let mut lint = lint.build(&format!(
54                     "trait method `{}` will become ambiguous in Rust 2021",
55                     segment.ident.name
56                 ));
57
58                 if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) {
59                     let derefs = "*".repeat(pick.autoderefs);
60
61                     let autoref = match pick.autoref_or_ptr_adjustment {
62                         Some(probe::AutorefOrPtrAdjustment::Autoref {
63                             mutbl: Mutability::Mut,
64                             ..
65                         }) => "&mut ",
66                         Some(probe::AutorefOrPtrAdjustment::Autoref {
67                             mutbl: Mutability::Not,
68                             ..
69                         }) => "&",
70                         Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
71                     };
72                     let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
73                         pick.autoref_or_ptr_adjustment
74                     {
75                         format!("{}{} as *const _", derefs, self_expr)
76                     } else {
77                         format!("{}{}{}", autoref, derefs, self_expr)
78                     };
79                     let args = args
80                         .iter()
81                         .skip(1)
82                         .map(|arg| {
83                             format!(
84                                 ", {}",
85                                 self.sess().source_map().span_to_snippet(arg.span).unwrap()
86                             )
87                         })
88                         .collect::<String>();
89
90                     lint.span_suggestion(
91                         sp,
92                         "disambiguate the associated function",
93                         format!(
94                             "{}::{}({}{})",
95                             trait_name, segment.ident.name, self_adjusted, args
96                         ),
97                         Applicability::MachineApplicable,
98                     );
99                 } else {
100                     lint.span_help(
101                         sp,
102                         &format!(
103                             "disambiguate the associated function with `{}::{}(...)`",
104                             trait_name, segment.ident,
105                         ),
106                     );
107                 }
108
109                 lint.emit();
110             },
111         );
112     }
113
114     pub(super) fn lint_fully_qualified_call_from_2018(
115         &self,
116         span: Span,
117         method_name: Ident,
118         self_ty: Ty<'tcx>,
119         self_ty_span: Span,
120         expr_id: hir::HirId,
121         pick: &Pick<'tcx>,
122     ) {
123         // Rust 2021 and later is already using the new prelude
124         if span.rust_2021() {
125             return;
126         }
127
128         // These are the fully qualified methods added to prelude in Rust 2021
129         if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
130             return;
131         }
132
133         // No need to lint if method came from std/core, as that will now be in the prelude
134         if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
135             return;
136         }
137
138         // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
139         // since such methods take precedence over trait methods.
140         if matches!(pick.kind, probe::PickKind::InherentImplPick) {
141             return;
142         }
143
144         self.tcx.struct_span_lint_hir(FUTURE_PRELUDE_COLLISION, expr_id, span, |lint| {
145             // "type" refers to either a type or, more likely, a trait from which
146             // the associated function or method is from.
147             let type_name = self.tcx.def_path_str(pick.item.container.id());
148             let type_generics = self.tcx.generics_of(pick.item.container.id());
149
150             let parameter_count = type_generics.count() - (type_generics.has_self as usize);
151             let trait_name = if parameter_count == 0 {
152                 type_name
153             } else {
154                 format!(
155                     "{}<{}>",
156                     type_name,
157                     std::iter::repeat("_").take(parameter_count).collect::<Vec<_>>().join(", ")
158                 )
159             };
160
161             let mut lint = lint.build(&format!(
162                 "trait-associated function `{}` will become ambiguous in Rust 2021",
163                 method_name.name
164             ));
165
166             let self_ty = self
167                 .sess()
168                 .source_map()
169                 .span_to_snippet(self_ty_span)
170                 .unwrap_or_else(|_| self_ty.to_string());
171
172             lint.span_suggestion(
173                 span,
174                 "disambiguate the associated function",
175                 format!("<{} as {}>::{}", self_ty, trait_name, method_name.name,),
176                 Applicability::MachineApplicable,
177             );
178
179             lint.emit();
180         });
181     }
182 }