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:
bors 2022-02-24 22:29:14 +00:00
commit 4e82f35492
66 changed files with 1078 additions and 268 deletions

View File

@ -16,6 +16,7 @@
#![feature(min_specialization)]
#![recursion_limit = "256"]
#![feature(slice_internals)]
#![feature(stmt_expr_attributes)]
#[macro_use]
extern crate rustc_macros;

View File

@ -56,20 +56,25 @@ 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 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 {
// 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 {

View File

@ -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")

View File

@ -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,

View File

@ -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(
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 name",
BuiltinLintDiagnostics::UnexpectedCfg(ident.span, name, None),
);
}
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(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))
}
}

View File

@ -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![

View File

@ -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 {

View File

@ -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
)
}
}
}

View File

@ -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.

View File

@ -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(),

View File

@ -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 { .. }`.

View File

@ -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) => {

View File

@ -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)
});

View File

@ -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
})
}

View File

@ -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);

View File

@ -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 \

View File

@ -765,8 +765,41 @@ 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));
});

View File

@ -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)]

View File

@ -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

View File

@ -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!(),
}
}

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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,
}

View File

@ -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,

View File

@ -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);

View File

@ -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, .. } => {

View File

@ -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 {

View File

@ -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,8 +972,10 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
_,
) => self.r.define(parent, ident, ValueNS, (res, vis, span, expansion)),
Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => {
if !macro_rules {
self.r.define(parent, ident, MacroNS, (res, vis, span, expansion))
}
}
Res::Def(
DefKind::TyParam
| DefKind::ConstParam

View File

@ -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,
});
}
}
});

View File

@ -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(),

View File

@ -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,

View File

@ -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);

View File

@ -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,
];
if let Some(names_valid) = &mut self.names_valid {
for &name in WELL_KNOWN_NAMES {
self.names_valid.insert(name);
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);
});
}
}
}

View File

@ -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) => {

View File

@ -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);

View File

@ -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.

View File

@ -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, .. } => {

View File

@ -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)]

View File

@ -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);
}
}

View File

@ -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) }
}
#[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"))]
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
{
// 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;
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) };
}
};
}
// 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);
}
// 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)
{
attempt_swap_as_chunks!(usize);
attempt_swap_as_chunks!(u8);
}
// 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;
}
}

View 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() {}
```

View File

@ -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),

View File

@ -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();

View File

@ -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:

View 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);
}
}

View 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);
}
}

View File

@ -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);
}
}

View File

@ -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() {}

View File

@ -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

View File

@ -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

View File

@ -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

View 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() {}

View 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

View 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() {}

View 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

View 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() {}

View 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

View File

@ -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 {}

View 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() {}

View 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`.

View File

@ -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`");
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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`