Auto merge of #106193 - compiler-errors:rollup-0l54wka, r=compiler-errors
Rollup of 9 pull requests Successful merges: - #103718 (More inference-friendly API for lazy) - #105765 (Detect likely `.` -> `..` typo in method calls) - #105852 (Suggest rewriting a malformed hex literal if we expect a float) - #105965 (Provide local extern function arg names) - #106064 (Partially fix `explicit_outlives_requirements` lint in macros) - #106179 (Fix a formatting error in Iterator::for_each docs) - #106181 (Fix doc comment parsing description in book) - #106187 (Update the documentation of `Vec` to use `extend(array)` instead of `extend(array.iter().copied())`) - #106189 (Fix UnsafeCell Documentation Spelling Error) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
739d68a76e
25 changed files with 781 additions and 108 deletions
|
@ -1316,6 +1316,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
param.id,
|
param.id,
|
||||||
¶m.kind,
|
¶m.kind,
|
||||||
¶m.bounds,
|
¶m.bounds,
|
||||||
|
param.colon_span,
|
||||||
|
generics.span,
|
||||||
itctx,
|
itctx,
|
||||||
PredicateOrigin::GenericParam,
|
PredicateOrigin::GenericParam,
|
||||||
)
|
)
|
||||||
|
@ -1365,6 +1367,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
kind: &GenericParamKind,
|
kind: &GenericParamKind,
|
||||||
bounds: &[GenericBound],
|
bounds: &[GenericBound],
|
||||||
|
colon_span: Option<Span>,
|
||||||
|
parent_span: Span,
|
||||||
itctx: &ImplTraitContext,
|
itctx: &ImplTraitContext,
|
||||||
origin: PredicateOrigin,
|
origin: PredicateOrigin,
|
||||||
) -> Option<hir::WherePredicate<'hir>> {
|
) -> Option<hir::WherePredicate<'hir>> {
|
||||||
|
@ -1377,21 +1381,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
|
|
||||||
let ident = self.lower_ident(ident);
|
let ident = self.lower_ident(ident);
|
||||||
let param_span = ident.span;
|
let param_span = ident.span;
|
||||||
let span = bounds
|
|
||||||
.iter()
|
// Reconstruct the span of the entire predicate from the individual generic bounds.
|
||||||
.fold(Some(param_span.shrink_to_hi()), |span: Option<Span>, bound| {
|
let span_start = colon_span.unwrap_or_else(|| param_span.shrink_to_hi());
|
||||||
let bound_span = bound.span();
|
let span = bounds.iter().fold(span_start, |span_accum, bound| {
|
||||||
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
|
match bound.span().find_ancestor_inside(parent_span) {
|
||||||
// as we use this method to get a span appropriate for suggestions.
|
Some(bound_span) => span_accum.to(bound_span),
|
||||||
if !bound_span.can_be_used_for_suggestions() {
|
None => span_accum,
|
||||||
None
|
}
|
||||||
} else if let Some(span) = span {
|
});
|
||||||
Some(span.to(bound_span))
|
let span = self.lower_span(span);
|
||||||
} else {
|
|
||||||
Some(bound_span)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(param_span.shrink_to_hi());
|
|
||||||
match kind {
|
match kind {
|
||||||
GenericParamKind::Const { .. } => None,
|
GenericParamKind::Const { .. } => None,
|
||||||
GenericParamKind::Type { .. } => {
|
GenericParamKind::Type { .. } => {
|
||||||
|
|
|
@ -2245,6 +2245,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
) -> (hir::GenericParam<'hir>, Option<hir::WherePredicate<'hir>>, hir::TyKind<'hir>) {
|
) -> (hir::GenericParam<'hir>, Option<hir::WherePredicate<'hir>>, hir::TyKind<'hir>) {
|
||||||
// Add a definition for the in-band `Param`.
|
// Add a definition for the in-band `Param`.
|
||||||
let def_id = self.local_def_id(node_id);
|
let def_id = self.local_def_id(node_id);
|
||||||
|
let span = self.lower_span(span);
|
||||||
|
|
||||||
// Set the name to `impl Bound1 + Bound2`.
|
// Set the name to `impl Bound1 + Bound2`.
|
||||||
let param = hir::GenericParam {
|
let param = hir::GenericParam {
|
||||||
|
@ -2252,7 +2253,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
def_id,
|
def_id,
|
||||||
name: ParamName::Plain(self.lower_ident(ident)),
|
name: ParamName::Plain(self.lower_ident(ident)),
|
||||||
pure_wrt_drop: false,
|
pure_wrt_drop: false,
|
||||||
span: self.lower_span(span),
|
span,
|
||||||
kind: hir::GenericParamKind::Type { default: None, synthetic: true },
|
kind: hir::GenericParamKind::Type { default: None, synthetic: true },
|
||||||
colon_span: None,
|
colon_span: None,
|
||||||
};
|
};
|
||||||
|
@ -2262,6 +2263,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
node_id,
|
node_id,
|
||||||
&GenericParamKind::Type { default: None },
|
&GenericParamKind::Type { default: None },
|
||||||
bounds,
|
bounds,
|
||||||
|
/* colon_span */ None,
|
||||||
|
span,
|
||||||
&ImplTraitContext::Universal,
|
&ImplTraitContext::Universal,
|
||||||
hir::PredicateOrigin::ImplTrait,
|
hir::PredicateOrigin::ImplTrait,
|
||||||
);
|
);
|
||||||
|
@ -2271,7 +2274,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
let ty = hir::TyKind::Path(hir::QPath::Resolved(
|
let ty = hir::TyKind::Path(hir::QPath::Resolved(
|
||||||
None,
|
None,
|
||||||
self.arena.alloc(hir::Path {
|
self.arena.alloc(hir::Path {
|
||||||
span: self.lower_span(span),
|
span,
|
||||||
res,
|
res,
|
||||||
segments:
|
segments:
|
||||||
arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)],
|
arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)],
|
||||||
|
|
|
@ -74,6 +74,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
||||||
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
||||||
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
||||||
|
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requires that the two types unify, and prints an error message if
|
/// Requires that the two types unify, and prints an error message if
|
||||||
|
@ -1607,4 +1608,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
|
||||||
|
pub fn check_for_range_as_method_call(
|
||||||
|
&self,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
expr: &hir::Expr<'_>,
|
||||||
|
checked_ty: Ty<'tcx>,
|
||||||
|
expected_ty: Ty<'tcx>,
|
||||||
|
) {
|
||||||
|
if !hir::is_range_literal(expr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let hir::ExprKind::Struct(
|
||||||
|
hir::QPath::LangItem(LangItem::Range, ..),
|
||||||
|
[start, end],
|
||||||
|
_,
|
||||||
|
) = expr.kind else { return; };
|
||||||
|
let parent = self.tcx.hir().get_parent_node(expr.hir_id);
|
||||||
|
if let Some(hir::Node::ExprField(_)) = self.tcx.hir().find(parent) {
|
||||||
|
// Ignore `Foo { field: a..Default::default() }`
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut expr = end.expr;
|
||||||
|
while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
|
||||||
|
// Getting to the root receiver and asserting it is a fn call let's us ignore cases in
|
||||||
|
// `src/test/ui/methods/issues/issue-90315.stderr`.
|
||||||
|
expr = rcvr;
|
||||||
|
}
|
||||||
|
let hir::ExprKind::Call(method_name, _) = expr.kind else { return; };
|
||||||
|
let ty::Adt(adt, _) = checked_ty.kind() else { return; };
|
||||||
|
if self.tcx.lang_items().range_struct() != Some(adt.did()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let ty::Adt(adt, _) = expected_ty.kind()
|
||||||
|
&& self.tcx.lang_items().range_struct() == Some(adt.did())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check if start has method named end.
|
||||||
|
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; };
|
||||||
|
let [hir::PathSegment { ident, .. }] = p.segments else { return; };
|
||||||
|
let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
|
||||||
|
let Ok(_pick) = self.probe_for_name(
|
||||||
|
probe::Mode::MethodCall,
|
||||||
|
*ident,
|
||||||
|
probe::IsSuggestion(true),
|
||||||
|
self_ty,
|
||||||
|
expr.hir_id,
|
||||||
|
probe::ProbeScope::AllTraits,
|
||||||
|
) else { return; };
|
||||||
|
let mut sugg = ".";
|
||||||
|
let mut span = start.expr.span.between(end.expr.span);
|
||||||
|
if span.lo() + BytePos(2) == span.hi() {
|
||||||
|
// There's no space between the start, the range op and the end, suggest removal which
|
||||||
|
// will be more noticeable than the replacement of `..` with `.`.
|
||||||
|
span = span.with_lo(span.lo() + BytePos(1));
|
||||||
|
sugg = "";
|
||||||
|
}
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
span,
|
||||||
|
"you likely meant to write a method call instead of a range",
|
||||||
|
sugg,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ use rustc_middle::ty::{
|
||||||
TypeVisitable,
|
TypeVisitable,
|
||||||
};
|
};
|
||||||
use rustc_session::errors::ExprParenthesesNeeded;
|
use rustc_session::errors::ExprParenthesesNeeded;
|
||||||
|
use rustc_span::source_map::Spanned;
|
||||||
use rustc_span::symbol::{sym, Ident};
|
use rustc_span::symbol::{sym, Ident};
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
use rustc_trait_selection::infer::InferCtxtExt;
|
use rustc_trait_selection::infer::InferCtxtExt;
|
||||||
|
@ -1259,6 +1260,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
ExprKind::Lit(Spanned {
|
||||||
|
node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed),
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) else { return false; };
|
||||||
|
if !(snippet.starts_with("0x") || snippet.starts_with("0X")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let (_, suffix) = snippet.split_at(snippet.len() - 3);
|
||||||
|
let value = match suffix {
|
||||||
|
"f32" => (lit - 0xf32) / (16 * 16 * 16),
|
||||||
|
"f64" => (lit - 0xf64) / (16 * 16 * 16),
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
err.span_suggestions(
|
||||||
|
expr.span,
|
||||||
|
"rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
|
||||||
|
[format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2184,6 +2184,7 @@ impl ExplicitOutlivesRequirements {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
bounds: &hir::GenericBounds<'_>,
|
bounds: &hir::GenericBounds<'_>,
|
||||||
inferred_outlives: &[ty::Region<'tcx>],
|
inferred_outlives: &[ty::Region<'tcx>],
|
||||||
|
predicate_span: Span,
|
||||||
) -> Vec<(usize, Span)> {
|
) -> Vec<(usize, Span)> {
|
||||||
use rustc_middle::middle::resolve_lifetime::Region;
|
use rustc_middle::middle::resolve_lifetime::Region;
|
||||||
|
|
||||||
|
@ -2191,23 +2192,28 @@ impl ExplicitOutlivesRequirements {
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, bound)| {
|
.filter_map(|(i, bound)| {
|
||||||
if let hir::GenericBound::Outlives(lifetime) = bound {
|
let hir::GenericBound::Outlives(lifetime) = bound else {
|
||||||
let is_inferred = match tcx.named_region(lifetime.hir_id) {
|
return None;
|
||||||
Some(Region::EarlyBound(def_id)) => inferred_outlives.iter().any(|r| {
|
};
|
||||||
if let ty::ReEarlyBound(ebr) = **r {
|
|
||||||
ebr.def_id == def_id
|
let is_inferred = match tcx.named_region(lifetime.hir_id) {
|
||||||
} else {
|
Some(Region::EarlyBound(def_id)) => inferred_outlives
|
||||||
false
|
.iter()
|
||||||
}
|
.any(|r| matches!(**r, ty::ReEarlyBound(ebr) if { ebr.def_id == def_id })),
|
||||||
}),
|
_ => false,
|
||||||
_ => false,
|
};
|
||||||
};
|
|
||||||
is_inferred.then_some((i, bound.span()))
|
if !is_inferred {
|
||||||
} else {
|
return None;
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let span = bound.span().find_ancestor_inside(predicate_span)?;
|
||||||
|
if in_external_macro(tcx.sess, span) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((i, span))
|
||||||
})
|
})
|
||||||
.filter(|(_, span)| !in_external_macro(tcx.sess, *span))
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2273,9 +2279,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
|
||||||
use rustc_middle::middle::resolve_lifetime::Region;
|
use rustc_middle::middle::resolve_lifetime::Region;
|
||||||
|
|
||||||
let def_id = item.owner_id.def_id;
|
let def_id = item.owner_id.def_id;
|
||||||
if let hir::ItemKind::Struct(_, ref hir_generics)
|
if let hir::ItemKind::Struct(_, hir_generics)
|
||||||
| hir::ItemKind::Enum(_, ref hir_generics)
|
| hir::ItemKind::Enum(_, hir_generics)
|
||||||
| hir::ItemKind::Union(_, ref hir_generics) = item.kind
|
| hir::ItemKind::Union(_, hir_generics) = item.kind
|
||||||
{
|
{
|
||||||
let inferred_outlives = cx.tcx.inferred_outlives_of(def_id);
|
let inferred_outlives = cx.tcx.inferred_outlives_of(def_id);
|
||||||
if inferred_outlives.is_empty() {
|
if inferred_outlives.is_empty() {
|
||||||
|
@ -2290,53 +2296,58 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
|
||||||
let mut dropped_predicate_count = 0;
|
let mut dropped_predicate_count = 0;
|
||||||
let num_predicates = hir_generics.predicates.len();
|
let num_predicates = hir_generics.predicates.len();
|
||||||
for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
|
for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
|
||||||
let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate {
|
let (relevant_lifetimes, bounds, predicate_span, in_where_clause) =
|
||||||
hir::WherePredicate::RegionPredicate(predicate) => {
|
match where_predicate {
|
||||||
if let Some(Region::EarlyBound(region_def_id)) =
|
hir::WherePredicate::RegionPredicate(predicate) => {
|
||||||
cx.tcx.named_region(predicate.lifetime.hir_id)
|
if let Some(Region::EarlyBound(region_def_id)) =
|
||||||
{
|
cx.tcx.named_region(predicate.lifetime.hir_id)
|
||||||
(
|
{
|
||||||
Self::lifetimes_outliving_lifetime(
|
|
||||||
inferred_outlives,
|
|
||||||
region_def_id,
|
|
||||||
),
|
|
||||||
&predicate.bounds,
|
|
||||||
predicate.span,
|
|
||||||
predicate.in_where_clause,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hir::WherePredicate::BoundPredicate(predicate) => {
|
|
||||||
// FIXME we can also infer bounds on associated types,
|
|
||||||
// and should check for them here.
|
|
||||||
match predicate.bounded_ty.kind {
|
|
||||||
hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
|
|
||||||
let Res::Def(DefKind::TyParam, def_id) = path.res else {
|
|
||||||
continue
|
|
||||||
};
|
|
||||||
let index = ty_generics.param_def_id_to_index[&def_id];
|
|
||||||
(
|
(
|
||||||
Self::lifetimes_outliving_type(inferred_outlives, index),
|
Self::lifetimes_outliving_lifetime(
|
||||||
|
inferred_outlives,
|
||||||
|
region_def_id,
|
||||||
|
),
|
||||||
&predicate.bounds,
|
&predicate.bounds,
|
||||||
predicate.span,
|
predicate.span,
|
||||||
predicate.origin == PredicateOrigin::WhereClause,
|
predicate.in_where_clause,
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
_ => {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
hir::WherePredicate::BoundPredicate(predicate) => {
|
||||||
_ => continue,
|
// FIXME we can also infer bounds on associated types,
|
||||||
};
|
// and should check for them here.
|
||||||
|
match predicate.bounded_ty.kind {
|
||||||
|
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
|
||||||
|
let Res::Def(DefKind::TyParam, def_id) = path.res else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let index = ty_generics.param_def_id_to_index[&def_id];
|
||||||
|
(
|
||||||
|
Self::lifetimes_outliving_type(inferred_outlives, index),
|
||||||
|
&predicate.bounds,
|
||||||
|
predicate.span,
|
||||||
|
predicate.origin == PredicateOrigin::WhereClause,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
if relevant_lifetimes.is_empty() {
|
if relevant_lifetimes.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bound_spans =
|
let bound_spans = self.collect_outlives_bound_spans(
|
||||||
self.collect_outlives_bound_spans(cx.tcx, bounds, &relevant_lifetimes);
|
cx.tcx,
|
||||||
|
bounds,
|
||||||
|
&relevant_lifetimes,
|
||||||
|
predicate_span,
|
||||||
|
);
|
||||||
bound_count += bound_spans.len();
|
bound_count += bound_spans.len();
|
||||||
|
|
||||||
let drop_predicate = bound_spans.len() == bounds.len();
|
let drop_predicate = bound_spans.len() == bounds.len();
|
||||||
|
@ -2345,15 +2356,15 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
|
||||||
}
|
}
|
||||||
|
|
||||||
if drop_predicate && !in_where_clause {
|
if drop_predicate && !in_where_clause {
|
||||||
lint_spans.push(span);
|
lint_spans.push(predicate_span);
|
||||||
} else if drop_predicate && i + 1 < num_predicates {
|
} else if drop_predicate && i + 1 < num_predicates {
|
||||||
// If all the bounds on a predicate were inferable and there are
|
// If all the bounds on a predicate were inferable and there are
|
||||||
// further predicates, we want to eat the trailing comma.
|
// further predicates, we want to eat the trailing comma.
|
||||||
let next_predicate_span = hir_generics.predicates[i + 1].span();
|
let next_predicate_span = hir_generics.predicates[i + 1].span();
|
||||||
where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo()));
|
where_lint_spans.push(predicate_span.to(next_predicate_span.shrink_to_lo()));
|
||||||
} else {
|
} else {
|
||||||
where_lint_spans.extend(self.consolidate_outlives_bound_spans(
|
where_lint_spans.extend(self.consolidate_outlives_bound_spans(
|
||||||
span.shrink_to_lo(),
|
predicate_span.shrink_to_lo(),
|
||||||
bounds,
|
bounds,
|
||||||
bound_spans,
|
bound_spans,
|
||||||
));
|
));
|
||||||
|
@ -2374,12 +2385,26 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
|
||||||
} else {
|
} else {
|
||||||
hir_generics.span.shrink_to_hi().to(where_span)
|
hir_generics.span.shrink_to_hi().to(where_span)
|
||||||
};
|
};
|
||||||
lint_spans.push(full_where_span);
|
|
||||||
|
// Due to macro expansions, the `full_where_span` might not actually contain all predicates.
|
||||||
|
if where_lint_spans.iter().all(|&sp| full_where_span.contains(sp)) {
|
||||||
|
lint_spans.push(full_where_span);
|
||||||
|
} else {
|
||||||
|
lint_spans.extend(where_lint_spans);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
lint_spans.extend(where_lint_spans);
|
lint_spans.extend(where_lint_spans);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !lint_spans.is_empty() {
|
if !lint_spans.is_empty() {
|
||||||
|
// Do not automatically delete outlives requirements from macros.
|
||||||
|
let applicability = if lint_spans.iter().all(|sp| sp.can_be_used_for_suggestions())
|
||||||
|
{
|
||||||
|
Applicability::MachineApplicable
|
||||||
|
} else {
|
||||||
|
Applicability::MaybeIncorrect
|
||||||
|
};
|
||||||
|
|
||||||
cx.struct_span_lint(
|
cx.struct_span_lint(
|
||||||
EXPLICIT_OUTLIVES_REQUIREMENTS,
|
EXPLICIT_OUTLIVES_REQUIREMENTS,
|
||||||
lint_spans.clone(),
|
lint_spans.clone(),
|
||||||
|
@ -2387,11 +2412,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
|
||||||
|lint| {
|
|lint| {
|
||||||
lint.set_arg("count", bound_count).multipart_suggestion(
|
lint.set_arg("count", bound_count).multipart_suggestion(
|
||||||
fluent::suggestion,
|
fluent::suggestion,
|
||||||
lint_spans
|
lint_spans.into_iter().map(|span| (span, String::new())).collect(),
|
||||||
.into_iter()
|
applicability,
|
||||||
.map(|span| (span, String::new()))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -160,9 +160,13 @@ pub fn provide(providers: &mut Providers) {
|
||||||
} else if let Node::TraitItem(&TraitItem {
|
} else if let Node::TraitItem(&TraitItem {
|
||||||
kind: TraitItemKind::Fn(_, TraitFn::Required(idents)),
|
kind: TraitItemKind::Fn(_, TraitFn::Required(idents)),
|
||||||
..
|
..
|
||||||
|
})
|
||||||
|
| Node::ForeignItem(&ForeignItem {
|
||||||
|
kind: ForeignItemKind::Fn(_, idents, _),
|
||||||
|
..
|
||||||
}) = hir.get(hir_id)
|
}) = hir.get(hir_id)
|
||||||
{
|
{
|
||||||
tcx.arena.alloc_slice(idents)
|
idents
|
||||||
} else {
|
} else {
|
||||||
span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", id);
|
span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use rustc_ast::ptr::P;
|
||||||
use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
|
use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
|
||||||
use rustc_ast::*;
|
use rustc_ast::*;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||||
use rustc_errors::{DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg};
|
use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg};
|
||||||
use rustc_hir::def::Namespace::{self, *};
|
use rustc_hir::def::Namespace::{self, *};
|
||||||
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS};
|
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||||
|
@ -536,6 +536,9 @@ struct DiagnosticMetadata<'ast> {
|
||||||
in_assignment: Option<&'ast Expr>,
|
in_assignment: Option<&'ast Expr>,
|
||||||
is_assign_rhs: bool,
|
is_assign_rhs: bool,
|
||||||
|
|
||||||
|
/// Used to detect possible `.` -> `..` typo when calling methods.
|
||||||
|
in_range: Option<(&'ast Expr, &'ast Expr)>,
|
||||||
|
|
||||||
/// If we are currently in a trait object definition. Used to point at the bounds when
|
/// If we are currently in a trait object definition. Used to point at the bounds when
|
||||||
/// encountering a struct or enum.
|
/// encountering a struct or enum.
|
||||||
current_trait_object: Option<&'ast [ast::GenericBound]>,
|
current_trait_object: Option<&'ast [ast::GenericBound]>,
|
||||||
|
@ -3320,6 +3323,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn smart_resolve_path_fragment(
|
fn smart_resolve_path_fragment(
|
||||||
&mut self,
|
&mut self,
|
||||||
qself: &Option<P<QSelf>>,
|
qself: &Option<P<QSelf>>,
|
||||||
|
@ -3327,10 +3331,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
source: PathSource<'ast>,
|
source: PathSource<'ast>,
|
||||||
finalize: Finalize,
|
finalize: Finalize,
|
||||||
) -> PartialRes {
|
) -> PartialRes {
|
||||||
debug!(
|
|
||||||
"smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})",
|
|
||||||
qself, path, finalize,
|
|
||||||
);
|
|
||||||
let ns = source.namespace();
|
let ns = source.namespace();
|
||||||
|
|
||||||
let Finalize { node_id, path_span, .. } = finalize;
|
let Finalize { node_id, path_span, .. } = finalize;
|
||||||
|
@ -3341,8 +3341,28 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
|
|
||||||
let def_id = this.parent_scope.module.nearest_parent_mod();
|
let def_id = this.parent_scope.module.nearest_parent_mod();
|
||||||
let instead = res.is_some();
|
let instead = res.is_some();
|
||||||
let suggestion =
|
let suggestion = if let Some((start, end)) = this.diagnostic_metadata.in_range
|
||||||
if res.is_none() { this.report_missing_type_error(path) } else { None };
|
&& path[0].ident.span.lo() == end.span.lo()
|
||||||
|
{
|
||||||
|
let mut sugg = ".";
|
||||||
|
let mut span = start.span.between(end.span);
|
||||||
|
if span.lo() + BytePos(2) == span.hi() {
|
||||||
|
// There's no space between the start, the range op and the end, suggest
|
||||||
|
// removal which will look better.
|
||||||
|
span = span.with_lo(span.lo() + BytePos(1));
|
||||||
|
sugg = "";
|
||||||
|
}
|
||||||
|
Some((
|
||||||
|
span,
|
||||||
|
"you might have meant to write `.` instead of `..`",
|
||||||
|
sugg.to_string(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
))
|
||||||
|
} else if res.is_none() {
|
||||||
|
this.report_missing_type_error(path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
this.r.use_injections.push(UseError {
|
this.r.use_injections.push(UseError {
|
||||||
err,
|
err,
|
||||||
|
@ -4005,6 +4025,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
self.visit_expr(rhs);
|
self.visit_expr(rhs);
|
||||||
self.diagnostic_metadata.is_assign_rhs = false;
|
self.diagnostic_metadata.is_assign_rhs = false;
|
||||||
}
|
}
|
||||||
|
ExprKind::Range(Some(ref start), Some(ref end), RangeLimits::HalfOpen) => {
|
||||||
|
self.diagnostic_metadata.in_range = Some((start, end));
|
||||||
|
self.resolve_expr(start, Some(expr));
|
||||||
|
self.resolve_expr(end, Some(expr));
|
||||||
|
self.diagnostic_metadata.in_range = None;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
visit::walk_expr(self, expr);
|
visit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -796,6 +796,9 @@ impl Span {
|
||||||
|
|
||||||
/// Returns a `Span` that would enclose both `self` and `end`.
|
/// Returns a `Span` that would enclose both `self` and `end`.
|
||||||
///
|
///
|
||||||
|
/// Note that this can also be used to extend the span "backwards":
|
||||||
|
/// `start.to(end)` and `end.to(start)` return the same `Span`.
|
||||||
|
///
|
||||||
/// ```text
|
/// ```text
|
||||||
/// ____ ___
|
/// ____ ___
|
||||||
/// self lorem ipsum end
|
/// self lorem ipsum end
|
||||||
|
|
|
@ -166,7 +166,7 @@ mod spec_extend;
|
||||||
/// vec[0] = 7;
|
/// vec[0] = 7;
|
||||||
/// assert_eq!(vec[0], 7);
|
/// assert_eq!(vec[0], 7);
|
||||||
///
|
///
|
||||||
/// vec.extend([1, 2, 3].iter().copied());
|
/// vec.extend([1, 2, 3]);
|
||||||
///
|
///
|
||||||
/// for x in &vec {
|
/// for x in &vec {
|
||||||
/// println!("{x}");
|
/// println!("{x}");
|
||||||
|
|
|
@ -1783,7 +1783,7 @@ impl<T: ?Sized + fmt::Display> fmt::Display for RefMut<'_, T> {
|
||||||
/// until the reference expires. As a special exception, given an `&T`, any part of it that is
|
/// until the reference expires. As a special exception, given an `&T`, any part of it that is
|
||||||
/// inside an `UnsafeCell<_>` may be deallocated during the lifetime of the reference, after the
|
/// inside an `UnsafeCell<_>` may be deallocated during the lifetime of the reference, after the
|
||||||
/// last time the reference is used (dereferenced or reborrowed). Since you cannot deallocate a part
|
/// last time the reference is used (dereferenced or reborrowed). Since you cannot deallocate a part
|
||||||
/// of what a reference points to, this means the memory an `&T` points to can be deallocted only if
|
/// of what a reference points to, this means the memory an `&T` points to can be deallocated only if
|
||||||
/// *every part of it* (including padding) is inside an `UnsafeCell`.
|
/// *every part of it* (including padding) is inside an `UnsafeCell`.
|
||||||
///
|
///
|
||||||
/// However, whenever a `&UnsafeCell<T>` is constructed or dereferenced, it must still point to
|
/// However, whenever a `&UnsafeCell<T>` is constructed or dereferenced, it must still point to
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub struct LazyCell<T, F = fn() -> T> {
|
||||||
init: Cell<Option<F>>,
|
init: Cell<Option<F>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, F> LazyCell<T, F> {
|
impl<T, F: FnOnce() -> T> LazyCell<T, F> {
|
||||||
/// Creates a new lazy value with the given initializing function.
|
/// Creates a new lazy value with the given initializing function.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -55,9 +55,7 @@ impl<T, F> LazyCell<T, F> {
|
||||||
pub const fn new(init: F) -> LazyCell<T, F> {
|
pub const fn new(init: F) -> LazyCell<T, F> {
|
||||||
LazyCell { cell: OnceCell::new(), init: Cell::new(Some(init)) }
|
LazyCell { cell: OnceCell::new(), init: Cell::new(Some(init)) }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, F: FnOnce() -> T> LazyCell<T, F> {
|
|
||||||
/// Forces the evaluation of this lazy value and returns a reference to
|
/// Forces the evaluation of this lazy value and returns a reference to
|
||||||
/// the result.
|
/// the result.
|
||||||
///
|
///
|
||||||
|
|
|
@ -803,7 +803,7 @@ pub trait Iterator {
|
||||||
/// (0..5).map(|x| x * 2 + 1)
|
/// (0..5).map(|x| x * 2 + 1)
|
||||||
/// .for_each(move |x| tx.send(x).unwrap());
|
/// .for_each(move |x| tx.send(x).unwrap());
|
||||||
///
|
///
|
||||||
/// let v: Vec<_> = rx.iter().collect();
|
/// let v: Vec<_> = rx.iter().collect();
|
||||||
/// assert_eq!(v, vec![1, 3, 5, 7, 9]);
|
/// assert_eq!(v, vec![1, 3, 5, 7, 9]);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
|
|
@ -106,6 +106,12 @@ fn lazy_new() {
|
||||||
assert_eq!(called.get(), 1);
|
assert_eq!(called.get(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that we can infer `T` from closure's type.
|
||||||
|
#[test]
|
||||||
|
fn lazy_type_inference() {
|
||||||
|
let _ = LazyCell::new(|| ());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn aliasing_in_get() {
|
fn aliasing_in_get() {
|
||||||
let x = OnceCell::new();
|
let x = OnceCell::new();
|
||||||
|
|
|
@ -46,17 +46,14 @@ pub struct LazyLock<T, F = fn() -> T> {
|
||||||
cell: OnceLock<T>,
|
cell: OnceLock<T>,
|
||||||
init: Cell<Option<F>>,
|
init: Cell<Option<F>>,
|
||||||
}
|
}
|
||||||
|
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
|
||||||
impl<T, F> LazyLock<T, F> {
|
|
||||||
/// Creates a new lazy value with the given initializing
|
/// Creates a new lazy value with the given initializing
|
||||||
/// function.
|
/// function.
|
||||||
#[unstable(feature = "once_cell", issue = "74465")]
|
#[unstable(feature = "once_cell", issue = "74465")]
|
||||||
pub const fn new(f: F) -> LazyLock<T, F> {
|
pub const fn new(f: F) -> LazyLock<T, F> {
|
||||||
LazyLock { cell: OnceLock::new(), init: Cell::new(Some(f)) }
|
LazyLock { cell: OnceLock::new(), init: Cell::new(Some(f)) }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
|
|
||||||
/// Forces the evaluation of this lazy value and
|
/// Forces the evaluation of this lazy value and
|
||||||
/// returns a reference to result. This is equivalent
|
/// returns a reference to result. This is equivalent
|
||||||
/// to the `Deref` impl, but is explicit.
|
/// to the `Deref` impl, but is explicit.
|
||||||
|
|
|
@ -136,6 +136,12 @@ fn sync_lazy_poisoning() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that we can infer `T` from closure's type.
|
||||||
|
#[test]
|
||||||
|
fn lazy_type_inference() {
|
||||||
|
let _ = LazyCell::new(|| ());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_sync_send() {
|
fn is_sync_send() {
|
||||||
fn assert_traits<T: Send + Sync>() {}
|
fn assert_traits<T: Send + Sync>() {}
|
||||||
|
|
|
@ -9,11 +9,11 @@ are the same:
|
||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
/// This is a doc comment.
|
/// This is a doc comment.
|
||||||
#[doc = " This is a doc comment."]
|
#[doc = r" This is a doc comment."]
|
||||||
# fn f() {}
|
# fn f() {}
|
||||||
```
|
```
|
||||||
|
|
||||||
(Note the leading space in the attribute version.)
|
(Note the leading space and the raw string literal in the attribute version.)
|
||||||
|
|
||||||
In most cases, `///` is easier to use than `#[doc]`. One case where the latter is easier is
|
In most cases, `///` is easier to use than `#[doc]`. One case where the latter is easier is
|
||||||
when generating documentation in macros; the `collapse-docs` pass will combine multiple
|
when generating documentation in macros; the `collapse-docs` pass will combine multiple
|
||||||
|
|
9
src/test/ui/argument-suggestions/extern-fn-arg-names.rs
Normal file
9
src/test/ui/argument-suggestions/extern-fn-arg-names.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
extern "Rust" {
|
||||||
|
fn dstfn(src: i32, dst: err);
|
||||||
|
//~^ ERROR cannot find type `err` in this scope
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dstfn(1);
|
||||||
|
//~^ ERROR this function takes 2 arguments but 1 argument was supplied
|
||||||
|
}
|
26
src/test/ui/argument-suggestions/extern-fn-arg-names.stderr
Normal file
26
src/test/ui/argument-suggestions/extern-fn-arg-names.stderr
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
error[E0412]: cannot find type `err` in this scope
|
||||||
|
--> $DIR/extern-fn-arg-names.rs:2:29
|
||||||
|
|
|
||||||
|
LL | fn dstfn(src: i32, dst: err);
|
||||||
|
| ^^^ not found in this scope
|
||||||
|
|
||||||
|
error[E0061]: this function takes 2 arguments but 1 argument was supplied
|
||||||
|
--> $DIR/extern-fn-arg-names.rs:7:5
|
||||||
|
|
|
||||||
|
LL | dstfn(1);
|
||||||
|
| ^^^^^--- an argument is missing
|
||||||
|
|
|
||||||
|
note: function defined here
|
||||||
|
--> $DIR/extern-fn-arg-names.rs:2:8
|
||||||
|
|
|
||||||
|
LL | fn dstfn(src: i32, dst: err);
|
||||||
|
| ^^^^^
|
||||||
|
help: provide the argument
|
||||||
|
|
|
||||||
|
LL | dstfn(1, /* dst */);
|
||||||
|
| ~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0061, E0412.
|
||||||
|
For more information about an error, try `rustc --explain E0061`.
|
137
src/test/ui/rust-2018/edition-lint-infer-outlives-macro.fixed
Normal file
137
src/test/ui/rust-2018/edition-lint-infer-outlives-macro.fixed
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
// edition:2018
|
||||||
|
// aux-build:edition-lint-infer-outlives-macro.rs
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![deny(explicit_outlives_requirements)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate edition_lint_infer_outlives_macro;
|
||||||
|
|
||||||
|
// Test that the lint does not fire if the predicate is from the local crate,
|
||||||
|
// but all the bounds are from an external macro.
|
||||||
|
macro_rules! make_foo {
|
||||||
|
($a:tt) => {
|
||||||
|
struct Foo<$a, 'b: $a> {
|
||||||
|
foo: &$a &'b (),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FooWhere<$a, 'b> where 'b: $a {
|
||||||
|
foo: &$a &'b (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gimme_a! {make_foo!}
|
||||||
|
|
||||||
|
struct Bar<'a, 'b> {
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
bar: &'a &'b (),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BarWhere<'a, 'b> {
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
bar: &'a &'b (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that the lint *does* fire if the predicate is contained in a local macro.
|
||||||
|
mod everything_inside {
|
||||||
|
macro_rules! m {
|
||||||
|
('b: 'a) => {
|
||||||
|
struct Foo<'a, 'b>(&'a &'b ());
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Bar<'a, 'b>(&'a &'b ()) ;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Baz<'a, 'b>(&'a &'b ()) where (): Sized, ;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
};
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod inner_lifetime_outside_colon_inside {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:lifetime: 'a) => {
|
||||||
|
struct Foo<'a, $b>(&'a &$b ());
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Bar<'a, $b>(&'a &$b ()) ;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Baz<'a, $b>(&'a &$b ()) where (): Sized, ;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod outer_lifetime_outside_colon_inside {
|
||||||
|
macro_rules! m {
|
||||||
|
('b: $a:lifetime) => {
|
||||||
|
struct Foo<$a, 'b: $a>(&$a &'b ());
|
||||||
|
struct Bar<$a, 'b>(&$a &'b ()) where 'b: $a;
|
||||||
|
struct Baz<$a, 'b>(&$a &'b ()) where (): Sized, 'b: $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod both_lifetimes_outside_colon_inside {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:lifetime: $a:lifetime) => {
|
||||||
|
struct Foo<$a, $b: $a>(&$a &$b ());
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where $b: $a;
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b: $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod everything_outside {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:lifetime $colon:tt $a:lifetime) => {
|
||||||
|
struct Foo<$a, $b $colon $a>(&$a &$b ());
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a;
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod everything_outside_with_tt_inner {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:tt $colon:tt $a:lifetime) => {
|
||||||
|
struct Foo<$a, $b $colon $a>(&$a &$b ());
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a;
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: These should be consistent.
|
||||||
|
mod everything_outside_with_tt_outer {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:lifetime $colon:tt $a:tt) => {
|
||||||
|
struct Foo<$a, $b >(&$a &$b ());
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a;
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod everything_outside_with_tt_both {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:tt $colon:tt $a:tt) => {
|
||||||
|
struct Foo<$a, $b >(&$a &$b ());
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where ;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, ;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -1,18 +1,22 @@
|
||||||
// edition:2018
|
// edition:2018
|
||||||
// aux-build:edition-lint-infer-outlives-macro.rs
|
// aux-build:edition-lint-infer-outlives-macro.rs
|
||||||
|
// run-rustfix
|
||||||
// Test that the lint does not fire if the where predicate
|
|
||||||
// is from the local crate, but all the bounds are from an
|
|
||||||
// external macro.
|
|
||||||
|
|
||||||
#![deny(explicit_outlives_requirements)]
|
#![deny(explicit_outlives_requirements)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate edition_lint_infer_outlives_macro;
|
extern crate edition_lint_infer_outlives_macro;
|
||||||
|
|
||||||
|
// Test that the lint does not fire if the predicate is from the local crate,
|
||||||
|
// but all the bounds are from an external macro.
|
||||||
macro_rules! make_foo {
|
macro_rules! make_foo {
|
||||||
($a:tt) => {
|
($a:tt) => {
|
||||||
struct Foo<$a, 'b> where 'b: $a {
|
struct Foo<$a, 'b: $a> {
|
||||||
|
foo: &$a &'b (),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FooWhere<$a, 'b> where 'b: $a {
|
||||||
foo: &$a &'b (),
|
foo: &$a &'b (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,4 +29,109 @@ struct Bar<'a, 'b: 'a> {
|
||||||
bar: &'a &'b (),
|
bar: &'a &'b (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BarWhere<'a, 'b> where 'b: 'a {
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
bar: &'a &'b (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that the lint *does* fire if the predicate is contained in a local macro.
|
||||||
|
mod everything_inside {
|
||||||
|
macro_rules! m {
|
||||||
|
('b: 'a) => {
|
||||||
|
struct Foo<'a, 'b: 'a>(&'a &'b ());
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Bar<'a, 'b>(&'a &'b ()) where 'b: 'a;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Baz<'a, 'b>(&'a &'b ()) where (): Sized, 'b: 'a;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
};
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod inner_lifetime_outside_colon_inside {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:lifetime: 'a) => {
|
||||||
|
struct Foo<'a, $b: 'a>(&'a &$b ());
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Bar<'a, $b>(&'a &$b ()) where $b: 'a;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Baz<'a, $b>(&'a &$b ()) where (): Sized, $b: 'a;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod outer_lifetime_outside_colon_inside {
|
||||||
|
macro_rules! m {
|
||||||
|
('b: $a:lifetime) => {
|
||||||
|
struct Foo<$a, 'b: $a>(&$a &'b ());
|
||||||
|
struct Bar<$a, 'b>(&$a &'b ()) where 'b: $a;
|
||||||
|
struct Baz<$a, 'b>(&$a &'b ()) where (): Sized, 'b: $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod both_lifetimes_outside_colon_inside {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:lifetime: $a:lifetime) => {
|
||||||
|
struct Foo<$a, $b: $a>(&$a &$b ());
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where $b: $a;
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b: $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod everything_outside {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:lifetime $colon:tt $a:lifetime) => {
|
||||||
|
struct Foo<$a, $b $colon $a>(&$a &$b ());
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a;
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod everything_outside_with_tt_inner {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:tt $colon:tt $a:lifetime) => {
|
||||||
|
struct Foo<$a, $b $colon $a>(&$a &$b ());
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a;
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: These should be consistent.
|
||||||
|
mod everything_outside_with_tt_outer {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:lifetime $colon:tt $a:tt) => {
|
||||||
|
struct Foo<$a, $b $colon $a>(&$a &$b ());
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a;
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod everything_outside_with_tt_both {
|
||||||
|
macro_rules! m {
|
||||||
|
($b:tt $colon:tt $a:tt) => {
|
||||||
|
struct Foo<$a, $b $colon $a>(&$a &$b ());
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a;
|
||||||
|
//~^ ERROR: outlives requirements can be inferred
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m!('b: 'a);
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,14 +1,110 @@
|
||||||
error: outlives requirements can be inferred
|
error: outlives requirements can be inferred
|
||||||
--> $DIR/edition-lint-infer-outlives-macro.rs:23:18
|
--> $DIR/edition-lint-infer-outlives-macro.rs:27:18
|
||||||
|
|
|
|
||||||
LL | struct Bar<'a, 'b: 'a> {
|
LL | struct Bar<'a, 'b: 'a> {
|
||||||
| ^^^^ help: remove this bound
|
| ^^^^ help: remove this bound
|
||||||
|
|
|
|
||||||
note: the lint level is defined here
|
note: the lint level is defined here
|
||||||
--> $DIR/edition-lint-infer-outlives-macro.rs:8:9
|
--> $DIR/edition-lint-infer-outlives-macro.rs:5:9
|
||||||
|
|
|
|
||||||
LL | #![deny(explicit_outlives_requirements)]
|
LL | #![deny(explicit_outlives_requirements)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:32:24
|
||||||
|
|
|
||||||
|
LL | struct BarWhere<'a, 'b> where 'b: 'a {
|
||||||
|
| ^^^^^^^^^^^^^ help: remove this bound
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:41:30
|
||||||
|
|
|
||||||
|
LL | struct Foo<'a, 'b: 'a>(&'a &'b ());
|
||||||
|
| ^^^^ help: remove this bound
|
||||||
|
...
|
||||||
|
LL | m!('b: 'a);
|
||||||
|
| ---------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:43:44
|
||||||
|
|
|
||||||
|
LL | struct Bar<'a, 'b>(&'a &'b ()) where 'b: 'a;
|
||||||
|
| ^^^^^^^^^^^^ help: remove this bound
|
||||||
|
...
|
||||||
|
LL | m!('b: 'a);
|
||||||
|
| ---------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:45:61
|
||||||
|
|
|
||||||
|
LL | struct Baz<'a, 'b>(&'a &'b ()) where (): Sized, 'b: 'a;
|
||||||
|
| ^^^^^^ help: remove this bound
|
||||||
|
...
|
||||||
|
LL | m!('b: 'a);
|
||||||
|
| ---------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:55:30
|
||||||
|
|
|
||||||
|
LL | struct Foo<'a, $b: 'a>(&'a &$b ());
|
||||||
|
| ^^^^ help: remove this bound
|
||||||
|
...
|
||||||
|
LL | m!('b: 'a);
|
||||||
|
| ---------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:57:44
|
||||||
|
|
|
||||||
|
LL | struct Bar<'a, $b>(&'a &$b ()) where $b: 'a;
|
||||||
|
| ^^^^^^^^^^^^ help: remove this bound
|
||||||
|
...
|
||||||
|
LL | m!('b: 'a);
|
||||||
|
| ---------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:59:61
|
||||||
|
|
|
||||||
|
LL | struct Baz<'a, $b>(&'a &$b ()) where (): Sized, $b: 'a;
|
||||||
|
| ^^^^^^ help: remove this bound
|
||||||
|
...
|
||||||
|
LL | m!('b: 'a);
|
||||||
|
| ---------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:114:31
|
||||||
|
|
|
||||||
|
LL | struct Foo<$a, $b $colon $a>(&$a &$b ());
|
||||||
|
| ^^^^^^^^^ help: remove this bound
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:126:31
|
||||||
|
|
|
||||||
|
LL | struct Foo<$a, $b $colon $a>(&$a &$b ());
|
||||||
|
| ^^^^^^^^^ help: remove this bound
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:128:50
|
||||||
|
|
|
||||||
|
LL | struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a;
|
||||||
|
| ^^^^^^^^^^^^ help: remove this bound
|
||||||
|
|
||||||
|
error: outlives requirements can be inferred
|
||||||
|
--> $DIR/edition-lint-infer-outlives-macro.rs:130:61
|
||||||
|
|
|
||||||
|
LL | struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a;
|
||||||
|
| ^^^^^^^^^^^^ help: remove this bound
|
||||||
|
|
||||||
|
error: aborting due to 12 previous errors
|
||||||
|
|
||||||
|
|
13
src/test/ui/suggestions/bad-hex-float-lit.rs
Normal file
13
src/test/ui/suggestions/bad-hex-float-lit.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
fn main() {
|
||||||
|
let _f: f32 = 0xAAf32;
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~| HELP rewrite this
|
||||||
|
|
||||||
|
let _f: f32 = 0xAB_f32;
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~| HELP rewrite this
|
||||||
|
|
||||||
|
let _f: f64 = 0xFF_f64;
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~| HELP rewrite this
|
||||||
|
}
|
48
src/test/ui/suggestions/bad-hex-float-lit.stderr
Normal file
48
src/test/ui/suggestions/bad-hex-float-lit.stderr
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/bad-hex-float-lit.rs:2:19
|
||||||
|
|
|
||||||
|
LL | let _f: f32 = 0xAAf32;
|
||||||
|
| --- ^^^^^^^ expected `f32`, found integer
|
||||||
|
| |
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
help: rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float
|
||||||
|
|
|
||||||
|
LL | let _f: f32 = 0xAA as f32;
|
||||||
|
| ~~~~~~~~~~~
|
||||||
|
LL | let _f: f32 = 170_f32;
|
||||||
|
| ~~~~~~~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/bad-hex-float-lit.rs:6:19
|
||||||
|
|
|
||||||
|
LL | let _f: f32 = 0xAB_f32;
|
||||||
|
| --- ^^^^^^^^ expected `f32`, found integer
|
||||||
|
| |
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
help: rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float
|
||||||
|
|
|
||||||
|
LL | let _f: f32 = 0xAB as f32;
|
||||||
|
| ~~~~~~~~~~~
|
||||||
|
LL | let _f: f32 = 171_f32;
|
||||||
|
| ~~~~~~~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/bad-hex-float-lit.rs:10:19
|
||||||
|
|
|
||||||
|
LL | let _f: f64 = 0xFF_f64;
|
||||||
|
| --- ^^^^^^^^ expected `f64`, found integer
|
||||||
|
| |
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
help: rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float
|
||||||
|
|
|
||||||
|
LL | let _f: f64 = 0xFF as f64;
|
||||||
|
| ~~~~~~~~~~~
|
||||||
|
LL | let _f: f64 = 255_f64;
|
||||||
|
| ~~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -0,0 +1,30 @@
|
||||||
|
fn as_ref() -> Option<Vec<u8>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
struct Type {
|
||||||
|
option: Option<Vec<u8>>
|
||||||
|
}
|
||||||
|
trait Trait {
|
||||||
|
fn foo(&self) -> Vec<u8>;
|
||||||
|
}
|
||||||
|
impl Trait for Option<Vec<u8>> {
|
||||||
|
fn foo(&self) -> Vec<u8> {
|
||||||
|
vec![1, 2, 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
fn method(&self) -> Option<Vec<u8>> {
|
||||||
|
self.option..as_ref().map(|x| x)
|
||||||
|
//~^ ERROR E0308
|
||||||
|
}
|
||||||
|
fn method2(&self) -> &u8 {
|
||||||
|
self.option..foo().get(0)
|
||||||
|
//~^ ERROR E0425
|
||||||
|
//~| ERROR E0308
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = Type { option: None }.method();
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
error[E0425]: cannot find function `foo` in this scope
|
||||||
|
--> $DIR/method-access-to-range-literal-typo.rs:22:22
|
||||||
|
|
|
||||||
|
LL | self.option..foo().get(0)
|
||||||
|
| ^^^ not found in this scope
|
||||||
|
|
|
||||||
|
help: you might have meant to write `.` instead of `..`
|
||||||
|
|
|
||||||
|
LL - self.option..foo().get(0)
|
||||||
|
LL + self.option.foo().get(0)
|
||||||
|
|
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/method-access-to-range-literal-typo.rs:18:9
|
||||||
|
|
|
||||||
|
LL | fn method(&self) -> Option<Vec<u8>> {
|
||||||
|
| --------------- expected `Option<Vec<u8>>` because of return type
|
||||||
|
LL | self.option..as_ref().map(|x| x)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range`
|
||||||
|
|
|
||||||
|
= note: expected enum `Option<_>`
|
||||||
|
found struct `std::ops::Range<Option<_>>`
|
||||||
|
help: you likely meant to write a method call instead of a range
|
||||||
|
|
|
||||||
|
LL - self.option..as_ref().map(|x| x)
|
||||||
|
LL + self.option.as_ref().map(|x| x)
|
||||||
|
|
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/method-access-to-range-literal-typo.rs:22:9
|
||||||
|
|
|
||||||
|
LL | fn method2(&self) -> &u8 {
|
||||||
|
| --- expected `&u8` because of return type
|
||||||
|
LL | self.option..foo().get(0)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found struct `Range`
|
||||||
|
|
|
||||||
|
= note: expected reference `&u8`
|
||||||
|
found struct `std::ops::Range<Option<Vec<u8>>>`
|
||||||
|
help: you likely meant to write a method call instead of a range
|
||||||
|
|
|
||||||
|
LL - self.option..foo().get(0)
|
||||||
|
LL + self.option.foo().get(0)
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0308, E0425.
|
||||||
|
For more information about an error, try `rustc --explain E0308`.
|
Loading…
Add table
Add a link
Reference in a new issue