|
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