]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #67538 - varkor:lhs-assign-diagnostics, r=Centril
authorMazdak Farrokhzad <twingoow@gmail.com>
Mon, 23 Dec 2019 14:16:29 +0000 (15:16 +0100)
committerGitHub <noreply@github.com>
Mon, 23 Dec 2019 14:16:29 +0000 (15:16 +0100)
Improve diagnostics for invalid assignment

- Improve wording and span information for invalid assignment diagnostics.
- Link to https://github.com/rust-lang/rfcs/issues/372 when it appears the user is trying a destructuring assignment.
- Make the equality constraint in `where` clauses error consistent with the invalid assignment error.

1  2 
src/librustc/hir/mod.rs
src/librustc_typeck/check/demand.rs

diff --combined src/librustc/hir/mod.rs
index 915b0afc48fcb0b3f4d55d35676aea622312ef5e,3c7860ae02fe7494fee6dcf840bc89d396f29aef..bf95324d776dc12fe0d87b1368781237f3b0eeb6
@@@ -29,10 -29,10 +29,10 @@@ use syntax::ast::{AttrVec, Attribute, F
  pub use syntax::ast::{BorrowKind, ImplPolarity, IsAuto};
  pub use syntax::ast::{CaptureBy, Constness, Movability, Mutability, Unsafety};
  use syntax::attr::{InlineAttr, OptimizeAttr};
 -use syntax::source_map::Spanned;
 -use syntax::symbol::{kw, Symbol};
  use syntax::tokenstream::TokenStream;
  use syntax::util::parser::ExprPrecedence;
 +use syntax_pos::source_map::{SourceMap, Spanned};
 +use syntax_pos::symbol::{kw, sym, Symbol};
  use syntax_pos::{MultiSpan, Span, DUMMY_SP};
  
  /// HIR doesn't commit to a concrete storage type and has its own alias for a vector.
@@@ -1564,68 -1564,6 +1564,68 @@@ impl fmt::Debug for Expr 
      }
  }
  
 +/// Checks if the specified expression is a built-in range literal.
 +/// (See: `LoweringContext::lower_expr()`).
 +///
 +/// FIXME(#60607): This function is a hack. If and when we have `QPath::Lang(...)`,
 +/// we can use that instead as simpler, more reliable mechanism, as opposed to using `SourceMap`.
 +pub fn is_range_literal(sm: &SourceMap, expr: &Expr) -> bool {
 +    // Returns whether the given path represents a (desugared) range,
 +    // either in std or core, i.e. has either a `::std::ops::Range` or
 +    // `::core::ops::Range` prefix.
 +    fn is_range_path(path: &Path) -> bool {
 +        let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.to_string()).collect();
 +        let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect();
 +
 +        // "{{root}}" is the equivalent of `::` prefix in `Path`.
 +        if let ["{{root}}", std_core, "ops", range] = segs.as_slice() {
 +            (*std_core == "std" || *std_core == "core") && range.starts_with("Range")
 +        } else {
 +            false
 +        }
 +    };
 +
 +    // Check whether a span corresponding to a range expression is a
 +    // range literal, rather than an explicit struct or `new()` call.
 +    fn is_lit(sm: &SourceMap, span: &Span) -> bool {
 +        let end_point = sm.end_point(*span);
 +
 +        if let Ok(end_string) = sm.span_to_snippet(end_point) {
 +            !(end_string.ends_with("}") || end_string.ends_with(")"))
 +        } else {
 +            false
 +        }
 +    };
 +
 +    match expr.kind {
 +        // All built-in range literals but `..=` and `..` desugar to `Struct`s.
 +        ExprKind::Struct(ref qpath, _, _) => {
 +            if let QPath::Resolved(None, ref path) = **qpath {
 +                return is_range_path(&path) && is_lit(sm, &expr.span);
 +            }
 +        }
 +
 +        // `..` desugars to its struct path.
 +        ExprKind::Path(QPath::Resolved(None, ref path)) => {
 +            return is_range_path(&path) && is_lit(sm, &expr.span);
 +        }
 +
 +        // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
 +        ExprKind::Call(ref func, _) => {
 +            if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind {
 +                if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind {
 +                    let new_call = segment.ident.name == sym::new;
 +                    return is_range_path(&path) && is_lit(sm, &expr.span) && new_call;
 +                }
 +            }
 +        }
 +
 +        _ => {}
 +    }
 +
 +    false
 +}
 +
  #[derive(RustcEncodable, RustcDecodable, Debug, HashStable)]
  pub enum ExprKind {
      /// A `box x` expression.
      Block(P<Block>, Option<Label>),
  
      /// An assignment (e.g., `a = foo()`).
-     Assign(P<Expr>, P<Expr>),
+     /// The `Span` argument is the span of the `=` token.
+     Assign(P<Expr>, P<Expr>, Span),
      /// An assignment with an operator.
      ///
      /// E.g., `a += 1`.
index 3a25786365b483d26dcee63c93ac7bbde45171a8,d63d30b7b8d83c53af330254ad9370eb84822925..9a14b75ca2f4dc4ec66be06434e117e04ba1efe4
@@@ -3,7 -3,9 +3,7 @@@ use rustc::infer::InferOk
  use rustc::traits::{self, ObligationCause, ObligationCauseCode};
  
  use errors::{Applicability, DiagnosticBuilder};
 -use rustc::hir;
 -use rustc::hir::Node;
 -use rustc::hir::{lowering::is_range_literal, print};
 +use rustc::hir::{self, is_range_literal, print, Node};
  use rustc::ty::adjustment::AllowTwoPhase;
  use rustc::ty::{self, AssocItem, Ty};
  use syntax::symbol::sym;
@@@ -476,7 -478,7 +476,7 @@@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> 
                              // parenthesize if needed (Issue #46756)
                              hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
                              // parenthesize borrows of range literals (Issue #54505)
 -                            _ if is_range_literal(self.tcx.sess, expr) => true,
 +                            _ if is_range_literal(self.tcx.sess.source_map(), expr) => true,
                              _ => false,
                          };
                          let sugg_expr = if needs_parens { format!("({})", src) } else { src };
                              String::new()
                          };
                          if let Some(hir::Node::Expr(hir::Expr {
-                             kind: hir::ExprKind::Assign(left_expr, _),
+                             kind: hir::ExprKind::Assign(left_expr, ..),
                              ..
                          })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
                          {