1
Fork 0

Auto merge of #75145 - davidtwco:issue-60607-preallocate-defid-for-lang-items, r=petrochenkov

Reference lang items during AST lowering

Fixes #60607 and fixes #61019.

This PR introduces `QPath::LangItem` to the HIR and uses it in AST lowering instead of constructing a `hir::Path` from a slice of symbols:

- Credit for much of this work goes to @matthewjasper, I basically just [rebased their earlier work](a227c706b7 (diff-c0f791ead38d2d02916faaad0f56f41d)).
- ~~Changes to Clippy might not be correct, they compile but attempting to run tests through `./x.py` produced failures which appeared spurious, so I didn't run any clippy tests.~~
- Changes to save analysis might not be correct - tests pass but I don't have a lot of confidence in those changes being correct.
- I've used `GenericBounds::LangItemTrait` rather than changing `PolyTraitRef`, as suggested by @matthewjasper [in this comment](a227c706b7 (r40107992)) but I'd prefer that be left for a follow-up.
- I've split things into smaller commits fairly arbitrarily to make the diff easier to review, each commit should compile but might not pass tests until the final commit.

r? @oli-obk
cc @matthewjasper
This commit is contained in:
bors 2020-08-17 20:51:59 +00:00
commit 792c645ca7
61 changed files with 588 additions and 458 deletions

View file

@ -385,6 +385,7 @@ pub trait Into<T>: Sized {
))] ))]
pub trait From<T>: Sized { pub trait From<T>: Sized {
/// Performs the conversion. /// Performs the conversion.
#[cfg_attr(not(bootstrap), lang = "from")]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
fn from(_: T) -> Self; fn from(_: T) -> Self;
} }

View file

@ -96,6 +96,7 @@ pub trait Future {
/// [`Context`]: ../task/struct.Context.html /// [`Context`]: ../task/struct.Context.html
/// [`Waker`]: ../task/struct.Waker.html /// [`Waker`]: ../task/struct.Waker.html
/// [`Waker::wake`]: ../task/struct.Waker.html#method.wake /// [`Waker::wake`]: ../task/struct.Waker.html#method.wake
#[cfg_attr(not(bootstrap), lang = "poll")]
#[stable(feature = "futures_api", since = "1.36.0")] #[stable(feature = "futures_api", since = "1.36.0")]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
} }

View file

@ -53,6 +53,7 @@ unsafe impl Sync for ResumeTy {}
/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give /// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give
/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). /// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`).
// This is `const` to avoid extra errors after we recover from `const async fn` // This is `const` to avoid extra errors after we recover from `const async fn`
#[cfg_attr(not(bootstrap), lang = "from_generator")]
#[doc(hidden)] #[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")] #[unstable(feature = "gen_future", issue = "50547")]
#[inline] #[inline]
@ -85,6 +86,7 @@ where
GenFuture(gen) GenFuture(gen)
} }
#[cfg_attr(not(bootstrap), lang = "get_context")]
#[doc(hidden)] #[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")] #[unstable(feature = "gen_future", issue = "50547")]
#[inline] #[inline]

View file

@ -235,6 +235,7 @@ pub trait IntoIterator {
/// assert_eq!(Some(3), iter.next()); /// assert_eq!(Some(3), iter.next());
/// assert_eq!(None, iter.next()); /// assert_eq!(None, iter.next());
/// ``` /// ```
#[cfg_attr(not(bootstrap), lang = "into_iter")]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
fn into_iter(self) -> Self::IntoIter; fn into_iter(self) -> Self::IntoIter;
} }

View file

