version = "0.0.0"
dependencies = [
"rustc_lexer",
- "rustc_span",
]
[[package]]
None
}
+ /// If the given expression is a path to a unit struct, returns that path.
+ /// It is not a complete check, but just tries to reject most paths early
+ /// if they are not unit structs.
+ /// Type checking will take care of the full validation later.
+ fn extract_unit_struct_path<'a>(
+ &mut self,
+ expr: &'a Expr,
+ ) -> Option<(&'a Option<QSelf>, &'a Path)> {
+ if let ExprKind::Path(qself, path) = &expr.kind {
+ // Does the path resolve to something disallowed in a unit struct/variant pattern?
+ if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
+ if partial_res.unresolved_segments() == 0
+ && !partial_res.base_res().expected_in_unit_struct_pat()
+ {
+ return None;
+ }
+ }
+ return Some((qself, path));
+ }
+ None
+ }
+
/// Convert the LHS of a destructuring assignment to a pattern.
/// Each sub-assignment is recorded in `assignments`.
fn destructure_assign(
return self.pat_without_dbm(lhs.span, tuple_struct_pat);
}
}
+ // Unit structs and enum variants.
+ ExprKind::Path(..) => {
+ if let Some((qself, path)) = self.extract_unit_struct_path(lhs) {
+ let qpath = self.lower_qpath(
+ lhs.id,
+ qself,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ // Destructure like a unit struct.
+ let unit_struct_pat = hir::PatKind::Path(qpath);
+ return self.pat_without_dbm(lhs.span, unit_struct_pat);
+ }
+ }
// Structs.
ExprKind::Struct(se) => {
let field_pats = self.arena.alloc_from_iter(se.fields.iter().map(|f| {
if !parser.errors.is_empty() {
let err = parser.errors.remove(0);
- let err_sp = template_span.from_inner(err.span);
+ let err_sp = template_span.from_inner(InnerSpan::new(err.span.start, err.span.end));
let msg = &format!("invalid asm template string: {}", err.description);
let mut e = ecx.struct_span_err(err_sp, msg);
e.span_label(err_sp, err.label + " in asm template string");
e.note(¬e);
}
if let Some((label, span)) = err.secondary_label {
- let err_sp = template_span.from_inner(span);
+ let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end));
e.span_label(err_sp, label);
}
e.emit();
curarg = parser.curarg;
- let mut arg_spans = parser.arg_places.iter().map(|span| template_span.from_inner(*span));
+ let mut arg_spans = parser
+ .arg_places
+ .iter()
+ .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end)));
for piece in unverified_pieces {
match piece {
parse::Piece::String(s) => {
Some(idx)
}
}
- parse::ArgumentNamed(name, span) => match args.named_args.get(&name) {
- Some(&idx) => Some(idx),
- None => {
- let msg = format!("there is no argument named `{}`", name);
- ecx.struct_span_err(template_span.from_inner(span), &msg).emit();
- None
+ parse::ArgumentNamed(name, span) => {
+ match args.named_args.get(&Symbol::intern(name)) {
+ Some(&idx) => Some(idx),
+ None => {
+ let msg = format!("there is no argument named `{}`", name);
+ ecx.struct_span_err(
+ template_span
+ .from_inner(InnerSpan::new(span.start, span.end)),
+ &msg,
+ )
+ .emit();
+ None
+ }
}
- },
+ }
};
let mut chars = arg.format.ty.chars();
let span = arg
.format
.ty_span
- .map(|sp| template_sp.from_inner(sp))
+ .map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end)))
.unwrap_or(template_sp);
ecx.struct_span_err(
span,
let template_num_lines = 1 + template_str.matches('\n').count();
line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
} else {
- line_spans.extend(parser.line_spans.iter().map(|span| template_span.from_inner(*span)));
+ line_spans.extend(
+ parser
+ .line_spans
+ .iter()
+ .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))),
+ );
};
}
fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) {
// NOTE: the `unwrap_or` branch is needed in case of invalid format
// arguments, e.g., `format_args!("{foo}")`.
- let lookup = |s: Symbol| *self.names.get(&s).unwrap_or(&0);
+ let lookup = |s: &str| *self.names.get(&Symbol::intern(s)).unwrap_or(&0);
match *p {
parse::String(_) => {}
// it's written second, so it should come after width/precision.
let pos = match arg.position {
parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i),
- parse::ArgumentNamed(s, span) => Named(s, span),
+ parse::ArgumentNamed(s, span) => {
+ Named(Symbol::intern(s), InnerSpan::new(span.start, span.end))
+ }
};
let ty = Placeholder(match arg.format.ty {
"X" => "UpperHex",
_ => {
let fmtsp = self.fmtsp;
- let sp = arg.format.ty_span.map(|sp| fmtsp.from_inner(sp));
+ let sp = arg
+ .format
+ .ty_span
+ .map(|sp| fmtsp.from_inner(InnerSpan::new(sp.start, sp.end)));
let mut err = self.ecx.struct_span_err(
sp.unwrap_or(fmtsp),
&format!("unknown format trait `{}`", arg.format.ty),
}
}
- fn verify_count(&mut self, c: parse::Count) {
+ fn verify_count(&mut self, c: parse::Count<'_>) {
match c {
parse::CountImplied | parse::CountIs(..) => {}
parse::CountIsParam(i) => {
self.verify_arg_type(Exact(i), Count);
}
parse::CountIsName(s, span) => {
- self.verify_arg_type(Named(s, span), Count);
+ self.verify_arg_type(
+ Named(Symbol::intern(s), InnerSpan::new(span.start, span.end)),
+ Count,
+ );
}
}
}
for fmt in &self.arg_with_formatting {
if let Some(span) = fmt.precision_span {
- let span = self.fmtsp.from_inner(span);
+ let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end));
match fmt.precision {
parse::CountIsParam(pos) if pos > self.num_args() => {
e.span_label(
}
}
if let Some(span) = fmt.width_span {
- let span = self.fmtsp.from_inner(span);
+ let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end));
match fmt.width {
parse::CountIsParam(pos) if pos > self.num_args() => {
e.span_label(
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, s])
}
- fn build_count(&self, c: parse::Count) -> P<ast::Expr> {
+ fn build_count(&self, c: parse::Count<'_>) -> P<ast::Expr> {
let sp = self.macsp;
let count = |c, arg| {
let mut path = Context::rtpath(self.ecx, sym::Count);
if !parser.errors.is_empty() {
let err = parser.errors.remove(0);
let sp = if efmt_kind_is_lit {
- fmt_span.from_inner(err.span)
+ fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end))
} else {
// The format string could be another macro invocation, e.g.:
// format!(concat!("abc", "{}"), 4);
}
if let Some((label, span)) = err.secondary_label {
if efmt_kind_is_lit {
- e.span_label(fmt_span.from_inner(span), label);
+ e.span_label(fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label);
}
}
e.emit();
return DummyResult::raw_expr(sp, true);
}
- let arg_spans = parser.arg_places.iter().map(|span| fmt_span.from_inner(*span)).collect();
+ let arg_spans = parser
+ .arg_places
+ .iter()
+ .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end)))
+ .collect();
let named_pos: FxHashSet<usize> = names.values().cloned().collect();
pub fn expected_in_tuple_struct_pat(&self) -> bool {
matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..))
}
+
+ /// Returns whether such a resolved path can occur in a unit struct/variant pattern
+ pub fn expected_in_unit_struct_pat(&self) -> bool {
+ matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Const), _) | Res::SelfCtor(..))
+ }
}
if n_arguments > 0 && fmt_parser.errors.is_empty() {
let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] {
[] => vec![fmt_span],
- v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(),
+ v => v
+ .iter()
+ .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end)))
+ .collect(),
};
cx.struct_span_lint(NON_FMT_PANICS, arg_spans, |lint| {
let mut l = lint.build(match n_arguments {
edition = "2021"
[dependencies]
-rustc_span = { path = "../rustc_span" }
rustc_lexer = { path = "../rustc_lexer" }
html_playground_url = "https://play.rust-lang.org/",
test(attr(deny(warnings)))
)]
-#![feature(nll)]
-#![feature(bool_to_option)]
+// We want to be able to build this crate with a stable compiler, so no
+// `#![feature]` attributes should be added.
pub use Alignment::*;
pub use Count::*;
use std::str;
use std::string;
-use rustc_span::{InnerSpan, Symbol};
+// Note: copied from rustc_span
+/// Range inside of a `Span` used for diagnostics when we only have access to relative positions.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct InnerSpan {
+ pub start: usize,
+ pub end: usize,
+}
+
+impl InnerSpan {
+ pub fn new(start: usize, end: usize) -> InnerSpan {
+ InnerSpan { start, end }
+ }
+}
/// The type of format string that we are parsing.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Argument<'a> {
/// Where to find this argument
- pub position: Position,
+ pub position: Position<'a>,
/// How to format the argument
pub format: FormatSpec<'a>,
}
/// Packed version of various flags provided.
pub flags: u32,
/// The integer precision to use.
- pub precision: Count,
+ pub precision: Count<'a>,
/// The span of the precision formatting flag (for diagnostics).
pub precision_span: Option<InnerSpan>,
/// The string width requested for the resulting format.
- pub width: Count,
+ pub width: Count<'a>,
/// The span of the width formatting flag (for diagnostics).
pub width_span: Option<InnerSpan>,
/// The descriptor string representing the name of the format desired for
/// Enum describing where an argument for a format can be located.
#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Position {
+pub enum Position<'a> {
/// The argument is implied to be located at an index
ArgumentImplicitlyIs(usize),
/// The argument is located at a specific index given in the format
ArgumentIs(usize),
/// The argument has a name.
- ArgumentNamed(Symbol, InnerSpan),
+ ArgumentNamed(&'a str, InnerSpan),
}
-impl Position {
+impl Position<'_> {
pub fn index(&self) -> Option<usize> {
match self {
ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i),
/// A count is used for the precision and width parameters of an integer, and
/// can reference either an argument or a literal integer.
#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Count {
+pub enum Count<'a> {
/// The count is specified explicitly.
CountIs(usize),
/// The count is specified by the argument with the given name.
- CountIsName(Symbol, InnerSpan),
+ CountIsName(&'a str, InnerSpan),
/// The count is specified by the argument at the given index.
CountIsParam(usize),
/// The count is implied and cannot be explicitly specified.
/// integer index of an argument, a named argument, or a blank string.
/// Returns `Some(parsed_position)` if the position is not implicitly
/// consuming a macro argument, `None` if it's the case.
- fn position(&mut self) -> Option<Position> {
+ fn position(&mut self) -> Option<Position<'a>> {
if let Some(i) = self.integer() {
Some(ArgumentIs(i))
} else {
let word = self.word();
let end = start + word.len();
let span = self.to_span_index(start).to(self.to_span_index(end));
- Some(ArgumentNamed(Symbol::intern(word), span))
+ Some(ArgumentNamed(word, span))
}
// This is an `ArgumentNext`.
/// Parses a `Count` parameter at the current position. This does not check
/// for 'CountIsNextParam' because that is only used in precision, not
/// width.
- fn count(&mut self, start: usize) -> (Count, Option<InnerSpan>) {
+ fn count(&mut self, start: usize) -> (Count<'a>, Option<InnerSpan>) {
if let Some(i) = self.integer() {
if let Some(end) = self.consume_pos('$') {
let span = self.to_span_index(start).to(self.to_span_index(end + 1));
(CountImplied, None)
} else if let Some(end) = self.consume_pos('$') {
let span = self.to_span_index(start + 1).to(self.to_span_index(end));
- (CountIsName(Symbol::intern(word), span), None)
+ (CountIsName(word, span), None)
} else {
self.cur = tmp;
(CountImplied, None)
break;
}
}
- found.then_some(cur)
+ if found { Some(cur) } else { None }
}
}
}
#[test]
fn format_counts() {
- rustc_span::create_default_session_globals_then(|| {
- same(
- "{:10x}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(0),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountImplied,
- width: CountIs(10),
- precision_span: None,
- width_span: None,
- ty: "x",
- ty_span: None,
- },
- })],
- );
- same(
- "{:10$.10x}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(0),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountIs(10),
- width: CountIsParam(10),
- precision_span: None,
- width_span: Some(InnerSpan::new(3, 6)),
- ty: "x",
- ty_span: None,
- },
- })],
- );
- same(
- "{:.*x}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(1),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountIsParam(0),
- width: CountImplied,
- precision_span: Some(InnerSpan::new(3, 5)),
- width_span: None,
- ty: "x",
- ty_span: None,
- },
- })],
- );
- same(
- "{:.10$x}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(0),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountIsParam(10),
- width: CountImplied,
- precision_span: Some(InnerSpan::new(3, 7)),
- width_span: None,
- ty: "x",
- ty_span: None,
- },
- })],
- );
- same(
- "{:a$.b$?}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(0),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountIsName(Symbol::intern("b"), InnerSpan::new(6, 7)),
- width: CountIsName(Symbol::intern("a"), InnerSpan::new(4, 4)),
- precision_span: None,
- width_span: None,
- ty: "?",
- ty_span: None,
- },
- })],
- );
- });
+ same(
+ "{:10x}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(0),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountImplied,
+ width: CountIs(10),
+ precision_span: None,
+ width_span: None,
+ ty: "x",
+ ty_span: None,
+ },
+ })],
+ );
+ same(
+ "{:10$.10x}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(0),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountIs(10),
+ width: CountIsParam(10),
+ precision_span: None,
+ width_span: Some(InnerSpan::new(3, 6)),
+ ty: "x",
+ ty_span: None,
+ },
+ })],
+ );
+ same(
+ "{:.*x}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(1),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountIsParam(0),
+ width: CountImplied,
+ precision_span: Some(InnerSpan::new(3, 5)),
+ width_span: None,
+ ty: "x",
+ ty_span: None,
+ },
+ })],
+ );
+ same(
+ "{:.10$x}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(0),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountIsParam(10),
+ width: CountImplied,
+ precision_span: Some(InnerSpan::new(3, 7)),
+ width_span: None,
+ ty: "x",
+ ty_span: None,
+ },
+ })],
+ );
+ same(
+ "{:a$.b$?}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(0),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountIsName("b", InnerSpan::new(6, 7)),
+ width: CountIsName("a", InnerSpan::new(4, 4)),
+ precision_span: None,
+ width_span: None,
+ ty: "?",
+ ty_span: None,
+ },
+ })],
+ );
}
#[test]
fn format_flags() {
}
fn report_with_use_injections(&mut self, krate: &Crate) {
- for UseError { mut err, candidates, def_id, instead, suggestion } in
+ for UseError { mut err, candidates, def_id, instead, suggestion, path } in
self.use_injections.drain(..)
{
let (span, found_use) = if let Some(def_id) = def_id.as_local() {
if instead { Instead::Yes } else { Instead::No },
found_use,
IsPattern::No,
+ path,
);
} else if let Some((span, msg, sugg, appl)) = suggestion {
err.span_suggestion(span, msg, sugg, appl);
Instead::No,
FoundUse::Yes,
IsPattern::Yes,
+ vec![],
);
}
err
Instead::No,
FoundUse::Yes,
IsPattern::No,
+ vec![],
);
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
instead: Instead,
found_use: FoundUse,
is_pattern: IsPattern,
+ path: Vec<Segment>,
) {
if candidates.is_empty() {
return;
accessible_path_strings.into_iter().map(|a| a.0),
Applicability::MaybeIncorrect,
);
+ if let [first, .., last] = &path[..] {
+ err.span_suggestion_verbose(
+ first.ident.span.until(last.ident.span),
+ &format!("if you import `{}`, refer to it directly", last.ident),
+ String::new(),
+ Applicability::Unspecified,
+ );
+ }
} else {
msg.push(':');
) | Res::Local(..)
| Res::SelfCtor(..)
),
- PathSource::Pat => matches!(
- res,
- Res::Def(
- DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst,
- _,
- ) | Res::SelfCtor(..)
- ),
+ PathSource::Pat => {
+ res.expected_in_unit_struct_pat()
+ || matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _))
+ }
PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(),
PathSource::Struct => matches!(
res,
def_id,
instead,
suggestion,
+ path: path.into(),
});
}
def_id,
instead: false,
suggestion: None,
+ path: path.into(),
});
} else {
err.cancel();
instead: bool,
/// Extra free-form suggestion.
suggestion: Option<(Span, &'static str, String, Applicability)>,
+ /// Path `Segment`s at the place of use that failed. Used for accurate suggestion after telling
+ /// the user to import the item directly.
+ path: Vec<Segment>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
return false;
}
- let orig_ty = old_pred.self_ty().skip_binder();
+ // This is a quick fix to resolve an ICE (#96223).
+ // This change should probably be deeper.
+ // As suggested by @jackh726, `mk_trait_obligation_with_new_self_ty` could take a `Binder<(TraitRef, Ty)>
+ // instead of `Binder<Ty>` leading to some changes to its call places.
+ let Some(orig_ty) = old_pred.self_ty().no_bound_vars() else {
+ return false;
+ };
let mk_result = |new_ty| {
let obligation =
self.mk_trait_obligation_with_new_self_ty(param_env, old_pred, new_ty);
match token {
Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position {
- // `{Self}` is allowed
- Position::ArgumentNamed(s, _) if s == kw::SelfUpper => (),
- // `{ThisTraitsName}` is allowed
- Position::ArgumentNamed(s, _) if s == trait_name => (),
- // `{from_method}` is allowed
- Position::ArgumentNamed(s, _) if s == sym::from_method => (),
- // `{from_desugaring}` is allowed
- Position::ArgumentNamed(s, _) if s == sym::from_desugaring => (),
- // `{ItemContext}` is allowed
- Position::ArgumentNamed(s, _) if s == sym::ItemContext => (),
- // `{integral}` and `{integer}` and `{float}` are allowed
- Position::ArgumentNamed(s, _)
- if s == sym::integral || s == sym::integer_ || s == sym::float =>
- {
- ()
- }
- // So is `{A}` if A is a type parameter
Position::ArgumentNamed(s, _) => {
- match generics.params.iter().find(|param| param.name == s) {
- Some(_) => (),
- None => {
- let reported = struct_span_err!(
- tcx.sess,
- span,
- E0230,
- "there is no parameter `{}` on {}",
- s,
- if trait_def_id == item_def_id {
- format!("trait `{}`", trait_name)
- } else {
- "impl".to_string()
- }
- )
- .emit();
- result = Err(reported);
- }
+ match Symbol::intern(s) {
+ // `{Self}` is allowed
+ kw::SelfUpper => (),
+ // `{ThisTraitsName}` is allowed
+ s if s == trait_name => (),
+ // `{from_method}` is allowed
+ sym::from_method => (),
+ // `{from_desugaring}` is allowed
+ sym::from_desugaring => (),
+ // `{ItemContext}` is allowed
+ sym::ItemContext => (),
+ // `{integral}` and `{integer}` and `{float}` are allowed
+ sym::integral | sym::integer_ | sym::float => (),
+ // So is `{A}` if A is a type parameter
+ s => match generics.params.iter().find(|param| param.name == s) {
+ Some(_) => (),
+ None => {
+ let reported = struct_span_err!(
+ tcx.sess,
+ span,
+ E0230,
+ "there is no parameter `{}` on {}",
+ s,
+ if trait_def_id == item_def_id {
+ format!("trait `{}`", trait_name)
+ } else {
+ "impl".to_string()
+ }
+ )
+ .emit();
+ result = Err(reported);
+ }
+ },
}
}
// `{:1}` and `{}` are not to be used
.map(|p| match p {
Piece::String(s) => s,
Piece::NextArgument(a) => match a.position {
- Position::ArgumentNamed(s, _) => match generic_map.get(&s) {
- Some(val) => val,
- None if s == name => &trait_str,
- None => {
- if let Some(val) = options.get(&s) {
- val
- } else if s == sym::from_desugaring || s == sym::from_method {
- // don't break messages using these two arguments incorrectly
- &empty_string
- } else if s == sym::ItemContext {
- &item_context
- } else if s == sym::integral {
- "{integral}"
- } else if s == sym::integer_ {
- "{integer}"
- } else if s == sym::float {
- "{float}"
- } else {
- bug!(
- "broken on_unimplemented {:?} for {:?}: \
+ Position::ArgumentNamed(s, _) => {
+ let s = Symbol::intern(s);
+ match generic_map.get(&s) {
+ Some(val) => val,
+ None if s == name => &trait_str,
+ None => {
+ if let Some(val) = options.get(&s) {
+ val
+ } else if s == sym::from_desugaring || s == sym::from_method {
+ // don't break messages using these two arguments incorrectly
+ &empty_string
+ } else if s == sym::ItemContext {
+ &item_context
+ } else if s == sym::integral {
+ "{integral}"
+ } else if s == sym::integer_ {
+ "{integer}"
+ } else if s == sym::float {
+ "{float}"
+ } else {
+ bug!(
+ "broken on_unimplemented {:?} for {:?}: \
no argument matching {:?}",
- self.0,
- trait_ref,
- s
- )
+ self.0,
+ trait_ref,
+ s
+ )
+ }
}
}
- },
+ }
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
},
})
// FIXME(davidtwco): #[cfg(not(bootstrap))] - #95612 needs to be in the bootstrap compiler
// for this conditional to be removed.
if !target.contains("windows") || compiler.stage >= 1 {
- if target.contains("linux") || target.contains("windows") {
+ if target.contains("linux") || target.contains("windows") || target.contains("openbsd")
+ {
rustflags.arg("-Zunstable-options");
}
match self.config.rust_split_debuginfo {
'force-warn' does for 'warn'. It's the same as 'deny' in that a lint at this
level will produce an error, but unlike the 'deny' level, the 'forbid' level
can not be overridden to be anything lower than an error. However, lint
-levels may still be capped with `--cap-lints` (see below) so `rustc --cap-
-lints warn` will make lints set to 'forbid' just
-warn.
+levels may still be capped with `--cap-lints` (see below) so `rustc --cap-lints warn`
+will make lints set to 'forbid' just warn.
## Configuring warning levels
if let Res::Def(DefKind::Mod, ..) = child.res {
self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope
}
- // Traits are processed in `add_extern_traits_in_scope`.
+ // `DefKind::Trait`s are processed in `process_extern_impls`.
if let Res::Def(DefKind::Mod | DefKind::Enum, ..) = child.res {
self.process_module_children_or_reexports(def_id);
}
+ if let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Variant, _) =
+ child.res
+ {
+ let field_def_ids = Vec::from_iter(
+ self.resolver
+ .cstore()
+ .associated_item_def_ids_untracked(def_id, self.sess),
+ );
+ for field_def_id in field_def_ids {
+ self.resolve_doc_links_extern_outer(field_def_id, scope_id);
+ }
+ }
}
}
}
// check-pass
// compile-flags:--test
+// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
// This test ensures that no code block is detected in the doc comments.
running 0 tests
-test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
--- /dev/null
+// Traits in scope are collected for doc links in field attributes.
+
+// check-pass
+// aux-build: assoc-field-dep.rs
+
+extern crate assoc_field_dep;
+pub use assoc_field_dep::*;
+
+#[derive(Clone)]
+pub struct Struct;
+
+pub mod mod1 {
+ pub struct Fields {
+ /// [crate::Struct::clone]
+ pub field: u8,
+ }
+}
+
+pub mod mod2 {
+ pub enum Fields {
+ V {
+ /// [crate::Struct::clone]
+ field: u8,
+ },
+ }
+}
--- /dev/null
+#[derive(Clone)]
+pub struct Struct;
+
+pub mod dep_mod1 {
+ pub struct Fields {
+ /// [crate::Struct::clone]
+ pub field: u8,
+ }
+}
+
+pub mod dep_mod2 {
+ pub enum Fields {
+ V {
+ /// [crate::Struct::clone]
+ field: u8,
+ },
+ }
+}
--- /dev/null
+// check-pass
+
+struct S;
+
+enum E {
+ V,
+}
+
+type A = E;
+
+fn main() {
+ let mut a;
+
+ (S, a) = (S, ());
+
+ (E::V, a) = (E::V, ());
+
+ (<E>::V, a) = (E::V, ());
+ (A::V, a) = (E::V, ());
+}
+
+impl S {
+ fn check() {
+ let a;
+ (Self, a) = (S, ());
+ }
+}
+
+impl E {
+ fn check() {
+ let a;
+ (Self::V, a) = (E::V, ());
+ }
+}
|
LL | use b::bar;
|
+help: if you import `bar`, refer to it directly
+ |
+LL - a::bar();
+LL + bar();
+ |
error: aborting due to previous error
|
LL | use xm2::S;
|
+help: if you import `S`, refer to it directly
+ |
+LL - check(m1::S);
+LL + check(S);
+ |
error[E0423]: expected value, found type alias `xm1::S`
--> $DIR/namespace-mix.rs:40:11
|
LL | use xm2::S;
|
+help: if you import `S`, refer to it directly
+ |
+LL - check(xm1::S);
+LL + check(S);
+ |
error[E0423]: expected value, found struct variant `m7::V`
--> $DIR/namespace-mix.rs:100:11
|
LL | use xm8::V;
|
+help: if you import `V`, refer to it directly
+ |
+LL - check(m7::V);
+LL + check(V);
+ |
error[E0423]: expected value, found struct variant `xm7::V`
--> $DIR/namespace-mix.rs:106:11
|
LL | use xm8::V;
|
+help: if you import `V`, refer to it directly
+ |
+LL - check(xm7::V);
+LL + check(V);
+ |
error[E0277]: the trait bound `c::Item: Impossible` is not satisfied
--> $DIR/namespace-mix.rs:33:11
|
LL | use hi_str;
|
+help: if you import `hi_str`, refer to it directly
+ |
+LL - println!("{}", circular_modules_main::hi_str());
+LL + println!("{}", hi_str());
+ |
error: aborting due to 2 previous errors
|
LL | use namespaced_enums::Foo::A;
|
+help: if you import `A`, refer to it directly
+ |
+LL - let _ = namespaced_enums::A;
+LL + let _ = A;
+ |
error[E0425]: cannot find function, tuple struct or tuple variant `B` in crate `namespaced_enums`
--> $DIR/enums-are-namespaced-xc.rs:7:31
|
LL | use namespaced_enums::Foo::B;
|
+help: if you import `B`, refer to it directly
+ |
+LL - let _ = namespaced_enums::B(10);
+LL + let _ = B(10);
+ |
error[E0422]: cannot find struct, variant or union type `C` in crate `namespaced_enums`
--> $DIR/enums-are-namespaced-xc.rs:9:31
|
LL | use namespaced_enums::Foo::C;
|
+help: if you import `C`, refer to it directly
+ |
+LL - let _ = namespaced_enums::C { a: 10 };
+LL + let _ = C { a: 10 };
+ |
error: aborting due to 3 previous errors
|
LL | use std::f64::consts::LOG10_2;
|
+help: if you import `LOG10_2`, refer to it directly
+ |
+LL - const M: usize = (f64::from(N) * std::f64::LOG10_2) as usize;
+LL + const M: usize = (f64::from(N) * LOG10_2) as usize;
+ |
error: aborting due to previous error
|
LL | use std::collections::HashMap;
|
+help: if you import `HashMap`, refer to it directly
+ |
+LL - let _map = std::hahmap::HashMap::new();
+LL + let _map = HashMap::new();
+ |
error: aborting due to previous error
|
LL | use std::f64::consts::E;
|
+help: if you import `E`, refer to it directly
+ |
+LL - let _: E = m::E;
+LL + let _: E = E;
+ |
error[E0423]: expected value, found struct variant `m::E::Struct`
--> $DIR/privacy-enum-ctor.rs:45:16
|
LL | use std::primitive::u8;
|
+help: if you import `u8`, refer to it directly
+ |
+LL - let _: ::u8;
+LL + let _: u8;
+ |
error[E0061]: this function takes 0 arguments but 1 argument was supplied
--> $DIR/resolve-primitive-fallback.rs:3:5
--- /dev/null
+// Previously ICEd because we didn't properly track binders in suggestions
+// check-fail
+
+pub trait Foo<'de>: Sized {}
+
+pub trait Bar<'a>: 'static {
+ type Inner: 'a;
+}
+
+pub trait Fubar {
+ type Bar: for<'a> Bar<'a>;
+}
+
+pub struct Baz<T>(pub T);
+
+impl<'de, T> Foo<'de> for Baz<T> where T: Foo<'de> {}
+
+struct Empty;
+
+impl<M> Dummy<M> for Empty
+where
+ M: Fubar,
+ for<'de> Baz<<M::Bar as Bar<'de>>::Inner>: Foo<'de>,
+{
+}
+
+pub trait Dummy<M>
+where
+ M: Fubar,
+{
+}
+
+pub struct EmptyBis<'a>(&'a [u8]);
+
+impl<'a> Bar<'a> for EmptyBis<'static> {
+ type Inner = EmptyBis<'a>;
+}
+
+pub struct EmptyMarker;
+
+impl Fubar for EmptyMarker {
+ type Bar = EmptyBis<'static>;
+}
+
+fn icey_bounds<D: Dummy<EmptyMarker>>(p: &D) {}
+
+fn trigger_ice() {
+ let p = Empty;
+ icey_bounds(&p); //~ERROR the trait bound
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `for<'de> EmptyBis<'de>: Foo<'_>` is not satisfied
+ --> $DIR/issue-96223.rs:49:17
+ |
+LL | icey_bounds(&p);
+ | ----------- ^^ the trait `for<'de> Foo<'_>` is not implemented for `EmptyBis<'de>`
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Foo<'de>` is implemented for `Baz<T>`
+note: required because of the requirements on the impl of `for<'de> Foo<'de>` for `Baz<EmptyBis<'de>>`
+ --> $DIR/issue-96223.rs:16:14
+ |
+LL | impl<'de, T> Foo<'de> for Baz<T> where T: Foo<'de> {}
+ | ^^^^^^^^ ^^^^^^
+note: required because of the requirements on the impl of `Dummy<EmptyMarker>` for `Empty`
+ --> $DIR/issue-96223.rs:20:9
+ |
+LL | impl<M> Dummy<M> for Empty
+ | ^^^^^^^^ ^^^^^
+note: required by a bound in `icey_bounds`
+ --> $DIR/issue-96223.rs:45:19
+ |
+LL | fn icey_bounds<D: Dummy<EmptyMarker>>(p: &D) {}
+ | ^^^^^^^^^^^^^^^^^^ required by this bound in `icey_bounds`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// run-rustfix
+#![allow(non_snake_case)]
+mod A {
+ pub trait Trait {}
+ impl Trait for i32 {}
+}
+
+mod B {
+ use A::Trait;
+
+pub struct A<H: Trait>(pub H); //~ ERROR cannot find trait
+}
+
+fn main() {
+ let _ = B::A(42);
+}
--- /dev/null
+// run-rustfix
+#![allow(non_snake_case)]
+mod A {
+ pub trait Trait {}
+ impl Trait for i32 {}
+}
+
+mod B {
+ pub struct A<H: A::Trait>(pub H); //~ ERROR cannot find trait
+}
+
+fn main() {
+ let _ = B::A(42);
+}
--- /dev/null
+error[E0405]: cannot find trait `Trait` in `A`
+ --> $DIR/shadowed-path-in-trait-bound-suggestion.rs:9:24
+ |
+LL | pub struct A<H: A::Trait>(pub H);
+ | ^^^^^ not found in `A`
+ |
+help: consider importing this trait
+ |
+LL | use A::Trait;
+ |
+help: if you import `Trait`, refer to it directly
+ |
+LL - pub struct A<H: A::Trait>(pub H);
+LL + pub struct A<H: Trait>(pub H);
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0405`.
use rustc_parse::parser;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, Symbol};
-use rustc_span::{sym, BytePos, Span, DUMMY_SP};
+use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP};
declare_clippy_lint! {
/// ### What it does
}
},
ArgumentNamed(n, _) => {
+ let n = Symbol::intern(n);
if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
match x.1.as_slice() {
// A non-empty format string has been seen already.
let span = parser
.arg_places
.last()
- .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x));
+ .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end)));
if !self.in_debug_impl && arg.format.ty == "?" {
// FIXME: modify rustc's fmt string parser to give us the current span