1
Fork 0

Auto merge of #125436 - matthiaskrgr:rollup-uijo2ga, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #122665 (Add some tests for public-private dependencies.)
 - #123623 (Fix OutsideLoop's error suggestion: adding label `'block` for `if` block.)
 - #125054 (Handle `ReVar` in `note_and_explain_region`)
 - #125156 (Expand `for_loops_over_fallibles` lint to lint on fallibles behind references.)
 - #125222 (Migrate `run-make/issue-46239` to `rmake`)
 - #125316 (Tweak `Spacing` use)
 - #125392 (Wrap Context.ext in AssertUnwindSafe)
 - #125417 (self-contained linker: retry linking without `-fuse-ld=lld` on CCs that don't support it)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-05-23 06:14:03 +00:00
commit 39d2f2affd
52 changed files with 931 additions and 158 deletions

View file

@ -661,11 +661,11 @@ impl TokenStream {
if attr_style == AttrStyle::Inner {
vec![
TokenTree::token_joint(token::Pound, span),
TokenTree::token_alone(token::Not, span),
TokenTree::token_joint_hidden(token::Not, span),
body,
]
} else {
vec![TokenTree::token_alone(token::Pound, span), body]
vec![TokenTree::token_joint_hidden(token::Pound, span), body]
}
}
}

View file

