Auto merge of #70931 - Dylan-DPC:rollup-f8orcao, r=Dylan-DPC

Rollup of 9 pull requests

Successful merges:

 - #70789 (remove false positives of unused_braces)
 - #70847 (ci: move /var/lib/docker to /mnt on GHA)
 - #70850 (BTreeMap first last proposal tweaks)
 - #70876 (Use a `SmallVec` for `Cache::predecessors`.)
 - #70883 (Clean up E0507 explanation)
 - #70892 (wf: refactor `compute_trait_ref`)
 - #70914 (Corrects a typo in rustdoc documentation.)
 - #70915 (Remove unnecessary TypeFlags::NOMINAL_FLAGS)
 - #70927 (Clean up E0510 explanation)

Failed merges:

r? @ghost
This commit is contained in:
bors 2020-04-08 17:55:45 +00:00
commit 485c5fb6e1
14 changed files with 346 additions and 273 deletions

View File

@ -24,4 +24,10 @@ elif isLinux && isGitHubActions; then
mv "${current_dir}" /mnt/more-space/workspace
ln -s /mnt/more-space/workspace "${current_dir}"
cd "${current_dir}"
# Move the Docker data directory to /mnt
sudo systemctl stop docker.service
sudo mv /var/lib/docker /mnt/docker
sudo ln -s /mnt/docker /var/lib/docker
sudo systemctl start docker.service
fi

View File

