Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 1 | use std::cmp::Reverse; |
| 2 | use std::ptr; |
| 3 | |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 4 | use rustc_ast::{self as ast, Path}; |
| 5 | use rustc_ast_pretty::pprust; |
| 6 | use rustc_data_structures::fx::FxHashSet; |
| 7 | use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; |
| 8 | use rustc_feature::BUILTIN_ATTRIBUTES; |
| 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}; |
Chris Wailes | e3116c4 | 2021-07-13 14:40:48 -0700 | [diff] [blame] | 12 | use rustc_hir::PrimTy; |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 13 | use rustc_middle::bug; |
| 14 | use rustc_middle::ty::{self, DefIdTree}; |
| 15 | use rustc_session::Session; |
| 16 | use rustc_span::hygiene::MacroKind; |
Jeff Vander Stoep | d59a287 | 2021-02-15 10:22:21 +0100 | [diff] [blame] | 17 | use rustc_span::lev_distance::find_best_match_for_name; |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 18 | use rustc_span::source_map::SourceMap; |
| 19 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
| 20 | use rustc_span::{BytePos, MultiSpan, Span}; |
| 21 | use tracing::debug; |
| 22 | |
| 23 | use crate::imports::{Import, ImportKind, ImportResolver}; |
| 24 | use crate::path_names_to_string; |
| 25 | use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; |
| 26 | use crate::{ |
| 27 | BindingError, CrateLint, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot, |
| 28 | }; |
| 29 | use crate::{NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; |
| 30 | use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet, Segment}; |
| 31 | |
| 32 | type Res = def::Res<ast::NodeId>; |
| 33 | |
| 34 | /// A vector of spans and replacements, a message and applicability. |
| 35 | crate type Suggestion = (Vec<(Span, String)>, String, Applicability); |
| 36 | |
| 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 | |
| 41 | crate struct TypoSuggestion { |
| 42 | pub candidate: Symbol, |
| 43 | pub res: Res, |
| 44 | } |
| 45 | |
| 46 | impl TypoSuggestion { |
| 47 | crate fn from_res(candidate: Symbol, res: Res) -> TypoSuggestion { |
| 48 | TypoSuggestion { candidate, res } |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | /// A free importable items suggested in case of resolution failure. |
| 53 | crate struct ImportSuggestion { |
| 54 | pub did: Option<DefId>, |
| 55 | pub descr: &'static str, |
| 56 | pub path: Path, |
| 57 | pub accessible: bool, |
| 58 | } |
| 59 | |
| 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. |
| 67 | fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { |
| 68 | let impl_span = sm.span_until_char(impl_span, '<'); |
| 69 | sm.span_until_whitespace(impl_span) |
| 70 | } |
| 71 | |
| 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 | ) { |
| 79 | for (key, resolution) in self.resolutions(module).borrow().iter() { |
| 80 | if let Some(binding) = resolution.borrow().binding { |
| 81 | let res = binding.res(); |
| 82 | if filter_fn(res) { |
| 83 | names.push(TypoSuggestion::from_res(key.ident.name, res)); |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 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 | } |
| 96 | |
| 97 | crate fn into_struct_error( |
| 98 | &self, |
| 99 | span: Span, |
| 100 | resolution_error: ResolutionError<'_>, |
| 101 | ) -> DiagnosticBuilder<'_> { |
| 102 | match resolution_error { |
| 103 | ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => { |
| 104 | let mut err = struct_span_err!( |
| 105 | self.session, |
| 106 | span, |
| 107 | E0401, |
| 108 | "can't use generic parameters from outer function", |
| 109 | ); |
| 110 | err.span_label(span, "use of generic parameter from outer function".to_string()); |
| 111 | |
| 112 | let sm = self.session.source_map(); |
| 113 | match outer_res { |
| 114 | Res::SelfTy(maybe_trait_defid, maybe_impl_defid) => { |
| 115 | if let Some(impl_span) = |
| 116 | maybe_impl_defid.and_then(|(def_id, _)| self.opt_span(def_id)) |
| 117 | { |
| 118 | err.span_label( |
| 119 | reduce_impl_span_to_impl_keyword(sm, impl_span), |
| 120 | "`Self` type implicitly declared here, by this `impl`", |
| 121 | ); |
| 122 | } |
| 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; |
| 133 | } |
| 134 | Res::Def(DefKind::TyParam, def_id) => { |
| 135 | if let Some(span) = self.opt_span(def_id) { |
| 136 | err.span_label(span, "type parameter from outer function"); |
| 137 | } |
| 138 | } |
| 139 | Res::Def(DefKind::ConstParam, def_id) => { |
| 140 | if let Some(span) = self.opt_span(def_id) { |
| 141 | err.span_label(span, "const parameter from outer function"); |
| 142 | } |
| 143 | } |
| 144 | _ => { |
| 145 | bug!( |
| 146 | "GenericParamsFromOuterFunction should only be used with Res::SelfTy, \ |
Jeff Vander Stoep | d59a287 | 2021-02-15 10:22:21 +0100 | [diff] [blame] | 147 | DefKind::TyParam or DefKind::ConstParam" |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 148 | ); |
| 149 | } |
| 150 | } |
| 151 | |
| 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. |
| 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) { |
| 157 | // Suggest the modification to the user |
| 158 | err.span_suggestion( |
| 159 | sugg_span, |
| 160 | sugg_msg, |
| 161 | snippet, |
| 162 | Applicability::MachineApplicable, |
| 163 | ); |
| 164 | } else if let Some(sp) = sm.generate_fn_name_span(span) { |
| 165 | err.span_label( |
| 166 | sp, |
| 167 | "try adding a local generic parameter in this method instead" |
| 168 | .to_string(), |
| 169 | ); |
| 170 | } else { |
| 171 | err.help("try using a local generic parameter instead"); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | err |
| 176 | } |
| 177 | ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { |
| 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 | ); |
| 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_) => { |
| 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 | ); |
| 199 | err.span_label(span, format!("not a member of trait `{}`", trait_)); |
| 200 | err |
| 201 | } |
| 202 | ResolutionError::TypeNotMemberOfTrait(type_, trait_) => { |
| 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 | ); |
| 211 | err.span_label(span, format!("not a member of trait `{}`", trait_)); |
| 212 | err |
| 213 | } |
| 214 | ResolutionError::ConstNotMemberOfTrait(const_, trait_) => { |
| 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 | ); |
| 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; |
| 228 | |
| 229 | let target_sp = target.iter().copied().collect::<Vec<_>>(); |
| 230 | let origin_sp = origin.iter().copied().collect::<Vec<_>>(); |
| 231 | |
| 232 | let msp = MultiSpan::from_spans(target_sp.clone()); |
| 233 | let mut err = struct_span_err!( |
| 234 | self.session, |
| 235 | msp, |
| 236 | E0408, |
| 237 | "variable `{}` is not bound in all patterns", |
| 238 | name, |
| 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: `?::{}`", |
| 250 | name, |
| 251 | ); |
| 252 | err.span_help(span, &help_msg); |
| 253 | } |
| 254 | err |
| 255 | } |
| 256 | ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { |
| 257 | let mut err = struct_span_err!( |
| 258 | self.session, |
| 259 | span, |
| 260 | E0409, |
| 261 | "variable `{}` is bound inconsistently across alternatives separated by `|`", |
| 262 | variable_name |
| 263 | ); |
| 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) => { |
| 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 | ); |
| 276 | err.span_label(span, "used as parameter more than once"); |
| 277 | err |
| 278 | } |
| 279 | ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { |
| 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 | ); |
| 287 | err.span_label(span, "used in a pattern more than once"); |
| 288 | err |
| 289 | } |
| 290 | ResolutionError::UndeclaredLabel { name, suggestion } => { |
| 291 | let mut err = struct_span_err!( |
| 292 | self.session, |
| 293 | span, |
| 294 | E0426, |
| 295 | "use of undeclared label `{}`", |
| 296 | name |
| 297 | ); |
| 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 => (), |
| 321 | } |
| 322 | |
| 323 | err |
| 324 | } |
| 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 | } |
| 359 | ResolutionError::SelfImportCanOnlyAppearOnceInTheList => { |
| 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 | ); |
| 366 | err.span_label(span, "can only appear once in an import list"); |
| 367 | err |
| 368 | } |
| 369 | ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => { |
| 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 | ); |
| 377 | err.span_label(span, "can only appear in an import list with a non-empty prefix"); |
| 378 | err |
| 379 | } |
| 380 | ResolutionError::FailedToResolve { label, suggestion } => { |
| 381 | let mut err = |
| 382 | struct_span_err!(self.session, span, E0433, "failed to resolve: {}", &label); |
| 383 | err.span_label(span, label); |
| 384 | |
| 385 | if let Some((suggestions, msg, applicability)) = suggestion { |
| 386 | err.multipart_suggestion(&msg, suggestions, applicability); |
| 387 | } |
| 388 | |
| 389 | err |
| 390 | } |
| 391 | ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { |
| 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 | ); |
| 399 | err.help("use the `|| { ... }` closure form instead"); |
| 400 | err |
| 401 | } |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 402 | ResolutionError::AttemptToUseNonConstantValueInConstant(ident, sugg, current) => { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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 | ); |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 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 | } |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 426 | err |
| 427 | } |
Chris Wailes | 2f3fdfe | 2021-07-29 10:56:18 -0700 | [diff] [blame] | 428 | ResolutionError::BindingShadowsSomethingUnacceptable { |
| 429 | shadowing_binding_descr, |
| 430 | name, |
| 431 | participle, |
| 432 | article, |
| 433 | shadowed_binding_descr, |
| 434 | shadowed_binding_span, |
| 435 | } => { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 436 | let mut err = struct_span_err!( |
| 437 | self.session, |
| 438 | span, |
| 439 | E0530, |
| 440 | "{}s cannot shadow {}s", |
Chris Wailes | 2f3fdfe | 2021-07-29 10:56:18 -0700 | [diff] [blame] | 441 | shadowing_binding_descr, |
| 442 | shadowed_binding_descr, |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 443 | ); |
| 444 | err.span_label( |
| 445 | span, |
Chris Wailes | 2f3fdfe | 2021-07-29 10:56:18 -0700 | [diff] [blame] | 446 | format!("cannot be named the same as {} {}", article, shadowed_binding_descr), |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 447 | ); |
Chris Wailes | 2f3fdfe | 2021-07-29 10:56:18 -0700 | [diff] [blame] | 448 | let msg = |
| 449 | format!("the {} `{}` is {} here", shadowed_binding_descr, name, participle); |
| 450 | err.span_label(shadowed_binding_span, msg); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 451 | err |
| 452 | } |
Chris Wailes | 2f3fdfe | 2021-07-29 10:56:18 -0700 | [diff] [blame] | 453 | ResolutionError::ForwardDeclaredGenericParam => { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 454 | let mut err = struct_span_err!( |
| 455 | self.session, |
| 456 | span, |
| 457 | E0128, |
Chris Wailes | 32f7835 | 2021-07-20 14:04:55 -0700 | [diff] [blame] | 458 | "generic parameters with a default cannot use \ |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 459 | forward declared identifiers" |
| 460 | ); |
| 461 | err.span_label( |
| 462 | span, |
Chris Wailes | 32f7835 | 2021-07-20 14:04:55 -0700 | [diff] [blame] | 463 | "defaulted generic parameters cannot be forward declared".to_string(), |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 464 | ); |
| 465 | err |
| 466 | } |
| 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 | } |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 480 | ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => { |
| 481 | let mut err = self.session.struct_span_err( |
| 482 | span, |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 483 | "generic parameters may not be used in const operations", |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 484 | ); |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 485 | err.span_label(span, &format!("cannot perform const operation using `{}`", name)); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 486 | |
| 487 | if is_type { |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 488 | err.note("type parameters may not be used in const expressions"); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 489 | } else { |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 490 | err.help(&format!( |
| 491 | "const parameters may only be used as standalone arguments, i.e. `{}`", |
| 492 | name |
| 493 | )); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 494 | } |
Chris Wailes | 32f7835 | 2021-07-20 14:04:55 -0700 | [diff] [blame] | 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 | } |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 502 | |
| 503 | err |
| 504 | } |
Chris Wailes | 54272ac | 2021-09-09 16:08:13 -0700 | [diff] [blame^] | 505 | ResolutionError::SelfInGenericParamDefault => { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 506 | let mut err = struct_span_err!( |
| 507 | self.session, |
| 508 | span, |
| 509 | E0735, |
Chris Wailes | 54272ac | 2021-09-09 16:08:13 -0700 | [diff] [blame^] | 510 | "generic parameters cannot use `Self` in their defaults" |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 511 | ); |
Chris Wailes | 54272ac | 2021-09-09 16:08:13 -0700 | [diff] [blame^] | 512 | err.span_label(span, "`Self` in generic parameter default".to_string()); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 513 | err |
| 514 | } |
| 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 | } |
| 554 | } |
| 555 | } |
| 556 | |
| 557 | crate fn report_vis_error(&self, vis_resolution_error: VisResolutionError<'_>) { |
| 558 | match vis_resolution_error { |
| 559 | VisResolutionError::Relative2018(span, path) => { |
| 560 | let mut err = self.session.struct_span_err( |
| 561 | span, |
| 562 | "relative paths are not supported in visibilities on 2018 edition", |
| 563 | ); |
| 564 | err.span_suggestion( |
| 565 | path.span, |
| 566 | "try", |
| 567 | format!("crate::{}", pprust::path_to_string(&path)), |
| 568 | Applicability::MaybeIncorrect, |
| 569 | ); |
| 570 | err |
| 571 | } |
| 572 | VisResolutionError::AncestorOnly(span) => struct_span_err!( |
| 573 | self.session, |
| 574 | span, |
| 575 | E0742, |
| 576 | "visibilities can only be restricted to ancestor modules" |
| 577 | ), |
| 578 | VisResolutionError::FailedToResolve(span, label, suggestion) => { |
| 579 | self.into_struct_error(span, ResolutionError::FailedToResolve { label, suggestion }) |
| 580 | } |
| 581 | VisResolutionError::ExpectedFound(span, path_str, res) => { |
| 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 | ); |
| 590 | err.span_label(span, "not a module"); |
| 591 | err |
| 592 | } |
| 593 | VisResolutionError::Indeterminate(span) => struct_span_err!( |
| 594 | self.session, |
| 595 | span, |
| 596 | E0578, |
| 597 | "cannot determine resolution for the visibility" |
| 598 | ), |
| 599 | VisResolutionError::ModuleOnly(span) => { |
| 600 | self.session.struct_span_err(span, "visibility must resolve to a module") |
| 601 | } |
| 602 | } |
| 603 | .emit() |
| 604 | } |
| 605 | |
| 606 | /// Lookup typo candidate in scope for a macro or import. |
| 607 | fn early_lookup_typo_candidate( |
| 608 | &mut self, |
Chris Wailes | 32f7835 | 2021-07-20 14:04:55 -0700 | [diff] [blame] | 609 | scope_set: ScopeSet<'a>, |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 610 | parent_scope: &ParentScope<'a>, |
| 611 | ident: Ident, |
| 612 | filter_fn: &impl Fn(Res) -> bool, |
| 613 | ) -> Option<TypoSuggestion> { |
| 614 | let mut suggestions = Vec::new(); |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 615 | let ctxt = ident.span.ctxt(); |
| 616 | self.visit_scopes(scope_set, parent_scope, ctxt, |this, scope, use_prelude, _| { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 617 | match scope { |
| 618 | Scope::DeriveHelpers(expn_id) => { |
| 619 | let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); |
| 620 | if filter_fn(res) { |
| 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 | ); |
| 628 | } |
| 629 | } |
| 630 | Scope::DeriveHelpersCompat => { |
Jeff Vander Stoep | d59a287 | 2021-02-15 10:22:21 +0100 | [diff] [blame] | 631 | let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 632 | if filter_fn(res) { |
| 633 | for derive in parent_scope.derives { |
| 634 | let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; |
| 635 | if let Ok((Some(ext), _)) = this.resolve_macro_path( |
| 636 | derive, |
| 637 | Some(MacroKind::Derive), |
| 638 | parent_scope, |
| 639 | false, |
| 640 | false, |
| 641 | ) { |
| 642 | suggestions.extend( |
| 643 | ext.helper_attrs |
| 644 | .iter() |
| 645 | .map(|name| TypoSuggestion::from_res(*name, res)), |
| 646 | ); |
| 647 | } |
| 648 | } |
| 649 | } |
| 650 | } |
| 651 | Scope::MacroRules(macro_rules_scope) => { |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 652 | if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope.get() { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 653 | let res = macro_rules_binding.binding.res(); |
| 654 | if filter_fn(res) { |
| 655 | suggestions |
| 656 | .push(TypoSuggestion::from_res(macro_rules_binding.ident.name, res)) |
| 657 | } |
| 658 | } |
| 659 | } |
| 660 | Scope::CrateRoot => { |
| 661 | let root_ident = Ident::new(kw::PathRoot, ident.span); |
| 662 | let root_module = this.resolve_crate_root(root_ident); |
| 663 | this.add_module_candidates(root_module, &mut suggestions, filter_fn); |
| 664 | } |
Chris Wailes | 32f7835 | 2021-07-20 14:04:55 -0700 | [diff] [blame] | 665 | Scope::Module(module, _) => { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 666 | this.add_module_candidates(module, &mut suggestions, filter_fn); |
| 667 | } |
| 668 | Scope::RegisteredAttrs => { |
| 669 | let res = Res::NonMacroAttr(NonMacroAttrKind::Registered); |
| 670 | if filter_fn(res) { |
| 671 | suggestions.extend( |
| 672 | this.registered_attrs |
| 673 | .iter() |
| 674 | .map(|ident| TypoSuggestion::from_res(ident.name, res)), |
| 675 | ); |
| 676 | } |
| 677 | } |
| 678 | Scope::MacroUsePrelude => { |
| 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 | )); |
| 685 | } |
| 686 | Scope::BuiltinAttrs => { |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 687 | let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(kw::Empty)); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 688 | if filter_fn(res) { |
| 689 | suggestions.extend( |
| 690 | BUILTIN_ATTRIBUTES |
| 691 | .iter() |
| 692 | .map(|(name, ..)| TypoSuggestion::from_res(*name, res)), |
| 693 | ); |
| 694 | } |
| 695 | } |
| 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)); |
| 699 | filter_fn(res).then_some(TypoSuggestion::from_res(ident.name, res)) |
| 700 | })); |
| 701 | } |
| 702 | Scope::ToolPrelude => { |
| 703 | let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); |
| 704 | suggestions.extend( |
| 705 | this.registered_tools |
| 706 | .iter() |
| 707 | .map(|ident| TypoSuggestion::from_res(ident.name, res)), |
| 708 | ); |
| 709 | } |
| 710 | Scope::StdLibPrelude => { |
| 711 | if let Some(prelude) = this.prelude { |
| 712 | let mut tmp_suggestions = Vec::new(); |
| 713 | this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn); |
| 714 | suggestions.extend( |
| 715 | tmp_suggestions |
| 716 | .into_iter() |
| 717 | .filter(|s| use_prelude || this.is_builtin_macro(s.res)), |
| 718 | ); |
| 719 | } |
| 720 | } |
| 721 | Scope::BuiltinTypes => { |
Chris Wailes | e3116c4 | 2021-07-13 14:40:48 -0700 | [diff] [blame] | 722 | suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 723 | let res = Res::PrimTy(*prim_ty); |
Chris Wailes | e3116c4 | 2021-07-13 14:40:48 -0700 | [diff] [blame] | 724 | filter_fn(res).then_some(TypoSuggestion::from_res(prim_ty.name(), res)) |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 725 | })) |
| 726 | } |
| 727 | } |
| 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( |
Jeff Vander Stoep | d59a287 | 2021-02-15 10:22:21 +0100 | [diff] [blame] | 736 | &suggestions.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(), |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 737 | ident.name, |
| 738 | None, |
| 739 | ) { |
| 740 | Some(found) if found != ident.name => { |
| 741 | suggestions.into_iter().find(|suggestion| suggestion.candidate == found) |
| 742 | } |
| 743 | _ => None, |
| 744 | } |
| 745 | } |
| 746 | |
| 747 | fn lookup_import_candidates_from_module<FilterFn>( |
| 748 | &mut self, |
| 749 | lookup_ident: Ident, |
| 750 | namespace: Namespace, |
| 751 | parent_scope: &ParentScope<'a>, |
| 752 | start_module: Module<'a>, |
| 753 | crate_name: Ident, |
| 754 | filter_fn: FilterFn, |
| 755 | ) -> Vec<ImportSuggestion> |
| 756 | where |
| 757 | FilterFn: Fn(Res) -> bool, |
| 758 | { |
| 759 | let mut candidates = Vec::new(); |
| 760 | let mut seen_modules = FxHashSet::default(); |
Chris Wailes | 32f7835 | 2021-07-20 14:04:55 -0700 | [diff] [blame] | 761 | let mut worklist = vec![(start_module, Vec::<ast::PathSegment>::new(), true)]; |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 762 | let mut worklist_via_import = vec![]; |
| 763 | |
Chris Wailes | 32f7835 | 2021-07-20 14:04:55 -0700 | [diff] [blame] | 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(); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 769 | // We have to visit module children in deterministic order to avoid |
| 770 | // instabilities in reported imports (#43552). |
| 771 | in_module.for_each_child(self, |this, ident, ns, name_binding| { |
| 772 | // avoid non-importable candidates |
| 773 | if !name_binding.is_importable() { |
| 774 | return; |
| 775 | } |
| 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() { |
| 793 | return; |
| 794 | } |
| 795 | |
| 796 | // collect results based on the filter function |
| 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 | { |
| 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) |
| 809 | segms.insert(0, ast::PathSegment::from_ident(crate_name)); |
| 810 | } |
| 811 | |
| 812 | segms.push(ast::PathSegment::from_ident(ident)); |
| 813 | let path = Path { span: name_binding.span, segments: segms, tokens: None }; |
| 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 | }); |
| 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 = |
| 847 | name_binding.is_extern_crate() && lookup_ident.span.rust_2018(); |
| 848 | |
| 849 | if !is_extern_crate_that_also_appears_in_prelude { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 850 | // add the module to the lookup |
| 851 | if seen_modules.insert(module.def_id().unwrap()) { |
| 852 | if via_import { &mut worklist_via_import } else { &mut worklist } |
Chris Wailes | 32f7835 | 2021-07-20 14:04:55 -0700 | [diff] [blame] | 853 | .push((module, path_segments, child_accessible)); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 854 | } |
| 855 | } |
| 856 | } |
| 857 | }) |
| 858 | } |
| 859 | |
| 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 | |
| 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>( |
| 876 | &mut self, |
| 877 | lookup_ident: Ident, |
| 878 | namespace: Namespace, |
| 879 | parent_scope: &ParentScope<'a>, |
| 880 | filter_fn: FilterFn, |
| 881 | ) -> Vec<ImportSuggestion> |
| 882 | where |
| 883 | FilterFn: Fn(Res) -> bool, |
| 884 | { |
| 885 | let mut suggestions = self.lookup_import_candidates_from_module( |
| 886 | lookup_ident, |
| 887 | namespace, |
| 888 | parent_scope, |
| 889 | self.graph_root, |
| 890 | Ident::with_dummy_span(kw::Crate), |
| 891 | &filter_fn, |
| 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() { |
| 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 | } |
| 905 | if let Some(crate_id) = self.crate_loader.maybe_process_path_extern(ident.name) { |
| 906 | let crate_root = |
| 907 | self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }); |
| 908 | suggestions.extend(self.lookup_import_candidates_from_module( |
| 909 | lookup_ident, |
| 910 | namespace, |
| 911 | parent_scope, |
| 912 | crate_root, |
| 913 | ident, |
| 914 | &filter_fn, |
| 915 | )); |
| 916 | } |
| 917 | } |
| 918 | } |
| 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( |
| 932 | ScopeSet::Macro(macro_kind), |
| 933 | parent_scope, |
| 934 | ident, |
| 935 | is_expected, |
| 936 | ); |
| 937 | self.add_typo_suggestion(err, suggestion, ident.span); |
| 938 | |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 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 | |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 945 | if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { |
| 946 | let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident); |
| 947 | err.span_note(ident.span, &msg); |
| 948 | } |
| 949 | if self.macro_names.contains(&ident.normalize_to_macros_2_0()) { |
| 950 | err.help("have you added the `#[macro_use]` on the module/import?"); |
| 951 | } |
| 952 | } |
| 953 | |
| 954 | crate fn add_typo_suggestion( |
| 955 | &self, |
| 956 | err: &mut DiagnosticBuilder<'_>, |
| 957 | suggestion: Option<TypoSuggestion>, |
| 958 | span: Span, |
| 959 | ) -> bool { |
| 960 | let suggestion = match suggestion { |
| 961 | None => return false, |
| 962 | // We shouldn't suggest underscore. |
| 963 | Some(suggestion) if suggestion.candidate == kw::Underscore => return false, |
| 964 | Some(suggestion) => suggestion, |
| 965 | }; |
| 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 | }); |
| 974 | if let Some(def_span) = def_span { |
| 975 | if span.overlaps(def_span) { |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 976 | // Don't suggest typo suggestion for itself like in the following: |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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 | } |
| 996 | err.span_label( |
| 997 | self.session.source_map().guess_head_span(def_span), |
| 998 | &format!( |
| 999 | "similarly named {} `{}` defined here", |
| 1000 | suggestion.res.descr(), |
| 1001 | suggestion.candidate.as_str(), |
| 1002 | ), |
| 1003 | ); |
| 1004 | } |
| 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 | ); |
| 1016 | true |
| 1017 | } |
| 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() { |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 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); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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 | |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 1038 | let a = if built_in.is_empty() { res.article() } else { "a" }; |
| 1039 | format!("{a}{built_in} {thing}{from}", thing = res.descr()) |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 1040 | } else { |
| 1041 | let introduced = if b.is_import() { "imported" } else { "defined" }; |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 1042 | format!("the {thing} {introduced} here", thing = res.descr()) |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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})", |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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); |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 1066 | let note_msg = format!("`{ident}` could{also} refer to {what}"); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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!( |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 1076 | "consider adding an explicit import of `{ident}` to disambiguate" |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 1077 | )) |
| 1078 | } |
| 1079 | if b.is_extern_crate() && ident.span.rust_2018() { |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 1080 | help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 1081 | } |
| 1082 | if misc == AmbiguityErrorMisc::SuggestCrate { |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 1083 | help_msgs |
| 1084 | .push(format!("use `crate::{ident}` to refer to this {thing} unambiguously")) |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 1085 | } else if misc == AmbiguityErrorMisc::SuggestSelf { |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 1086 | help_msgs |
| 1087 | .push(format!("use `self::{ident}` to refer to this {thing} unambiguously")) |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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 | |
| 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( |
| 1106 | Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id), |
| 1107 | _, |
| 1108 | ) = binding.kind |
| 1109 | { |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 1110 | let def_id = self.parent(ctor_def_id).expect("no parent for a constructor"); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 1111 | let fields = self.field_names.get(&def_id)?; |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 1112 | return fields.iter().map(|name| name.span).reduce(Span::to); // None for `struct Foo()` |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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 " }, |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 1162 | item = get_descr(binding), |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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 | } |
| 1173 | |
| 1174 | err.emit(); |
| 1175 | } |
| 1176 | } |
| 1177 | |
| 1178 | impl<'a, 'b> ImportResolver<'a, 'b> { |
| 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. |
| 1191 | (Some(fst), Some(snd)) |
| 1192 | if fst.ident.name == kw::PathRoot && !snd.ident.is_path_segment_keyword() => {} |
| 1193 | // `ident::...` on 2018. |
| 1194 | (Some(fst), _) |
| 1195 | if fst.ident.span.rust_2018() && !fst.ident.is_path_segment_keyword() => |
| 1196 | { |
| 1197 | // Insert a placeholder that's later replaced by `self`/`super`/etc. |
| 1198 | path.insert(0, Segment::from_ident(Ident::invalid())); |
| 1199 | } |
| 1200 | _ => return None, |
| 1201 | } |
| 1202 | |
| 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)) |
| 1207 | } |
| 1208 | |
| 1209 | /// Suggest a missing `self::` if that resolves to an correct module. |
| 1210 | /// |
| 1211 | /// ```text |
| 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. |
| 1223 | path[0].ident.name = kw::SelfLower; |
| 1224 | let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); |
| 1225 | debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); |
| 1226 | if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } |
| 1227 | } |
| 1228 | |
| 1229 | /// Suggests a missing `crate::` if that resolves to an correct module. |
| 1230 | /// |
| 1231 | /// ```text |
| 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. |
| 1243 | path[0].ident.name = kw::Crate; |
| 1244 | let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); |
| 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-\ |
| 1252 | clarity.html>" |
| 1253 | .to_string(), |
| 1254 | ], |
| 1255 | )) |
| 1256 | } else { |
| 1257 | None |
| 1258 | } |
| 1259 | } |
| 1260 | |
| 1261 | /// Suggests a missing `super::` if that resolves to an correct module. |
| 1262 | /// |
| 1263 | /// ```text |
| 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. |
| 1275 | path[0].ident.name = kw::Super; |
| 1276 | let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); |
| 1277 | debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); |
| 1278 | if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } |
| 1279 | } |
| 1280 | |
| 1281 | /// Suggests a missing external crate name if that resolves to an correct module. |
| 1282 | /// |
| 1283 | /// ```text |
| 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 | } |
| 1300 | |
| 1301 | // Sort extern crate names in reverse order to get |
| 1302 | // 1) some consistent ordering for emitted diagnostics, and |
| 1303 | // 2) `std` suggestions before `core` suggestions. |
| 1304 | let mut extern_crate_names = |
| 1305 | self.r.extern_prelude.iter().map(|(ident, _)| ident.name).collect::<Vec<_>>(); |
| 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; |
| 1311 | let result = self.r.resolve_path(&path, None, parent_scope, false, span, CrateLint::No); |
| 1312 | debug!( |
| 1313 | "make_external_crate_suggestion: name={:?} path={:?} result={:?}", |
| 1314 | name, path, result |
| 1315 | ); |
| 1316 | if let PathResult::Module(..) = result { |
| 1317 | return Some((path, Vec::new())); |
| 1318 | } |
| 1319 | } |
| 1320 | |
| 1321 | None |
| 1322 | } |
| 1323 | |
| 1324 | /// Suggests importing a macro from the root of the crate rather than a module within |
| 1325 | /// the crate. |
| 1326 | /// |
| 1327 | /// ```text |
| 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( |
| 1337 | &mut self, |
| 1338 | import: &'b Import<'b>, |
| 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 | } |
| 1351 | |
| 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 | } |
| 1357 | |
| 1358 | let resolutions = self.r.resolutions(crate_module).borrow(); |
| 1359 | let resolution = resolutions.get(&self.r.new_key(ident, MacroNS))?; |
| 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(); |
| 1363 | let import_snippet = match import.kind { |
| 1364 | ImportKind::Single { source, target, .. } if source != target => { |
| 1365 | format!("{} as {}", source, target) |
| 1366 | } |
| 1367 | _ => format!("{}", ident), |
| 1368 | }; |
| 1369 | |
| 1370 | let mut corrections: Vec<(Span, String)> = Vec::new(); |
| 1371 | if !import.is_nested() { |
| 1372 | // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove |
| 1373 | // intermediate segments. |
| 1374 | corrections.push((import.span, format!("{}::{}", module_name, import_snippet))); |
| 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( |
| 1380 | self.r.session, |
| 1381 | import.span, |
| 1382 | import.use_span, |
| 1383 | ); |
| 1384 | debug!( |
| 1385 | "check_for_module_export_macro: found_closing_brace={:?} binding_span={:?}", |
| 1386 | found_closing_brace, binding_span |
| 1387 | ); |
| 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 | // ^^^ |
| 1398 | if let Some(previous_span) = |
| 1399 | extend_span_to_previous_binding(self.r.session, binding_span) |
| 1400 | { |
| 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( |
| 1417 | self.r.session, |
| 1418 | module_name, |
| 1419 | import.use_span, |
| 1420 | ); |
| 1421 | debug!( |
| 1422 | "check_for_module_export_macro: has_nested={:?} after_crate_name={:?}", |
| 1423 | has_nested, after_crate_name |
| 1424 | ); |
| 1425 | |
| 1426 | let source_map = self.r.session.source_map(); |
| 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 '{'. |
| 1435 | format!("{}{}, ", start_snippet, import_snippet) |
| 1436 | } else { |
| 1437 | // In this case, add a `{`, then the moved import, then whatever |
| 1438 | // was there before. |
| 1439 | format!("{{{}, {}", import_snippet, start_snippet) |
| 1440 | }, |
| 1441 | )); |
| 1442 | } |
| 1443 | |
| 1444 | // Add a `};` to the end if nested, matching the `{` added at the start. |
| 1445 | if !has_nested { |
| 1446 | corrections.push((source_map.end_point(after_crate_name), "};".to_string())); |
| 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 \ |
| 1457 | at the root of the crate instead of the module where it is defined" |
| 1458 | .to_string(), |
| 1459 | ]; |
| 1460 | Some((suggestion, note)) |
| 1461 | } else { |
| 1462 | None |
| 1463 | } |
| 1464 | } |
| 1465 | } |
| 1466 | |
| 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; |
| 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 | } |
| 1507 | ch == ' ' || ch == ',' |
| 1508 | }); |
| 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 | /// ``` |
| 1531 | pub(crate) fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option<Span> { |
| 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; |
| 1543 | } |
| 1544 | |
| 1545 | let prev_comma = prev_comma.first().unwrap(); |
| 1546 | let prev_starting_brace = prev_starting_brace.first().unwrap(); |
| 1547 | |
| 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; |
| 1553 | } |
| 1554 | |
| 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. |
| 1558 | binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1, |
| 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) { |
| 1580 | debug!( |
| 1581 | "find_span_immediately_after_crate_name: module_name={:?} use_span={:?}", |
| 1582 | module_name, use_span |
| 1583 | ); |
| 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| { |
| 1590 | if *c == ':' { |
| 1591 | num_colons += 1; |
| 1592 | } |
Thiébaud Weksteen | 5bd94c1 | 2021-01-06 15:18:42 +0100 | [diff] [blame] | 1593 | !matches!(c, ':' if num_colons == 2) |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 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| { |
| 1601 | if found_a_non_whitespace_character { |
| 1602 | return false; |
| 1603 | } |
| 1604 | if !c.is_whitespace() { |
| 1605 | found_a_non_whitespace_character = true; |
| 1606 | } |
| 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) |
| 1614 | } |
| 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 |
| 1622 | use_placement_span: Option<Span>, |
| 1623 | candidates: &[ImportSuggestion], |
| 1624 | instead: bool, |
| 1625 | found_use: bool, |
| 1626 | ) { |
| 1627 | if candidates.is_empty() { |
| 1628 | return; |
| 1629 | } |
| 1630 | |
| 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<_> = |
| 1634 | candidates.iter().map(|c| path_names_to_string(&c.path)).collect(); |
| 1635 | |
| 1636 | path_strings.sort(); |
| 1637 | path_strings.dedup(); |
| 1638 | |
| 1639 | let (determiner, kind) = if candidates.len() == 1 { |
| 1640 | ("this", candidates[0].descr) |
| 1641 | } else { |
| 1642 | ("one of these", "items") |
| 1643 | }; |
| 1644 | |
| 1645 | let instead = if instead { " instead" } else { "" }; |
| 1646 | let mut msg = format!("consider importing {} {}{}", determiner, kind, instead); |
| 1647 | |
| 1648 | if let Some(span) = use_placement_span { |
| 1649 | for candidate in &mut path_strings { |
| 1650 | // produce an additional newline to separate the new use statement |
| 1651 | // from the directly following item. |
| 1652 | let additional_newline = if found_use { "" } else { "\n" }; |
| 1653 | *candidate = format!("use {};\n{}", candidate, additional_newline); |
| 1654 | } |
| 1655 | |
| 1656 | err.span_suggestions(span, &msg, path_strings.into_iter(), Applicability::Unspecified); |
| 1657 | } else { |
| 1658 | msg.push(':'); |
| 1659 | |
| 1660 | for candidate in path_strings { |
| 1661 | msg.push('\n'); |
| 1662 | msg.push_str(&candidate); |
| 1663 | } |
| 1664 | |
| 1665 | err.note(&msg); |
| 1666 | } |
| 1667 | } |