@ -129,6 +129,7 @@ pub trait Iterator {
/// assert_eq!(None, iter.next()); /// assert_eq!(None, iter.next());
/// assert_eq!(None, iter.next()); /// assert_eq!(None, iter.next());
/// ``` /// ```
#[cfg_attr(not(bootstrap), lang = "next")]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
fn next(&mut self) -> Option<Self::Item>; fn next(&mut self) -> Option<Self::Item>;

View file

@ -38,6 +38,7 @@ use crate::hash::Hash;
/// [`IntoIterator`]: ../iter/trait.Iterator.html /// [`IntoIterator`]: ../iter/trait.Iterator.html
/// [`Iterator`]: ../iter/trait.IntoIterator.html /// [`Iterator`]: ../iter/trait.IntoIterator.html
/// [slicing index]: ../slice/trait.SliceIndex.html /// [slicing index]: ../slice/trait.SliceIndex.html
#[cfg_attr(not(bootstrap), lang = "RangeFull")]
#[doc(alias = "..")] #[doc(alias = "..")]
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -70,6 +71,7 @@ impl fmt::Debug for RangeFull {
/// assert_eq!(arr[1.. 3], [ 1,2 ]); // Range /// assert_eq!(arr[1.. 3], [ 1,2 ]); // Range
/// assert_eq!(arr[1..=3], [ 1,2,3 ]); /// assert_eq!(arr[1..=3], [ 1,2,3 ]);
/// ``` /// ```
#[cfg_attr(not(bootstrap), lang = "Range")]
#[doc(alias = "..")] #[doc(alias = "..")]
#[derive(Clone, Default, PartialEq, Eq, Hash)] // not Copy -- see #27186 #[derive(Clone, Default, PartialEq, Eq, Hash)] // not Copy -- see #27186
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -178,6 +180,7 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
/// ``` /// ```
/// ///
/// [`Iterator`]: ../iter/trait.IntoIterator.html /// [`Iterator`]: ../iter/trait.IntoIterator.html
#[cfg_attr(not(bootstrap), lang = "RangeFrom")]
#[doc(alias = "..")] #[doc(alias = "..")]
#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -260,6 +263,7 @@ impl<Idx: PartialOrd<Idx>> RangeFrom<Idx> {
/// [`IntoIterator`]: ../iter/trait.Iterator.html /// [`IntoIterator`]: ../iter/trait.Iterator.html
/// [`Iterator`]: ../iter/trait.IntoIterator.html /// [`Iterator`]: ../iter/trait.IntoIterator.html
/// [slicing index]: ../slice/trait.SliceIndex.html /// [slicing index]: ../slice/trait.SliceIndex.html
#[cfg_attr(not(bootstrap), lang = "RangeTo")]
#[doc(alias = "..")] #[doc(alias = "..")]
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -328,6 +332,7 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
/// assert_eq!(arr[1.. 3], [ 1,2 ]); /// assert_eq!(arr[1.. 3], [ 1,2 ]);
/// assert_eq!(arr[1..=3], [ 1,2,3 ]); // RangeInclusive /// assert_eq!(arr[1..=3], [ 1,2,3 ]); // RangeInclusive
/// ``` /// ```
#[cfg_attr(not(bootstrap), lang = "RangeInclusive")]
#[doc(alias = "..=")] #[doc(alias = "..=")]
#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
#[stable(feature = "inclusive_range", since = "1.26.0")] #[stable(feature = "inclusive_range", since = "1.26.0")]
@ -359,6 +364,7 @@ impl<Idx> RangeInclusive<Idx> {
/// ///
/// assert_eq!(3..=5, RangeInclusive::new(3, 5)); /// assert_eq!(3..=5, RangeInclusive::new(3, 5));
/// ``` /// ```
#[cfg_attr(not(bootstrap), lang = "range_inclusive_new")]
#[stable(feature = "inclusive_range_methods", since = "1.27.0")] #[stable(feature = "inclusive_range_methods", since = "1.27.0")]
#[inline] #[inline]
#[rustc_promotable] #[rustc_promotable]
@ -555,6 +561,7 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// [`IntoIterator`]: ../iter/trait.Iterator.html /// [`IntoIterator`]: ../iter/trait.Iterator.html
/// [`Iterator`]: ../iter/trait.IntoIterator.html /// [`Iterator`]: ../iter/trait.IntoIterator.html
/// [slicing index]: ../slice/trait.SliceIndex.html /// [slicing index]: ../slice/trait.SliceIndex.html
#[cfg_attr(not(bootstrap), lang = "RangeToInclusive")]
#[doc(alias = "..=")] #[doc(alias = "..=")]
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[stable(feature = "inclusive_range", since = "1.26.0")] #[stable(feature = "inclusive_range", since = "1.26.0")]

View file

@ -43,16 +43,19 @@ pub trait Try {
/// in the return type of the enclosing scope (which must itself implement /// in the return type of the enclosing scope (which must itself implement
/// `Try`). Specifically, the value `X::from_error(From::from(e))` /// `Try`). Specifically, the value `X::from_error(From::from(e))`
/// is returned, where `X` is the return type of the enclosing function. /// is returned, where `X` is the return type of the enclosing function.
#[cfg_attr(not(bootstrap), lang = "into_result")]
#[unstable(feature = "try_trait", issue = "42327")] #[unstable(feature = "try_trait", issue = "42327")]
fn into_result(self) -> Result<Self::Ok, Self::Error>; fn into_result(self) -> Result<Self::Ok, Self::Error>;
/// Wrap an error value to construct the composite result. For example, /// Wrap an error value to construct the composite result. For example,
/// `Result::Err(x)` and `Result::from_error(x)` are equivalent. /// `Result::Err(x)` and `Result::from_error(x)` are equivalent.
#[cfg_attr(not(bootstrap), lang = "from_error")]
#[unstable(feature = "try_trait", issue = "42327")] #[unstable(feature = "try_trait", issue = "42327")]
fn from_error(v: Self::Error) -> Self; fn from_error(v: Self::Error) -> Self;
/// Wrap an OK value to construct the composite result. For example, /// Wrap an OK value to construct the composite result. For example,
/// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent. /// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent.
#[cfg_attr(not(bootstrap), lang = "from_ok")]
#[unstable(feature = "try_trait", issue = "42327")] #[unstable(feature = "try_trait", issue = "42327")]
fn from_ok(v: Self::Ok) -> Self; fn from_ok(v: Self::Ok) -> Self;
} }

View file

@ -144,9 +144,11 @@ use crate::{
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub enum Option<T> { pub enum Option<T> {
/// No value /// No value
#[cfg_attr(not(bootstrap), lang = "None")]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
None, None,
/// Some value `T` /// Some value `T`
#[cfg_attr(not(bootstrap), lang = "Some")]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
Some(#[stable(feature = "rust1", since = "1.0.0")] T), Some(#[stable(feature = "rust1", since = "1.0.0")] T),
} }

View file

@ -569,6 +569,7 @@ impl<P: Deref> Pin<P> {
/// ``` /// ```
/// ///
/// [`mem::swap`]: ../../std/mem/fn.swap.html /// [`mem::swap`]: ../../std/mem/fn.swap.html
#[cfg_attr(not(bootstrap), lang = "new_unchecked")]
#[stable(feature = "pin", since = "1.33.0")] #[stable(feature = "pin", since = "1.33.0")]
#[inline(always)] #[inline(always)]
pub unsafe fn new_unchecked(pointer: P) -> Pin<P> { pub unsafe fn new_unchecked(pointer: P) -> Pin<P> {

View file

@ -246,10 +246,12 @@ use crate::{convert, fmt};
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub enum Result<T, E> { pub enum Result<T, E> {
/// Contains the success value /// Contains the success value
#[cfg_attr(not(bootstrap), lang = "Ok")]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
Ok(#[stable(feature = "rust1", since = "1.0.0")] T), Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
/// Contains the error value /// Contains the error value
#[cfg_attr(not(bootstrap), lang = "Err")]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
Err(#[stable(feature = "rust1", since = "1.0.0")] E), Err(#[stable(feature = "rust1", since = "1.0.0")] E),
} }

View file

@ -10,6 +10,7 @@ use crate::result::Result;
#[stable(feature = "futures_api", since = "1.36.0")] #[stable(feature = "futures_api", since = "1.36.0")]
pub enum Poll<T> { pub enum Poll<T> {
/// Represents that a value is immediately ready. /// Represents that a value is immediately ready.
#[cfg_attr(not(bootstrap), lang = "Ready")]
#[stable(feature = "futures_api", since = "1.36.0")] #[stable(feature = "futures_api", since = "1.36.0")]
Ready(#[stable(feature = "futures_api", since = "1.36.0")] T), Ready(#[stable(feature = "futures_api", since = "1.36.0")] T),
@ -18,6 +19,7 @@ pub enum Poll<T> {
/// When a function returns `Pending`, the function *must* also /// When a function returns `Pending`, the function *must* also
/// ensure that the current task is scheduled to be awoken when /// ensure that the current task is scheduled to be awoken when
/// progress can be made. /// progress can be made.
#[cfg_attr(not(bootstrap), lang = "Pending")]
#[stable(feature = "futures_api", since = "1.36.0")] #[stable(feature = "futures_api", since = "1.36.0")]
Pending, Pending,
} }

View file

@ -449,7 +449,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `::std::ops::Try::from_ok($tail_expr)` // `::std::ops::Try::from_ok($tail_expr)`
block.expr = Some(this.wrap_in_try_constructor( block.expr = Some(this.wrap_in_try_constructor(
sym::from_ok, hir::LangItem::TryFromOk,
try_span, try_span,
tail_expr, tail_expr,
ok_wrapped_span, ok_wrapped_span,
@ -461,14 +461,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn wrap_in_try_constructor( fn wrap_in_try_constructor(
&mut self, &mut self,
method: Symbol, lang_item: hir::LangItem,
method_span: Span, method_span: Span,
expr: &'hir hir::Expr<'hir>, expr: &'hir hir::Expr<'hir>,
overall_span: Span, overall_span: Span,
) -> &'hir hir::Expr<'hir> { ) -> &'hir hir::Expr<'hir> {
let path = &[sym::ops, sym::Try, method];
let constructor = let constructor =
self.arena.alloc(self.expr_std_path(method_span, path, None, ThinVec::new())); self.arena.alloc(self.expr_lang_item_path(method_span, lang_item, ThinVec::new()));
self.expr_call(overall_span, constructor, std::slice::from_ref(expr)) self.expr_call(overall_span, constructor, std::slice::from_ref(expr))
} }
@ -558,12 +557,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `future::from_generator`: // `future::from_generator`:
let unstable_span = let unstable_span =
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone()); self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
let gen_future = self.expr_std_path( let gen_future =
unstable_span, self.expr_lang_item_path(unstable_span, hir::LangItem::FromGenerator, ThinVec::new());
&[sym::future, sym::from_generator],
None,
ThinVec::new(),
);
// `future::from_generator(generator)`: // `future::from_generator(generator)`:
hir::ExprKind::Call(self.arena.alloc(gen_future), arena_vec![self; generator]) hir::ExprKind::Call(self.arena.alloc(gen_future), arena_vec![self; generator])
@ -630,23 +625,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Use of `await` outside of an async context, we cannot use `task_context` here. // Use of `await` outside of an async context, we cannot use `task_context` here.
self.expr_err(span) self.expr_err(span)
}; };
let pin_ty_id = self.next_id(); let new_unchecked = self.expr_call_lang_item_fn_mut(
let new_unchecked_expr_kind = self.expr_call_std_assoc_fn(
pin_ty_id,
span, span,
&[sym::pin, sym::Pin], hir::LangItem::PinNewUnchecked,
"new_unchecked",
arena_vec![self; ref_mut_pinned], arena_vec![self; ref_mut_pinned],
); );
let new_unchecked = self.expr(span, new_unchecked_expr_kind, ThinVec::new()); let get_context = self.expr_call_lang_item_fn_mut(
let get_context = self.expr_call_std_path_mut(
gen_future_span, gen_future_span,
&[sym::future, sym::get_context], hir::LangItem::GetContext,
arena_vec![self; task_context], arena_vec![self; task_context],
); );
let call = self.expr_call_std_path( let call = self.expr_call_lang_item_fn(
span, span,
&[sym::future, sym::Future, sym::poll], hir::LangItem::FuturePoll,
arena_vec![self; new_unchecked, get_context], arena_vec![self; new_unchecked, get_context],
); );
self.arena.alloc(self.expr_unsafe(call)) self.arena.alloc(self.expr_unsafe(call))
@ -659,11 +650,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
let x_ident = Ident::with_dummy_span(sym::result); let x_ident = Ident::with_dummy_span(sym::result);
let (x_pat, x_pat_hid) = self.pat_ident(span, x_ident); let (x_pat, x_pat_hid) = self.pat_ident(span, x_ident);
let x_expr = self.expr_ident(span, x_ident, x_pat_hid); let x_expr = self.expr_ident(span, x_ident, x_pat_hid);
let ready_pat = self.pat_std_enum( let ready_field = self.single_pat_field(span, x_pat);
span, let ready_pat = self.pat_lang_item_variant(span, hir::LangItem::PollReady, ready_field);
&[sym::task, sym::Poll, sym::Ready],
arena_vec![self; x_pat],
);
let break_x = self.with_loop_scope(loop_node_id, move |this| { let break_x = self.with_loop_scope(loop_node_id, move |this| {
let expr_break = let expr_break =
hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr)); hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr));
@ -674,7 +662,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `::std::task::Poll::Pending => {}` // `::std::task::Poll::Pending => {}`
let pending_arm = { let pending_arm = {
let pending_pat = self.pat_std_enum(span, &[sym::task, sym::Poll, sym::Pending], &[]); let pending_pat = self.pat_lang_item_variant(span, hir::LangItem::PollPending, &[]);
let empty_block = self.expr_block_empty(span); let empty_block = self.expr_block_empty(span);
self.arm(pending_pat, empty_block) self.arm(pending_pat, empty_block)
}; };
@ -842,16 +830,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`. /// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> { fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
let id = self.next_id();
let e1 = self.lower_expr_mut(e1); let e1 = self.lower_expr_mut(e1);
let e2 = self.lower_expr_mut(e2); let e2 = self.lower_expr_mut(e2);
self.expr_call_std_assoc_fn( let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, span);
id, let fn_expr =
span, self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path), ThinVec::new()));
&[sym::ops, sym::RangeInclusive], hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2])
"new",
arena_vec![self; e1, e2],
)
} }
fn lower_expr_range( fn lower_expr_range(
@ -863,12 +847,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> hir::ExprKind<'hir> { ) -> hir::ExprKind<'hir> {
use rustc_ast::ast::RangeLimits::*; use rustc_ast::ast::RangeLimits::*;
let path = match (e1, e2, lims) { let lang_item = match (e1, e2, lims) {
(None, None, HalfOpen) => sym::RangeFull, (None, None, HalfOpen) => hir::LangItem::RangeFull,
(Some(..), None, HalfOpen) => sym::RangeFrom, (Some(..), None, HalfOpen) => hir::LangItem::RangeFrom,
(None, Some(..), HalfOpen) => sym::RangeTo, (None, Some(..), HalfOpen) => hir::LangItem::RangeTo,
(Some(..), Some(..), HalfOpen) => sym::Range, (Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
(None, Some(..), Closed) => sym::RangeToInclusive, (None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
(Some(..), Some(..), Closed) => unreachable!(), (Some(..), Some(..), Closed) => unreachable!(),
(_, None, Closed) => { (_, None, Closed) => {
self.diagnostic().span_fatal(span, "inclusive range with no end").raise() self.diagnostic().span_fatal(span, "inclusive range with no end").raise()
@ -883,16 +867,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}), }),
); );
let is_unit = fields.is_empty(); hir::ExprKind::Struct(self.arena.alloc(hir::QPath::LangItem(lang_item, span)), fields, None)
let struct_path = [sym::ops, path];
let struct_path = self.std_path(span, &struct_path, None, is_unit);
let struct_path = hir::QPath::Resolved(None, struct_path);
if is_unit {
hir::ExprKind::Path(struct_path)
} else {
hir::ExprKind::Struct(self.arena.alloc(struct_path), fields, None)
}
} }
fn lower_loop_destination(&mut self, destination: Option<(NodeId, Label)>) -> hir::Destination { fn lower_loop_destination(&mut self, destination: Option<(NodeId, Label)>) -> hir::Destination {
@ -1412,9 +1387,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
let match_expr = { let match_expr = {
let iter = self.expr_ident(desugared_span, iter, iter_pat_nid); let iter = self.expr_ident(desugared_span, iter, iter_pat_nid);
let ref_mut_iter = self.expr_mut_addr_of(desugared_span, iter); let ref_mut_iter = self.expr_mut_addr_of(desugared_span, iter);
let next_path = &[sym::iter, sym::Iterator, sym::next]; let next_expr = self.expr_call_lang_item_fn(
let next_expr = desugared_span,
self.expr_call_std_path(desugared_span, next_path, arena_vec![self; ref_mut_iter]); hir::LangItem::IteratorNext,
arena_vec![self; ref_mut_iter],
);
let arms = arena_vec![self; pat_arm, break_arm]; let arms = arena_vec![self; pat_arm, break_arm];
self.expr_match(desugared_span, next_expr, arms, hir::MatchSource::ForLoopDesugar) self.expr_match(desugared_span, next_expr, arms, hir::MatchSource::ForLoopDesugar)
@ -1472,8 +1449,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }` // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
let into_iter_expr = { let into_iter_expr = {
let into_iter_path = &[sym::iter, sym::IntoIterator, sym::into_iter]; self.expr_call_lang_item_fn(
self.expr_call_std_path(into_iter_span, into_iter_path, arena_vec![self; head]) into_iter_span,
hir::LangItem::IntoIterIntoIter,
arena_vec![self; head],
)
}; };
let match_expr = self.arena.alloc(self.expr_match( let match_expr = self.arena.alloc(self.expr_match(
@ -1521,8 +1501,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
// expand <expr> // expand <expr>
let sub_expr = self.lower_expr_mut(sub_expr); let sub_expr = self.lower_expr_mut(sub_expr);
let path = &[sym::ops, sym::Try, sym::into_result]; self.expr_call_lang_item_fn(
self.expr_call_std_path(unstable_span, path, arena_vec![self; sub_expr]) unstable_span,
hir::LangItem::TryIntoResult,
arena_vec![self; sub_expr],
)
}; };
// `#[allow(unreachable_code)]` // `#[allow(unreachable_code)]`
@ -1558,12 +1541,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
let err_ident = Ident::with_dummy_span(sym::err); let err_ident = Ident::with_dummy_span(sym::err);
let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident); let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident);
let from_expr = { let from_expr = {
let from_path = &[sym::convert, sym::From, sym::from];
let err_expr = self.expr_ident_mut(try_span, err_ident, err_local_nid); let err_expr = self.expr_ident_mut(try_span, err_ident, err_local_nid);
self.expr_call_std_path(try_span, from_path, arena_vec![self; err_expr]) self.expr_call_lang_item_fn(
try_span,
hir::LangItem::FromFrom,
arena_vec![self; err_expr],
)
}; };
let from_err_expr = let from_err_expr = self.wrap_in_try_constructor(
self.wrap_in_try_constructor(sym::from_error, unstable_span, from_expr, try_span); hir::LangItem::TryFromError,
unstable_span,
from_expr,
try_span,
);
let thin_attrs = ThinVec::from(attrs); let thin_attrs = ThinVec::from(attrs);
let catch_scope = self.catch_scopes.last().copied(); let catch_scope = self.catch_scopes.last().copied();
let ret_expr = if let Some(catch_node) = catch_scope { let ret_expr = if let Some(catch_node) = catch_scope {
@ -1674,63 +1664,32 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.arena.alloc(self.expr_call_mut(span, e, args)) self.arena.alloc(self.expr_call_mut(span, e, args))
} }
// Note: associated functions must use `expr_call_std_path`. fn expr_call_lang_item_fn_mut(
fn expr_call_std_path_mut(
&mut self, &mut self,
span: Span, span: Span,
path_components: &[Symbol], lang_item: hir::LangItem,
args: &'hir [hir::Expr<'hir>], args: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> { ) -> hir::Expr<'hir> {
let path = let path = self.arena.alloc(self.expr_lang_item_path(span, lang_item, ThinVec::new()));
self.arena.alloc(self.expr_std_path(span, path_components, None, ThinVec::new()));
self.expr_call_mut(span, path, args) self.expr_call_mut(span, path, args)
} }
fn expr_call_std_path( fn expr_call_lang_item_fn(
&mut self, &mut self,
span: Span, span: Span,
path_components: &[Symbol], lang_item: hir::LangItem,
args: &'hir [hir::Expr<'hir>], args: &'hir [hir::Expr<'hir>],
) -> &'hir hir::Expr<'hir> { ) -> &'hir hir::Expr<'hir> {
self.arena.alloc(self.expr_call_std_path_mut(span, path_components, args)) self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args))
} }
// Create an expression calling an associated function of an std type. fn expr_lang_item_path(
//
// Associated functions cannot be resolved through the normal `std_path` function,
// as they are resolved differently and so cannot use `expr_call_std_path`.
//
// This function accepts the path component (`ty_path_components`) separately from
// the name of the associated function (`assoc_fn_name`) in order to facilitate
// separate resolution of the type and creation of a path referring to its associated
// function.
fn expr_call_std_assoc_fn(
&mut self,
ty_path_id: hir::HirId,
span: Span,
ty_path_components: &[Symbol],
assoc_fn_name: &str,
args: &'hir [hir::Expr<'hir>],
) -> hir::ExprKind<'hir> {
let ty_path = self.std_path(span, ty_path_components, None, false);
let ty =
self.arena.alloc(self.ty_path(ty_path_id, span, hir::QPath::Resolved(None, ty_path)));
let fn_seg = self.arena.alloc(hir::PathSegment::from_ident(Ident::from_str(assoc_fn_name)));
let fn_path = hir::QPath::TypeRelative(ty, fn_seg);
let fn_expr =
self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path), ThinVec::new()));
hir::ExprKind::Call(fn_expr, args)
}
fn expr_std_path(
&mut self, &mut self,
span: Span, span: Span,
components: &[Symbol], lang_item: hir::LangItem,
params: Option<&'hir hir::GenericArgs<'hir>>,
attrs: AttrVec, attrs: AttrVec,
) -> hir::Expr<'hir> { ) -> hir::Expr<'hir> {
let path = self.std_path(span, components, params, true); self.expr(span, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, span)), attrs)
self.expr(span, hir::ExprKind::Path(hir::QPath::Resolved(None, path)), attrs)
} }
pub(super) fn expr_ident( pub(super) fn expr_ident(

View file

@ -85,8 +85,6 @@ const HIR_ID_COUNTER_LOCKED: u32 = 0xFFFFFFFF;
rustc_hir::arena_types!(rustc_arena::declare_arena, [], 'tcx); rustc_hir::arena_types!(rustc_arena::declare_arena, [], 'tcx);
struct LoweringContext<'a, 'hir: 'a> { struct LoweringContext<'a, 'hir: 'a> {
crate_root: Option<Symbol>,
/// Used to assign IDs to HIR nodes that do not directly correspond to AST nodes. /// Used to assign IDs to HIR nodes that do not directly correspond to AST nodes.
sess: &'a Session, sess: &'a Session,
@ -189,16 +187,6 @@ pub trait ResolverAstLowering {
/// This should only return `None` during testing. /// This should only return `None` during testing.
fn definitions(&mut self) -> &mut Definitions; fn definitions(&mut self) -> &mut Definitions;
/// Given suffix `["b", "c", "d"]`, creates an AST path for `[::crate_root]::b::c::d` and
/// resolves it based on `is_value`.
fn resolve_str_path(
&mut self,
span: Span,
crate_root: Option<Symbol>,
components: &[Symbol],
ns: Namespace,
) -> (ast::Path, Res<NodeId>);
fn lint_buffer(&mut self) -> &mut LintBuffer; fn lint_buffer(&mut self) -> &mut LintBuffer;
fn next_node_id(&mut self) -> NodeId; fn next_node_id(&mut self) -> NodeId;
@ -305,7 +293,6 @@ pub fn lower_crate<'a, 'hir>(
let _prof_timer = sess.prof.verbose_generic_activity("hir_lowering"); let _prof_timer = sess.prof.verbose_generic_activity("hir_lowering");
LoweringContext { LoweringContext {
crate_root: sess.parse_sess.injected_crate_name.get().copied(),
sess, sess,
resolver, resolver,
nt_to_tokenstream, nt_to_tokenstream,
@ -2064,23 +2051,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}; };
// "<Output = T>" // "<Output = T>"
let future_params = self.arena.alloc(hir::GenericArgs { let future_args = self.arena.alloc(hir::GenericArgs {
args: &[], args: &[],
bindings: arena_vec![self; self.output_ty_binding(span, output_ty)], bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
parenthesized: false, parenthesized: false,
}); });
// ::std::future::Future<future_params> hir::GenericBound::LangItemTrait(
let future_path = // ::std::future::Future<future_params>
self.std_path(span, &[sym::future, sym::Future], Some(future_params), false); hir::LangItem::FutureTraitLangItem,
span,
hir::GenericBound::Trait( self.next_id(),
hir::PolyTraitRef { future_args,
trait_ref: hir::TraitRef { path: future_path, hir_ref_id: self.next_id() },
bound_generic_params: &[],
span,
},
hir::TraitBoundModifier::None,
) )
} }
@ -2480,35 +2462,47 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} }
fn pat_ok(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> { fn pat_ok(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
self.pat_std_enum(span, &[sym::result, sym::Result, sym::Ok], arena_vec![self; pat]) let field = self.single_pat_field(span, pat);
self.pat_lang_item_variant(span, hir::LangItem::ResultOk, field)
} }
fn pat_err(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> { fn pat_err(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
self.pat_std_enum(span, &[sym::result, sym::Result, sym::Err], arena_vec![self; pat]) let field = self.single_pat_field(span, pat);
self.pat_lang_item_variant(span, hir::LangItem::ResultErr, field)
} }
fn pat_some(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> { fn pat_some(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
self.pat_std_enum(span, &[sym::option, sym::Option, sym::Some], arena_vec![self; pat]) let field = self.single_pat_field(span, pat);
self.pat_lang_item_variant(span, hir::LangItem::OptionSome, field)
} }
fn pat_none(&mut self, span: Span) -> &'hir hir::Pat<'hir> { fn pat_none(&mut self, span: Span) -> &'hir hir::Pat<'hir> {
self.pat_std_enum(span, &[sym::option, sym::Option, sym::None], &[]) self.pat_lang_item_variant(span, hir::LangItem::OptionNone, &[])
} }
fn pat_std_enum( fn single_pat_field(
&mut self, &mut self,
span: Span, span: Span,
components: &[Symbol], pat: &'hir hir::Pat<'hir>,
subpats: &'hir [&'hir hir::Pat<'hir>], ) -> &'hir [hir::FieldPat<'hir>] {
) -> &'hir hir::Pat<'hir> { let field = hir::FieldPat {
let path = self.std_path(span, components, None, true); hir_id: self.next_id(),
let qpath = hir::QPath::Resolved(None, path); ident: Ident::new(sym::integer(0), span),
let pt = if subpats.is_empty() { is_shorthand: false,
hir::PatKind::Path(qpath) pat,
} else { span,
hir::PatKind::TupleStruct(qpath, subpats, None)
}; };
self.pat(span, pt) arena_vec![self; field]
}
fn pat_lang_item_variant(
&mut self,
span: Span,
lang_item: hir::LangItem,
fields: &'hir [hir::FieldPat<'hir>],
) -> &'hir hir::Pat<'hir> {
let qpath = hir::QPath::LangItem(lang_item, span);
self.pat(span, hir::PatKind::Struct(qpath, fields, false))
} }
fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, hir::HirId) { fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, hir::HirId) {
@ -2541,42 +2535,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.arena.alloc(hir::Pat { hir_id: self.next_id(), kind, span }) self.arena.alloc(hir::Pat { hir_id: self.next_id(), kind, span })
} }
/// Given a suffix `["b", "c", "d"]`, returns path `::std::b::c::d` when
/// `fld.cx.use_std`, and `::core::b::c::d` otherwise.
/// The path is also resolved according to `is_value`.
fn std_path(
&mut self,
span: Span,
components: &[Symbol],
params: Option<&'hir hir::GenericArgs<'hir>>,
is_value: bool,
) -> &'hir hir::Path<'hir> {
let ns = if is_value { Namespace::ValueNS } else { Namespace::TypeNS };
let (path, res) = self.resolver.resolve_str_path(span, self.crate_root, components, ns);
let mut segments: Vec<_> = path
.segments
.iter()
.map(|segment| {
let res = self.expect_full_res(segment.id);
hir::PathSegment {
ident: segment.ident,
hir_id: Some(self.lower_node_id(segment.id)),
res: Some(self.lower_res(res)),
infer_args: true,
args: None,
}
})
.collect();
segments.last_mut().unwrap().args = params;
self.arena.alloc(hir::Path {
span,
res: res.map_id(|_| panic!("unexpected `NodeId`")),
segments: self.arena.alloc_from_iter(segments),
})
}
fn ty_path( fn ty_path(
&mut self, &mut self,
mut hir_id: hir::HirId, mut hir_id: hir::HirId,

View file

@ -1,7 +1,7 @@
use crate::def::{DefKind, Namespace, Res}; use crate::def::{DefKind, Namespace, Res};
use crate::def_id::DefId; use crate::def_id::DefId;
crate use crate::hir_id::HirId; crate use crate::hir_id::HirId;
use crate::itemlikevisit; use crate::{itemlikevisit, LangItem};
use rustc_ast::ast::{self, CrateSugar, LlvmAsmDialect}; use rustc_ast::ast::{self, CrateSugar, LlvmAsmDialect};
use rustc_ast::ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, UintTy}; use rustc_ast::ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, UintTy};
@ -13,7 +13,7 @@ use rustc_ast::util::parser::ExprPrecedence;
use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; use rustc_data_structures::sync::{par_for_each_in, Send, Sync};
use rustc_macros::HashStable_Generic; use rustc_macros::HashStable_Generic;
use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{MultiSpan, Span, DUMMY_SP}; use rustc_span::{MultiSpan, Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass; use rustc_target::asm::InlineAsmRegOrRegClass;
@ -363,6 +363,8 @@ pub enum TraitBoundModifier {
#[derive(Debug, HashStable_Generic)] #[derive(Debug, HashStable_Generic)]
pub enum GenericBound<'hir> { pub enum GenericBound<'hir> {
Trait(PolyTraitRef<'hir>, TraitBoundModifier), Trait(PolyTraitRef<'hir>, TraitBoundModifier),
// FIXME(davidtwco): Introduce `PolyTraitRef::LangItem`
LangItemTrait(LangItem, Span, HirId, &'hir GenericArgs<'hir>),
Outlives(Lifetime), Outlives(Lifetime),
} }
@ -377,6 +379,7 @@ impl GenericBound<'_> {
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
match self { match self {
&GenericBound::Trait(ref t, ..) => t.span, &GenericBound::Trait(ref t, ..) => t.span,
&GenericBound::LangItemTrait(_, span, ..) => span,
&GenericBound::Outlives(ref l) => l.span, &GenericBound::Outlives(ref l) => l.span,
} }
} }
@ -1419,10 +1422,10 @@ impl Expr<'_> {
self.is_place_expr(|_| true) self.is_place_expr(|_| true)
} }
// Whether this is a place expression. /// Whether this is a place expression.
// `allow_projections_from` should return `true` if indexing a field or ///
// index expression based on the given expression should be considered a /// `allow_projections_from` should return `true` if indexing a field or index expression based
// place expression. /// on the given expression should be considered a place expression.
pub fn is_place_expr(&self, mut allow_projections_from: impl FnMut(&Self) -> bool) -> bool { pub fn is_place_expr(&self, mut allow_projections_from: impl FnMut(&Self) -> bool) -> bool {
match self.kind { match self.kind {
ExprKind::Path(QPath::Resolved(_, ref path)) => match path.res { ExprKind::Path(QPath::Resolved(_, ref path)) => match path.res {
@ -1441,6 +1444,9 @@ impl Expr<'_> {
allow_projections_from(base) || base.is_place_expr(allow_projections_from) allow_projections_from(base) || base.is_place_expr(allow_projections_from)
} }
// Lang item paths cannot currently be local variables or statics.
ExprKind::Path(QPath::LangItem(..)) => false,
// Partially qualified paths in expressions can only legally // Partially qualified paths in expressions can only legally
// refer to associated items which are always rvalues. // refer to associated items which are always rvalues.
ExprKind::Path(QPath::TypeRelative(..)) ExprKind::Path(QPath::TypeRelative(..))
@ -1489,58 +1495,28 @@ impl Expr<'_> {
/// Checks if the specified expression is a built-in range literal. /// Checks if the specified expression is a built-in range literal.
/// (See: `LoweringContext::lower_expr()`). /// (See: `LoweringContext::lower_expr()`).
/// pub fn is_range_literal(expr: &Expr<'_>) -> bool {
/// 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 {
sm.span_to_snippet(*span).map(|range_src| range_src.contains("..")).unwrap_or(false)
};
match expr.kind { match expr.kind {
// All built-in range literals but `..=` and `..` desugar to `Struct`s. // All built-in range literals but `..=` and `..` desugar to `Struct`s.
ExprKind::Struct(ref qpath, _, _) => { ExprKind::Struct(ref qpath, _, _) => matches!(
if let QPath::Resolved(None, ref path) = **qpath { **qpath,
return is_range_path(&path) && is_lit(sm, &expr.span); QPath::LangItem(
} LangItem::Range
} | LangItem::RangeTo
| LangItem::RangeFrom
// `..` desugars to its struct path. | LangItem::RangeFull
ExprKind::Path(QPath::Resolved(None, ref path)) => { | LangItem::RangeToInclusive,
return is_range_path(&path) && is_lit(sm, &expr.span); _,
} )
),
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`. // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
ExprKind::Call(ref func, _) => { ExprKind::Call(ref func, _) => {
if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind { matches!(func.kind, ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, _)))
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,
} }
false
} }
#[derive(Debug, HashStable_Generic)] #[derive(Debug, HashStable_Generic)]
@ -1677,6 +1653,40 @@ pub enum QPath<'hir> {
/// `<Vec>::new`, and `T::X::Y::method` into `<<<T>::X>::Y>::method`, /// `<Vec>::new`, and `T::X::Y::method` into `<<<T>::X>::Y>::method`,
/// the `X` and `Y` nodes each being a `TyKind::Path(QPath::TypeRelative(..))`. /// the `X` and `Y` nodes each being a `TyKind::Path(QPath::TypeRelative(..))`.
TypeRelative(&'hir Ty<'hir>, &'hir PathSegment<'hir>), TypeRelative(&'hir Ty<'hir>, &'hir PathSegment<'hir>),
/// Reference to a `#[lang = "foo"]` item.
LangItem(LangItem, Span),
}
impl<'hir> QPath<'hir> {
/// Returns the span of this `QPath`.
pub fn span(&self) -> Span {
match *self {
QPath::Resolved(_, path) => path.span,
QPath::TypeRelative(_, ps) => ps.ident.span,
QPath::LangItem(_, span) => span,
}
}
/// Returns the span of the qself of this `QPath`. For example, `()` in
/// `<() as Trait>::method`.
pub fn qself_span(&self) -> Span {
match *self {
QPath::Resolved(_, path) => path.span,
QPath::TypeRelative(qself, _) => qself.span,
QPath::LangItem(_, span) => span,
}
}
/// Returns the span of the last segment of this `QPath`. For example, `method` in
/// `<() as Trait>::method`.
pub fn last_segment_span(&self) -> Span {
match *self {
QPath::Resolved(_, path) => path.segments.last().unwrap().ident.span,
QPath::TypeRelative(_, segment) => segment.ident.span,
QPath::LangItem(_, span) => span,
}
}
} }
/// Hints at the original code for a let statement. /// Hints at the original code for a let statement.

View file

@ -724,6 +724,7 @@ pub fn walk_qpath<'v, V: Visitor<'v>>(
visitor.visit_ty(qself); visitor.visit_ty(qself);
visitor.visit_path_segment(span, segment); visitor.visit_path_segment(span, segment);
} }
QPath::LangItem(..) => {}
} }
} }
@ -838,6 +839,10 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericB
GenericBound::Trait(ref typ, modifier) => { GenericBound::Trait(ref typ, modifier) => {
visitor.visit_poly_trait_ref(typ, modifier); visitor.visit_poly_trait_ref(typ, modifier);
} }
GenericBound::LangItemTrait(_, span, hir_id, args) => {
visitor.visit_id(hir_id);
visitor.visit_generic_args(span, args);
}
GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime), GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime),
} }
} }

