Auto merge of #94333 - Dylan-DPC:rollup-7yxtywp, r=Dylan-DPC
Rollup of 9 pull requests Successful merges: - #91795 (resolve/metadata: Stop encoding macros as reexports) - #93714 (better ObligationCause for normalization errors in `can_type_implement_copy`) - #94175 (Improve `--check-cfg` implementation) - #94212 (Stop manually SIMDing in `swap_nonoverlapping`) - #94242 (properly handle fat pointers to uninhabitable types) - #94308 (Normalize main return type during mono item collection & codegen) - #94315 (update auto trait lint for `PhantomData`) - #94316 (Improve string literal unescaping) - #94327 (Avoid emitting full macro body into JSON errors) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
4e82f35492
@ -16,6 +16,7 @@
|
||||
#![feature(min_specialization)]
|
||||
#![recursion_limit = "256"]
|
||||
#![feature(slice_internals)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate rustc_macros;
|
||||
|
@ -56,25 +56,30 @@ impl LitKind {
|
||||
// new symbol because the string in the LitKind is different to the
|
||||
// string in the token.
|
||||
let s = symbol.as_str();
|
||||
let symbol =
|
||||
if s.contains(&['\\', '\r']) {
|
||||
let mut buf = String::with_capacity(s.len());
|
||||
let mut error = Ok(());
|
||||
unescape_literal(&s, Mode::Str, &mut |_, unescaped_char| {
|
||||
match unescaped_char {
|
||||
Ok(c) => buf.push(c),
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Err(LitError::LexerError);
|
||||
}
|
||||
let symbol = if s.contains(&['\\', '\r']) {
|
||||
let mut buf = String::with_capacity(s.len());
|
||||
let mut error = Ok(());
|
||||
// Force-inlining here is aggressive but the closure is
|
||||
// called on every char in the string, so it can be
|
||||
// hot in programs with many long strings.
|
||||
unescape_literal(
|
||||
&s,
|
||||
Mode::Str,
|
||||
&mut #[inline(always)]
|
||||
|_, unescaped_char| match unescaped_char {
|
||||
Ok(c) => buf.push(c),
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Err(LitError::LexerError);
|
||||
}
|
||||
}
|
||||
});
|
||||
error?;
|
||||
Symbol::intern(&buf)
|
||||
} else {
|
||||
symbol
|
||||
};
|
||||
},
|
||||
);
|
||||
error?;
|
||||
Symbol::intern(&buf)
|
||||
} else {
|
||||
symbol
|
||||
};
|
||||
LitKind::Str(symbol, ast::StrStyle::Cooked)
|
||||
}
|
||||
token::StrRaw(n) => {
|
||||
|
@ -444,8 +444,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
),
|
||||
ItemKind::MacroDef(MacroDef { ref body, macro_rules }) => {
|
||||
let body = P(self.lower_mac_args(body));
|
||||
|
||||
hir::ItemKind::Macro(ast::MacroDef { body, macro_rules })
|
||||
let macro_kind = self.resolver.decl_macro_kind(self.resolver.local_def_id(id));
|
||||
hir::ItemKind::Macro(ast::MacroDef { body, macro_rules }, macro_kind)
|
||||
}
|
||||
ItemKind::MacCall(..) => {
|
||||
panic!("`TyMac` should have been expanded by now")
|
||||
|
@ -61,7 +61,7 @@ use rustc_session::lint::LintBuffer;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_session::utils::{FlattenNonterminals, NtToTokenstream};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::ExpnId;
|
||||
use rustc_span::hygiene::{ExpnId, MacroKind};
|
||||
use rustc_span::source_map::{respan, DesugaringKind};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
@ -210,6 +210,8 @@ pub trait ResolverAstLowering {
|
||||
expn_id: ExpnId,
|
||||
span: Span,
|
||||
) -> LocalDefId;
|
||||
|
||||
fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind;
|
||||
}
|
||||
|
||||
/// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree,
|
||||
|
@ -8,6 +8,7 @@ use rustc_errors::{struct_span_err, Applicability};
|
||||
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
|
||||
use rustc_macros::HashStable_Generic;
|
||||
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||
use rustc_session::lint::BuiltinLintDiagnostics;
|
||||
use rustc_session::parse::{feature_err, ParseSess};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
@ -461,29 +462,37 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
|
||||
true
|
||||
}
|
||||
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
|
||||
let name = cfg.ident().expect("multi-segment cfg predicate").name;
|
||||
let ident = cfg.ident().expect("multi-segment cfg predicate");
|
||||
let name = ident.name;
|
||||
let value = cfg.value_str();
|
||||
if sess.check_config.names_checked && !sess.check_config.names_valid.contains(&name)
|
||||
{
|
||||
sess.buffer_lint(
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
CRATE_NODE_ID,
|
||||
"unexpected `cfg` condition name",
|
||||
);
|
||||
}
|
||||
if let Some(val) = value {
|
||||
if sess.check_config.values_checked.contains(&name)
|
||||
&& !sess.check_config.values_valid.contains(&(name, val))
|
||||
{
|
||||
sess.buffer_lint(
|
||||
if let Some(names_valid) = &sess.check_config.names_valid {
|
||||
if !names_valid.contains(&name) {
|
||||
sess.buffer_lint_with_diagnostic(
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
CRATE_NODE_ID,
|
||||
"unexpected `cfg` condition value",
|
||||
"unexpected `cfg` condition name",
|
||||
BuiltinLintDiagnostics::UnexpectedCfg(ident.span, name, None),
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(value) = value {
|
||||
if let Some(values) = &sess.check_config.values_valid.get(&name) {
|
||||
if !values.contains(&value) {
|
||||
sess.buffer_lint_with_diagnostic(
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
CRATE_NODE_ID,
|
||||
"unexpected `cfg` condition value",
|
||||
BuiltinLintDiagnostics::UnexpectedCfg(
|
||||
cfg.name_value_literal_span().unwrap(),
|
||||
name,
|
||||
Some(value),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
sess.config.contains(&(name, value))
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,10 @@ pub(crate) fn maybe_create_entry_wrapper(
|
||||
// late-bound regions, since late-bound
|
||||
// regions must appear in the argument
|
||||
// listing.
|
||||
let main_ret_ty = tcx.erase_regions(main_ret_ty.no_bound_vars().unwrap());
|
||||
let main_ret_ty = tcx.normalize_erasing_regions(
|
||||
ty::ParamEnv::reveal_all(),
|
||||
main_ret_ty.no_bound_vars().unwrap(),
|
||||
);
|
||||
|
||||
let cmain_sig = Signature {
|
||||
params: vec![
|
||||
|
@ -449,7 +449,10 @@ fn pointer_or_reference_metadata<'ll, 'tcx>(
|
||||
// This is a thin pointer. Create a regular pointer type and give it the correct name.
|
||||
debug_assert_eq!(
|
||||
(thin_pointer_size, thin_pointer_align),
|
||||
cx.size_and_align_of(ptr_type)
|
||||
cx.size_and_align_of(ptr_type),
|
||||
"ptr_type={}, pointee_type={}",
|
||||
ptr_type,
|
||||
pointee_type,
|
||||
);
|
||||
|
||||
unsafe {
|
||||
|
@ -4,9 +4,9 @@ use super::namespace::item_namespace;
|
||||
use super::CrateDebugContext;
|
||||
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
|
||||
use rustc_middle::ty::{self, DefIdTree, Ty};
|
||||
use rustc_target::abi::Variants;
|
||||
use tracing::trace;
|
||||
|
||||
use crate::common::CodegenCx;
|
||||
use crate::llvm;
|
||||
@ -63,30 +63,26 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
pointee_ty: Ty<'tcx>,
|
||||
) -> Option<FatPtrKind> {
|
||||
let layout = cx.layout_of(pointee_ty);
|
||||
let pointee_tail_ty = cx.tcx.struct_tail_erasing_lifetimes(pointee_ty, cx.param_env());
|
||||
let layout = cx.layout_of(pointee_tail_ty);
|
||||
trace!(
|
||||
"fat_pointer_kind: {:?} has layout {:?} (is_unsized? {})",
|
||||
pointee_tail_ty,
|
||||
layout,
|
||||
layout.is_unsized()
|
||||
);
|
||||
|
||||
if !layout.is_unsized() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match *pointee_ty.kind() {
|
||||
match *pointee_tail_ty.kind() {
|
||||
ty::Str | ty::Slice(_) => Some(FatPtrKind::Slice),
|
||||
ty::Dynamic(..) => Some(FatPtrKind::Dyn),
|
||||
ty::Adt(..) | ty::Tuple(..) if matches!(layout.variants, Variants::Single { .. }) => {
|
||||
let last_field_index = layout.fields.count() - 1;
|
||||
debug_assert!(
|
||||
(0..last_field_index)
|
||||
.all(|field_index| { !layout.field(cx, field_index).is_unsized() })
|
||||
);
|
||||
|
||||
let unsized_field = layout.field(cx, last_field_index);
|
||||
debug_assert!(unsized_field.is_unsized());
|
||||
fat_pointer_kind(cx, unsized_field.ty)
|
||||
}
|
||||
ty::Foreign(_) => {
|
||||
// Assert that pointers to foreign types really are thin:
|
||||
debug_assert_eq!(
|
||||
cx.size_of(cx.tcx.mk_imm_ptr(pointee_ty)),
|
||||
cx.size_of(cx.tcx.mk_imm_ptr(pointee_tail_ty)),
|
||||
cx.size_of(cx.tcx.mk_imm_ptr(cx.tcx.types.u8))
|
||||
);
|
||||
None
|
||||
@ -94,7 +90,10 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>(
|
||||
_ => {
|
||||
// For all other pointee types we should already have returned None
|
||||
// at the beginning of the function.
|
||||
panic!("fat_pointer_kind() - Encountered unexpected `pointee_ty`: {:?}", pointee_ty)
|
||||
panic!(
|
||||
"fat_pointer_kind() - Encountered unexpected `pointee_tail_ty`: {:?}",
|
||||
pointee_tail_ty
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -407,7 +407,10 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
// late-bound regions, since late-bound
|
||||
// regions must appear in the argument
|
||||
// listing.
|
||||
let main_ret_ty = cx.tcx().erase_regions(main_ret_ty.no_bound_vars().unwrap());
|
||||
let main_ret_ty = cx.tcx().normalize_erasing_regions(
|
||||
ty::ParamEnv::reveal_all(),
|
||||
main_ret_ty.no_bound_vars().unwrap(),
|
||||
);
|
||||
|
||||
let Some(llfn) = cx.declare_c_main(llfty) else {
|
||||
// FIXME: We should be smart and show a better diagnostic here.
|
||||
|
@ -454,8 +454,14 @@ impl DiagnosticSpan {
|
||||
let end = je.sm.lookup_char_pos(span.hi());
|
||||
let backtrace_step = backtrace.next().map(|bt| {
|
||||
let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je);
|
||||
let def_site_span =
|
||||
Self::from_span_full(bt.def_site, false, None, None, [].into_iter(), je);
|
||||
let def_site_span = Self::from_span_full(
|
||||
je.sm.guess_head_span(bt.def_site),
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
[].into_iter(),
|
||||
je,
|
||||
);
|
||||
Box::new(DiagnosticSpanMacroExpansion {
|
||||
span: call_site,
|
||||
macro_decl_name: bt.kind.descr(),
|
||||
|
@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_macros::HashStable_Generic;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{def_id::LocalDefId, BytePos, MultiSpan, Span, DUMMY_SP};
|
||||
@ -2803,7 +2804,7 @@ pub enum ItemKind<'hir> {
|
||||
/// A function declaration.
|
||||
Fn(FnSig<'hir>, Generics<'hir>, BodyId),
|
||||
/// A MBE macro definition (`macro_rules!` or `macro`).
|
||||
Macro(ast::MacroDef),
|
||||
Macro(ast::MacroDef, MacroKind),
|
||||
/// A module.
|
||||
Mod(Mod<'hir>),
|
||||
/// An external module, e.g. `extern { .. }`.
|
||||
|
@ -575,7 +575,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
|
||||
item.span,
|
||||
item.hir_id(),
|
||||
),
|
||||
ItemKind::Macro(_) => {
|
||||
ItemKind::Macro(..) => {
|
||||
visitor.visit_id(item.hir_id());
|
||||
}
|
||||
ItemKind::Mod(ref module) => {
|
||||
|
@ -570,7 +570,7 @@ impl<'a> State<'a> {
|
||||
self.end(); // need to close a box
|
||||
self.ann.nested(self, Nested::Body(body));
|
||||
}
|
||||
hir::ItemKind::Macro(ref macro_def) => {
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
self.print_mac_def(macro_def, &item.ident, item.span, |state| {
|
||||
state.print_visibility(&item.vis)
|
||||
});
|
||||
|
@ -169,11 +169,12 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
|
||||
Ok(meta_item) if parser.token == token::Eof => {
|
||||
if let Some(args) = meta_item.meta_item_list() {
|
||||
if meta_item.has_name(sym::names) {
|
||||
cfg.names_checked = true;
|
||||
let names_valid =
|
||||
cfg.names_valid.get_or_insert_with(|| FxHashSet::default());
|
||||
for arg in args {
|
||||
if arg.is_word() && arg.ident().is_some() {
|
||||
let ident = arg.ident().expect("multi-segment cfg key");
|
||||
cfg.names_valid.insert(ident.name.to_string());
|
||||
names_valid.insert(ident.name.to_string());
|
||||
} else {
|
||||
error!("`names()` arguments must be simple identifers");
|
||||
}
|
||||
@ -183,13 +184,16 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
|
||||
if let Some((name, values)) = args.split_first() {
|
||||
if name.is_word() && name.ident().is_some() {
|
||||
let ident = name.ident().expect("multi-segment cfg key");
|
||||
cfg.values_checked.insert(ident.to_string());
|
||||
let ident_values = cfg
|
||||
.values_valid
|
||||
.entry(ident.name.to_string())
|
||||
.or_insert_with(|| FxHashSet::default());
|
||||
|
||||
for val in values {
|
||||
if let Some(LitKind::Str(s, _)) =
|
||||
val.literal().map(|lit| &lit.kind)
|
||||
{
|
||||
cfg.values_valid
|
||||
.insert((ident.to_string(), s.to_string()));
|
||||
ident_values.insert(s.to_string());
|
||||
} else {
|
||||
error!(
|
||||
"`values()` arguments must be string literals"
|
||||
@ -219,7 +223,9 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
|
||||
);
|
||||
}
|
||||
|
||||
cfg.names_valid.extend(cfg.values_checked.iter().cloned());
|
||||
if let Some(names_valid) = &mut cfg.names_valid {
|
||||
names_valid.extend(cfg.values_valid.keys().cloned());
|
||||
}
|
||||
cfg
|
||||
})
|
||||
}
|
||||
|
@ -159,26 +159,8 @@ impl Mode {
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_escape(first_char: char, chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
|
||||
if first_char != '\\' {
|
||||
// Previous character was not a slash, and we don't expect it to be
|
||||
// an escape-only character.
|
||||
return match first_char {
|
||||
'\t' | '\n' => Err(EscapeError::EscapeOnlyChar),
|
||||
'\r' => Err(EscapeError::BareCarriageReturn),
|
||||
'\'' if mode.in_single_quotes() => Err(EscapeError::EscapeOnlyChar),
|
||||
'"' if mode.in_double_quotes() => Err(EscapeError::EscapeOnlyChar),
|
||||
_ => {
|
||||
if mode.is_bytes() && !first_char.is_ascii() {
|
||||
// Byte literal can't be a non-ascii character.
|
||||
return Err(EscapeError::NonAsciiCharInByte);
|
||||
}
|
||||
Ok(first_char)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Previous character is '\\', try to unescape it.
|
||||
fn scan_escape(chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
|
||||
// Previous character was '\\', unescape what follows.
|
||||
|
||||
let second_char = chars.next().ok_or(EscapeError::LoneSlash)?;
|
||||
|
||||
@ -270,9 +252,24 @@ fn scan_escape(first_char: char, chars: &mut Chars<'_>, mode: Mode) -> Result<ch
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ascii_check(first_char: char, mode: Mode) -> Result<char, EscapeError> {
|
||||
if mode.is_bytes() && !first_char.is_ascii() {
|
||||
// Byte literal can't be a non-ascii character.
|
||||
Err(EscapeError::NonAsciiCharInByte)
|
||||
} else {
|
||||
Ok(first_char)
|
||||
}
|
||||
}
|
||||
|
||||
fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
|
||||
let first_char = chars.next().ok_or(EscapeError::ZeroChars)?;
|
||||
let res = scan_escape(first_char, chars, mode)?;
|
||||
let res = match first_char {
|
||||
'\\' => scan_escape(chars, mode),
|
||||
'\n' | '\t' | '\'' => Err(EscapeError::EscapeOnlyChar),
|
||||
'\r' => Err(EscapeError::BareCarriageReturn),
|
||||
_ => ascii_check(first_char, mode),
|
||||
}?;
|
||||
if chars.next().is_some() {
|
||||
return Err(EscapeError::MoreThanOneChar);
|
||||
}
|
||||
@ -303,12 +300,14 @@ where
|
||||
skip_ascii_whitespace(&mut chars, start, callback);
|
||||
continue;
|
||||
}
|
||||
_ => scan_escape(first_char, &mut chars, mode),
|
||||
_ => scan_escape(&mut chars, mode),
|
||||
}
|
||||
}
|
||||
'\n' => Ok('\n'),
|
||||
'\t' => Ok('\t'),
|
||||
_ => scan_escape(first_char, &mut chars, mode),
|
||||
'"' => Err(EscapeError::EscapeOnlyChar),
|
||||
'\r' => Err(EscapeError::BareCarriageReturn),
|
||||
_ => ascii_check(first_char, mode),
|
||||
};
|
||||
let end = initial_len - chars.as_str().len();
|
||||
callback(start..end, unescaped_char);
|
||||
|
@ -51,7 +51,7 @@ use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{BytePos, InnerSpan, MultiSpan, Span};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
use rustc_trait_selection::traits::misc::can_type_implement_copy;
|
||||
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};
|
||||
|
||||
use crate::nonstandard_style::{method_context, MethodLateContext};
|
||||
|
||||
@ -764,7 +764,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
||||
if ty.is_copy_modulo_regions(cx.tcx.at(item.span), param_env) {
|
||||
return;
|
||||
}
|
||||
if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() {
|
||||
if can_type_implement_copy(
|
||||
cx.tcx,
|
||||
param_env,
|
||||
ty,
|
||||
traits::ObligationCause::misc(item.span, item.hir_id()),
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| {
|
||||
lint.build(
|
||||
"type could implement `Copy`; consider adding `impl \
|
||||
|
@ -765,7 +765,40 @@ pub trait LintContext: Sized {
|
||||
BuiltinLintDiagnostics::NamedAsmLabel(help) => {
|
||||
db.help(&help);
|
||||
db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
|
||||
}
|
||||
},
|
||||
BuiltinLintDiagnostics::UnexpectedCfg(span, name, value) => {
|
||||
let possibilities: Vec<Symbol> = if value.is_some() {
|
||||
let Some(values) = &sess.parse_sess.check_config.values_valid.get(&name) else {
|
||||
bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values");
|
||||
};
|
||||
values.iter().map(|&s| s).collect()
|
||||
} else {
|
||||
let Some(names_valid) = &sess.parse_sess.check_config.names_valid else {
|
||||
bug!("it shouldn't be possible to have a diagnostic on a name if name checking is not enabled");
|
||||
};
|
||||
names_valid.iter().map(|s| *s).collect()
|
||||
};
|
||||
|
||||
// Show the full list if all possible values for a given name, but don't do it
|
||||
// for names as the possibilities could be very long
|
||||
if value.is_some() {
|
||||
if !possibilities.is_empty() {
|
||||
let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
|
||||
possibilities.sort();
|
||||
|
||||
let possibilities = possibilities.join(", ");
|
||||
db.note(&format!("expected values for `{name}` are: {possibilities}"));
|
||||
} else {
|
||||
db.note(&format!("no expected value for `{name}`"));
|
||||
}
|
||||
}
|
||||
|
||||
// Suggest the most probable if we found one
|
||||
if let Some(best_match) = find_best_match_for_name(&possibilities, value.unwrap_or(name), None) {
|
||||
let punctuation = if value.is_some() { "\"" } else { "" };
|
||||
db.span_suggestion(span, "did you mean", format!("{punctuation}{best_match}{punctuation}"), Applicability::MaybeIncorrect);
|
||||
}
|
||||
},
|
||||
}
|
||||
// Rewrap `db`, and pass control to the user.
|
||||
decorate(LintDiagnosticBuilder::new(db));
|
||||
|
@ -31,6 +31,7 @@
|
||||
#![feature(box_patterns)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(iter_order_by)]
|
||||
#![feature(let_else)]
|
||||
#![feature(never_type)]
|
||||
|
@ -310,6 +310,7 @@ pub enum BuiltinLintDiagnostics {
|
||||
BreakWithLabelAndLoop(Span),
|
||||
NamedAsmLabel(String),
|
||||
UnicodeTextFlow(Span, String),
|
||||
UnexpectedCfg(Span, Symbol, Option<Symbol>),
|
||||
}
|
||||
|
||||
/// Lints that are buffered up early on in the `Session` before the
|
||||
|
@ -5,6 +5,7 @@ use crate::rmeta::table::{FixedSizeEncoding, Table};
|
||||
use crate::rmeta::*;
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
@ -1076,6 +1077,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
res,
|
||||
vis: ty::Visibility::Public,
|
||||
span: ident.span,
|
||||
macro_rules: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1087,17 +1089,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
for child_index in children.decode((self, sess)) {
|
||||
if let Some(ident) = self.opt_item_ident(child_index, sess) {
|
||||
let kind = self.def_kind(child_index);
|
||||
if matches!(kind, DefKind::Macro(..)) {
|
||||
// FIXME: Macros are currently encoded twice, once as items and once as
|
||||
// reexports. We ignore the items here and only use the reexports.
|
||||
continue;
|
||||
}
|
||||
let def_id = self.local_def_id(child_index);
|
||||
let res = Res::Def(kind, def_id);
|
||||
let vis = self.get_visibility(child_index);
|
||||
let span = self.get_span(child_index, sess);
|
||||
let macro_rules = match kind {
|
||||
DefKind::Macro(..) => match self.kind(child_index) {
|
||||
EntryKind::MacroDef(_, macro_rules) => macro_rules,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
||||
callback(ModChild { ident, res, vis, span });
|
||||
callback(ModChild { ident, res, vis, span, macro_rules });
|
||||
|
||||
// For non-re-export structs and variants add their constructors to children.
|
||||
// Re-export lists automatically contain constructors when necessary.
|
||||
@ -1109,7 +1113,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
let ctor_res =
|
||||
Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id);
|
||||
let vis = self.get_visibility(ctor_def_id.index);
|
||||
callback(ModChild { ident, res: ctor_res, vis, span });
|
||||
callback(ModChild {
|
||||
ident,
|
||||
res: ctor_res,
|
||||
vis,
|
||||
span,
|
||||
macro_rules: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
DefKind::Variant => {
|
||||
@ -1134,7 +1144,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
vis = ty::Visibility::Restricted(crate_def_id);
|
||||
}
|
||||
}
|
||||
callback(ModChild { ident, res: ctor_res, vis, span });
|
||||
callback(ModChild {
|
||||
ident,
|
||||
res: ctor_res,
|
||||
vis,
|
||||
span,
|
||||
macro_rules: false,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -1402,9 +1418,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
tcx.arena.alloc_from_iter(self.root.exported_symbols.decode((self, tcx)))
|
||||
}
|
||||
|
||||
fn get_macro(self, id: DefIndex, sess: &Session) -> MacroDef {
|
||||
fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef {
|
||||
match self.kind(id) {
|
||||
EntryKind::MacroDef(macro_def) => macro_def.decode((self, sess)),
|
||||
EntryKind::MacroDef(mac_args, macro_rules) => {
|
||||
ast::MacroDef { body: P(mac_args.decode((self, sess))), macro_rules }
|
||||
}
|
||||
_ => bug!(),
|
||||
}
|
||||
}
|
||||
|
@ -1406,8 +1406,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
|
||||
EntryKind::Fn(self.lazy(data))
|
||||
}
|
||||
hir::ItemKind::Macro(ref macro_def) => {
|
||||
EntryKind::MacroDef(self.lazy(macro_def.clone()))
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
EntryKind::MacroDef(self.lazy(&*macro_def.body), macro_def.macro_rules)
|
||||
}
|
||||
hir::ItemKind::Mod(ref m) => {
|
||||
return self.encode_info_for_mod(item.def_id, m);
|
||||
|
@ -2,7 +2,7 @@ use decoder::Metadata;
|
||||
use def_path_hash_map::DefPathHashMapRef;
|
||||
use table::{Table, TableBuilder};
|
||||
|
||||
use rustc_ast::{self as ast, MacroDef};
|
||||
use rustc_ast as ast;
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::MetadataRef;
|
||||
@ -350,7 +350,7 @@ enum EntryKind {
|
||||
Fn(Lazy<FnData>),
|
||||
ForeignFn(Lazy<FnData>),
|
||||
Mod(Lazy<[ModChild]>),
|
||||
MacroDef(Lazy<MacroDef>),
|
||||
MacroDef(Lazy<ast::MacArgs>, /*macro_rules*/ bool),
|
||||
ProcMacro(MacroKind),
|
||||
Closure,
|
||||
Generator,
|
||||
|
@ -14,7 +14,6 @@ use rustc_hir::*;
|
||||
use rustc_index::vec::Idx;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_span::def_id::StableCrateId;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::Span;
|
||||
@ -232,7 +231,7 @@ impl<'hir> Map<'hir> {
|
||||
ItemKind::Static(..) => DefKind::Static,
|
||||
ItemKind::Const(..) => DefKind::Const,
|
||||
ItemKind::Fn(..) => DefKind::Fn,
|
||||
ItemKind::Macro(..) => DefKind::Macro(MacroKind::Bang),
|
||||
ItemKind::Macro(_, macro_kind) => DefKind::Macro(macro_kind),
|
||||
ItemKind::Mod(..) => DefKind::Mod,
|
||||
ItemKind::OpaqueTy(..) => DefKind::OpaqueTy,
|
||||
ItemKind::TyAlias(..) => DefKind::TyAlias,
|
||||
|
@ -21,4 +21,6 @@ pub struct ModChild {
|
||||
pub vis: ty::Visibility,
|
||||
/// Span of the item.
|
||||
pub span: Span,
|
||||
/// A proper `macro_rules` item (not a reexport).
|
||||
pub macro_rules: bool,
|
||||
}
|
||||
|
@ -1270,7 +1270,10 @@ impl<'v> RootCollector<'_, 'v> {
|
||||
// late-bound regions, since late-bound
|
||||
// regions must appear in the argument
|
||||
// listing.
|
||||
let main_ret_ty = self.tcx.erase_regions(main_ret_ty.no_bound_vars().unwrap());
|
||||
let main_ret_ty = self.tcx.normalize_erasing_regions(
|
||||
ty::ParamEnv::reveal_all(),
|
||||
main_ret_ty.no_bound_vars().unwrap(),
|
||||
);
|
||||
|
||||
let start_instance = Instance::resolve(
|
||||
self.tcx,
|
||||
|
@ -1951,7 +1951,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
||||
// Historically we've run more checks on non-exported than exported macros,
|
||||
// so this lets us continue to run them while maintaining backwards compatibility.
|
||||
// In the long run, the checks should be harmonized.
|
||||
if let ItemKind::Macro(ref macro_def) = item.kind {
|
||||
if let ItemKind::Macro(ref macro_def, _) = item.kind {
|
||||
let def_id = item.def_id.to_def_id();
|
||||
if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
|
||||
check_non_exported_macro_for_invalid_attrs(self.tcx, item);
|
||||
|
@ -564,7 +564,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
|
||||
// privacy and mark them reachable.
|
||||
DefKind::Macro(_) => {
|
||||
let item = self.tcx.hir().expect_item(def_id);
|
||||
if let hir::ItemKind::Macro(MacroDef { macro_rules: false, .. }) = item.kind {
|
||||
if let hir::ItemKind::Macro(MacroDef { macro_rules: false, .. }, _) = item.kind {
|
||||
if vis.is_accessible_from(module.to_def_id(), self.tcx) {
|
||||
self.update(def_id, level);
|
||||
}
|
||||
@ -686,7 +686,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Macro(ref macro_def) => {
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
self.update_reachability_from_macro(item.def_id, macro_def);
|
||||
}
|
||||
hir::ItemKind::ForeignMod { items, .. } => {
|
||||
|
@ -133,7 +133,7 @@ impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
|
||||
ast::ItemKind::Impl(..) => return,
|
||||
|
||||
// Only exported `macro_rules!` items are public, but they always are
|
||||
ast::ItemKind::MacroDef(..) => {
|
||||
ast::ItemKind::MacroDef(ref macro_def) if macro_def.macro_rules => {
|
||||
let is_macro_export =
|
||||
item.attrs.iter().any(|attr| attr.has_name(sym::macro_export));
|
||||
if is_macro_export { Some(AccessLevel::Public) } else { None }
|
||||
@ -155,7 +155,8 @@ impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
|
||||
| ast::ItemKind::Struct(..)
|
||||
| ast::ItemKind::Union(..)
|
||||
| ast::ItemKind::Trait(..)
|
||||
| ast::ItemKind::TraitAlias(..) => {
|
||||
| ast::ItemKind::TraitAlias(..)
|
||||
| ast::ItemKind::MacroDef(..) => {
|
||||
if item.vis.kind.is_pub() {
|
||||
self.prev_level
|
||||
} else {
|
||||
|
@ -940,7 +940,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
|
||||
/// Builds the reduced graph for a single item in an external crate.
|
||||
fn build_reduced_graph_for_external_crate_res(&mut self, child: ModChild) {
|
||||
let parent = self.parent_scope.module;
|
||||
let ModChild { ident, res, vis, span } = child;
|
||||
let ModChild { ident, res, vis, span, macro_rules } = child;
|
||||
let res = res.expect_non_local();
|
||||
let expansion = self.parent_scope.expansion;
|
||||
// Record primary definitions.
|
||||
@ -972,7 +972,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
|
||||
_,
|
||||
) => self.r.define(parent, ident, ValueNS, (res, vis, span, expansion)),
|
||||
Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => {
|
||||
self.r.define(parent, ident, MacroNS, (res, vis, span, expansion))
|
||||
if !macro_rules {
|
||||
self.r.define(parent, ident, MacroNS, (res, vis, span, expansion))
|
||||
}
|
||||
}
|
||||
Res::Def(
|
||||
DefKind::TyParam
|
||||
|
@ -1399,14 +1399,22 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
||||
let mut reexports = Vec::new();
|
||||
|
||||
module.for_each_child(self.r, |_, ident, _, binding| {
|
||||
// Filter away ambiguous imports and anything that has def-site hygiene.
|
||||
// FIXME: Implement actual cross-crate hygiene.
|
||||
let is_good_import =
|
||||
binding.is_import() && !binding.is_ambiguity() && !ident.span.from_expansion();
|
||||
if is_good_import || binding.is_macro_def() {
|
||||
// FIXME: Consider changing the binding inserted by `#[macro_export] macro_rules`
|
||||
// into the crate root to actual `NameBindingKind::Import`.
|
||||
if binding.is_import()
|
||||
|| matches!(binding.kind, NameBindingKind::Res(_, _is_macro_export @ true))
|
||||
{
|
||||
let res = binding.res().expect_non_local();
|
||||
if res != def::Res::Err {
|
||||
reexports.push(ModChild { ident, res, vis: binding.vis, span: binding.span });
|
||||
// Ambiguous imports are treated as errors at this point and are
|
||||
// not exposed to other crates (see #36837 for more details).
|
||||
if res != def::Res::Err && !binding.is_ambiguity() {
|
||||
reexports.push(ModChild {
|
||||
ident,
|
||||
res,
|
||||
vis: binding.vis,
|
||||
span: binding.span,
|
||||
macro_rules: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -845,10 +845,6 @@ impl<'a> NameBinding<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
fn is_macro_def(&self) -> bool {
|
||||
matches!(self.kind, NameBindingKind::Res(Res::Def(DefKind::Macro(..), _), _))
|
||||
}
|
||||
|
||||
fn macro_kind(&self) -> Option<MacroKind> {
|
||||
self.res().macro_kind()
|
||||
}
|
||||
@ -990,6 +986,9 @@ pub struct Resolver<'a> {
|
||||
crate_loader: CrateLoader<'a>,
|
||||
macro_names: FxHashSet<Ident>,
|
||||
builtin_macros: FxHashMap<Symbol, BuiltinMacroState>,
|
||||
/// A small map keeping true kinds of built-in macros that appear to be fn-like on
|
||||
/// the surface (`macro` items in libcore), but are actually attributes or derives.
|
||||
builtin_macro_kinds: FxHashMap<LocalDefId, MacroKind>,
|
||||
registered_attrs: FxHashSet<Ident>,
|
||||
registered_tools: RegisteredTools,
|
||||
macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>,
|
||||
@ -1261,6 +1260,10 @@ impl ResolverAstLowering for Resolver<'_> {
|
||||
|
||||
def_id
|
||||
}
|
||||
|
||||
fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind {
|
||||
self.builtin_macro_kinds.get(&def_id).copied().unwrap_or(MacroKind::Bang)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
@ -1381,6 +1384,7 @@ impl<'a> Resolver<'a> {
|
||||
crate_loader: CrateLoader::new(session, metadata_loader, crate_name),
|
||||
macro_names: FxHashSet::default(),
|
||||
builtin_macros: Default::default(),
|
||||
builtin_macro_kinds: Default::default(),
|
||||
registered_attrs,
|
||||
registered_tools,
|
||||
macro_use_prelude: FxHashMap::default(),
|
||||
|
@ -1209,7 +1209,13 @@ impl<'a> Resolver<'a> {
|
||||
// while still taking everything else from the source code.
|
||||
// If we already loaded this builtin macro, give a better error message than 'no such builtin macro'.
|
||||
match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) {
|
||||
BuiltinMacroState::NotYetSeen(ext) => result.kind = ext,
|
||||
BuiltinMacroState::NotYetSeen(ext) => {
|
||||
result.kind = ext;
|
||||
if item.id != ast::DUMMY_NODE_ID {
|
||||
self.builtin_macro_kinds
|
||||
.insert(self.local_def_id(item.id), result.macro_kind());
|
||||
}
|
||||
}
|
||||
BuiltinMacroState::AlreadySeen(span) => {
|
||||
struct_span_err!(
|
||||
self.session,
|
||||
|
@ -416,7 +416,7 @@ impl<'hir> Sig for hir::Item<'hir> {
|
||||
|
||||
Ok(sig)
|
||||
}
|
||||
hir::ItemKind::Macro(_) => {
|
||||
hir::ItemKind::Macro(..) => {
|
||||
let mut text = "macro".to_owned();
|
||||
let name = self.ident.to_string();
|
||||
text.push_str(&name);
|
||||
|
@ -8,7 +8,7 @@ use crate::search_paths::SearchPath;
|
||||
use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
|
||||
use crate::{early_error, early_warn, Session};
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::impl_stable_hash_via_hash;
|
||||
|
||||
use rustc_target::abi::{Align, TargetDataLayout};
|
||||
@ -1023,34 +1023,30 @@ pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig
|
||||
|
||||
/// The parsed `--check-cfg` options
|
||||
pub struct CheckCfg<T = String> {
|
||||
/// Set if `names()` checking is enabled
|
||||
pub names_checked: bool,
|
||||
/// The union of all `names()`
|
||||
pub names_valid: FxHashSet<T>,
|
||||
/// The set of names for which `values()` was used
|
||||
pub values_checked: FxHashSet<T>,
|
||||
/// The set of all (name, value) pairs passed in `values()`
|
||||
pub values_valid: FxHashSet<(T, T)>,
|
||||
/// The set of all `names()`, if None no name checking is performed
|
||||
pub names_valid: Option<FxHashSet<T>>,
|
||||
/// The set of all `values()`
|
||||
pub values_valid: FxHashMap<T, FxHashSet<T>>,
|
||||
}
|
||||
|
||||
impl<T> Default for CheckCfg<T> {
|
||||
fn default() -> Self {
|
||||
CheckCfg {
|
||||
names_checked: false,
|
||||
names_valid: FxHashSet::default(),
|
||||
values_checked: FxHashSet::default(),
|
||||
values_valid: FxHashSet::default(),
|
||||
}
|
||||
CheckCfg { names_valid: Default::default(), values_valid: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CheckCfg<T> {
|
||||
fn map_data<O: Eq + Hash>(&self, f: impl Fn(&T) -> O) -> CheckCfg<O> {
|
||||
CheckCfg {
|
||||
names_checked: self.names_checked,
|
||||
names_valid: self.names_valid.iter().map(|a| f(a)).collect(),
|
||||
values_checked: self.values_checked.iter().map(|a| f(a)).collect(),
|
||||
values_valid: self.values_valid.iter().map(|(a, b)| (f(a), f(b))).collect(),
|
||||
names_valid: self
|
||||
.names_valid
|
||||
.as_ref()
|
||||
.map(|names_valid| names_valid.iter().map(|a| f(a)).collect()),
|
||||
values_valid: self
|
||||
.values_valid
|
||||
.iter()
|
||||
.map(|(a, b)| (f(a), b.iter().map(|b| f(b)).collect()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1090,17 +1086,23 @@ impl CrateCheckConfig {
|
||||
sym::doctest,
|
||||
sym::feature,
|
||||
];
|
||||
for &name in WELL_KNOWN_NAMES {
|
||||
self.names_valid.insert(name);
|
||||
if let Some(names_valid) = &mut self.names_valid {
|
||||
for &name in WELL_KNOWN_NAMES {
|
||||
names_valid.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills a `CrateCheckConfig` with configuration names and values that are actually active.
|
||||
pub fn fill_actual(&mut self, cfg: &CrateConfig) {
|
||||
for &(k, v) in cfg {
|
||||
self.names_valid.insert(k);
|
||||
if let Some(names_valid) = &mut self.names_valid {
|
||||
names_valid.insert(k);
|
||||
}
|
||||
if let Some(v) = v {
|
||||
self.values_valid.insert((k, v));
|
||||
self.values_valid.entry(k).and_modify(|values| {
|
||||
values.insert(v);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ pub fn can_type_implement_copy<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
self_type: Ty<'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
) -> Result<(), CopyImplementationError<'tcx>> {
|
||||
// FIXME: (@jroesch) float this code up
|
||||
tcx.infer_ctxt().enter(|infcx| {
|
||||
@ -49,7 +50,19 @@ pub fn can_type_implement_copy<'tcx>(
|
||||
continue;
|
||||
}
|
||||
let span = tcx.def_span(field.did);
|
||||
let cause = ObligationCause::dummy_with_span(span);
|
||||
// FIXME(compiler-errors): This gives us better spans for bad
|
||||
// projection types like in issue-50480.
|
||||
// If the ADT has substs, point to the cause we are given.
|
||||
// If it does not, then this field probably doesn't normalize
|
||||
// to begin with, and point to the bad field's span instead.
|
||||
let cause = if field
|
||||
.ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did))
|
||||
.has_param_types_or_consts()
|
||||
{
|
||||
cause.clone()
|
||||
} else {
|
||||
ObligationCause::dummy_with_span(span)
|
||||
};
|
||||
let ctx = traits::FulfillmentContext::new();
|
||||
match traits::fully_normalize(&infcx, ctx, cause, param_env, ty) {
|
||||
Ok(ty) => {
|
||||
|
@ -74,7 +74,8 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
|
||||
|
||||
debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);
|
||||
|
||||
match can_type_implement_copy(tcx, param_env, self_type) {
|
||||
let cause = traits::ObligationCause::misc(span, impl_hir_id);
|
||||
match can_type_implement_copy(tcx, param_env, self_type, cause) {
|
||||
Ok(()) => {}
|
||||
Err(CopyImplementationError::InfrigingFields(fields)) => {
|
||||
let item = tcx.hir().expect_item(impl_did);
|
||||
|
@ -439,6 +439,7 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty:
|
||||
}
|
||||
|
||||
match t.kind() {
|
||||
ty::Adt(def, substs) if def.is_phantom_data() => substs.super_visit_with(self),
|
||||
ty::Adt(def, substs) => {
|
||||
// @lcnr: This is the only place where cycles can happen. We avoid this
|
||||
// by only visiting each `DefId` once.
|
||||
|
@ -730,7 +730,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
|
||||
// These don't define types.
|
||||
hir::ItemKind::ExternCrate(_)
|
||||
| hir::ItemKind::Use(..)
|
||||
| hir::ItemKind::Macro(_)
|
||||
| hir::ItemKind::Macro(..)
|
||||
| hir::ItemKind::Mod(_)
|
||||
| hir::ItemKind::GlobalAsm(_) => {}
|
||||
hir::ItemKind::ForeignMod { items, .. } => {
|
||||
|
@ -89,6 +89,15 @@ fn binary_search_l3_worst_case(b: &mut Bencher) {
|
||||
binary_search_worst_case(b, Cache::L3);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Rgb(u8, u8, u8);
|
||||
|
||||
impl Rgb {
|
||||
fn gen(i: usize) -> Self {
|
||||
Rgb(i as u8, (i as u8).wrapping_add(7), (i as u8).wrapping_add(42))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! rotate {
|
||||
($fn:ident, $n:expr, $mapper:expr) => {
|
||||
#[bench]
|
||||
@ -104,17 +113,43 @@ macro_rules! rotate {
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Rgb(u8, u8, u8);
|
||||
|
||||
rotate!(rotate_u8, 32, |i| i as u8);
|
||||
rotate!(rotate_rgb, 32, |i| Rgb(i as u8, (i as u8).wrapping_add(7), (i as u8).wrapping_add(42)));
|
||||
rotate!(rotate_rgb, 32, Rgb::gen);
|
||||
rotate!(rotate_usize, 32, |i| i);
|
||||
rotate!(rotate_16_usize_4, 16, |i| [i; 4]);
|
||||
rotate!(rotate_16_usize_5, 16, |i| [i; 5]);
|
||||
rotate!(rotate_64_usize_4, 64, |i| [i; 4]);
|
||||
rotate!(rotate_64_usize_5, 64, |i| [i; 5]);
|
||||
|
||||
macro_rules! swap_with_slice {
|
||||
($fn:ident, $n:expr, $mapper:expr) => {
|
||||
#[bench]
|
||||
fn $fn(b: &mut Bencher) {
|
||||
let mut x = (0usize..$n).map(&$mapper).collect::<Vec<_>>();
|
||||
let mut y = ($n..($n * 2)).map(&$mapper).collect::<Vec<_>>();
|
||||
let mut skip = 0;
|
||||
b.iter(|| {
|
||||
for _ in 0..32 {
|
||||
x[skip..].swap_with_slice(&mut y[..($n - skip)]);
|
||||
skip = black_box(skip + 1) % 8;
|
||||
}
|
||||
black_box((x[$n / 3].clone(), y[$n * 2 / 3].clone()))
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
swap_with_slice!(swap_with_slice_u8_30, 30, |i| i as u8);
|
||||
swap_with_slice!(swap_with_slice_u8_3000, 3000, |i| i as u8);
|
||||
swap_with_slice!(swap_with_slice_rgb_30, 30, Rgb::gen);
|
||||
swap_with_slice!(swap_with_slice_rgb_3000, 3000, Rgb::gen);
|
||||
swap_with_slice!(swap_with_slice_usize_30, 30, |i| i);
|
||||
swap_with_slice!(swap_with_slice_usize_3000, 3000, |i| i);
|
||||
swap_with_slice!(swap_with_slice_4x_usize_30, 30, |i| [i; 4]);
|
||||
swap_with_slice!(swap_with_slice_4x_usize_3000, 3000, |i| [i; 4]);
|
||||
swap_with_slice!(swap_with_slice_5x_usize_30, 30, |i| [i; 5]);
|
||||
swap_with_slice!(swap_with_slice_5x_usize_3000, 3000, |i| [i; 5]);
|
||||
|
||||
#[bench]
|
||||
fn fill_byte_sized(b: &mut Bencher) {
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -700,10 +700,49 @@ pub unsafe fn uninitialized<T>() -> T {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
pub const fn swap<T>(x: &mut T, y: &mut T) {
|
||||
// SAFETY: the raw pointers have been created from safe mutable references satisfying all the
|
||||
// constraints on `ptr::swap_nonoverlapping_one`
|
||||
// NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary
|
||||
// reinterpretation of values as (chunkable) byte arrays, and the loop in the
|
||||
// block optimization in `swap_slice` is hard to rewrite back
|
||||
// into the (unoptimized) direct swapping implementation, so we disable it.
|
||||
// FIXME(eddyb) the block optimization also prevents MIR optimizations from
|
||||
// understanding `mem::replace`, `Option::take`, etc. - a better overall
|
||||
// solution might be to make `ptr::swap_nonoverlapping` into an intrinsic, which
|
||||
// a backend can choose to implement using the block optimization, or not.
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
{
|
||||
// For types that are larger multiples of their alignment, the simple way
|
||||
// tends to copy the whole thing to stack rather than doing it one part
|
||||
// at a time, so instead treat them as one-element slices and piggy-back
|
||||
// the slice optimizations that will split up the swaps.
|
||||
if size_of::<T>() / align_of::<T>() > 4 {
|
||||
// SAFETY: exclusive references always point to one non-overlapping
|
||||
// element and are non-null and properly aligned.
|
||||
return unsafe { ptr::swap_nonoverlapping(x, y, 1) };
|
||||
}
|
||||
}
|
||||
|
||||
// If a scalar consists of just a small number of alignment units, let
|
||||
// the codegen just swap those pieces directly, as it's likely just a
|
||||
// few instructions and anything else is probably overcomplicated.
|
||||
//
|
||||
// Most importantly, this covers primitives and simd types that tend to
|
||||
// have size=align where doing anything else can be a pessimization.
|
||||
// (This will also be used for ZSTs, though any solution works for them.)
|
||||
swap_simple(x, y);
|
||||
}
|
||||
|
||||
/// Same as [`swap`] semantically, but always uses the simple implementation.
|
||||
///
|
||||
/// Used elsewhere in `mem` and `ptr` at the bottom layer of calls.
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[inline]
|
||||
pub(crate) const fn swap_simple<T>(x: &mut T, y: &mut T) {
|
||||
// SAFETY: exclusive references are always valid to read/write,
|
||||
// are non-overlapping, and nothing here panics so it's drop-safe.
|
||||
unsafe {
|
||||
ptr::swap_nonoverlapping_one(x, y);
|
||||
let z = ptr::read(x);
|
||||
ptr::copy_nonoverlapping(y, x, 1);
|
||||
ptr::write(y, z);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -419,106 +419,58 @@ pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
|
||||
#[stable(feature = "swap_nonoverlapping", since = "1.27.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
|
||||
let x = x as *mut u8;
|
||||
let y = y as *mut u8;
|
||||
let len = mem::size_of::<T>() * count;
|
||||
// SAFETY: the caller must guarantee that `x` and `y` are
|
||||
// valid for writes and properly aligned.
|
||||
unsafe { swap_nonoverlapping_bytes(x, y, len) }
|
||||
}
|
||||
macro_rules! attempt_swap_as_chunks {
|
||||
($ChunkTy:ty) => {
|
||||
if mem::align_of::<T>() >= mem::align_of::<$ChunkTy>()
|
||||
&& mem::size_of::<T>() % mem::size_of::<$ChunkTy>() == 0
|
||||
{
|
||||
let x: *mut MaybeUninit<$ChunkTy> = x.cast();
|
||||
let y: *mut MaybeUninit<$ChunkTy> = y.cast();
|
||||
let count = count * (mem::size_of::<T>() / mem::size_of::<$ChunkTy>());
|
||||
// SAFETY: these are the same bytes that the caller promised were
|
||||
// ok, just typed as `MaybeUninit<ChunkTy>`s instead of as `T`s.
|
||||
// The `if` condition above ensures that we're not violating
|
||||
// alignment requirements, and that the division is exact so
|
||||
// that we don't lose any bytes off the end.
|
||||
return unsafe { swap_nonoverlapping_simple(x, y, count) };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
pub(crate) const unsafe fn swap_nonoverlapping_one<T>(x: *mut T, y: *mut T) {
|
||||
// NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary
|
||||
// reinterpretation of values as (chunkable) byte arrays, and the loop in the
|
||||
// block optimization in `swap_nonoverlapping_bytes` is hard to rewrite back
|
||||
// into the (unoptimized) direct swapping implementation, so we disable it.
|
||||
// FIXME(eddyb) the block optimization also prevents MIR optimizations from
|
||||
// understanding `mem::replace`, `Option::take`, etc. - a better overall
|
||||
// solution might be to make `swap_nonoverlapping` into an intrinsic, which
|
||||
// a backend can choose to implement using the block optimization, or not.
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
// Split up the slice into small power-of-two-sized chunks that LLVM is able
|
||||
// to vectorize (unless it's a special type with more-than-pointer alignment,
|
||||
// because we don't want to pessimize things like slices of SIMD vectors.)
|
||||
if mem::align_of::<T>() <= mem::size_of::<usize>()
|
||||
&& (!mem::size_of::<T>().is_power_of_two()
|
||||
|| mem::size_of::<T>() > mem::size_of::<usize>() * 2)
|
||||
{
|
||||
// Only apply the block optimization in `swap_nonoverlapping_bytes` for types
|
||||
// at least as large as the block size, to avoid pessimizing codegen.
|
||||
if mem::size_of::<T>() >= 32 {
|
||||
// SAFETY: the caller must uphold the safety contract for `swap_nonoverlapping`.
|
||||
unsafe { swap_nonoverlapping(x, y, 1) };
|
||||
return;
|
||||
}
|
||||
attempt_swap_as_chunks!(usize);
|
||||
attempt_swap_as_chunks!(u8);
|
||||
}
|
||||
|
||||
// Direct swapping, for the cases not going through the block optimization.
|
||||
// SAFETY: the caller must guarantee that `x` and `y` are valid
|
||||
// for writes, properly aligned, and non-overlapping.
|
||||
unsafe {
|
||||
let z = read(x);
|
||||
copy_nonoverlapping(y, x, 1);
|
||||
write(y, z);
|
||||
}
|
||||
// SAFETY: Same preconditions as this function
|
||||
unsafe { swap_nonoverlapping_simple(x, y, count) }
|
||||
}
|
||||
|
||||
/// Same behaviour and safety conditions as [`swap_nonoverlapping`]
|
||||
///
|
||||
/// LLVM can vectorize this (at least it can for the power-of-two-sized types
|
||||
/// `swap_nonoverlapping` tries to use) so no need to manually SIMD it.
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
const unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) {
|
||||
// The approach here is to utilize simd to swap x & y efficiently. Testing reveals
|
||||
// that swapping either 32 bytes or 64 bytes at a time is most efficient for Intel
|
||||
// Haswell E processors. LLVM is more able to optimize if we give a struct a
|
||||
// #[repr(simd)], even if we don't actually use this struct directly.
|
||||
//
|
||||
// FIXME repr(simd) broken on emscripten and redox
|
||||
#[cfg_attr(not(any(target_os = "emscripten", target_os = "redox")), repr(simd))]
|
||||
struct Block(u64, u64, u64, u64);
|
||||
struct UnalignedBlock(u64, u64, u64, u64);
|
||||
|
||||
let block_size = mem::size_of::<Block>();
|
||||
|
||||
// Loop through x & y, copying them `Block` at a time
|
||||
// The optimizer should unroll the loop fully for most types
|
||||
// N.B. We can't use a for loop as the `range` impl calls `mem::swap` recursively
|
||||
const unsafe fn swap_nonoverlapping_simple<T>(x: *mut T, y: *mut T, count: usize) {
|
||||
let mut i = 0;
|
||||
while i + block_size <= len {
|
||||
// Create some uninitialized memory as scratch space
|
||||
// Declaring `t` here avoids aligning the stack when this loop is unused
|
||||
let mut t = mem::MaybeUninit::<Block>::uninit();
|
||||
let t = t.as_mut_ptr() as *mut u8;
|
||||
while i < count {
|
||||
let x: &mut T =
|
||||
// SAFETY: By precondition, `i` is in-bounds because it's below `n`
|
||||
unsafe { &mut *x.add(i) };
|
||||
let y: &mut T =
|
||||
// SAFETY: By precondition, `i` is in-bounds because it's below `n`
|
||||
// and it's distinct from `x` since the ranges are non-overlapping
|
||||
unsafe { &mut *y.add(i) };
|
||||
mem::swap_simple(x, y);
|
||||
|
||||
// SAFETY: As `i < len`, and as the caller must guarantee that `x` and `y` are valid
|
||||
// for `len` bytes, `x + i` and `y + i` must be valid addresses, which fulfills the
|
||||
// safety contract for `add`.
|
||||
//
|
||||
// Also, the caller must guarantee that `x` and `y` are valid for writes, properly aligned,
|
||||
// and non-overlapping, which fulfills the safety contract for `copy_nonoverlapping`.
|
||||
unsafe {
|
||||
let x = x.add(i);
|
||||
let y = y.add(i);
|
||||
|
||||
// Swap a block of bytes of x & y, using t as a temporary buffer
|
||||
// This should be optimized into efficient SIMD operations where available
|
||||
copy_nonoverlapping(x, t, block_size);
|
||||
copy_nonoverlapping(y, x, block_size);
|
||||
copy_nonoverlapping(t, y, block_size);
|
||||
}
|
||||
i += block_size;
|
||||
}
|
||||
|
||||
if i < len {
|
||||
// Swap any remaining bytes
|
||||
let mut t = mem::MaybeUninit::<UnalignedBlock>::uninit();
|
||||
let rem = len - i;
|
||||
|
||||
let t = t.as_mut_ptr() as *mut u8;
|
||||
|
||||
// SAFETY: see previous safety comment.
|
||||
unsafe {
|
||||
let x = x.add(i);
|
||||
let y = y.add(i);
|
||||
|
||||
copy_nonoverlapping(x, t, rem);
|
||||
copy_nonoverlapping(y, x, rem);
|
||||
copy_nonoverlapping(t, y, rem);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
221
src/doc/unstable-book/src/compiler-flags/check-cfg.md
Normal file
221
src/doc/unstable-book/src/compiler-flags/check-cfg.md
Normal file
@ -0,0 +1,221 @@
|
||||
# `check-cfg`
|
||||
|
||||
The tracking issue for this feature is: [#82450](https://github.com/rust-lang/rust/issues/82450).
|
||||
|
||||
------------------------
|
||||
|
||||
This feature allows you to enable complete or partial checking of configuration.
|
||||
|
||||
`rustc` accepts the `--check-cfg` option, which specifies whether to check conditions and how to
|
||||
check them. The `--check-cfg` option takes a value, called the _check cfg specification_. The
|
||||
check cfg specification is parsed using the Rust metadata syntax, just as the `--cfg` option is.
|
||||
|
||||
`--check-cfg` option can take one of two forms:
|
||||
|
||||
1. `--check-cfg names(...)` enables checking condition names.
|
||||
2. `--check-cfg values(...)` enables checking the values within list-valued conditions.
|
||||
|
||||
These two options are independent. `names` checks only the namespace of condition names
|
||||
while `values` checks only the namespace of the values of list-valued conditions.
|
||||
|
||||
## The `names(...)` form
|
||||
|
||||
The `names(...)` form enables checking the names. This form uses a named list:
|
||||
|
||||
```bash
|
||||
rustc --check-cfg 'names(name1, name2, ... nameN)'
|
||||
```
|
||||
|
||||
where each `name` is a bare identifier (has no quotes). The order of the names is not significant.
|
||||
|
||||
If `--check-cfg names(...)` is specified at least once, then `rustc` will check all references to
|
||||
condition names. `rustc` will check every `#[cfg]` attribute, `#[cfg_attr]` attribute, `cfg` clause
|
||||
inside `#[link]` attribute and `cfg!(...)` call against the provided list of expected condition
|
||||
names. If a name is not present in this list, then `rustc` will report an `unexpected_cfgs` lint
|
||||
diagnostic. The default diagnostic level for this lint is `Warn`.
|
||||
|
||||
If `--check-cfg names(...)` is not specified, then `rustc` will not check references to condition
|
||||
names.
|
||||
|
||||
`--check-cfg names(...)` may be specified more than once. The result is that the list of valid
|
||||
condition names is merged across all options. It is legal for a condition name to be specified
|
||||
more than once; redundantly specifying a condition name has no effect.
|
||||
|
||||
To enable checking condition names with an empty set of valid condition names, use the following
|
||||
form. The parentheses are required.
|
||||
|
||||
```bash
|
||||
rustc --check-cfg 'names()'
|
||||
```
|
||||
|
||||
Note that `--check-cfg 'names()'` is _not_ equivalent to omitting the option entirely.
|
||||
The first form enables checking condition names, while specifying that there are no valid
|
||||
condition names (outside of the set of well-known names defined by `rustc`). Omitting the
|
||||
`--check-cfg 'names(...)'` option does not enable checking condition names.
|
||||
|
||||
Conditions that are enabled are implicitly valid; it is unnecessary (but legal) to specify a
|
||||
condition name as both enabled and valid. For example, the following invocations are equivalent:
|
||||
|
||||
```bash
|
||||
# condition names will be checked, and 'has_time_travel' is valid
|
||||
rustc --cfg 'has_time_travel' --check-cfg 'names()'
|
||||
|
||||
# condition names will be checked, and 'has_time_travel' is valid
|
||||
rustc --cfg 'has_time_travel' --check-cfg 'names(has_time_travel)'
|
||||
```
|
||||
|
||||
In contrast, the following two invocations are _not_ equivalent:
|
||||
|
||||
```bash
|
||||
# condition names will not be checked (because there is no --check-cfg names(...))
|
||||
rustc --cfg 'has_time_travel'
|
||||
|
||||
# condition names will be checked, and 'has_time_travel' is both valid and enabled.
|
||||
rustc --cfg 'has_time_travel' --check-cfg 'names(has_time_travel)'
|
||||
```
|
||||
|
||||
## The `values(...)` form
|
||||
|
||||
The `values(...)` form enables checking the values within list-valued conditions. It has this
|
||||
form:
|
||||
|
||||
```bash
|
||||
rustc --check-cfg `values(name, "value1", "value2", ... "valueN")'
|
||||
```
|
||||
|
||||
where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
|
||||
string. `name` specifies the name of the condition, such as `feature` or `target_os`.
|
||||
|
||||
When the `values(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
|
||||
attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
|
||||
and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
|
||||
list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
|
||||
lint diagnostic. The default diagnostic level for this lint is `Warn`.
|
||||
|
||||
The form `values()` is an error, because it does not specify a condition name.
|
||||
|
||||
To enable checking of values, but to provide an empty set of valid values, use this form:
|
||||
|
||||
```bash
|
||||
rustc --check-cfg `values(name)`
|
||||
```
|
||||
|
||||
The `--check-cfg values(...)` option can be repeated, both for the same condition name and for
|
||||
different names. If it is repeated for the same condition name, then the sets of values for that
|
||||
condition are merged together.
|
||||
|
||||
## Examples
|
||||
|
||||
Consider this command line:
|
||||
|
||||
```bash
|
||||
rustc --check-cfg 'names(feature)' \
|
||||
--check-cfg 'values(feature,"lion","zebra")' \
|
||||
--cfg 'feature="lion"' -Z unstable-options \
|
||||
example.rs
|
||||
```
|
||||
|
||||
This command line indicates that this crate has two features: `lion` and `zebra`. The `lion`
|
||||
feature is enabled, while the `zebra` feature is disabled. Consider compiling this code:
|
||||
|
||||
```rust
|
||||
// This is expected, and tame_lion() will be compiled
|
||||
#[cfg(feature = "lion")]
|
||||
fn tame_lion(lion: Lion) {}
|
||||
|
||||
// This is expected, and ride_zebra() will NOT be compiled.
|
||||
#[cfg(feature = "zebra")]
|
||||
fn ride_zebra(zebra: Zebra) {}
|
||||
|
||||
// This is UNEXPECTED, and will cause a compiler warning (by default).
|
||||
#[cfg(feature = "platypus")]
|
||||
fn poke_platypus() {}
|
||||
|
||||
// This is UNEXPECTED, because 'feechure' is not a known condition name,
|
||||
// and will cause a compiler warning (by default).
|
||||
#[cfg(feechure = "lion")]
|
||||
fn tame_lion() {}
|
||||
```
|
||||
|
||||
> Note: The `--check-cfg names(feature)` option is necessary only to enable checking the condition
|
||||
> name, as in the last example. `feature` is a well-known (always-expected) condition name, and so
|
||||
> it is not necessary to specify it in a `--check-cfg 'names(...)'` option. That option can be
|
||||
> shortened to > `--check-cfg names()` in order to enable checking well-known condition names.
|
||||
|
||||
### Example: Checking condition names, but not values
|
||||
|
||||
```bash
|
||||
# This turns on checking for condition names, but not values, such as 'feature' values.
|
||||
rustc --check-cfg 'names(is_embedded, has_feathers)' \
|
||||
--cfg has_feathers --cfg 'feature = "zapping"' -Z unstable-options
|
||||
```
|
||||
|
||||
```rust
|
||||
#[cfg(is_embedded)] // This is expected as "is_embedded" was provided in names()
|
||||
fn do_embedded() {}
|
||||
|
||||
#[cfg(has_feathers)] // This is expected as "has_feathers" was provided in names()
|
||||
fn do_features() {}
|
||||
|
||||
#[cfg(has_mumble_frotz)] // This is UNEXPECTED because names checking is enable and
|
||||
// "has_mumble_frotz" was not provided in names()
|
||||
fn do_mumble_frotz() {}
|
||||
|
||||
#[cfg(feature = "lasers")] // This doesn't raise a warning, because values checking for "feature"
|
||||
// was never used
|
||||
fn shoot_lasers() {}
|
||||
```
|
||||
|
||||
### Example: Checking feature values, but not condition names
|
||||
|
||||
```bash
|
||||
# This turns on checking for feature values, but not for condition names.
|
||||
rustc --check-cfg 'values(feature, "zapping", "lasers")' \
|
||||
--cfg 'feature="zapping"' -Z unstable-options
|
||||
```
|
||||
|
||||
```rust
|
||||
#[cfg(is_embedded)] // This is doesn't raise a warning, because names checking was not
|
||||
// enable (ie not names())
|
||||
fn do_embedded() {}
|
||||
|
||||
#[cfg(has_feathers)] // Same as above, --check-cfg names(...) was never used so no name
|
||||
// checking is performed
|
||||
fn do_features() {}
|
||||
|
||||
|
||||
#[cfg(feature = "lasers")] // This is expected, "lasers" is in the values(feature) list
|
||||
fn shoot_lasers() {}
|
||||
|
||||
#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in the
|
||||
// --check-cfg values(feature) list
|
||||
fn write_shakespeare() {}
|
||||
```
|
||||
|
||||
### Example: Checking both condition names and feature values
|
||||
|
||||
```bash
|
||||
# This turns on checking for feature values and for condition names.
|
||||
rustc --check-cfg 'names(is_embedded, has_feathers)' \
|
||||
--check-cfg 'values(feature, "zapping", "lasers")' \
|
||||
--cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options
|
||||
```
|
||||
|
||||
```rust
|
||||
#[cfg(is_embedded)] // This is expected because "is_embedded" was provided in names()
|
||||
fn do_embedded() {}
|
||||
|
||||
#[cfg(has_feathers)] // This is expected because "has_feathers" was provided in names()
|
||||
fn do_features() {}
|
||||
|
||||
#[cfg(has_mumble_frotz)] // This is UNEXPECTED, because has_mumble_frotz is not in the
|
||||
// --check-cfg names(...) list
|
||||
fn do_mumble_frotz() {}
|
||||
|
||||
#[cfg(feature = "lasers")] // This is expected, "lasers" is in the values(feature) list
|
||||
fn shoot_lasers() {}
|
||||
|
||||
#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in
|
||||
// the values(feature) list
|
||||
fn write_shakespear() {}
|
||||
```
|
@ -1855,7 +1855,7 @@ fn clean_maybe_renamed_item(
|
||||
ItemKind::Fn(ref sig, ref generics, body_id) => {
|
||||
clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
|
||||
}
|
||||
ItemKind::Macro(ref macro_def) => {
|
||||
ItemKind::Macro(ref macro_def, _) => {
|
||||
let ty_vis = cx.tcx.visibility(def_id).clean(cx);
|
||||
MacroItem(Macro {
|
||||
source: display_macro_source(cx, name, macro_def, def_id, ty_vis),
|
||||
|
@ -1164,7 +1164,7 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
|
||||
|
||||
fn visit_item(&mut self, item: &'hir hir::Item<'_>) {
|
||||
let name = match &item.kind {
|
||||
hir::ItemKind::Macro(ref macro_def) => {
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
// FIXME(#88038): Non exported macros have historically not been tested,
|
||||
// but we really ought to start testing them.
|
||||
let def_id = item.def_id.to_def_id();
|
||||
|
@ -325,7 +325,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||
|
||||
om.items.push((item, renamed))
|
||||
}
|
||||
hir::ItemKind::Macro(ref macro_def) => {
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
// `#[macro_export] macro_rules!` items are handled seperately in `visit()`,
|
||||
// above, since they need to be documented at the module top level. Accordingly,
|
||||
// we only want to handle macros if one of three conditions holds:
|
||||
|
64
src/test/codegen/swap-large-types.rs
Normal file
64
src/test/codegen/swap-large-types.rs
Normal file
@ -0,0 +1,64 @@
|
||||
// compile-flags: -O
|
||||
// only-x86_64
|
||||
// ignore-debug: the debug assertions get in the way
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
use std::mem::swap;
|
||||
use std::ptr::{read, copy_nonoverlapping, write};
|
||||
|
||||
type KeccakBuffer = [[u64; 5]; 5];
|
||||
|
||||
// A basic read+copy+write swap implementation ends up copying one of the values
|
||||
// to stack for large types, which is completely unnecessary as the lack of
|
||||
// overlap means we can just do whatever fits in registers at a time.
|
||||
|
||||
// CHECK-LABEL: @swap_basic
|
||||
#[no_mangle]
|
||||
pub fn swap_basic(x: &mut KeccakBuffer, y: &mut KeccakBuffer) {
|
||||
// CHECK: alloca [5 x [5 x i64]]
|
||||
|
||||
// SAFETY: exclusive references are always valid to read/write,
|
||||
// are non-overlapping, and nothing here panics so it's drop-safe.
|
||||
unsafe {
|
||||
let z = read(x);
|
||||
copy_nonoverlapping(y, x, 1);
|
||||
write(y, z);
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that the library does something smarter, and thus
|
||||
// doesn't need any scratch space on the stack.
|
||||
|
||||
// CHECK-LABEL: @swap_std
|
||||
#[no_mangle]
|
||||
pub fn swap_std(x: &mut KeccakBuffer, y: &mut KeccakBuffer) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: load <{{[0-9]+}} x i64>
|
||||
// CHECK: store <{{[0-9]+}} x i64>
|
||||
swap(x, y)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @swap_slice
|
||||
#[no_mangle]
|
||||
pub fn swap_slice(x: &mut [KeccakBuffer], y: &mut [KeccakBuffer]) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: load <{{[0-9]+}} x i64>
|
||||
// CHECK: store <{{[0-9]+}} x i64>
|
||||
if x.len() == y.len() {
|
||||
x.swap_with_slice(y);
|
||||
}
|
||||
}
|
||||
|
||||
type OneKilobyteBuffer = [u8; 1024];
|
||||
|
||||
// CHECK-LABEL: @swap_1kb_slices
|
||||
#[no_mangle]
|
||||
pub fn swap_1kb_slices(x: &mut [OneKilobyteBuffer], y: &mut [OneKilobyteBuffer]) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: load <{{[0-9]+}} x i8>
|
||||
// CHECK: store <{{[0-9]+}} x i8>
|
||||
if x.len() == y.len() {
|
||||
x.swap_with_slice(y);
|
||||
}
|
||||
}
|
32
src/test/codegen/swap-simd-types.rs
Normal file
32
src/test/codegen/swap-simd-types.rs
Normal file
@ -0,0 +1,32 @@
|
||||
// compile-flags: -O -C target-feature=+avx
|
||||
// only-x86_64
|
||||
// ignore-debug: the debug assertions get in the way
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
use std::mem::swap;
|
||||
|
||||
// SIMD types are highly-aligned already, so make sure the swap code leaves their
|
||||
// types alone and doesn't pessimize them (such as by swapping them as `usize`s).
|
||||
extern crate core;
|
||||
use core::arch::x86_64::__m256;
|
||||
|
||||
// CHECK-LABEL: @swap_single_m256
|
||||
#[no_mangle]
|
||||
pub fn swap_single_m256(x: &mut __m256, y: &mut __m256) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: load <8 x float>{{.+}}align 32
|
||||
// CHECK: store <8 x float>{{.+}}align 32
|
||||
swap(x, y)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @swap_m256_slice
|
||||
#[no_mangle]
|
||||
pub fn swap_m256_slice(x: &mut [__m256], y: &mut [__m256]) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: load <8 x float>{{.+}}align 32
|
||||
// CHECK: store <8 x float>{{.+}}align 32
|
||||
if x.len() == y.len() {
|
||||
x.swap_with_slice(y);
|
||||
}
|
||||
}
|
@ -16,3 +16,47 @@ pub fn swap_rgb48(x: &mut RGB48, y: &mut RGB48) {
|
||||
// CHECK: store i48
|
||||
swap(x, y)
|
||||
}
|
||||
|
||||
// LLVM doesn't vectorize a loop over 3-byte elements,
|
||||
// so we chunk it down to bytes and loop over those instead.
|
||||
type RGB24 = [u8; 3];
|
||||
|
||||
// CHECK-LABEL: @swap_rgb24_slices
|
||||
#[no_mangle]
|
||||
pub fn swap_rgb24_slices(x: &mut [RGB24], y: &mut [RGB24]) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: load <{{[0-9]+}} x i8>
|
||||
// CHECK: store <{{[0-9]+}} x i8>
|
||||
if x.len() == y.len() {
|
||||
x.swap_with_slice(y);
|
||||
}
|
||||
}
|
||||
|
||||
// This one has a power-of-two size, so we iterate over it directly
|
||||
type RGBA32 = [u8; 4];
|
||||
|
||||
// CHECK-LABEL: @swap_rgba32_slices
|
||||
#[no_mangle]
|
||||
pub fn swap_rgba32_slices(x: &mut [RGBA32], y: &mut [RGBA32]) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: load <{{[0-9]+}} x i32>
|
||||
// CHECK: store <{{[0-9]+}} x i32>
|
||||
if x.len() == y.len() {
|
||||
x.swap_with_slice(y);
|
||||
}
|
||||
}
|
||||
|
||||
// Strings have a non-power-of-two size, but have pointer alignment,
|
||||
// so we swap usizes instead of dropping all the way down to bytes.
|
||||
const _: () = assert!(!std::mem::size_of::<String>().is_power_of_two());
|
||||
|
||||
// CHECK-LABEL: @swap_string_slices
|
||||
#[no_mangle]
|
||||
pub fn swap_string_slices(x: &mut [String], y: &mut [String]) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: load <{{[0-9]+}} x i64>
|
||||
// CHECK: store <{{[0-9]+}} x i64>
|
||||
if x.len() == y.len() {
|
||||
x.swap_with_slice(y);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![deny(suspicious_auto_trait_impls)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct MayImplementSendOk<T>(T);
|
||||
unsafe impl<T: Send> Send for MayImplementSendOk<T> {} // ok
|
||||
|
||||
@ -31,4 +33,12 @@ unsafe impl<T: Send> Send for TwoParamsSame<T, T> {}
|
||||
//~^ ERROR
|
||||
//~| WARNING this will change its meaning
|
||||
|
||||
pub struct WithPhantomDataNonSend<T, U>(PhantomData<*const T>, U);
|
||||
unsafe impl<T> Send for WithPhantomDataNonSend<T, i8> {} // ok
|
||||
|
||||
pub struct WithPhantomDataSend<T, U>(PhantomData<T>, U);
|
||||
unsafe impl<T> Send for WithPhantomDataSend<*const T, i8> {}
|
||||
//~^ ERROR
|
||||
//~| WARNING this will change its meaning
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: cross-crate traits with a default impl, like `Send`, should not be specialized
|
||||
--> $DIR/suspicious-impls-lint.rs:7:1
|
||||
--> $DIR/suspicious-impls-lint.rs:9:1
|
||||
|
|
||||
LL | unsafe impl<T: Send> Send for MayImplementSendErr<&T> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -12,14 +12,14 @@ LL | #![deny(suspicious_auto_trait_impls)]
|
||||
= warning: this will change its meaning in a future release!
|
||||
= note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
|
||||
note: try using the same sequence of generic parameters as the struct definition
|
||||
--> $DIR/suspicious-impls-lint.rs:6:1
|
||||
--> $DIR/suspicious-impls-lint.rs:8:1
|
||||
|
|
||||
LL | struct MayImplementSendErr<T>(T);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: `&T` is not a generic parameter
|
||||
|
||||
error: cross-crate traits with a default impl, like `Send`, should not be specialized
|
||||
--> $DIR/suspicious-impls-lint.rs:19:1
|
||||
--> $DIR/suspicious-impls-lint.rs:21:1
|
||||
|
|
||||
LL | unsafe impl Send for ContainsVec<i32> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -27,14 +27,14 @@ LL | unsafe impl Send for ContainsVec<i32> {}
|
||||
= warning: this will change its meaning in a future release!
|
||||
= note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
|
||||
note: try using the same sequence of generic parameters as the struct definition
|
||||
--> $DIR/suspicious-impls-lint.rs:18:1
|
||||
--> $DIR/suspicious-impls-lint.rs:20:1
|
||||
|
|
||||
LL | struct ContainsVec<T>(Vec<T>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: `i32` is not a generic parameter
|
||||
|
||||
error: cross-crate traits with a default impl, like `Send`, should not be specialized
|
||||
--> $DIR/suspicious-impls-lint.rs:30:1
|
||||
--> $DIR/suspicious-impls-lint.rs:32:1
|
||||
|
|
||||
LL | unsafe impl<T: Send> Send for TwoParamsSame<T, T> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -42,11 +42,26 @@ LL | unsafe impl<T: Send> Send for TwoParamsSame<T, T> {}
|
||||
= warning: this will change its meaning in a future release!
|
||||
= note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
|
||||
note: try using the same sequence of generic parameters as the struct definition
|
||||
--> $DIR/suspicious-impls-lint.rs:29:1
|
||||
--> $DIR/suspicious-impls-lint.rs:31:1
|
||||
|
|
||||
LL | struct TwoParamsSame<T, U>(T, U);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: `T` is mentioned multiple times
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: cross-crate traits with a default impl, like `Send`, should not be specialized
|
||||
--> $DIR/suspicious-impls-lint.rs:40:1
|
||||
|
|
||||
LL | unsafe impl<T> Send for WithPhantomDataSend<*const T, i8> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this will change its meaning in a future release!
|
||||
= note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
|
||||
note: try using the same sequence of generic parameters as the struct definition
|
||||
--> $DIR/suspicious-impls-lint.rs:39:1
|
||||
|
|
||||
LL | pub struct WithPhantomDataSend<T, U>(PhantomData<T>, U);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: `*const T` is not a generic parameter
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
@ -2,7 +2,7 @@ warning: unexpected `cfg` condition name
|
||||
--> $DIR/invalid-cfg-name.rs:7:7
|
||||
|
|
||||
LL | #[cfg(widnows)]
|
||||
| ^^^^^^^
|
||||
| ^^^^^^^ help: did you mean: `windows`
|
||||
|
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
|
@ -5,6 +5,7 @@ LL | #[cfg(feature = "sedre")]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
= note: expected values for `feature` are: full, rand, serde
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
|
50
src/test/ui/check-cfg/mix.rs
Normal file
50
src/test/ui/check-cfg/mix.rs
Normal file
@ -0,0 +1,50 @@
|
||||
// This test checks the combination of well known names, their activation via names(), the usage of
|
||||
// partial values() with a --cfg and test that we also correctly lint on the `cfg!` macro and
|
||||
// `cfg_attr` attribute.
|
||||
//
|
||||
// check-pass
|
||||
// compile-flags: --check-cfg=names() --check-cfg=values(feature,"foo") --cfg feature="bar" -Z unstable-options
|
||||
|
||||
#[cfg(windows)]
|
||||
fn do_windows_stuff() {}
|
||||
|
||||
#[cfg(widnows)]
|
||||
//~^ WARNING unexpected `cfg` condition name
|
||||
fn do_windows_stuff() {}
|
||||
|
||||
#[cfg(feature = "foo")]
|
||||
fn use_foo() {}
|
||||
|
||||
#[cfg(feature = "bar")]
|
||||
fn use_bar() {}
|
||||
|
||||
#[cfg(feature = "zebra")]
|
||||
//~^ WARNING unexpected `cfg` condition value
|
||||
fn use_zebra() {}
|
||||
|
||||
#[cfg_attr(uu, test)]
|
||||
//~^ WARNING unexpected `cfg` condition name
|
||||
fn do_test() {}
|
||||
|
||||
#[cfg_attr(feature = "foo", no_mangle)]
|
||||
fn do_test_foo() {}
|
||||
|
||||
fn test_cfg_macro() {
|
||||
cfg!(windows);
|
||||
cfg!(widnows);
|
||||
//~^ WARNING unexpected `cfg` condition name
|
||||
cfg!(feature = "foo");
|
||||
cfg!(feature = "bar");
|
||||
cfg!(feature = "zebra");
|
||||
//~^ WARNING unexpected `cfg` condition value
|
||||
cfg!(xxx = "foo");
|
||||
//~^ WARNING unexpected `cfg` condition name
|
||||
cfg!(xxx);
|
||||
//~^ WARNING unexpected `cfg` condition name
|
||||
cfg!(any(xxx, windows));
|
||||
//~^ WARNING unexpected `cfg` condition name
|
||||
cfg!(any(feature = "bad", windows));
|
||||
//~^ WARNING unexpected `cfg` condition value
|
||||
}
|
||||
|
||||
fn main() {}
|
66
src/test/ui/check-cfg/mix.stderr
Normal file
66
src/test/ui/check-cfg/mix.stderr
Normal file
@ -0,0 +1,66 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
--> $DIR/mix.rs:11:7
|
||||
|
|
||||
LL | #[cfg(widnows)]
|
||||
| ^^^^^^^ help: did you mean: `windows`
|
||||
|
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
--> $DIR/mix.rs:21:7
|
||||
|
|
||||
LL | #[cfg(feature = "zebra")]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected values for `feature` are: bar, foo
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
--> $DIR/mix.rs:25:12
|
||||
|
|
||||
LL | #[cfg_attr(uu, test)]
|
||||
| ^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
--> $DIR/mix.rs:34:10
|
||||
|
|
||||
LL | cfg!(widnows);
|
||||
| ^^^^^^^ help: did you mean: `windows`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
--> $DIR/mix.rs:38:10
|
||||
|
|
||||
LL | cfg!(feature = "zebra");
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected values for `feature` are: bar, foo
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
--> $DIR/mix.rs:40:10
|
||||
|
|
||||
LL | cfg!(xxx = "foo");
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
--> $DIR/mix.rs:42:10
|
||||
|
|
||||
LL | cfg!(xxx);
|
||||
| ^^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
--> $DIR/mix.rs:44:14
|
||||
|
|
||||
LL | cfg!(any(xxx, windows));
|
||||
| ^^^
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
--> $DIR/mix.rs:46:14
|
||||
|
|
||||
LL | cfg!(any(feature = "bad", windows));
|
||||
| ^^^^^^^^^^-----
|
||||
| |
|
||||
| help: did you mean: `"bar"`
|
||||
|
|
||||
= note: expected values for `feature` are: bar, foo
|
||||
|
||||
warning: 9 warnings emitted
|
||||
|
10
src/test/ui/check-cfg/no-values.rs
Normal file
10
src/test/ui/check-cfg/no-values.rs
Normal file
@ -0,0 +1,10 @@
|
||||
// Check that we detect unexpected value when none are allowed
|
||||
//
|
||||
// check-pass
|
||||
// compile-flags: --check-cfg=values(feature) -Z unstable-options
|
||||
|
||||
#[cfg(feature = "foo")]
|
||||
//~^ WARNING unexpected `cfg` condition value
|
||||
fn do_foo() {}
|
||||
|
||||
fn main() {}
|
11
src/test/ui/check-cfg/no-values.stderr
Normal file
11
src/test/ui/check-cfg/no-values.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
--> $DIR/no-values.rs:6:7
|
||||
|
|
||||
LL | #[cfg(feature = "foo")]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
= note: no expected value for `feature`
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
27
src/test/ui/check-cfg/well-known-names.rs
Normal file
27
src/test/ui/check-cfg/well-known-names.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// This test checks that we lint on non well known names and that we don't lint on well known names
|
||||
//
|
||||
// check-pass
|
||||
// compile-flags: --check-cfg=names() -Z unstable-options
|
||||
|
||||
#[cfg(target_oz = "linux")]
|
||||
//~^ WARNING unexpected `cfg` condition name
|
||||
fn target_os_misspell() {}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn target_os() {}
|
||||
|
||||
#[cfg(features = "foo")]
|
||||
//~^ WARNING unexpected `cfg` condition name
|
||||
fn feature_misspell() {}
|
||||
|
||||
#[cfg(feature = "foo")]
|
||||
fn feature() {}
|
||||
|
||||
#[cfg(uniw)]
|
||||
//~^ WARNING unexpected `cfg` condition name
|
||||
fn unix_misspell() {}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn unix() {}
|
||||
|
||||
fn main() {}
|
26
src/test/ui/check-cfg/well-known-names.stderr
Normal file
26
src/test/ui/check-cfg/well-known-names.stderr
Normal file
@ -0,0 +1,26 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
--> $DIR/well-known-names.rs:6:7
|
||||
|
|
||||
LL | #[cfg(target_oz = "linux")]
|
||||
| ---------^^^^^^^^^^
|
||||
| |
|
||||
| help: did you mean: `target_os`
|
||||
|
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
--> $DIR/well-known-names.rs:13:7
|
||||
|
|
||||
LL | #[cfg(features = "foo")]
|
||||
| --------^^^^^^^^
|
||||
| |
|
||||
| help: did you mean: `feature`
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
--> $DIR/well-known-names.rs:20:7
|
||||
|
|
||||
LL | #[cfg(uniw)]
|
||||
| ^^^^ help: did you mean: `unix`
|
||||
|
||||
warning: 3 warnings emitted
|
||||
|
@ -0,0 +1,29 @@
|
||||
// check-pass
|
||||
// compile-flags: -Cdebuginfo=2
|
||||
// fixes issue #94149
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub fn main() {
|
||||
let _ = Foo::<dyn FooTrait>::new();
|
||||
}
|
||||
|
||||
pub struct Foo<T: FooTrait + ?Sized> {
|
||||
base: FooBase,
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl<T: FooTrait + ?Sized> Foo<T> {
|
||||
pub fn new() -> Box<Foo<T>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FooTrait {}
|
||||
|
||||
pub struct FooBase {
|
||||
cls: Bar,
|
||||
}
|
||||
|
||||
// Bar *must* be a fieldless enum
|
||||
pub enum Bar {}
|
25
src/test/ui/traits/copy-impl-cannot-normalize.rs
Normal file
25
src/test/ui/traits/copy-impl-cannot-normalize.rs
Normal file
@ -0,0 +1,25 @@
|
||||
trait TraitFoo {
|
||||
type Bar;
|
||||
}
|
||||
|
||||
struct Foo<T>
|
||||
where
|
||||
T: TraitFoo,
|
||||
{
|
||||
inner: T::Bar,
|
||||
}
|
||||
|
||||
impl<T> Clone for Foo<T>
|
||||
where
|
||||
T: TraitFoo,
|
||||
T::Bar: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self { inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Foo<T> {}
|
||||
//~^ ERROR the trait bound `T: TraitFoo` is not satisfied
|
||||
|
||||
fn main() {}
|
14
src/test/ui/traits/copy-impl-cannot-normalize.stderr
Normal file
14
src/test/ui/traits/copy-impl-cannot-normalize.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error[E0277]: the trait bound `T: TraitFoo` is not satisfied
|
||||
--> $DIR/copy-impl-cannot-normalize.rs:22:1
|
||||
|
|
||||
LL | impl<T> Copy for Foo<T> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TraitFoo` is not implemented for `T`
|
||||
|
|
||||
help: consider restricting type parameter `T`
|
||||
|
|
||||
LL | impl<T: TraitFoo> Copy for Foo<T> {}
|
||||
| ++++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||
let sugg = |diag: &mut DiagnosticBuilder<'_>| {
|
||||
if let ty::Adt(def, ..) = ty.kind() {
|
||||
if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
|
||||
if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
|
||||
if can_type_implement_copy(cx.tcx, cx.param_env, ty, traits::ObligationCause::dummy_with_span(span)).is_ok() {
|
||||
diag.span_help(span, "consider marking this type as `Copy`");
|
||||
}
|
||||
}
|
||||
|
@ -373,7 +373,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
let item_ty = cx.tcx.type_of(did);
|
||||
println!("function of type {:#?}", item_ty);
|
||||
},
|
||||
hir::ItemKind::Macro(ref macro_def) => {
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
if macro_def.macro_rules {
|
||||
println!("macro introduced by `macro_rules!`");
|
||||
} else {
|
||||
|
@ -15,7 +15,7 @@ extern crate macro_use_helper as mac;
|
||||
extern crate proc_macro_derive as mini_mac;
|
||||
|
||||
mod a {
|
||||
use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};
|
||||
use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};
|
||||
use mac;
|
||||
use mini_mac::ClippyMiniMacroTest;
|
||||
use mini_mac;
|
||||
|
@ -2,7 +2,7 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition
|
||||
--> $DIR/macro_use_imports.rs:18:5
|
||||
|
|
||||
LL | #[macro_use]
|
||||
| ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};`
|
||||
| ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};`
|
||||
|
|
||||
= note: `-D clippy::macro-use-imports` implied by `-D warnings`
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user