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