View file

@ -10,7 +10,7 @@
pub use self::LangItem::*; pub use self::LangItem::*;
use crate::def_id::DefId; use crate::def_id::DefId;
use crate::Target; use crate::{MethodKind, Target};
use rustc_ast::ast; use rustc_ast::ast;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
@ -307,4 +307,38 @@ language_item_table! {
CountCodeRegionFnLangItem, sym::count_code_region, count_code_region_fn, Target::Fn; CountCodeRegionFnLangItem, sym::count_code_region, count_code_region_fn, Target::Fn;
CoverageCounterAddFnLangItem, sym::coverage_counter_add, coverage_counter_add_fn, Target::Fn; CoverageCounterAddFnLangItem, sym::coverage_counter_add, coverage_counter_add_fn, Target::Fn;
CoverageCounterSubtractFnLangItem, sym::coverage_counter_subtract, coverage_counter_subtract_fn, Target::Fn; CoverageCounterSubtractFnLangItem, sym::coverage_counter_subtract, coverage_counter_subtract_fn, Target::Fn;
// Language items from AST lowering
TryFromError, sym::from_error, from_error_fn, Target::Method(MethodKind::Trait { body: false });
TryFromOk, sym::from_ok, from_ok_fn, Target::Method(MethodKind::Trait { body: false });
TryIntoResult, sym::into_result, into_result_fn, Target::Method(MethodKind::Trait { body: false });
PollReady, sym::Ready, poll_ready_variant, Target::Variant;
PollPending, sym::Pending, poll_pending_variant, Target::Variant;
FromGenerator, sym::from_generator, from_generator_fn, Target::Fn;
GetContext, sym::get_context, get_context_fn, Target::Fn;
FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false });
FromFrom, sym::from, from_fn, Target::Method(MethodKind::Trait { body: false });
OptionSome, sym::Some, option_some_variant, Target::Variant;
OptionNone, sym::None, option_none_variant, Target::Variant;
ResultOk, sym::Ok, result_ok_variant, Target::Variant;
ResultErr, sym::Err, result_err_variant, Target::Variant;
IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false });
IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false});
PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent);
RangeFrom, sym::RangeFrom, range_from_struct, Target::Struct;
RangeFull, sym::RangeFull, range_full_struct, Target::Struct;
RangeInclusiveStruct, sym::RangeInclusive, range_inclusive_struct, Target::Struct;
RangeInclusiveNew, sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent);
Range, sym::Range, range_struct, Target::Struct;
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct;
RangeTo, sym::RangeTo, range_to_struct, Target::Struct;
} }

