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 {
/// Performs the conversion.
#[cfg_attr(not(bootstrap), lang = "from")]
#[stable(feature = "rust1", since = "1.0.0")]
fn from(_: T) -> Self;
}

View file

@ -96,6 +96,7 @@ pub trait Future {
/// [`Context`]: ../task/struct.Context.html
/// [`Waker`]: ../task/struct.Waker.html
/// [`Waker::wake`]: ../task/struct.Waker.html#method.wake
#[cfg_attr(not(bootstrap), lang = "poll")]
#[stable(feature = "futures_api", since = "1.36.0")]
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
/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`).
// This is `const` to avoid extra errors after we recover from `const async fn`
#[cfg_attr(not(bootstrap), lang = "from_generator")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[inline]
@ -85,6 +86,7 @@ where
GenFuture(gen)
}
#[cfg_attr(not(bootstrap), lang = "get_context")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[inline]

View file

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

View file

@ -38,6 +38,7 @@ use crate::hash::Hash;
/// [`IntoIterator`]: ../iter/trait.Iterator.html
/// [`Iterator`]: ../iter/trait.IntoIterator.html
/// [slicing index]: ../slice/trait.SliceIndex.html
#[cfg_attr(not(bootstrap), lang = "RangeFull")]
#[doc(alias = "..")]
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
#[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,3 ]);
/// ```
#[cfg_attr(not(bootstrap), lang = "Range")]
#[doc(alias = "..")]
#[derive(Clone, Default, PartialEq, Eq, Hash)] // not Copy -- see #27186
#[stable(feature = "rust1", since = "1.0.0")]
@ -178,6 +180,7 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
/// ```
///
/// [`Iterator`]: ../iter/trait.IntoIterator.html
#[cfg_attr(not(bootstrap), lang = "RangeFrom")]
#[doc(alias = "..")]
#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
#[stable(feature = "rust1", since = "1.0.0")]
@ -260,6 +263,7 @@ impl<Idx: PartialOrd<Idx>> RangeFrom<Idx> {
/// [`IntoIterator`]: ../iter/trait.Iterator.html
/// [`Iterator`]: ../iter/trait.IntoIterator.html
/// [slicing index]: ../slice/trait.SliceIndex.html
#[cfg_attr(not(bootstrap), lang = "RangeTo")]
#[doc(alias = "..")]
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[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,3 ]); // RangeInclusive
/// ```
#[cfg_attr(not(bootstrap), lang = "RangeInclusive")]
#[doc(alias = "..=")]
#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
#[stable(feature = "inclusive_range", since = "1.26.0")]
@ -359,6 +364,7 @@ impl<Idx> RangeInclusive<Idx> {
///
/// 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")]
#[inline]
#[rustc_promotable]
@ -555,6 +561,7 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// [`IntoIterator`]: ../iter/trait.Iterator.html
/// [`Iterator`]: ../iter/trait.IntoIterator.html
/// [slicing index]: ../slice/trait.SliceIndex.html
#[cfg_attr(not(bootstrap), lang = "RangeToInclusive")]
#[doc(alias = "..=")]
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[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
/// `Try`). Specifically, the value `X::from_error(From::from(e))`
/// 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")]
fn into_result(self) -> Result<Self::Ok, Self::Error>;
/// Wrap an error value to construct the composite result. For example,
/// `Result::Err(x)` and `Result::from_error(x)` are equivalent.
#[cfg_attr(not(bootstrap), lang = "from_error")]
#[unstable(feature = "try_trait", issue = "42327")]
fn from_error(v: Self::Error) -> Self;
/// Wrap an OK value to construct the composite result. For example,
/// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent.
#[cfg_attr(not(bootstrap), lang = "from_ok")]
#[unstable(feature = "try_trait", issue = "42327")]
fn from_ok(v: Self::Ok) -> Self;
}

View file

@ -144,9 +144,11 @@ use crate::{
#[stable(feature = "rust1", since = "1.0.0")]
pub enum Option<T> {
/// No value
#[cfg_attr(not(bootstrap), lang = "None")]
#[stable(feature = "rust1", since = "1.0.0")]
None,
/// Some value `T`
#[cfg_attr(not(bootstrap), lang = "Some")]
#[stable(feature = "rust1", since = "1.0.0")]
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
#[cfg_attr(not(bootstrap), lang = "new_unchecked")]
#[stable(feature = "pin", since = "1.33.0")]
#[inline(always)]
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")]
pub enum Result<T, E> {
/// Contains the success value
#[cfg_attr(not(bootstrap), lang = "Ok")]
#[stable(feature = "rust1", since = "1.0.0")]
Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
/// Contains the error value
#[cfg_attr(not(bootstrap), lang = "Err")]
#[stable(feature = "rust1", since = "1.0.0")]
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")]
pub enum Poll<T> {
/// Represents that a value is immediately ready.
#[cfg_attr(not(bootstrap), lang = "Ready")]
#[stable(feature = "futures_api", since = "1.36.0")]
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
/// ensure that the current task is scheduled to be awoken when
/// progress can be made.
#[cfg_attr(not(bootstrap), lang = "Pending")]
#[stable(feature = "futures_api", since = "1.36.0")]
Pending,
}

View file

@ -449,7 +449,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `::std::ops::Try::from_ok($tail_expr)`
block.expr = Some(this.wrap_in_try_constructor(
sym::from_ok,
hir::LangItem::TryFromOk,
try_span,
tail_expr,
ok_wrapped_span,
@ -461,14 +461,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn wrap_in_try_constructor(
&mut self,
method: Symbol,
lang_item: hir::LangItem,
method_span: Span,
expr: &'hir hir::Expr<'hir>,
overall_span: Span,
) -> &'hir hir::Expr<'hir> {
let path = &[sym::ops, sym::Try, method];
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))
}
@ -558,12 +557,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `future::from_generator`:
let unstable_span =
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
let gen_future = self.expr_std_path(
unstable_span,
&[sym::future, sym::from_generator],
None,
ThinVec::new(),
);
let gen_future =
self.expr_lang_item_path(unstable_span, hir::LangItem::FromGenerator, ThinVec::new());
// `future::from_generator(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.
self.expr_err(span)
};
let pin_ty_id = self.next_id();
let new_unchecked_expr_kind = self.expr_call_std_assoc_fn(
pin_ty_id,
let new_unchecked = self.expr_call_lang_item_fn_mut(
span,
&[sym::pin, sym::Pin],
"new_unchecked",
hir::LangItem::PinNewUnchecked,
arena_vec![self; ref_mut_pinned],
);
let new_unchecked = self.expr(span, new_unchecked_expr_kind, ThinVec::new());
let get_context = self.expr_call_std_path_mut(
let get_context = self.expr_call_lang_item_fn_mut(
gen_future_span,
&[sym::future, sym::get_context],
hir::LangItem::GetContext,
arena_vec![self; task_context],
);
let call = self.expr_call_std_path(
let call = self.expr_call_lang_item_fn(
span,
&[sym::future, sym::Future, sym::poll],
hir::LangItem::FuturePoll,
arena_vec![self; new_unchecked, get_context],
);
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_pat, x_pat_hid) = self.pat_ident(span, x_ident);
let x_expr = self.expr_ident(span, x_ident, x_pat_hid);
let ready_pat = self.pat_std_enum(
span,
&[sym::task, sym::Poll, sym::Ready],
arena_vec![self; x_pat],
);
let ready_field = self.single_pat_field(span, x_pat);
let ready_pat = self.pat_lang_item_variant(span, hir::LangItem::PollReady, ready_field);
let break_x = self.with_loop_scope(loop_node_id, move |this| {
let expr_break =
hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr));
@ -674,7 +662,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `::std::task::Poll::Pending => {}`
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);
self.arm(pending_pat, empty_block)
};
@ -842,16 +830,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// 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> {
let id = self.next_id();
let e1 = self.lower_expr_mut(e1);
let e2 = self.lower_expr_mut(e2);
self.expr_call_std_assoc_fn(
id,
span,
&[sym::ops, sym::RangeInclusive],
"new",
arena_vec![self; e1, e2],
)
let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, span);
let fn_expr =
self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path), ThinVec::new()));
hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2])
}
fn lower_expr_range(
@ -863,12 +847,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> hir::ExprKind<'hir> {
use rustc_ast::ast::RangeLimits::*;
let path = match (e1, e2, lims) {
(None, None, HalfOpen) => sym::RangeFull,
(Some(..), None, HalfOpen) => sym::RangeFrom,
(None, Some(..), HalfOpen) => sym::RangeTo,
(Some(..), Some(..), HalfOpen) => sym::Range,
(None, Some(..), Closed) => sym::RangeToInclusive,
let lang_item = match (e1, e2, lims) {
(None, None, HalfOpen) => hir::LangItem::RangeFull,
(Some(..), None, HalfOpen) => hir::LangItem::RangeFrom,
(None, Some(..), HalfOpen) => hir::LangItem::RangeTo,
(Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
(None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
(Some(..), Some(..), Closed) => unreachable!(),
(_, None, Closed) => {
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();
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)
}
hir::ExprKind::Struct(self.arena.alloc(hir::QPath::LangItem(lang_item, span)), fields, None)
}
fn lower_loop_destination(&mut self, destination: Option<(NodeId, Label)>) -> hir::Destination {
@ -1412,9 +1387,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
let match_expr = {
let iter = self.expr_ident(desugared_span, iter, iter_pat_nid);
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_std_path(desugared_span, next_path, arena_vec![self; ref_mut_iter]);
let next_expr = self.expr_call_lang_item_fn(
desugared_span,
hir::LangItem::IteratorNext,
arena_vec![self; ref_mut_iter],
);
let arms = arena_vec![self; pat_arm, break_arm];
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>) { ... }`
let into_iter_expr = {
let into_iter_path = &[sym::iter, sym::IntoIterator, sym::into_iter];
self.expr_call_std_path(into_iter_span, into_iter_path, arena_vec![self; head])
self.expr_call_lang_item_fn(
into_iter_span,
hir::LangItem::IntoIterIntoIter,
arena_vec![self; head],
)
};
let match_expr = self.arena.alloc(self.expr_match(
@ -1521,8 +1501,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
// expand <expr>
let sub_expr = self.lower_expr_mut(sub_expr);
let path = &[sym::ops, sym::Try, sym::into_result];
self.expr_call_std_path(unstable_span, path, arena_vec![self; sub_expr])
self.expr_call_lang_item_fn(
unstable_span,
hir::LangItem::TryIntoResult,
arena_vec![self; sub_expr],
)
};
// `#[allow(unreachable_code)]`
@ -1558,12 +1541,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
let err_ident = Ident::with_dummy_span(sym::err);
let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident);
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);
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 =
self.wrap_in_try_constructor(sym::from_error, unstable_span, from_expr, try_span);
let from_err_expr = self.wrap_in_try_constructor(
hir::LangItem::TryFromError,
unstable_span,
from_expr,
try_span,
);
let thin_attrs = ThinVec::from(attrs);
let catch_scope = self.catch_scopes.last().copied();
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))
}
// Note: associated functions must use `expr_call_std_path`.
fn expr_call_std_path_mut(
fn expr_call_lang_item_fn_mut(
&mut self,
span: Span,
path_components: &[Symbol],
lang_item: hir::LangItem,
args: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
let path =
self.arena.alloc(self.expr_std_path(span, path_components, None, ThinVec::new()));
let path = self.arena.alloc(self.expr_lang_item_path(span, lang_item, ThinVec::new()));
self.expr_call_mut(span, path, args)
}
fn expr_call_std_path(
fn expr_call_lang_item_fn(
&mut self,
span: Span,
path_components: &[Symbol],
lang_item: hir::LangItem,
args: &'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.
//
// 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(
fn expr_lang_item_path(
&mut self,
span: Span,
components: &[Symbol],
params: Option<&'hir hir::GenericArgs<'hir>>,
lang_item: hir::LangItem,
attrs: AttrVec,
) -> hir::Expr<'hir> {
let path = self.std_path(span, components, params, true);
self.expr(span, hir::ExprKind::Path(hir::QPath::Resolved(None, path)), attrs)
self.expr(span, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, span)), attrs)
}
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);
struct LoweringContext<'a, 'hir: 'a> {
crate_root: Option<Symbol>,
/// Used to assign IDs to HIR nodes that do not directly correspond to AST nodes.
sess: &'a Session,
@ -189,16 +187,6 @@ pub trait ResolverAstLowering {
/// This should only return `None` during testing.
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 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");
LoweringContext {
crate_root: sess.parse_sess.injected_crate_name.get().copied(),
sess,
resolver,
nt_to_tokenstream,
@ -2064,23 +2051,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
// "<Output = T>"
let future_params = self.arena.alloc(hir::GenericArgs {
let future_args = self.arena.alloc(hir::GenericArgs {
args: &[],
bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
parenthesized: false,
});
// ::std::future::Future<future_params>
let future_path =
self.std_path(span, &[sym::future, sym::Future], Some(future_params), false);
hir::GenericBound::Trait(
hir::PolyTraitRef {
trait_ref: hir::TraitRef { path: future_path, hir_ref_id: self.next_id() },
bound_generic_params: &[],
span,
},
hir::TraitBoundModifier::None,
hir::GenericBound::LangItemTrait(
// ::std::future::Future<future_params>
hir::LangItem::FutureTraitLangItem,
span,
self.next_id(),
future_args,
)
}
@ -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> {
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> {
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> {
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> {
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,
span: Span,
components: &[Symbol],
subpats: &'hir [&'hir hir::Pat<'hir>],
) -> &'hir hir::Pat<'hir> {
let path = self.std_path(span, components, None, true);
let qpath = hir::QPath::Resolved(None, path);
let pt = if subpats.is_empty() {
hir::PatKind::Path(qpath)
} else {
hir::PatKind::TupleStruct(qpath, subpats, None)
pat: &'hir hir::Pat<'hir>,
) -> &'hir [hir::FieldPat<'hir>] {
let field = hir::FieldPat {
hir_id: self.next_id(),
ident: Ident::new(sym::integer(0), span),
is_shorthand: false,
pat,
span,
};
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) {
@ -2541,42 +2535,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
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(
&mut self,
mut hir_id: hir::HirId,

View file

@ -1,7 +1,7 @@
use crate::def::{DefKind, Namespace, Res};
use crate::def_id::DefId;
crate use crate::hir_id::HirId;
use crate::itemlikevisit;
use crate::{itemlikevisit, LangItem};
use rustc_ast::ast::{self, CrateSugar, LlvmAsmDialect};
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_macros::HashStable_Generic;
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::{MultiSpan, Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass;
@ -363,6 +363,8 @@ pub enum TraitBoundModifier {
#[derive(Debug, HashStable_Generic)]
pub enum GenericBound<'hir> {
Trait(PolyTraitRef<'hir>, TraitBoundModifier),
// FIXME(davidtwco): Introduce `PolyTraitRef::LangItem`
LangItemTrait(LangItem, Span, HirId, &'hir GenericArgs<'hir>),
Outlives(Lifetime),
}
@ -377,6 +379,7 @@ impl GenericBound<'_> {
pub fn span(&self) -> Span {
match self {
&GenericBound::Trait(ref t, ..) => t.span,
&GenericBound::LangItemTrait(_, span, ..) => span,
&GenericBound::Outlives(ref l) => l.span,
}
}
@ -1419,10 +1422,10 @@ impl Expr<'_> {
self.is_place_expr(|_| true)
}
// 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
// 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 place expression.
pub fn is_place_expr(&self, mut allow_projections_from: impl FnMut(&Self) -> bool) -> bool {
match self.kind {
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)
}
// Lang item paths cannot currently be local variables or statics.
ExprKind::Path(QPath::LangItem(..)) => false,
// Partially qualified paths in expressions can only legally
// refer to associated items which are always rvalues.
ExprKind::Path(QPath::TypeRelative(..))
@ -1489,58 +1495,28 @@ impl Expr<'_> {
/// Checks if the specified expression is a built-in range literal.
/// (See: `LoweringContext::lower_expr()`).
///
/// FIXME(#60607): This function is a hack. If and when we have `QPath::Lang(...)`,
/// we can use that instead as simpler, more reliable mechanism, as opposed to using `SourceMap`.
pub fn is_range_literal(sm: &SourceMap, expr: &Expr<'_>) -> bool {
// Returns whether the given path represents a (desugared) range,
// either in std or core, i.e. has either a `::std::ops::Range` or
// `::core::ops::Range` prefix.
fn is_range_path(path: &Path<'_>) -> bool {
let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.to_string()).collect();
let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect();
// "{{root}}" is the equivalent of `::` prefix in `Path`.
if let ["{{root}}", std_core, "ops", range] = segs.as_slice() {
(*std_core == "std" || *std_core == "core") && range.starts_with("Range")
} else {
false
}
};
// Check whether a span corresponding to a range expression is a
// range literal, rather than an explicit struct or `new()` call.
fn is_lit(sm: &SourceMap, span: &Span) -> bool {
sm.span_to_snippet(*span).map(|range_src| range_src.contains("..")).unwrap_or(false)
};
pub fn is_range_literal(expr: &Expr<'_>) -> bool {
match expr.kind {
// All built-in range literals but `..=` and `..` desugar to `Struct`s.
ExprKind::Struct(ref qpath, _, _) => {
if let QPath::Resolved(None, ref path) = **qpath {
return is_range_path(&path) && is_lit(sm, &expr.span);
}
}
// `..` desugars to its struct path.
ExprKind::Path(QPath::Resolved(None, ref path)) => {
return is_range_path(&path) && is_lit(sm, &expr.span);
}
ExprKind::Struct(ref qpath, _, _) => matches!(
**qpath,
QPath::LangItem(
LangItem::Range
| LangItem::RangeTo
| LangItem::RangeFrom
| LangItem::RangeFull
| LangItem::RangeToInclusive,
_,
)
),
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
ExprKind::Call(ref func, _) => {
if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind {
if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind {
let new_call = segment.ident.name == sym::new;
return is_range_path(&path) && is_lit(sm, &expr.span) && new_call;
}
}
matches!(func.kind, ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, _)))
}
_ => {}
_ => false,
}
false
}
#[derive(Debug, HashStable_Generic)]
@ -1677,6 +1653,40 @@ pub enum QPath<'hir> {
/// `<Vec>::new`, and `T::X::Y::method` into `<<<T>::X>::Y>::method`,
/// the `X` and `Y` nodes each being a `TyKind::Path(QPath::TypeRelative(..))`.
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.

View file

@ -724,6 +724,7 @@ pub fn walk_qpath<'v, V: Visitor<'v>>(
visitor.visit_ty(qself);
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) => {
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),
}
}

View file

@ -10,7 +10,7 @@
pub use self::LangItem::*;
use crate::def_id::DefId;
use crate::Target;
use crate::{MethodKind, Target};
use rustc_ast::ast;
use rustc_data_structures::fx::FxHashMap;
@ -307,4 +307,38 @@ language_item_table! {
CountCodeRegionFnLangItem, sym::count_code_region, count_code_region_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;
// 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,
OpaqueTy,
Enum,
Variant,
Struct,
Union,
Trait,
@ -62,6 +63,7 @@ impl Display for Target {
Target::TyAlias => "type alias",
Target::OpaqueTy => "opaque type",
Target::Enum => "enum",
Target::Variant => "enum variant",
Target::Struct => "struct",
Target::Union => "union",
Target::Trait => "trait",

View file

@ -1729,6 +1729,11 @@ impl<'a> State<'a> {
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);
}
GenericBound::LangItemTrait(lang_item, span, ..) => {
self.s.word("#[lang = \"");
self.print_ident(Ident::new(lang_item.name(), *span));
self.s.word("\"]");
}
GenericBound::Outlives(lt) => {
self.print_lifetime(lt);
}

View file

@ -1057,7 +1057,7 @@ impl TypeAliasBounds {
_ => 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 {
match *qpath {
hir::QPath::Resolved(_, ref path) => path.res,
hir::QPath::TypeRelative(..) => self
hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
.maybe_typeck_results()
.and_then(|typeck_results| typeck_results.type_dependent_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);
if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) {
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())
{
// The overflowing literal lint was overridden.
@ -317,7 +317,7 @@ fn lint_uint_literal<'tcx>(
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();
if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) {
// 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 {
match *qpath {
hir::QPath::Resolved(_, ref path) => path.res,
hir::QPath::TypeRelative(..) => self
hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
.type_dependent_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> {
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<'_>) {

View file

@ -526,7 +526,8 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
| hir::ExprKind::Yield(..)
| hir::ExprKind::Type(..)
| 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);
}
}
@ -1310,7 +1311,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprKind::Lit(..)
| 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
// 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)),
_ => None,
},
hir::QPath::TypeRelative(..) => self
hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
.maybe_typeck_results
.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 sm = sess.source_map();
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()),
};
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(
&mut self,
trait_ref: &'tcx hir::PolyTraitRef<'tcx>,
@ -2296,6 +2314,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
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) {
if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
match lifetime {

View file

@ -1076,37 +1076,6 @@ impl ResolverAstLowering for Resolver<'_> {
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> {
self.partial_res_map.get(&id).cloned()
}

View file

@ -702,14 +702,18 @@ impl<'tcx> DumpVisitor<'tcx> {
// super-traits
for super_bound in trait_refs.iter() {
let trait_ref = match *super_bound {
hir::GenericBound::Trait(ref trait_ref, _) => trait_ref,
let (def_id, sub_span) = match *super_bound {
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,
};
let trait_ref = &trait_ref.trait_ref;
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 let Some(id) = def_id {
if !self.span.filter_generated(sub_span) {
let span = self.span_from_span(sub_span);
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>) {
let span = match path {
hir::QPath::Resolved(_, path) => path.span,
hir::QPath::TypeRelative(_, segment) => segment.ident.span,
};
if self.span.filter_generated(span) {
if self.span.filter_generated(path.span()) {
return;
}
self.dump_path_ref(id, path);
@ -783,6 +783,7 @@ impl<'tcx> DumpVisitor<'tcx> {
self.visit_ty(ty);
std::slice::from_ref(*segment)
}
hir::QPath::LangItem(..) => return,
};
for seg in segments {
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) {
let sub_span = match path {
hir::QPath::Resolved(_, path) => path.segments.last().unwrap().ident.span,
hir::QPath::TypeRelative(_, segment) => segment.ident.span,
};
let sub_span = path.last_segment_span();
let span = self.span_from_span(sub_span);
self.dumper.dump_ref(Ref {
kind: RefKind::Type,

View file

@ -551,28 +551,22 @@ impl<'tcx> SaveContext<'tcx> {
}
}
}
hir::ExprKind::Struct(qpath, ..) => {
let segment = match qpath {
hir::QPath::Resolved(_, path) => path.segments.last().unwrap(),
hir::QPath::TypeRelative(_, segment) => segment,
};
match ty.kind {
ty::Adt(def, _) => {
let sub_span = segment.ident.span;
filter!(self.span_utils, sub_span);
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
}
hir::ExprKind::Struct(qpath, ..) => match ty.kind {
ty::Adt(def, _) => {
let sub_span = qpath.last_segment_span();
filter!(self.span_utils, sub_span);
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
}
},
hir::ExprKind::MethodCall(ref seg, ..) => {
let method_id = match self.typeck_results().type_dependent_def_id(expr.hir_id) {
Some(id) => id,
@ -636,7 +630,7 @@ impl<'tcx> SaveContext<'tcx> {
})
| Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => match qpath {
hir::QPath::Resolved(_, path) => path.res,
hir::QPath::TypeRelative(..) => self
hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
.maybe_typeck_results
.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 {
hir::QPath::Resolved(_, path) => path.segments.last(),
hir::QPath::TypeRelative(_, segment) => Some(*segment),
hir::QPath::LangItem(..) => None,
};
segment.and_then(|seg| {
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 }],
})
}
hir::TyKind::Path(hir::QPath::LangItem(lang_item, _)) => {
Ok(text_sig(format!("#[lang = \"{}\"]", lang_item.name())))
}
hir::TyKind::TraitObject(bounds, ..) => {
// FIXME recurse into bounds
let bounds: Vec<hir::GenericBound<'_>> = bounds

View file

@ -706,6 +706,7 @@ symbols! {
never_type,
never_type_fallback,
new,
new_unchecked,
next,
nll,
no,
@ -828,6 +829,7 @@ symbols! {
quad_precision_float,
question_mark,
quote,
range_inclusive_new,
raw_dylib,
raw_identifiers,
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(
&self,
span: Span,
@ -1392,6 +1422,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_bounds.push((b, Constness::NotConst))
}
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),
}
}
@ -2960,6 +2994,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.map(|(ty, _, _)| ty)
.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) => {
let length_def_id = tcx.hir().local_def_id(length.hir_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)
hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
// 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,
};
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) => {
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::InlineAsm(asm) => self.check_expr_asm(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> {
let tcx = self.tcx;
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();
};
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.
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() {
@ -1099,7 +1105,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
adt_ty,
expected,
expr.hir_id,
path_span,
qpath.span(),
variant,
fields,
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::{GenericArgKind, UserSelfTy, UserSubsts};
use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
use rustc_middle::ty::{
self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef,
ToPredicate, Ty, TyCtxt, UserType, WithConstness,
};
use rustc_middle::ty::WithConstness;
use rustc_middle::ty::{self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind};
use rustc_middle::ty::{RegionKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, UserType};
use rustc_session::config::{self, EntryFnType};
use rustc_session::lint;
use rustc_session::parse::feature_err;
@ -4430,10 +4429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
qpath: &QPath<'_>,
hir_id: hir::HirId,
) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
let path_span = match *qpath {
QPath::Resolved(_, ref path) => path.span,
QPath::TypeRelative(ref qself, _) => qself.span,
};
let path_span = qpath.qself_span();
let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
let variant = match def {
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)
}
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
/// resolution. The newly resolved definition is written into `type_dependent_defs`.
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::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)
{

View file

@ -947,13 +947,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// |
// L | let A(()) = A(());
// | ^ ^
[] => {
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)
}
[] => (qpath.span().shrink_to_hi(), pat_span),
// 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.
// 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));
}
&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) => {
let region = AstConv::ast_region_to_region(&icx, lifetime, None);
predicates.push((
@ -2108,6 +2122,18 @@ fn predicates_from_bound<'tcx>(
let _ = astconv.instantiate_poly_trait_ref(tr, constness, param_ty, &mut bounds);
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) => {
let region = astconv.ast_region_to_region(lifetime, None);
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_index::vec::{Idx, IndexVec};
use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
use rustc_middle::bug;
use rustc_middle::middle::resolve_lifetime as rl;
use rustc_middle::middle::stability;
use rustc_middle::ty::fold::TypeFolder;
@ -291,6 +292,22 @@ impl Clean<GenericBound> for hir::GenericBound<'_> {
fn clean(&self, cx: &DocContext<'_>) -> GenericBound {
match *self {
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) => {
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),
}
}
TyKind::Path(hir::QPath::LangItem(..)) => {
bug!("clean: requiring documentation of lang item")
}
TyKind::TraitObject(ref bounds, ref lifetime) => {
match bounds[0].clean(cx).trait_ {
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 {
hir::QPath::Resolved(_, ref path) => &path.segments,
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();

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

@ -18,8 +18,12 @@ error[E0277]: the size for values of type `[{integer}]` cannot be known at compi
LL | let range = *arr..;
| ^^^^^^ 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}]`
= note: required by `std::ops::RangeFrom`
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<'_>) {
if let ExprKind::Index(ref array, ref index) = &expr.kind {
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[..]
if let ty::Array(_, s) = ty.kind {
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
}
},
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,
}
}

View file

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

View file

@ -1003,7 +1003,7 @@ fn detect_manual_memcpy<'tcx>(
start: Some(start),
end: Some(end),
limits,
}) = higher::range(cx, arg)
}) = higher::range(arg)
{
// the var must be a single name
if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
@ -1177,7 +1177,7 @@ fn check_for_loop_range<'tcx>(
start: Some(start),
ref end,
limits,
}) = higher::range(cx, arg)
}) = higher::range(arg)
{
// the var must be a single name
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),
end: Some(end),
..
}) = higher::range(cx, arg)
}) = higher::range(arg)
{
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() {

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 rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, MatchSource};
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
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 {
let ty = cx.typeck_results().expr_ty(expr);
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 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 })
= higher::range(cx, index_expr);
= higher::range(index_expr);
if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
if let ast::LitKind::Int(start_idx, _) = start_lit.node;
then {

View file

@ -433,6 +433,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
return;
}
let binding = match expr.kind {
ExprKind::Path(hir::QPath::LangItem(..)) => None,
ExprKind::Path(ref qpath) => {
let binding = last_path_segment(qpath).ident.as_str();
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 iter_path.ident.name == sym!(iter);
// 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);
// `.len()` call
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,
end: Some(end),
limits: RangeLimits::HalfOpen
}) = higher::range(cx, expr);
}) = higher::range(expr);
if let Some(y) = y_plus_one(cx, end);
then {
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)`
fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
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);
then {
span_lint_and_then(
@ -279,7 +279,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
}
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);
if let ty::Int(_) | ty::Uint(_) = ty.kind;
if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);

View file

@ -1,10 +1,10 @@
use crate::utils::{
is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite,
span_lint_and_sugg,
is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet,
snippet_with_macro_callsite, span_lint_and_sugg,
};
use if_chain::if_chain;
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_middle::lint::in_external_macro;
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::Call(ref match_fun, ref try_args) = match_arg.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 ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind;
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),

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_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -42,10 +42,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
match expr.kind {
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::Path(ref path) = func.kind {
if match_qpath(path, &paths::TRY_INTO_RESULT) && args.len() == 1 {
check_method_call(cx, &args[0], expr);
}
if matches!(
func.kind,
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _))
) {
check_method_call(cx, &args[0], expr);
}
} else {
check_method_call(cx, res, expr);

View file

@ -175,9 +175,19 @@ impl PrintVisitor {
}
fn print_qpath(&mut self, path: &QPath<'_>) {
print!(" if match_qpath({}, &[", self.current);
print_path(path, &mut true);
println!("]);");
match *path {
QPath::LangItem(lang_item, _) => {
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),
},
QPath::LangItem(..) => panic!("print_path: called for lang item qpath"),
}
}

View file

@ -3,12 +3,11 @@
#![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 rustc_ast::ast;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
/// Converts a hir binary operator to the corresponding `ast` type.
#[must_use]
@ -47,7 +46,7 @@ pub struct Range<'a> {
}
/// 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
/// convenience.
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)
}
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 {
hir::ExprKind::Path(ref path) => {
if match_qpath(path, &paths::RANGE_FULL_STD) || match_qpath(path, &paths::RANGE_FULL) {
Some(Range {
hir::ExprKind::Call(ref path, ref args) if matches!(
path.kind,
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,
end: None,
limits: ast::RangeLimits::HalfOpen,
})
} else {
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 {
}),
hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
start: Some(get_field("start", fields)?),
end: None,
limits: ast::RangeLimits::HalfOpen,
})
} else if match_qpath(path, &paths::RANGE_STD) || match_qpath(path, &paths::RANGE) {
Some(Range {
}),
hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
start: Some(get_field("start", fields)?),
end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::HalfOpen,
})
} else if match_qpath(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_qpath(path, &paths::RANGE_TO_INCLUSIVE)
{
Some(Range {
}),
hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
start: None,
end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::Closed,
})
} else if match_qpath(path, &paths::RANGE_TO_STD) || match_qpath(path, &paths::RANGE_TO) {
Some(Range {
}),
hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
start: None,
end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::HalfOpen,
})
} else {
None
}),
_ => None,
}
},
_ => None,

