use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
-use rustc_errors::{DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg};
+use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg};
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS};
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
in_assignment: Option<&'ast Expr>,
is_assign_rhs: bool,
+ /// Used to detect possible `.` -> `..` typo when calling methods.
+ in_range: Option<(&'ast Expr, &'ast Expr)>,
+
/// If we are currently in a trait object definition. Used to point at the bounds when
/// encountering a struct or enum.
current_trait_object: Option<&'ast [ast::GenericBound]>,
/// FIXME #4948: Reuse ribs to avoid allocation.
ribs: PerNS<Vec<Rib<'a>>>,
+ /// Previous poped `rib`, only used for diagnostic.
+ last_block_rib: Option<Rib<'a>>,
+
/// The current set of local scopes, for labels.
label_ribs: Vec<Rib<'a, NodeId>>,
// Ignore errors in function bodies if this is rustdoc
// Be sure not to set this until the function signature has been resolved.
let previous_state = replace(&mut this.in_func_body, true);
+ // We only care block in the same function
+ this.last_block_rib = None;
// Resolve the function body, potentially inside the body of an async closure
this.with_lifetime_rib(
LifetimeRibKind::Elided(LifetimeRes::Infer),
type_ns: vec![Rib::new(start_rib_kind)],
macro_ns: vec![Rib::new(start_rib_kind)],
},
+ last_block_rib: None,
label_ribs: Vec::new(),
lifetime_ribs: Vec::new(),
lifetime_elision_candidates: None,
);
}
+ #[instrument(level = "debug", skip(self))]
fn smart_resolve_path_fragment(
&mut self,
qself: &Option<P<QSelf>>,
source: PathSource<'ast>,
finalize: Finalize,
) -> PartialRes {
- debug!(
- "smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})",
- qself, path, finalize,
- );
let ns = source.namespace();
let Finalize { node_id, path_span, .. } = finalize;
let def_id = this.parent_scope.module.nearest_parent_mod();
let instead = res.is_some();
- let suggestion =
- if res.is_none() { this.report_missing_type_error(path) } else { None };
+ let suggestion = if let Some((start, end)) = this.diagnostic_metadata.in_range
+ && path[0].ident.span.lo() == end.span.lo()
+ {
+ let mut sugg = ".";
+ let mut span = start.span.between(end.span);
+ if span.lo() + BytePos(2) == span.hi() {
+ // There's no space between the start, the range op and the end, suggest
+ // removal which will look better.
+ span = span.with_lo(span.lo() + BytePos(1));
+ sugg = "";
+ }
+ Some((
+ span,
+ "you might have meant to write `.` instead of `..`",
+ sugg.to_string(),
+ Applicability::MaybeIncorrect,
+ ))
+ } else if res.is_none() {
+ this.report_missing_type_error(path)
+ } else {
+ None
+ };
this.r.use_injections.push(UseError {
err,
self.ribs[ValueNS].pop();
self.label_ribs.pop();
}
- self.ribs[ValueNS].pop();
+ self.last_block_rib = self.ribs[ValueNS].pop();
if anonymous_module.is_some() {
self.ribs[TypeNS].pop();
}
self.visit_expr(rhs);
self.diagnostic_metadata.is_assign_rhs = false;
}
+ ExprKind::Range(Some(ref start), Some(ref end), RangeLimits::HalfOpen) => {
+ self.diagnostic_metadata.in_range = Some((start, end));
+ self.resolve_expr(start, Some(expr));
+ self.resolve_expr(end, Some(expr));
+ self.diagnostic_metadata.in_range = None;
+ }
_ => {
visit::walk_expr(self, expr);
}