View file

@ -29,6 +29,7 @@ pub enum Target {
TyAlias, TyAlias,
OpaqueTy, OpaqueTy,
Enum, Enum,
Variant,
Struct, Struct,
Union, Union,
Trait, Trait,
@ -62,6 +63,7 @@ impl Display for Target {
Target::TyAlias => "type alias", Target::TyAlias => "type alias",
Target::OpaqueTy => "opaque type", Target::OpaqueTy => "opaque type",
Target::Enum => "enum", Target::Enum => "enum",
Target::Variant => "enum variant",
Target::Struct => "struct", Target::Struct => "struct",
Target::Union => "union", Target::Union => "union",
Target::Trait => "trait", Target::Trait => "trait",

View file

@ -1729,6 +1729,11 @@ impl<'a> State<'a> {
colons_before_params, colons_before_params,
) )
} }
hir::QPath::LangItem(lang_item, span) => {
self.s.word("#[lang = \"");
self.print_ident(Ident::new(lang_item.name(), span));
self.s.word("\"]");
}
} }
} }
@ -2142,6 +2147,11 @@ impl<'a> State<'a> {
} }
self.print_poly_trait_ref(tref); self.print_poly_trait_ref(tref);
} }
GenericBound::LangItemTrait(lang_item, span, ..) => {
self.s.word("#[lang = \"");
self.print_ident(Ident::new(lang_item.name(), *span));
self.s.word("\"]");
}
GenericBound::Outlives(lt) => { GenericBound::Outlives(lt) => {
self.print_lifetime(lt); self.print_lifetime(lt);
} }

View file

@ -1057,7 +1057,7 @@ impl TypeAliasBounds {
_ => false, _ => false,
} }
} }
hir::QPath::Resolved(..) => false, hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
} }
} }

View file

@ -703,7 +703,7 @@ impl<'tcx> LateContext<'tcx> {
pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
match *qpath { match *qpath {
hir::QPath::Resolved(_, ref path) => path.res, hir::QPath::Resolved(_, ref path) => path.res,
hir::QPath::TypeRelative(..) => self hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
.maybe_typeck_results() .maybe_typeck_results()
.and_then(|typeck_results| typeck_results.type_dependent_def(id)) .and_then(|typeck_results| typeck_results.type_dependent_def(id))
.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),

View file

@ -258,7 +258,7 @@ fn lint_int_literal<'tcx>(
let par_id = cx.tcx.hir().get_parent_node(e.hir_id); let par_id = cx.tcx.hir().get_parent_node(e.hir_id);
if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) { if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) {
if let hir::ExprKind::Struct(..) = par_e.kind { if let hir::ExprKind::Struct(..) = par_e.kind {
if is_range_literal(cx.sess().source_map(), par_e) if is_range_literal(par_e)
&& lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t.name_str()) && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t.name_str())
{ {
// The overflowing literal lint was overridden. // The overflowing literal lint was overridden.
@ -317,7 +317,7 @@ fn lint_uint_literal<'tcx>(
return; return;
} }
} }
hir::ExprKind::Struct(..) if is_range_literal(cx.sess().source_map(), par_e) => { hir::ExprKind::Struct(..) if is_range_literal(par_e) => {
let t = t.name_str(); let t = t.name_str();
if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) { if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) {
// The overflowing literal lint was overridden. // The overflowing literal lint was overridden.

View file

@ -452,7 +452,7 @@ impl<'tcx> TypeckResults<'tcx> {
pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
match *qpath { match *qpath {
hir::QPath::Resolved(_, ref path) => path.res, hir::QPath::Resolved(_, ref path) => path.res,
hir::QPath::TypeRelative(..) => self hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
.type_dependent_def(id) .type_dependent_def(id)
.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),
} }

View file

@ -30,7 +30,13 @@ struct LanguageItemCollector<'tcx> {
impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> { impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> {
fn visit_item(&mut self, item: &hir::Item<'_>) { fn visit_item(&mut self, item: &hir::Item<'_>) {
self.check_for_lang(Target::from_item(item), item.hir_id, item.attrs) self.check_for_lang(Target::from_item(item), item.hir_id, item.attrs);
if let hir::ItemKind::Enum(def, ..) = &item.kind {
for variant in def.variants {
self.check_for_lang(Target::Variant, variant.id, variant.attrs);
}
}
} }
fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) {

View file

@ -526,7 +526,8 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
| hir::ExprKind::Yield(..) | hir::ExprKind::Yield(..)
| hir::ExprKind::Type(..) | hir::ExprKind::Type(..)
| hir::ExprKind::Err | hir::ExprKind::Err
| hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => { | hir::ExprKind::Path(hir::QPath::TypeRelative(..))
| hir::ExprKind::Path(hir::QPath::LangItem(..)) => {
intravisit::walk_expr(ir, expr); intravisit::walk_expr(ir, expr);
} }
} }
@ -1310,7 +1311,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprKind::Lit(..) hir::ExprKind::Lit(..)
| hir::ExprKind::Err | hir::ExprKind::Err
| hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => succ, | hir::ExprKind::Path(hir::QPath::TypeRelative(..))
| hir::ExprKind::Path(hir::QPath::LangItem(..)) => succ,
// Note that labels have been resolved, so we don't need to look // Note that labels have been resolved, so we don't need to look
// at the label ident // at the label ident

View file

@ -1325,7 +1325,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
Res::Def(kind, def_id) => Some((kind, def_id)), Res::Def(kind, def_id) => Some((kind, def_id)),
_ => None, _ => None,
}, },
hir::QPath::TypeRelative(..) => self hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
.maybe_typeck_results .maybe_typeck_results
.and_then(|typeck_results| typeck_results.type_dependent_def(id)), .and_then(|typeck_results| typeck_results.type_dependent_def(id)),
}; };
@ -1340,7 +1340,9 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
let sess = self.tcx.sess; let sess = self.tcx.sess;
let sm = sess.source_map(); let sm = sess.source_map();
let name = match qpath { let name = match qpath {
hir::QPath::Resolved(_, path) => sm.span_to_snippet(path.span).ok(), hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => {
sm.span_to_snippet(qpath.span()).ok()
}
hir::QPath::TypeRelative(_, segment) => Some(segment.ident.to_string()), hir::QPath::TypeRelative(_, segment) => Some(segment.ident.to_string()),
}; };
let kind = kind.descr(def_id); let kind = kind.descr(def_id);