@ -681,22 +681,40 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
}
}
// The easiest way to implement token stream pretty printing would be to
// print each token followed by a single space. But that would produce ugly
// output, so we go to some effort to do better.
//
// First, we track whether each token that appears in source code is
// followed by a space, with `Spacing`, and reproduce that in the output.
// This works well in a lot of cases. E.g. `stringify!(x + y)` produces
// "x + y" and `stringify!(x+y)` produces "x+y".
//
// But this doesn't work for code produced by proc macros (which have no
// original source text representation) nor for code produced by decl
// macros (which are tricky because the whitespace after tokens appearing
// in macro rules isn't always what you want in the produced output). For
// these we mostly use `Spacing::Alone`, which is the conservative choice.
//
// So we have a backup mechanism for when `Spacing::Alone` occurs between a
// pair of tokens: we check if that pair of tokens can obviously go
// together without a space between them. E.g. token `x` followed by token
// `,` is better printed as `x,` than `x ,`. (Even if the original source
// code was `x ,`.)
//
// Finally, we must be careful about changing the output. Token pretty
// printing is used by `stringify!` and `impl Display for
// proc_macro::TokenStream`, and some programs rely on the output having a
// particular form, even though they shouldn't. In particular, some proc
// macros do `format!({stream})` on a token stream and then "parse" the
// output with simple string matching that can't handle whitespace changes.
// E.g. we have seen cases where a proc macro can handle `a :: b` but not
// `a::b`. See #117433 for some examples.
fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
let mut iter = tts.trees().peekable();
while let Some(tt) = iter.next() {
let spacing = self.print_tt(tt, convert_dollar_crate);
if let Some(next) = iter.peek() {
// Should we print a space after `tt`? There are two guiding
// factors.
// - `spacing` is the more important and accurate one. Most
// tokens have good spacing information, and
// `Joint`/`JointHidden` get used a lot.
// - `space_between` is the backup. Code produced by proc
// macros has worse spacing information, with no
// `JointHidden` usage and too much `Alone` usage, which
// would result in over-spaced output such as
// `( x () , y . z )`. `space_between` avoids some of the
// excess whitespace.
if spacing == Spacing::Alone && space_between(tt, next) {
self.space();
}

View file

@ -153,7 +153,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
fn build_panic(&self, expr_str: &str, panic_path: Path) -> P<Expr> {
let escaped_expr_str = escape_to_fmt(expr_str);
let initial = [
TokenTree::token_joint_hidden(
TokenTree::token_joint(
token::Literal(token::Lit {
kind: token::LitKind::Str,
symbol: Symbol::intern(&if self.fmt_string.is_empty() {
@ -172,7 +172,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
];
let captures = self.capture_decls.iter().flat_map(|cap| {
[
TokenTree::token_joint_hidden(
TokenTree::token_joint(
token::Ident(cap.ident.name, IdentIsRaw::No),
cap.ident.span,
),

View file

@ -3,7 +3,7 @@ use crate::deriving::generic::*;
use crate::errors;
use core::ops::ControlFlow;
use rustc_ast as ast;
use rustc_ast::visit::walk_list;
use rustc_ast::visit::visit_opt;
use rustc_ast::{attr, EnumDef, VariantData};
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
use rustc_span::symbol::Ident;
@ -224,7 +224,7 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, '
self.visit_ident(v.ident);
self.visit_vis(&v.vis);
self.visit_variant_data(&v.data);
walk_list!(self, visit_anon_const, &v.disr_expr);
visit_opt!(self, visit_anon_const, &v.disr_expr);
for attr in &v.attrs {
rustc_ast::visit::walk_attribute(self, attr);
}

View file

@ -799,6 +799,27 @@ fn link_natively(
continue;
}
// Check if linking failed with an error message that indicates the driver didn't recognize
// the `-fuse-ld=lld` option. If so, re-perform the link step without it. This avoids having
// to spawn multiple instances on the happy path to do version checking, and ensures things
// keep working on the tier 1 baseline of GLIBC 2.17+. That is generally understood as GCCs
// circa RHEL/CentOS 7, 4.5 or so, whereas lld support was added in GCC 9.
if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, Lld::Yes))
&& unknown_arg_regex.is_match(&out)
&& out.contains("-fuse-ld=lld")
&& cmd.get_args().iter().any(|e| e.to_string_lossy() == "-fuse-ld=lld")
{
info!("linker output: {:?}", out);
warn!("The linker driver does not support `-fuse-ld=lld`. Retrying without it.");
for arg in cmd.take_args() {
if arg.to_string_lossy() != "-fuse-ld=lld" {
cmd.arg(arg);
}
}
info!("{:?}", &cmd);
continue;
}
// Detect '-static-pie' used with an older version of gcc or clang not supporting it.
// Fallback from '-static-pie' to '-static' in that case.
if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))

View file

@ -68,12 +68,15 @@ pub(crate) enum KleeneOp {
/// `MetaVarExpr` are "first-class" token trees. Useful for parsing macros.
#[derive(Debug, PartialEq, Encodable, Decodable)]
enum TokenTree {
/// A token. Unlike `tokenstream::TokenTree::Token` this lacks a `Spacing`.
/// See the comments about `Spacing` in the `transcribe` function.
Token(Token),
/// A delimited sequence, e.g. `($e:expr)` (RHS) or `{ $e }` (LHS).
Delimited(DelimSpan, DelimSpacing, Delimited),
/// A kleene-style repetition sequence, e.g. `$($e:expr)*` (RHS) or `$($e),*` (LHS).
Sequence(DelimSpan, SequenceRepetition),
/// e.g., `$var`.
/// e.g., `$var`. The span covers the leading dollar and the ident. (The span within the ident
/// only covers the ident, e.g. `var`.)
MetaVar(Span, Ident),
/// e.g., `$var:expr`. Only appears on the LHS.
MetaVarDecl(Span, Ident /* name to bind */, Option<NonterminalKind>),

View file

@ -62,7 +62,10 @@ pub(super) fn parse(
match tree {
TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
let span = match trees.next() {
Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => {
Some(&tokenstream::TokenTree::Token(
Token { kind: token::Colon, span: colon_span },
_,
)) => {
match trees.next() {
Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() {
Some((fragment, _)) => {
@ -126,10 +129,12 @@ pub(super) fn parse(
}
_ => token.span,
},
tree => tree.map_or(span, tokenstream::TokenTree::span),
Some(tree) => tree.span(),
None => colon_span,
}
}
tree => tree.map_or(start_sp, tokenstream::TokenTree::span),
Some(tree) => tree.span(),
None => start_sp,
};
result.push(TokenTree::MetaVarDecl(span, ident, None));
@ -176,7 +181,7 @@ fn parse_tree<'a>(
// Depending on what `tree` is, we could be parsing different parts of a macro
match tree {
// `tree` is a `$` token. Look at the next token in `trees`
&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => {
&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span: dollar_span }, _) => {
// FIXME: Handle `Invisible`-delimited groups in a more systematic way
// during parsing.
let mut next = outer_trees.next();
@ -209,7 +214,7 @@ fn parse_tree<'a>(
err.emit();
// Returns early the same read `$` to avoid spanning
// unrelated diagnostics that could be performed afterwards
return TokenTree::token(token::Dollar, span);
return TokenTree::token(token::Dollar, dollar_span);
}
Ok(elem) => {
maybe_emit_macro_metavar_expr_feature(
@ -251,7 +256,7 @@ fn parse_tree<'a>(
// special metavariable that names the crate of the invocation.
Some(tokenstream::TokenTree::Token(token, _)) if token.is_ident() => {
let (ident, is_raw) = token.ident().unwrap();
let span = ident.span.with_lo(span.lo());
let span = ident.span.with_lo(dollar_span.lo());
if ident.name == kw::Crate && matches!(is_raw, IdentIsRaw::No) {
TokenTree::token(token::Ident(kw::DollarCrate, is_raw), span)
} else {
@ -260,16 +265,19 @@ fn parse_tree<'a>(
}
// `tree` is followed by another `$`. This is an escaped `$`.
Some(&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => {
Some(&tokenstream::TokenTree::Token(
Token { kind: token::Dollar, span: dollar_span2 },
_,
)) => {
if parsing_patterns {
span_dollar_dollar_or_metavar_in_the_lhs_err(
sess,
&Token { kind: token::Dollar, span },
&Token { kind: token::Dollar, span: dollar_span2 },
);
} else {
maybe_emit_macro_metavar_expr_feature(features, sess, span);
maybe_emit_macro_metavar_expr_feature(features, sess, dollar_span2);
}
TokenTree::token(token::Dollar, span)
TokenTree::token(token::Dollar, dollar_span2)
}
// `tree` is followed by some other token. This is an error.
@ -281,7 +289,7 @@ fn parse_tree<'a>(
}
// There are no more tokens. Just return the `$` we already have.
None => TokenTree::token(token::Dollar, span),
None => TokenTree::token(token::Dollar, dollar_span),
}
}

View file

@ -253,8 +253,23 @@ pub(super) fn transcribe<'a>(
mbe::TokenTree::MetaVar(mut sp, mut original_ident) => {
// Find the matched nonterminal from the macro invocation, and use it to replace
// the meta-var.
//
// We use `Spacing::Alone` everywhere here, because that's the conservative choice
// and spacing of declarative macros is tricky. E.g. in this macro:
// ```
// macro_rules! idents {
// ($($a:ident,)*) => { stringify!($($a)*) }
// }
// ```
// `$a` has no whitespace after it and will be marked `JointHidden`. If you then
// call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So
// if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up
// producing "xyz", which is bad because it effectively merges tokens.
// `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid
// some of the unnecessary whitespace.
let ident = MacroRulesNormalizedIdent::new(original_ident);
if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
// njn: explain the use of alone here
let tt = match cur_matched {
MatchedSingle(ParseNtResult::Tt(tt)) => {
// `tt`s are emitted into the output stream directly as "raw tokens",

View file

@ -309,10 +309,10 @@ impl ToInternal<SmallVec<[tokenstream::TokenTree; 2]>>
use rustc_ast::token::*;
// The code below is conservative, using `token_alone`/`Spacing::Alone`
// in most places. When the resulting code is pretty-printed by
// `print_tts` it ends up with spaces between most tokens, which is
// safe but ugly. It's hard in general to do better when working at the
// token level.
// in most places. It's hard in general to do better when working at
// the token level. When the resulting code is pretty-printed by
// `print_tts` the `space_between` function helps avoid a lot of
// unnecessary whitespace, so the results aren't too bad.
let (tree, rustc) = self;
match tree {
TokenTree::Punct(Punct { ch, joint, span }) => {

View file

@ -6,7 +6,7 @@
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html
use rustc_ast::visit::walk_list;
use rustc_ast::visit::visit_opt;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
@ -168,7 +168,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
}
}
walk_list!(visitor, visit_expr, &blk.expr);
visit_opt!(visitor, visit_expr, &blk.expr);
}
visitor.cx = prev_cx;

View file

@ -173,7 +173,10 @@ pub(super) fn note_and_explain_region<'tcx>(
ty::ReError(_) => return,
ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => {
// FIXME(#125431): `ReVar` shouldn't reach here.
ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span),
ty::ReBound(..) | ty::ReErased => {
bug!("unexpected region for note_and_explain_region: {:?}", region);
}
};

View file

@ -263,7 +263,7 @@ lint_extern_without_abi = extern declarations without an explicit ABI are deprec
.help = the default ABI is {$default_abi}
lint_for_loops_over_fallibles =
for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement
.suggestion = consider using `if let` to clear intent
.remove_next = to iterate over `{$recv_snip}` remove the call to `next`
.use_while_let = to check pattern in a loop use `while let`

View file

@ -52,14 +52,27 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
let ty = cx.typeck_results().expr_ty(arg);
let &ty::Adt(adt, args) = ty.kind() else { return };
let (adt, args, ref_mutability) = match ty.kind() {
&ty::Adt(adt, args) => (adt, args, None),
&ty::Ref(_, ty, mutability) => match ty.kind() {
&ty::Adt(adt, args) => (adt, args, Some(mutability)),
_ => return,
},
_ => return,
};
let (article, ty, var) = match adt.did() {
did if cx.tcx.is_diagnostic_item(sym::Option, did) && ref_mutability.is_some() => ("a", "Option", "Some"),
did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"),
_ => return,
};
let ref_prefix = match ref_mutability {
None => "",
Some(ref_mutability) => ref_mutability.ref_prefix_str(),
};
let sub = if let Some(recv) = extract_iterator_next_call(cx, arg)
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
{
@ -85,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
cx.emit_span_lint(
FOR_LOOPS_OVER_FALLIBLES,
arg.span,
ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion },
ForLoopsOverFalliblesDiag { article, ref_prefix, ty, sub, question_mark, suggestion },
);
}
}

View file

@ -620,6 +620,7 @@ pub enum PtrNullChecksDiag<'a> {
#[diag(lint_for_loops_over_fallibles)]
pub struct ForLoopsOverFalliblesDiag<'a> {
pub article: &'static str,
pub ref_prefix: &'static str,
pub ty: &'static str,
#[subdiagnostic]
pub sub: ForLoopsOverFalliblesLoopSub<'a>,

View file

@ -925,7 +925,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
for subpattern in prefix.iter() {
self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f);
}
for subpattern in slice {
if let Some(subpattern) = slice {
self.visit_primary_bindings(
subpattern,
pattern_user_ty.clone().subslice(from, to),

View file

@ -1100,7 +1100,7 @@ pub struct BreakInsideCoroutine<'a> {
pub struct OutsideLoop<'a> {
#[primary_span]
#[label]
pub span: Span,
pub spans: Vec<Span>,
pub name: &'a str,
pub is_break: bool,
#[subdiagnostic]
@ -1112,7 +1112,7 @@ pub struct OutsideLoopSuggestion {
#[suggestion_part(code = "'block: ")]
pub block_span: Span,
#[suggestion_part(code = " 'block")]
pub break_span: Span,
pub break_spans: Vec<Span>,
}
#[derive(Diagnostic)]

View file

@ -1,3 +1,5 @@
use std::collections::BTreeMap;
use std::fmt;
use Context::*;
use rustc_hir as hir;
@ -25,22 +27,55 @@ enum Context {
Closure(Span),
Coroutine { coroutine_span: Span, kind: hir::CoroutineDesugaring, source: hir::CoroutineSource },
UnlabeledBlock(Span),
UnlabeledIfBlock(Span),
LabeledBlock,
Constant,
}
#[derive(Copy, Clone)]
#[derive(Clone)]
struct BlockInfo {
name: String,
spans: Vec<Span>,
suggs: Vec<Span>,
}
#[derive(PartialEq)]
enum BreakContextKind {
Break,
Continue,
}
impl fmt::Display for BreakContextKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BreakContextKind::Break => "break",
BreakContextKind::Continue => "continue",
}
.fmt(f)
}
}
#[derive(Clone)]
struct CheckLoopVisitor<'a, 'tcx> {
sess: &'a Session,
tcx: TyCtxt<'tcx>,
cx: Context,
// Keep track of a stack of contexts, so that suggestions
// are not made for contexts where it would be incorrect,
// such as adding a label for an `if`.
// e.g. `if 'foo: {}` would be incorrect.
cx_stack: Vec<Context>,
block_breaks: BTreeMap<Span, BlockInfo>,
}
fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
tcx.hir().visit_item_likes_in_module(
module_def_id,
&mut CheckLoopVisitor { sess: tcx.sess, tcx, cx: Normal },
);
let mut check = CheckLoopVisitor {
sess: tcx.sess,
tcx,
cx_stack: vec![Normal],
block_breaks: Default::default(),
};
tcx.hir().visit_item_likes_in_module(module_def_id, &mut check);
check.report_outside_loop_error();
}
pub(crate) fn provide(providers: &mut Providers) {
@ -83,6 +118,45 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
match e.kind {
hir::ExprKind::If(cond, then, else_opt) => {
self.visit_expr(cond);
let get_block = |ck_loop: &CheckLoopVisitor<'a, 'hir>,
expr: &hir::Expr<'hir>|
-> Option<&hir::Block<'hir>> {
if let hir::ExprKind::Block(b, None) = expr.kind
&& matches!(
ck_loop.cx_stack.last(),
Some(&Normal)
| Some(&Constant)
| Some(&UnlabeledBlock(_))
| Some(&UnlabeledIfBlock(_))
)
{
Some(b)
} else {
None
}
};
if let Some(b) = get_block(self, then) {
self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| {
v.visit_block(b)
});
} else {
self.visit_expr(then);
}
if let Some(else_expr) = else_opt {
if let Some(b) = get_block(self, else_expr) {
self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| {
v.visit_block(b)
});
} else {
self.visit_expr(else_expr);
}
}
}
hir::ExprKind::Loop(ref b, _, source, _) => {
self.with_context(Loop(source), |v| v.visit_block(b));
}
@ -101,11 +175,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
hir::ExprKind::Block(ref b, Some(_label)) => {
self.with_context(LabeledBlock, |v| v.visit_block(b));
}
hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => {
hir::ExprKind::Block(ref b, None) if matches!(self.cx_stack.last(), Some(&Fn)) => {
self.with_context(Normal, |v| v.visit_block(b));
}
hir::ExprKind::Block(ref b, None)
if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) =>
if matches!(
self.cx_stack.last(),
Some(&Normal) | Some(&Constant) | Some(&UnlabeledBlock(_))
) =>
{
self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b));
}
@ -178,7 +255,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
Some(label) => sp_lo.with_hi(label.ident.span.hi()),
None => sp_lo.shrink_to_lo(),
};
self.require_break_cx("break", e.span, label_sp);
self.require_break_cx(
BreakContextKind::Break,
e.span,
label_sp,
self.cx_stack.len() - 1,
);
}
hir::ExprKind::Continue(destination) => {
self.require_label_in_labeled_block(e.span, &destination, "continue");
@ -200,7 +282,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
}
Err(_) => {}
}
self.require_break_cx("continue", e.span, e.span)
self.require_break_cx(
BreakContextKind::Continue,
e.span,
e.span,
self.cx_stack.len() - 1,
)
}
_ => intravisit::walk_expr(self, e),
}
@ -212,18 +299,26 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
where
F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>),
{
let old_cx = self.cx;
self.cx = cx;
self.cx_stack.push(cx);
f(self);
self.cx = old_cx;
self.cx_stack.pop();
}
fn require_break_cx(&self, name: &str, span: Span, break_span: Span) {
let is_break = name == "break";
match self.cx {
fn require_break_cx(
&mut self,
br_cx_kind: BreakContextKind,
span: Span,
break_span: Span,
cx_pos: usize,
) {
match self.cx_stack[cx_pos] {
LabeledBlock | Loop(_) => {}
Closure(closure_span) => {
self.sess.dcx().emit_err(BreakInsideClosure { span, closure_span, name });
self.sess.dcx().emit_err(BreakInsideClosure {
span,
closure_span,
name: &br_cx_kind.to_string(),
});
}
Coroutine { coroutine_span, kind, source } => {
let kind = match kind {
@ -239,17 +334,32 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
self.sess.dcx().emit_err(BreakInsideCoroutine {
span,
coroutine_span,
name,
name: &br_cx_kind.to_string(),
kind,
source,
});
}
UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => {
let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });
self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion });
UnlabeledBlock(block_span)
if br_cx_kind == BreakContextKind::Break && block_span.eq_ctxt(break_span) =>
{
let block = self.block_breaks.entry(block_span).or_insert_with(|| BlockInfo {
name: br_cx_kind.to_string(),
spans: vec![],
suggs: vec![],
});
block.spans.push(span);
block.suggs.push(break_span);
}
Normal | Constant | Fn | UnlabeledBlock(_) => {
self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion: None });
UnlabeledIfBlock(_) if br_cx_kind == BreakContextKind::Break => {
self.require_break_cx(br_cx_kind, span, break_span, cx_pos - 1);
}
Normal | Constant | Fn | UnlabeledBlock(_) | UnlabeledIfBlock(_) => {
self.sess.dcx().emit_err(OutsideLoop {
spans: vec![span],
name: &br_cx_kind.to_string(),
is_break: br_cx_kind == BreakContextKind::Break,
suggestion: None,
});
}
}
}
@ -261,7 +371,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
cf_type: &str,
) -> bool {
if !span.is_desugaring(DesugaringKind::QuestionMark)
&& self.cx == LabeledBlock
&& self.cx_stack.last() == Some(&LabeledBlock)
&& label.label.is_none()
{
self.sess.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type });
@ -269,4 +379,18 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
}
false
}
fn report_outside_loop_error(&mut self) {
for (s, block) in &self.block_breaks {
self.sess.dcx().emit_err(OutsideLoop {
spans: block.spans.clone(),
name: &block.name,
is_break: true,
suggestion: Some(OutsideLoopSuggestion {
block_span: *s,
break_spans: block.suggs.clone(),
}),
});
}
}
}

