]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | //! Implementation of lint checking. |
2 | //! | |
3 | //! The lint checking is mostly consolidated into one pass which runs | |
4 | //! after all other analyses. Throughout compilation, lint warnings | |
5 | //! can be added via the `add_lint` method on the Session structure. This | |
6 | //! requires a span and an ID of the node that the lint is being added to. The | |
7 | //! lint isn't actually emitted at that time because it is unknown what the | |
8 | //! actual lint level at that location is. | |
9 | //! | |
10 | //! To actually emit lint warnings/errors, a separate pass is used. | |
11 | //! A context keeps track of the current state of all lint levels. | |
12 | //! Upon entering a node of the ast which can modify the lint settings, the | |
13 | //! previous lint state is pushed onto a stack and the ast is then recursed | |
14 | //! upon. As the ast is traversed, this keeps track of the current lint level | |
15 | //! for all lint attributes. | |
16 | ||
17 | use self::TargetLint::*; | |
18 | ||
19 | use crate::levels::LintLevelsBuilder; | |
20 | use crate::passes::{EarlyLintPassObject, LateLintPassObject}; | |
3dfed10e | 21 | use rustc_ast as ast; |
dfeec247 XL |
22 | use rustc_data_structures::fx::FxHashMap; |
23 | use rustc_data_structures::sync; | |
6a06907d XL |
24 | use rustc_errors::{ |
25 | add_elided_lifetime_in_path_suggestion, struct_span_err, Applicability, SuggestionStyle, | |
26 | }; | |
dfeec247 | 27 | use rustc_hir as hir; |
f035d41b | 28 | use rustc_hir::def::Res; |
dfeec247 | 29 | use rustc_hir::def_id::{CrateNum, DefId}; |
ba9703b0 XL |
30 | use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; |
31 | use rustc_middle::lint::LintDiagnosticBuilder; | |
32 | use rustc_middle::middle::privacy::AccessLevels; | |
33 | use rustc_middle::middle::stability; | |
34 | use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; | |
1b1a35ee | 35 | use rustc_middle::ty::print::with_no_trimmed_paths; |
ba9703b0 | 36 | use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; |
6a06907d XL |
37 | use rustc_serialize::json::Json; |
38 | use rustc_session::lint::{BuiltinLintDiagnostics, ExternDepSpec}; | |
dfeec247 XL |
39 | use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; |
40 | use rustc_session::Session; | |
29967ef6 | 41 | use rustc_session::SessionLintStore; |
fc512014 | 42 | use rustc_span::lev_distance::find_best_match_for_name; |
dfeec247 | 43 | use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP}; |
ba9703b0 | 44 | use rustc_target::abi::LayoutOf; |
fc512014 | 45 | use tracing::debug; |
dfeec247 | 46 | |
f035d41b | 47 | use std::cell::Cell; |
cdc7bbd5 | 48 | use std::iter; |
dfeec247 XL |
49 | use std::slice; |
50 | ||
51 | /// Information about the registered lints. | |
52 | /// | |
53 | /// This is basically the subset of `Context` that we can | |
54 | /// build early in the compile pipeline. | |
55 | pub struct LintStore { | |
56 | /// Registered lints. | |
57 | lints: Vec<&'static Lint>, | |
58 | ||
59 | /// Constructor functions for each variety of lint pass. | |
60 | /// | |
61 | /// These should only be called once, but since we want to avoid locks or | |
62 | /// interior mutability, we don't enforce this (and lints should, in theory, | |
63 | /// be compatible with being constructed more than once, though not | |
64 | /// necessarily in a sane manner. This is safe though.) | |
65 | pub pre_expansion_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>, | |
66 | pub early_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>, | |
67 | pub late_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>, | |
68 | /// This is unique in that we construct them per-module, so not once. | |
69 | pub late_module_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>, | |
70 | ||
71 | /// Lints indexed by name. | |
72 | by_name: FxHashMap<String, TargetLint>, | |
73 | ||
74 | /// Map of registered lint groups to what lints they expand to. | |
75 | lint_groups: FxHashMap<&'static str, LintGroup>, | |
76 | } | |
77 | ||
29967ef6 XL |
78 | impl SessionLintStore for LintStore { |
79 | fn name_to_lint(&self, lint_name: &str) -> LintId { | |
80 | let lints = self | |
81 | .find_lints(lint_name) | |
82 | .unwrap_or_else(|_| panic!("Failed to find lint with name `{}`", lint_name)); | |
83 | ||
84 | if let &[lint] = lints.as_slice() { | |
85 | return lint; | |
86 | } else { | |
87 | panic!("Found mutliple lints with name `{}`: {:?}", lint_name, lints); | |
88 | } | |
89 | } | |
90 | } | |
91 | ||
dfeec247 | 92 | /// The target of the `by_name` map, which accounts for renaming/deprecation. |
6a06907d | 93 | #[derive(Debug)] |
dfeec247 XL |
94 | enum TargetLint { |
95 | /// A direct lint target | |
96 | Id(LintId), | |
97 | ||
98 | /// Temporary renaming, used for easing migration pain; see #16545 | |
99 | Renamed(String, LintId), | |
100 | ||
101 | /// Lint with this name existed previously, but has been removed/deprecated. | |
102 | /// The string argument is the reason for removal. | |
103 | Removed(String), | |
6a06907d XL |
104 | |
105 | /// A lint name that should give no warnings and have no effect. | |
106 | /// | |
107 | /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints. | |
108 | Ignored, | |
dfeec247 XL |
109 | } |
110 | ||
111 | pub enum FindLintError { | |
112 | NotFound, | |
113 | Removed, | |
114 | } | |
115 | ||
116 | struct LintAlias { | |
117 | name: &'static str, | |
118 | /// Whether deprecation warnings should be suppressed for this alias. | |
119 | silent: bool, | |
120 | } | |
121 | ||
122 | struct LintGroup { | |
123 | lint_ids: Vec<LintId>, | |
124 | from_plugin: bool, | |
125 | depr: Option<LintAlias>, | |
126 | } | |
127 | ||
128 | pub enum CheckLintNameResult<'a> { | |
129 | Ok(&'a [LintId]), | |
130 | /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. | |
131 | NoLint(Option<Symbol>), | |
132 | /// The lint is either renamed or removed. This is the warning | |
133 | /// message, and an optional new name (`None` if removed). | |
134 | Warning(String, Option<String>), | |
135 | /// The lint is from a tool. If the Option is None, then either | |
136 | /// the lint does not exist in the tool or the code was not | |
137 | /// compiled with the tool and therefore the lint was never | |
138 | /// added to the `LintStore`. Otherwise the `LintId` will be | |
139 | /// returned as if it where a rustc lint. | |
140 | Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>), | |
141 | } | |
142 | ||
143 | impl LintStore { | |
144 | pub fn new() -> LintStore { | |
145 | LintStore { | |
146 | lints: vec![], | |
147 | pre_expansion_passes: vec![], | |
148 | early_passes: vec![], | |
149 | late_passes: vec![], | |
150 | late_module_passes: vec![], | |
151 | by_name: Default::default(), | |
152 | lint_groups: Default::default(), | |
153 | } | |
154 | } | |
155 | ||
156 | pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] { | |
157 | &self.lints | |
158 | } | |
159 | ||
160 | pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> { | |
161 | self.lint_groups | |
162 | .iter() | |
163 | .filter(|(_, LintGroup { depr, .. })| { | |
164 | // Don't display deprecated lint groups. | |
165 | depr.is_none() | |
166 | }) | |
167 | .map(|(k, LintGroup { lint_ids, from_plugin, .. })| { | |
168 | (*k, lint_ids.clone(), *from_plugin) | |
169 | }) | |
170 | .collect() | |
171 | } | |
172 | ||
173 | pub fn register_early_pass( | |
174 | &mut self, | |
175 | pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, | |
176 | ) { | |
177 | self.early_passes.push(Box::new(pass)); | |
178 | } | |
179 | ||
cdc7bbd5 | 180 | /// Used by clippy. |
dfeec247 XL |
181 | pub fn register_pre_expansion_pass( |
182 | &mut self, | |
183 | pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, | |
184 | ) { | |
185 | self.pre_expansion_passes.push(Box::new(pass)); | |
186 | } | |
187 | ||
188 | pub fn register_late_pass( | |
189 | &mut self, | |
190 | pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, | |
191 | ) { | |
192 | self.late_passes.push(Box::new(pass)); | |
193 | } | |
194 | ||
195 | pub fn register_late_mod_pass( | |
196 | &mut self, | |
197 | pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, | |
198 | ) { | |
199 | self.late_module_passes.push(Box::new(pass)); | |
200 | } | |
201 | ||
202 | // Helper method for register_early/late_pass | |
203 | pub fn register_lints(&mut self, lints: &[&'static Lint]) { | |
204 | for lint in lints { | |
205 | self.lints.push(lint); | |
206 | ||
207 | let id = LintId::of(lint); | |
208 | if self.by_name.insert(lint.name_lower(), Id(id)).is_some() { | |
209 | bug!("duplicate specification of lint {}", lint.name_lower()) | |
210 | } | |
211 | ||
212 | if let Some(FutureIncompatibleInfo { edition, .. }) = lint.future_incompatible { | |
213 | if let Some(edition) = edition { | |
214 | self.lint_groups | |
215 | .entry(edition.lint_name()) | |
216 | .or_insert(LintGroup { | |
217 | lint_ids: vec![], | |
218 | from_plugin: lint.is_plugin, | |
219 | depr: None, | |
220 | }) | |
221 | .lint_ids | |
222 | .push(id); | |
223 | } | |
224 | ||
225 | self.lint_groups | |
226 | .entry("future_incompatible") | |
227 | .or_insert(LintGroup { | |
228 | lint_ids: vec![], | |
229 | from_plugin: lint.is_plugin, | |
230 | depr: None, | |
231 | }) | |
232 | .lint_ids | |
233 | .push(id); | |
234 | } | |
235 | } | |
236 | } | |
237 | ||
238 | pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) { | |
239 | self.lint_groups.insert( | |
240 | alias, | |
241 | LintGroup { | |
242 | lint_ids: vec![], | |
243 | from_plugin: false, | |
244 | depr: Some(LintAlias { name: lint_name, silent: true }), | |
245 | }, | |
246 | ); | |
247 | } | |
248 | ||
249 | pub fn register_group( | |
250 | &mut self, | |
251 | from_plugin: bool, | |
252 | name: &'static str, | |
253 | deprecated_name: Option<&'static str>, | |
254 | to: Vec<LintId>, | |
255 | ) { | |
256 | let new = self | |
257 | .lint_groups | |
258 | .insert(name, LintGroup { lint_ids: to, from_plugin, depr: None }) | |
259 | .is_none(); | |
260 | if let Some(deprecated) = deprecated_name { | |
261 | self.lint_groups.insert( | |
262 | deprecated, | |
263 | LintGroup { | |
264 | lint_ids: vec![], | |
265 | from_plugin, | |
266 | depr: Some(LintAlias { name, silent: false }), | |
267 | }, | |
268 | ); | |
269 | } | |
270 | ||
271 | if !new { | |
272 | bug!("duplicate specification of lint group {}", name); | |
273 | } | |
274 | } | |
275 | ||
6a06907d XL |
276 | /// This lint should be available with either the old or the new name. |
277 | /// | |
278 | /// Using the old name will not give a warning. | |
279 | /// You must register a lint with the new name before calling this function. | |
280 | #[track_caller] | |
281 | pub fn register_alias(&mut self, old_name: &str, new_name: &str) { | |
282 | let target = match self.by_name.get(new_name) { | |
283 | Some(&Id(lint_id)) => lint_id, | |
284 | _ => bug!("cannot add alias {} for lint {} that does not exist", old_name, new_name), | |
285 | }; | |
286 | match self.by_name.insert(old_name.to_string(), Id(target)) { | |
287 | None | Some(Ignored) => {} | |
288 | Some(x) => bug!("duplicate specification of lint {} (was {:?})", old_name, x), | |
289 | } | |
290 | } | |
291 | ||
292 | /// This lint should give no warning and have no effect. | |
293 | /// | |
294 | /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints. | |
295 | #[track_caller] | |
296 | pub fn register_ignored(&mut self, name: &str) { | |
297 | if self.by_name.insert(name.to_string(), Ignored).is_some() { | |
298 | bug!("duplicate specification of lint {}", name); | |
299 | } | |
300 | } | |
301 | ||
302 | /// This lint has been renamed; warn about using the new name and apply the lint. | |
5869c6ff | 303 | #[track_caller] |
dfeec247 XL |
304 | pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { |
305 | let target = match self.by_name.get(new_name) { | |
306 | Some(&Id(lint_id)) => lint_id, | |
307 | _ => bug!("invalid lint renaming of {} to {}", old_name, new_name), | |
308 | }; | |
309 | self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target)); | |
310 | } | |
311 | ||
312 | pub fn register_removed(&mut self, name: &str, reason: &str) { | |
313 | self.by_name.insert(name.into(), Removed(reason.into())); | |
314 | } | |
315 | ||
316 | pub fn find_lints(&self, mut lint_name: &str) -> Result<Vec<LintId>, FindLintError> { | |
317 | match self.by_name.get(lint_name) { | |
318 | Some(&Id(lint_id)) => Ok(vec![lint_id]), | |
319 | Some(&Renamed(_, lint_id)) => Ok(vec![lint_id]), | |
320 | Some(&Removed(_)) => Err(FindLintError::Removed), | |
6a06907d | 321 | Some(&Ignored) => Ok(vec![]), |
dfeec247 XL |
322 | None => loop { |
323 | return match self.lint_groups.get(lint_name) { | |
324 | Some(LintGroup { lint_ids, depr, .. }) => { | |
325 | if let Some(LintAlias { name, .. }) = depr { | |
326 | lint_name = name; | |
327 | continue; | |
328 | } | |
329 | Ok(lint_ids.clone()) | |
330 | } | |
331 | None => Err(FindLintError::Removed), | |
332 | }; | |
333 | }, | |
334 | } | |
335 | } | |
336 | ||
17df50a5 XL |
337 | /// Checks the validity of lint names derived from the command line. Returns |
338 | /// true if the lint is valid, false otherwise. | |
339 | pub fn check_lint_name_cmdline( | |
340 | &self, | |
341 | sess: &Session, | |
342 | lint_name: &str, | |
343 | level: Option<Level>, | |
344 | ) -> bool { | |
dfeec247 XL |
345 | let db = match self.check_lint_name(lint_name, None) { |
346 | CheckLintNameResult::Ok(_) => None, | |
347 | CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)), | |
348 | CheckLintNameResult::NoLint(suggestion) => { | |
349 | let mut err = | |
350 | struct_span_err!(sess, DUMMY_SP, E0602, "unknown lint: `{}`", lint_name); | |
351 | ||
352 | if let Some(suggestion) = suggestion { | |
353 | err.help(&format!("did you mean: `{}`", suggestion)); | |
354 | } | |
355 | ||
356 | Some(err) | |
357 | } | |
358 | CheckLintNameResult::Tool(result) => match result { | |
359 | Err((Some(_), new_name)) => Some(sess.struct_warn(&format!( | |
360 | "lint name `{}` is deprecated \ | |
361 | and does not have an effect anymore. \ | |
362 | Use: {}", | |
363 | lint_name, new_name | |
364 | ))), | |
365 | _ => None, | |
366 | }, | |
367 | }; | |
368 | ||
369 | if let Some(mut db) = db { | |
17df50a5 XL |
370 | if let Some(level) = level { |
371 | let msg = format!( | |
372 | "requested on the command line with `{} {}`", | |
373 | match level { | |
374 | Level::Allow => "-A", | |
375 | Level::Warn => "-W", | |
376 | Level::Deny => "-D", | |
377 | Level::Forbid => "-F", | |
378 | }, | |
379 | lint_name | |
380 | ); | |
381 | db.note(&msg); | |
382 | } | |
dfeec247 | 383 | db.emit(); |
17df50a5 XL |
384 | false |
385 | } else { | |
386 | true | |
dfeec247 XL |
387 | } |
388 | } | |
389 | ||
fc512014 XL |
390 | /// True if this symbol represents a lint group name. |
391 | pub fn is_lint_group(&self, lint_name: Symbol) -> bool { | |
392 | debug!( | |
393 | "is_lint_group(lint_name={:?}, lint_groups={:?})", | |
394 | lint_name, | |
395 | self.lint_groups.keys().collect::<Vec<_>>() | |
396 | ); | |
397 | let lint_name_str = &*lint_name.as_str(); | |
398 | self.lint_groups.contains_key(&lint_name_str) || { | |
399 | let warnings_name_str = crate::WARNINGS.name_lower(); | |
400 | lint_name_str == &*warnings_name_str | |
401 | } | |
402 | } | |
403 | ||
dfeec247 XL |
404 | /// Checks the name of a lint for its existence, and whether it was |
405 | /// renamed or removed. Generates a DiagnosticBuilder containing a | |
406 | /// warning for renamed and removed lints. This is over both lint | |
407 | /// names from attributes and those passed on the command line. Since | |
408 | /// it emits non-fatal warnings and there are *two* lint passes that | |
409 | /// inspect attributes, this is only run from the late pass to avoid | |
410 | /// printing duplicate warnings. | |
411 | pub fn check_lint_name( | |
412 | &self, | |
413 | lint_name: &str, | |
414 | tool_name: Option<Symbol>, | |
415 | ) -> CheckLintNameResult<'_> { | |
416 | let complete_name = if let Some(tool_name) = tool_name { | |
417 | format!("{}::{}", tool_name, lint_name) | |
418 | } else { | |
419 | lint_name.to_string() | |
420 | }; | |
421 | // If the lint was scoped with `tool::` check if the tool lint exists | |
5869c6ff | 422 | if let Some(tool_name) = tool_name { |
dfeec247 XL |
423 | match self.by_name.get(&complete_name) { |
424 | None => match self.lint_groups.get(&*complete_name) { | |
5869c6ff XL |
425 | // If the lint isn't registered, there are two possibilities: |
426 | None => { | |
427 | // 1. The tool is currently running, so this lint really doesn't exist. | |
428 | // FIXME: should this handle tools that never register a lint, like rustfmt? | |
429 | tracing::debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>()); | |
430 | let tool_prefix = format!("{}::", tool_name); | |
431 | return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) { | |
432 | self.no_lint_suggestion(&complete_name) | |
433 | } else { | |
434 | // 2. The tool isn't currently running, so no lints will be registered. | |
435 | // To avoid giving a false positive, ignore all unknown lints. | |
436 | CheckLintNameResult::Tool(Err((None, String::new()))) | |
437 | }; | |
438 | } | |
dfeec247 XL |
439 | Some(LintGroup { lint_ids, .. }) => { |
440 | return CheckLintNameResult::Tool(Ok(&lint_ids)); | |
441 | } | |
442 | }, | |
443 | Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))), | |
444 | // If the lint was registered as removed or renamed by the lint tool, we don't need | |
445 | // to treat tool_lints and rustc lints different and can use the code below. | |
446 | _ => {} | |
447 | } | |
448 | } | |
449 | match self.by_name.get(&complete_name) { | |
450 | Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning( | |
451 | format!("lint `{}` has been renamed to `{}`", complete_name, new_name), | |
452 | Some(new_name.to_owned()), | |
453 | ), | |
454 | Some(&Removed(ref reason)) => CheckLintNameResult::Warning( | |
5869c6ff | 455 | format!("lint `{}` has been removed: {}", complete_name, reason), |
dfeec247 XL |
456 | None, |
457 | ), | |
458 | None => match self.lint_groups.get(&*complete_name) { | |
459 | // If neither the lint, nor the lint group exists check if there is a `clippy::` | |
460 | // variant of this lint | |
461 | None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"), | |
462 | Some(LintGroup { lint_ids, depr, .. }) => { | |
463 | // Check if the lint group name is deprecated | |
464 | if let Some(LintAlias { name, silent }) = depr { | |
465 | let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); | |
466 | return if *silent { | |
467 | CheckLintNameResult::Ok(&lint_ids) | |
468 | } else { | |
74b04a01 | 469 | CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) |
dfeec247 XL |
470 | }; |
471 | } | |
472 | CheckLintNameResult::Ok(&lint_ids) | |
473 | } | |
474 | }, | |
475 | Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)), | |
6a06907d | 476 | Some(&Ignored) => CheckLintNameResult::Ok(&[]), |
dfeec247 XL |
477 | } |
478 | } | |
479 | ||
5869c6ff XL |
480 | fn no_lint_suggestion(&self, lint_name: &str) -> CheckLintNameResult<'_> { |
481 | let name_lower = lint_name.to_lowercase(); | |
482 | let symbols = | |
483 | self.get_lints().iter().map(|l| Symbol::intern(&l.name_lower())).collect::<Vec<_>>(); | |
484 | ||
485 | if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_ok() { | |
486 | // First check if the lint name is (partly) in upper case instead of lower case... | |
487 | CheckLintNameResult::NoLint(Some(Symbol::intern(&name_lower))) | |
488 | } else { | |
489 | // ...if not, search for lints with a similar name | |
490 | let suggestion = find_best_match_for_name(&symbols, Symbol::intern(&name_lower), None); | |
491 | CheckLintNameResult::NoLint(suggestion) | |
492 | } | |
493 | } | |
494 | ||
dfeec247 XL |
495 | fn check_tool_name_for_backwards_compat( |
496 | &self, | |
497 | lint_name: &str, | |
498 | tool_name: &str, | |
499 | ) -> CheckLintNameResult<'_> { | |
500 | let complete_name = format!("{}::{}", tool_name, lint_name); | |
501 | match self.by_name.get(&complete_name) { | |
502 | None => match self.lint_groups.get(&*complete_name) { | |
503 | // Now we are sure, that this lint exists nowhere | |
5869c6ff | 504 | None => self.no_lint_suggestion(lint_name), |
dfeec247 XL |
505 | Some(LintGroup { lint_ids, depr, .. }) => { |
506 | // Reaching this would be weird, but let's cover this case anyway | |
507 | if let Some(LintAlias { name, silent }) = depr { | |
508 | let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); | |
509 | return if *silent { | |
510 | CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) | |
511 | } else { | |
74b04a01 | 512 | CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) |
dfeec247 XL |
513 | }; |
514 | } | |
515 | CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) | |
516 | } | |
517 | }, | |
518 | Some(&Id(ref id)) => { | |
519 | CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name))) | |
520 | } | |
6a06907d XL |
521 | Some(other) => { |
522 | tracing::debug!("got renamed lint {:?}", other); | |
523 | CheckLintNameResult::NoLint(None) | |
524 | } | |
dfeec247 XL |
525 | } |
526 | } | |
527 | } | |
528 | ||
529 | /// Context for lint checking after type checking. | |
f035d41b | 530 | pub struct LateContext<'tcx> { |
dfeec247 XL |
531 | /// Type context we're checking in. |
532 | pub tcx: TyCtxt<'tcx>, | |
533 | ||
f035d41b XL |
534 | /// Current body, or `None` if outside a body. |
535 | pub enclosing_body: Option<hir::BodyId>, | |
536 | ||
3dfed10e XL |
537 | /// Type-checking results for the current body. Access using the `typeck_results` |
538 | /// and `maybe_typeck_results` methods, which handle querying the typeck results on demand. | |
f035d41b XL |
539 | // FIXME(eddyb) move all the code accessing internal fields like this, |
540 | // to this module, to avoid exposing it to lint logic. | |
3dfed10e | 541 | pub(super) cached_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>, |
dfeec247 XL |
542 | |
543 | /// Parameter environment for the item we are in. | |
544 | pub param_env: ty::ParamEnv<'tcx>, | |
545 | ||
546 | /// Items accessible from the crate being checked. | |
f035d41b | 547 | pub access_levels: &'tcx AccessLevels, |
dfeec247 XL |
548 | |
549 | /// The store of registered lints and the lint levels. | |
550 | pub lint_store: &'tcx LintStore, | |
551 | ||
552 | pub last_node_with_lint_attrs: hir::HirId, | |
553 | ||
554 | /// Generic type parameters in scope for the item we are in. | |
555 | pub generics: Option<&'tcx hir::Generics<'tcx>>, | |
556 | ||
557 | /// We are only looking at one module | |
558 | pub only_module: bool, | |
559 | } | |
560 | ||
561 | /// Context for lint checking of the AST, after expansion, before lowering to | |
562 | /// HIR. | |
563 | pub struct EarlyContext<'a> { | |
564 | /// Type context we're checking in. | |
565 | pub sess: &'a Session, | |
566 | ||
567 | /// The crate being checked. | |
568 | pub krate: &'a ast::Crate, | |
569 | ||
570 | pub builder: LintLevelsBuilder<'a>, | |
571 | ||
572 | /// The store of registered lints and the lint levels. | |
573 | pub lint_store: &'a LintStore, | |
574 | ||
575 | pub buffered: LintBuffer, | |
576 | } | |
577 | ||
578 | pub trait LintPassObject: Sized {} | |
579 | ||
580 | impl LintPassObject for EarlyLintPassObject {} | |
581 | ||
582 | impl LintPassObject for LateLintPassObject {} | |
583 | ||
584 | pub trait LintContext: Sized { | |
585 | type PassObject: LintPassObject; | |
586 | ||
587 | fn sess(&self) -> &Session; | |
588 | fn lints(&self) -> &LintStore; | |
589 | ||
74b04a01 | 590 | fn lookup_with_diagnostics( |
dfeec247 XL |
591 | &self, |
592 | lint: &'static Lint, | |
74b04a01 XL |
593 | span: Option<impl Into<MultiSpan>>, |
594 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), | |
dfeec247 XL |
595 | diagnostic: BuiltinLintDiagnostics, |
596 | ) { | |
74b04a01 XL |
597 | self.lookup(lint, span, |lint| { |
598 | // We first generate a blank diagnostic. | |
599 | let mut db = lint.build(""); | |
600 | ||
601 | // Now, set up surrounding context. | |
602 | let sess = self.sess(); | |
603 | match diagnostic { | |
604 | BuiltinLintDiagnostics::Normal => (), | |
605 | BuiltinLintDiagnostics::BareTraitObject(span, is_global) => { | |
606 | let (sugg, app) = match sess.source_map().span_to_snippet(span) { | |
607 | Ok(s) if is_global => { | |
608 | (format!("dyn ({})", s), Applicability::MachineApplicable) | |
609 | } | |
610 | Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable), | |
611 | Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders), | |
612 | }; | |
613 | db.span_suggestion(span, "use `dyn`", sugg, app); | |
614 | } | |
615 | BuiltinLintDiagnostics::AbsPathWithModule(span) => { | |
616 | let (sugg, app) = match sess.source_map().span_to_snippet(span) { | |
617 | Ok(ref s) => { | |
618 | // FIXME(Manishearth) ideally the emitting code | |
619 | // can tell us whether or not this is global | |
620 | let opt_colon = | |
621 | if s.trim_start().starts_with("::") { "" } else { "::" }; | |
622 | ||
623 | (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable) | |
624 | } | |
625 | Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders), | |
626 | }; | |
627 | db.span_suggestion(span, "use `crate`", sugg, app); | |
628 | } | |
629 | BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => { | |
630 | db.span_label( | |
631 | span, | |
632 | "names from parent modules are not accessible without an explicit import", | |
633 | ); | |
634 | } | |
635 | BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths( | |
636 | span_def, | |
637 | ) => { | |
638 | db.span_note(span_def, "the macro is defined here"); | |
639 | } | |
640 | BuiltinLintDiagnostics::ElidedLifetimesInPaths( | |
dfeec247 XL |
641 | n, |
642 | path_span, | |
643 | incl_angl_brckt, | |
644 | insertion_span, | |
645 | anon_lts, | |
74b04a01 XL |
646 | ) => { |
647 | add_elided_lifetime_in_path_suggestion( | |
29967ef6 | 648 | sess.source_map(), |
74b04a01 XL |
649 | &mut db, |
650 | n, | |
651 | path_span, | |
652 | incl_angl_brckt, | |
653 | insertion_span, | |
654 | anon_lts, | |
dfeec247 XL |
655 | ); |
656 | } | |
74b04a01 XL |
657 | BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { |
658 | db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect); | |
659 | } | |
660 | BuiltinLintDiagnostics::UnusedImports(message, replaces) => { | |
661 | if !replaces.is_empty() { | |
662 | db.tool_only_multipart_suggestion( | |
663 | &message, | |
664 | replaces, | |
665 | Applicability::MachineApplicable, | |
666 | ); | |
667 | } | |
668 | } | |
669 | BuiltinLintDiagnostics::RedundantImport(spans, ident) => { | |
670 | for (span, is_imported) in spans { | |
671 | let introduced = if is_imported { "imported" } else { "defined" }; | |
672 | db.span_label( | |
673 | span, | |
674 | format!("the item `{}` is already {} here", ident, introduced), | |
675 | ); | |
676 | } | |
677 | } | |
678 | BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => { | |
3dfed10e | 679 | stability::deprecation_suggestion(&mut db, "macro", suggestion, span) |
74b04a01 XL |
680 | } |
681 | BuiltinLintDiagnostics::UnusedDocComment(span) => { | |
f9f354fc | 682 | db.span_label(span, "rustdoc does not generate documentation for macro invocations"); |
74b04a01 XL |
683 | db.help("to document an item produced by a macro, \ |
684 | the macro must produce the documentation as part of its expansion"); | |
dfeec247 | 685 | } |
5869c6ff XL |
686 | BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => { |
687 | db.span_suggestion(span, "remove `mut` from the parameter", ident.to_string(), Applicability::MachineApplicable); | |
688 | } | |
689 | BuiltinLintDiagnostics::MissingAbi(span, default_abi) => { | |
690 | db.span_label(span, "ABI should be specified here"); | |
691 | db.help(&format!("the default ABI is {}", default_abi.name())); | |
692 | } | |
6a06907d XL |
693 | BuiltinLintDiagnostics::LegacyDeriveHelpers(span) => { |
694 | db.span_label(span, "the attribute is introduced here"); | |
695 | } | |
696 | BuiltinLintDiagnostics::ExternDepSpec(krate, loc) => { | |
697 | let json = match loc { | |
698 | ExternDepSpec::Json(json) => { | |
699 | db.help(&format!("remove unnecessary dependency `{}`", krate)); | |
700 | json | |
701 | } | |
702 | ExternDepSpec::Raw(raw) => { | |
703 | db.help(&format!("remove unnecessary dependency `{}` at `{}`", krate, raw)); | |
704 | db.span_suggestion_with_style( | |
705 | DUMMY_SP, | |
706 | "raw extern location", | |
707 | raw.clone(), | |
708 | Applicability::Unspecified, | |
709 | SuggestionStyle::CompletelyHidden, | |
710 | ); | |
711 | Json::String(raw) | |
712 | } | |
713 | }; | |
714 | db.tool_only_suggestion_with_metadata( | |
715 | "json extern location", | |
716 | Applicability::Unspecified, | |
717 | json | |
718 | ); | |
719 | } | |
720 | BuiltinLintDiagnostics::ProcMacroBackCompat(note) => { | |
721 | db.note(¬e); | |
722 | } | |
cdc7bbd5 XL |
723 | BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => { |
724 | db.span_suggestion(span, "use pat_param to preserve semantics", suggestion, Applicability::MachineApplicable); | |
725 | } | |
dfeec247 | 726 | } |
74b04a01 XL |
727 | // Rewrap `db`, and pass control to the user. |
728 | decorate(LintDiagnosticBuilder::new(db)); | |
729 | }); | |
dfeec247 XL |
730 | } |
731 | ||
74b04a01 XL |
732 | // FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to |
733 | // set the span in their `decorate` function (preferably using set_span). | |
dfeec247 XL |
734 | fn lookup<S: Into<MultiSpan>>( |
735 | &self, | |
736 | lint: &'static Lint, | |
737 | span: Option<S>, | |
74b04a01 XL |
738 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), |
739 | ); | |
dfeec247 XL |
740 | |
741 | fn struct_span_lint<S: Into<MultiSpan>>( | |
742 | &self, | |
743 | lint: &'static Lint, | |
744 | span: S, | |
74b04a01 | 745 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), |
dfeec247 | 746 | ) { |
74b04a01 | 747 | self.lookup(lint, Some(span), decorate); |
dfeec247 | 748 | } |
dfeec247 | 749 | /// Emit a lint at the appropriate level, with no associated span. |
74b04a01 XL |
750 | fn lint(&self, lint: &'static Lint, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) { |
751 | self.lookup(lint, None as Option<Span>, decorate); | |
dfeec247 XL |
752 | } |
753 | } | |
754 | ||
755 | impl<'a> EarlyContext<'a> { | |
756 | pub fn new( | |
757 | sess: &'a Session, | |
758 | lint_store: &'a LintStore, | |
759 | krate: &'a ast::Crate, | |
760 | buffered: LintBuffer, | |
761 | warn_about_weird_lints: bool, | |
762 | ) -> EarlyContext<'a> { | |
763 | EarlyContext { | |
764 | sess, | |
765 | krate, | |
766 | lint_store, | |
6a06907d | 767 | builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store, &krate.attrs), |
dfeec247 XL |
768 | buffered, |
769 | } | |
770 | } | |
771 | } | |
772 | ||
f035d41b | 773 | impl LintContext for LateContext<'_> { |
dfeec247 XL |
774 | type PassObject = LateLintPassObject; |
775 | ||
776 | /// Gets the overall compiler `Session` object. | |
777 | fn sess(&self) -> &Session { | |
778 | &self.tcx.sess | |
779 | } | |
780 | ||
781 | fn lints(&self) -> &LintStore { | |
782 | &*self.lint_store | |
783 | } | |
784 | ||
785 | fn lookup<S: Into<MultiSpan>>( | |
786 | &self, | |
787 | lint: &'static Lint, | |
788 | span: Option<S>, | |
74b04a01 XL |
789 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), |
790 | ) { | |
dfeec247 XL |
791 | let hir_id = self.last_node_with_lint_attrs; |
792 | ||
793 | match span { | |
74b04a01 XL |
794 | Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate), |
795 | None => self.tcx.struct_lint_node(lint, hir_id, decorate), | |
dfeec247 XL |
796 | } |
797 | } | |
798 | } | |
799 | ||
800 | impl LintContext for EarlyContext<'_> { | |
801 | type PassObject = EarlyLintPassObject; | |
802 | ||
803 | /// Gets the overall compiler `Session` object. | |
804 | fn sess(&self) -> &Session { | |
805 | &self.sess | |
806 | } | |
807 | ||
808 | fn lints(&self) -> &LintStore { | |
809 | &*self.lint_store | |
810 | } | |
811 | ||
812 | fn lookup<S: Into<MultiSpan>>( | |
813 | &self, | |
814 | lint: &'static Lint, | |
815 | span: Option<S>, | |
74b04a01 XL |
816 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), |
817 | ) { | |
818 | self.builder.struct_lint(lint, span.map(|s| s.into()), decorate) | |
dfeec247 XL |
819 | } |
820 | } | |
821 | ||
f035d41b | 822 | impl<'tcx> LateContext<'tcx> { |
3dfed10e | 823 | /// Gets the type-checking results for the current body, |
f035d41b | 824 | /// or `None` if outside a body. |
3dfed10e XL |
825 | pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> { |
826 | self.cached_typeck_results.get().or_else(|| { | |
f035d41b | 827 | self.enclosing_body.map(|body| { |
3dfed10e XL |
828 | let typeck_results = self.tcx.typeck_body(body); |
829 | self.cached_typeck_results.set(Some(typeck_results)); | |
830 | typeck_results | |
f035d41b XL |
831 | }) |
832 | }) | |
833 | } | |
834 | ||
3dfed10e | 835 | /// Gets the type-checking results for the current body. |
f035d41b XL |
836 | /// As this will ICE if called outside bodies, only call when working with |
837 | /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). | |
838 | #[track_caller] | |
3dfed10e XL |
839 | pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> { |
840 | self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body") | |
f035d41b XL |
841 | } |
842 | ||
843 | /// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable. | |
3dfed10e | 844 | /// Unlike `.typeck_results().qpath_res(qpath, id)`, this can be used even outside |
f035d41b XL |
845 | /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing. |
846 | pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { | |
847 | match *qpath { | |
848 | hir::QPath::Resolved(_, ref path) => path.res, | |
3dfed10e XL |
849 | hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self |
850 | .maybe_typeck_results() | |
5869c6ff XL |
851 | .filter(|typeck_results| typeck_results.hir_owner == id.owner) |
852 | .or_else(|| { | |
853 | if self.tcx.has_typeck_results(id.owner.to_def_id()) { | |
854 | Some(self.tcx.typeck(id.owner)) | |
855 | } else { | |
856 | None | |
857 | } | |
858 | }) | |
3dfed10e | 859 | .and_then(|typeck_results| typeck_results.type_dependent_def(id)) |
f035d41b XL |
860 | .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), |
861 | } | |
862 | } | |
863 | ||
dfeec247 XL |
864 | /// Check if a `DefId`'s path matches the given absolute type path usage. |
865 | /// | |
5869c6ff | 866 | /// Anonymous scopes such as `extern` imports are matched with `kw::Empty`; |
dfeec247 XL |
867 | /// inherent `impl` blocks are matched with the name of the type. |
868 | /// | |
1b1a35ee XL |
869 | /// Instead of using this method, it is often preferable to instead use |
870 | /// `rustc_diagnostic_item` or a `lang_item`. This is less prone to errors | |
871 | /// as paths get invalidated if the target definition moves. | |
872 | /// | |
dfeec247 XL |
873 | /// # Examples |
874 | /// | |
875 | /// ```rust,ignore (no context or def id available) | |
876 | /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) { | |
877 | /// // The given `def_id` is that of an `Option` type | |
878 | /// } | |
879 | /// ``` | |
cdc7bbd5 XL |
880 | /// |
881 | /// Used by clippy, but should be replaced by diagnostic items eventually. | |
dfeec247 XL |
882 | pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool { |
883 | let names = self.get_def_path(def_id); | |
884 | ||
cdc7bbd5 | 885 | names.len() == path.len() && iter::zip(names, path).all(|(a, &b)| a == b) |
dfeec247 XL |
886 | } |
887 | ||
888 | /// Gets the absolute path of `def_id` as a vector of `Symbol`. | |
889 | /// | |
890 | /// # Examples | |
891 | /// | |
892 | /// ```rust,ignore (no context or def id available) | |
893 | /// let def_path = cx.get_def_path(def_id); | |
894 | /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] { | |
895 | /// // The given `def_id` is that of an `Option` type | |
896 | /// } | |
897 | /// ``` | |
898 | pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> { | |
899 | pub struct AbsolutePathPrinter<'tcx> { | |
900 | pub tcx: TyCtxt<'tcx>, | |
901 | } | |
902 | ||
903 | impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | |
904 | type Error = !; | |
905 | ||
906 | type Path = Vec<Symbol>; | |
907 | type Region = (); | |
908 | type Type = (); | |
909 | type DynExistential = (); | |
910 | type Const = (); | |
911 | ||
912 | fn tcx(&self) -> TyCtxt<'tcx> { | |
913 | self.tcx | |
914 | } | |
915 | ||
916 | fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> { | |
917 | Ok(()) | |
918 | } | |
919 | ||
920 | fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> { | |
921 | Ok(()) | |
922 | } | |
923 | ||
924 | fn print_dyn_existential( | |
925 | self, | |
cdc7bbd5 | 926 | _predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, |
dfeec247 XL |
927 | ) -> Result<Self::DynExistential, Self::Error> { |
928 | Ok(()) | |
929 | } | |
930 | ||
931 | fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> { | |
932 | Ok(()) | |
933 | } | |
934 | ||
935 | fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> { | |
17df50a5 | 936 | Ok(vec![self.tcx.crate_name(cnum)]) |
dfeec247 XL |
937 | } |
938 | ||
939 | fn path_qualified( | |
940 | self, | |
941 | self_ty: Ty<'tcx>, | |
942 | trait_ref: Option<ty::TraitRef<'tcx>>, | |
943 | ) -> Result<Self::Path, Self::Error> { | |
944 | if trait_ref.is_none() { | |
1b1a35ee | 945 | if let ty::Adt(def, substs) = self_ty.kind() { |
dfeec247 XL |
946 | return self.print_def_path(def.did, substs); |
947 | } | |
948 | } | |
949 | ||
950 | // This shouldn't ever be needed, but just in case: | |
1b1a35ee XL |
951 | with_no_trimmed_paths(|| { |
952 | Ok(vec![match trait_ref { | |
953 | Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)), | |
954 | None => Symbol::intern(&format!("<{}>", self_ty)), | |
955 | }]) | |
956 | }) | |
dfeec247 XL |
957 | } |
958 | ||
959 | fn path_append_impl( | |
960 | self, | |
961 | print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, | |
962 | _disambiguated_data: &DisambiguatedDefPathData, | |
963 | self_ty: Ty<'tcx>, | |
964 | trait_ref: Option<ty::TraitRef<'tcx>>, | |
965 | ) -> Result<Self::Path, Self::Error> { | |
966 | let mut path = print_prefix(self)?; | |
967 | ||
968 | // This shouldn't ever be needed, but just in case: | |
969 | path.push(match trait_ref { | |
1b1a35ee XL |
970 | Some(trait_ref) => with_no_trimmed_paths(|| { |
971 | Symbol::intern(&format!( | |
972 | "<impl {} for {}>", | |
973 | trait_ref.print_only_trait_path(), | |
974 | self_ty | |
975 | )) | |
976 | }), | |
977 | None => { | |
978 | with_no_trimmed_paths(|| Symbol::intern(&format!("<impl {}>", self_ty))) | |
979 | } | |
dfeec247 XL |
980 | }); |
981 | ||
982 | Ok(path) | |
983 | } | |
984 | ||
985 | fn path_append( | |
986 | self, | |
987 | print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, | |
988 | disambiguated_data: &DisambiguatedDefPathData, | |
989 | ) -> Result<Self::Path, Self::Error> { | |
990 | let mut path = print_prefix(self)?; | |
991 | ||
992 | // Skip `::{{constructor}}` on tuple/unit structs. | |
ba9703b0 XL |
993 | if let DefPathData::Ctor = disambiguated_data.data { |
994 | return Ok(path); | |
dfeec247 XL |
995 | } |
996 | ||
1b1a35ee | 997 | path.push(Symbol::intern(&disambiguated_data.data.to_string())); |
dfeec247 XL |
998 | Ok(path) |
999 | } | |
1000 | ||
1001 | fn path_generic_args( | |
1002 | self, | |
1003 | print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, | |
1004 | _args: &[GenericArg<'tcx>], | |
1005 | ) -> Result<Self::Path, Self::Error> { | |
1006 | print_prefix(self) | |
1007 | } | |
1008 | } | |
1009 | ||
1010 | AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap() | |
1011 | } | |
1012 | } | |
1013 | ||
f035d41b | 1014 | impl<'tcx> LayoutOf for LateContext<'tcx> { |
dfeec247 | 1015 | type Ty = Ty<'tcx>; |
ba9703b0 | 1016 | type TyAndLayout = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>; |
dfeec247 | 1017 | |
ba9703b0 | 1018 | fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { |
dfeec247 XL |
1019 | self.tcx.layout_of(self.param_env.and(ty)) |
1020 | } | |
1021 | } |