View file

@ -941,6 +941,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
} }
} }
fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) {
match bound {
hir::GenericBound::LangItemTrait { .. } if !self.trait_ref_hack => {
let scope = Scope::Binder {
lifetimes: FxHashMap::default(),
s: self.scope,
next_early_index: self.next_early_index(),
track_lifetime_uses: true,
opaque_type_parent: false,
};
self.with(scope, |_, this| {
intravisit::walk_param_bound(this, bound);
});
}
_ => intravisit::walk_param_bound(self, bound),
}
}
fn visit_poly_trait_ref( fn visit_poly_trait_ref(
&mut self, &mut self,
trait_ref: &'tcx hir::PolyTraitRef<'tcx>, trait_ref: &'tcx hir::PolyTraitRef<'tcx>,
@ -2296,6 +2314,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
self.outer_index.shift_out(1); self.outer_index.shift_out(1);
} }
fn visit_param_bound(&mut self, bound: &hir::GenericBound<'_>) {
if let hir::GenericBound::LangItemTrait { .. } = bound {
self.outer_index.shift_in(1);
intravisit::walk_param_bound(self, bound);
self.outer_index.shift_out(1);
} else {
intravisit::walk_param_bound(self, bound);
}
}
fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
match lifetime { match lifetime {

View file

@ -1076,37 +1076,6 @@ impl ResolverAstLowering for Resolver<'_> {
self.cstore().item_generics_num_lifetimes(def_id, sess) self.cstore().item_generics_num_lifetimes(def_id, sess)
} }
fn resolve_str_path(
&mut self,
span: Span,
crate_root: Option<Symbol>,
components: &[Symbol],
ns: Namespace,
) -> (ast::Path, Res) {
let root = if crate_root.is_some() { kw::PathRoot } else { kw::Crate };
let segments = iter::once(Ident::with_dummy_span(root))
.chain(
crate_root
.into_iter()
.chain(components.iter().cloned())
.map(Ident::with_dummy_span),
)
.map(|i| self.new_ast_path_segment(i))
.collect::<Vec<_>>();
let path = ast::Path { span, segments };
let parent_scope = &ParentScope::module(self.graph_root);
let res = match self.resolve_ast_path(&path, ns, parent_scope) {
Ok(res) => res,
Err((span, error)) => {
self.report_error(span, error);
Res::Err
}
};
(path, res)
}
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes> { fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes> {
self.partial_res_map.get(&id).cloned() self.partial_res_map.get(&id).cloned()
} }

View file