@ -352,9 +352,9 @@ are added.
/// ```
```
`edition2018` tells `rustdoc` that the code sample should be compiled the 2018
edition of Rust. Similarly, you can specify `edition2015` to compile the code
with the 2015 edition.
`edition2018` tells `rustdoc` that the code sample should be compiled using
the 2018 edition of Rust. Similarly, you can specify `edition2015` to compile
the code with the 2015 edition.
## Syntax reference

View File

@ -653,11 +653,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
/// assert_eq!(map.first_key_value(), Some((&1, &"b")));
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn first_key_value<T: ?Sized>(&self) -> Option<(&K, &V)>
where
T: Ord,
K: Borrow<T>,
{
pub fn first_key_value(&self) -> Option<(&K, &V)> {
let front = self.root.as_ref()?.as_ref().first_leaf_edge();
front.right_kv().ok().map(Handle::into_kv)
}
@ -667,7 +663,38 @@ impl<K: Ord, V> BTreeMap<K, V> {
///
/// # Examples
///
/// Contrived way to `clear` a map:
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeMap;
///
/// let mut map = BTreeMap::new();
/// map.insert(1, "a");
/// map.insert(2, "b");
/// if let Some(mut entry) = map.first_entry() {
/// if *entry.key() > 0 {
/// entry.insert("first");
/// }
/// }
/// assert_eq!(*map.get(&1).unwrap(), "first");
/// assert_eq!(*map.get(&2).unwrap(), "b");
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> {
let front = self.root.as_mut()?.as_mut().first_leaf_edge();
let kv = front.right_kv().ok()?;
Some(OccupiedEntry {
handle: kv.forget_node_type(),
length: &mut self.length,
_marker: PhantomData,
})
}
/// Removes and returns the first element in the map.
/// The key of this element is the minimum key that was in the map.
///
/// # Examples
///
/// Draining elements in ascending order, while keeping a usable map each iteration.
///
/// ```
/// #![feature(map_first_last)]
@ -676,27 +703,14 @@ impl<K: Ord, V> BTreeMap<K, V> {
/// let mut map = BTreeMap::new();
/// map.insert(1, "a");
/// map.insert(2, "b");
/// while let Some(entry) = map.first_entry() {
/// let (key, val) = entry.remove_entry();
/// assert!(!map.contains_key(&key));
/// while let Some((key, _val)) = map.pop_first() {
/// assert!(map.iter().all(|(k, _v)| *k > key));
/// }
/// assert!(map.is_empty());
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn first_entry<T: ?Sized>(&mut self) -> Option<OccupiedEntry<'_, K, V>>
where
T: Ord,
K: Borrow<T>,
{
let front = self.root.as_mut()?.as_mut().first_leaf_edge();
if let Ok(kv) = front.right_kv() {
Some(OccupiedEntry {
handle: kv.forget_node_type(),
length: &mut self.length,
_marker: PhantomData,
})
} else {
None
}
pub fn pop_first(&mut self) -> Option<(K, V)> {
self.first_entry().map(|entry| entry.remove_entry())
}
/// Returns the last key-value pair in the map.
@ -716,11 +730,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
/// assert_eq!(map.last_key_value(), Some((&2, &"a")));
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn last_key_value<T: ?Sized>(&self) -> Option<(&K, &V)>
where
T: Ord,
K: Borrow<T>,
{
pub fn last_key_value(&self) -> Option<(&K, &V)> {
let back = self.root.as_ref()?.as_ref().last_leaf_edge();
back.left_kv().ok().map(Handle::into_kv)
}
@ -730,7 +740,38 @@ impl<K: Ord, V> BTreeMap<K, V> {
///
/// # Examples
///
/// Contrived way to `clear` a map:
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeMap;
///
/// let mut map = BTreeMap::new();
/// map.insert(1, "a");
/// map.insert(2, "b");
/// if let Some(mut entry) = map.last_entry() {
/// if *entry.key() > 0 {
/// entry.insert("last");
/// }
/// }
/// assert_eq!(*map.get(&1).unwrap(), "a");
/// assert_eq!(*map.get(&2).unwrap(), "last");
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> {
let back = self.root.as_mut()?.as_mut().last_leaf_edge();
let kv = back.left_kv().ok()?;
Some(OccupiedEntry {
handle: kv.forget_node_type(),
length: &mut self.length,
_marker: PhantomData,
})
}
/// Removes and returns the last element in the map.
/// The key of this element is the maximum key that was in the map.
///
/// # Examples
///
/// Draining elements in descending order, while keeping a usable map each iteration.
///
/// ```
/// #![feature(map_first_last)]
@ -739,27 +780,14 @@ impl<K: Ord, V> BTreeMap<K, V> {
/// let mut map = BTreeMap::new();
/// map.insert(1, "a");
/// map.insert(2, "b");
/// while let Some(entry) = map.last_entry() {
/// let (key, val) = entry.remove_entry();
/// assert!(!map.contains_key(&key));
/// while let Some((key, _val)) = map.pop_last() {
/// assert!(map.iter().all(|(k, _v)| *k < key));
/// }
/// assert!(map.is_empty());
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn last_entry<T: ?Sized>(&mut self) -> Option<OccupiedEntry<'_, K, V>>
where
T: Ord,
K: Borrow<T>,
{
let back = self.root.as_mut()?.as_mut().last_leaf_edge();
if let Ok(kv) = back.left_kv() {
Some(OccupiedEntry {
handle: kv.forget_node_type(),
length: &mut self.length,
_marker: PhantomData,
})
} else {
None
}
pub fn pop_last(&mut self) -> Option<(K, V)> {
self.last_entry().map(|entry| entry.remove_entry())
}
/// Returns `true` if the map contains a value for the specified key.

View File

@ -1,9 +1,4 @@
You tried to move out of a value which was borrowed.
This can also happen when using a type implementing `Fn` or `FnMut`, as neither
allows moving out of them (they usually represent closures which can be called
more than once). Much of the text following applies equally well to non-`FnOnce`
closure bodies.
A borrowed value was moved out.
Erroneous code example:
@ -32,6 +27,11 @@ you have three choices:
* Somehow reclaim the ownership.
* Implement the `Copy` trait on the type.
This can also happen when using a type implementing `Fn` or `FnMut`, as neither
allows moving out of them (they usually represent closures which can be called
more than once). Much of the text following applies equally well to non-`FnOnce`
closure bodies.
Examples:
```

View File

@ -1,16 +1,29 @@
Cannot mutate place in this match guard.
The matched value was assigned in a match guard.
When matching on a variable it cannot be mutated in the match guards, as this
could cause the match to be non-exhaustive:
Erroneous code example:
```compile_fail,E0510
let mut x = Some(0);
match x {
None => (),
Some(_) if { x = None; false } => (),
Some(v) => (), // No longer matches
None => {}
Some(_) if { x = None; false } => {} // error!
Some(_) => {}
}
```
When matching on a variable it cannot be mutated in the match guards, as this
could cause the match to be non-exhaustive.
Here executing `x = None` would modify the value being matched and require us
to go "back in time" to the `None` arm.
to go "back in time" to the `None` arm. To fix it, change the value in the match
arm:
```
let mut x = Some(0);
match x {
None => {}
Some(_) => {
x = None; // ok!
}
}
```

View File

@ -355,6 +355,19 @@ impl From<UnusedDelimsCtx> for &'static str {
trait UnusedDelimLint {
const DELIM_STR: &'static str;
/// Due to `ref` pattern, there can be a difference between using
/// `{ expr }` and `expr` in pattern-matching contexts. This means
/// that we should only lint `unused_parens` and not `unused_braces`
/// in this case.
///
/// ```rust
/// let mut a = 7;
/// let ref b = { a }; // We actually borrow a copy of `a` here.
/// a += 1; // By mutating `a` we invalidate any borrows of `a`.
/// assert_eq!(b + 1, a); // `b` does not borrow `a`, so we can still use it here.
/// ```
const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
// this cannot be a constant is it refers to a static.
fn lint(&self) -> &'static Lint;
@ -454,7 +467,10 @@ trait UnusedDelimLint {
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
use rustc_ast::ast::ExprKind::*;
let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind {
If(ref cond, ref block, ..) => {
// Do not lint `unused_braces` in `if let` expressions.
If(ref cond, ref block, ..)
if !matches!(cond.kind, Let(_, _)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
{
let left = e.span.lo() + rustc_span::BytePos(2);
let right = block.span.lo();
(cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right))
@ -470,7 +486,7 @@ trait UnusedDelimLint {
(cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo()))
}
Match(ref head, _) => {
Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
let left = e.span.lo() + rustc_span::BytePos(5);
(head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None)
}
@ -512,7 +528,7 @@ trait UnusedDelimLint {
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
match s.kind {
StmtKind::Local(ref local) => {
StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
if let Some(ref value) = local.init {
self.check_unused_delims_expr(
cx,
@ -565,6 +581,8 @@ declare_lint_pass!(UnusedParens => [UNUSED_PARENS]);
impl UnusedDelimLint for UnusedParens {
const DELIM_STR: &'static str = "parentheses";
const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
fn lint(&self) -> &'static Lint {
UNUSED_PARENS
}
@ -736,6 +754,8 @@ declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
impl UnusedDelimLint for UnusedBraces {
const DELIM_STR: &'static str = "braces";
const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
fn lint(&self) -> &'static Lint {
UNUSED_BRACES
}

View File

@ -5,13 +5,15 @@ use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_index::vec::IndexVec;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use smallvec::SmallVec;
use std::iter;
use std::ops::{Deref, DerefMut, Index, IndexMut};
use std::vec::IntoIter;
#[derive(Clone, Debug)]
pub struct Cache {
predecessors: Option<IndexVec<BasicBlock, Vec<BasicBlock>>>,
// Typically 95%+ of the inner vectors have 4 or fewer elements.
predecessors: Option<IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>>>,
}
impl rustc_serialize::Encodable for Cache {
@ -44,7 +46,7 @@ impl Cache {
pub fn ensure_predecessors(&mut self, body: &Body<'_>) {
if self.predecessors.is_none() {
let mut result = IndexVec::from_elem(vec![], body.basic_blocks());
let mut result = IndexVec::from_elem(smallvec![], body.basic_blocks());
for (bb, data) in body.basic_blocks().iter_enumerated() {
if let Some(ref term) = data.terminator {
for &tgt in term.successors() {
@ -58,7 +60,11 @@ impl Cache {
}
/// This will recompute the predecessors cache if it is not available
fn predecessors(&mut self, body: &Body<'_>) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
// njn: typedef?
fn predecessors(
&mut self,
body: &Body<'_>,
) -> &IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>> {
self.ensure_predecessors(body);
self.predecessors.as_ref().unwrap()
}
@ -137,7 +143,7 @@ impl BodyAndCache<'tcx> {
self.cache.ensure_predecessors(&self.body);
}
pub fn predecessors(&mut self) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
pub fn predecessors(&mut self) -> &IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>> {
self.cache.predecessors(&self.body)
}
@ -199,7 +205,7 @@ impl ReadOnlyBodyAndCache<'a, 'tcx> {
Self { body, cache }
}
pub fn predecessors(&self) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
pub fn predecessors(&self) -> &IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>> {
self.cache.predecessors.as_ref().unwrap()
}

View File

@ -28,7 +28,7 @@ impl FlagComputation {
}
fn add_flags(&mut self, flags: TypeFlags) {
self.flags = self.flags | (flags & TypeFlags::NOMINAL_FLAGS);
self.flags = self.flags | flags;
}
/// indicates that `self` refers to something at binding level `binder`

View File

@ -598,29 +598,6 @@ bitflags! {
/// Does this value have parameters/placeholders/inference variables which could be
/// replaced later, in a way that would change the results of `impl` specialization?
const STILL_FURTHER_SPECIALIZABLE = 1 << 18;
/// Flags representing the nominal content of a type,
/// computed by FlagsComputation. If you add a new nominal
/// flag, it should be added here too.
const NOMINAL_FLAGS = TypeFlags::HAS_TY_PARAM.bits
| TypeFlags::HAS_RE_PARAM.bits
| TypeFlags::HAS_CT_PARAM.bits
| TypeFlags::HAS_TY_INFER.bits
| TypeFlags::HAS_RE_INFER.bits
| TypeFlags::HAS_CT_INFER.bits
| TypeFlags::HAS_TY_PLACEHOLDER.bits
| TypeFlags::HAS_RE_PLACEHOLDER.bits
| TypeFlags::HAS_CT_PLACEHOLDER.bits
| TypeFlags::HAS_FREE_LOCAL_REGIONS.bits
| TypeFlags::HAS_TY_PROJECTION.bits
| TypeFlags::HAS_TY_OPAQUE.bits
| TypeFlags::HAS_CT_PROJECTION.bits
| TypeFlags::KEEP_IN_LOCAL_TCX.bits
| TypeFlags::HAS_TY_ERR.bits
| TypeFlags::HAS_FREE_REGIONS.bits
| TypeFlags::HAS_RE_LATE_BOUND.bits
| TypeFlags::HAS_RE_ERASED.bits
| TypeFlags::STILL_FURTHER_SPECIALIZABLE.bits;
}
}

View File

@ -134,6 +134,152 @@ enum Elaborate {
None,
}
fn extend_cause_with_original_assoc_item_obligation<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: &ty::TraitRef<'tcx>,
item: Option<&hir::Item<'tcx>>,
cause: &mut traits::ObligationCause<'tcx>,
pred: &ty::Predicate<'_>,
mut trait_assoc_items: impl Iterator<Item = ty::AssocItem>,
) {
let trait_item =
tcx.hir().as_local_hir_id(trait_ref.def_id).and_then(|trait_id| tcx.hir().find(trait_id));
let (trait_name, trait_generics) = match trait_item {
Some(hir::Node::Item(hir::Item {
ident,
kind: hir::ItemKind::Trait(.., generics, _, _),
..
}))
| Some(hir::Node::Item(hir::Item {
ident,
kind: hir::ItemKind::TraitAlias(generics, _),
..
})) => (Some(ident), Some(generics)),
_ => (None, None),
};
let item_span = item.map(|i| tcx.sess.source_map().guess_head_span(i.span));
match pred {
ty::Predicate::Projection(proj) => {
// The obligation comes not from the current `impl` nor the `trait` being
// implemented, but rather from a "second order" obligation, like in
// `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`:
//
// error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()`
// --> $DIR/point-at-type-on-obligation-failure.rs:13:5
// |
// LL | type Ok;
// | -- associated type defined here
// ...
// LL | impl Bar for Foo {
// | ---------------- in this `impl` item
// LL | type Ok = ();
// | ^^^^^^^^^^^^^ expected `u32`, found `()`
// |
// = note: expected type `u32`
// found type `()`
//
// FIXME: we would want to point a span to all places that contributed to this
// obligation. In the case above, it should be closer to:
//
// error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()`
// --> $DIR/point-at-type-on-obligation-failure.rs:13:5
// |
// LL | type Ok;
// | -- associated type defined here
// LL | type Sibling: Bar2<Ok=Self::Ok>;
// | -------------------------------- obligation set here
// ...
// LL | impl Bar for Foo {
// | ---------------- in this `impl` item
// LL | type Ok = ();
// | ^^^^^^^^^^^^^ expected `u32`, found `()`
// ...
// LL | impl Bar2 for Foo2 {
// | ---------------- in this `impl` item
// LL | type Ok = u32;
// | -------------- obligation set here
// |
// = note: expected type `u32`
// found type `()`
if let Some(hir::ItemKind::Impl { items, .. }) = item.map(|i| &i.kind) {
let trait_assoc_item = tcx.associated_item(proj.projection_def_id());
if let Some(impl_item) =
items.iter().find(|item| item.ident == trait_assoc_item.ident)
{
cause.span = impl_item.span;
cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
impl_span: item_span,
original: trait_assoc_item.ident.span,
bounds: vec![],
}));
}
}
}
ty::Predicate::Trait(proj, _) => {
// An associated item obligation born out of the `trait` failed to be met.
// Point at the `impl` that failed the obligation, the associated item that
// needed to meet the obligation, and the definition of that associated item,
// which should hold the obligation in most cases. An example can be seen in
// `src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs`:
//
// error[E0277]: the trait bound `bool: Bar` is not satisfied
// --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
// |
// LL | type Assoc: Bar;
// | ----- associated type defined here
// ...
// LL | impl Foo for () {
// | --------------- in this `impl` item
// LL | type Assoc = bool;
// | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
//
// If the obligation comes from the where clause in the `trait`, we point at it:
//
// error[E0277]: the trait bound `bool: Bar` is not satisfied
// --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
// |
// | trait Foo where <Self as Foo>>::Assoc: Bar {
// | -------------------------- restricted in this bound
// LL | type Assoc;
// | ----- associated type defined here
// ...
// LL | impl Foo for () {
// | --------------- in this `impl` item
// LL | type Assoc = bool;
// | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
if let (
ty::Projection(ty::ProjectionTy { item_def_id, .. }),
Some(hir::ItemKind::Impl { items, .. }),
) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind))
{
if let Some((impl_item, trait_assoc_item)) = trait_assoc_items
.find(|i| i.def_id == *item_def_id)
.and_then(|trait_assoc_item| {
items
.iter()
.find(|i| i.ident == trait_assoc_item.ident)
.map(|impl_item| (impl_item, trait_assoc_item))
})
{
let bounds = trait_generics
.map(|generics| {
get_generic_bound_spans(&generics, trait_name, trait_assoc_item.ident)
})
.unwrap_or_else(Vec::new);
cause.span = impl_item.span;
cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
impl_span: item_span,
original: trait_assoc_item.ident.span,
bounds,
}));
}
}
}
_ => {}
}
}
impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
traits::ObligationCause::new(self.span, self.body_id, code)
@ -163,170 +309,20 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let cause = self.cause(traits::MiscObligation);
let param_env = self.param_env;
let item = &self.item;
let extend_cause_with_original_assoc_item_obligation =
|cause: &mut traits::ObligationCause<'_>,
pred: &ty::Predicate<'_>,
trait_assoc_items: &[ty::AssocItem]| {
let trait_item = tcx
.hir()
.as_local_hir_id(trait_ref.def_id)
.and_then(|trait_id| tcx.hir().find(trait_id));
let (trait_name, trait_generics) = match trait_item {
Some(hir::Node::Item(hir::Item {
ident,
kind: hir::ItemKind::Trait(.., generics, _, _),
..
}))
| Some(hir::Node::Item(hir::Item {
ident,
kind: hir::ItemKind::TraitAlias(generics, _),
..
})) => (Some(ident), Some(generics)),
_ => (None, None),
};
let item_span = item.map(|i| tcx.sess.source_map().guess_head_span(i.span));
match pred {
ty::Predicate::Projection(proj) => {
// The obligation comes not from the current `impl` nor the `trait` being
// implemented, but rather from a "second order" obligation, like in
// `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`:
//
// error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()`
// --> $DIR/point-at-type-on-obligation-failure.rs:13:5
// |
// LL | type Ok;
// | -- associated type defined here
// ...
// LL | impl Bar for Foo {
// | ---------------- in this `impl` item
// LL | type Ok = ();
// | ^^^^^^^^^^^^^ expected `u32`, found `()`
// |
// = note: expected type `u32`
// found type `()`
//
// FIXME: we would want to point a span to all places that contributed to this
// obligation. In the case above, it should be closer to:
//
// error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()`
// --> $DIR/point-at-type-on-obligation-failure.rs:13:5
// |
// LL | type Ok;
// | -- associated type defined here
// LL | type Sibling: Bar2<Ok=Self::Ok>;
// | -------------------------------- obligation set here
// ...
// LL | impl Bar for Foo {
// | ---------------- in this `impl` item
// LL | type Ok = ();
// | ^^^^^^^^^^^^^ expected `u32`, found `()`
// ...
// LL | impl Bar2 for Foo2 {
// | ---------------- in this `impl` item
// LL | type Ok = u32;
// | -------------- obligation set here
// |
// = note: expected type `u32`
// found type `()`
if let Some(hir::ItemKind::Impl { items, .. }) = item.map(|i| &i.kind) {
let trait_assoc_item = tcx.associated_item(proj.projection_def_id());
if let Some(impl_item) =
items.iter().find(|item| item.ident == trait_assoc_item.ident)
{
cause.span = impl_item.span;
cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
impl_span: item_span,
original: trait_assoc_item.ident.span,
bounds: vec![],
}));
}
}
}
ty::Predicate::Trait(proj, _) => {
// An associated item obligation born out of the `trait` failed to be met.
// Point at the `impl` that failed the obligation, the associated item that
// needed to meet the obligation, and the definition of that associated item,
// which should hold the obligation in most cases. An example can be seen in
// `src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs`:
//
// error[E0277]: the trait bound `bool: Bar` is not satisfied
// --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
// |
// LL | type Assoc: Bar;
// | ----- associated type defined here
// ...
// LL | impl Foo for () {
// | --------------- in this `impl` item
// LL | type Assoc = bool;
// | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
//
// If the obligation comes from the where clause in the `trait`, we point at it:
//
// error[E0277]: the trait bound `bool: Bar` is not satisfied
// --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
// |
// | trait Foo where <Self as Foo>>::Assoc: Bar {
// | -------------------------- restricted in this bound
// LL | type Assoc;
// | ----- associated type defined here
// ...
// LL | impl Foo for () {
// | --------------- in this `impl` item
// LL | type Assoc = bool;
// | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
if let (
ty::Projection(ty::ProjectionTy { item_def_id, .. }),
Some(hir::ItemKind::Impl { items, .. }),
) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind))
{
if let Some((impl_item, trait_assoc_item)) = trait_assoc_items
.iter()
.find(|i| i.def_id == *item_def_id)
.and_then(|trait_assoc_item| {
items
.iter()
.find(|i| i.ident == trait_assoc_item.ident)
.map(|impl_item| (impl_item, trait_assoc_item))
})
{
let bounds = trait_generics
.map(|generics| {
get_generic_bound_spans(
&generics,
trait_name,
trait_assoc_item.ident,
)
})
.unwrap_or_else(Vec::new);
cause.span = impl_item.span;
cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
impl_span: item_span,
original: trait_assoc_item.ident.span,
bounds,
}));
}
}
}
_ => {}
}
};
let item = self.item;
if let Elaborate::All = elaborate {
// FIXME: Make `extend_cause_with_original_assoc_item_obligation` take an iterator
// instead of a slice.
let trait_assoc_items: Vec<_> =
tcx.associated_items(trait_ref.def_id).in_definition_order().copied().collect();
let predicates = obligations.iter().map(|obligation| obligation.predicate).collect();
let implied_obligations = traits::elaborate_predicates(tcx, predicates);
let implied_obligations = implied_obligations.map(|pred| {
let mut cause = cause.clone();
extend_cause_with_original_assoc_item_obligation(
tcx,
trait_ref,
item,
&mut cause,
&pred,
&*trait_assoc_items,
tcx.associated_items(trait_ref.def_id).in_definition_order().copied(),
);
traits::Obligation::new(cause, param_env, pred)
});

View File

@ -1,29 +1,48 @@
// check-pass
#![warn(unused_braces, unused_parens)]
fn consume<T>(_: T) {}
fn main() {
let _ = (7);
//~^WARN unnecessary parentheses
let _ = { 7 };
//~^ WARN unnecessary braces
// Do not emit a lint in these cases,
// as we have to be careful with
// `ref` patterns.
{
let _ = { 7 };
if let 7 = { 7 } {
if let 7 = { 7 } { }
match { 7 } {
_ => (),
}
}
if { true } {
//~^ WARN unnecessary braces
}
while { false } {
//~^ WARN unnecessary braces
}
let _: [u8; { 3 }];
//~^ WARN unnecessary braces
// do not emit error for multiline blocks.
consume({ 7 });
//~^ WARN unnecessary braces
// Do not emit lint for multiline blocks.
let _ = {
7
};
// do not emit error for unsafe blocks.
// Do not emit lint for unsafe blocks.
let _ = unsafe { 7 };
// do not emit error, as the `{` would then
// Do not emit lint, as the `{` would then
// be parsed as part of the `return`.
if { return } {

View File

@ -1,5 +1,5 @@
warning: unnecessary parentheses around assigned value
--> $DIR/unused_braces.rs:5:13
--> $DIR/unused_braces.rs:7:13
|
LL | let _ = (7);
| ^^^ help: remove these parentheses
@ -10,11 +10,11 @@ note: the lint level is defined here
LL | #![warn(unused_braces, unused_parens)]
| ^^^^^^^^^^^^^
warning: unnecessary braces around assigned value
--> $DIR/unused_braces.rs:8:13
warning: unnecessary braces around `if` condition
--> $DIR/unused_braces.rs:23:8
|
LL | let _ = { 7 };
| ^^^^^ help: remove these braces
LL | if { true } {
| ^^^^^^^^ help: remove these braces
|
note: the lint level is defined here
--> $DIR/unused_braces.rs:2:9
@ -22,15 +22,21 @@ note: the lint level is defined here
LL | #![warn(unused_braces, unused_parens)]
| ^^^^^^^^^^^^^
warning: unnecessary braces around `let` scrutinee expression
--> $DIR/unused_braces.rs:11:16
warning: unnecessary braces around `while` condition
--> $DIR/unused_braces.rs:27:11
|
LL | if let 7 = { 7 } {
| ^^^^^ help: remove these braces
LL | while { false } {
| ^^^^^^^^^ help: remove these braces
warning: unnecessary braces around const expression
--> $DIR/unused_braces.rs:15:17
--> $DIR/unused_braces.rs:31:17
|
LL | let _: [u8; { 3 }];
| ^^^^^ help: remove these braces
warning: unnecessary braces around function argument
--> $DIR/unused_braces.rs:34:13
|
LL | consume({ 7 });
| ^^^^^ help: remove these braces

View File

@ -10,13 +10,15 @@ struct A {
b: u32,
}
fn consume<T>(_: T) {}
fn main() {
let a = A {
a: 42,
b: 1729,
};
let _ = &{ a.b };
let _ = { a.b };
consume(&{ a.b });
consume({ a.b });
//~^ WARN unnecessary braces
}

View File

@ -1,11 +1,11 @@
warning: unnecessary braces around assigned value
--> $DIR/unused_parens_borrow.rs:20:13
warning: unnecessary braces around function argument
--> $DIR/unused_braces_borrow.rs:22:13
|
LL | let _ = { a.b };
LL | consume({ a.b });
| ^^^^^^^ help: remove these braces
|
note: the lint level is defined here
--> $DIR/unused_parens_borrow.rs:2:9
--> $DIR/unused_braces_borrow.rs:2:9
|
LL | #![warn(unused_braces)]
| ^^^^^^^^^^^^^