View file

@ -3,9 +3,9 @@ use crate::utils::differing_macro_contexts;
use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::{
BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FnRetTy, GenericArg,
GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, PathSegment, QPath,
Stmt, StmtKind, Ty, TyKind, TypeBinding,
BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat,
FnRetTy, GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName,
Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
};
use rustc_lint::LateContext;
use rustc_middle::ich::StableHashingContextProvider;
@ -185,10 +185,20 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
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.
pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
match (&left.kind, &right.kind) {
(&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)) => {
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)) => {
self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
},
(&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) =>
llang_item == rlang_item,
_ => false,
}
}
@ -601,6 +613,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
QPath::TypeRelative(_, ref path) => {
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);
}
@ -710,6 +725,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_ty(ty);
segment.ident.name.hash(&mut self.s);
},
QPath::LangItem(lang_item, ..) => {
lang_item.hash(&mut self.s);
}
},
TyKind::OpaqueDef(_, 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!("{}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) => {
println!("{}AddrOf", ind);
println!("kind: {:?}", kind);
@ -488,6 +491,9 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) {
println!("{}Relative Path, {:?}", ind, ty);
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) => {
println!("{}Tuple", ind);
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.
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();
@ -163,6 +171,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
match *path {
QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"),
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 {
QPath::Resolved(_, ref path) => path.segments.get(0),
QPath::TypeRelative(_, ref seg) => Some(seg),
QPath::LangItem(..) => None,
}
}
@ -196,6 +206,7 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
},
_ => 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 {
match qpath {
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()) {
cx.tcx.typeck(id.owner.to_def_id().expect_local()).qpath_res(qpath, id)
} 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_NULL: [&str; 2] = ["ptr", "null"];
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_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_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
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 TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
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 VEC: [&str; 3] = ["alloc", "vec", "Vec"];
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> {
snippet_opt(cx, expr.span).map(|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 {
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_*`
/// function variants of `Sugg`, since these use different snippet functions.
fn hir_from_snippet(cx: &LateContext<'_>, expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
if let Some(range) = higher::range(cx, expr) {
fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
if let Some(range) = higher::range(expr) {
let op = match range.limits {
ast::RangeLimits::HalfOpen => AssocOp::DotDot,
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::Call(ref func, ref args) = expr1.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 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;
// unimplemented: field checks
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::Call(ref func1, ref args1) = expr2.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 let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind;
if let ExprKind::Path(ref path3) = inner.kind;
@ -31,13 +31,15 @@ if_chain! {
if match_qpath(path4, &["__next"]);
if let ExprKind::Path(ref path5) = value.kind;
if match_qpath(path5, &["val"]);
if let PatKind::TupleStruct(ref path6, ref fields1, None) = arms1[0].pat.kind;
if match_qpath(path6, &["{{root}}", "std", "option", "Option", "Some"]);
if let PatKind::Struct(ref path6, ref fields1, false) = arms1[0].pat.kind;
if matches!(path6, QPath::LangItem(LangItem::OptionSome, _));
if fields1.len() == 1;
// unimplemented: field checks
if let ExprKind::Break(ref destination, None) = arms1[1].body.kind;
if let PatKind::Path(ref path7) = arms1[1].pat.kind;
if match_qpath(path7, &["{{root}}", "std", "option", "Option", "None"]);
if let PatKind::Struct(ref path7, ref fields2, false) = arms1[1].pat.kind;
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 Some(ref init) = local1.init;
if let ExprKind::Path(ref path8) = init.kind;