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