|
2 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ |
4 | 4 |
|
5 | | -use rustc_ast::Mutability; |
6 | | -use rustc_hir::def::{DefKind, Res}; |
7 | | -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; |
8 | | -use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, PrimTy, TraitItemRef}; |
| 5 | +use rustc_hir::def::DefKind; |
| 6 | +use rustc_hir::def_id::{CrateNum, DefId}; |
9 | 7 | use rustc_infer::infer::TyCtxtInferExt; |
| 8 | +use rustc_infer::traits::{EvaluationResult, Obligation, ObligationCause}; |
10 | 9 | use rustc_lint::LateContext; |
11 | | -use rustc_middle::ty::fast_reject::SimplifiedType; |
12 | | -use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeVisitableExt, TypingEnv}; |
| 10 | +use rustc_middle::ty::{self, GenericArg, TraitRef, Ty, TyCtxt, TypeVisitableExt}; |
13 | 11 | use rustc_span::hygiene::{ExpnKind, MacroKind}; |
14 | | -use rustc_span::symbol::{Ident, Symbol}; |
| 12 | +use rustc_span::symbol::Symbol; |
15 | 13 | use rustc_span::{Span, DUMMY_SP}; |
16 | | -use rustc_trait_selection::infer::InferCtxtExt; |
17 | | -use rustc_type_ir::{FloatTy, IntTy, UintTy}; |
| 14 | +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; |
| 15 | +use rustc_type_ir::Upcast as _; |
18 | 16 |
|
19 | 17 | /// check if a DefId's path matches the given absolute type path |
20 | 18 | /// usage e.g. with |
@@ -65,286 +63,101 @@ macro_rules! symbols { |
65 | 63 | } |
66 | 64 | } |
67 | 65 |
|
68 | | -/* |
69 | | -Stuff copied from clippy: |
70 | | -*/ |
71 | | - |
72 | | -// This is adapted from |
73 | | -// https://github.com/rust-lang/rust-clippy/blob/546408be416f0355a39601c1457b37727bc74395/clippy_utils/src/lib.rs#L517. |
74 | | -fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx { |
75 | | - let ty = match name { |
76 | | - "bool" => SimplifiedType::Bool, |
77 | | - "char" => SimplifiedType::Char, |
78 | | - "str" => SimplifiedType::Str, |
79 | | - "array" => SimplifiedType::Array, |
80 | | - "slice" => SimplifiedType::Slice, |
81 | | - // FIXME: rustdoc documents these two using just `pointer`. |
82 | | - // |
83 | | - // Maybe this is something we should do here too. |
84 | | - "const_ptr" => SimplifiedType::Ptr(Mutability::Not), |
85 | | - "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut), |
86 | | - "isize" => SimplifiedType::Int(IntTy::Isize), |
87 | | - "i8" => SimplifiedType::Int(IntTy::I8), |
88 | | - "i16" => SimplifiedType::Int(IntTy::I16), |
89 | | - "i32" => SimplifiedType::Int(IntTy::I32), |
90 | | - "i64" => SimplifiedType::Int(IntTy::I64), |
91 | | - "i128" => SimplifiedType::Int(IntTy::I128), |
92 | | - "usize" => SimplifiedType::Uint(UintTy::Usize), |
93 | | - "u8" => SimplifiedType::Uint(UintTy::U8), |
94 | | - "u16" => SimplifiedType::Uint(UintTy::U16), |
95 | | - "u32" => SimplifiedType::Uint(UintTy::U32), |
96 | | - "u64" => SimplifiedType::Uint(UintTy::U64), |
97 | | - "u128" => SimplifiedType::Uint(UintTy::U128), |
98 | | - "f32" => SimplifiedType::Float(FloatTy::F32), |
99 | | - "f64" => SimplifiedType::Float(FloatTy::F64), |
100 | | - #[allow(trivial_casts)] |
101 | | - _ => { |
102 | | - return [].iter().copied(); |
103 | | - }, |
104 | | - }; |
105 | | - |
106 | | - tcx.incoherent_impls(ty).iter().copied() |
107 | | -} |
108 | | - |
109 | | -fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> { |
110 | | - match tcx.def_kind(def_id) { |
111 | | - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx |
112 | | - .module_children(def_id) |
113 | | - .iter() |
114 | | - .filter(|item| item.ident.name == name) |
115 | | - .map(|child| child.res.expect_non_local()) |
116 | | - .collect(), |
117 | | - DefKind::Impl { .. } => tcx |
118 | | - .associated_item_def_ids(def_id) |
119 | | - .iter() |
120 | | - .copied() |
121 | | - .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name) |
122 | | - .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)) |
123 | | - .collect(), |
124 | | - _ => Vec::new(), |
125 | | - } |
126 | | -} |
127 | | - |
128 | | -// This is adapted from clippy: |
129 | | -// https://github.com/rust-lang/rust-clippy/blob/546408be416f0355a39601c1457b37727bc74395/clippy_utils/src/lib.rs#L574. |
130 | | -fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> { |
131 | | - let hir = tcx.hir(); |
132 | | - |
133 | | - let root_mod; |
134 | | - let item_kind = match tcx.hir_node_by_def_id(local_id) { |
135 | | - Node::Crate(r#mod) => { |
136 | | - root_mod = ItemKind::Mod(r#mod); |
137 | | - &root_mod |
138 | | - }, |
139 | | - Node::Item(item) => &item.kind, |
140 | | - _ => return Vec::new(), |
141 | | - }; |
142 | | - |
143 | | - let res = |ident: Ident, owner_id: OwnerId| { |
144 | | - if ident.name == name { |
145 | | - let def_id = owner_id.to_def_id(); |
146 | | - Some(Res::Def(tcx.def_kind(def_id), def_id)) |
147 | | - } else { |
148 | | - None |
149 | | - } |
150 | | - }; |
151 | | - |
152 | | - match item_kind { |
153 | | - ItemKind::Mod(r#mod) => r#mod |
154 | | - .item_ids |
155 | | - .iter() |
156 | | - .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id)) |
157 | | - .collect(), |
158 | | - ItemKind::Impl(r#impl) => r#impl |
159 | | - .items |
160 | | - .iter() |
161 | | - .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)) |
162 | | - .collect(), |
163 | | - ItemKind::Trait(.., trait_item_refs) => trait_item_refs |
164 | | - .iter() |
165 | | - .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)) |
166 | | - .collect(), |
167 | | - _ => Vec::new(), |
168 | | - } |
169 | | -} |
170 | | - |
171 | | -fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> { |
172 | | - if let Some(local_id) = def_id.as_local() { |
173 | | - local_item_children_by_name(tcx, local_id, name) |
174 | | - } else { |
175 | | - non_local_item_children_by_name(tcx, def_id, name) |
176 | | - } |
177 | | -} |
178 | | - |
179 | | -/// Resolves a def path like `std::vec::Vec`. |
180 | | -/// |
181 | | -/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. |
182 | | -/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. |
183 | | -/// |
184 | | -/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec` |
185 | | -/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. |
186 | | -/// |
187 | | -/// This function is expensive and should be used sparingly. |
188 | | -pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> { |
189 | | - fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ { |
190 | | - tcx.crates(()) |
191 | | - .iter() |
192 | | - .copied() |
193 | | - .filter(move |&num| tcx.crate_name(num) == name) |
194 | | - .map(CrateNum::as_def_id) |
195 | | - } |
196 | | - |
197 | | - let tcx = cx.tcx; |
198 | | - |
199 | | - let (base, mut path) = match *path { |
200 | | - [primitive] => { |
201 | | - return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; |
202 | | - }, |
203 | | - [base, ref path @ ..] => (base, path), |
204 | | - _ => return Vec::new(), |
205 | | - }; |
206 | | - |
207 | | - let base_sym = Symbol::intern(base); |
208 | | - |
209 | | - let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym { |
210 | | - Some(LOCAL_CRATE.as_def_id()) |
211 | | - } else { |
212 | | - None |
213 | | - }; |
214 | | - |
215 | | - let starts = find_primitive_impls(tcx, base) |
216 | | - .chain(find_crates(tcx, base_sym)) |
217 | | - .chain(local_crate) |
218 | | - .map(|id| Res::Def(tcx.def_kind(id), id)); |
219 | | - |
220 | | - let mut resolutions: Vec<Res> = starts.collect(); |
221 | | - |
222 | | - while let [segment, rest @ ..] = path { |
223 | | - path = rest; |
224 | | - let segment = Symbol::intern(segment); |
225 | | - |
226 | | - resolutions = resolutions |
227 | | - .into_iter() |
228 | | - .filter_map(|res| res.opt_def_id()) |
229 | | - .flat_map(|def_id| { |
230 | | - // When the current def_id is e.g. `struct S`, check the impl items in |
231 | | - // `impl S { ... }` |
232 | | - let inherent_impl_children = tcx |
233 | | - .inherent_impls(def_id) |
234 | | - .into_iter() |
235 | | - .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); |
236 | | - |
237 | | - let direct_children = item_children_by_name(tcx, def_id, segment); |
238 | | - |
239 | | - inherent_impl_children.chain(direct_children) |
240 | | - }) |
241 | | - .collect(); |
242 | | - } |
243 | | - |
244 | | - resolutions |
245 | | -} |
246 | | - |
247 | | -pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> { |
248 | | - def_path_res(cx, path) |
249 | | - .into_iter() |
250 | | - .find_map(|res| match res { |
251 | | - Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), |
252 | | - _ => None, |
253 | | - }) |
| 66 | +pub fn find_first_crate<'tcx>(tcx: &TyCtxt<'tcx>, crate_name: Symbol) -> Option<CrateNum> { |
| 67 | + tcx.crates(()) |
| 68 | + .iter() |
| 69 | + .find(|c| tcx.crate_name(**c) == crate_name) |
| 70 | + .copied() |
254 | 71 | } |
255 | 72 |
|
256 | | -// These are special variants made from the above functions. |
257 | | -/// Resolves a def path like `std::vec::Vec`, but searches only local crate |
258 | | -/// |
259 | | -/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec` |
260 | | -/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. |
261 | | -/// |
262 | | -/// This function is less expensive than `def_path_res` and should be used sparingly. |
263 | | -pub fn def_local_res(cx: &LateContext<'_>, path: &str) -> Vec<Res> { |
264 | | - let tcx = cx.tcx; |
265 | | - let local_crate = LOCAL_CRATE.as_def_id(); |
266 | | - let starts = Res::Def(tcx.def_kind(local_crate), local_crate); |
267 | | - let mut resolutions: Vec<Res> = vec![starts]; |
268 | | - let segment = Symbol::intern(path); |
269 | | - |
270 | | - resolutions = resolutions |
271 | | - .into_iter() |
272 | | - .filter_map(|res| res.opt_def_id()) |
273 | | - .flat_map(|def_id| { |
274 | | - // When the current def_id is e.g. `struct S`, check the impl items in |
275 | | - // `impl S { ... }` |
276 | | - let inherent_impl_children = tcx |
277 | | - .inherent_impls(def_id) |
278 | | - .into_iter() |
279 | | - .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); |
280 | | - |
281 | | - let direct_children = item_children_by_name(tcx, def_id, segment); |
282 | | - |
283 | | - inherent_impl_children.chain(direct_children) |
284 | | - }) |
285 | | - .collect(); |
286 | | - |
287 | | - resolutions |
| 73 | +pub fn trait_in_crate<'tcx>( |
| 74 | + tcx: &TyCtxt<'tcx>, |
| 75 | + krate: CrateNum, |
| 76 | + trait_sym: Symbol, |
| 77 | +) -> Option<DefId> { |
| 78 | + tcx.traits(krate) |
| 79 | + .iter() |
| 80 | + .find(|id| tcx.opt_item_name(**id) == Some(trait_sym)) |
| 81 | + .copied() |
288 | 82 | } |
289 | 83 |
|
290 | | -pub fn get_local_trait_def_id(cx: &LateContext<'_>, path: &str) -> Option<DefId> { |
291 | | - def_local_res(cx, path) |
292 | | - .into_iter() |
293 | | - .find_map(|res| match res { |
294 | | - Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), |
295 | | - _ => None, |
296 | | - }) |
297 | | -} |
| 84 | +/* |
| 85 | +Stuff copied from clippy: |
| 86 | +*/ |
298 | 87 |
|
299 | 88 | /// Checks whether a type implements a trait. |
300 | 89 | /// The function returns false in case the type contains an inference variable. |
301 | 90 | /// |
302 | | -/// See: |
303 | | -/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`]. |
304 | | -/// * [Common tools for writing lints] for an example how to use this function and other options. |
| 91 | +/// See [Common tools for writing lints] for an example how to use this function and other options. |
305 | 92 | /// |
306 | 93 | /// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait |
307 | 94 | pub fn implements_trait<'tcx>( |
308 | 95 | cx: &LateContext<'tcx>, |
309 | 96 | ty: Ty<'tcx>, |
310 | 97 | trait_id: DefId, |
311 | | - ty_params: &[GenericArg<'tcx>], |
| 98 | + args: &[GenericArg<'tcx>], |
312 | 99 | ) -> bool { |
313 | | - implements_trait_with_env( |
| 100 | + implements_trait_with_env_from_iter( |
314 | 101 | cx.tcx, |
315 | 102 | cx.typing_env(), |
316 | 103 | ty, |
317 | 104 | trait_id, |
318 | | - ty_params.iter().map(|&arg| Some(arg)), |
| 105 | + None, |
| 106 | + args.iter().map(|&x| Some(x)), |
319 | 107 | ) |
320 | 108 | } |
321 | 109 |
|
322 | | -/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. |
323 | | -pub fn implements_trait_with_env<'tcx>( |
| 110 | +/// Same as `implements_trait_from_env` but takes the arguments as an iterator. |
| 111 | +pub fn implements_trait_with_env_from_iter<'tcx>( |
324 | 112 | tcx: TyCtxt<'tcx>, |
325 | | - typing_env: TypingEnv<'tcx>, |
326 | | - ty: ty::Ty<'tcx>, |
| 113 | + typing_env: ty::TypingEnv<'tcx>, |
| 114 | + ty: Ty<'tcx>, |
327 | 115 | trait_id: DefId, |
328 | | - ty_params: impl IntoIterator<Item = Option<GenericArg<'tcx>>>, |
| 116 | + callee_id: Option<DefId>, |
| 117 | + args: impl IntoIterator<Item = impl Into<Option<GenericArg<'tcx>>>>, |
329 | 118 | ) -> bool { |
| 119 | + // Clippy shouldn't have infer types |
| 120 | + assert!(!ty.has_infer()); |
| 121 | + |
| 122 | + // If a `callee_id` is passed, then we assert that it is a body owner |
| 123 | + // through calling `body_owner_kind`, which would panic if the callee |
| 124 | + // does not have a body. |
| 125 | + if let Some(callee_id) = callee_id { |
| 126 | + let _ = tcx.hir_body_owner_kind(callee_id); |
| 127 | + } |
| 128 | + |
330 | 129 | let ty = tcx.erase_regions(ty); |
331 | 130 | if ty.has_escaping_bound_vars() { |
332 | 131 | return false; |
333 | 132 | } |
334 | 133 |
|
335 | 134 | let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); |
336 | | - let ty_params = tcx.mk_args_from_iter( |
337 | | - ty_params |
338 | | - .into_iter() |
339 | | - .map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into())), |
| 135 | + let args = args |
| 136 | + .into_iter() |
| 137 | + .map(|arg| { |
| 138 | + arg.into() |
| 139 | + .unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into()) |
| 140 | + }) |
| 141 | + .collect::<Vec<_>>(); |
| 142 | + |
| 143 | + let trait_ref = TraitRef::new( |
| 144 | + tcx, |
| 145 | + trait_id, |
| 146 | + [GenericArg::from(ty)].into_iter().chain(args), |
340 | 147 | ); |
| 148 | + |
| 149 | + debug_assert!( |
| 150 | + matches!(tcx.def_kind(trait_id), DefKind::Trait | DefKind::TraitAlias), |
| 151 | + "`DefId` must belong to a trait or trait alias" |
| 152 | + ); |
| 153 | + |
| 154 | + let obligation = Obligation { |
| 155 | + cause: ObligationCause::dummy(), |
| 156 | + param_env, |
| 157 | + recursion_depth: 0, |
| 158 | + predicate: trait_ref.upcast(tcx), |
| 159 | + }; |
341 | 160 | infcx |
342 | | - .type_implements_trait( |
343 | | - trait_id, |
344 | | - // for some unknown reason we need to have vec here |
345 | | - // clippy has array |
346 | | - vec![ty.into()].into_iter().chain(ty_params), |
347 | | - param_env, |
348 | | - ) |
349 | | - .must_apply_modulo_regions() |
| 161 | + .evaluate_obligation(&obligation) |
| 162 | + .is_ok_and(EvaluationResult::must_apply_modulo_regions) |
350 | 163 | } |
0 commit comments