@ -702,14 +702,18 @@ impl<'tcx> DumpVisitor<'tcx> {
// super-traits // super-traits
for super_bound in trait_refs.iter() { for super_bound in trait_refs.iter() {
let trait_ref = match *super_bound { let (def_id, sub_span) = match *super_bound {
hir::GenericBound::Trait(ref trait_ref, _) => trait_ref, hir::GenericBound::Trait(ref trait_ref, _) => (
self.lookup_def_id(trait_ref.trait_ref.hir_ref_id),
trait_ref.trait_ref.path.segments.last().unwrap().ident.span,
),
hir::GenericBound::LangItemTrait(lang_item, span, _, _) => {
(Some(self.tcx.require_lang_item(lang_item, Some(span))), span)
}
hir::GenericBound::Outlives(..) => continue, hir::GenericBound::Outlives(..) => continue,
}; };
let trait_ref = &trait_ref.trait_ref; if let Some(id) = def_id {
if let Some(id) = self.lookup_def_id(trait_ref.hir_ref_id) {
let sub_span = trait_ref.path.segments.last().unwrap().ident.span;
if !self.span.filter_generated(sub_span) { if !self.span.filter_generated(sub_span) {
let span = self.span_from_span(sub_span); let span = self.span_from_span(sub_span);
self.dumper.dump_ref(Ref { self.dumper.dump_ref(Ref {
@ -762,11 +766,7 @@ impl<'tcx> DumpVisitor<'tcx> {
} }
fn process_path(&mut self, id: hir::HirId, path: &hir::QPath<'tcx>) { fn process_path(&mut self, id: hir::HirId, path: &hir::QPath<'tcx>) {
let span = match path { if self.span.filter_generated(path.span()) {
hir::QPath::Resolved(_, path) => path.span,
hir::QPath::TypeRelative(_, segment) => segment.ident.span,
};
if self.span.filter_generated(span) {
return; return;
} }
self.dump_path_ref(id, path); self.dump_path_ref(id, path);
@ -783,6 +783,7 @@ impl<'tcx> DumpVisitor<'tcx> {
self.visit_ty(ty); self.visit_ty(ty);
std::slice::from_ref(*segment) std::slice::from_ref(*segment)
} }
hir::QPath::LangItem(..) => return,
}; };
for seg in segments { for seg in segments {
if let Some(ref generic_args) = seg.args { if let Some(ref generic_args) = seg.args {
@ -1355,10 +1356,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
} }
if let Some(id) = self.lookup_def_id(t.hir_id) { if let Some(id) = self.lookup_def_id(t.hir_id) {
let sub_span = match path { let sub_span = path.last_segment_span();
hir::QPath::Resolved(_, path) => path.segments.last().unwrap().ident.span,
hir::QPath::TypeRelative(_, segment) => segment.ident.span,
};
let span = self.span_from_span(sub_span); let span = self.span_from_span(sub_span);
self.dumper.dump_ref(Ref { self.dumper.dump_ref(Ref {
kind: RefKind::Type, kind: RefKind::Type,

View file

@ -551,28 +551,22 @@ impl<'tcx> SaveContext<'tcx> {
} }
} }
} }
hir::ExprKind::Struct(qpath, ..) => { hir::ExprKind::Struct(qpath, ..) => match ty.kind {
let segment = match qpath { ty::Adt(def, _) => {
hir::QPath::Resolved(_, path) => path.segments.last().unwrap(), let sub_span = qpath.last_segment_span();
hir::QPath::TypeRelative(_, segment) => segment, filter!(self.span_utils, sub_span);
}; let span = self.span_from_span(sub_span);
match ty.kind { Some(Data::RefData(Ref {
ty::Adt(def, _) => { kind: RefKind::Type,
let sub_span = segment.ident.span; span,
filter!(self.span_utils, sub_span); ref_id: id_from_def_id(def.did),
let span = self.span_from_span(sub_span); }))
Some(Data::RefData(Ref {
kind: RefKind::Type,
span,
ref_id: id_from_def_id(def.did),
}))
}
_ => {
debug!("expected adt, found {:?}", ty);
None
}
} }
} _ => {
debug!("expected adt, found {:?}", ty);
None
}
},
hir::ExprKind::MethodCall(ref seg, ..) => { hir::ExprKind::MethodCall(ref seg, ..) => {
let method_id = match self.typeck_results().type_dependent_def_id(expr.hir_id) { let method_id = match self.typeck_results().type_dependent_def_id(expr.hir_id) {
Some(id) => id, Some(id) => id,
@ -636,7 +630,7 @@ impl<'tcx> SaveContext<'tcx> {
}) })
| Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => match qpath { | Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => match qpath {
hir::QPath::Resolved(_, path) => path.res, hir::QPath::Resolved(_, path) => path.res,
hir::QPath::TypeRelative(..) => self hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
.maybe_typeck_results .maybe_typeck_results
.map_or(Res::Err, |typeck_results| typeck_results.qpath_res(qpath, hir_id)), .map_or(Res::Err, |typeck_results| typeck_results.qpath_res(qpath, hir_id)),
}, },
@ -653,6 +647,7 @@ impl<'tcx> SaveContext<'tcx> {
let segment = match path { let segment = match path {
hir::QPath::Resolved(_, path) => path.segments.last(), hir::QPath::Resolved(_, path) => path.segments.last(),
hir::QPath::TypeRelative(_, segment) => Some(*segment), hir::QPath::TypeRelative(_, segment) => Some(*segment),
hir::QPath::LangItem(..) => None,
}; };
segment.and_then(|seg| { segment.and_then(|seg| {
self.get_path_segment_data(seg).or_else(|| self.get_path_segment_data_with_id(seg, id)) self.get_path_segment_data(seg).or_else(|| self.get_path_segment_data_with_id(seg, id))

View file

@ -286,6 +286,9 @@ impl<'hir> Sig for hir::Ty<'hir> {
refs: vec![SigElement { id, start, end }], refs: vec![SigElement { id, start, end }],
}) })
} }
hir::TyKind::Path(hir::QPath::LangItem(lang_item, _)) => {
Ok(text_sig(format!("#[lang = \"{}\"]", lang_item.name())))
}
hir::TyKind::TraitObject(bounds, ..) => { hir::TyKind::TraitObject(bounds, ..) => {
// FIXME recurse into bounds // FIXME recurse into bounds
let bounds: Vec<hir::GenericBound<'_>> = bounds let bounds: Vec<hir::GenericBound<'_>> = bounds

View file

@ -706,6 +706,7 @@ symbols! {
never_type, never_type,
never_type_fallback, never_type_fallback,
new, new,
new_unchecked,
next, next,
nll, nll,
no, no,
@ -828,6 +829,7 @@ symbols! {
quad_precision_float, quad_precision_float,
question_mark, question_mark,
quote, quote,
range_inclusive_new,
raw_dylib, raw_dylib,
raw_identifiers, raw_identifiers,
raw_ref_op, raw_ref_op,

View file

@ -1202,6 +1202,36 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
) )
} }
pub fn instantiate_lang_item_trait_ref(
&self,
lang_item: hir::LangItem,
span: Span,
hir_id: hir::HirId,
args: &GenericArgs<'_>,
self_ty: Ty<'tcx>,
bounds: &mut Bounds<'tcx>,
) {
let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span));
let (substs, assoc_bindings, _) =
self.create_substs_for_ast_path(span, trait_def_id, &[], args, false, Some(self_ty));
let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs));
bounds.trait_bounds.push((poly_trait_ref, span, Constness::NotConst));
let mut dup_bindings = FxHashMap::default();
for binding in assoc_bindings {
let _: Result<_, ErrorReported> = self.add_predicates_for_ast_type_binding(
hir_id,
poly_trait_ref,
&binding,
bounds,
false,
&mut dup_bindings,
span,
);
}
}
fn ast_path_to_mono_trait_ref( fn ast_path_to_mono_trait_ref(
&self, &self,
span: Span, span: Span,
@ -1392,6 +1422,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_bounds.push((b, Constness::NotConst)) trait_bounds.push((b, Constness::NotConst))
} }
hir::GenericBound::Trait(_, hir::TraitBoundModifier::Maybe) => {} hir::GenericBound::Trait(_, hir::TraitBoundModifier::Maybe) => {}
hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => self
.instantiate_lang_item_trait_ref(
lang_item, span, hir_id, args, param_ty, bounds,
),
hir::GenericBound::Outlives(ref l) => region_bounds.push(l), hir::GenericBound::Outlives(ref l) => region_bounds.push(l),
} }
} }
@ -2960,6 +2994,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.map(|(ty, _, _)| ty) .map(|(ty, _, _)| ty)
.unwrap_or_else(|_| tcx.ty_error()) .unwrap_or_else(|_| tcx.ty_error())
} }
hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => {
let def_id = tcx.require_lang_item(lang_item, Some(span));
let (substs, _, _) = self.create_substs_for_ast_path(
span,
def_id,
&[],
&GenericArgs::none(),
true,
None,
);
self.normalize_ty(span, tcx.at(span).type_of(def_id).subst(tcx, substs))
}
hir::TyKind::Array(ref ty, ref length) => { hir::TyKind::Array(ref ty, ref length) => {
let length_def_id = tcx.hir().local_def_id(length.hir_id); let length_def_id = tcx.hir().local_def_id(length.hir_id);
let length = ty::Const::from_anon_const(tcx, length_def_id); let length = ty::Const::from_anon_const(tcx, length_def_id);

View file

@ -485,7 +485,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// parenthesize if needed (Issue #46756) // parenthesize if needed (Issue #46756)
hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
// parenthesize borrows of range literals (Issue #54505) // parenthesize borrows of range literals (Issue #54505)
_ if is_range_literal(self.tcx.sess.source_map(), expr) => true, _ if is_range_literal(expr) => true,
_ => false, _ => false,
}; };
let sugg_expr = if needs_parens { format!("({})", src) } else { src }; let sugg_expr = if needs_parens { format!("({})", src) } else { src };

View file

@ -236,6 +236,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::AddrOf(kind, mutbl, ref oprnd) => { ExprKind::AddrOf(kind, mutbl, ref oprnd) => {
self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr)
} }
ExprKind::Path(QPath::LangItem(lang_item, _)) => {
self.check_lang_item_path(lang_item, expr)
}
ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr), ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr),
ExprKind::InlineAsm(asm) => self.check_expr_asm(asm), ExprKind::InlineAsm(asm) => self.check_expr_asm(asm),
ExprKind::LlvmInlineAsm(ref asm) => { ExprKind::LlvmInlineAsm(ref asm) => {
@ -447,6 +450,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} }
fn check_lang_item_path(
&self,
lang_item: hir::LangItem,
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id).1
}
fn check_expr_path(&self, qpath: &hir::QPath<'_>, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { fn check_expr_path(&self, qpath: &hir::QPath<'_>, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> {
let tcx = self.tcx; let tcx = self.tcx;
let (res, opt_ty, segs) = self.resolve_ty_and_res_ufcs(qpath, expr.hir_id, expr.span); let (res, opt_ty, segs) = self.resolve_ty_and_res_ufcs(qpath, expr.hir_id, expr.span);
@ -1077,11 +1088,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return self.tcx.ty_error(); return self.tcx.ty_error();
}; };
let path_span = match *qpath {
QPath::Resolved(_, ref path) => path.span,
QPath::TypeRelative(ref qself, _) => qself.span,
};
// Prohibit struct expressions when non-exhaustive flag is set. // Prohibit struct expressions when non-exhaustive flag is set.
let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
if !adt.did.is_local() && variant.is_field_list_non_exhaustive() { if !adt.did.is_local() && variant.is_field_list_non_exhaustive() {
@ -1099,7 +1105,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
adt_ty, adt_ty,
expected, expected,
expr.hir_id, expr.hir_id,
path_span, qpath.span(),
variant, variant,
fields, fields,
base_expr.is_none(), base_expr.is_none(),

View file

@ -122,10 +122,9 @@ use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::subst::{GenericArgKind, UserSelfTy, UserSubsts}; use rustc_middle::ty::subst::{GenericArgKind, UserSelfTy, UserSubsts};
use rustc_middle::ty::util::{Discr, IntTypeExt, Representability}; use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
use rustc_middle::ty::{ use rustc_middle::ty::WithConstness;
self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef, use rustc_middle::ty::{self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind};
ToPredicate, Ty, TyCtxt, UserType, WithConstness, use rustc_middle::ty::{RegionKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, UserType};
};
use rustc_session::config::{self, EntryFnType}; use rustc_session::config::{self, EntryFnType};
use rustc_session::lint; use rustc_session::lint;
use rustc_session::parse::feature_err; use rustc_session::parse::feature_err;
@ -4430,10 +4429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
qpath: &QPath<'_>, qpath: &QPath<'_>,
hir_id: hir::HirId, hir_id: hir::HirId,
) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
let path_span = match *qpath { let path_span = qpath.qself_span();
QPath::Resolved(_, ref path) => path.span,
QPath::TypeRelative(ref qself, _) => qself.span,
};
let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
let variant = match def { let variant = match def {
Res::Err => { Res::Err => {
@ -4511,9 +4507,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty) (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty)
} }
QPath::LangItem(lang_item, span) => {
self.resolve_lang_item_path(lang_item, span, hir_id)
}
} }
} }
fn resolve_lang_item_path(
&self,
lang_item: hir::LangItem,
span: Span,
hir_id: hir::HirId,
) -> (Res, Ty<'tcx>) {
let def_id = self.tcx.require_lang_item(lang_item, Some(span));
let def_kind = self.tcx.def_kind(def_id);
let item_ty = if let DefKind::Variant = def_kind {
self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent"))
} else {
self.tcx.type_of(def_id)
};
let substs = self.infcx.fresh_substs_for_item(span, def_id);
let ty = item_ty.subst(self.tcx, substs);
self.write_resolution(hir_id, Ok((def_kind, def_id)));
self.add_required_obligations(span, def_id, &substs);
(Res::Def(def_kind, def_id), ty)
}
/// Resolves an associated value path into a base type and associated constant, or method /// Resolves an associated value path into a base type and associated constant, or method
/// resolution. The newly resolved definition is written into `type_dependent_defs`. /// resolution. The newly resolved definition is written into `type_dependent_defs`.
pub fn resolve_ty_and_res_ufcs<'b>( pub fn resolve_ty_and_res_ufcs<'b>(
@ -4532,6 +4553,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
); );
} }
QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment), QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment),
QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"),
}; };
if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
{ {

View file

@ -947,13 +947,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// | // |
// L | let A(()) = A(()); // L | let A(()) = A(());
// | ^ ^ // | ^ ^
[] => { [] => (qpath.span().shrink_to_hi(), pat_span),
let qpath_span = match qpath {
hir::QPath::Resolved(_, path) => path.span,
hir::QPath::TypeRelative(_, ps) => ps.ident.span,
};
(qpath_span.shrink_to_hi(), pat_span)
}
// Easy case. Just take the "lo" of the first sub-pattern and the "hi" of the // Easy case. Just take the "lo" of the first sub-pattern and the "hi" of the
// last sub-pattern. In the case of `A(x)` the first and last may coincide. // last sub-pattern. In the case of `A(x)` the first and last may coincide.
// This looks like: // This looks like:

View file

@ -1959,6 +1959,20 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
predicates.extend(bounds.predicates(tcx, ty)); predicates.extend(bounds.predicates(tcx, ty));
} }
&hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => {
let mut bounds = Bounds::default();
AstConv::instantiate_lang_item_trait_ref(
&icx,
lang_item,
span,
hir_id,
args,
ty,
&mut bounds,
);
predicates.extend(bounds.predicates(tcx, ty));
}
&hir::GenericBound::Outlives(ref lifetime) => { &hir::GenericBound::Outlives(ref lifetime) => {
let region = AstConv::ast_region_to_region(&icx, lifetime, None); let region = AstConv::ast_region_to_region(&icx, lifetime, None);
predicates.push(( predicates.push((
@ -2108,6 +2122,18 @@ fn predicates_from_bound<'tcx>(
let _ = astconv.instantiate_poly_trait_ref(tr, constness, param_ty, &mut bounds); let _ = astconv.instantiate_poly_trait_ref(tr, constness, param_ty, &mut bounds);
bounds.predicates(astconv.tcx(), param_ty) bounds.predicates(astconv.tcx(), param_ty)
} }
hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => {
let mut bounds = Bounds::default();
astconv.instantiate_lang_item_trait_ref(
lang_item,
span,
hir_id,
args,
param_ty,
&mut bounds,
);
bounds.predicates(astconv.tcx(), param_ty)
}
hir::GenericBound::Outlives(ref lifetime) => { hir::GenericBound::Outlives(ref lifetime) => {
let region = astconv.ast_region_to_region(lifetime, None); let region = astconv.ast_region_to_region(lifetime, None);
let pred = ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(param_ty, region)) let pred = ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(param_ty, region))

View file

@ -17,6 +17,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
use rustc_index::vec::{Idx, IndexVec}; use rustc_index::vec::{Idx, IndexVec};
use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
use rustc_middle::bug;
use rustc_middle::middle::resolve_lifetime as rl; use rustc_middle::middle::resolve_lifetime as rl;
use rustc_middle::middle::stability; use rustc_middle::middle::stability;
use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::fold::TypeFolder;
@ -291,6 +292,22 @@ impl Clean<GenericBound> for hir::GenericBound<'_> {
fn clean(&self, cx: &DocContext<'_>) -> GenericBound { fn clean(&self, cx: &DocContext<'_>) -> GenericBound {
match *self { match *self {
hir::GenericBound::Outlives(lt) => GenericBound::Outlives(lt.clean(cx)), hir::GenericBound::Outlives(lt) => GenericBound::Outlives(lt.clean(cx)),
hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => {
let def_id = cx.tcx.require_lang_item(lang_item, Some(span));
let trait_ref = ty::TraitRef::identity(cx.tcx, def_id);
let generic_args = generic_args.clean(cx);
let bindings = match generic_args {
GenericArgs::AngleBracketed { bindings, .. } => bindings,
_ => bug!("clean: parenthesized `GenericBound::LangItemTrait`"),
};
GenericBound::TraitBound(
PolyTrait { trait_: (trait_ref, &*bindings).clean(cx), generic_params: vec![] },
hir::TraitBoundModifier::None,
)
}
hir::GenericBound::Trait(ref t, modifier) => { hir::GenericBound::Trait(ref t, modifier) => {
GenericBound::TraitBound(t.clean(cx), modifier) GenericBound::TraitBound(t.clean(cx), modifier)
} }
@ -1504,6 +1521,9 @@ impl Clean<Type> for hir::Ty<'_> {
trait_: box resolve_type(cx, trait_path.clean(cx), self.hir_id), trait_: box resolve_type(cx, trait_path.clean(cx), self.hir_id),
} }
} }
TyKind::Path(hir::QPath::LangItem(..)) => {
bug!("clean: requiring documentation of lang item")
}
TyKind::TraitObject(ref bounds, ref lifetime) => { TyKind::TraitObject(ref bounds, ref lifetime) => {
match bounds[0].clean(cx).trait_ { match bounds[0].clean(cx).trait_ {
ResolvedPath { path, param_names: None, did, is_generic } => { ResolvedPath { path, param_names: None, did, is_generic } => {

View file

@ -335,6 +335,7 @@ pub fn qpath_to_string(p: &hir::QPath<'_>) -> String {
let segments = match *p { let segments = match *p {
hir::QPath::Resolved(_, ref path) => &path.segments, hir::QPath::Resolved(_, ref path) => &path.segments,
hir::QPath::TypeRelative(_, ref segment) => return segment.ident.to_string(), hir::QPath::TypeRelative(_, ref segment) => return segment.ident.to_string(),
hir::QPath::LangItem(lang_item, ..) => return lang_item.name().to_string(),
}; };
let mut s = String::new(); let mut s = String::new();

View file

@ -0,0 +1,18 @@
// check-pass
// edition:2018
// aux-build:not-libstd.rs
// Check that paths created in HIR are not affected by in scope names.
extern crate not_libstd as std;
async fn the_future() {
async {}.await;
}
fn main() -> Result<(), ()> {
for i in 0..10 {}
for j in 0..=10 {}
Ok(())?;
Ok(())
}

View file

@ -17,9 +17,13 @@ error[E0277]: the size for values of type `[{integer}]` cannot be known at compi
| |
LL | let range = *arr..; LL | let range = *arr..;
| ^^^^^^ doesn't have a size known at compile-time | ^^^^^^ doesn't have a size known at compile-time
|
::: $SRC_DIR/core/src/ops/range.rs:LL:COL
|
LL | pub struct RangeFrom<Idx> {
| --- required by this bound in `std::ops::RangeFrom`
| |
= help: the trait `std::marker::Sized` is not implemented for `[{integer}]` = help: the trait `std::marker::Sized` is not implemented for `[{integer}]`
= note: required by `std::ops::RangeFrom`
error: aborting due to 3 previous errors error: aborting due to 3 previous errors

View file

@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess {
); );
} }
}, },
QPath::TypeRelative(..) => {}, QPath::TypeRelative(..) | QPath::LangItem(..) => {},
} }
} }
} }

