]>
Commit | Line | Data |
---|---|---|
ba9703b0 | 1 | use std::ptr; |
48663c56 | 2 | |
04454e1e FG |
3 | use rustc_ast::ptr::P; |
4 | use rustc_ast::visit::{self, Visitor}; | |
5 | use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID}; | |
74b04a01 | 6 | use rustc_ast_pretty::pprust; |
dfeec247 | 7 | use rustc_data_structures::fx::FxHashSet; |
04454e1e FG |
8 | use rustc_errors::struct_span_err; |
9 | use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; | |
60c5eb7d | 10 | use rustc_feature::BUILTIN_ATTRIBUTES; |
dfeec247 | 11 | use rustc_hir::def::Namespace::{self, *}; |
04454e1e | 12 | use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS}; |
923072b8 | 13 | use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; |
6a06907d | 14 | use rustc_hir::PrimTy; |
923072b8 | 15 | use rustc_index::vec::IndexVec; |
ba9703b0 | 16 | use rustc_middle::bug; |
3c0e092e | 17 | use rustc_middle::ty::DefIdTree; |
04454e1e FG |
18 | use rustc_session::lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE; |
19 | use rustc_session::lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS; | |
20 | use rustc_session::lint::BuiltinLintDiagnostics; | |
ba9703b0 | 21 | use rustc_session::Session; |
04454e1e | 22 | use rustc_span::edition::Edition; |
dfeec247 | 23 | use rustc_span::hygiene::MacroKind; |
fc512014 | 24 | use rustc_span::lev_distance::find_best_match_for_name; |
dfeec247 | 25 | use rustc_span::source_map::SourceMap; |
3dfed10e | 26 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
04454e1e | 27 | use rustc_span::{BytePos, Span}; |
3dfed10e | 28 | use tracing::debug; |
416331ca | 29 | |
74b04a01 | 30 | use crate::imports::{Import, ImportKind, ImportResolver}; |
064997fb | 31 | use crate::late::{PatternSource, Rib}; |
60c5eb7d | 32 | use crate::path_names_to_string; |
04454e1e FG |
33 | use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, Finalize}; |
34 | use crate::{HasGenericParams, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot}; | |
35 | use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; | |
36 | use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet}; | |
37 | use crate::{Segment, UseError}; | |
38 | ||
39 | #[cfg(test)] | |
40 | mod tests; | |
60c5eb7d | 41 | |
48663c56 XL |
42 | type Res = def::Res<ast::NodeId>; |
43 | ||
416331ca | 44 | /// A vector of spans and replacements, a message and applicability. |
923072b8 | 45 | pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); |
48663c56 | 46 | |
f035d41b XL |
47 | /// Potential candidate for an undeclared or out-of-scope label - contains the ident of a |
48 | /// similarly named label and whether or not it is reachable. | |
923072b8 | 49 | pub(crate) type LabelSuggestion = (Ident, bool); |
f035d41b | 50 | |
923072b8 | 51 | pub(crate) enum SuggestionTarget { |
94222f64 XL |
52 | /// The target has a similar name as the name used by the programmer (probably a typo) |
53 | SimilarlyNamed, | |
54 | /// The target is the only valid item that can be used in the corresponding context | |
55 | SingleItem, | |
56 | } | |
57 | ||
923072b8 | 58 | pub(crate) struct TypoSuggestion { |
416331ca XL |
59 | pub candidate: Symbol, |
60 | pub res: Res, | |
94222f64 | 61 | pub target: SuggestionTarget, |
416331ca | 62 | } |
48663c56 | 63 | |
416331ca | 64 | impl TypoSuggestion { |
923072b8 | 65 | pub(crate) fn typo_from_res(candidate: Symbol, res: Res) -> TypoSuggestion { |
94222f64 XL |
66 | Self { candidate, res, target: SuggestionTarget::SimilarlyNamed } |
67 | } | |
923072b8 | 68 | pub(crate) fn single_item_from_res(candidate: Symbol, res: Res) -> TypoSuggestion { |
94222f64 | 69 | Self { candidate, res, target: SuggestionTarget::SingleItem } |
416331ca XL |
70 | } |
71 | } | |
48663c56 | 72 | |
416331ca | 73 | /// A free importable items suggested in case of resolution failure. |
923072b8 | 74 | pub(crate) struct ImportSuggestion { |
416331ca | 75 | pub did: Option<DefId>, |
f9f354fc | 76 | pub descr: &'static str, |
416331ca | 77 | pub path: Path, |
f035d41b | 78 | pub accessible: bool, |
3c0e092e XL |
79 | /// An extra note that should be issued if this item is suggested |
80 | pub note: Option<String>, | |
416331ca | 81 | } |
48663c56 | 82 | |
416331ca XL |
83 | /// Adjust the impl span so that just the `impl` keyword is taken by removing |
84 | /// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and | |
85 | /// everything after the first whitespace (`"impl Iterator for A" -> "impl"`). | |
86 | /// | |
87 | /// *Attention*: the method used is very fragile since it essentially duplicates the work of the | |
88 | /// parser. If you need to use this function or something similar, please consider updating the | |
89 | /// `source_map` functions and this function to something more robust. | |
74b04a01 XL |
90 | fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { |
91 | let impl_span = sm.span_until_char(impl_span, '<'); | |
ba9703b0 | 92 | sm.span_until_whitespace(impl_span) |
416331ca | 93 | } |
3157f602 | 94 | |
e1599b0c | 95 | impl<'a> Resolver<'a> { |
923072b8 | 96 | pub(crate) fn report_errors(&mut self, krate: &Crate) { |
04454e1e FG |
97 | self.report_with_use_injections(krate); |
98 | ||
99 | for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { | |
100 | let msg = "macro-expanded `macro_export` macros from the current crate \ | |
101 | cannot be referred to by absolute paths"; | |
102 | self.lint_buffer.buffer_lint_with_diagnostic( | |
103 | MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, | |
104 | CRATE_NODE_ID, | |
105 | span_use, | |
106 | msg, | |
107 | BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def), | |
108 | ); | |
109 | } | |
110 | ||
111 | for ambiguity_error in &self.ambiguity_errors { | |
112 | self.report_ambiguity_error(ambiguity_error); | |
113 | } | |
114 | ||
115 | let mut reported_spans = FxHashSet::default(); | |
116 | for error in &self.privacy_errors { | |
117 | if reported_spans.insert(error.dedup_span) { | |
118 | self.report_privacy_error(error); | |
119 | } | |
120 | } | |
121 | } | |
122 | ||
123 | fn report_with_use_injections(&mut self, krate: &Crate) { | |
124 | for UseError { mut err, candidates, def_id, instead, suggestion, path } in | |
125 | self.use_injections.drain(..) | |
126 | { | |
127 | let (span, found_use) = if let Some(def_id) = def_id.as_local() { | |
128 | UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id]) | |
129 | } else { | |
130 | (None, FoundUse::No) | |
131 | }; | |
132 | if !candidates.is_empty() { | |
133 | show_candidates( | |
923072b8 FG |
134 | &self.session, |
135 | &self.source_span, | |
04454e1e FG |
136 | &mut err, |
137 | span, | |
138 | &candidates, | |
139 | if instead { Instead::Yes } else { Instead::No }, | |
140 | found_use, | |
141 | IsPattern::No, | |
142 | path, | |
143 | ); | |
144 | } else if let Some((span, msg, sugg, appl)) = suggestion { | |
145 | err.span_suggestion(span, msg, sugg, appl); | |
146 | } | |
147 | err.emit(); | |
148 | } | |
149 | } | |
150 | ||
923072b8 | 151 | pub(crate) fn report_conflict<'b>( |
04454e1e FG |
152 | &mut self, |
153 | parent: Module<'_>, | |
154 | ident: Ident, | |
155 | ns: Namespace, | |
156 | new_binding: &NameBinding<'b>, | |
157 | old_binding: &NameBinding<'b>, | |
158 | ) { | |
159 | // Error on the second of two conflicting names | |
160 | if old_binding.span.lo() > new_binding.span.lo() { | |
161 | return self.report_conflict(parent, ident, ns, old_binding, new_binding); | |
162 | } | |
163 | ||
164 | let container = match parent.kind { | |
165 | ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()), | |
064997fb | 166 | ModuleKind::Block => "block", |
04454e1e FG |
167 | }; |
168 | ||
169 | let old_noun = match old_binding.is_import() { | |
170 | true => "import", | |
171 | false => "definition", | |
172 | }; | |
173 | ||
174 | let new_participle = match new_binding.is_import() { | |
175 | true => "imported", | |
176 | false => "defined", | |
177 | }; | |
178 | ||
179 | let (name, span) = | |
180 | (ident.name, self.session.source_map().guess_head_span(new_binding.span)); | |
181 | ||
182 | if let Some(s) = self.name_already_seen.get(&name) { | |
183 | if s == &span { | |
184 | return; | |
185 | } | |
186 | } | |
187 | ||
188 | let old_kind = match (ns, old_binding.module()) { | |
189 | (ValueNS, _) => "value", | |
190 | (MacroNS, _) => "macro", | |
191 | (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", | |
192 | (TypeNS, Some(module)) if module.is_normal() => "module", | |
193 | (TypeNS, Some(module)) if module.is_trait() => "trait", | |
194 | (TypeNS, _) => "type", | |
195 | }; | |
196 | ||
197 | let msg = format!("the name `{}` is defined multiple times", name); | |
198 | ||
199 | let mut err = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { | |
200 | (true, true) => struct_span_err!(self.session, span, E0259, "{}", msg), | |
201 | (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { | |
202 | true => struct_span_err!(self.session, span, E0254, "{}", msg), | |
203 | false => struct_span_err!(self.session, span, E0260, "{}", msg), | |
204 | }, | |
205 | _ => match (old_binding.is_import(), new_binding.is_import()) { | |
206 | (false, false) => struct_span_err!(self.session, span, E0428, "{}", msg), | |
207 | (true, true) => struct_span_err!(self.session, span, E0252, "{}", msg), | |
208 | _ => struct_span_err!(self.session, span, E0255, "{}", msg), | |
209 | }, | |
210 | }; | |
211 | ||
212 | err.note(&format!( | |
213 | "`{}` must be defined only once in the {} namespace of this {}", | |
214 | name, | |
215 | ns.descr(), | |
216 | container | |
217 | )); | |
218 | ||
219 | err.span_label(span, format!("`{}` re{} here", name, new_participle)); | |
220 | err.span_label( | |
221 | self.session.source_map().guess_head_span(old_binding.span), | |
222 | format!("previous {} of the {} `{}` here", old_noun, old_kind, name), | |
223 | ); | |
224 | ||
225 | // See https://github.com/rust-lang/rust/issues/32354 | |
226 | use NameBindingKind::Import; | |
227 | let import = match (&new_binding.kind, &old_binding.kind) { | |
228 | // If there are two imports where one or both have attributes then prefer removing the | |
229 | // import without attributes. | |
230 | (Import { import: new, .. }, Import { import: old, .. }) | |
231 | if { | |
232 | !new_binding.span.is_dummy() | |
233 | && !old_binding.span.is_dummy() | |
234 | && (new.has_attributes || old.has_attributes) | |
235 | } => | |
236 | { | |
237 | if old.has_attributes { | |
238 | Some((new, new_binding.span, true)) | |
239 | } else { | |
240 | Some((old, old_binding.span, true)) | |
241 | } | |
242 | } | |
243 | // Otherwise prioritize the new binding. | |
244 | (Import { import, .. }, other) if !new_binding.span.is_dummy() => { | |
245 | Some((import, new_binding.span, other.is_import())) | |
246 | } | |
247 | (other, Import { import, .. }) if !old_binding.span.is_dummy() => { | |
248 | Some((import, old_binding.span, other.is_import())) | |
249 | } | |
250 | _ => None, | |
251 | }; | |
252 | ||
253 | // Check if the target of the use for both bindings is the same. | |
254 | let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); | |
255 | let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); | |
256 | let from_item = | |
257 | self.extern_prelude.get(&ident).map_or(true, |entry| entry.introduced_by_item); | |
258 | // Only suggest removing an import if both bindings are to the same def, if both spans | |
259 | // aren't dummy spans. Further, if both bindings are imports, then the ident must have | |
260 | // been introduced by an item. | |
261 | let should_remove_import = duplicate | |
262 | && !has_dummy_span | |
263 | && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item); | |
264 | ||
265 | match import { | |
266 | Some((import, span, true)) if should_remove_import && import.is_nested() => { | |
267 | self.add_suggestion_for_duplicate_nested_use(&mut err, import, span) | |
268 | } | |
269 | Some((import, _, true)) if should_remove_import && !import.is_glob() => { | |
270 | // Simple case - remove the entire import. Due to the above match arm, this can | |
271 | // only be a single use so just remove it entirely. | |
272 | err.tool_only_span_suggestion( | |
273 | import.use_span_with_attributes, | |
274 | "remove unnecessary import", | |
923072b8 | 275 | "", |
04454e1e FG |
276 | Applicability::MaybeIncorrect, |
277 | ); | |
278 | } | |
279 | Some((import, span, _)) => { | |
280 | self.add_suggestion_for_rename_of_use(&mut err, name, import, span) | |
281 | } | |
282 | _ => {} | |
283 | } | |
284 | ||
285 | err.emit(); | |
286 | self.name_already_seen.insert(name, span); | |
287 | } | |
288 | ||
289 | /// This function adds a suggestion to change the binding name of a new import that conflicts | |
290 | /// with an existing import. | |
291 | /// | |
292 | /// ```text,ignore (diagnostic) | |
293 | /// help: you can use `as` to change the binding name of the import | |
294 | /// | | |
295 | /// LL | use foo::bar as other_bar; | |
296 | /// | ^^^^^^^^^^^^^^^^^^^^^ | |
297 | /// ``` | |
298 | fn add_suggestion_for_rename_of_use( | |
299 | &self, | |
300 | err: &mut Diagnostic, | |
301 | name: Symbol, | |
302 | import: &Import<'_>, | |
303 | binding_span: Span, | |
304 | ) { | |
305 | let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { | |
306 | format!("Other{}", name) | |
307 | } else { | |
308 | format!("other_{}", name) | |
309 | }; | |
310 | ||
311 | let mut suggestion = None; | |
312 | match import.kind { | |
313 | ImportKind::Single { type_ns_only: true, .. } => { | |
314 | suggestion = Some(format!("self as {}", suggested_name)) | |
315 | } | |
316 | ImportKind::Single { source, .. } => { | |
317 | if let Some(pos) = | |
318 | source.span.hi().0.checked_sub(binding_span.lo().0).map(|pos| pos as usize) | |
319 | { | |
320 | if let Ok(snippet) = self.session.source_map().span_to_snippet(binding_span) { | |
321 | if pos <= snippet.len() { | |
322 | suggestion = Some(format!( | |
323 | "{} as {}{}", | |
324 | &snippet[..pos], | |
325 | suggested_name, | |
326 | if snippet.ends_with(';') { ";" } else { "" } | |
327 | )) | |
328 | } | |
329 | } | |
330 | } | |
331 | } | |
332 | ImportKind::ExternCrate { source, target } => { | |
333 | suggestion = Some(format!( | |
334 | "extern crate {} as {};", | |
335 | source.unwrap_or(target.name), | |
336 | suggested_name, | |
337 | )) | |
338 | } | |
339 | _ => unreachable!(), | |
340 | } | |
341 | ||
342 | let rename_msg = "you can use `as` to change the binding name of the import"; | |
343 | if let Some(suggestion) = suggestion { | |
344 | err.span_suggestion( | |
345 | binding_span, | |
346 | rename_msg, | |
347 | suggestion, | |
348 | Applicability::MaybeIncorrect, | |
349 | ); | |
350 | } else { | |
351 | err.span_label(binding_span, rename_msg); | |
352 | } | |
353 | } | |
354 | ||
355 | /// This function adds a suggestion to remove an unnecessary binding from an import that is | |
356 | /// nested. In the following example, this function will be invoked to remove the `a` binding | |
357 | /// in the second use statement: | |
358 | /// | |
359 | /// ```ignore (diagnostic) | |
360 | /// use issue_52891::a; | |
361 | /// use issue_52891::{d, a, e}; | |
362 | /// ``` | |
363 | /// | |
364 | /// The following suggestion will be added: | |
365 | /// | |
366 | /// ```ignore (diagnostic) | |
367 | /// use issue_52891::{d, a, e}; | |
368 | /// ^-- help: remove unnecessary import | |
369 | /// ``` | |
370 | /// | |
371 | /// If the nested use contains only one import then the suggestion will remove the entire | |
372 | /// line. | |
373 | /// | |
374 | /// It is expected that the provided import is nested - this isn't checked by the | |
375 | /// function. If this invariant is not upheld, this function's behaviour will be unexpected | |
376 | /// as characters expected by span manipulations won't be present. | |
377 | fn add_suggestion_for_duplicate_nested_use( | |
378 | &self, | |
379 | err: &mut Diagnostic, | |
380 | import: &Import<'_>, | |
381 | binding_span: Span, | |
382 | ) { | |
383 | assert!(import.is_nested()); | |
384 | let message = "remove unnecessary import"; | |
385 | ||
386 | // Two examples will be used to illustrate the span manipulations we're doing: | |
387 | // | |
388 | // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is | |
389 | // `a` and `import.use_span` is `issue_52891::{d, a, e};`. | |
390 | // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is | |
391 | // `a` and `import.use_span` is `issue_52891::{d, e, a};`. | |
392 | ||
393 | let (found_closing_brace, span) = | |
394 | find_span_of_binding_until_next_binding(self.session, binding_span, import.use_span); | |
395 | ||
396 | // If there was a closing brace then identify the span to remove any trailing commas from | |
397 | // previous imports. | |
398 | if found_closing_brace { | |
399 | if let Some(span) = extend_span_to_previous_binding(self.session, span) { | |
923072b8 | 400 | err.tool_only_span_suggestion(span, message, "", Applicability::MaybeIncorrect); |
04454e1e FG |
401 | } else { |
402 | // Remove the entire line if we cannot extend the span back, this indicates an | |
403 | // `issue_52891::{self}` case. | |
404 | err.span_suggestion( | |
405 | import.use_span_with_attributes, | |
406 | message, | |
923072b8 | 407 | "", |
04454e1e FG |
408 | Applicability::MaybeIncorrect, |
409 | ); | |
410 | } | |
411 | ||
412 | return; | |
413 | } | |
414 | ||
923072b8 | 415 | err.span_suggestion(span, message, "", Applicability::MachineApplicable); |
04454e1e FG |
416 | } |
417 | ||
923072b8 | 418 | pub(crate) fn lint_if_path_starts_with_module( |
04454e1e FG |
419 | &mut self, |
420 | finalize: Option<Finalize>, | |
421 | path: &[Segment], | |
422 | second_binding: Option<&NameBinding<'_>>, | |
423 | ) { | |
424 | let Some(Finalize { node_id, root_span, .. }) = finalize else { | |
425 | return; | |
426 | }; | |
427 | ||
428 | let first_name = match path.get(0) { | |
429 | // In the 2018 edition this lint is a hard error, so nothing to do | |
430 | Some(seg) if seg.ident.span.rust_2015() && self.session.rust_2015() => seg.ident.name, | |
431 | _ => return, | |
432 | }; | |
433 | ||
434 | // We're only interested in `use` paths which should start with | |
435 | // `{{root}}` currently. | |
436 | if first_name != kw::PathRoot { | |
437 | return; | |
438 | } | |
439 | ||
440 | match path.get(1) { | |
441 | // If this import looks like `crate::...` it's already good | |
442 | Some(Segment { ident, .. }) if ident.name == kw::Crate => return, | |
443 | // Otherwise go below to see if it's an extern crate | |
444 | Some(_) => {} | |
445 | // If the path has length one (and it's `PathRoot` most likely) | |
446 | // then we don't know whether we're gonna be importing a crate or an | |
447 | // item in our crate. Defer this lint to elsewhere | |
448 | None => return, | |
449 | } | |
450 | ||
451 | // If the first element of our path was actually resolved to an | |
452 | // `ExternCrate` (also used for `crate::...`) then no need to issue a | |
453 | // warning, this looks all good! | |
454 | if let Some(binding) = second_binding { | |
455 | if let NameBindingKind::Import { import, .. } = binding.kind { | |
456 | // Careful: we still want to rewrite paths from renamed extern crates. | |
457 | if let ImportKind::ExternCrate { source: None, .. } = import.kind { | |
458 | return; | |
459 | } | |
460 | } | |
461 | } | |
462 | ||
463 | let diag = BuiltinLintDiagnostics::AbsPathWithModule(root_span); | |
464 | self.lint_buffer.buffer_lint_with_diagnostic( | |
465 | ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, | |
466 | node_id, | |
467 | root_span, | |
468 | "absolute paths must start with `self`, `super`, \ | |
469 | `crate`, or an external crate name in the 2018 edition", | |
470 | diag, | |
471 | ); | |
472 | } | |
473 | ||
923072b8 | 474 | pub(crate) fn add_module_candidates( |
e1599b0c XL |
475 | &mut self, |
476 | module: Module<'a>, | |
477 | names: &mut Vec<TypoSuggestion>, | |
478 | filter_fn: &impl Fn(Res) -> bool, | |
479 | ) { | |
e74abb32 | 480 | for (key, resolution) in self.resolutions(module).borrow().iter() { |
e1599b0c XL |
481 | if let Some(binding) = resolution.borrow().binding { |
482 | let res = binding.res(); | |
483 | if filter_fn(res) { | |
94222f64 | 484 | names.push(TypoSuggestion::typo_from_res(key.ident.name, res)); |
e1599b0c | 485 | } |
416331ca | 486 | } |
48663c56 | 487 | } |
416331ca | 488 | } |
3157f602 | 489 | |
416331ca XL |
490 | /// Combines an error with provided span and emits it. |
491 | /// | |
492 | /// This takes the error provided, combines it with the span and any additional spans inside the | |
493 | /// error and emits it. | |
923072b8 | 494 | pub(crate) fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) { |
416331ca XL |
495 | self.into_struct_error(span, resolution_error).emit(); |
496 | } | |
48663c56 | 497 | |
923072b8 | 498 | pub(crate) fn into_struct_error( |
04454e1e | 499 | &mut self, |
dfeec247 | 500 | span: Span, |
04454e1e | 501 | resolution_error: ResolutionError<'a>, |
5e7ed085 | 502 | ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { |
416331ca | 503 | match resolution_error { |
e74abb32 | 504 | ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => { |
dfeec247 XL |
505 | let mut err = struct_span_err!( |
506 | self.session, | |
48663c56 | 507 | span, |
416331ca XL |
508 | E0401, |
509 | "can't use generic parameters from outer function", | |
48663c56 | 510 | ); |
064997fb | 511 | err.span_label(span, "use of generic parameter from outer function"); |
416331ca | 512 | |
74b04a01 | 513 | let sm = self.session.source_map(); |
416331ca | 514 | match outer_res { |
5099ac24 | 515 | Res::SelfTy { trait_: maybe_trait_defid, alias_to: maybe_impl_defid } => { |
dfeec247 | 516 | if let Some(impl_span) = |
1b1a35ee | 517 | maybe_impl_defid.and_then(|(def_id, _)| self.opt_span(def_id)) |
dfeec247 | 518 | { |
48663c56 | 519 | err.span_label( |
74b04a01 | 520 | reduce_impl_span_to_impl_keyword(sm, impl_span), |
416331ca | 521 | "`Self` type implicitly declared here, by this `impl`", |
48663c56 XL |
522 | ); |
523 | } | |
416331ca XL |
524 | match (maybe_trait_defid, maybe_impl_defid) { |
525 | (Some(_), None) => { | |
526 | err.span_label(span, "can't use `Self` here"); | |
527 | } | |
528 | (_, Some(_)) => { | |
529 | err.span_label(span, "use a type here instead"); | |
530 | } | |
531 | (None, None) => bug!("`impl` without trait nor type?"), | |
532 | } | |
533 | return err; | |
dfeec247 | 534 | } |
416331ca | 535 | Res::Def(DefKind::TyParam, def_id) => { |
f035d41b | 536 | if let Some(span) = self.opt_span(def_id) { |
416331ca XL |
537 | err.span_label(span, "type parameter from outer function"); |
538 | } | |
48663c56 | 539 | } |
416331ca | 540 | Res::Def(DefKind::ConstParam, def_id) => { |
f035d41b | 541 | if let Some(span) = self.opt_span(def_id) { |
416331ca XL |
542 | err.span_label(span, "const parameter from outer function"); |
543 | } | |
48663c56 | 544 | } |
416331ca | 545 | _ => { |
dfeec247 XL |
546 | bug!( |
547 | "GenericParamsFromOuterFunction should only be used with Res::SelfTy, \ | |
fc512014 | 548 | DefKind::TyParam or DefKind::ConstParam" |
dfeec247 | 549 | ); |
48663c56 XL |
550 | } |
551 | } | |
d9579d0f | 552 | |
e74abb32 XL |
553 | if has_generic_params == HasGenericParams::Yes { |
554 | // Try to retrieve the span of the function signature and generate a new | |
555 | // message with a local type or const parameter. | |
74b04a01 XL |
556 | let sugg_msg = "try using a local generic parameter instead"; |
557 | if let Some((sugg_span, snippet)) = sm.generate_local_type_param_snippet(span) { | |
e74abb32 XL |
558 | // Suggest the modification to the user |
559 | err.span_suggestion( | |
560 | sugg_span, | |
561 | sugg_msg, | |
562 | snippet, | |
563 | Applicability::MachineApplicable, | |
564 | ); | |
74b04a01 | 565 | } else if let Some(sp) = sm.generate_fn_name_span(span) { |
dfeec247 XL |
566 | err.span_label( |
567 | sp, | |
064997fb | 568 | "try adding a local generic parameter in this method instead", |
dfeec247 | 569 | ); |
e74abb32 | 570 | } else { |
74b04a01 | 571 | err.help("try using a local generic parameter instead"); |
e74abb32 | 572 | } |
416331ca | 573 | } |
c1a9b12d | 574 | |
416331ca | 575 | err |
48663c56 | 576 | } |
416331ca | 577 | ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { |
e1599b0c XL |
578 | let mut err = struct_span_err!( |
579 | self.session, | |
580 | span, | |
581 | E0403, | |
582 | "the name `{}` is already used for a generic \ | |
583 | parameter in this item's generic parameters", | |
584 | name, | |
585 | ); | |
416331ca XL |
586 | err.span_label(span, "already used"); |
587 | err.span_label(first_use_span, format!("first use of `{}`", name)); | |
588 | err | |
589 | } | |
c295e0f8 | 590 | ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => { |
dfeec247 XL |
591 | let mut err = struct_span_err!( |
592 | self.session, | |
593 | span, | |
594 | E0407, | |
595 | "method `{}` is not a member of trait `{}`", | |
596 | method, | |
597 | trait_ | |
598 | ); | |
416331ca | 599 | err.span_label(span, format!("not a member of trait `{}`", trait_)); |
c295e0f8 XL |
600 | if let Some(candidate) = candidate { |
601 | err.span_suggestion( | |
602 | method.span, | |
603 | "there is an associated function with a similar name", | |
604 | candidate.to_ident_string(), | |
605 | Applicability::MaybeIncorrect, | |
606 | ); | |
607 | } | |
416331ca XL |
608 | err |
609 | } | |
c295e0f8 | 610 | ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => { |
dfeec247 XL |
611 | let mut err = struct_span_err!( |
612 | self.session, | |
613 | span, | |
614 | E0437, | |
615 | "type `{}` is not a member of trait `{}`", | |
616 | type_, | |
617 | trait_ | |
618 | ); | |
416331ca | 619 | err.span_label(span, format!("not a member of trait `{}`", trait_)); |
c295e0f8 XL |
620 | if let Some(candidate) = candidate { |
621 | err.span_suggestion( | |
622 | type_.span, | |
623 | "there is an associated type with a similar name", | |
624 | candidate.to_ident_string(), | |
625 | Applicability::MaybeIncorrect, | |
626 | ); | |
627 | } | |
416331ca XL |
628 | err |
629 | } | |
c295e0f8 | 630 | ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => { |
dfeec247 XL |
631 | let mut err = struct_span_err!( |
632 | self.session, | |
633 | span, | |
634 | E0438, | |
635 | "const `{}` is not a member of trait `{}`", | |
636 | const_, | |
637 | trait_ | |
638 | ); | |
416331ca | 639 | err.span_label(span, format!("not a member of trait `{}`", trait_)); |
c295e0f8 XL |
640 | if let Some(candidate) = candidate { |
641 | err.span_suggestion( | |
642 | const_.span, | |
643 | "there is an associated constant with a similar name", | |
644 | candidate.to_ident_string(), | |
645 | Applicability::MaybeIncorrect, | |
646 | ); | |
647 | } | |
416331ca XL |
648 | err |
649 | } | |
04454e1e | 650 | ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { |
416331ca | 651 | let BindingError { name, target, origin, could_be_path } = binding_error; |
3157f602 | 652 | |
416331ca XL |
653 | let target_sp = target.iter().copied().collect::<Vec<_>>(); |
654 | let origin_sp = origin.iter().copied().collect::<Vec<_>>(); | |
5bcae85e | 655 | |
416331ca | 656 | let msp = MultiSpan::from_spans(target_sp.clone()); |
60c5eb7d XL |
657 | let mut err = struct_span_err!( |
658 | self.session, | |
416331ca | 659 | msp, |
60c5eb7d | 660 | E0408, |
dfeec247 XL |
661 | "variable `{}` is not bound in all patterns", |
662 | name, | |
416331ca XL |
663 | ); |
664 | for sp in target_sp { | |
665 | err.span_label(sp, format!("pattern doesn't bind `{}`", name)); | |
666 | } | |
667 | for sp in origin_sp { | |
668 | err.span_label(sp, "variable not in all patterns"); | |
669 | } | |
04454e1e FG |
670 | if could_be_path { |
671 | let import_suggestions = self.lookup_import_candidates( | |
672 | Ident::with_dummy_span(name), | |
673 | Namespace::ValueNS, | |
674 | &parent_scope, | |
675 | &|res: Res| match res { | |
676 | Res::Def( | |
677 | DefKind::Ctor(CtorOf::Variant, CtorKind::Const) | |
678 | | DefKind::Ctor(CtorOf::Struct, CtorKind::Const) | |
679 | | DefKind::Const | |
680 | | DefKind::AssocConst, | |
681 | _, | |
682 | ) => true, | |
683 | _ => false, | |
684 | }, | |
685 | ); | |
686 | ||
687 | if import_suggestions.is_empty() { | |
688 | let help_msg = format!( | |
689 | "if you meant to match on a variant or a `const` item, consider \ | |
690 | making the path in the pattern qualified: `path::to::ModOrType::{}`", | |
691 | name, | |
692 | ); | |
693 | err.span_help(span, &help_msg); | |
694 | } | |
695 | show_candidates( | |
923072b8 FG |
696 | &self.session, |
697 | &self.source_span, | |
04454e1e FG |
698 | &mut err, |
699 | Some(span), | |
700 | &import_suggestions, | |
701 | Instead::No, | |
702 | FoundUse::Yes, | |
703 | IsPattern::Yes, | |
704 | vec![], | |
dfeec247 | 705 | ); |
48663c56 | 706 | } |
416331ca | 707 | err |
48663c56 | 708 | } |
dfeec247 XL |
709 | ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { |
710 | let mut err = struct_span_err!( | |
711 | self.session, | |
712 | span, | |
713 | E0409, | |
74b04a01 | 714 | "variable `{}` is bound inconsistently across alternatives separated by `|`", |
dfeec247 XL |
715 | variable_name |
716 | ); | |
416331ca XL |
717 | err.span_label(span, "bound in different ways"); |
718 | err.span_label(first_binding_span, "first binding"); | |
719 | err | |
720 | } | |
721 | ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { | |
dfeec247 XL |
722 | let mut err = struct_span_err!( |
723 | self.session, | |
724 | span, | |
725 | E0415, | |
726 | "identifier `{}` is bound more than once in this parameter list", | |
727 | identifier | |
728 | ); | |
416331ca XL |
729 | err.span_label(span, "used as parameter more than once"); |
730 | err | |
731 | } | |
732 | ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { | |
dfeec247 XL |
733 | let mut err = struct_span_err!( |
734 | self.session, | |
735 | span, | |
736 | E0416, | |
737 | "identifier `{}` is bound more than once in the same pattern", | |
738 | identifier | |
739 | ); | |
416331ca XL |
740 | err.span_label(span, "used in a pattern more than once"); |
741 | err | |
742 | } | |
f035d41b | 743 | ResolutionError::UndeclaredLabel { name, suggestion } => { |
dfeec247 XL |
744 | let mut err = struct_span_err!( |
745 | self.session, | |
746 | span, | |
747 | E0426, | |
748 | "use of undeclared label `{}`", | |
749 | name | |
750 | ); | |
f035d41b XL |
751 | |
752 | err.span_label(span, format!("undeclared label `{}`", name)); | |
753 | ||
754 | match suggestion { | |
755 | // A reachable label with a similar name exists. | |
756 | Some((ident, true)) => { | |
757 | err.span_label(ident.span, "a label with a similar name is reachable"); | |
758 | err.span_suggestion( | |
759 | span, | |
760 | "try using similarly named label", | |
923072b8 | 761 | ident.name, |
f035d41b XL |
762 | Applicability::MaybeIncorrect, |
763 | ); | |
764 | } | |
765 | // An unreachable label with a similar name exists. | |
766 | Some((ident, false)) => { | |
767 | err.span_label( | |
768 | ident.span, | |
769 | "a label with a similar name exists but is unreachable", | |
770 | ); | |
771 | } | |
772 | // No similarly-named labels exist. | |
773 | None => (), | |
48663c56 | 774 | } |
f035d41b | 775 | |
416331ca | 776 | err |
48663c56 | 777 | } |
f9f354fc XL |
778 | ResolutionError::SelfImportsOnlyAllowedWithin { root, span_with_rename } => { |
779 | let mut err = struct_span_err!( | |
780 | self.session, | |
781 | span, | |
782 | E0429, | |
783 | "{}", | |
784 | "`self` imports are only allowed within a { } list" | |
785 | ); | |
786 | ||
787 | // None of the suggestions below would help with a case like `use self`. | |
788 | if !root { | |
789 | // use foo::bar::self -> foo::bar | |
790 | // use foo::bar::self as abc -> foo::bar as abc | |
791 | err.span_suggestion( | |
792 | span, | |
793 | "consider importing the module directly", | |
923072b8 | 794 | "", |
f9f354fc XL |
795 | Applicability::MachineApplicable, |
796 | ); | |
797 | ||
798 | // use foo::bar::self -> foo::bar::{self} | |
799 | // use foo::bar::self as abc -> foo::bar::{self as abc} | |
800 | let braces = vec![ | |
801 | (span_with_rename.shrink_to_lo(), "{".to_string()), | |
802 | (span_with_rename.shrink_to_hi(), "}".to_string()), | |
803 | ]; | |
804 | err.multipart_suggestion( | |
805 | "alternatively, use the multi-path `use` syntax to import `self`", | |
806 | braces, | |
807 | Applicability::MachineApplicable, | |
808 | ); | |
809 | } | |
810 | err | |
811 | } | |
416331ca | 812 | ResolutionError::SelfImportCanOnlyAppearOnceInTheList => { |
dfeec247 XL |
813 | let mut err = struct_span_err!( |
814 | self.session, | |
815 | span, | |
816 | E0430, | |
817 | "`self` import can only appear once in an import list" | |
818 | ); | |
416331ca XL |
819 | err.span_label(span, "can only appear once in an import list"); |
820 | err | |
48663c56 | 821 | } |
416331ca | 822 | ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => { |
dfeec247 XL |
823 | let mut err = struct_span_err!( |
824 | self.session, | |
825 | span, | |
826 | E0431, | |
827 | "`self` import can only appear in an import list with \ | |
828 | a non-empty prefix" | |
829 | ); | |
416331ca XL |
830 | err.span_label(span, "can only appear in an import list with a non-empty prefix"); |
831 | err | |
48663c56 | 832 | } |
416331ca | 833 | ResolutionError::FailedToResolve { label, suggestion } => { |
dfeec247 XL |
834 | let mut err = |
835 | struct_span_err!(self.session, span, E0433, "failed to resolve: {}", &label); | |
416331ca | 836 | err.span_label(span, label); |
48663c56 | 837 | |
416331ca | 838 | if let Some((suggestions, msg, applicability)) = suggestion { |
3c0e092e XL |
839 | if suggestions.is_empty() { |
840 | err.help(&msg); | |
841 | return err; | |
842 | } | |
416331ca | 843 | err.multipart_suggestion(&msg, suggestions, applicability); |
48663c56 | 844 | } |
416331ca XL |
845 | |
846 | err | |
847 | } | |
848 | ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { | |
dfeec247 XL |
849 | let mut err = struct_span_err!( |
850 | self.session, | |
851 | span, | |
852 | E0434, | |
853 | "{}", | |
854 | "can't capture dynamic environment in a fn item" | |
855 | ); | |
416331ca XL |
856 | err.help("use the `|| { ... }` closure form instead"); |
857 | err | |
48663c56 | 858 | } |
5869c6ff | 859 | ResolutionError::AttemptToUseNonConstantValueInConstant(ident, sugg, current) => { |
dfeec247 XL |
860 | let mut err = struct_span_err!( |
861 | self.session, | |
862 | span, | |
863 | E0435, | |
864 | "attempt to use a non-constant value in a constant" | |
865 | ); | |
5869c6ff XL |
866 | // let foo =... |
867 | // ^^^ given this Span | |
868 | // ------- get this Span to have an applicable suggestion | |
3c0e092e XL |
869 | |
870 | // edit: | |
871 | // only do this if the const and usage of the non-constant value are on the same line | |
872 | // the further the two are apart, the higher the chance of the suggestion being wrong | |
3c0e092e | 873 | |
5099ac24 FG |
874 | let sp = self |
875 | .session | |
876 | .source_map() | |
877 | .span_extend_to_prev_str(ident.span, current, true, false); | |
3c0e092e | 878 | |
5099ac24 FG |
879 | match sp { |
880 | Some(sp) if !self.session.source_map().is_multiline(sp) => { | |
881 | let sp = sp.with_lo(BytePos(sp.lo().0 - (current.len() as u32))); | |
882 | err.span_suggestion( | |
883 | sp, | |
884 | &format!("consider using `{}` instead of `{}`", sugg, current), | |
885 | format!("{} {}", sugg, ident), | |
886 | Applicability::MaybeIncorrect, | |
887 | ); | |
888 | err.span_label(span, "non-constant value"); | |
889 | } | |
890 | _ => { | |
891 | err.span_label(ident.span, &format!("this would need to be a `{}`", sugg)); | |
892 | } | |
5869c6ff | 893 | } |
5099ac24 | 894 | |
416331ca XL |
895 | err |
896 | } | |
17df50a5 | 897 | ResolutionError::BindingShadowsSomethingUnacceptable { |
064997fb | 898 | shadowing_binding, |
17df50a5 XL |
899 | name, |
900 | participle, | |
901 | article, | |
064997fb | 902 | shadowed_binding, |
17df50a5 XL |
903 | shadowed_binding_span, |
904 | } => { | |
064997fb | 905 | let shadowed_binding_descr = shadowed_binding.descr(); |
dfeec247 XL |
906 | let mut err = struct_span_err!( |
907 | self.session, | |
908 | span, | |
909 | E0530, | |
910 | "{}s cannot shadow {}s", | |
064997fb | 911 | shadowing_binding.descr(), |
17df50a5 | 912 | shadowed_binding_descr, |
dfeec247 XL |
913 | ); |
914 | err.span_label( | |
915 | span, | |
17df50a5 | 916 | format!("cannot be named the same as {} {}", article, shadowed_binding_descr), |
dfeec247 | 917 | ); |
064997fb FG |
918 | match (shadowing_binding, shadowed_binding) { |
919 | ( | |
920 | PatternSource::Match, | |
921 | Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _), | |
922 | ) => { | |
923 | err.span_suggestion( | |
924 | span, | |
925 | "try specify the pattern arguments", | |
926 | format!("{}(..)", name), | |
927 | Applicability::Unspecified, | |
928 | ); | |
929 | } | |
930 | _ => (), | |
931 | } | |
17df50a5 XL |
932 | let msg = |
933 | format!("the {} `{}` is {} here", shadowed_binding_descr, name, participle); | |
934 | err.span_label(shadowed_binding_span, msg); | |
416331ca XL |
935 | err |
936 | } | |
17df50a5 | 937 | ResolutionError::ForwardDeclaredGenericParam => { |
dfeec247 XL |
938 | let mut err = struct_span_err!( |
939 | self.session, | |
940 | span, | |
941 | E0128, | |
cdc7bbd5 | 942 | "generic parameters with a default cannot use \ |
dfeec247 XL |
943 | forward declared identifiers" |
944 | ); | |
064997fb | 945 | err.span_label(span, "defaulted generic parameters cannot be forward declared"); |
416331ca XL |
946 | err |
947 | } | |
3dfed10e XL |
948 | ResolutionError::ParamInTyOfConstParam(name) => { |
949 | let mut err = struct_span_err!( | |
950 | self.session, | |
951 | span, | |
952 | E0770, | |
953 | "the type of const parameters must not depend on other generic parameters" | |
954 | ); | |
955 | err.span_label( | |
956 | span, | |
957 | format!("the type must not depend on the parameter `{}`", name), | |
958 | ); | |
959 | err | |
960 | } | |
1b1a35ee | 961 | ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => { |
3dfed10e XL |
962 | let mut err = self.session.struct_span_err( |
963 | span, | |
29967ef6 | 964 | "generic parameters may not be used in const operations", |
3dfed10e | 965 | ); |
29967ef6 | 966 | err.span_label(span, &format!("cannot perform const operation using `{}`", name)); |
1b1a35ee XL |
967 | |
968 | if is_type { | |
29967ef6 | 969 | err.note("type parameters may not be used in const expressions"); |
1b1a35ee | 970 | } else { |
29967ef6 XL |
971 | err.help(&format!( |
972 | "const parameters may only be used as standalone arguments, i.e. `{}`", | |
973 | name | |
974 | )); | |
1b1a35ee | 975 | } |
cdc7bbd5 XL |
976 | |
977 | if self.session.is_nightly_build() { | |
978 | err.help( | |
94222f64 | 979 | "use `#![feature(generic_const_exprs)]` to allow generic const expressions", |
cdc7bbd5 XL |
980 | ); |
981 | } | |
1b1a35ee | 982 | |
3dfed10e XL |
983 | err |
984 | } | |
136023e0 | 985 | ResolutionError::SelfInGenericParamDefault => { |
416331ca XL |
986 | let mut err = struct_span_err!( |
987 | self.session, | |
48663c56 | 988 | span, |
e74abb32 | 989 | E0735, |
136023e0 | 990 | "generic parameters cannot use `Self` in their defaults" |
48663c56 | 991 | ); |
064997fb | 992 | err.span_label(span, "`Self` in generic parameter default"); |
416331ca | 993 | err |
48663c56 | 994 | } |
f035d41b XL |
995 | ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { |
996 | let mut err = struct_span_err!( | |
997 | self.session, | |
998 | span, | |
999 | E0767, | |
1000 | "use of unreachable label `{}`", | |
1001 | name, | |
1002 | ); | |
1003 | ||
1004 | err.span_label(definition_span, "unreachable label defined here"); | |
1005 | err.span_label(span, format!("unreachable label `{}`", name)); | |
1006 | err.note( | |
1007 | "labels are unreachable through functions, closures, async blocks and modules", | |
1008 | ); | |
1009 | ||
1010 | match suggestion { | |
1011 | // A reachable label with a similar name exists. | |
1012 | Some((ident, true)) => { | |
1013 | err.span_label(ident.span, "a label with a similar name is reachable"); | |
1014 | err.span_suggestion( | |
1015 | span, | |
1016 | "try using similarly named label", | |
923072b8 | 1017 | ident.name, |
f035d41b XL |
1018 | Applicability::MaybeIncorrect, |
1019 | ); | |
1020 | } | |
1021 | // An unreachable label with a similar name exists. | |
1022 | Some((ident, false)) => { | |
1023 | err.span_label( | |
1024 | ident.span, | |
1025 | "a label with a similar name exists but is also unreachable", | |
1026 | ); | |
1027 | } | |
1028 | // No similarly-named labels exist. | |
1029 | None => (), | |
1030 | } | |
1031 | ||
1032 | err | |
1033 | } | |
5099ac24 FG |
1034 | ResolutionError::TraitImplMismatch { |
1035 | name, | |
1036 | kind, | |
1037 | code, | |
1038 | trait_item_span, | |
1039 | trait_path, | |
1040 | } => { | |
1041 | let mut err = self.session.struct_span_err_with_code( | |
1042 | span, | |
1043 | &format!( | |
1044 | "item `{}` is an associated {}, which doesn't match its trait `{}`", | |
1045 | name, kind, trait_path, | |
1046 | ), | |
1047 | code, | |
1048 | ); | |
1049 | err.span_label(span, "does not match trait"); | |
1050 | err.span_label(trait_item_span, "item in trait"); | |
1051 | err | |
1052 | } | |
04454e1e FG |
1053 | ResolutionError::InvalidAsmSym => { |
1054 | let mut err = self.session.struct_span_err(span, "invalid `sym` operand"); | |
1055 | err.span_label(span, "is a local variable"); | |
1056 | err.help("`sym` operands must refer to either a function or a static"); | |
1057 | err | |
1058 | } | |
416331ca XL |
1059 | } |
1060 | } | |
48663c56 | 1061 | |
923072b8 | 1062 | pub(crate) fn report_vis_error( |
04454e1e | 1063 | &mut self, |
5e7ed085 FG |
1064 | vis_resolution_error: VisResolutionError<'_>, |
1065 | ) -> ErrorGuaranteed { | |
e74abb32 XL |
1066 | match vis_resolution_error { |
1067 | VisResolutionError::Relative2018(span, path) => { | |
dfeec247 XL |
1068 | let mut err = self.session.struct_span_err( |
1069 | span, | |
04454e1e | 1070 | "relative paths are not supported in visibilities in 2018 edition or later", |
dfeec247 | 1071 | ); |
e74abb32 XL |
1072 | err.span_suggestion( |
1073 | path.span, | |
1074 | "try", | |
1075 | format!("crate::{}", pprust::path_to_string(&path)), | |
1076 | Applicability::MaybeIncorrect, | |
1077 | ); | |
1078 | err | |
1079 | } | |
dfeec247 XL |
1080 | VisResolutionError::AncestorOnly(span) => struct_span_err!( |
1081 | self.session, | |
1082 | span, | |
1083 | E0742, | |
1084 | "visibilities can only be restricted to ancestor modules" | |
1085 | ), | |
e74abb32 | 1086 | VisResolutionError::FailedToResolve(span, label, suggestion) => { |
dfeec247 | 1087 | self.into_struct_error(span, ResolutionError::FailedToResolve { label, suggestion }) |
e74abb32 XL |
1088 | } |
1089 | VisResolutionError::ExpectedFound(span, path_str, res) => { | |
dfeec247 XL |
1090 | let mut err = struct_span_err!( |
1091 | self.session, | |
1092 | span, | |
1093 | E0577, | |
1094 | "expected module, found {} `{}`", | |
1095 | res.descr(), | |
1096 | path_str | |
1097 | ); | |
e74abb32 XL |
1098 | err.span_label(span, "not a module"); |
1099 | err | |
1100 | } | |
dfeec247 XL |
1101 | VisResolutionError::Indeterminate(span) => struct_span_err!( |
1102 | self.session, | |
1103 | span, | |
1104 | E0578, | |
1105 | "cannot determine resolution for the visibility" | |
1106 | ), | |
e74abb32 XL |
1107 | VisResolutionError::ModuleOnly(span) => { |
1108 | self.session.struct_span_err(span, "visibility must resolve to a module") | |
1109 | } | |
dfeec247 XL |
1110 | } |
1111 | .emit() | |
e74abb32 XL |
1112 | } |
1113 | ||
416331ca XL |
1114 | /// Lookup typo candidate in scope for a macro or import. |
1115 | fn early_lookup_typo_candidate( | |
1116 | &mut self, | |
cdc7bbd5 | 1117 | scope_set: ScopeSet<'a>, |
416331ca XL |
1118 | parent_scope: &ParentScope<'a>, |
1119 | ident: Ident, | |
1120 | filter_fn: &impl Fn(Res) -> bool, | |
1121 | ) -> Option<TypoSuggestion> { | |
1122 | let mut suggestions = Vec::new(); | |
5869c6ff XL |
1123 | let ctxt = ident.span.ctxt(); |
1124 | self.visit_scopes(scope_set, parent_scope, ctxt, |this, scope, use_prelude, _| { | |
416331ca | 1125 | match scope { |
60c5eb7d XL |
1126 | Scope::DeriveHelpers(expn_id) => { |
1127 | let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); | |
1128 | if filter_fn(res) { | |
dfeec247 XL |
1129 | suggestions.extend( |
1130 | this.helper_attrs | |
1131 | .get(&expn_id) | |
1132 | .into_iter() | |
1133 | .flatten() | |
94222f64 | 1134 | .map(|ident| TypoSuggestion::typo_from_res(ident.name, res)), |
dfeec247 | 1135 | ); |
60c5eb7d XL |
1136 | } |
1137 | } | |
1138 | Scope::DeriveHelpersCompat => { | |
fc512014 | 1139 | let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat); |
416331ca | 1140 | if filter_fn(res) { |
e1599b0c | 1141 | for derive in parent_scope.derives { |
dfeec247 | 1142 | let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; |
416331ca | 1143 | if let Ok((Some(ext), _)) = this.resolve_macro_path( |
dfeec247 XL |
1144 | derive, |
1145 | Some(MacroKind::Derive), | |
1146 | parent_scope, | |
1147 | false, | |
1148 | false, | |
416331ca | 1149 | ) { |
dfeec247 XL |
1150 | suggestions.extend( |
1151 | ext.helper_attrs | |
1152 | .iter() | |
94222f64 | 1153 | .map(|name| TypoSuggestion::typo_from_res(*name, res)), |
dfeec247 | 1154 | ); |
416331ca XL |
1155 | } |
1156 | } | |
1157 | } | |
48663c56 | 1158 | } |
ba9703b0 | 1159 | Scope::MacroRules(macro_rules_scope) => { |
29967ef6 | 1160 | if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope.get() { |
ba9703b0 | 1161 | let res = macro_rules_binding.binding.res(); |
416331ca | 1162 | if filter_fn(res) { |
94222f64 XL |
1163 | suggestions.push(TypoSuggestion::typo_from_res( |
1164 | macro_rules_binding.ident.name, | |
1165 | res, | |
1166 | )) | |
416331ca XL |
1167 | } |
1168 | } | |
48663c56 | 1169 | } |
416331ca XL |
1170 | Scope::CrateRoot => { |
1171 | let root_ident = Ident::new(kw::PathRoot, ident.span); | |
1172 | let root_module = this.resolve_crate_root(root_ident); | |
e1599b0c | 1173 | this.add_module_candidates(root_module, &mut suggestions, filter_fn); |
48663c56 | 1174 | } |
cdc7bbd5 | 1175 | Scope::Module(module, _) => { |
e1599b0c | 1176 | this.add_module_candidates(module, &mut suggestions, filter_fn); |
416331ca | 1177 | } |
60c5eb7d XL |
1178 | Scope::RegisteredAttrs => { |
1179 | let res = Res::NonMacroAttr(NonMacroAttrKind::Registered); | |
1180 | if filter_fn(res) { | |
dfeec247 XL |
1181 | suggestions.extend( |
1182 | this.registered_attrs | |
1183 | .iter() | |
94222f64 | 1184 | .map(|ident| TypoSuggestion::typo_from_res(ident.name, res)), |
dfeec247 | 1185 | ); |
60c5eb7d XL |
1186 | } |
1187 | } | |
416331ca | 1188 | Scope::MacroUsePrelude => { |
dfeec247 XL |
1189 | suggestions.extend(this.macro_use_prelude.iter().filter_map( |
1190 | |(name, binding)| { | |
1191 | let res = binding.res(); | |
94222f64 | 1192 | filter_fn(res).then_some(TypoSuggestion::typo_from_res(*name, res)) |
dfeec247 XL |
1193 | }, |
1194 | )); | |
416331ca XL |
1195 | } |
1196 | Scope::BuiltinAttrs => { | |
5869c6ff | 1197 | let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(kw::Empty)); |
416331ca | 1198 | if filter_fn(res) { |
dfeec247 XL |
1199 | suggestions.extend( |
1200 | BUILTIN_ATTRIBUTES | |
1201 | .iter() | |
3c0e092e | 1202 | .map(|attr| TypoSuggestion::typo_from_res(attr.name, res)), |
dfeec247 | 1203 | ); |
48663c56 | 1204 | } |
48663c56 | 1205 | } |
416331ca XL |
1206 | Scope::ExternPrelude => { |
1207 | suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, _)| { | |
04454e1e | 1208 | let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()); |
94222f64 | 1209 | filter_fn(res).then_some(TypoSuggestion::typo_from_res(ident.name, res)) |
416331ca XL |
1210 | })); |
1211 | } | |
1212 | Scope::ToolPrelude => { | |
1213 | let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); | |
dfeec247 XL |
1214 | suggestions.extend( |
1215 | this.registered_tools | |
1216 | .iter() | |
94222f64 | 1217 | .map(|ident| TypoSuggestion::typo_from_res(ident.name, res)), |
dfeec247 | 1218 | ); |
416331ca XL |
1219 | } |
1220 | Scope::StdLibPrelude => { | |
1221 | if let Some(prelude) = this.prelude { | |
1222 | let mut tmp_suggestions = Vec::new(); | |
e1599b0c | 1223 | this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn); |
dfeec247 XL |
1224 | suggestions.extend( |
1225 | tmp_suggestions | |
1226 | .into_iter() | |
1227 | .filter(|s| use_prelude || this.is_builtin_macro(s.res)), | |
1228 | ); | |
416331ca XL |
1229 | } |
1230 | } | |
1231 | Scope::BuiltinTypes => { | |
6a06907d | 1232 | suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| { |
dfeec247 | 1233 | let res = Res::PrimTy(*prim_ty); |
94222f64 | 1234 | filter_fn(res).then_some(TypoSuggestion::typo_from_res(prim_ty.name(), res)) |
dfeec247 | 1235 | })) |
48663c56 XL |
1236 | } |
1237 | } | |
416331ca XL |
1238 | |
1239 | None::<()> | |
1240 | }); | |
1241 | ||
1242 | // Make sure error reporting is deterministic. | |
a2a8927a | 1243 | suggestions.sort_by(|a, b| a.candidate.as_str().partial_cmp(b.candidate.as_str()).unwrap()); |
416331ca XL |
1244 | |
1245 | match find_best_match_for_name( | |
fc512014 | 1246 | &suggestions.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(), |
3dfed10e | 1247 | ident.name, |
416331ca XL |
1248 | None, |
1249 | ) { | |
dfeec247 XL |
1250 | Some(found) if found != ident.name => { |
1251 | suggestions.into_iter().find(|suggestion| suggestion.candidate == found) | |
1252 | } | |
416331ca XL |
1253 | _ => None, |
1254 | } | |
1255 | } | |
1256 | ||
dfeec247 XL |
1257 | fn lookup_import_candidates_from_module<FilterFn>( |
1258 | &mut self, | |
1259 | lookup_ident: Ident, | |
1260 | namespace: Namespace, | |
f035d41b | 1261 | parent_scope: &ParentScope<'a>, |
dfeec247 XL |
1262 | start_module: Module<'a>, |
1263 | crate_name: Ident, | |
1264 | filter_fn: FilterFn, | |
1265 | ) -> Vec<ImportSuggestion> | |
1266 | where | |
1267 | FilterFn: Fn(Res) -> bool, | |
416331ca XL |
1268 | { |
1269 | let mut candidates = Vec::new(); | |
1270 | let mut seen_modules = FxHashSet::default(); | |
cdc7bbd5 | 1271 | let mut worklist = vec![(start_module, Vec::<ast::PathSegment>::new(), true)]; |
f035d41b XL |
1272 | let mut worklist_via_import = vec![]; |
1273 | ||
cdc7bbd5 XL |
1274 | while let Some((in_module, path_segments, accessible)) = match worklist.pop() { |
1275 | None => worklist_via_import.pop(), | |
1276 | Some(x) => Some(x), | |
1277 | } { | |
c295e0f8 | 1278 | let in_module_is_extern = !in_module.def_id().is_local(); |
416331ca XL |
1279 | // We have to visit module children in deterministic order to avoid |
1280 | // instabilities in reported imports (#43552). | |
e74abb32 | 1281 | in_module.for_each_child(self, |this, ident, ns, name_binding| { |
f035d41b XL |
1282 | // avoid non-importable candidates |
1283 | if !name_binding.is_importable() { | |
dfeec247 XL |
1284 | return; |
1285 | } | |
f035d41b XL |
1286 | |
1287 | let child_accessible = | |
1288 | accessible && this.is_accessible_from(name_binding.vis, parent_scope.module); | |
1289 | ||
1290 | // do not venture inside inaccessible items of other crates | |
1291 | if in_module_is_extern && !child_accessible { | |
1292 | return; | |
1293 | } | |
1294 | ||
1295 | let via_import = name_binding.is_import() && !name_binding.is_extern_crate(); | |
1296 | ||
1297 | // There is an assumption elsewhere that paths of variants are in the enum's | |
1298 | // declaration and not imported. With this assumption, the variant component is | |
1299 | // chopped and the rest of the path is assumed to be the enum's own path. For | |
1300 | // errors where a variant is used as the type instead of the enum, this causes | |
1301 | // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`. | |
1302 | if via_import && name_binding.is_possibly_imported_variant() { | |
dfeec247 XL |
1303 | return; |
1304 | } | |
416331ca | 1305 | |
3c0e092e XL |
1306 | // #90113: Do not count an inaccessible reexported item as a candidate. |
1307 | if let NameBindingKind::Import { binding, .. } = name_binding.kind { | |
1308 | if this.is_accessible_from(binding.vis, parent_scope.module) | |
1309 | && !this.is_accessible_from(name_binding.vis, parent_scope.module) | |
1310 | { | |
1311 | return; | |
1312 | } | |
1313 | } | |
1314 | ||
416331ca | 1315 | // collect results based on the filter function |
f035d41b | 1316 | // avoid suggesting anything from the same module in which we are resolving |
3c0e092e | 1317 | // avoid suggesting anything with a hygienic name |
f035d41b XL |
1318 | if ident.name == lookup_ident.name |
1319 | && ns == namespace | |
1320 | && !ptr::eq(in_module, parent_scope.module) | |
3c0e092e | 1321 | && !ident.span.normalize_to_macros_2_0().from_expansion() |
f035d41b | 1322 | { |
416331ca XL |
1323 | let res = name_binding.res(); |
1324 | if filter_fn(res) { | |
1325 | // create the path | |
1326 | let mut segms = path_segments.clone(); | |
1327 | if lookup_ident.span.rust_2018() { | |
1328 | // crate-local absolute paths start with `crate::` in edition 2018 | |
1329 | // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) | |
dfeec247 | 1330 | segms.insert(0, ast::PathSegment::from_ident(crate_name)); |
416331ca XL |
1331 | } |
1332 | ||
1333 | segms.push(ast::PathSegment::from_ident(ident)); | |
1b1a35ee | 1334 | let path = Path { span: name_binding.span, segments: segms, tokens: None }; |
f035d41b | 1335 | let did = match res { |
04454e1e | 1336 | Res::Def(DefKind::Ctor(..), did) => this.opt_parent(did), |
f035d41b XL |
1337 | _ => res.opt_def_id(), |
1338 | }; | |
1339 | ||
1340 | if child_accessible { | |
1341 | // Remove invisible match if exists | |
1342 | if let Some(idx) = candidates | |
1343 | .iter() | |
1344 | .position(|v: &ImportSuggestion| v.did == did && !v.accessible) | |
1345 | { | |
1346 | candidates.remove(idx); | |
1347 | } | |
1348 | } | |
1349 | ||
1350 | if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { | |
3c0e092e XL |
1351 | // See if we're recommending TryFrom, TryInto, or FromIterator and add |
1352 | // a note about editions | |
1353 | let note = if let Some(did) = did { | |
1354 | let requires_note = !did.is_local() | |
5099ac24 FG |
1355 | && this.cstore().item_attrs_untracked(did, this.session).any( |
1356 | |attr| { | |
3c0e092e XL |
1357 | if attr.has_name(sym::rustc_diagnostic_item) { |
1358 | [sym::TryInto, sym::TryFrom, sym::FromIterator] | |
1359 | .map(|x| Some(x)) | |
1360 | .contains(&attr.value_str()) | |
1361 | } else { | |
1362 | false | |
1363 | } | |
5099ac24 FG |
1364 | }, |
1365 | ); | |
3c0e092e XL |
1366 | |
1367 | requires_note.then(|| { | |
1368 | format!( | |
1369 | "'{}' is included in the prelude starting in Edition 2021", | |
1370 | path_names_to_string(&path) | |
1371 | ) | |
1372 | }) | |
1373 | } else { | |
1374 | None | |
1375 | }; | |
1376 | ||
f035d41b XL |
1377 | candidates.push(ImportSuggestion { |
1378 | did, | |
1379 | descr: res.descr(), | |
1380 | path, | |
1381 | accessible: child_accessible, | |
3c0e092e | 1382 | note, |
f035d41b | 1383 | }); |
416331ca XL |
1384 | } |
1385 | } | |
1386 | } | |
1387 | ||
1388 | // collect submodules to explore | |
1389 | if let Some(module) = name_binding.module() { | |
1390 | // form the path | |
1391 | let mut path_segments = path_segments.clone(); | |
1392 | path_segments.push(ast::PathSegment::from_ident(ident)); | |
1393 | ||
1394 | let is_extern_crate_that_also_appears_in_prelude = | |
dfeec247 | 1395 | name_binding.is_extern_crate() && lookup_ident.span.rust_2018(); |
416331ca | 1396 | |
f035d41b | 1397 | if !is_extern_crate_that_also_appears_in_prelude { |
f035d41b | 1398 | // add the module to the lookup |
c295e0f8 | 1399 | if seen_modules.insert(module.def_id()) { |
f035d41b | 1400 | if via_import { &mut worklist_via_import } else { &mut worklist } |
cdc7bbd5 | 1401 | .push((module, path_segments, child_accessible)); |
416331ca XL |
1402 | } |
1403 | } | |
1404 | } | |
1405 | }) | |
1406 | } | |
1407 | ||
f035d41b XL |
1408 | // If only some candidates are accessible, take just them |
1409 | if !candidates.iter().all(|v: &ImportSuggestion| !v.accessible) { | |
1410 | candidates = candidates.into_iter().filter(|x| x.accessible).collect(); | |
1411 | } | |
1412 | ||
416331ca XL |
1413 | candidates |
1414 | } | |
1415 | ||
1416 | /// When name resolution fails, this method can be used to look up candidate | |
1417 | /// entities with the expected name. It allows filtering them using the | |
1418 | /// supplied predicate (which should be used to only accept the types of | |
1419 | /// definitions expected, e.g., traits). The lookup spans across all crates. | |
1420 | /// | |
1421 | /// N.B., the method does not look into imports, but this is not a problem, | |
1422 | /// since we report the definitions (thus, the de-aliased imports). | |
923072b8 | 1423 | pub(crate) fn lookup_import_candidates<FilterFn>( |
dfeec247 XL |
1424 | &mut self, |
1425 | lookup_ident: Ident, | |
1426 | namespace: Namespace, | |
f035d41b | 1427 | parent_scope: &ParentScope<'a>, |
dfeec247 | 1428 | filter_fn: FilterFn, |
416331ca | 1429 | ) -> Vec<ImportSuggestion> |
dfeec247 XL |
1430 | where |
1431 | FilterFn: Fn(Res) -> bool, | |
416331ca XL |
1432 | { |
1433 | let mut suggestions = self.lookup_import_candidates_from_module( | |
dfeec247 XL |
1434 | lookup_ident, |
1435 | namespace, | |
f035d41b | 1436 | parent_scope, |
dfeec247 XL |
1437 | self.graph_root, |
1438 | Ident::with_dummy_span(kw::Crate), | |
1439 | &filter_fn, | |
416331ca XL |
1440 | ); |
1441 | ||
1442 | if lookup_ident.span.rust_2018() { | |
1443 | let extern_prelude_names = self.extern_prelude.clone(); | |
1444 | for (ident, _) in extern_prelude_names.into_iter() { | |
e1599b0c XL |
1445 | if ident.span.from_expansion() { |
1446 | // Idents are adjusted to the root context before being | |
1447 | // resolved in the extern prelude, so reporting this to the | |
1448 | // user is no help. This skips the injected | |
1449 | // `extern crate std` in the 2018 edition, which would | |
1450 | // otherwise cause duplicate suggestions. | |
1451 | continue; | |
1452 | } | |
3dfed10e | 1453 | if let Some(crate_id) = self.crate_loader.maybe_process_path_extern(ident.name) { |
c295e0f8 | 1454 | let crate_root = self.expect_module(crate_id.as_def_id()); |
416331ca | 1455 | suggestions.extend(self.lookup_import_candidates_from_module( |
dfeec247 XL |
1456 | lookup_ident, |
1457 | namespace, | |
f035d41b | 1458 | parent_scope, |
dfeec247 XL |
1459 | crate_root, |
1460 | ident, | |
1461 | &filter_fn, | |
1462 | )); | |
416331ca | 1463 | } |
48663c56 | 1464 | } |
48663c56 | 1465 | } |
416331ca XL |
1466 | |
1467 | suggestions | |
1468 | } | |
1469 | ||
923072b8 | 1470 | pub(crate) fn unresolved_macro_suggestions( |
416331ca | 1471 | &mut self, |
5e7ed085 | 1472 | err: &mut Diagnostic, |
416331ca XL |
1473 | macro_kind: MacroKind, |
1474 | parent_scope: &ParentScope<'a>, | |
1475 | ident: Ident, | |
1476 | ) { | |
1477 | let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind); | |
1478 | let suggestion = self.early_lookup_typo_candidate( | |
dfeec247 XL |
1479 | ScopeSet::Macro(macro_kind), |
1480 | parent_scope, | |
1481 | ident, | |
1482 | is_expected, | |
416331ca | 1483 | ); |
e74abb32 | 1484 | self.add_typo_suggestion(err, suggestion, ident.span); |
416331ca | 1485 | |
29967ef6 | 1486 | let import_suggestions = |
94222f64 | 1487 | self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); |
c295e0f8 | 1488 | show_candidates( |
923072b8 FG |
1489 | &self.session, |
1490 | &self.source_span, | |
c295e0f8 XL |
1491 | err, |
1492 | None, | |
1493 | &import_suggestions, | |
04454e1e FG |
1494 | Instead::No, |
1495 | FoundUse::Yes, | |
1496 | IsPattern::No, | |
1497 | vec![], | |
c295e0f8 | 1498 | ); |
29967ef6 | 1499 | |
3dfed10e | 1500 | if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { |
416331ca XL |
1501 | let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident); |
1502 | err.span_note(ident.span, &msg); | |
94222f64 | 1503 | return; |
416331ca | 1504 | } |
ba9703b0 | 1505 | if self.macro_names.contains(&ident.normalize_to_macros_2_0()) { |
416331ca | 1506 | err.help("have you added the `#[macro_use]` on the module/import?"); |
94222f64 XL |
1507 | return; |
1508 | } | |
064997fb FG |
1509 | if ident.name == kw::Default |
1510 | && let ModuleKind::Def(DefKind::Enum, def_id, _) = parent_scope.module.kind | |
1511 | && let Some(span) = self.opt_span(def_id) | |
1512 | { | |
1513 | let source_map = self.session.source_map(); | |
1514 | let head_span = source_map.guess_head_span(span); | |
1515 | if let Ok(head) = source_map.span_to_snippet(head_span) { | |
1516 | err.span_suggestion(head_span, "consider adding a derive", format!("#[derive(Default)]\n{head}"), Applicability::MaybeIncorrect); | |
1517 | } else { | |
1518 | err.span_help( | |
1519 | head_span, | |
1520 | "consider adding `#[derive(Default)]` to this enum", | |
1521 | ); | |
1522 | } | |
1523 | } | |
94222f64 XL |
1524 | for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { |
1525 | if let Ok(binding) = self.early_resolve_ident_in_lexical_scope( | |
1526 | ident, | |
1527 | ScopeSet::All(ns, false), | |
1528 | &parent_scope, | |
5e7ed085 | 1529 | None, |
94222f64 | 1530 | false, |
04454e1e | 1531 | None, |
94222f64 XL |
1532 | ) { |
1533 | let desc = match binding.res() { | |
1534 | Res::Def(DefKind::Macro(MacroKind::Bang), _) => { | |
1535 | "a function-like macro".to_string() | |
1536 | } | |
1537 | Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => { | |
1538 | format!("an attribute: `#[{}]`", ident) | |
1539 | } | |
1540 | Res::Def(DefKind::Macro(MacroKind::Derive), _) => { | |
1541 | format!("a derive macro: `#[derive({})]`", ident) | |
1542 | } | |
1543 | Res::ToolMod => { | |
1544 | // Don't confuse the user with tool modules. | |
1545 | continue; | |
1546 | } | |
1547 | Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { | |
1548 | "only a trait, without a derive macro".to_string() | |
1549 | } | |
1550 | res => format!( | |
1551 | "{} {}, not {} {}", | |
1552 | res.article(), | |
1553 | res.descr(), | |
1554 | macro_kind.article(), | |
1555 | macro_kind.descr_expected(), | |
1556 | ), | |
1557 | }; | |
1558 | if let crate::NameBindingKind::Import { import, .. } = binding.kind { | |
1559 | if !import.span.is_dummy() { | |
1560 | err.span_note( | |
1561 | import.span, | |
1562 | &format!("`{}` is imported here, but it is {}", ident, desc), | |
1563 | ); | |
1564 | // Silence the 'unused import' warning we might get, | |
1565 | // since this diagnostic already covers that import. | |
1566 | self.record_use(ident, binding, false); | |
1567 | return; | |
1568 | } | |
1569 | } | |
1570 | err.note(&format!("`{}` is in scope, but it is {}", ident, desc)); | |
1571 | return; | |
1572 | } | |
416331ca | 1573 | } |
5bcae85e | 1574 | } |
e74abb32 | 1575 | |
923072b8 | 1576 | pub(crate) fn add_typo_suggestion( |
e74abb32 | 1577 | &self, |
5e7ed085 | 1578 | err: &mut Diagnostic, |
e74abb32 XL |
1579 | suggestion: Option<TypoSuggestion>, |
1580 | span: Span, | |
1581 | ) -> bool { | |
f035d41b XL |
1582 | let suggestion = match suggestion { |
1583 | None => return false, | |
74b04a01 | 1584 | // We shouldn't suggest underscore. |
f035d41b XL |
1585 | Some(suggestion) if suggestion.candidate == kw::Underscore => return false, |
1586 | Some(suggestion) => suggestion, | |
1587 | }; | |
f035d41b XL |
1588 | let def_span = suggestion.res.opt_def_id().and_then(|def_id| match def_id.krate { |
1589 | LOCAL_CRATE => self.opt_span(def_id), | |
064997fb | 1590 | _ => Some(self.cstore().get_span_untracked(def_id, self.session)), |
f035d41b | 1591 | }); |
3dfed10e XL |
1592 | if let Some(def_span) = def_span { |
1593 | if span.overlaps(def_span) { | |
5869c6ff | 1594 | // Don't suggest typo suggestion for itself like in the following: |
3dfed10e XL |
1595 | // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` |
1596 | // --> $DIR/issue-64792-bad-unicode-ctor.rs:3:14 | |
1597 | // | | |
1598 | // LL | struct X {} | |
1599 | // | ----------- `X` defined here | |
1600 | // LL | | |
1601 | // LL | const Y: X = X("ö"); | |
1602 | // | -------------^^^^^^- similarly named constant `Y` defined here | |
1603 | // | | |
1604 | // help: use struct literal syntax instead | |
1605 | // | | |
1606 | // LL | const Y: X = X {}; | |
1607 | // | ^^^^ | |
1608 | // help: a constant with a similar name exists | |
1609 | // | | |
1610 | // LL | const Y: X = Y("ö"); | |
1611 | // | ^ | |
1612 | return false; | |
1613 | } | |
94222f64 XL |
1614 | let prefix = match suggestion.target { |
1615 | SuggestionTarget::SimilarlyNamed => "similarly named ", | |
1616 | SuggestionTarget::SingleItem => "", | |
1617 | }; | |
1618 | ||
f035d41b | 1619 | err.span_label( |
3dfed10e | 1620 | self.session.source_map().guess_head_span(def_span), |
f035d41b | 1621 | &format!( |
94222f64 XL |
1622 | "{}{} `{}` defined here", |
1623 | prefix, | |
f035d41b | 1624 | suggestion.res.descr(), |
064997fb | 1625 | suggestion.candidate, |
dfeec247 | 1626 | ), |
f035d41b | 1627 | ); |
e74abb32 | 1628 | } |
94222f64 XL |
1629 | let msg = match suggestion.target { |
1630 | SuggestionTarget::SimilarlyNamed => format!( | |
1631 | "{} {} with a similar name exists", | |
1632 | suggestion.res.article(), | |
1633 | suggestion.res.descr() | |
1634 | ), | |
1635 | SuggestionTarget::SingleItem => { | |
1636 | format!("maybe you meant this {}", suggestion.res.descr()) | |
1637 | } | |
1638 | }; | |
923072b8 | 1639 | err.span_suggestion(span, &msg, suggestion.candidate, Applicability::MaybeIncorrect); |
f035d41b | 1640 | true |
e74abb32 | 1641 | } |
dfeec247 XL |
1642 | |
1643 | fn binding_description(&self, b: &NameBinding<'_>, ident: Ident, from_prelude: bool) -> String { | |
1644 | let res = b.res(); | |
064997fb | 1645 | if b.span.is_dummy() || !self.session.source_map().is_span_accessible(b.span) { |
29967ef6 XL |
1646 | // These already contain the "built-in" prefix or look bad with it. |
1647 | let add_built_in = | |
1648 | !matches!(b.res(), Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod); | |
dfeec247 XL |
1649 | let (built_in, from) = if from_prelude { |
1650 | ("", " from prelude") | |
1651 | } else if b.is_extern_crate() | |
1652 | && !b.is_import() | |
a2a8927a | 1653 | && self.session.opts.externs.get(ident.as_str()).is_some() |
dfeec247 XL |
1654 | { |
1655 | ("", " passed with `--extern`") | |
1656 | } else if add_built_in { | |
1657 | (" built-in", "") | |
1658 | } else { | |
1659 | ("", "") | |
1660 | }; | |
1661 | ||
29967ef6 XL |
1662 | let a = if built_in.is_empty() { res.article() } else { "a" }; |
1663 | format!("{a}{built_in} {thing}{from}", thing = res.descr()) | |
dfeec247 XL |
1664 | } else { |
1665 | let introduced = if b.is_import() { "imported" } else { "defined" }; | |
29967ef6 | 1666 | format!("the {thing} {introduced} here", thing = res.descr()) |
dfeec247 XL |
1667 | } |
1668 | } | |
1669 | ||
04454e1e | 1670 | fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError<'_>) { |
dfeec247 XL |
1671 | let AmbiguityError { kind, ident, b1, b2, misc1, misc2 } = *ambiguity_error; |
1672 | let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { | |
1673 | // We have to print the span-less alternative first, otherwise formatting looks bad. | |
1674 | (b2, b1, misc2, misc1, true) | |
1675 | } else { | |
1676 | (b1, b2, misc1, misc2, false) | |
1677 | }; | |
1678 | ||
3c0e092e | 1679 | let mut err = struct_span_err!(self.session, ident.span, E0659, "`{ident}` is ambiguous"); |
dfeec247 | 1680 | err.span_label(ident.span, "ambiguous name"); |
3c0e092e | 1681 | err.note(&format!("ambiguous because of {}", kind.descr())); |
dfeec247 XL |
1682 | |
1683 | let mut could_refer_to = |b: &NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| { | |
1684 | let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude); | |
29967ef6 | 1685 | let note_msg = format!("`{ident}` could{also} refer to {what}"); |
dfeec247 XL |
1686 | |
1687 | let thing = b.res().descr(); | |
1688 | let mut help_msgs = Vec::new(); | |
1689 | if b.is_glob_import() | |
1690 | && (kind == AmbiguityKind::GlobVsGlob | |
1691 | || kind == AmbiguityKind::GlobVsExpanded | |
1692 | || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty()) | |
1693 | { | |
1694 | help_msgs.push(format!( | |
29967ef6 | 1695 | "consider adding an explicit import of `{ident}` to disambiguate" |
dfeec247 XL |
1696 | )) |
1697 | } | |
1698 | if b.is_extern_crate() && ident.span.rust_2018() { | |
29967ef6 | 1699 | help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) |
dfeec247 XL |
1700 | } |
1701 | if misc == AmbiguityErrorMisc::SuggestCrate { | |
29967ef6 XL |
1702 | help_msgs |
1703 | .push(format!("use `crate::{ident}` to refer to this {thing} unambiguously")) | |
dfeec247 | 1704 | } else if misc == AmbiguityErrorMisc::SuggestSelf { |
29967ef6 XL |
1705 | help_msgs |
1706 | .push(format!("use `self::{ident}` to refer to this {thing} unambiguously")) | |
dfeec247 XL |
1707 | } |
1708 | ||
1709 | err.span_note(b.span, ¬e_msg); | |
1710 | for (i, help_msg) in help_msgs.iter().enumerate() { | |
1711 | let or = if i == 0 { "" } else { "or " }; | |
1712 | err.help(&format!("{}{}", or, help_msg)); | |
1713 | } | |
1714 | }; | |
1715 | ||
1716 | could_refer_to(b1, misc1, ""); | |
1717 | could_refer_to(b2, misc2, " also"); | |
1718 | err.emit(); | |
1719 | } | |
1720 | ||
ba9703b0 XL |
1721 | /// If the binding refers to a tuple struct constructor with fields, |
1722 | /// returns the span of its fields. | |
1723 | fn ctor_fields_span(&self, binding: &NameBinding<'_>) -> Option<Span> { | |
1724 | if let NameBindingKind::Res( | |
dfeec247 XL |
1725 | Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id), |
1726 | _, | |
1727 | ) = binding.kind | |
1728 | { | |
04454e1e | 1729 | let def_id = self.parent(ctor_def_id); |
3dfed10e | 1730 | let fields = self.field_names.get(&def_id)?; |
5869c6ff | 1731 | return fields.iter().map(|name| name.span).reduce(Span::to); // None for `struct Foo()` |
ba9703b0 XL |
1732 | } |
1733 | None | |
1734 | } | |
1735 | ||
04454e1e | 1736 | fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) { |
ba9703b0 XL |
1737 | let PrivacyError { ident, binding, .. } = *privacy_error; |
1738 | ||
1739 | let res = binding.res(); | |
1740 | let ctor_fields_span = self.ctor_fields_span(binding); | |
1741 | let plain_descr = res.descr().to_string(); | |
1742 | let nonimport_descr = | |
1743 | if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; | |
1744 | let import_descr = nonimport_descr.clone() + " import"; | |
1745 | let get_descr = | |
1746 | |b: &NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; | |
1747 | ||
1748 | // Print the primary message. | |
1749 | let descr = get_descr(binding); | |
1750 | let mut err = | |
1751 | struct_span_err!(self.session, ident.span, E0603, "{} `{}` is private", descr, ident); | |
1752 | err.span_label(ident.span, &format!("private {}", descr)); | |
1753 | if let Some(span) = ctor_fields_span { | |
1754 | err.span_label(span, "a constructor is private if any of the fields is private"); | |
1755 | } | |
1756 | ||
1757 | // Print the whole import chain to make it easier to see what happens. | |
1758 | let first_binding = binding; | |
1759 | let mut next_binding = Some(binding); | |
1760 | let mut next_ident = ident; | |
1761 | while let Some(binding) = next_binding { | |
1762 | let name = next_ident; | |
1763 | next_binding = match binding.kind { | |
1764 | _ if res == Res::Err => None, | |
1765 | NameBindingKind::Import { binding, import, .. } => match import.kind { | |
1766 | _ if binding.span.is_dummy() => None, | |
1767 | ImportKind::Single { source, .. } => { | |
1768 | next_ident = source; | |
1769 | Some(binding) | |
1770 | } | |
1771 | ImportKind::Glob { .. } | ImportKind::MacroUse => Some(binding), | |
1772 | ImportKind::ExternCrate { .. } => None, | |
1773 | }, | |
1774 | _ => None, | |
1775 | }; | |
1776 | ||
1777 | let first = ptr::eq(binding, first_binding); | |
ba9703b0 XL |
1778 | let msg = format!( |
1779 | "{and_refers_to}the {item} `{name}`{which} is defined here{dots}", | |
1780 | and_refers_to = if first { "" } else { "...and refers to " }, | |
29967ef6 | 1781 | item = get_descr(binding), |
ba9703b0 XL |
1782 | which = if first { "" } else { " which" }, |
1783 | dots = if next_binding.is_some() { "..." } else { "" }, | |
1784 | ); | |
1785 | let def_span = self.session.source_map().guess_head_span(binding.span); | |
1786 | let mut note_span = MultiSpan::from_span(def_span); | |
3c0e092e | 1787 | if !first && binding.vis.is_public() { |
04454e1e | 1788 | note_span.push_span_label(def_span, "consider importing it directly"); |
ba9703b0 XL |
1789 | } |
1790 | err.span_note(note_span, &msg); | |
1791 | } | |
dfeec247 XL |
1792 | |
1793 | err.emit(); | |
1794 | } | |
c295e0f8 | 1795 | |
923072b8 | 1796 | pub(crate) fn find_similarly_named_module_or_crate( |
c295e0f8 XL |
1797 | &mut self, |
1798 | ident: Symbol, | |
1799 | current_module: &Module<'a>, | |
1800 | ) -> Option<Symbol> { | |
1801 | let mut candidates = self | |
1802 | .extern_prelude | |
1803 | .iter() | |
1804 | .map(|(ident, _)| ident.name) | |
1805 | .chain( | |
1806 | self.module_map | |
1807 | .iter() | |
1808 | .filter(|(_, module)| { | |
1809 | current_module.is_ancestor_of(module) && !ptr::eq(current_module, *module) | |
1810 | }) | |
5099ac24 | 1811 | .flat_map(|(_, module)| module.kind.name()), |
c295e0f8 XL |
1812 | ) |
1813 | .filter(|c| !c.to_string().is_empty()) | |
1814 | .collect::<Vec<_>>(); | |
1815 | candidates.sort(); | |
1816 | candidates.dedup(); | |
1817 | match find_best_match_for_name(&candidates, ident, None) { | |
1818 | Some(sugg) if sugg == ident => None, | |
1819 | sugg => sugg, | |
1820 | } | |
1821 | } | |
04454e1e | 1822 | |
923072b8 | 1823 | pub(crate) fn report_path_resolution_error( |
04454e1e FG |
1824 | &mut self, |
1825 | path: &[Segment], | |
1826 | opt_ns: Option<Namespace>, // `None` indicates a module path in import | |
1827 | parent_scope: &ParentScope<'a>, | |
1828 | ribs: Option<&PerNS<Vec<Rib<'a>>>>, | |
1829 | ignore_binding: Option<&'a NameBinding<'a>>, | |
1830 | module: Option<ModuleOrUniformRoot<'a>>, | |
1831 | i: usize, | |
1832 | ident: Ident, | |
1833 | ) -> (String, Option<Suggestion>) { | |
1834 | let is_last = i == path.len() - 1; | |
1835 | let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; | |
1836 | let module_res = match module { | |
1837 | Some(ModuleOrUniformRoot::Module(module)) => module.res(), | |
1838 | _ => None, | |
1839 | }; | |
1840 | if module_res == self.graph_root.res() { | |
1841 | let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); | |
1842 | let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); | |
1843 | candidates | |
1844 | .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); | |
1845 | if let Some(candidate) = candidates.get(0) { | |
1846 | ( | |
1847 | String::from("unresolved import"), | |
1848 | Some(( | |
1849 | vec![(ident.span, pprust::path_to_string(&candidate.path))], | |
1850 | String::from("a similar path exists"), | |
1851 | Applicability::MaybeIncorrect, | |
1852 | )), | |
1853 | ) | |
1854 | } else if self.session.edition() == Edition::Edition2015 { | |
923072b8 FG |
1855 | ( |
1856 | format!("maybe a missing crate `{ident}`?"), | |
1857 | Some(( | |
1858 | vec![], | |
1859 | format!( | |
1860 | "consider adding `extern crate {ident}` to use the `{ident}` crate" | |
1861 | ), | |
1862 | Applicability::MaybeIncorrect, | |
1863 | )), | |
1864 | ) | |
04454e1e | 1865 | } else { |
923072b8 | 1866 | (format!("could not find `{ident}` in the crate root"), None) |
04454e1e FG |
1867 | } |
1868 | } else if i > 0 { | |
1869 | let parent = path[i - 1].ident.name; | |
1870 | let parent = match parent { | |
1871 | // ::foo is mounted at the crate root for 2015, and is the extern | |
1872 | // prelude for 2018+ | |
1873 | kw::PathRoot if self.session.edition() > Edition::Edition2015 => { | |
1874 | "the list of imported crates".to_owned() | |
1875 | } | |
1876 | kw::PathRoot | kw::Crate => "the crate root".to_owned(), | |
923072b8 | 1877 | _ => format!("`{parent}`"), |
04454e1e FG |
1878 | }; |
1879 | ||
1880 | let mut msg = format!("could not find `{}` in {}", ident, parent); | |
1881 | if ns == TypeNS || ns == ValueNS { | |
1882 | let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; | |
1883 | let binding = if let Some(module) = module { | |
1884 | self.resolve_ident_in_module( | |
1885 | module, | |
1886 | ident, | |
1887 | ns_to_try, | |
1888 | parent_scope, | |
1889 | None, | |
1890 | ignore_binding, | |
1891 | ).ok() | |
1892 | } else if let Some(ribs) = ribs | |
1893 | && let Some(TypeNS | ValueNS) = opt_ns | |
1894 | { | |
1895 | match self.resolve_ident_in_lexical_scope( | |
1896 | ident, | |
1897 | ns_to_try, | |
1898 | parent_scope, | |
1899 | None, | |
1900 | &ribs[ns_to_try], | |
1901 | ignore_binding, | |
1902 | ) { | |
1903 | // we found a locally-imported or available item/module | |
1904 | Some(LexicalScopeBinding::Item(binding)) => Some(binding), | |
1905 | _ => None, | |
1906 | } | |
1907 | } else { | |
1908 | let scopes = ScopeSet::All(ns_to_try, opt_ns.is_none()); | |
1909 | self.early_resolve_ident_in_lexical_scope( | |
1910 | ident, | |
1911 | scopes, | |
1912 | parent_scope, | |
1913 | None, | |
1914 | false, | |
1915 | ignore_binding, | |
1916 | ).ok() | |
1917 | }; | |
1918 | if let Some(binding) = binding { | |
1919 | let mut found = |what| { | |
1920 | msg = format!( | |
1921 | "expected {}, found {} `{}` in {}", | |
1922 | ns.descr(), | |
1923 | what, | |
1924 | ident, | |
1925 | parent | |
1926 | ) | |
1927 | }; | |
1928 | if binding.module().is_some() { | |
1929 | found("module") | |
1930 | } else { | |
1931 | match binding.res() { | |
1932 | Res::Def(kind, id) => found(kind.descr(id)), | |
1933 | _ => found(ns_to_try.descr()), | |
1934 | } | |
1935 | } | |
1936 | }; | |
1937 | } | |
1938 | (msg, None) | |
923072b8 FG |
1939 | } else if ident.name == kw::SelfUpper { |
1940 | ("`Self` is only available in impls, traits, and type definitions".to_string(), None) | |
04454e1e FG |
1941 | } else if ident.name.as_str().chars().next().map_or(false, |c| c.is_ascii_uppercase()) { |
1942 | // Check whether the name refers to an item in the value namespace. | |
1943 | let binding = if let Some(ribs) = ribs { | |
1944 | self.resolve_ident_in_lexical_scope( | |
1945 | ident, | |
1946 | ValueNS, | |
1947 | parent_scope, | |
1948 | None, | |
1949 | &ribs[ValueNS], | |
1950 | ignore_binding, | |
1951 | ) | |
1952 | } else { | |
1953 | None | |
1954 | }; | |
1955 | let match_span = match binding { | |
1956 | // Name matches a local variable. For example: | |
1957 | // ``` | |
1958 | // fn f() { | |
1959 | // let Foo: &str = ""; | |
1960 | // println!("{}", Foo::Bar); // Name refers to local | |
1961 | // // variable `Foo`. | |
1962 | // } | |
1963 | // ``` | |
1964 | Some(LexicalScopeBinding::Res(Res::Local(id))) => { | |
1965 | Some(*self.pat_span_map.get(&id).unwrap()) | |
1966 | } | |
1967 | // Name matches item from a local name binding | |
1968 | // created by `use` declaration. For example: | |
1969 | // ``` | |
1970 | // pub Foo: &str = ""; | |
1971 | // | |
1972 | // mod submod { | |
1973 | // use super::Foo; | |
1974 | // println!("{}", Foo::Bar); // Name refers to local | |
1975 | // // binding `Foo`. | |
1976 | // } | |
1977 | // ``` | |
1978 | Some(LexicalScopeBinding::Item(name_binding)) => Some(name_binding.span), | |
1979 | _ => None, | |
1980 | }; | |
1981 | let suggestion = if let Some(span) = match_span { | |
1982 | Some(( | |
1983 | vec![(span, String::from(""))], | |
1984 | format!("`{}` is defined here, but is not a type", ident), | |
1985 | Applicability::MaybeIncorrect, | |
1986 | )) | |
1987 | } else { | |
1988 | None | |
1989 | }; | |
1990 | ||
1991 | (format!("use of undeclared type `{}`", ident), suggestion) | |
1992 | } else { | |
1993 | let suggestion = if ident.name == sym::alloc { | |
1994 | Some(( | |
1995 | vec![], | |
1996 | String::from("add `extern crate alloc` to use the `alloc` crate"), | |
1997 | Applicability::MaybeIncorrect, | |
1998 | )) | |
1999 | } else { | |
2000 | self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map( | |
2001 | |sugg| { | |
2002 | ( | |
2003 | vec![(ident.span, sugg.to_string())], | |
2004 | String::from("there is a crate or module with a similar name"), | |
2005 | Applicability::MaybeIncorrect, | |
2006 | ) | |
2007 | }, | |
2008 | ) | |
2009 | }; | |
2010 | (format!("use of undeclared crate or module `{}`", ident), suggestion) | |
2011 | } | |
2012 | } | |
5bcae85e SL |
2013 | } |
2014 | ||
dc9dc135 | 2015 | impl<'a, 'b> ImportResolver<'a, 'b> { |
48663c56 XL |
2016 | /// Adds suggestions for a path that cannot be resolved. |
2017 | pub(crate) fn make_path_suggestion( | |
2018 | &mut self, | |
2019 | span: Span, | |
2020 | mut path: Vec<Segment>, | |
2021 | parent_scope: &ParentScope<'b>, | |
064997fb | 2022 | ) -> Option<(Vec<Segment>, Option<String>)> { |
48663c56 XL |
2023 | debug!("make_path_suggestion: span={:?} path={:?}", span, path); |
2024 | ||
2025 | match (path.get(0), path.get(1)) { | |
2026 | // `{{root}}::ident::...` on both editions. | |
2027 | // On 2015 `{{root}}` is usually added implicitly. | |
dfeec247 XL |
2028 | (Some(fst), Some(snd)) |
2029 | if fst.ident.name == kw::PathRoot && !snd.ident.is_path_segment_keyword() => {} | |
48663c56 | 2030 | // `ident::...` on 2018. |
dfeec247 XL |
2031 | (Some(fst), _) |
2032 | if fst.ident.span.rust_2018() && !fst.ident.is_path_segment_keyword() => | |
2033 | { | |
48663c56 | 2034 | // Insert a placeholder that's later replaced by `self`/`super`/etc. |
3c0e092e | 2035 | path.insert(0, Segment::from_ident(Ident::empty())); |
48663c56 XL |
2036 | } |
2037 | _ => return None, | |
2038 | } | |
3157f602 | 2039 | |
5e7ed085 FG |
2040 | self.make_missing_self_suggestion(path.clone(), parent_scope) |
2041 | .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope)) | |
2042 | .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope)) | |
2043 | .or_else(|| self.make_external_crate_suggestion(path, parent_scope)) | |
92a42be0 | 2044 | } |
9cc50fc6 | 2045 | |
48663c56 XL |
2046 | /// Suggest a missing `self::` if that resolves to an correct module. |
2047 | /// | |
ba9703b0 | 2048 | /// ```text |
48663c56 XL |
2049 | /// | |
2050 | /// LL | use foo::Bar; | |
2051 | /// | ^^^ did you mean `self::foo`? | |
2052 | /// ``` | |
2053 | fn make_missing_self_suggestion( | |
2054 | &mut self, | |
48663c56 XL |
2055 | mut path: Vec<Segment>, |
2056 | parent_scope: &ParentScope<'b>, | |
064997fb | 2057 | ) -> Option<(Vec<Segment>, Option<String>)> { |
48663c56 | 2058 | // Replace first ident with `self` and check if that is valid. |
dc9dc135 | 2059 | path[0].ident.name = kw::SelfLower; |
04454e1e | 2060 | let result = self.r.maybe_resolve_path(&path, None, parent_scope); |
48663c56 | 2061 | debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); |
064997fb | 2062 | if let PathResult::Module(..) = result { Some((path, None)) } else { None } |
9cc50fc6 | 2063 | } |
92a42be0 | 2064 | |
48663c56 XL |
2065 | /// Suggests a missing `crate::` if that resolves to an correct module. |
2066 | /// | |
f9f354fc | 2067 | /// ```text |
48663c56 XL |
2068 | /// | |
2069 | /// LL | use foo::Bar; | |
2070 | /// | ^^^ did you mean `crate::foo`? | |
2071 | /// ``` | |
2072 | fn make_missing_crate_suggestion( | |
2073 | &mut self, | |
48663c56 XL |
2074 | mut path: Vec<Segment>, |
2075 | parent_scope: &ParentScope<'b>, | |
064997fb | 2076 | ) -> Option<(Vec<Segment>, Option<String>)> { |
48663c56 | 2077 | // Replace first ident with `crate` and check if that is valid. |
dc9dc135 | 2078 | path[0].ident.name = kw::Crate; |
04454e1e | 2079 | let result = self.r.maybe_resolve_path(&path, None, parent_scope); |
48663c56 XL |
2080 | debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); |
2081 | if let PathResult::Module(..) = result { | |
2082 | Some(( | |
2083 | path, | |
064997fb | 2084 | Some( |
48663c56 XL |
2085 | "`use` statements changed in Rust 2018; read more at \ |
2086 | <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\ | |
dfeec247 XL |
2087 | clarity.html>" |
2088 | .to_string(), | |
064997fb | 2089 | ), |
48663c56 XL |
2090 | )) |
2091 | } else { | |
2092 | None | |
2093 | } | |
2094 | } | |
92a42be0 | 2095 | |
48663c56 XL |
2096 | /// Suggests a missing `super::` if that resolves to an correct module. |
2097 | /// | |
ba9703b0 | 2098 | /// ```text |
48663c56 XL |
2099 | /// | |
2100 | /// LL | use foo::Bar; | |
2101 | /// | ^^^ did you mean `super::foo`? | |
2102 | /// ``` | |
2103 | fn make_missing_super_suggestion( | |
2104 | &mut self, | |
48663c56 XL |
2105 | mut path: Vec<Segment>, |
2106 | parent_scope: &ParentScope<'b>, | |
064997fb | 2107 | ) -> Option<(Vec<Segment>, Option<String>)> { |
48663c56 | 2108 | // Replace first ident with `crate` and check if that is valid. |
dc9dc135 | 2109 | path[0].ident.name = kw::Super; |
04454e1e | 2110 | let result = self.r.maybe_resolve_path(&path, None, parent_scope); |
48663c56 | 2111 | debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); |
064997fb | 2112 | if let PathResult::Module(..) = result { Some((path, None)) } else { None } |
48663c56 | 2113 | } |
92a42be0 | 2114 | |
48663c56 XL |
2115 | /// Suggests a missing external crate name if that resolves to an correct module. |
2116 | /// | |
ba9703b0 | 2117 | /// ```text |
48663c56 XL |
2118 | /// | |
2119 | /// LL | use foobar::Baz; | |
2120 | /// | ^^^^^^ did you mean `baz::foobar`? | |
2121 | /// ``` | |
2122 | /// | |
2123 | /// Used when importing a submodule of an external crate but missing that crate's | |
2124 | /// name as the first part of path. | |
2125 | fn make_external_crate_suggestion( | |
2126 | &mut self, | |
48663c56 XL |
2127 | mut path: Vec<Segment>, |
2128 | parent_scope: &ParentScope<'b>, | |
064997fb | 2129 | ) -> Option<(Vec<Segment>, Option<String>)> { |
48663c56 XL |
2130 | if path[1].ident.span.rust_2015() { |
2131 | return None; | |
2132 | } | |
92a42be0 | 2133 | |
a2a8927a | 2134 | // Sort extern crate names in *reverse* order to get |
74b04a01 | 2135 | // 1) some consistent ordering for emitted diagnostics, and |
48663c56 XL |
2136 | // 2) `std` suggestions before `core` suggestions. |
2137 | let mut extern_crate_names = | |
416331ca | 2138 | self.r.extern_prelude.iter().map(|(ident, _)| ident.name).collect::<Vec<_>>(); |
a2a8927a | 2139 | extern_crate_names.sort_by(|a, b| b.as_str().partial_cmp(a.as_str()).unwrap()); |
48663c56 XL |
2140 | |
2141 | for name in extern_crate_names.into_iter() { | |
2142 | // Replace first ident with a crate name and check if that is valid. | |
2143 | path[0].ident.name = name; | |
04454e1e | 2144 | let result = self.r.maybe_resolve_path(&path, None, parent_scope); |
dfeec247 XL |
2145 | debug!( |
2146 | "make_external_crate_suggestion: name={:?} path={:?} result={:?}", | |
2147 | name, path, result | |
2148 | ); | |
48663c56 | 2149 | if let PathResult::Module(..) = result { |
064997fb | 2150 | return Some((path, None)); |
48663c56 XL |
2151 | } |
2152 | } | |
92a42be0 | 2153 | |
48663c56 | 2154 | None |
92a42be0 | 2155 | } |
92a42be0 | 2156 | |
48663c56 XL |
2157 | /// Suggests importing a macro from the root of the crate rather than a module within |
2158 | /// the crate. | |
2159 | /// | |
f9f354fc | 2160 | /// ```text |
48663c56 XL |
2161 | /// help: a macro with this name exists at the root of the crate |
2162 | /// | | |
2163 | /// LL | use issue_59764::makro; | |
2164 | /// | ^^^^^^^^^^^^^^^^^^ | |
2165 | /// | | |
2166 | /// = note: this could be because a macro annotated with `#[macro_export]` will be exported | |
2167 | /// at the root of the crate instead of the module where it is defined | |
2168 | /// ``` | |
2169 | pub(crate) fn check_for_module_export_macro( | |
e1599b0c | 2170 | &mut self, |
74b04a01 | 2171 | import: &'b Import<'b>, |
48663c56 XL |
2172 | module: ModuleOrUniformRoot<'b>, |
2173 | ident: Ident, | |
064997fb | 2174 | ) -> Option<(Option<Suggestion>, Option<String>)> { |
3c0e092e | 2175 | let ModuleOrUniformRoot::Module(mut crate_module) = module else { |
48663c56 XL |
2176 | return None; |
2177 | }; | |
2178 | ||
2179 | while let Some(parent) = crate_module.parent { | |
2180 | crate_module = parent; | |
2181 | } | |
9cc50fc6 | 2182 | |
48663c56 XL |
2183 | if ModuleOrUniformRoot::same_def(ModuleOrUniformRoot::Module(crate_module), module) { |
2184 | // Don't make a suggestion if the import was already from the root of the | |
2185 | // crate. | |
2186 | return None; | |
2187 | } | |
92a42be0 | 2188 | |
e1599b0c | 2189 | let resolutions = self.r.resolutions(crate_module).borrow(); |
e74abb32 | 2190 | let resolution = resolutions.get(&self.r.new_key(ident, MacroNS))?; |
48663c56 XL |
2191 | let binding = resolution.borrow().binding()?; |
2192 | if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() { | |
2193 | let module_name = crate_module.kind.name().unwrap(); | |
74b04a01 XL |
2194 | let import_snippet = match import.kind { |
2195 | ImportKind::Single { source, target, .. } if source != target => { | |
dfeec247 XL |
2196 | format!("{} as {}", source, target) |
2197 | } | |
48663c56 XL |
2198 | _ => format!("{}", ident), |
2199 | }; | |
2200 | ||
2201 | let mut corrections: Vec<(Span, String)> = Vec::new(); | |
74b04a01 | 2202 | if !import.is_nested() { |
48663c56 XL |
2203 | // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove |
2204 | // intermediate segments. | |
74b04a01 | 2205 | corrections.push((import.span, format!("{}::{}", module_name, import_snippet))); |
48663c56 XL |
2206 | } else { |
2207 | // Find the binding span (and any trailing commas and spaces). | |
2208 | // ie. `use a::b::{c, d, e};` | |
2209 | // ^^^ | |
2210 | let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( | |
dfeec247 | 2211 | self.r.session, |
74b04a01 XL |
2212 | import.span, |
2213 | import.use_span, | |
dfeec247 XL |
2214 | ); |
2215 | debug!( | |
2216 | "check_for_module_export_macro: found_closing_brace={:?} binding_span={:?}", | |
2217 | found_closing_brace, binding_span | |
48663c56 | 2218 | ); |
48663c56 XL |
2219 | |
2220 | let mut removal_span = binding_span; | |
2221 | if found_closing_brace { | |
2222 | // If the binding span ended with a closing brace, as in the below example: | |
2223 | // ie. `use a::b::{c, d};` | |
2224 | // ^ | |
2225 | // Then expand the span of characters to remove to include the previous | |
2226 | // binding's trailing comma. | |
2227 | // ie. `use a::b::{c, d};` | |
2228 | // ^^^ | |
dfeec247 XL |
2229 | if let Some(previous_span) = |
2230 | extend_span_to_previous_binding(self.r.session, binding_span) | |
2231 | { | |
48663c56 XL |
2232 | debug!("check_for_module_export_macro: previous_span={:?}", previous_span); |
2233 | removal_span = removal_span.with_lo(previous_span.lo()); | |
2234 | } | |
2235 | } | |
2236 | debug!("check_for_module_export_macro: removal_span={:?}", removal_span); | |
2237 | ||
2238 | // Remove the `removal_span`. | |
2239 | corrections.push((removal_span, "".to_string())); | |
2240 | ||
5e7ed085 | 2241 | // Find the span after the crate name and if it has nested imports immediately |
48663c56 XL |
2242 | // after the crate name already. |
2243 | // ie. `use a::b::{c, d};` | |
2244 | // ^^^^^^^^^ | |
2245 | // or `use a::{b, c, d}};` | |
2246 | // ^^^^^^^^^^^ | |
2247 | let (has_nested, after_crate_name) = find_span_immediately_after_crate_name( | |
dfeec247 XL |
2248 | self.r.session, |
2249 | module_name, | |
74b04a01 | 2250 | import.use_span, |
dfeec247 XL |
2251 | ); |
2252 | debug!( | |
2253 | "check_for_module_export_macro: has_nested={:?} after_crate_name={:?}", | |
2254 | has_nested, after_crate_name | |
48663c56 | 2255 | ); |
48663c56 | 2256 | |
416331ca | 2257 | let source_map = self.r.session.source_map(); |
48663c56 XL |
2258 | |
2259 | // Add the import to the start, with a `{` if required. | |
2260 | let start_point = source_map.start_point(after_crate_name); | |
2261 | if let Ok(start_snippet) = source_map.span_to_snippet(start_point) { | |
2262 | corrections.push(( | |
2263 | start_point, | |
2264 | if has_nested { | |
2265 | // In this case, `start_snippet` must equal '{'. | |
74b04a01 | 2266 | format!("{}{}, ", start_snippet, import_snippet) |
48663c56 XL |
2267 | } else { |
2268 | // In this case, add a `{`, then the moved import, then whatever | |
2269 | // was there before. | |
74b04a01 | 2270 | format!("{{{}, {}", import_snippet, start_snippet) |
dfeec247 | 2271 | }, |
48663c56 XL |
2272 | )); |
2273 | } | |
2274 | ||
2275 | // Add a `};` to the end if nested, matching the `{` added at the start. | |
2276 | if !has_nested { | |
dfeec247 | 2277 | corrections.push((source_map.end_point(after_crate_name), "};".to_string())); |
48663c56 XL |
2278 | } |
2279 | } | |
2280 | ||
2281 | let suggestion = Some(( | |
2282 | corrections, | |
2283 | String::from("a macro with this name exists at the root of the crate"), | |
2284 | Applicability::MaybeIncorrect, | |
2285 | )); | |
064997fb FG |
2286 | Some((suggestion, Some("this could be because a macro annotated with `#[macro_export]` will be exported \ |
2287 | at the root of the crate instead of the module where it is defined" | |
2288 | .to_string()))) | |
48663c56 XL |
2289 | } else { |
2290 | None | |
2291 | } | |
92a42be0 | 2292 | } |
92a42be0 | 2293 | } |
92a42be0 | 2294 | |
48663c56 XL |
2295 | /// Given a `binding_span` of a binding within a use statement: |
2296 | /// | |
04454e1e | 2297 | /// ```ignore (illustrative) |
48663c56 | 2298 | /// use foo::{a, b, c}; |
04454e1e | 2299 | /// // ^ |
48663c56 XL |
2300 | /// ``` |
2301 | /// | |
2302 | /// then return the span until the next binding or the end of the statement: | |
2303 | /// | |
04454e1e | 2304 | /// ```ignore (illustrative) |
48663c56 | 2305 | /// use foo::{a, b, c}; |
04454e1e | 2306 | /// // ^^^ |
48663c56 | 2307 | /// ``` |
04454e1e | 2308 | fn find_span_of_binding_until_next_binding( |
48663c56 XL |
2309 | sess: &Session, |
2310 | binding_span: Span, | |
2311 | use_span: Span, | |
2312 | ) -> (bool, Span) { | |
2313 | let source_map = sess.source_map(); | |
2314 | ||
2315 | // Find the span of everything after the binding. | |
2316 | // ie. `a, e};` or `a};` | |
2317 | let binding_until_end = binding_span.with_hi(use_span.hi()); | |
2318 | ||
2319 | // Find everything after the binding but not including the binding. | |
2320 | // ie. `, e};` or `};` | |
2321 | let after_binding_until_end = binding_until_end.with_lo(binding_span.hi()); | |
2322 | ||
2323 | // Keep characters in the span until we encounter something that isn't a comma or | |
2324 | // whitespace. | |
2325 | // ie. `, ` or ``. | |
2326 | // | |
2327 | // Also note whether a closing brace character was encountered. If there | |
2328 | // was, then later go backwards to remove any trailing commas that are left. | |
2329 | let mut found_closing_brace = false; | |
dfeec247 XL |
2330 | let after_binding_until_next_binding = |
2331 | source_map.span_take_while(after_binding_until_end, |&ch| { | |
2332 | if ch == '}' { | |
2333 | found_closing_brace = true; | |
2334 | } | |
48663c56 | 2335 | ch == ' ' || ch == ',' |
dfeec247 | 2336 | }); |
48663c56 XL |
2337 | |
2338 | // Combine the two spans. | |
2339 | // ie. `a, ` or `a`. | |
2340 | // | |
2341 | // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };` | |
2342 | let span = binding_span.with_hi(after_binding_until_next_binding.hi()); | |
2343 | ||
2344 | (found_closing_brace, span) | |
2345 | } | |
2346 | ||
2347 | /// Given a `binding_span`, return the span through to the comma or opening brace of the previous | |
2348 | /// binding. | |
2349 | /// | |
04454e1e | 2350 | /// ```ignore (illustrative) |
48663c56 | 2351 | /// use foo::a::{a, b, c}; |
04454e1e FG |
2352 | /// // ^^--- binding span |
2353 | /// // | | |
2354 | /// // returned span | |
48663c56 XL |
2355 | /// |
2356 | /// use foo::{a, b, c}; | |
04454e1e | 2357 | /// // --- binding span |
48663c56 | 2358 | /// ``` |
04454e1e | 2359 | fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option<Span> { |
48663c56 XL |
2360 | let source_map = sess.source_map(); |
2361 | ||
2362 | // `prev_source` will contain all of the source that came before the span. | |
2363 | // Then split based on a command and take the first (ie. closest to our span) | |
2364 | // snippet. In the example, this is a space. | |
2365 | let prev_source = source_map.span_to_prev_source(binding_span).ok()?; | |
2366 | ||
2367 | let prev_comma = prev_source.rsplit(',').collect::<Vec<_>>(); | |
2368 | let prev_starting_brace = prev_source.rsplit('{').collect::<Vec<_>>(); | |
2369 | if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 { | |
2370 | return None; | |
9cc50fc6 | 2371 | } |
92a42be0 | 2372 | |
48663c56 XL |
2373 | let prev_comma = prev_comma.first().unwrap(); |
2374 | let prev_starting_brace = prev_starting_brace.first().unwrap(); | |
7453a54e | 2375 | |
48663c56 XL |
2376 | // If the amount of source code before the comma is greater than |
2377 | // the amount of source code before the starting brace then we've only | |
2378 | // got one item in the nested item (eg. `issue_52891::{self}`). | |
2379 | if prev_comma.len() > prev_starting_brace.len() { | |
2380 | return None; | |
92a42be0 | 2381 | } |
a7813a04 | 2382 | |
48663c56 XL |
2383 | Some(binding_span.with_lo(BytePos( |
2384 | // Take away the number of bytes for the characters we've found and an | |
2385 | // extra for the comma. | |
dfeec247 | 2386 | binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1, |
48663c56 XL |
2387 | ))) |
2388 | } | |
2389 | ||
2390 | /// Given a `use_span` of a binding within a use statement, returns the highlighted span and if | |
2391 | /// it is a nested use tree. | |
2392 | /// | |
04454e1e | 2393 | /// ```ignore (illustrative) |
48663c56 | 2394 | /// use foo::a::{b, c}; |
04454e1e | 2395 | /// // ^^^^^^^^^^ -- false |
48663c56 XL |
2396 | /// |
2397 | /// use foo::{a, b, c}; | |
04454e1e | 2398 | /// // ^^^^^^^^^^ -- true |
48663c56 XL |
2399 | /// |
2400 | /// use foo::{a, b::{c, d}}; | |
04454e1e | 2401 | /// // ^^^^^^^^^^^^^^^ -- true |
48663c56 XL |
2402 | /// ``` |
2403 | fn find_span_immediately_after_crate_name( | |
2404 | sess: &Session, | |
2405 | module_name: Symbol, | |
2406 | use_span: Span, | |
2407 | ) -> (bool, Span) { | |
dfeec247 XL |
2408 | debug!( |
2409 | "find_span_immediately_after_crate_name: module_name={:?} use_span={:?}", | |
2410 | module_name, use_span | |
2411 | ); | |
48663c56 XL |
2412 | let source_map = sess.source_map(); |
2413 | ||
2414 | // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. | |
2415 | let mut num_colons = 0; | |
2416 | // Find second colon.. `use issue_59764:` | |
2417 | let until_second_colon = source_map.span_take_while(use_span, |c| { | |
dfeec247 XL |
2418 | if *c == ':' { |
2419 | num_colons += 1; | |
2420 | } | |
29967ef6 | 2421 | !matches!(c, ':' if num_colons == 2) |
48663c56 XL |
2422 | }); |
2423 | // Find everything after the second colon.. `foo::{baz, makro};` | |
2424 | let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1)); | |
2425 | ||
2426 | let mut found_a_non_whitespace_character = false; | |
2427 | // Find the first non-whitespace character in `from_second_colon`.. `f` | |
2428 | let after_second_colon = source_map.span_take_while(from_second_colon, |c| { | |
dfeec247 XL |
2429 | if found_a_non_whitespace_character { |
2430 | return false; | |
2431 | } | |
2432 | if !c.is_whitespace() { | |
2433 | found_a_non_whitespace_character = true; | |
2434 | } | |
48663c56 XL |
2435 | true |
2436 | }); | |
2437 | ||
2438 | // Find the first `{` in from_second_colon.. `foo::{` | |
2439 | let next_left_bracket = source_map.span_through_char(from_second_colon, '{'); | |
2440 | ||
2441 | (next_left_bracket == after_second_colon, from_second_colon) | |
85aaf69f | 2442 | } |
416331ca | 2443 | |
04454e1e FG |
2444 | /// A suggestion has already been emitted, change the wording slightly to clarify that both are |
2445 | /// independent options. | |
2446 | enum Instead { | |
2447 | Yes, | |
2448 | No, | |
2449 | } | |
2450 | ||
2451 | /// Whether an existing place with an `use` item was found. | |
2452 | enum FoundUse { | |
2453 | Yes, | |
2454 | No, | |
2455 | } | |
2456 | ||
2457 | /// Whether a binding is part of a pattern or an expression. Used for diagnostics. | |
2458 | enum IsPattern { | |
2459 | /// The binding is part of a pattern | |
2460 | Yes, | |
2461 | /// The binding is part of an expression | |
2462 | No, | |
2463 | } | |
2464 | ||
416331ca XL |
2465 | /// When an entity with a given name is not available in scope, we search for |
2466 | /// entities with that name in all crates. This method allows outputting the | |
2467 | /// results of this search in a programmer-friendly way | |
04454e1e | 2468 | fn show_candidates( |
c295e0f8 | 2469 | session: &Session, |
923072b8 | 2470 | source_span: &IndexVec<LocalDefId, Span>, |
5e7ed085 | 2471 | err: &mut Diagnostic, |
416331ca | 2472 | // This is `None` if all placement locations are inside expansions |
f9f354fc | 2473 | use_placement_span: Option<Span>, |
416331ca | 2474 | candidates: &[ImportSuggestion], |
04454e1e FG |
2475 | instead: Instead, |
2476 | found_use: FoundUse, | |
2477 | is_pattern: IsPattern, | |
2478 | path: Vec<Segment>, | |
416331ca | 2479 | ) { |
dfeec247 XL |
2480 | if candidates.is_empty() { |
2481 | return; | |
2482 | } | |
f9f354fc | 2483 | |
3c0e092e XL |
2484 | let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> = |
2485 | Vec::new(); | |
2486 | let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> = | |
2487 | Vec::new(); | |
c295e0f8 XL |
2488 | |
2489 | candidates.iter().for_each(|c| { | |
2490 | (if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings }) | |
3c0e092e | 2491 | .push((path_names_to_string(&c.path), c.descr, c.did, &c.note)) |
c295e0f8 XL |
2492 | }); |
2493 | ||
416331ca XL |
2494 | // we want consistent results across executions, but candidates are produced |
2495 | // by iterating through a hash map, so make sure they are ordered: | |
c295e0f8 XL |
2496 | for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] { |
2497 | path_strings.sort_by(|a, b| a.0.cmp(&b.0)); | |
2498 | let core_path_strings = | |
2499 | path_strings.drain_filter(|p| p.0.starts_with("core::")).collect::<Vec<_>>(); | |
2500 | path_strings.extend(core_path_strings); | |
2501 | path_strings.dedup_by(|a, b| a.0 == b.0); | |
2502 | } | |
f035d41b | 2503 | |
c295e0f8 | 2504 | if !accessible_path_strings.is_empty() { |
04454e1e FG |
2505 | let (determiner, kind, name) = if accessible_path_strings.len() == 1 { |
2506 | ("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0)) | |
c295e0f8 | 2507 | } else { |
04454e1e | 2508 | ("one of these", "items", String::new()) |
c295e0f8 | 2509 | }; |
416331ca | 2510 | |
04454e1e FG |
2511 | let instead = if let Instead::Yes = instead { " instead" } else { "" }; |
2512 | let mut msg = if let IsPattern::Yes = is_pattern { | |
2513 | format!( | |
2514 | "if you meant to match on {}{}{}, use the full path in the pattern", | |
2515 | kind, instead, name | |
2516 | ) | |
2517 | } else { | |
2518 | format!("consider importing {} {}{}", determiner, kind, instead) | |
2519 | }; | |
416331ca | 2520 | |
5099ac24 | 2521 | for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { |
3c0e092e XL |
2522 | err.note(note); |
2523 | } | |
2524 | ||
04454e1e FG |
2525 | if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) { |
2526 | err.span_suggestions( | |
2527 | span, | |
2528 | &msg, | |
2529 | accessible_path_strings.into_iter().map(|a| a.0), | |
2530 | Applicability::MaybeIncorrect, | |
2531 | ); | |
2532 | } else if let Some(span) = use_placement_span { | |
c295e0f8 XL |
2533 | for candidate in &mut accessible_path_strings { |
2534 | // produce an additional newline to separate the new use statement | |
2535 | // from the directly following item. | |
04454e1e | 2536 | let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" }; |
c295e0f8 XL |
2537 | candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline); |
2538 | } | |
2539 | ||
2540 | err.span_suggestions( | |
2541 | span, | |
2542 | &msg, | |
2543 | accessible_path_strings.into_iter().map(|a| a.0), | |
04454e1e | 2544 | Applicability::MaybeIncorrect, |
c295e0f8 | 2545 | ); |
04454e1e FG |
2546 | if let [first, .., last] = &path[..] { |
2547 | err.span_suggestion_verbose( | |
2548 | first.ident.span.until(last.ident.span), | |
2549 | &format!("if you import `{}`, refer to it directly", last.ident), | |
923072b8 | 2550 | "", |
04454e1e FG |
2551 | Applicability::Unspecified, |
2552 | ); | |
2553 | } | |
c295e0f8 XL |
2554 | } else { |
2555 | msg.push(':'); | |
f035d41b | 2556 | |
c295e0f8 XL |
2557 | for candidate in accessible_path_strings { |
2558 | msg.push('\n'); | |
2559 | msg.push_str(&candidate.0); | |
2560 | } | |
2561 | ||
2562 | err.note(&msg); | |
416331ca | 2563 | } |
c295e0f8 XL |
2564 | } else { |
2565 | assert!(!inaccessible_path_strings.is_empty()); | |
2566 | ||
04454e1e FG |
2567 | let prefix = |
2568 | if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" }; | |
c295e0f8 | 2569 | if inaccessible_path_strings.len() == 1 { |
3c0e092e | 2570 | let (name, descr, def_id, note) = &inaccessible_path_strings[0]; |
04454e1e FG |
2571 | let msg = format!( |
2572 | "{}{} `{}`{} exists but is inaccessible", | |
2573 | prefix, | |
2574 | descr, | |
2575 | name, | |
2576 | if let IsPattern::Yes = is_pattern { ", which" } else { "" } | |
2577 | ); | |
c295e0f8 XL |
2578 | |
2579 | if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { | |
923072b8 | 2580 | let span = source_span[local_def_id]; |
c295e0f8 XL |
2581 | let span = session.source_map().guess_head_span(span); |
2582 | let mut multi_span = MultiSpan::from_span(span); | |
064997fb | 2583 | multi_span.push_span_label(span, "not accessible"); |
c295e0f8 XL |
2584 | err.span_note(multi_span, &msg); |
2585 | } else { | |
2586 | err.note(&msg); | |
2587 | } | |
3c0e092e XL |
2588 | if let Some(note) = (*note).as_deref() { |
2589 | err.note(note); | |
2590 | } | |
c295e0f8 | 2591 | } else { |
3c0e092e | 2592 | let (_, descr_first, _, _) = &inaccessible_path_strings[0]; |
c295e0f8 XL |
2593 | let descr = if inaccessible_path_strings |
2594 | .iter() | |
2595 | .skip(1) | |
3c0e092e | 2596 | .all(|(_, descr, _, _)| descr == descr_first) |
c295e0f8 | 2597 | { |
064997fb | 2598 | descr_first |
c295e0f8 | 2599 | } else { |
064997fb | 2600 | "item" |
c295e0f8 | 2601 | }; |
064997fb FG |
2602 | let plural_descr = |
2603 | if descr.ends_with('s') { format!("{}es", descr) } else { format!("{}s", descr) }; | |
c295e0f8 | 2604 | |
064997fb | 2605 | let mut msg = format!("{}these {} exist but are inaccessible", prefix, plural_descr); |
c295e0f8 XL |
2606 | let mut has_colon = false; |
2607 | ||
2608 | let mut spans = Vec::new(); | |
3c0e092e | 2609 | for (name, _, def_id, _) in &inaccessible_path_strings { |
c295e0f8 | 2610 | if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { |
923072b8 | 2611 | let span = source_span[local_def_id]; |
c295e0f8 XL |
2612 | let span = session.source_map().guess_head_span(span); |
2613 | spans.push((name, span)); | |
2614 | } else { | |
2615 | if !has_colon { | |
2616 | msg.push(':'); | |
2617 | has_colon = true; | |
2618 | } | |
2619 | msg.push('\n'); | |
2620 | msg.push_str(name); | |
2621 | } | |
2622 | } | |
f035d41b | 2623 | |
c295e0f8 XL |
2624 | let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect()); |
2625 | for (name, span) in spans { | |
2626 | multi_span.push_span_label(span, format!("`{}`: not accessible", name)); | |
2627 | } | |
2628 | ||
5099ac24 | 2629 | for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { |
3c0e092e XL |
2630 | err.note(note); |
2631 | } | |
2632 | ||
c295e0f8 XL |
2633 | err.span_note(multi_span, &msg); |
2634 | } | |
dfeec247 XL |
2635 | } |
2636 | } | |
04454e1e FG |
2637 | |
2638 | #[derive(Debug)] | |
2639 | struct UsePlacementFinder { | |
2640 | target_module: NodeId, | |
2641 | first_legal_span: Option<Span>, | |
2642 | first_use_span: Option<Span>, | |
2643 | } | |
2644 | ||
2645 | impl UsePlacementFinder { | |
2646 | fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, FoundUse) { | |
2647 | let mut finder = | |
2648 | UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; | |
2649 | finder.visit_crate(krate); | |
2650 | if let Some(use_span) = finder.first_use_span { | |
2651 | (Some(use_span), FoundUse::Yes) | |
2652 | } else { | |
2653 | (finder.first_legal_span, FoundUse::No) | |
2654 | } | |
2655 | } | |
2656 | } | |
2657 | ||
2658 | impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { | |
2659 | fn visit_crate(&mut self, c: &Crate) { | |
2660 | if self.target_module == CRATE_NODE_ID { | |
2661 | let inject = c.spans.inject_use_span; | |
2662 | if is_span_suitable_for_use_injection(inject) { | |
2663 | self.first_legal_span = Some(inject); | |
2664 | } | |
2665 | self.first_use_span = search_for_any_use_in_items(&c.items); | |
2666 | return; | |
2667 | } else { | |
2668 | visit::walk_crate(self, c); | |
2669 | } | |
2670 | } | |
2671 | ||
2672 | fn visit_item(&mut self, item: &'tcx ast::Item) { | |
2673 | if self.target_module == item.id { | |
2674 | if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { | |
2675 | let inject = mod_spans.inject_use_span; | |
2676 | if is_span_suitable_for_use_injection(inject) { | |
2677 | self.first_legal_span = Some(inject); | |
2678 | } | |
2679 | self.first_use_span = search_for_any_use_in_items(items); | |
2680 | return; | |
2681 | } | |
2682 | } else { | |
2683 | visit::walk_item(self, item); | |
2684 | } | |
2685 | } | |
2686 | } | |
2687 | ||
2688 | fn search_for_any_use_in_items(items: &[P<ast::Item>]) -> Option<Span> { | |
2689 | for item in items { | |
2690 | if let ItemKind::Use(..) = item.kind { | |
2691 | if is_span_suitable_for_use_injection(item.span) { | |
2692 | return Some(item.span.shrink_to_lo()); | |
2693 | } | |
2694 | } | |
2695 | } | |
2696 | return None; | |
2697 | } | |
2698 | ||
2699 | fn is_span_suitable_for_use_injection(s: Span) -> bool { | |
2700 | // don't suggest placing a use before the prelude | |
2701 | // import or other generated ones | |
2702 | !s.from_expansion() | |
2703 | } | |
2704 | ||
2705 | /// Convert the given number into the corresponding ordinal | |
923072b8 | 2706 | pub(crate) fn ordinalize(v: usize) -> String { |
04454e1e FG |
2707 | let suffix = match ((11..=13).contains(&(v % 100)), v % 10) { |
2708 | (false, 1) => "st", | |
2709 | (false, 2) => "nd", | |
2710 | (false, 3) => "rd", | |
2711 | _ => "th", | |
2712 | }; | |
2713 | format!("{v}{suffix}") | |
2714 | } |