]>
Commit | Line | Data |
---|---|---|
48663c56 | 1 | use std::cmp::Reverse; |
ba9703b0 | 2 | use std::ptr; |
48663c56 | 3 | |
74b04a01 | 4 | use rustc_ast::util::lev_distance::find_best_match_for_name; |
3dfed10e | 5 | use rustc_ast::{self as ast, Path}; |
74b04a01 | 6 | use rustc_ast_pretty::pprust; |
dfeec247 | 7 | use rustc_data_structures::fx::FxHashSet; |
74b04a01 | 8 | use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; |
60c5eb7d | 9 | use rustc_feature::BUILTIN_ATTRIBUTES; |
dfeec247 XL |
10 | use rustc_hir::def::Namespace::{self, *}; |
11 | use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind}; | |
12 | use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; | |
ba9703b0 XL |
13 | use rustc_middle::bug; |
14 | use rustc_middle::ty::{self, DefIdTree}; | |
15 | use rustc_session::Session; | |
dfeec247 XL |
16 | use rustc_span::hygiene::MacroKind; |
17 | use rustc_span::source_map::SourceMap; | |
3dfed10e | 18 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
dfeec247 | 19 | use rustc_span::{BytePos, MultiSpan, Span}; |
3dfed10e | 20 | use tracing::debug; |
416331ca | 21 | |
74b04a01 | 22 | use crate::imports::{Import, ImportKind, ImportResolver}; |
60c5eb7d | 23 | use crate::path_names_to_string; |
dfeec247 | 24 | use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; |
ba9703b0 XL |
25 | use crate::{ |
26 | BindingError, CrateLint, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot, | |
27 | }; | |
dfeec247 XL |
28 | use crate::{NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; |
29 | use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet, Segment}; | |
60c5eb7d | 30 | |
48663c56 XL |
31 | type Res = def::Res<ast::NodeId>; |
32 | ||
416331ca XL |
33 | /// A vector of spans and replacements, a message and applicability. |
34 | crate type Suggestion = (Vec<(Span, String)>, String, Applicability); | |
48663c56 | 35 | |
f035d41b XL |
36 | /// Potential candidate for an undeclared or out-of-scope label - contains the ident of a |
37 | /// similarly named label and whether or not it is reachable. | |
38 | crate type LabelSuggestion = (Ident, bool); | |
39 | ||
416331ca XL |
40 | crate struct TypoSuggestion { |
41 | pub candidate: Symbol, | |
42 | pub res: Res, | |
43 | } | |
48663c56 | 44 | |
416331ca XL |
45 | impl TypoSuggestion { |
46 | crate fn from_res(candidate: Symbol, res: Res) -> TypoSuggestion { | |
47 | TypoSuggestion { candidate, res } | |
48 | } | |
49 | } | |
48663c56 | 50 | |
416331ca XL |
51 | /// A free importable items suggested in case of resolution failure. |
52 | crate struct ImportSuggestion { | |
53 | pub did: Option<DefId>, | |
f9f354fc | 54 | pub descr: &'static str, |
416331ca | 55 | pub path: Path, |
f035d41b | 56 | pub accessible: bool, |
416331ca | 57 | } |
48663c56 | 58 | |
416331ca XL |
59 | /// Adjust the impl span so that just the `impl` keyword is taken by removing |
60 | /// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and | |
61 | /// everything after the first whitespace (`"impl Iterator for A" -> "impl"`). | |
62 | /// | |
63 | /// *Attention*: the method used is very fragile since it essentially duplicates the work of the | |
64 | /// parser. If you need to use this function or something similar, please consider updating the | |
65 | /// `source_map` functions and this function to something more robust. | |
74b04a01 XL |
66 | fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { |
67 | let impl_span = sm.span_until_char(impl_span, '<'); | |
ba9703b0 | 68 | sm.span_until_whitespace(impl_span) |
416331ca | 69 | } |
3157f602 | 70 | |
e1599b0c XL |
71 | impl<'a> Resolver<'a> { |
72 | crate fn add_module_candidates( | |
73 | &mut self, | |
74 | module: Module<'a>, | |
75 | names: &mut Vec<TypoSuggestion>, | |
76 | filter_fn: &impl Fn(Res) -> bool, | |
77 | ) { | |
e74abb32 | 78 | for (key, resolution) in self.resolutions(module).borrow().iter() { |
e1599b0c XL |
79 | if let Some(binding) = resolution.borrow().binding { |
80 | let res = binding.res(); | |
81 | if filter_fn(res) { | |
e74abb32 | 82 | names.push(TypoSuggestion::from_res(key.ident.name, res)); |
e1599b0c | 83 | } |
416331ca | 84 | } |
48663c56 | 85 | } |
416331ca | 86 | } |
3157f602 | 87 | |
416331ca XL |
88 | /// Combines an error with provided span and emits it. |
89 | /// | |
90 | /// This takes the error provided, combines it with the span and any additional spans inside the | |
91 | /// error and emits it. | |
92 | crate fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) { | |
93 | self.into_struct_error(span, resolution_error).emit(); | |
94 | } | |
48663c56 | 95 | |
416331ca | 96 | crate fn into_struct_error( |
dfeec247 XL |
97 | &self, |
98 | span: Span, | |
99 | resolution_error: ResolutionError<'_>, | |
416331ca XL |
100 | ) -> DiagnosticBuilder<'_> { |
101 | match resolution_error { | |
e74abb32 | 102 | ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => { |
dfeec247 XL |
103 | let mut err = struct_span_err!( |
104 | self.session, | |
48663c56 | 105 | span, |
416331ca XL |
106 | E0401, |
107 | "can't use generic parameters from outer function", | |
48663c56 | 108 | ); |
74b04a01 | 109 | err.span_label(span, "use of generic parameter from outer function".to_string()); |
416331ca | 110 | |
74b04a01 | 111 | let sm = self.session.source_map(); |
416331ca XL |
112 | match outer_res { |
113 | Res::SelfTy(maybe_trait_defid, maybe_impl_defid) => { | |
dfeec247 | 114 | if let Some(impl_span) = |
f035d41b | 115 | maybe_impl_defid.and_then(|def_id| self.opt_span(def_id)) |
dfeec247 | 116 | { |
48663c56 | 117 | err.span_label( |
74b04a01 | 118 | reduce_impl_span_to_impl_keyword(sm, impl_span), |
416331ca | 119 | "`Self` type implicitly declared here, by this `impl`", |
48663c56 XL |
120 | ); |
121 | } | |
416331ca XL |
122 | match (maybe_trait_defid, maybe_impl_defid) { |
123 | (Some(_), None) => { | |
124 | err.span_label(span, "can't use `Self` here"); | |
125 | } | |
126 | (_, Some(_)) => { | |
127 | err.span_label(span, "use a type here instead"); | |
128 | } | |
129 | (None, None) => bug!("`impl` without trait nor type?"), | |
130 | } | |
131 | return err; | |
dfeec247 | 132 | } |
416331ca | 133 | Res::Def(DefKind::TyParam, def_id) => { |
f035d41b | 134 | if let Some(span) = self.opt_span(def_id) { |
416331ca XL |
135 | err.span_label(span, "type parameter from outer function"); |
136 | } | |
48663c56 | 137 | } |
416331ca | 138 | Res::Def(DefKind::ConstParam, def_id) => { |
f035d41b | 139 | if let Some(span) = self.opt_span(def_id) { |
416331ca XL |
140 | err.span_label(span, "const parameter from outer function"); |
141 | } | |
48663c56 | 142 | } |
416331ca | 143 | _ => { |
dfeec247 XL |
144 | bug!( |
145 | "GenericParamsFromOuterFunction should only be used with Res::SelfTy, \ | |
146 | DefKind::TyParam" | |
147 | ); | |
48663c56 XL |
148 | } |
149 | } | |
d9579d0f | 150 | |
e74abb32 XL |
151 | if has_generic_params == HasGenericParams::Yes { |
152 | // Try to retrieve the span of the function signature and generate a new | |
153 | // message with a local type or const parameter. | |
74b04a01 XL |
154 | let sugg_msg = "try using a local generic parameter instead"; |
155 | if let Some((sugg_span, snippet)) = sm.generate_local_type_param_snippet(span) { | |
e74abb32 XL |
156 | // Suggest the modification to the user |
157 | err.span_suggestion( | |
158 | sugg_span, | |
159 | sugg_msg, | |
160 | snippet, | |
161 | Applicability::MachineApplicable, | |
162 | ); | |
74b04a01 | 163 | } else if let Some(sp) = sm.generate_fn_name_span(span) { |
dfeec247 XL |
164 | err.span_label( |
165 | sp, | |
74b04a01 XL |
166 | "try adding a local generic parameter in this method instead" |
167 | .to_string(), | |
dfeec247 | 168 | ); |
e74abb32 | 169 | } else { |
74b04a01 | 170 | err.help("try using a local generic parameter instead"); |
e74abb32 | 171 | } |
416331ca | 172 | } |
c1a9b12d | 173 | |
416331ca | 174 | err |
48663c56 | 175 | } |
416331ca | 176 | ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { |
e1599b0c XL |
177 | let mut err = struct_span_err!( |
178 | self.session, | |
179 | span, | |
180 | E0403, | |
181 | "the name `{}` is already used for a generic \ | |
182 | parameter in this item's generic parameters", | |
183 | name, | |
184 | ); | |
416331ca XL |
185 | err.span_label(span, "already used"); |
186 | err.span_label(first_use_span, format!("first use of `{}`", name)); | |
187 | err | |
188 | } | |
189 | ResolutionError::MethodNotMemberOfTrait(method, trait_) => { | |
dfeec247 XL |
190 | let mut err = struct_span_err!( |
191 | self.session, | |
192 | span, | |
193 | E0407, | |
194 | "method `{}` is not a member of trait `{}`", | |
195 | method, | |
196 | trait_ | |
197 | ); | |
416331ca XL |
198 | err.span_label(span, format!("not a member of trait `{}`", trait_)); |
199 | err | |
200 | } | |
201 | ResolutionError::TypeNotMemberOfTrait(type_, trait_) => { | |
dfeec247 XL |
202 | let mut err = struct_span_err!( |
203 | self.session, | |
204 | span, | |
205 | E0437, | |
206 | "type `{}` is not a member of trait `{}`", | |
207 | type_, | |
208 | trait_ | |
209 | ); | |
416331ca XL |
210 | err.span_label(span, format!("not a member of trait `{}`", trait_)); |
211 | err | |
212 | } | |
213 | ResolutionError::ConstNotMemberOfTrait(const_, trait_) => { | |
dfeec247 XL |
214 | let mut err = struct_span_err!( |
215 | self.session, | |
216 | span, | |
217 | E0438, | |
218 | "const `{}` is not a member of trait `{}`", | |
219 | const_, | |
220 | trait_ | |
221 | ); | |
416331ca XL |
222 | err.span_label(span, format!("not a member of trait `{}`", trait_)); |
223 | err | |
224 | } | |
225 | ResolutionError::VariableNotBoundInPattern(binding_error) => { | |
226 | let BindingError { name, target, origin, could_be_path } = binding_error; | |
3157f602 | 227 | |
416331ca XL |
228 | let target_sp = target.iter().copied().collect::<Vec<_>>(); |
229 | let origin_sp = origin.iter().copied().collect::<Vec<_>>(); | |
5bcae85e | 230 | |
416331ca | 231 | let msp = MultiSpan::from_spans(target_sp.clone()); |
60c5eb7d XL |
232 | let mut err = struct_span_err!( |
233 | self.session, | |
416331ca | 234 | msp, |
60c5eb7d | 235 | E0408, |
dfeec247 XL |
236 | "variable `{}` is not bound in all patterns", |
237 | name, | |
416331ca XL |
238 | ); |
239 | for sp in target_sp { | |
240 | err.span_label(sp, format!("pattern doesn't bind `{}`", name)); | |
241 | } | |
242 | for sp in origin_sp { | |
243 | err.span_label(sp, "variable not in all patterns"); | |
244 | } | |
245 | if *could_be_path { | |
246 | let help_msg = format!( | |
247 | "if you meant to match on a variant or a `const` item, consider \ | |
248 | making the path in the pattern qualified: `?::{}`", | |
dfeec247 XL |
249 | name, |
250 | ); | |
416331ca | 251 | err.span_help(span, &help_msg); |
48663c56 | 252 | } |
416331ca | 253 | err |
48663c56 | 254 | } |
dfeec247 XL |
255 | ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { |
256 | let mut err = struct_span_err!( | |
257 | self.session, | |
258 | span, | |
259 | E0409, | |
74b04a01 | 260 | "variable `{}` is bound inconsistently across alternatives separated by `|`", |
dfeec247 XL |
261 | variable_name |
262 | ); | |
416331ca XL |
263 | err.span_label(span, "bound in different ways"); |
264 | err.span_label(first_binding_span, "first binding"); | |
265 | err | |
266 | } | |
267 | ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { | |
dfeec247 XL |
268 | let mut err = struct_span_err!( |
269 | self.session, | |
270 | span, | |
271 | E0415, | |
272 | "identifier `{}` is bound more than once in this parameter list", | |
273 | identifier | |
274 | ); | |
416331ca XL |
275 | err.span_label(span, "used as parameter more than once"); |
276 | err | |
277 | } | |
278 | ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { | |
dfeec247 XL |
279 | let mut err = struct_span_err!( |
280 | self.session, | |
281 | span, | |
282 | E0416, | |
283 | "identifier `{}` is bound more than once in the same pattern", | |
284 | identifier | |
285 | ); | |
416331ca XL |
286 | err.span_label(span, "used in a pattern more than once"); |
287 | err | |
288 | } | |
f035d41b | 289 | ResolutionError::UndeclaredLabel { name, suggestion } => { |
dfeec247 XL |
290 | let mut err = struct_span_err!( |
291 | self.session, | |
292 | span, | |
293 | E0426, | |
294 | "use of undeclared label `{}`", | |
295 | name | |
296 | ); | |
f035d41b XL |
297 | |
298 | err.span_label(span, format!("undeclared label `{}`", name)); | |
299 | ||
300 | match suggestion { | |
301 | // A reachable label with a similar name exists. | |
302 | Some((ident, true)) => { | |
303 | err.span_label(ident.span, "a label with a similar name is reachable"); | |
304 | err.span_suggestion( | |
305 | span, | |
306 | "try using similarly named label", | |
307 | ident.name.to_string(), | |
308 | Applicability::MaybeIncorrect, | |
309 | ); | |
310 | } | |
311 | // An unreachable label with a similar name exists. | |
312 | Some((ident, false)) => { | |
313 | err.span_label( | |
314 | ident.span, | |
315 | "a label with a similar name exists but is unreachable", | |
316 | ); | |
317 | } | |
318 | // No similarly-named labels exist. | |
319 | None => (), | |
48663c56 | 320 | } |
f035d41b | 321 | |
416331ca | 322 | err |
48663c56 | 323 | } |
f9f354fc XL |
324 | ResolutionError::SelfImportsOnlyAllowedWithin { root, span_with_rename } => { |
325 | let mut err = struct_span_err!( | |
326 | self.session, | |
327 | span, | |
328 | E0429, | |
329 | "{}", | |
330 | "`self` imports are only allowed within a { } list" | |
331 | ); | |
332 | ||
333 | // None of the suggestions below would help with a case like `use self`. | |
334 | if !root { | |
335 | // use foo::bar::self -> foo::bar | |
336 | // use foo::bar::self as abc -> foo::bar as abc | |
337 | err.span_suggestion( | |
338 | span, | |
339 | "consider importing the module directly", | |
340 | "".to_string(), | |
341 | Applicability::MachineApplicable, | |
342 | ); | |
343 | ||
344 | // use foo::bar::self -> foo::bar::{self} | |
345 | // use foo::bar::self as abc -> foo::bar::{self as abc} | |
346 | let braces = vec![ | |
347 | (span_with_rename.shrink_to_lo(), "{".to_string()), | |
348 | (span_with_rename.shrink_to_hi(), "}".to_string()), | |
349 | ]; | |
350 | err.multipart_suggestion( | |
351 | "alternatively, use the multi-path `use` syntax to import `self`", | |
352 | braces, | |
353 | Applicability::MachineApplicable, | |
354 | ); | |
355 | } | |
356 | err | |
357 | } | |
416331ca | 358 | ResolutionError::SelfImportCanOnlyAppearOnceInTheList => { |
dfeec247 XL |
359 | let mut err = struct_span_err!( |
360 | self.session, | |
361 | span, | |
362 | E0430, | |
363 | "`self` import can only appear once in an import list" | |
364 | ); | |
416331ca XL |
365 | err.span_label(span, "can only appear once in an import list"); |
366 | err | |
48663c56 | 367 | } |
416331ca | 368 | ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => { |
dfeec247 XL |
369 | let mut err = struct_span_err!( |
370 | self.session, | |
371 | span, | |
372 | E0431, | |
373 | "`self` import can only appear in an import list with \ | |
374 | a non-empty prefix" | |
375 | ); | |
416331ca XL |
376 | err.span_label(span, "can only appear in an import list with a non-empty prefix"); |
377 | err | |
48663c56 | 378 | } |
416331ca | 379 | ResolutionError::FailedToResolve { label, suggestion } => { |
dfeec247 XL |
380 | let mut err = |
381 | struct_span_err!(self.session, span, E0433, "failed to resolve: {}", &label); | |
416331ca | 382 | err.span_label(span, label); |
48663c56 | 383 | |
416331ca XL |
384 | if let Some((suggestions, msg, applicability)) = suggestion { |
385 | err.multipart_suggestion(&msg, suggestions, applicability); | |
48663c56 | 386 | } |
416331ca XL |
387 | |
388 | err | |
389 | } | |
390 | ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { | |
dfeec247 XL |
391 | let mut err = struct_span_err!( |
392 | self.session, | |
393 | span, | |
394 | E0434, | |
395 | "{}", | |
396 | "can't capture dynamic environment in a fn item" | |
397 | ); | |
416331ca XL |
398 | err.help("use the `|| { ... }` closure form instead"); |
399 | err | |
48663c56 | 400 | } |
416331ca | 401 | ResolutionError::AttemptToUseNonConstantValueInConstant => { |
dfeec247 XL |
402 | let mut err = struct_span_err!( |
403 | self.session, | |
404 | span, | |
405 | E0435, | |
406 | "attempt to use a non-constant value in a constant" | |
407 | ); | |
416331ca XL |
408 | err.span_label(span, "non-constant value"); |
409 | err | |
410 | } | |
411 | ResolutionError::BindingShadowsSomethingUnacceptable(what_binding, name, binding) => { | |
412 | let res = binding.res(); | |
413 | let shadows_what = res.descr(); | |
dfeec247 XL |
414 | let mut err = struct_span_err!( |
415 | self.session, | |
416 | span, | |
417 | E0530, | |
418 | "{}s cannot shadow {}s", | |
419 | what_binding, | |
420 | shadows_what | |
421 | ); | |
422 | err.span_label( | |
423 | span, | |
424 | format!("cannot be named the same as {} {}", res.article(), shadows_what), | |
425 | ); | |
416331ca XL |
426 | let participle = if binding.is_import() { "imported" } else { "defined" }; |
427 | let msg = format!("the {} `{}` is {} here", shadows_what, name, participle); | |
428 | err.span_label(binding.span, msg); | |
429 | err | |
430 | } | |
431 | ResolutionError::ForwardDeclaredTyParam => { | |
dfeec247 XL |
432 | let mut err = struct_span_err!( |
433 | self.session, | |
434 | span, | |
435 | E0128, | |
436 | "type parameters with a default cannot use \ | |
437 | forward declared identifiers" | |
438 | ); | |
48663c56 | 439 | err.span_label( |
dfeec247 XL |
440 | span, |
441 | "defaulted type parameters cannot be forward declared".to_string(), | |
442 | ); | |
416331ca XL |
443 | err |
444 | } | |
3dfed10e XL |
445 | ResolutionError::ParamInTyOfConstParam(name) => { |
446 | let mut err = struct_span_err!( | |
447 | self.session, | |
448 | span, | |
449 | E0770, | |
450 | "the type of const parameters must not depend on other generic parameters" | |
451 | ); | |
452 | err.span_label( | |
453 | span, | |
454 | format!("the type must not depend on the parameter `{}`", name), | |
455 | ); | |
456 | err | |
457 | } | |
458 | ResolutionError::ParamInAnonConstInTyDefault(name) => { | |
459 | let mut err = self.session.struct_span_err( | |
460 | span, | |
461 | "constant values inside of type parameter defaults must not depend on generic parameters", | |
462 | ); | |
463 | err.span_label( | |
464 | span, | |
465 | format!("the anonymous constant must not depend on the parameter `{}`", name), | |
466 | ); | |
467 | err | |
468 | } | |
469 | ResolutionError::ParamInNonTrivialAnonConst(name) => { | |
470 | let mut err = self.session.struct_span_err( | |
471 | span, | |
472 | "generic parameters must not be used inside of non trivial constant values", | |
473 | ); | |
474 | err.span_label( | |
475 | span, | |
476 | &format!( | |
477 | "non-trivial anonymous constants must not depend on the parameter `{}`", | |
478 | name | |
479 | ), | |
480 | ); | |
481 | err.help( | |
482 | &format!("it is currently only allowed to use either `{0}` or `{{ {0} }}` as generic constants", name) | |
483 | ); | |
484 | err | |
485 | } | |
e74abb32 | 486 | ResolutionError::SelfInTyParamDefault => { |
416331ca XL |
487 | let mut err = struct_span_err!( |
488 | self.session, | |
48663c56 | 489 | span, |
e74abb32 XL |
490 | E0735, |
491 | "type parameters cannot use `Self` in their defaults" | |
48663c56 | 492 | ); |
dfeec247 | 493 | err.span_label(span, "`Self` in type parameter default".to_string()); |
416331ca | 494 | err |
48663c56 | 495 | } |
f035d41b XL |
496 | ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { |
497 | let mut err = struct_span_err!( | |
498 | self.session, | |
499 | span, | |
500 | E0767, | |
501 | "use of unreachable label `{}`", | |
502 | name, | |
503 | ); | |
504 | ||
505 | err.span_label(definition_span, "unreachable label defined here"); | |
506 | err.span_label(span, format!("unreachable label `{}`", name)); | |
507 | err.note( | |
508 | "labels are unreachable through functions, closures, async blocks and modules", | |
509 | ); | |
510 | ||
511 | match suggestion { | |
512 | // A reachable label with a similar name exists. | |
513 | Some((ident, true)) => { | |
514 | err.span_label(ident.span, "a label with a similar name is reachable"); | |
515 | err.span_suggestion( | |
516 | span, | |
517 | "try using similarly named label", | |
518 | ident.name.to_string(), | |
519 | Applicability::MaybeIncorrect, | |
520 | ); | |
521 | } | |
522 | // An unreachable label with a similar name exists. | |
523 | Some((ident, false)) => { | |
524 | err.span_label( | |
525 | ident.span, | |
526 | "a label with a similar name exists but is also unreachable", | |
527 | ); | |
528 | } | |
529 | // No similarly-named labels exist. | |
530 | None => (), | |
531 | } | |
532 | ||
533 | err | |
534 | } | |
416331ca XL |
535 | } |
536 | } | |
48663c56 | 537 | |
e74abb32 XL |
538 | crate fn report_vis_error(&self, vis_resolution_error: VisResolutionError<'_>) { |
539 | match vis_resolution_error { | |
540 | VisResolutionError::Relative2018(span, path) => { | |
dfeec247 XL |
541 | let mut err = self.session.struct_span_err( |
542 | span, | |
543 | "relative paths are not supported in visibilities on 2018 edition", | |
544 | ); | |
e74abb32 XL |
545 | err.span_suggestion( |
546 | path.span, | |
547 | "try", | |
548 | format!("crate::{}", pprust::path_to_string(&path)), | |
549 | Applicability::MaybeIncorrect, | |
550 | ); | |
551 | err | |
552 | } | |
dfeec247 XL |
553 | VisResolutionError::AncestorOnly(span) => struct_span_err!( |
554 | self.session, | |
555 | span, | |
556 | E0742, | |
557 | "visibilities can only be restricted to ancestor modules" | |
558 | ), | |
e74abb32 | 559 | VisResolutionError::FailedToResolve(span, label, suggestion) => { |
dfeec247 | 560 | self.into_struct_error(span, ResolutionError::FailedToResolve { label, suggestion }) |
e74abb32 XL |
561 | } |
562 | VisResolutionError::ExpectedFound(span, path_str, res) => { | |
dfeec247 XL |
563 | let mut err = struct_span_err!( |
564 | self.session, | |
565 | span, | |
566 | E0577, | |
567 | "expected module, found {} `{}`", | |
568 | res.descr(), | |
569 | path_str | |
570 | ); | |
e74abb32 XL |
571 | err.span_label(span, "not a module"); |
572 | err | |
573 | } | |
dfeec247 XL |
574 | VisResolutionError::Indeterminate(span) => struct_span_err!( |
575 | self.session, | |
576 | span, | |
577 | E0578, | |
578 | "cannot determine resolution for the visibility" | |
579 | ), | |
e74abb32 XL |
580 | VisResolutionError::ModuleOnly(span) => { |
581 | self.session.struct_span_err(span, "visibility must resolve to a module") | |
582 | } | |
dfeec247 XL |
583 | } |
584 | .emit() | |
e74abb32 XL |
585 | } |
586 | ||
416331ca XL |
587 | /// Lookup typo candidate in scope for a macro or import. |
588 | fn early_lookup_typo_candidate( | |
589 | &mut self, | |
590 | scope_set: ScopeSet, | |
591 | parent_scope: &ParentScope<'a>, | |
592 | ident: Ident, | |
593 | filter_fn: &impl Fn(Res) -> bool, | |
594 | ) -> Option<TypoSuggestion> { | |
595 | let mut suggestions = Vec::new(); | |
596 | self.visit_scopes(scope_set, parent_scope, ident, |this, scope, use_prelude, _| { | |
597 | match scope { | |
60c5eb7d XL |
598 | Scope::DeriveHelpers(expn_id) => { |
599 | let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); | |
600 | if filter_fn(res) { | |
dfeec247 XL |
601 | suggestions.extend( |
602 | this.helper_attrs | |
603 | .get(&expn_id) | |
604 | .into_iter() | |
605 | .flatten() | |
606 | .map(|ident| TypoSuggestion::from_res(ident.name, res)), | |
607 | ); | |
60c5eb7d XL |
608 | } |
609 | } | |
610 | Scope::DeriveHelpersCompat => { | |
416331ca XL |
611 | let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); |
612 | if filter_fn(res) { | |
e1599b0c | 613 | for derive in parent_scope.derives { |
dfeec247 | 614 | let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; |
416331ca | 615 | if let Ok((Some(ext), _)) = this.resolve_macro_path( |
dfeec247 XL |
616 | derive, |
617 | Some(MacroKind::Derive), | |
618 | parent_scope, | |
619 | false, | |
620 | false, | |
416331ca | 621 | ) { |
dfeec247 XL |
622 | suggestions.extend( |
623 | ext.helper_attrs | |
624 | .iter() | |
625 | .map(|name| TypoSuggestion::from_res(*name, res)), | |
626 | ); | |
416331ca XL |
627 | } |
628 | } | |
629 | } | |
48663c56 | 630 | } |
ba9703b0 XL |
631 | Scope::MacroRules(macro_rules_scope) => { |
632 | if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope { | |
633 | let res = macro_rules_binding.binding.res(); | |
416331ca | 634 | if filter_fn(res) { |
dfeec247 | 635 | suggestions |
ba9703b0 | 636 | .push(TypoSuggestion::from_res(macro_rules_binding.ident.name, res)) |
416331ca XL |
637 | } |
638 | } | |
48663c56 | 639 | } |
416331ca XL |
640 | Scope::CrateRoot => { |
641 | let root_ident = Ident::new(kw::PathRoot, ident.span); | |
642 | let root_module = this.resolve_crate_root(root_ident); | |
e1599b0c | 643 | this.add_module_candidates(root_module, &mut suggestions, filter_fn); |
48663c56 | 644 | } |
416331ca | 645 | Scope::Module(module) => { |
e1599b0c | 646 | this.add_module_candidates(module, &mut suggestions, filter_fn); |
416331ca | 647 | } |
60c5eb7d XL |
648 | Scope::RegisteredAttrs => { |
649 | let res = Res::NonMacroAttr(NonMacroAttrKind::Registered); | |
650 | if filter_fn(res) { | |
dfeec247 XL |
651 | suggestions.extend( |
652 | this.registered_attrs | |
653 | .iter() | |
654 | .map(|ident| TypoSuggestion::from_res(ident.name, res)), | |
655 | ); | |
60c5eb7d XL |
656 | } |
657 | } | |
416331ca | 658 | Scope::MacroUsePrelude => { |
dfeec247 XL |
659 | suggestions.extend(this.macro_use_prelude.iter().filter_map( |
660 | |(name, binding)| { | |
661 | let res = binding.res(); | |
662 | filter_fn(res).then_some(TypoSuggestion::from_res(*name, res)) | |
663 | }, | |
664 | )); | |
416331ca XL |
665 | } |
666 | Scope::BuiltinAttrs => { | |
667 | let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin); | |
668 | if filter_fn(res) { | |
dfeec247 XL |
669 | suggestions.extend( |
670 | BUILTIN_ATTRIBUTES | |
671 | .iter() | |
672 | .map(|(name, ..)| TypoSuggestion::from_res(*name, res)), | |
673 | ); | |
48663c56 | 674 | } |
48663c56 | 675 | } |
416331ca XL |
676 | Scope::ExternPrelude => { |
677 | suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, _)| { | |
678 | let res = Res::Def(DefKind::Mod, DefId::local(CRATE_DEF_INDEX)); | |
60c5eb7d | 679 | filter_fn(res).then_some(TypoSuggestion::from_res(ident.name, res)) |
416331ca XL |
680 | })); |
681 | } | |
682 | Scope::ToolPrelude => { | |
683 | let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); | |
dfeec247 XL |
684 | suggestions.extend( |
685 | this.registered_tools | |
686 | .iter() | |
687 | .map(|ident| TypoSuggestion::from_res(ident.name, res)), | |
688 | ); | |
416331ca XL |
689 | } |
690 | Scope::StdLibPrelude => { | |
691 | if let Some(prelude) = this.prelude { | |
692 | let mut tmp_suggestions = Vec::new(); | |
e1599b0c | 693 | this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn); |
dfeec247 XL |
694 | suggestions.extend( |
695 | tmp_suggestions | |
696 | .into_iter() | |
697 | .filter(|s| use_prelude || this.is_builtin_macro(s.res)), | |
698 | ); | |
416331ca XL |
699 | } |
700 | } | |
701 | Scope::BuiltinTypes => { | |
702 | let primitive_types = &this.primitive_type_table.primitive_types; | |
dfeec247 XL |
703 | suggestions.extend(primitive_types.iter().flat_map(|(name, prim_ty)| { |
704 | let res = Res::PrimTy(*prim_ty); | |
705 | filter_fn(res).then_some(TypoSuggestion::from_res(*name, res)) | |
706 | })) | |
48663c56 XL |
707 | } |
708 | } | |
416331ca XL |
709 | |
710 | None::<()> | |
711 | }); | |
712 | ||
713 | // Make sure error reporting is deterministic. | |
714 | suggestions.sort_by_cached_key(|suggestion| suggestion.candidate.as_str()); | |
715 | ||
716 | match find_best_match_for_name( | |
717 | suggestions.iter().map(|suggestion| &suggestion.candidate), | |
3dfed10e | 718 | ident.name, |
416331ca XL |
719 | None, |
720 | ) { | |
dfeec247 XL |
721 | Some(found) if found != ident.name => { |
722 | suggestions.into_iter().find(|suggestion| suggestion.candidate == found) | |
723 | } | |
416331ca XL |
724 | _ => None, |
725 | } | |
726 | } | |
727 | ||
dfeec247 XL |
728 | fn lookup_import_candidates_from_module<FilterFn>( |
729 | &mut self, | |
730 | lookup_ident: Ident, | |
731 | namespace: Namespace, | |
f035d41b | 732 | parent_scope: &ParentScope<'a>, |
dfeec247 XL |
733 | start_module: Module<'a>, |
734 | crate_name: Ident, | |
735 | filter_fn: FilterFn, | |
736 | ) -> Vec<ImportSuggestion> | |
737 | where | |
738 | FilterFn: Fn(Res) -> bool, | |
416331ca XL |
739 | { |
740 | let mut candidates = Vec::new(); | |
741 | let mut seen_modules = FxHashSet::default(); | |
742 | let not_local_module = crate_name.name != kw::Crate; | |
f035d41b XL |
743 | let mut worklist = |
744 | vec![(start_module, Vec::<ast::PathSegment>::new(), true, not_local_module)]; | |
745 | let mut worklist_via_import = vec![]; | |
746 | ||
747 | while let Some((in_module, path_segments, accessible, in_module_is_extern)) = | |
748 | match worklist.pop() { | |
749 | None => worklist_via_import.pop(), | |
750 | Some(x) => Some(x), | |
751 | } | |
752 | { | |
416331ca XL |
753 | // We have to visit module children in deterministic order to avoid |
754 | // instabilities in reported imports (#43552). | |
e74abb32 | 755 | in_module.for_each_child(self, |this, ident, ns, name_binding| { |
f035d41b XL |
756 | // avoid non-importable candidates |
757 | if !name_binding.is_importable() { | |
dfeec247 XL |
758 | return; |
759 | } | |
f035d41b XL |
760 | |
761 | let child_accessible = | |
762 | accessible && this.is_accessible_from(name_binding.vis, parent_scope.module); | |
763 | ||
764 | // do not venture inside inaccessible items of other crates | |
765 | if in_module_is_extern && !child_accessible { | |
766 | return; | |
767 | } | |
768 | ||
769 | let via_import = name_binding.is_import() && !name_binding.is_extern_crate(); | |
770 | ||
771 | // There is an assumption elsewhere that paths of variants are in the enum's | |
772 | // declaration and not imported. With this assumption, the variant component is | |
773 | // chopped and the rest of the path is assumed to be the enum's own path. For | |
774 | // errors where a variant is used as the type instead of the enum, this causes | |
775 | // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`. | |
776 | if via_import && name_binding.is_possibly_imported_variant() { | |
dfeec247 XL |
777 | return; |
778 | } | |
416331ca XL |
779 | |
780 | // collect results based on the filter function | |
f035d41b XL |
781 | // avoid suggesting anything from the same module in which we are resolving |
782 | if ident.name == lookup_ident.name | |
783 | && ns == namespace | |
784 | && !ptr::eq(in_module, parent_scope.module) | |
785 | { | |
416331ca XL |
786 | let res = name_binding.res(); |
787 | if filter_fn(res) { | |
788 | // create the path | |
789 | let mut segms = path_segments.clone(); | |
790 | if lookup_ident.span.rust_2018() { | |
791 | // crate-local absolute paths start with `crate::` in edition 2018 | |
792 | // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) | |
dfeec247 | 793 | segms.insert(0, ast::PathSegment::from_ident(crate_name)); |
416331ca XL |
794 | } |
795 | ||
796 | segms.push(ast::PathSegment::from_ident(ident)); | |
dfeec247 | 797 | let path = Path { span: name_binding.span, segments: segms }; |
f035d41b XL |
798 | let did = match res { |
799 | Res::Def(DefKind::Ctor(..), did) => this.parent(did), | |
800 | _ => res.opt_def_id(), | |
801 | }; | |
802 | ||
803 | if child_accessible { | |
804 | // Remove invisible match if exists | |
805 | if let Some(idx) = candidates | |
806 | .iter() | |
807 | .position(|v: &ImportSuggestion| v.did == did && !v.accessible) | |
808 | { | |
809 | candidates.remove(idx); | |
810 | } | |
811 | } | |
812 | ||
813 | if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { | |
814 | candidates.push(ImportSuggestion { | |
815 | did, | |
816 | descr: res.descr(), | |
817 | path, | |
818 | accessible: child_accessible, | |
819 | }); | |
416331ca XL |
820 | } |
821 | } | |
822 | } | |
823 | ||
824 | // collect submodules to explore | |
825 | if let Some(module) = name_binding.module() { | |
826 | // form the path | |
827 | let mut path_segments = path_segments.clone(); | |
828 | path_segments.push(ast::PathSegment::from_ident(ident)); | |
829 | ||
830 | let is_extern_crate_that_also_appears_in_prelude = | |
dfeec247 | 831 | name_binding.is_extern_crate() && lookup_ident.span.rust_2018(); |
416331ca | 832 | |
f035d41b | 833 | if !is_extern_crate_that_also_appears_in_prelude { |
416331ca | 834 | let is_extern = in_module_is_extern || name_binding.is_extern_crate(); |
f035d41b | 835 | // add the module to the lookup |
416331ca | 836 | if seen_modules.insert(module.def_id().unwrap()) { |
f035d41b XL |
837 | if via_import { &mut worklist_via_import } else { &mut worklist } |
838 | .push((module, path_segments, child_accessible, is_extern)); | |
416331ca XL |
839 | } |
840 | } | |
841 | } | |
842 | }) | |
843 | } | |
844 | ||
f035d41b XL |
845 | // If only some candidates are accessible, take just them |
846 | if !candidates.iter().all(|v: &ImportSuggestion| !v.accessible) { | |
847 | candidates = candidates.into_iter().filter(|x| x.accessible).collect(); | |
848 | } | |
849 | ||
416331ca XL |
850 | candidates |
851 | } | |
852 | ||
853 | /// When name resolution fails, this method can be used to look up candidate | |
854 | /// entities with the expected name. It allows filtering them using the | |
855 | /// supplied predicate (which should be used to only accept the types of | |
856 | /// definitions expected, e.g., traits). The lookup spans across all crates. | |
857 | /// | |
858 | /// N.B., the method does not look into imports, but this is not a problem, | |
859 | /// since we report the definitions (thus, the de-aliased imports). | |
860 | crate fn lookup_import_candidates<FilterFn>( | |
dfeec247 XL |
861 | &mut self, |
862 | lookup_ident: Ident, | |
863 | namespace: Namespace, | |
f035d41b | 864 | parent_scope: &ParentScope<'a>, |
dfeec247 | 865 | filter_fn: FilterFn, |
416331ca | 866 | ) -> Vec<ImportSuggestion> |
dfeec247 XL |
867 | where |
868 | FilterFn: Fn(Res) -> bool, | |
416331ca XL |
869 | { |
870 | let mut suggestions = self.lookup_import_candidates_from_module( | |
dfeec247 XL |
871 | lookup_ident, |
872 | namespace, | |
f035d41b | 873 | parent_scope, |
dfeec247 XL |
874 | self.graph_root, |
875 | Ident::with_dummy_span(kw::Crate), | |
876 | &filter_fn, | |
416331ca XL |
877 | ); |
878 | ||
879 | if lookup_ident.span.rust_2018() { | |
880 | let extern_prelude_names = self.extern_prelude.clone(); | |
881 | for (ident, _) in extern_prelude_names.into_iter() { | |
e1599b0c XL |
882 | if ident.span.from_expansion() { |
883 | // Idents are adjusted to the root context before being | |
884 | // resolved in the extern prelude, so reporting this to the | |
885 | // user is no help. This skips the injected | |
886 | // `extern crate std` in the 2018 edition, which would | |
887 | // otherwise cause duplicate suggestions. | |
888 | continue; | |
889 | } | |
3dfed10e | 890 | if let Some(crate_id) = self.crate_loader.maybe_process_path_extern(ident.name) { |
dfeec247 XL |
891 | let crate_root = |
892 | self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }); | |
416331ca | 893 | suggestions.extend(self.lookup_import_candidates_from_module( |
dfeec247 XL |
894 | lookup_ident, |
895 | namespace, | |
f035d41b | 896 | parent_scope, |
dfeec247 XL |
897 | crate_root, |
898 | ident, | |
899 | &filter_fn, | |
900 | )); | |
416331ca | 901 | } |
48663c56 | 902 | } |
48663c56 | 903 | } |
416331ca XL |
904 | |
905 | suggestions | |
906 | } | |
907 | ||
908 | crate fn unresolved_macro_suggestions( | |
909 | &mut self, | |
910 | err: &mut DiagnosticBuilder<'a>, | |
911 | macro_kind: MacroKind, | |
912 | parent_scope: &ParentScope<'a>, | |
913 | ident: Ident, | |
914 | ) { | |
915 | let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind); | |
916 | let suggestion = self.early_lookup_typo_candidate( | |
dfeec247 XL |
917 | ScopeSet::Macro(macro_kind), |
918 | parent_scope, | |
919 | ident, | |
920 | is_expected, | |
416331ca | 921 | ); |
e74abb32 | 922 | self.add_typo_suggestion(err, suggestion, ident.span); |
416331ca | 923 | |
3dfed10e | 924 | if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { |
416331ca XL |
925 | let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident); |
926 | err.span_note(ident.span, &msg); | |
927 | } | |
ba9703b0 | 928 | if self.macro_names.contains(&ident.normalize_to_macros_2_0()) { |
416331ca XL |
929 | err.help("have you added the `#[macro_use]` on the module/import?"); |
930 | } | |
5bcae85e | 931 | } |
e74abb32 XL |
932 | |
933 | crate fn add_typo_suggestion( | |
934 | &self, | |
935 | err: &mut DiagnosticBuilder<'_>, | |
936 | suggestion: Option<TypoSuggestion>, | |
937 | span: Span, | |
938 | ) -> bool { | |
f035d41b XL |
939 | let suggestion = match suggestion { |
940 | None => return false, | |
74b04a01 | 941 | // We shouldn't suggest underscore. |
f035d41b XL |
942 | Some(suggestion) if suggestion.candidate == kw::Underscore => return false, |
943 | Some(suggestion) => suggestion, | |
944 | }; | |
f035d41b XL |
945 | let def_span = suggestion.res.opt_def_id().and_then(|def_id| match def_id.krate { |
946 | LOCAL_CRATE => self.opt_span(def_id), | |
947 | _ => Some( | |
948 | self.session | |
949 | .source_map() | |
950 | .guess_head_span(self.cstore().get_span_untracked(def_id, self.session)), | |
951 | ), | |
952 | }); | |
3dfed10e XL |
953 | if let Some(def_span) = def_span { |
954 | if span.overlaps(def_span) { | |
955 | // Don't suggest typo suggestion for itself like in the followoing: | |
956 | // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` | |
957 | // --> $DIR/issue-64792-bad-unicode-ctor.rs:3:14 | |
958 | // | | |
959 | // LL | struct X {} | |
960 | // | ----------- `X` defined here | |
961 | // LL | | |
962 | // LL | const Y: X = X("ö"); | |
963 | // | -------------^^^^^^- similarly named constant `Y` defined here | |
964 | // | | |
965 | // help: use struct literal syntax instead | |
966 | // | | |
967 | // LL | const Y: X = X {}; | |
968 | // | ^^^^ | |
969 | // help: a constant with a similar name exists | |
970 | // | | |
971 | // LL | const Y: X = Y("ö"); | |
972 | // | ^ | |
973 | return false; | |
974 | } | |
f035d41b | 975 | err.span_label( |
3dfed10e | 976 | self.session.source_map().guess_head_span(def_span), |
f035d41b XL |
977 | &format!( |
978 | "similarly named {} `{}` defined here", | |
979 | suggestion.res.descr(), | |
980 | suggestion.candidate.as_str(), | |
dfeec247 | 981 | ), |
f035d41b | 982 | ); |
e74abb32 | 983 | } |
3dfed10e XL |
984 | let msg = format!( |
985 | "{} {} with a similar name exists", | |
986 | suggestion.res.article(), | |
987 | suggestion.res.descr() | |
988 | ); | |
989 | err.span_suggestion( | |
990 | span, | |
991 | &msg, | |
992 | suggestion.candidate.to_string(), | |
993 | Applicability::MaybeIncorrect, | |
994 | ); | |
f035d41b | 995 | true |
e74abb32 | 996 | } |
dfeec247 XL |
997 | |
998 | fn binding_description(&self, b: &NameBinding<'_>, ident: Ident, from_prelude: bool) -> String { | |
999 | let res = b.res(); | |
1000 | if b.span.is_dummy() { | |
1001 | let add_built_in = match b.res() { | |
1002 | // These already contain the "built-in" prefix or look bad with it. | |
1003 | Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod => false, | |
1004 | _ => true, | |
1005 | }; | |
1006 | let (built_in, from) = if from_prelude { | |
1007 | ("", " from prelude") | |
1008 | } else if b.is_extern_crate() | |
1009 | && !b.is_import() | |
1010 | && self.session.opts.externs.get(&ident.as_str()).is_some() | |
1011 | { | |
1012 | ("", " passed with `--extern`") | |
1013 | } else if add_built_in { | |
1014 | (" built-in", "") | |
1015 | } else { | |
1016 | ("", "") | |
1017 | }; | |
1018 | ||
1019 | let article = if built_in.is_empty() { res.article() } else { "a" }; | |
1020 | format!( | |
1021 | "{a}{built_in} {thing}{from}", | |
1022 | a = article, | |
1023 | thing = res.descr(), | |
1024 | built_in = built_in, | |
1025 | from = from | |
1026 | ) | |
1027 | } else { | |
1028 | let introduced = if b.is_import() { "imported" } else { "defined" }; | |
1029 | format!("the {thing} {introduced} here", thing = res.descr(), introduced = introduced) | |
1030 | } | |
1031 | } | |
1032 | ||
1033 | crate fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError<'_>) { | |
1034 | let AmbiguityError { kind, ident, b1, b2, misc1, misc2 } = *ambiguity_error; | |
1035 | let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { | |
1036 | // We have to print the span-less alternative first, otherwise formatting looks bad. | |
1037 | (b2, b1, misc2, misc1, true) | |
1038 | } else { | |
1039 | (b1, b2, misc1, misc2, false) | |
1040 | }; | |
1041 | ||
1042 | let mut err = struct_span_err!( | |
1043 | self.session, | |
1044 | ident.span, | |
1045 | E0659, | |
1046 | "`{ident}` is ambiguous ({why})", | |
1047 | ident = ident, | |
1048 | why = kind.descr() | |
1049 | ); | |
1050 | err.span_label(ident.span, "ambiguous name"); | |
1051 | ||
1052 | let mut could_refer_to = |b: &NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| { | |
1053 | let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude); | |
1054 | let note_msg = format!( | |
1055 | "`{ident}` could{also} refer to {what}", | |
1056 | ident = ident, | |
1057 | also = also, | |
1058 | what = what | |
1059 | ); | |
1060 | ||
1061 | let thing = b.res().descr(); | |
1062 | let mut help_msgs = Vec::new(); | |
1063 | if b.is_glob_import() | |
1064 | && (kind == AmbiguityKind::GlobVsGlob | |
1065 | || kind == AmbiguityKind::GlobVsExpanded | |
1066 | || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty()) | |
1067 | { | |
1068 | help_msgs.push(format!( | |
1069 | "consider adding an explicit import of \ | |
1070 | `{ident}` to disambiguate", | |
1071 | ident = ident | |
1072 | )) | |
1073 | } | |
1074 | if b.is_extern_crate() && ident.span.rust_2018() { | |
1075 | help_msgs.push(format!( | |
1076 | "use `::{ident}` to refer to this {thing} unambiguously", | |
1077 | ident = ident, | |
1078 | thing = thing, | |
1079 | )) | |
1080 | } | |
1081 | if misc == AmbiguityErrorMisc::SuggestCrate { | |
1082 | help_msgs.push(format!( | |
1083 | "use `crate::{ident}` to refer to this {thing} unambiguously", | |
1084 | ident = ident, | |
1085 | thing = thing, | |
1086 | )) | |
1087 | } else if misc == AmbiguityErrorMisc::SuggestSelf { | |
1088 | help_msgs.push(format!( | |
1089 | "use `self::{ident}` to refer to this {thing} unambiguously", | |
1090 | ident = ident, | |
1091 | thing = thing, | |
1092 | )) | |
1093 | } | |
1094 | ||
1095 | err.span_note(b.span, ¬e_msg); | |
1096 | for (i, help_msg) in help_msgs.iter().enumerate() { | |
1097 | let or = if i == 0 { "" } else { "or " }; | |
1098 | err.help(&format!("{}{}", or, help_msg)); | |
1099 | } | |
1100 | }; | |
1101 | ||
1102 | could_refer_to(b1, misc1, ""); | |
1103 | could_refer_to(b2, misc2, " also"); | |
1104 | err.emit(); | |
1105 | } | |
1106 | ||
ba9703b0 XL |
1107 | /// If the binding refers to a tuple struct constructor with fields, |
1108 | /// returns the span of its fields. | |
1109 | fn ctor_fields_span(&self, binding: &NameBinding<'_>) -> Option<Span> { | |
1110 | if let NameBindingKind::Res( | |
dfeec247 XL |
1111 | Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id), |
1112 | _, | |
1113 | ) = binding.kind | |
1114 | { | |
1115 | let def_id = (&*self).parent(ctor_def_id).expect("no parent for a constructor"); | |
3dfed10e XL |
1116 | let fields = self.field_names.get(&def_id)?; |
1117 | let first_field = fields.first()?; // Handle `struct Foo()` | |
1118 | return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span))); | |
ba9703b0 XL |
1119 | } |
1120 | None | |
1121 | } | |
1122 | ||
1123 | crate fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) { | |
1124 | let PrivacyError { ident, binding, .. } = *privacy_error; | |
1125 | ||
1126 | let res = binding.res(); | |
1127 | let ctor_fields_span = self.ctor_fields_span(binding); | |
1128 | let plain_descr = res.descr().to_string(); | |
1129 | let nonimport_descr = | |
1130 | if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; | |
1131 | let import_descr = nonimport_descr.clone() + " import"; | |
1132 | let get_descr = | |
1133 | |b: &NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; | |
1134 | ||
1135 | // Print the primary message. | |
1136 | let descr = get_descr(binding); | |
1137 | let mut err = | |
1138 | struct_span_err!(self.session, ident.span, E0603, "{} `{}` is private", descr, ident); | |
1139 | err.span_label(ident.span, &format!("private {}", descr)); | |
1140 | if let Some(span) = ctor_fields_span { | |
1141 | err.span_label(span, "a constructor is private if any of the fields is private"); | |
1142 | } | |
1143 | ||
1144 | // Print the whole import chain to make it easier to see what happens. | |
1145 | let first_binding = binding; | |
1146 | let mut next_binding = Some(binding); | |
1147 | let mut next_ident = ident; | |
1148 | while let Some(binding) = next_binding { | |
1149 | let name = next_ident; | |
1150 | next_binding = match binding.kind { | |
1151 | _ if res == Res::Err => None, | |
1152 | NameBindingKind::Import { binding, import, .. } => match import.kind { | |
1153 | _ if binding.span.is_dummy() => None, | |
1154 | ImportKind::Single { source, .. } => { | |
1155 | next_ident = source; | |
1156 | Some(binding) | |
1157 | } | |
1158 | ImportKind::Glob { .. } | ImportKind::MacroUse => Some(binding), | |
1159 | ImportKind::ExternCrate { .. } => None, | |
1160 | }, | |
1161 | _ => None, | |
1162 | }; | |
1163 | ||
1164 | let first = ptr::eq(binding, first_binding); | |
1165 | let descr = get_descr(binding); | |
1166 | let msg = format!( | |
1167 | "{and_refers_to}the {item} `{name}`{which} is defined here{dots}", | |
1168 | and_refers_to = if first { "" } else { "...and refers to " }, | |
1169 | item = descr, | |
1170 | name = name, | |
1171 | which = if first { "" } else { " which" }, | |
1172 | dots = if next_binding.is_some() { "..." } else { "" }, | |
1173 | ); | |
1174 | let def_span = self.session.source_map().guess_head_span(binding.span); | |
1175 | let mut note_span = MultiSpan::from_span(def_span); | |
1176 | if !first && binding.vis == ty::Visibility::Public { | |
1177 | note_span.push_span_label(def_span, "consider importing it directly".into()); | |
1178 | } | |
1179 | err.span_note(note_span, &msg); | |
1180 | } | |
dfeec247 XL |
1181 | |
1182 | err.emit(); | |
1183 | } | |
5bcae85e SL |
1184 | } |
1185 | ||
dc9dc135 | 1186 | impl<'a, 'b> ImportResolver<'a, 'b> { |
48663c56 XL |
1187 | /// Adds suggestions for a path that cannot be resolved. |
1188 | pub(crate) fn make_path_suggestion( | |
1189 | &mut self, | |
1190 | span: Span, | |
1191 | mut path: Vec<Segment>, | |
1192 | parent_scope: &ParentScope<'b>, | |
1193 | ) -> Option<(Vec<Segment>, Vec<String>)> { | |
1194 | debug!("make_path_suggestion: span={:?} path={:?}", span, path); | |
1195 | ||
1196 | match (path.get(0), path.get(1)) { | |
1197 | // `{{root}}::ident::...` on both editions. | |
1198 | // On 2015 `{{root}}` is usually added implicitly. | |
dfeec247 XL |
1199 | (Some(fst), Some(snd)) |
1200 | if fst.ident.name == kw::PathRoot && !snd.ident.is_path_segment_keyword() => {} | |
48663c56 | 1201 | // `ident::...` on 2018. |
dfeec247 XL |
1202 | (Some(fst), _) |
1203 | if fst.ident.span.rust_2018() && !fst.ident.is_path_segment_keyword() => | |
1204 | { | |
48663c56 | 1205 | // Insert a placeholder that's later replaced by `self`/`super`/etc. |
dc9dc135 | 1206 | path.insert(0, Segment::from_ident(Ident::invalid())); |
48663c56 XL |
1207 | } |
1208 | _ => return None, | |
1209 | } | |
3157f602 | 1210 | |
48663c56 XL |
1211 | self.make_missing_self_suggestion(span, path.clone(), parent_scope) |
1212 | .or_else(|| self.make_missing_crate_suggestion(span, path.clone(), parent_scope)) | |
1213 | .or_else(|| self.make_missing_super_suggestion(span, path.clone(), parent_scope)) | |
1214 | .or_else(|| self.make_external_crate_suggestion(span, path, parent_scope)) | |
92a42be0 | 1215 | } |
9cc50fc6 | 1216 | |
48663c56 XL |
1217 | /// Suggest a missing `self::` if that resolves to an correct module. |
1218 | /// | |
ba9703b0 | 1219 | /// ```text |
48663c56 XL |
1220 | /// | |
1221 | /// LL | use foo::Bar; | |
1222 | /// | ^^^ did you mean `self::foo`? | |
1223 | /// ``` | |
1224 | fn make_missing_self_suggestion( | |
1225 | &mut self, | |
1226 | span: Span, | |
1227 | mut path: Vec<Segment>, | |
1228 | parent_scope: &ParentScope<'b>, | |
1229 | ) -> Option<(Vec<Segment>, Vec<String>)> { | |
1230 | // Replace first ident with `self` and check if that is valid. | |
dc9dc135 | 1231 | path[0].ident.name = kw::SelfLower; |
416331ca | 1232 | let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); |
48663c56 | 1233 | debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); |
dfeec247 | 1234 | if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } |
9cc50fc6 | 1235 | } |
92a42be0 | 1236 | |
48663c56 XL |
1237 | /// Suggests a missing `crate::` if that resolves to an correct module. |
1238 | /// | |
f9f354fc | 1239 | /// ```text |
48663c56 XL |
1240 | /// | |
1241 | /// LL | use foo::Bar; | |
1242 | /// | ^^^ did you mean `crate::foo`? | |
1243 | /// ``` | |
1244 | fn make_missing_crate_suggestion( | |
1245 | &mut self, | |
1246 | span: Span, | |
1247 | mut path: Vec<Segment>, | |
1248 | parent_scope: &ParentScope<'b>, | |
1249 | ) -> Option<(Vec<Segment>, Vec<String>)> { | |
1250 | // Replace first ident with `crate` and check if that is valid. | |
dc9dc135 | 1251 | path[0].ident.name = kw::Crate; |
416331ca | 1252 | let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); |
48663c56 XL |
1253 | debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); |
1254 | if let PathResult::Module(..) = result { | |
1255 | Some(( | |
1256 | path, | |
1257 | vec![ | |
1258 | "`use` statements changed in Rust 2018; read more at \ | |
1259 | <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\ | |
dfeec247 XL |
1260 | clarity.html>" |
1261 | .to_string(), | |
48663c56 XL |
1262 | ], |
1263 | )) | |
1264 | } else { | |
1265 | None | |
1266 | } | |
1267 | } | |
92a42be0 | 1268 | |
48663c56 XL |
1269 | /// Suggests a missing `super::` if that resolves to an correct module. |
1270 | /// | |
ba9703b0 | 1271 | /// ```text |
48663c56 XL |
1272 | /// | |
1273 | /// LL | use foo::Bar; | |
1274 | /// | ^^^ did you mean `super::foo`? | |
1275 | /// ``` | |
1276 | fn make_missing_super_suggestion( | |
1277 | &mut self, | |
1278 | span: Span, | |
1279 | mut path: Vec<Segment>, | |
1280 | parent_scope: &ParentScope<'b>, | |
1281 | ) -> Option<(Vec<Segment>, Vec<String>)> { | |
1282 | // Replace first ident with `crate` and check if that is valid. | |
dc9dc135 | 1283 | path[0].ident.name = kw::Super; |
416331ca | 1284 | let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); |
48663c56 | 1285 | debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); |
dfeec247 | 1286 | if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } |
48663c56 | 1287 | } |
92a42be0 | 1288 | |
48663c56 XL |
1289 | /// Suggests a missing external crate name if that resolves to an correct module. |
1290 | /// | |
ba9703b0 | 1291 | /// ```text |
48663c56 XL |
1292 | /// | |
1293 | /// LL | use foobar::Baz; | |
1294 | /// | ^^^^^^ did you mean `baz::foobar`? | |
1295 | /// ``` | |
1296 | /// | |
1297 | /// Used when importing a submodule of an external crate but missing that crate's | |
1298 | /// name as the first part of path. | |
1299 | fn make_external_crate_suggestion( | |
1300 | &mut self, | |
1301 | span: Span, | |
1302 | mut path: Vec<Segment>, | |
1303 | parent_scope: &ParentScope<'b>, | |
1304 | ) -> Option<(Vec<Segment>, Vec<String>)> { | |
1305 | if path[1].ident.span.rust_2015() { | |
1306 | return None; | |
1307 | } | |
92a42be0 | 1308 | |
48663c56 | 1309 | // Sort extern crate names in reverse order to get |
74b04a01 | 1310 | // 1) some consistent ordering for emitted diagnostics, and |
48663c56 XL |
1311 | // 2) `std` suggestions before `core` suggestions. |
1312 | let mut extern_crate_names = | |
416331ca | 1313 | self.r.extern_prelude.iter().map(|(ident, _)| ident.name).collect::<Vec<_>>(); |
48663c56 XL |
1314 | extern_crate_names.sort_by_key(|name| Reverse(name.as_str())); |
1315 | ||
1316 | for name in extern_crate_names.into_iter() { | |
1317 | // Replace first ident with a crate name and check if that is valid. | |
1318 | path[0].ident.name = name; | |
416331ca | 1319 | let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); |
dfeec247 XL |
1320 | debug!( |
1321 | "make_external_crate_suggestion: name={:?} path={:?} result={:?}", | |
1322 | name, path, result | |
1323 | ); | |
48663c56 XL |
1324 | if let PathResult::Module(..) = result { |
1325 | return Some((path, Vec::new())); | |
1326 | } | |
1327 | } | |
92a42be0 | 1328 | |
48663c56 | 1329 | None |
92a42be0 | 1330 | } |
92a42be0 | 1331 | |
48663c56 XL |
1332 | /// Suggests importing a macro from the root of the crate rather than a module within |
1333 | /// the crate. | |
1334 | /// | |
f9f354fc | 1335 | /// ```text |
48663c56 XL |
1336 | /// help: a macro with this name exists at the root of the crate |
1337 | /// | | |
1338 | /// LL | use issue_59764::makro; | |
1339 | /// | ^^^^^^^^^^^^^^^^^^ | |
1340 | /// | | |
1341 | /// = note: this could be because a macro annotated with `#[macro_export]` will be exported | |
1342 | /// at the root of the crate instead of the module where it is defined | |
1343 | /// ``` | |
1344 | pub(crate) fn check_for_module_export_macro( | |
e1599b0c | 1345 | &mut self, |
74b04a01 | 1346 | import: &'b Import<'b>, |
48663c56 XL |
1347 | module: ModuleOrUniformRoot<'b>, |
1348 | ident: Ident, | |
1349 | ) -> Option<(Option<Suggestion>, Vec<String>)> { | |
1350 | let mut crate_module = if let ModuleOrUniformRoot::Module(module) = module { | |
1351 | module | |
1352 | } else { | |
1353 | return None; | |
1354 | }; | |
1355 | ||
1356 | while let Some(parent) = crate_module.parent { | |
1357 | crate_module = parent; | |
1358 | } | |
9cc50fc6 | 1359 | |
48663c56 XL |
1360 | if ModuleOrUniformRoot::same_def(ModuleOrUniformRoot::Module(crate_module), module) { |
1361 | // Don't make a suggestion if the import was already from the root of the | |
1362 | // crate. | |
1363 | return None; | |
1364 | } | |
92a42be0 | 1365 | |
e1599b0c | 1366 | let resolutions = self.r.resolutions(crate_module).borrow(); |
e74abb32 | 1367 | let resolution = resolutions.get(&self.r.new_key(ident, MacroNS))?; |
48663c56 XL |
1368 | let binding = resolution.borrow().binding()?; |
1369 | if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() { | |
1370 | let module_name = crate_module.kind.name().unwrap(); | |
74b04a01 XL |
1371 | let import_snippet = match import.kind { |
1372 | ImportKind::Single { source, target, .. } if source != target => { | |
dfeec247 XL |
1373 | format!("{} as {}", source, target) |
1374 | } | |
48663c56 XL |
1375 | _ => format!("{}", ident), |
1376 | }; | |
1377 | ||
1378 | let mut corrections: Vec<(Span, String)> = Vec::new(); | |
74b04a01 | 1379 | if !import.is_nested() { |
48663c56 XL |
1380 | // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove |
1381 | // intermediate segments. | |
74b04a01 | 1382 | corrections.push((import.span, format!("{}::{}", module_name, import_snippet))); |
48663c56 XL |
1383 | } else { |
1384 | // Find the binding span (and any trailing commas and spaces). | |
1385 | // ie. `use a::b::{c, d, e};` | |
1386 | // ^^^ | |
1387 | let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( | |
dfeec247 | 1388 | self.r.session, |
74b04a01 XL |
1389 | import.span, |
1390 | import.use_span, | |
dfeec247 XL |
1391 | ); |
1392 | debug!( | |
1393 | "check_for_module_export_macro: found_closing_brace={:?} binding_span={:?}", | |
1394 | found_closing_brace, binding_span | |
48663c56 | 1395 | ); |
48663c56 XL |
1396 | |
1397 | let mut removal_span = binding_span; | |
1398 | if found_closing_brace { | |
1399 | // If the binding span ended with a closing brace, as in the below example: | |
1400 | // ie. `use a::b::{c, d};` | |
1401 | // ^ | |
1402 | // Then expand the span of characters to remove to include the previous | |
1403 | // binding's trailing comma. | |
1404 | // ie. `use a::b::{c, d};` | |
1405 | // ^^^ | |
dfeec247 XL |
1406 | if let Some(previous_span) = |
1407 | extend_span_to_previous_binding(self.r.session, binding_span) | |
1408 | { | |
48663c56 XL |
1409 | debug!("check_for_module_export_macro: previous_span={:?}", previous_span); |
1410 | removal_span = removal_span.with_lo(previous_span.lo()); | |
1411 | } | |
1412 | } | |
1413 | debug!("check_for_module_export_macro: removal_span={:?}", removal_span); | |
1414 | ||
1415 | // Remove the `removal_span`. | |
1416 | corrections.push((removal_span, "".to_string())); | |
1417 | ||
1418 | // Find the span after the crate name and if it has nested imports immediatately | |
1419 | // after the crate name already. | |
1420 | // ie. `use a::b::{c, d};` | |
1421 | // ^^^^^^^^^ | |
1422 | // or `use a::{b, c, d}};` | |
1423 | // ^^^^^^^^^^^ | |
1424 | let (has_nested, after_crate_name) = find_span_immediately_after_crate_name( | |
dfeec247 XL |
1425 | self.r.session, |
1426 | module_name, | |
74b04a01 | 1427 | import.use_span, |
dfeec247 XL |
1428 | ); |
1429 | debug!( | |
1430 | "check_for_module_export_macro: has_nested={:?} after_crate_name={:?}", | |
1431 | has_nested, after_crate_name | |
48663c56 | 1432 | ); |
48663c56 | 1433 | |
416331ca | 1434 | let source_map = self.r.session.source_map(); |
48663c56 XL |
1435 | |
1436 | // Add the import to the start, with a `{` if required. | |
1437 | let start_point = source_map.start_point(after_crate_name); | |
1438 | if let Ok(start_snippet) = source_map.span_to_snippet(start_point) { | |
1439 | corrections.push(( | |
1440 | start_point, | |
1441 | if has_nested { | |
1442 | // In this case, `start_snippet` must equal '{'. | |
74b04a01 | 1443 | format!("{}{}, ", start_snippet, import_snippet) |
48663c56 XL |
1444 | } else { |
1445 | // In this case, add a `{`, then the moved import, then whatever | |
1446 | // was there before. | |
74b04a01 | 1447 | format!("{{{}, {}", import_snippet, start_snippet) |
dfeec247 | 1448 | }, |
48663c56 XL |
1449 | )); |
1450 | } | |
1451 | ||
1452 | // Add a `};` to the end if nested, matching the `{` added at the start. | |
1453 | if !has_nested { | |
dfeec247 | 1454 | corrections.push((source_map.end_point(after_crate_name), "};".to_string())); |
48663c56 XL |
1455 | } |
1456 | } | |
1457 | ||
1458 | let suggestion = Some(( | |
1459 | corrections, | |
1460 | String::from("a macro with this name exists at the root of the crate"), | |
1461 | Applicability::MaybeIncorrect, | |
1462 | )); | |
1463 | let note = vec![ | |
1464 | "this could be because a macro annotated with `#[macro_export]` will be exported \ | |
dfeec247 XL |
1465 | at the root of the crate instead of the module where it is defined" |
1466 | .to_string(), | |
48663c56 XL |
1467 | ]; |
1468 | Some((suggestion, note)) | |
1469 | } else { | |
1470 | None | |
1471 | } | |
92a42be0 | 1472 | } |
92a42be0 | 1473 | } |
92a42be0 | 1474 | |
48663c56 XL |
1475 | /// Given a `binding_span` of a binding within a use statement: |
1476 | /// | |
1477 | /// ``` | |
1478 | /// use foo::{a, b, c}; | |
1479 | /// ^ | |
1480 | /// ``` | |
1481 | /// | |
1482 | /// then return the span until the next binding or the end of the statement: | |
1483 | /// | |
1484 | /// ``` | |
1485 | /// use foo::{a, b, c}; | |
1486 | /// ^^^ | |
1487 | /// ``` | |
1488 | pub(crate) fn find_span_of_binding_until_next_binding( | |
1489 | sess: &Session, | |
1490 | binding_span: Span, | |
1491 | use_span: Span, | |
1492 | ) -> (bool, Span) { | |
1493 | let source_map = sess.source_map(); | |
1494 | ||
1495 | // Find the span of everything after the binding. | |
1496 | // ie. `a, e};` or `a};` | |
1497 | let binding_until_end = binding_span.with_hi(use_span.hi()); | |
1498 | ||
1499 | // Find everything after the binding but not including the binding. | |
1500 | // ie. `, e};` or `};` | |
1501 | let after_binding_until_end = binding_until_end.with_lo(binding_span.hi()); | |
1502 | ||
1503 | // Keep characters in the span until we encounter something that isn't a comma or | |
1504 | // whitespace. | |
1505 | // ie. `, ` or ``. | |
1506 | // | |
1507 | // Also note whether a closing brace character was encountered. If there | |
1508 | // was, then later go backwards to remove any trailing commas that are left. | |
1509 | let mut found_closing_brace = false; | |
dfeec247 XL |
1510 | let after_binding_until_next_binding = |
1511 | source_map.span_take_while(after_binding_until_end, |&ch| { | |
1512 | if ch == '}' { | |
1513 | found_closing_brace = true; | |
1514 | } | |
48663c56 | 1515 | ch == ' ' || ch == ',' |
dfeec247 | 1516 | }); |
48663c56 XL |
1517 | |
1518 | // Combine the two spans. | |
1519 | // ie. `a, ` or `a`. | |
1520 | // | |
1521 | // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };` | |
1522 | let span = binding_span.with_hi(after_binding_until_next_binding.hi()); | |
1523 | ||
1524 | (found_closing_brace, span) | |
1525 | } | |
1526 | ||
1527 | /// Given a `binding_span`, return the span through to the comma or opening brace of the previous | |
1528 | /// binding. | |
1529 | /// | |
1530 | /// ``` | |
1531 | /// use foo::a::{a, b, c}; | |
1532 | /// ^^--- binding span | |
1533 | /// | | |
1534 | /// returned span | |
1535 | /// | |
1536 | /// use foo::{a, b, c}; | |
1537 | /// --- binding span | |
1538 | /// ``` | |
dfeec247 | 1539 | pub(crate) fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option<Span> { |
48663c56 XL |
1540 | let source_map = sess.source_map(); |
1541 | ||
1542 | // `prev_source` will contain all of the source that came before the span. | |
1543 | // Then split based on a command and take the first (ie. closest to our span) | |
1544 | // snippet. In the example, this is a space. | |
1545 | let prev_source = source_map.span_to_prev_source(binding_span).ok()?; | |
1546 | ||
1547 | let prev_comma = prev_source.rsplit(',').collect::<Vec<_>>(); | |
1548 | let prev_starting_brace = prev_source.rsplit('{').collect::<Vec<_>>(); | |
1549 | if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 { | |
1550 | return None; | |
9cc50fc6 | 1551 | } |
92a42be0 | 1552 | |
48663c56 XL |
1553 | let prev_comma = prev_comma.first().unwrap(); |
1554 | let prev_starting_brace = prev_starting_brace.first().unwrap(); | |
7453a54e | 1555 | |
48663c56 XL |
1556 | // If the amount of source code before the comma is greater than |
1557 | // the amount of source code before the starting brace then we've only | |
1558 | // got one item in the nested item (eg. `issue_52891::{self}`). | |
1559 | if prev_comma.len() > prev_starting_brace.len() { | |
1560 | return None; | |
92a42be0 | 1561 | } |
a7813a04 | 1562 | |
48663c56 XL |
1563 | Some(binding_span.with_lo(BytePos( |
1564 | // Take away the number of bytes for the characters we've found and an | |
1565 | // extra for the comma. | |
dfeec247 | 1566 | binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1, |
48663c56 XL |
1567 | ))) |
1568 | } | |
1569 | ||
1570 | /// Given a `use_span` of a binding within a use statement, returns the highlighted span and if | |
1571 | /// it is a nested use tree. | |
1572 | /// | |
1573 | /// ``` | |
1574 | /// use foo::a::{b, c}; | |
1575 | /// ^^^^^^^^^^ // false | |
1576 | /// | |
1577 | /// use foo::{a, b, c}; | |
1578 | /// ^^^^^^^^^^ // true | |
1579 | /// | |
1580 | /// use foo::{a, b::{c, d}}; | |
1581 | /// ^^^^^^^^^^^^^^^ // true | |
1582 | /// ``` | |
1583 | fn find_span_immediately_after_crate_name( | |
1584 | sess: &Session, | |
1585 | module_name: Symbol, | |
1586 | use_span: Span, | |
1587 | ) -> (bool, Span) { | |
dfeec247 XL |
1588 | debug!( |
1589 | "find_span_immediately_after_crate_name: module_name={:?} use_span={:?}", | |
1590 | module_name, use_span | |
1591 | ); | |
48663c56 XL |
1592 | let source_map = sess.source_map(); |
1593 | ||
1594 | // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. | |
1595 | let mut num_colons = 0; | |
1596 | // Find second colon.. `use issue_59764:` | |
1597 | let until_second_colon = source_map.span_take_while(use_span, |c| { | |
dfeec247 XL |
1598 | if *c == ':' { |
1599 | num_colons += 1; | |
1600 | } | |
48663c56 XL |
1601 | match c { |
1602 | ':' if num_colons == 2 => false, | |
1603 | _ => true, | |
a7813a04 | 1604 | } |
48663c56 XL |
1605 | }); |
1606 | // Find everything after the second colon.. `foo::{baz, makro};` | |
1607 | let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1)); | |
1608 | ||
1609 | let mut found_a_non_whitespace_character = false; | |
1610 | // Find the first non-whitespace character in `from_second_colon`.. `f` | |
1611 | let after_second_colon = source_map.span_take_while(from_second_colon, |c| { | |
dfeec247 XL |
1612 | if found_a_non_whitespace_character { |
1613 | return false; | |
1614 | } | |
1615 | if !c.is_whitespace() { | |
1616 | found_a_non_whitespace_character = true; | |
1617 | } | |
48663c56 XL |
1618 | true |
1619 | }); | |
1620 | ||
1621 | // Find the first `{` in from_second_colon.. `foo::{` | |
1622 | let next_left_bracket = source_map.span_through_char(from_second_colon, '{'); | |
1623 | ||
1624 | (next_left_bracket == after_second_colon, from_second_colon) | |
85aaf69f | 1625 | } |
416331ca XL |
1626 | |
1627 | /// When an entity with a given name is not available in scope, we search for | |
1628 | /// entities with that name in all crates. This method allows outputting the | |
1629 | /// results of this search in a programmer-friendly way | |
1630 | crate fn show_candidates( | |
1631 | err: &mut DiagnosticBuilder<'_>, | |
1632 | // This is `None` if all placement locations are inside expansions | |
f9f354fc | 1633 | use_placement_span: Option<Span>, |
416331ca | 1634 | candidates: &[ImportSuggestion], |
f035d41b | 1635 | instead: bool, |
416331ca XL |
1636 | found_use: bool, |
1637 | ) { | |
dfeec247 XL |
1638 | if candidates.is_empty() { |
1639 | return; | |
1640 | } | |
f9f354fc | 1641 | |
416331ca XL |
1642 | // we want consistent results across executions, but candidates are produced |
1643 | // by iterating through a hash map, so make sure they are ordered: | |
1644 | let mut path_strings: Vec<_> = | |
74b04a01 | 1645 | candidates.iter().map(|c| path_names_to_string(&c.path)).collect(); |
f035d41b | 1646 | |
416331ca | 1647 | path_strings.sort(); |
dfeec247 | 1648 | path_strings.dedup(); |
416331ca | 1649 | |
f9f354fc XL |
1650 | let (determiner, kind) = if candidates.len() == 1 { |
1651 | ("this", candidates[0].descr) | |
1652 | } else { | |
1653 | ("one of these", "items") | |
416331ca | 1654 | }; |
f035d41b XL |
1655 | |
1656 | let instead = if instead { " instead" } else { "" }; | |
1657 | let mut msg = format!("consider importing {} {}{}", determiner, kind, instead); | |
416331ca | 1658 | |
f9f354fc | 1659 | if let Some(span) = use_placement_span { |
416331ca XL |
1660 | for candidate in &mut path_strings { |
1661 | // produce an additional newline to separate the new use statement | |
1662 | // from the directly following item. | |
dfeec247 | 1663 | let additional_newline = if found_use { "" } else { "\n" }; |
416331ca XL |
1664 | *candidate = format!("use {};\n{}", candidate, additional_newline); |
1665 | } | |
1666 | ||
dfeec247 | 1667 | err.span_suggestions(span, &msg, path_strings.into_iter(), Applicability::Unspecified); |
416331ca | 1668 | } else { |
416331ca | 1669 | msg.push(':'); |
f035d41b | 1670 | |
416331ca XL |
1671 | for candidate in path_strings { |
1672 | msg.push('\n'); | |
1673 | msg.push_str(&candidate); | |
1674 | } | |
f035d41b | 1675 | |
dfeec247 XL |
1676 | err.note(&msg); |
1677 | } | |
1678 | } |