View file

@ -89,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Index(ref array, ref index) = &expr.kind { if let ExprKind::Index(ref array, ref index) = &expr.kind {
let ty = cx.typeck_results().expr_ty(array); let ty = cx.typeck_results().expr_ty(array);
if let Some(range) = higher::range(cx, index) { if let Some(range) = higher::range(index) {
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
if let ty::Array(_, s) = ty.kind { if let ty::Array(_, s) = ty.kind {
let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) { let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) {

View file

@ -171,7 +171,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
Finite Finite
} }
}, },
ExprKind::Struct(..) => higher::range(cx, expr).map_or(false, |r| r.end.is_none()).into(), ExprKind::Struct(..) => higher::range(expr).map_or(false, |r| r.end.is_none()).into(),
_ => Finite, _ => Finite,
} }
} }

View file

@ -262,7 +262,7 @@ fn check_len(
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Special case ranges until `range_is_empty` is stabilized. See issue 3807. /// Special case ranges until `range_is_empty` is stabilized. See issue 3807.
fn should_skip_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn should_skip_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
higher::range(cx, expr).map_or(false, |_| { higher::range(expr).map_or(false, |_| {
!cx.tcx !cx.tcx
.features() .features()
.declared_lib_features .declared_lib_features

View file

@ -1003,7 +1003,7 @@ fn detect_manual_memcpy<'tcx>(
start: Some(start), start: Some(start),
end: Some(end), end: Some(end),
limits, limits,
}) = higher::range(cx, arg) }) = higher::range(arg)
{ {
// the var must be a single name // the var must be a single name
if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
@ -1177,7 +1177,7 @@ fn check_for_loop_range<'tcx>(
start: Some(start), start: Some(start),
ref end, ref end,
limits, limits,
}) = higher::range(cx, arg) }) = higher::range(arg)
{ {
// the var must be a single name // the var must be a single name
if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
@ -1679,7 +1679,7 @@ fn check_for_mut_range_bound(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'
start: Some(start), start: Some(start),
end: Some(end), end: Some(end),
.. ..
}) = higher::range(cx, arg) }) = higher::range(arg)
{ {
let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)]; let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
if mut_ids[0].is_some() || mut_ids[1].is_some() { if mut_ids[0].is_some() || mut_ids[1].is_some() {

View file

@ -1,7 +1,8 @@
use crate::utils::{self, is_type_diagnostic_item, match_type, snippet, span_lint_and_sugg, walk_ptrs_ty}; use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
use crate::utils::walk_ptrs_ty;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, MatchSource}; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -96,5 +97,5 @@ fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr); let ty = cx.typeck_results().expr_ty(expr);
let ty = walk_ptrs_ty(ty); let ty = walk_ptrs_ty(ty);
match_type(cx, ty, &utils::paths::RANGE_FULL) is_type_lang_item(cx, ty, LangItem::RangeFull)
} }

View file

@ -2271,7 +2271,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_
if_chain! { if_chain! {
if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind; if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind;
if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
= higher::range(cx, index_expr); = higher::range(index_expr);
if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
if let ast::LitKind::Int(start_idx, _) = start_lit.node; if let ast::LitKind::Int(start_idx, _) = start_lit.node;
then { then {

View file

@ -433,6 +433,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
return; return;
} }
let binding = match expr.kind { let binding = match expr.kind {
ExprKind::Path(hir::QPath::LangItem(..)) => None,
ExprKind::Path(ref qpath) => { ExprKind::Path(ref qpath) => {
let binding = last_path_segment(qpath).ident.as_str(); let binding = last_path_segment(qpath).ident.as_str();
if binding.starts_with('_') && if binding.starts_with('_') &&

View file

@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter; if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter;
if iter_path.ident.name == sym!(iter); if iter_path.ident.name == sym!(iter);
// range expression in `.zip()` call: `0..x.len()` // range expression in `.zip()` call: `0..x.len()`
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(cx, zip_arg); if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
if is_integer_const(cx, start, 0); if is_integer_const(cx, start, 0);
// `.len()` call // `.len()` call
if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind;
@ -180,7 +180,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
start, start,
end: Some(end), end: Some(end),
limits: RangeLimits::HalfOpen limits: RangeLimits::HalfOpen
}) = higher::range(cx, expr); }) = higher::range(expr);
if let Some(y) = y_plus_one(cx, end); if let Some(y) = y_plus_one(cx, end);
then { then {
let span = if expr.span.from_expansion() { let span = if expr.span.from_expansion() {
@ -225,7 +225,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
// inclusive range minus one: `x..=(y-1)` // inclusive range minus one: `x..=(y-1)`
fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! { if_chain! {
if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(cx, expr); if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(expr);
if let Some(y) = y_minus_one(cx, end); if let Some(y) = y_minus_one(cx, end);
then { then {
span_lint_and_then( span_lint_and_then(
@ -279,7 +279,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
} }
if_chain! { if_chain! {
if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(cx, expr); if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(expr);
let ty = cx.typeck_results().expr_ty(start); let ty = cx.typeck_results().expr_ty(start);
if let ty::Int(_) | ty::Uint(_) = ty.kind; if let ty::Int(_) | ty::Uint(_) = ty.kind;
if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start); if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);

View file

@ -1,10 +1,10 @@
use crate::utils::{ use crate::utils::{
is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet,
span_lint_and_sugg, snippet_with_macro_callsite, span_lint_and_sugg,
}; };
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, MatchSource}; use rustc_hir::{Expr, ExprKind, QPath, LangItem, MatchSource};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
if let ExprKind::Match(ref match_arg, _, MatchSource::TryDesugar) = expr.kind; if let ExprKind::Match(ref match_arg, _, MatchSource::TryDesugar) = expr.kind;
if let ExprKind::Call(ref match_fun, ref try_args) = match_arg.kind; if let ExprKind::Call(ref match_fun, ref try_args) = match_arg.kind;
if let ExprKind::Path(ref match_fun_path) = match_fun.kind; if let ExprKind::Path(ref match_fun_path) = match_fun.kind;
if match_qpath(match_fun_path, &paths::TRY_INTO_RESULT); if matches!(match_fun_path, QPath::LangItem(LangItem::TryIntoResult, _));
if let Some(ref try_arg) = try_args.get(0); if let Some(ref try_arg) = try_args.get(0);
if let ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind; if let ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind;
if let Some(ref err_arg) = err_args.get(0); if let Some(ref err_arg) = err_args.get(0);

View file

@ -475,6 +475,7 @@ impl Types {
} }
} }
}, },
QPath::LangItem(..) => {},
} }
}, },
TyKind::Rptr(ref lt, ref mut_ty) => self.check_ty_rptr(cx, hir_ty, is_local, lt, mut_ty), TyKind::Rptr(ref lt, ref mut_ty) => self.check_ty_rptr(cx, hir_ty, is_local, lt, mut_ty),

View file

@ -1,4 +1,4 @@
use crate::utils::{is_try, match_qpath, match_trait_method, paths, span_lint}; use crate::utils::{is_try, match_trait_method, paths, span_lint};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -42,10 +42,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
match expr.kind { match expr.kind {
hir::ExprKind::Match(ref res, _, _) if is_try(expr).is_some() => { hir::ExprKind::Match(ref res, _, _) if is_try(expr).is_some() => {
if let hir::ExprKind::Call(ref func, ref args) = res.kind { if let hir::ExprKind::Call(ref func, ref args) = res.kind {
if let hir::ExprKind::Path(ref path) = func.kind { if matches!(
if match_qpath(path, &paths::TRY_INTO_RESULT) && args.len() == 1 { func.kind,
check_method_call(cx, &args[0], expr); hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _))
} ) {
check_method_call(cx, &args[0], expr);
} }
} else { } else {
check_method_call(cx, res, expr); check_method_call(cx, res, expr);

View file

@ -175,9 +175,19 @@ impl PrintVisitor {
} }
fn print_qpath(&mut self, path: &QPath<'_>) { fn print_qpath(&mut self, path: &QPath<'_>) {
print!(" if match_qpath({}, &[", self.current); match *path {
print_path(path, &mut true); QPath::LangItem(lang_item, _) => {
println!("]);"); println!(
" if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
self.current, lang_item,
);
},
_ => {
print!(" if match_qpath({}, &[", self.current);
print_path(path, &mut true);
println!("]);");
},
}
} }
} }
@ -760,5 +770,6 @@ fn print_path(path: &QPath<'_>, first: &mut bool) {
}, },
ref other => print!("/* unimplemented: {:?}*/", other), ref other => print!("/* unimplemented: {:?}*/", other),
}, },
QPath::LangItem(..) => panic!("print_path: called for lang item qpath"),
} }
} }

View file