View file

@ -12,7 +12,7 @@ use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
use crate::{ResolutionError, Resolver, Segment, UseError};
use rustc_ast::ptr::P;
use rustc_ast::visit::{walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::{codes::*, Applicability, DiagArgValue, IntoDiagArg, StashKey};
@ -3280,7 +3280,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
fn resolve_local(&mut self, local: &'ast Local) {
debug!("resolving local ({:?})", local);
// Resolve the type.
walk_list!(self, visit_ty, &local.ty);
visit_opt!(self, visit_ty, &local.ty);
// Resolve the initializer.
if let Some((init, els)) = local.kind.init_else_opt() {
@ -3479,8 +3479,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
fn resolve_arm(&mut self, arm: &'ast Arm) {
self.with_rib(ValueNS, RibKind::Normal, |this| {
this.resolve_pattern_top(&arm.pat, PatternSource::Match);
walk_list!(this, visit_expr, &arm.guard);
walk_list!(this, visit_expr, &arm.body);
visit_opt!(this, visit_expr, &arm.guard);
visit_opt!(this, visit_expr, &arm.body);
});
}

View file

@ -5,6 +5,7 @@ use crate::mem::transmute;
use crate::any::Any;
use crate::fmt;
use crate::marker::PhantomData;
use crate::panic::AssertUnwindSafe;
use crate::ptr;
/// A `RawWaker` allows the implementor of a task executor to create a [`Waker`]
@ -236,7 +237,7 @@ enum ExtData<'a> {
pub struct Context<'a> {
waker: &'a Waker,
local_waker: &'a LocalWaker,
ext: ExtData<'a>,
ext: AssertUnwindSafe<ExtData<'a>>,
// Ensure we future-proof against variance changes by forcing
// the lifetime to be invariant (argument-position lifetimes
// are contravariant while return-position lifetimes are
@ -279,7 +280,9 @@ impl<'a> Context<'a> {
#[unstable(feature = "context_ext", issue = "123392")]
#[rustc_const_unstable(feature = "const_waker", issue = "102012")]
pub const fn ext(&mut self) -> &mut dyn Any {
match &mut self.ext {
// FIXME: this field makes Context extra-weird about unwind safety
// can we justify AssertUnwindSafe if we stabilize this? do we care?
match &mut *self.ext {
ExtData::Some(data) => *data,
ExtData::None(unit) => unit,
}
@ -353,7 +356,7 @@ impl<'a> ContextBuilder<'a> {
#[rustc_const_unstable(feature = "const_waker", issue = "102012")]
#[unstable(feature = "context_ext", issue = "123392")]
pub const fn from(cx: &'a mut Context<'_>) -> Self {
let ext = match &mut cx.ext {
let ext = match &mut *cx.ext {
ExtData::Some(ext) => ExtData::Some(*ext),
ExtData::None(()) => ExtData::None(()),
};
@ -396,7 +399,7 @@ impl<'a> ContextBuilder<'a> {
#[rustc_const_unstable(feature = "const_waker", issue = "102012")]
pub const fn build(self) -> Context<'a> {
let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self;
Context { waker, local_waker, ext, _marker, _marker2 }
Context { waker, local_waker, ext: AssertUnwindSafe(ext), _marker, _marker2 }
}
}

View file

@ -170,6 +170,7 @@ pub fn test_iter() {
}
#[test]
#[allow(for_loops_over_fallibles)]
pub fn test_iter_mut() {
let mut ok: Result<isize, &'static str> = Ok(100);
for loc in ok.iter_mut() {

View file

@ -282,7 +282,7 @@ fn push_expected_errors(
// Add notes for the backtrace
for span in primary_spans {
for frame in &span.expansion {
if let Some(frame) = &span.expansion {
push_backtrace(expected_errors, frame, file_name);
}
}
@ -315,7 +315,7 @@ fn push_backtrace(
});
}
for previous_expansion in &expansion.span.expansion {
if let Some(previous_expansion) = &expansion.span.expansion {
push_backtrace(expected_errors, previous_expansion, file_name);
}
}

View file

@ -111,7 +111,6 @@ run-make/issue-37839/Makefile
run-make/issue-37893/Makefile
run-make/issue-38237/Makefile
run-make/issue-40535/Makefile
run-make/issue-46239/Makefile
run-make/issue-47384/Makefile
run-make/issue-47551/Makefile
run-make/issue-51671/Makefile

View file

@ -0,0 +1,21 @@
//@ compile-flags: -Copt-level=1
// References returned by a Frozen pointer type
// could be marked as "noalias", which caused miscompilation errors.
// This test runs the most minimal possible code that can reproduce this bug,
// and checks that noalias does not appear.
// See https://github.com/rust-lang/rust/issues/46239
#![crate_type = "lib"]
fn project<T>(x: &(T,)) -> &T { &x.0 }
fn dummy() {}
// CHECK-LABEL: @foo(
// CHECK-NOT: noalias
#[no_mangle]
pub fn foo() {
let f = (dummy as fn(),);
(*project(&f))();
}

View file

@ -1,6 +0,0 @@
# ignore-cross-compile
include ../tools.mk
all:
$(RUSTC) main.rs -C opt-level=1
$(call RUN,main)

View file

@ -1,8 +0,0 @@
fn project<T>(x: &(T,)) -> &T { &x.0 }
fn dummy() {}
fn main() {
let f = (dummy as fn(),);
(*project(&f))();
}

View file

@ -11,7 +11,6 @@ fn main() {
is_unwindsafe(async {
//~^ ERROR the type `&mut Context<'_>` may not be safely transferred across an unwind boundary
//~| ERROR the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary
use std::ptr::null;
use std::task::{Context, RawWaker, RawWakerVTable, Waker};
let waker = unsafe {

View file

@ -6,18 +6,19 @@ LL | is_unwindsafe(async {
| |_____|
| ||
LL | ||
LL | ||
LL | || use std::ptr::null;
LL | || use std::task::{Context, RawWaker, RawWakerVTable, Waker};
... ||
LL | || drop(cx_ref);
LL | || });
| ||_____-^ `&mut Context<'_>` may not be safely transferred across an unwind boundary
| |_____|
| within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`
| within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}`
|
= help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe`
= help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}: UnwindSafe`
= note: `UnwindSafe` is implemented for `&Context<'_>`, but not for `&mut Context<'_>`
note: future does not implement `UnwindSafe` as this value is used across an await
--> $DIR/async-is-unwindsafe.rs:26:18
--> $DIR/async-is-unwindsafe.rs:25:18
|
LL | let cx_ref = &mut cx;
| ------ has type `&mut Context<'_>` which does not implement `UnwindSafe`
@ -30,38 +31,6 @@ note: required by a bound in `is_unwindsafe`
LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {}
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe`
error[E0277]: the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary
--> $DIR/async-is-unwindsafe.rs:12:5
|
LL | is_unwindsafe(async {
| _____^_____________-
| |_____|
| ||
LL | ||
LL | ||
LL | || use std::ptr::null;
... ||
LL | || drop(cx_ref);
LL | || });
| ||_____-^ `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary
| |_____|
| within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`
|
= help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut (dyn Any + 'static)`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe`
note: future does not implement `UnwindSafe` as this value is used across an await
--> $DIR/async-is-unwindsafe.rs:26:18
|
LL | let mut cx = Context::from_waker(&waker);
| ------ has type `Context<'_>` which does not implement `UnwindSafe`
...
LL | async {}.await; // this needs an inner await point
| ^^^^^ await occurs here, with `mut cx` maybe used later
note: required by a bound in `is_unwindsafe`
--> $DIR/async-is-unwindsafe.rs:3:26
|
LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {}
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe`
error: aborting due to 2 previous errors
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,14 @@
//@ run-pass
// Tests against a regression surfaced by crater in https://github.com/rust-lang/rust/issues/125193
// Unwind Safety is not a very coherent concept, but we'd prefer no regressions until we kibosh it
// and this is an unstable feature anyways sooo...
use std::panic::UnwindSafe;
use std::task::Context;
fn unwind_safe<T: UnwindSafe>() {}
fn main() {
unwind_safe::<Context<'_>>(); // test UnwindSafe
unwind_safe::<&Context<'_>>(); // test RefUnwindSafe
}

View file

@ -0,0 +1,8 @@
//@ edition:2018
#![feature(c_variadic)]
async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {}
//~^ ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
fn main() {}

View file

@ -0,0 +1,13 @@
error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
--> $DIR/note-and-explain-ReVar-124973.rs:5:73
|
LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {}
| ----------------------------------------------------------------------- ^^
| |
| opaque type defined here
|
= note: hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures lifetime `'_`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0700`.

View file

@ -41,3 +41,25 @@ fn _returns_result() -> Result<(), ()> {
Ok(())
}
fn _by_ref() {
// Shared refs
for _ in &Some(1) {}
//~^ WARN for loop over a `&Option`. This is more readably written as an `if let` statement
//~| HELP to check pattern in a loop use `while let`
//~| HELP consider using `if let` to clear intent
for _ in &Ok::<_, ()>(1) {}
//~^ WARN for loop over a `&Result`. This is more readably written as an `if let` statement
//~| HELP to check pattern in a loop use `while let`
//~| HELP consider using `if let` to clear intent
// Mutable refs
for _ in &mut Some(1) {}
//~^ WARN for loop over a `&mut Option`. This is more readably written as an `if let` statement
//~| HELP to check pattern in a loop use `while let`
//~| HELP consider using `if let` to clear intent
for _ in &mut Ok::<_, ()>(1) {}
//~^ WARN for loop over a `&mut Result`. This is more readably written as an `if let` statement
//~| HELP to check pattern in a loop use `while let`
//~| HELP consider using `if let` to clear intent
}

View file

@ -97,5 +97,65 @@ help: consider using `if let` to clear intent
LL | if let Ok(_) = Ok::<_, ()>([0; 0]) {}
| ~~~~~~~~~~ ~~~
warning: 6 warnings emitted
warning: for loop over a `&Option`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:47:14
|
LL | for _ in &Some(1) {}
| ^^^^^^^^
|
help: to check pattern in a loop use `while let`
|
LL | while let Some(_) = &Some(1) {}
| ~~~~~~~~~~~~~~~ ~~~
help: consider using `if let` to clear intent
|
LL | if let Some(_) = &Some(1) {}
| ~~~~~~~~~~~~ ~~~
warning: for loop over a `&Result`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:51:14
|
LL | for _ in &Ok::<_, ()>(1) {}
| ^^^^^^^^^^^^^^^
|
help: to check pattern in a loop use `while let`
|
LL | while let Ok(_) = &Ok::<_, ()>(1) {}
| ~~~~~~~~~~~~~ ~~~
help: consider using `if let` to clear intent
|
LL | if let Ok(_) = &Ok::<_, ()>(1) {}
| ~~~~~~~~~~ ~~~
warning: for loop over a `&mut Option`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:57:14
|
LL | for _ in &mut Some(1) {}
| ^^^^^^^^^^^^
|
help: to check pattern in a loop use `while let`
|
LL | while let Some(_) = &mut Some(1) {}
| ~~~~~~~~~~~~~~~ ~~~
help: consider using `if let` to clear intent
|
LL | if let Some(_) = &mut Some(1) {}
| ~~~~~~~~~~~~ ~~~
warning: for loop over a `&mut Result`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:61:14
|
LL | for _ in &mut Ok::<_, ()>(1) {}
| ^^^^^^^^^^^^^^^^^^^
|
help: to check pattern in a loop use `while let`
|
LL | while let Ok(_) = &mut Ok::<_, ()>(1) {}
| ~~~~~~~~~~~~~ ~~~
help: consider using `if let` to clear intent
|
LL | if let Ok(_) = &mut Ok::<_, ()>(1) {}
| ~~~~~~~~~~ ~~~
warning: 10 warnings emitted

View file

@ -0,0 +1,31 @@
//@ run-rustfix
#![allow(unused)]
fn main() {
let n = 1;
let m = 2;
let x = 'block: {
if n == 0 {
break 'block 1; //~ ERROR [E0268]
} else {
break 'block 2;
}
};
let y = 'block: {
if n == 0 {
break 'block 1; //~ ERROR [E0268]
}
break 'block 0;
};
let z = 'block: {
if n == 0 {
if m > 1 {
break 'block 3; //~ ERROR [E0268]
}
}
break 'block 1;
};
}

View file

@ -0,0 +1,31 @@
//@ run-rustfix
#![allow(unused)]
fn main() {
let n = 1;
let m = 2;
let x = {
if n == 0 {
break 1; //~ ERROR [E0268]
} else {
break 2;
}
};
let y = {
if n == 0 {
break 1; //~ ERROR [E0268]
}
break 0;
};
let z = {
if n == 0 {
if m > 1 {
break 3; //~ ERROR [E0268]
}
}
break 1;
};
}

View file

@ -0,0 +1,59 @@
error[E0268]: `break` outside of a loop or labeled block
--> $DIR/loop-if-else-break-issue-123261.rs:10:13
|
LL | break 1;
| ^^^^^^^ cannot `break` outside of a loop or labeled block
LL | } else {
LL | break 2;
| ^^^^^^^ cannot `break` outside of a loop or labeled block
|
help: consider labeling this block to be able to break within it
|
LL ~ let x = 'block: {
LL | if n == 0 {
LL ~ break 'block 1;
LL | } else {
LL ~ break 'block 2;
|
error[E0268]: `break` outside of a loop or labeled block
--> $DIR/loop-if-else-break-issue-123261.rs:18:13
|
LL | break 1;
| ^^^^^^^ cannot `break` outside of a loop or labeled block
LL | }
LL | break 0;
| ^^^^^^^ cannot `break` outside of a loop or labeled block
|
help: consider labeling this block to be able to break within it
|
LL ~ let y = 'block: {
LL | if n == 0 {
LL ~ break 'block 1;
LL | }
LL ~ break 'block 0;
|
error[E0268]: `break` outside of a loop or labeled block
--> $DIR/loop-if-else-break-issue-123261.rs:26:17
|
LL | break 3;
| ^^^^^^^ cannot `break` outside of a loop or labeled block
...
LL | break 1;
| ^^^^^^^ cannot `break` outside of a loop or labeled block
|
help: consider labeling this block to be able to break within it
|
LL ~ let z = 'block: {
LL | if n == 0 {
LL | if m > 1 {
LL ~ break 'block 3;
LL | }
LL | }
LL ~ break 'block 1;
|
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0268`.

View file

@ -21,16 +21,21 @@ LL | foo!(());
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0268]: `break` outside of a loop or labeled block
--> $DIR/break-in-unlabeled-block-in-macro.rs:27:19
--> $DIR/break-in-unlabeled-block-in-macro.rs:33:17
|
LL | foo!(stmt break ());
LL | foo!(=> break ());
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
error[E0268]: `break` outside of a loop or labeled block
--> $DIR/break-in-unlabeled-block-in-macro.rs:38:17
|
help: consider labeling this block to be able to break within it
|
LL ~ 'block: {
LL ~ foo!(stmt break 'block ());
LL | break ()
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
...
LL | bar!()
| ------ in this macro invocation
|
= note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0268]: `break` outside of a loop or labeled block
--> $DIR/break-in-unlabeled-block-in-macro.rs:12:11
@ -48,21 +53,16 @@ LL | 'block: { break 'block $e; }
| +++++++ ++++++
error[E0268]: `break` outside of a loop or labeled block
--> $DIR/break-in-unlabeled-block-in-macro.rs:33:17
--> $DIR/break-in-unlabeled-block-in-macro.rs:27:19
|
LL | foo!(=> break ());
LL | foo!(stmt break ());
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
error[E0268]: `break` outside of a loop or labeled block
--> $DIR/break-in-unlabeled-block-in-macro.rs:38:17
|
LL | break ()
| ^^^^^^^^ cannot `break` outside of a loop or labeled block
...
LL | bar!()
| ------ in this macro invocation
help: consider labeling this block to be able to break within it
|
LL ~ 'block: {
LL ~ foo!(stmt break 'block ());
|
= note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 6 previous errors

View file

@ -0,0 +1,9 @@
//@ aux-crate:shared=shared.rs
extern crate shared;
pub use shared::Shared;
pub struct SharedInType {
pub f: Shared
}

View file

@ -0,0 +1,9 @@
//@ aux-crate:shared=shared.rs
extern crate shared;
pub use shared::Shared;
pub struct SharedInType {
pub f: Shared
}

View file

@ -0,0 +1,4 @@
//@ aux-crate:priv:indirect2=indirect2.rs
//@ compile-flags: -Zunstable-options
extern crate indirect2;

View file

@ -0,0 +1,4 @@
//@ aux-crate:shared=shared.rs
// This is public.
extern crate shared;

View file

@ -0,0 +1,22 @@
//@ force-host
//@ no-prefer-dynamic
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro]
pub fn fn_like(input: TokenStream) -> TokenStream {
"".parse().unwrap()
}
#[proc_macro_derive(PmDerive)]
pub fn pm_derive(item: TokenStream) -> TokenStream {
"".parse().unwrap()
}
#[proc_macro_attribute]
pub fn pm_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
"".parse().unwrap()
}

View file

@ -1,2 +1,11 @@
pub struct OtherType;
pub trait OtherTrait {}
#[macro_export]
macro_rules! m {
() => {};
}
pub enum E {
V1
}

View file

@ -0,0 +1,5 @@
//@ aux-crate:shared=shared.rs
extern crate shared;
pub use shared::Shared;

View file

@ -0,0 +1 @@
pub struct Shared;

View file

@ -0,0 +1,48 @@
//@ aux-crate:priv:diamond_priv_dep=diamond_priv_dep.rs
//@ aux-crate:diamond_pub_dep=diamond_pub_dep.rs
//@ compile-flags: -Zunstable-options
// A diamond dependency:
//
// diamond_reepxort
// /\
// (public) / \ (PRIVATE)
// / \
// diamond_pub_dep diamond_priv_dep
// \ /
// (public) \ / (public)
// \/
// shared
//
// Where the pub and private crates reexport something from the shared crate.
//
// Checks the behavior when the same shared item appears in the public API,
// depending on whether it comes from the public side or the private side.
//
// NOTE: compiletest does not support deduplicating shared dependencies.
// However, it should work well enough for this test, the only downside is
// that diamond_shared gets built twice.
#![crate_type = "lib"]
#![deny(exported_private_dependencies)]
extern crate diamond_priv_dep;
extern crate diamond_pub_dep;
// FIXME: This should trigger.
pub fn leaks_priv() -> diamond_priv_dep::Shared {
diamond_priv_dep::Shared
}
pub fn leaks_pub() -> diamond_pub_dep::Shared {
diamond_pub_dep::Shared
}
pub struct PrivInStruct {
pub f: diamond_priv_dep::SharedInType
//~^ ERROR type `diamond_priv_dep::SharedInType` from private dependency 'diamond_priv_dep' in public interface
}
pub struct PubInStruct {
pub f: diamond_pub_dep::SharedInType
}

View file

@ -0,0 +1,14 @@
error: type `diamond_priv_dep::SharedInType` from private dependency 'diamond_priv_dep' in public interface
--> $DIR/diamond_deps.rs:42:5
|
LL | pub f: diamond_priv_dep::SharedInType
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/diamond_deps.rs:27:9
|
LL | #![deny(exported_private_dependencies)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -1,12 +1,20 @@
//@ aux-crate:priv:priv_dep=priv_dep.rs
//@ aux-build:pub_dep.rs
//@ aux-crate:priv:pm=pm.rs
//@ compile-flags: -Zunstable-options
// Basic behavior check of exported_private_dependencies from either a public
// dependency or a private one.
#![deny(exported_private_dependencies)]
// This crate is a private dependency
extern crate priv_dep;
// FIXME: This should trigger.
pub extern crate priv_dep;
// This crate is a public dependency
extern crate pub_dep;
// This crate is a private dependency
extern crate pm;
use priv_dep::{OtherTrait, OtherType};
use pub_dep::PubType;
@ -25,7 +33,10 @@ pub struct PublicType {
}
impl PublicType {
pub fn pub_fn(param: OtherType) {}
pub fn pub_fn_param(param: OtherType) {}
//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
pub fn pub_fn_return() -> OtherType { OtherType }
//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
fn priv_fn(param: OtherType) {}
@ -36,9 +47,61 @@ pub trait MyPubTrait {
}
//~^^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
pub trait WithSuperTrait: OtherTrait {}
//~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
pub trait PubLocalTraitWithAssoc {
type X;
}
pub struct PrivateAssoc;
impl PubLocalTraitWithAssoc for PrivateAssoc {
type X = OtherType;
//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
}
pub fn in_bounds<T: OtherTrait>(x: T) { unimplemented!() }
//~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
pub fn private_in_generic() -> std::num::Saturating<OtherType> { unimplemented!() }
//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
pub static STATIC: OtherType = OtherType;
//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
pub const CONST: OtherType = OtherType;
//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
pub type Alias = OtherType;
//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
pub struct PublicWithPrivateImpl;
// FIXME: This should trigger.
// See https://github.com/rust-lang/rust/issues/71043
impl OtherTrait for PublicWithPrivateImpl {}
pub trait PubTraitOnPrivate {}
// FIXME: This should trigger.
// See https://github.com/rust-lang/rust/issues/71043
impl PubTraitOnPrivate for OtherType {}
pub struct AllowedPrivType {
#[allow(exported_private_dependencies)]
pub allowed: OtherType,
}
// FIXME: This should trigger.
pub use priv_dep::m;
// FIXME: This should trigger.
pub use pm::fn_like;
// FIXME: This should trigger.
pub use pm::PmDerive;
// FIXME: This should trigger.
pub use pm::pm_attr;
// FIXME: This should trigger.
pub use priv_dep::E::V1;
fn main() {}

View file

@ -1,26 +1,74 @@
error: type `OtherType` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:21:5
--> $DIR/pub-priv1.rs:29:5
|
LL | pub field: OtherType,
| ^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/pub-priv1.rs:4:9
--> $DIR/pub-priv1.rs:9:9
|
LL | #![deny(exported_private_dependencies)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: type `OtherType` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:28:5
--> $DIR/pub-priv1.rs:36:5
|
LL | pub fn pub_fn(param: OtherType) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | pub fn pub_fn_param(param: OtherType) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: type `OtherType` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:39:5
|
LL | pub fn pub_fn_return() -> OtherType { OtherType }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:35:5
--> $DIR/pub-priv1.rs:46:5
|
LL | type Foo: OtherTrait;
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:50:1
|
LL | pub trait WithSuperTrait: OtherTrait {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: type `OtherType` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:59:5
|
LL | type X = OtherType;
| ^^^^^^
error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:63:1
|
LL | pub fn in_bounds<T: OtherTrait>(x: T) { unimplemented!() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: type `OtherType` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:66:1
|
LL | pub fn private_in_generic() -> std::num::Saturating<OtherType> { unimplemented!() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: type `OtherType` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:69:1
|
LL | pub static STATIC: OtherType = OtherType;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: type `OtherType` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:72:1
|
LL | pub const CONST: OtherType = OtherType;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: type `OtherType` from private dependency 'priv_dep' in public interface
--> $DIR/pub-priv1.rs:75:1
|
LL | pub type Alias = OtherType;
| ^^^^^^^^^^^^^^
error: aborting due to 11 previous errors

View file

@ -0,0 +1,15 @@
//@ aux-crate:priv:reexport=reexport.rs
//@ compile-flags: -Zunstable-options
//@ check-pass
// Checks the behavior of a reexported item from a private dependency.
#![crate_type = "lib"]
#![deny(exported_private_dependencies)]
extern crate reexport;
// FIXME: This should trigger.
pub fn leaks_priv() -> reexport::Shared {
reexport::Shared
}

View file

@ -0,0 +1,32 @@
//@ aux-crate:priv:shared=shared.rs
//@ aux-crate:reexport=reexport.rs
//@ compile-flags: -Zunstable-options
//@ check-pass
// A shared dependency, where a private dependency reexports a public dependency.
//
// shared_both_private
// /\
// (PRIVATE) / | (PRIVATE)
// / |
// reexport |
// \ |
// (public) \ /
// \/
// shared
#![crate_type = "lib"]
#![deny(exported_private_dependencies)]
extern crate shared;
extern crate reexport;
// FIXME: This should trigger.
pub fn leaks_priv() -> shared::Shared {
shared::Shared
}
// FIXME: This should trigger.
pub fn leaks_priv_reexport() -> reexport::Shared {
reexport::Shared
}

View file

@ -0,0 +1,39 @@
//@ aux-crate:priv:shared=shared.rs
//@ aux-crate:reexport=reexport.rs
//@ compile-flags: -Zunstable-options
//@ check-pass
// A shared dependency, where the public side reexports the same item as a
// direct private dependency.
//
// shared_direct_private
// /\
// (public) / | (PRIVATE)
// / |
// reexport |
// \ |
// (public) \ /
// \/
// shared
//
#![crate_type = "lib"]
#![deny(exported_private_dependencies)]
extern crate shared;
extern crate reexport;
// FIXME: Should this trigger?
//
// One could make an argument that I said I want "reexport" to be public, and
// since "reexport" says "shared_direct_private" is public, then it should
// transitively be public for me. However, as written, this is explicitly
// referring to a dependency that is marked "private", which I think is
// confusing.
pub fn leaks_priv() -> shared::Shared {
shared::Shared
}
pub fn leaks_pub() -> reexport::Shared {
reexport::Shared
}

View file

@ -0,0 +1,29 @@
//@ aux-crate:priv:shared=shared.rs
//@ aux-crate:priv:indirect1=indirect1.rs
//@ compile-flags: -Zunstable-options
//@ check-pass
// A shared dependency, where it is only indirectly public.
//
// shared_indirect
// /\
// (PRIVATE) / | (PRIVATE)
// / |
// indirect1 | |
// (PRIVATE) | |
// indirect2 | |
// \ |
// (public) \ /
// \/
// shared
#![crate_type = "lib"]
#![deny(exported_private_dependencies)]
extern crate shared;
extern crate indirect1;
// FIXME: This should trigger.
pub fn leaks_priv() -> shared::Shared {
shared::Shared
}