@ -3,12 +3,11 @@
#![deny(clippy::missing_docs_in_private_items)] #![deny(clippy::missing_docs_in_private_items)]
use crate::utils::{is_expn_of, match_def_path, match_qpath, paths}; use crate::utils::{is_expn_of, match_def_path, paths};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty;
/// Converts a hir binary operator to the corresponding `ast` type. /// Converts a hir binary operator to the corresponding `ast` type.
#[must_use] #[must_use]
@ -47,7 +46,7 @@ pub struct Range<'a> {
} }
/// Higher a `hir` range to something similar to `ast::ExprKind::Range`. /// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
pub fn range<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a hir::Expr<'_>) -> Option<Range<'a>> { pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
/// Finds the field named `name` in the field. Always return `Some` for /// Finds the field named `name` in the field. Always return `Some` for
/// convenience. /// convenience.
fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Expr<'c>> { fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Expr<'c>> {
@ -56,94 +55,43 @@ pub fn range<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a hir::Expr<'_>) -> Optio
Some(expr) Some(expr)
} }
let def_path = match cx.typeck_results().expr_ty(expr).kind {
ty::Adt(def, _) => cx.tcx.def_path(def.did),
_ => return None,
};
// sanity checks for std::ops::RangeXXXX
if def_path.data.len() != 3 {
return None;
}
if def_path.data.get(0)?.data.as_symbol() != sym!(ops) {
return None;
}
if def_path.data.get(1)?.data.as_symbol() != sym!(range) {
return None;
}
let type_name = def_path.data.get(2)?.data.as_symbol();
let range_types = [
"RangeFrom",
"RangeFull",
"RangeInclusive",
"Range",
"RangeTo",
"RangeToInclusive",
];
if !range_types.contains(&&*type_name.as_str()) {
return None;
}
// The range syntax is expanded to literal paths starting with `core` or `std`
// depending on
// `#[no_std]`. Testing both instead of resolving the paths.
match expr.kind { match expr.kind {
hir::ExprKind::Path(ref path) => { hir::ExprKind::Call(ref path, ref args) if matches!(
if match_qpath(path, &paths::RANGE_FULL_STD) || match_qpath(path, &paths::RANGE_FULL) { path.kind,
Some(Range { hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
) => Some(Range {
start: Some(&args[0]),
end: Some(&args[1]),
limits: ast::RangeLimits::Closed,
}),
hir::ExprKind::Struct(ref path, ref fields, None) => {
match path {
hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
start: None, start: None,
end: None, end: None,
limits: ast::RangeLimits::HalfOpen, limits: ast::RangeLimits::HalfOpen,
}) }),
} else { hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
None
}
},
hir::ExprKind::Call(ref path, ref args) => {
if let hir::ExprKind::Path(ref path) = path.kind {
if match_qpath(path, &paths::RANGE_INCLUSIVE_STD_NEW) || match_qpath(path, &paths::RANGE_INCLUSIVE_NEW)
{
Some(Range {
start: Some(&args[0]),
end: Some(&args[1]),
limits: ast::RangeLimits::Closed,
})
} else {
None
}
} else {
None
}
},
hir::ExprKind::Struct(ref path, ref fields, None) => {
if match_qpath(path, &paths::RANGE_FROM_STD) || match_qpath(path, &paths::RANGE_FROM) {
Some(Range {
start: Some(get_field("start", fields)?), start: Some(get_field("start", fields)?),
end: None, end: None,
limits: ast::RangeLimits::HalfOpen, limits: ast::RangeLimits::HalfOpen,
}) }),
} else if match_qpath(path, &paths::RANGE_STD) || match_qpath(path, &paths::RANGE) { hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
Some(Range {
start: Some(get_field("start", fields)?), start: Some(get_field("start", fields)?),
end: Some(get_field("end", fields)?), end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::HalfOpen, limits: ast::RangeLimits::HalfOpen,
}) }),
} else if match_qpath(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_qpath(path, &paths::RANGE_TO_INCLUSIVE) hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
{
Some(Range {
start: None, start: None,
end: Some(get_field("end", fields)?), end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::Closed, limits: ast::RangeLimits::Closed,
}) }),
} else if match_qpath(path, &paths::RANGE_TO_STD) || match_qpath(path, &paths::RANGE_TO) { hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
Some(Range {
start: None, start: None,
end: Some(get_field("end", fields)?), end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::HalfOpen, limits: ast::RangeLimits::HalfOpen,
}) }),
} else { _ => None,
None
} }
}, },
_ => None, _ => None,

View file

@ -3,9 +3,9 @@ use crate::utils::differing_macro_contexts;
use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::{ use rustc_hir::{
BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FnRetTy, GenericArg, BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat,
GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, PathSegment, QPath, FnRetTy, GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName,
Stmt, StmtKind, Ty, TyKind, TypeBinding, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
}; };
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ich::StableHashingContextProvider; use rustc_middle::ich::StableHashingContextProvider;
@ -185,10 +185,20 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
left.name == right.name left.name == right.name
} }
pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool {
match (&left, &right) {
(FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) =>
li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp),
}
}
/// Checks whether two patterns are the same. /// Checks whether two patterns are the same.
pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
match (&left.kind, &right.kind) { match (&left.kind, &right.kind) {
(&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r), (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r),
(&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => {
self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_fieldpat(l, r))
},
(&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => { (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => {
self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
}, },
@ -223,6 +233,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
(&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => { (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => {
self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
}, },
(&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) =>
llang_item == rlang_item,
_ => false, _ => false,
} }
} }
@ -601,6 +613,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
QPath::TypeRelative(_, ref path) => { QPath::TypeRelative(_, ref path) => {
self.hash_name(path.ident.name); self.hash_name(path.ident.name);
}, },
QPath::LangItem(lang_item, ..) => {
lang_item.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
}
} }
// self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
} }
@ -710,6 +725,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_ty(ty); self.hash_ty(ty);
segment.ident.name.hash(&mut self.s); segment.ident.name.hash(&mut self.s);
}, },
QPath::LangItem(lang_item, ..) => {
lang_item.hash(&mut self.s);
}
}, },
TyKind::OpaqueDef(_, arg_list) => { TyKind::OpaqueDef(_, arg_list) => {
self.hash_generic_args(arg_list); self.hash_generic_args(arg_list);

View file

@ -266,6 +266,9 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
println!("{}Relative Path, {:?}", ind, ty); println!("{}Relative Path, {:?}", ind, ty);
println!("{}seg: {:?}", ind, seg); println!("{}seg: {:?}", ind, seg);
}, },
hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
println!("{}Lang Item Path, {:?}", ind, lang_item.name());
},
hir::ExprKind::AddrOf(kind, ref muta, ref e) => { hir::ExprKind::AddrOf(kind, ref muta, ref e) => {
println!("{}AddrOf", ind); println!("{}AddrOf", ind);
println!("kind: {:?}", kind); println!("kind: {:?}", kind);
@ -488,6 +491,9 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) {
println!("{}Relative Path, {:?}", ind, ty); println!("{}Relative Path, {:?}", ind, ty);
println!("{}seg: {:?}", ind, seg); println!("{}seg: {:?}", ind, seg);
}, },
hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
println!("{}Lang Item Path, {:?}", ind, lang_item.name());
},
hir::PatKind::Tuple(pats, opt_dots_position) => { hir::PatKind::Tuple(pats, opt_dots_position) => {
println!("{}Tuple", ind); println!("{}Tuple", ind);
if let Some(dot_position) = opt_dots_position { if let Some(dot_position) = opt_dots_position {

View file

@ -142,6 +142,14 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb
} }
} }
/// Checks if the type is equal to a lang item
pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
match ty.kind {
ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did,
_ => false,
}
}
/// Checks if the method call given in `expr` belongs to the given trait. /// Checks if the method call given in `expr` belongs to the given trait.
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
@ -163,6 +171,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
match *path { match *path {
QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"), QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"),
QPath::TypeRelative(_, ref seg) => seg, QPath::TypeRelative(_, ref seg) => seg,
QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
} }
} }
@ -170,6 +179,7 @@ pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment
match *path { match *path {
QPath::Resolved(_, ref path) => path.segments.get(0), QPath::Resolved(_, ref path) => path.segments.get(0),
QPath::TypeRelative(_, ref seg) => Some(seg), QPath::TypeRelative(_, ref seg) => Some(seg),
QPath::LangItem(..) => None,
} }
} }
@ -196,6 +206,7 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
}, },
_ => false, _ => false,
}, },
QPath::LangItem(..) => false,
} }
} }
@ -277,7 +288,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> {
pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
match qpath { match qpath {
hir::QPath::Resolved(_, path) => path.res, hir::QPath::Resolved(_, path) => path.res,
hir::QPath::TypeRelative(..) => { hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => {
if cx.tcx.has_typeck_results(id.owner.to_def_id()) { if cx.tcx.has_typeck_results(id.owner.to_def_id()) {
cx.tcx.typeck(id.owner.to_def_id().expect_local()).qpath_res(qpath, id) cx.tcx.typeck(id.owner.to_def_id().expect_local()).qpath_res(qpath, id)
} else { } else {

View file

@ -84,19 +84,7 @@ pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"];
pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"];
pub const RANGE_FULL: [&str; 4] = ["core", "ops", "range", "RangeFull"];
pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"];
pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"];
pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"];
pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"];
pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"];
pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"];
pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
@ -130,7 +118,6 @@ pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"]; pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"];
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];

View file

@ -42,7 +42,7 @@ impl<'a> Sugg<'a> {
pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> { pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
snippet_opt(cx, expr.span).map(|snippet| { snippet_opt(cx, expr.span).map(|snippet| {
let snippet = Cow::Owned(snippet); let snippet = Cow::Owned(snippet);
Self::hir_from_snippet(cx, expr, snippet) Self::hir_from_snippet(expr, snippet)
}) })
} }
@ -80,13 +80,13 @@ impl<'a> Sugg<'a> {
pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self { pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
let snippet = snippet_with_macro_callsite(cx, expr.span, default); let snippet = snippet_with_macro_callsite(cx, expr.span, default);
Self::hir_from_snippet(cx, expr, snippet) Self::hir_from_snippet(expr, snippet)
} }
/// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
/// function variants of `Sugg`, since these use different snippet functions. /// function variants of `Sugg`, since these use different snippet functions.
fn hir_from_snippet(cx: &LateContext<'_>, expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self { fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
if let Some(range) = higher::range(cx, expr) { if let Some(range) = higher::range(expr) {
let op = match range.limits { let op = match range.limits {
ast::RangeLimits::HalfOpen => AssocOp::DotDot, ast::RangeLimits::HalfOpen => AssocOp::DotDot,
ast::RangeLimits::Closed => AssocOp::DotDotEq, ast::RangeLimits::Closed => AssocOp::DotDotEq,

View file

@ -3,10 +3,10 @@ if_chain! {
if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind; if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind;
if let ExprKind::Call(ref func, ref args) = expr1.kind; if let ExprKind::Call(ref func, ref args) = expr1.kind;
if let ExprKind::Path(ref path) = func.kind; if let ExprKind::Path(ref path) = func.kind;
if match_qpath(path, &["{{root}}", "std", "iter", "IntoIterator", "into_iter"]); if matches!(path, QPath::LangItem(LangItem::IntoIterIntoIter, _));
if args.len() == 1; if args.len() == 1;
if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind; if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind;
if match_qpath(path1, &["{{root}}", "std", "ops", "Range"]); if matches!(path1, QPath::LangItem(LangItem::Range, _));
if fields.len() == 2; if fields.len() == 2;
// unimplemented: field checks // unimplemented: field checks
if arms.len() == 1; if arms.len() == 1;
@ -20,7 +20,7 @@ if_chain! {
if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind; if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind;
if let ExprKind::Call(ref func1, ref args1) = expr2.kind; if let ExprKind::Call(ref func1, ref args1) = expr2.kind;
if let ExprKind::Path(ref path2) = func1.kind; if let ExprKind::Path(ref path2) = func1.kind;
if match_qpath(path2, &["{{root}}", "std", "iter", "Iterator", "next"]); if matches!(path2, QPath::LangItem(LangItem::IteratorNext, _));
if args1.len() == 1; if args1.len() == 1;
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind; if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind;
if let ExprKind::Path(ref path3) = inner.kind; if let ExprKind::Path(ref path3) = inner.kind;
@ -31,13 +31,15 @@ if_chain! {
if match_qpath(path4, &["__next"]); if match_qpath(path4, &["__next"]);
if let ExprKind::Path(ref path5) = value.kind; if let ExprKind::Path(ref path5) = value.kind;
if match_qpath(path5, &["val"]); if match_qpath(path5, &["val"]);
if let PatKind::TupleStruct(ref path6, ref fields1, None) = arms1[0].pat.kind; if let PatKind::Struct(ref path6, ref fields1, false) = arms1[0].pat.kind;
if match_qpath(path6, &["{{root}}", "std", "option", "Option", "Some"]); if matches!(path6, QPath::LangItem(LangItem::OptionSome, _));
if fields1.len() == 1; if fields1.len() == 1;
// unimplemented: field checks // unimplemented: field checks
if let ExprKind::Break(ref destination, None) = arms1[1].body.kind; if let ExprKind::Break(ref destination, None) = arms1[1].body.kind;
if let PatKind::Path(ref path7) = arms1[1].pat.kind; if let PatKind::Struct(ref path7, ref fields2, false) = arms1[1].pat.kind;
if match_qpath(path7, &["{{root}}", "std", "option", "Option", "None"]); if matches!(path7, QPath::LangItem(LangItem::OptionNone, _));
if fields2.len() == 0;
// unimplemented: field checks
if let StmtKind::Local(ref local1) = body.stmts[2].kind; if let StmtKind::Local(ref local1) = body.stmts[2].kind;
if let Some(ref init) = local1.init; if let Some(ref init) = local1.init;
if let ExprKind::Path(ref path8) = init.kind; if let ExprKind::Path(ref path8) = init.kind;