Auto merge of #111248 - Dylan-DPC:rollup-lbp0ui3, r=Dylan-DPC

Rollup of 6 pull requests

Successful merges:

 - #103056 (Fix `checked_{add,sub}_duration` incorrectly returning `None` when `other` has more than `i64::MAX` seconds)
 - #108801 (Implement RFC 3348, `c"foo"` literals)
 - #110773 (Reduce MIR dump file count for MIR-opt tests)
 - #110876 (Added default target cpu to `--print target-cpus` output and updated docs)
 - #111068 (Improve check-cfg implementation)
 - #111238 (btree_map: `Cursor{,Mut}::peek_prev` must agree)

Failed merges:

 - #110694 (Implement builtin # syntax and use it for offset_of!(...))

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-05-05 15:59:20 +00:00
commit 4b94c23219
70 changed files with 1180 additions and 540 deletions

View File

@ -1821,6 +1821,8 @@ pub enum LitKind {
/// A byte string (`b"foo"`). Not stored as a symbol because it might be
/// non-utf8, and symbols only allow utf8 strings.
ByteStr(Lrc<[u8]>, StrStyle),
/// A C String (`c"foo"`). Guaranteed to only have `\0` at the end.
CStr(Lrc<[u8]>, StrStyle),
/// A byte char (`b'f'`).
Byte(u8),
/// A character literal (`'a'`).
@ -1875,6 +1877,7 @@ impl LitKind {
// unsuffixed variants
LitKind::Str(..)
| LitKind::ByteStr(..)
| LitKind::CStr(..)
| LitKind::Byte(..)
| LitKind::Char(..)
| LitKind::Int(_, LitIntType::Unsuffixed)

View File

@ -74,6 +74,8 @@ pub enum LitKind {
StrRaw(u8), // raw string delimited by `n` hash symbols
ByteStr,
ByteStrRaw(u8), // raw byte string delimited by `n` hash symbols
CStr,
CStrRaw(u8),
Err,
}
@ -141,6 +143,10 @@ impl fmt::Display for Lit {
delim = "#".repeat(n as usize),
string = symbol
)?,
CStr => write!(f, "c\"{symbol}\"")?,
CStrRaw(n) => {
write!(f, "cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize))?
}
Integer | Float | Bool | Err => write!(f, "{symbol}")?,
}
@ -170,6 +176,7 @@ impl LitKind {
Float => "float",
Str | StrRaw(..) => "string",
ByteStr | ByteStrRaw(..) => "byte string",
CStr | CStrRaw(..) => "C string",
Err => "error",
}
}

View File

@ -2,9 +2,13 @@
use crate::ast::{self, LitKind, MetaItemLit, StrStyle};
use crate::token::{self, Token};
use rustc_lexer::unescape::{byte_from_char, unescape_byte, unescape_char, unescape_literal, Mode};
use rustc_lexer::unescape::{
byte_from_char, unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit,
Mode,
};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
use std::ops::Range;
use std::{ascii, fmt, str};
// Escapes a string, represented as a symbol. Reuses the original symbol,
@ -35,6 +39,7 @@ pub enum LitError {
InvalidFloatSuffix,
NonDecimalFloat(u32),
IntTooLarge(u32),
NulInCStr(Range<usize>),
}
impl LitKind {
@ -158,6 +163,52 @@ impl LitKind {
LitKind::ByteStr(bytes.into(), StrStyle::Raw(n))
}
token::CStr => {
let s = symbol.as_str();
let mut buf = Vec::with_capacity(s.len());
let mut error = Ok(());
unescape_c_string(s, Mode::CStr, &mut |span, c| match c {
Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
error = Err(LitError::NulInCStr(span));
}
Ok(CStrUnit::Byte(b)) => buf.push(b),
Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
Ok(CStrUnit::Char(c)) => {
buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
}
Err(err) => {
if err.is_fatal() {
error = Err(LitError::LexerError);
}
}
});
error?;
buf.push(0);
LitKind::CStr(buf.into(), StrStyle::Cooked)
}
token::CStrRaw(n) => {
let s = symbol.as_str();
let mut buf = Vec::with_capacity(s.len());
let mut error = Ok(());
unescape_c_string(s, Mode::RawCStr, &mut |span, c| match c {
Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
error = Err(LitError::NulInCStr(span));
}
Ok(CStrUnit::Byte(b)) => buf.push(b),
Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
Ok(CStrUnit::Char(c)) => {
buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
}
Err(err) => {
if err.is_fatal() {
error = Err(LitError::LexerError);
}
}
});
error?;
buf.push(0);
LitKind::CStr(buf.into(), StrStyle::Raw(n))
}
token::Err => LitKind::Err,
})
}
@ -191,6 +242,14 @@ impl fmt::Display for LitKind {
string = symbol
)?;
}
LitKind::CStr(ref bytes, StrStyle::Cooked) => {
write!(f, "c\"{}\"", escape_byte_str_symbol(bytes))?
}
LitKind::CStr(ref bytes, StrStyle::Raw(n)) => {
// This can only be valid UTF-8.
let symbol = str::from_utf8(bytes).unwrap();
write!(f, "cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize),)?;
}
LitKind::Int(n, ty) => {
write!(f, "{n}")?;
match ty {
@ -237,6 +296,8 @@ impl MetaItemLit {
LitKind::Str(_, ast::StrStyle::Raw(n)) => token::StrRaw(n),
LitKind::ByteStr(_, ast::StrStyle::Cooked) => token::ByteStr,
LitKind::ByteStr(_, ast::StrStyle::Raw(n)) => token::ByteStrRaw(n),
LitKind::CStr(_, ast::StrStyle::Cooked) => token::CStr,
LitKind::CStr(_, ast::StrStyle::Raw(n)) => token::CStrRaw(n),
LitKind::Byte(_) => token::Byte,
LitKind::Char(_) => token::Char,
LitKind::Int(..) => token::Integer,

View File

@ -572,6 +572,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
}
};
}
gate_all!(c_str_literals, "`c\"..\"` literals are experimental");
gate_all!(
if_let_guard,
"`if let` guards are experimental",

View File

@ -210,6 +210,10 @@ pub fn literal_to_string(lit: token::Lit) -> String {
token::ByteStrRaw(n) => {
format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
}
token::CStr => format!("c\"{symbol}\""),
token::CStrRaw(n) => {
format!("cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize))
}
token::Integer | token::Float | token::Bool | token::Err => symbol.to_string(),
};

View File

@ -5,6 +5,7 @@ use rustc_ast::{Attribute, LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedM
use rustc_ast_pretty::pprust;
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
use rustc_macros::HashStable_Generic;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_session::parse::{feature_err, ParseSess};
@ -581,32 +582,32 @@ pub fn cfg_matches(
) -> bool {
eval_condition(cfg, sess, features, &mut |cfg| {
try_gate_cfg(cfg.name, cfg.span, sess, features);
if let Some(names_valid) = &sess.check_config.names_valid {
if !names_valid.contains(&cfg.name) {
match sess.check_config.expecteds.get(&cfg.name) {
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
sess.buffer_lint_with_diagnostic(
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
"unexpected `cfg` condition value",
BuiltinLintDiagnostics::UnexpectedCfgValue(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
}
None if sess.check_config.exhaustive_names => {
sess.buffer_lint_with_diagnostic(
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
"unexpected `cfg` condition name",
BuiltinLintDiagnostics::UnexpectedCfg((cfg.name, cfg.name_span), None),
BuiltinLintDiagnostics::UnexpectedCfgName(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
}
}
if let Some(value) = cfg.value {
if let Some(values) = &sess.check_config.values_valid.get(&cfg.name) {
if !values.contains(&value) {
sess.buffer_lint_with_diagnostic(
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
"unexpected `cfg` condition value",
BuiltinLintDiagnostics::UnexpectedCfg(
(cfg.name, cfg.name_span),
cfg.value_span.map(|vs| (value, vs)),
),
);
}
}
_ => { /* not unexpected */ }
}
sess.config.contains(&(cfg.name, cfg.value))
})

View File

@ -32,6 +32,10 @@ pub fn expand_concat(
Ok(ast::LitKind::Bool(b)) => {
accumulator.push_str(&b.to_string());
}
Ok(ast::LitKind::CStr(..)) => {
cx.span_err(e.span, "cannot concatenate a C string literal");
has_errors = true;
}
Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => {
cx.emit_err(errors::ConcatBytestr { span: e.span });
has_errors = true;

View File

@ -18,6 +18,11 @@ fn invalid_type_err(
};
let snippet = cx.sess.source_map().span_to_snippet(span).ok();
match ast::LitKind::from_token_lit(token_lit) {
Ok(ast::LitKind::CStr(_, _)) => {
// FIXME(c_str_literals): should concatenation of C string literals
// include the null bytes in the end?
cx.span_err(span, "cannot concatenate C string literals");
}
Ok(ast::LitKind::Char(_)) => {
let sugg =
snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });

View File

@ -2249,7 +2249,7 @@ extern "C" {
pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool;
pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine);
pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine, cpu: *const c_char);
pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t;
pub fn LLVMRustGetTargetFeature(
T: &TargetMachine,

View File

@ -329,7 +329,14 @@ pub(crate) fn print(req: PrintRequest, sess: &Session) {
require_inited();
let tm = create_informational_target_machine(sess);
match req {
PrintRequest::TargetCPUs => unsafe { llvm::LLVMRustPrintTargetCPUs(tm) },
PrintRequest::TargetCPUs => {
// SAFETY generate a C compatible string from a byte slice to pass
// the target CPU name into LLVM, the lifetime of the reference is
// at least as long as the C function
let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref()))
.unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e));
unsafe { llvm::LLVMRustPrintTargetCPUs(tm, cpu_cstring.as_ptr()) };
}
PrintRequest::TargetFeatures => print_target_features(sess, tm),
_ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
}

View File

@ -61,6 +61,8 @@ impl FromInternal<token::LitKind> for LitKind {
token::StrRaw(n) => LitKind::StrRaw(n),
token::ByteStr => LitKind::ByteStr,
token::ByteStrRaw(n) => LitKind::ByteStrRaw(n),
token::CStr => LitKind::CStr,
token::CStrRaw(n) => LitKind::CStrRaw(n),
token::Err => LitKind::Err,
token::Bool => unreachable!(),
}
@ -78,6 +80,8 @@ impl ToInternal<token::LitKind> for LitKind {
LitKind::StrRaw(n) => token::StrRaw(n),
LitKind::ByteStr => token::ByteStr,
LitKind::ByteStrRaw(n) => token::ByteStrRaw(n),
LitKind::CStr => token::CStr,
LitKind::CStrRaw(n) => token::CStrRaw(n),
LitKind::Err => token::Err,
}
}
@ -436,6 +440,8 @@ impl server::FreeFunctions for Rustc<'_, '_> {
| token::LitKind::StrRaw(_)
| token::LitKind::ByteStr
| token::LitKind::ByteStrRaw(_)
| token::LitKind::CStr
| token::LitKind::CStrRaw(_)
| token::LitKind::Err => return Err(()),
token::LitKind::Integer | token::LitKind::Float => {}
}

View File

@ -313,6 +313,8 @@ declare_features! (
(active, async_closure, "1.37.0", Some(62290), None),
/// Allows async functions to be declared, implemented, and used in traits.
(active, async_fn_in_trait, "1.66.0", Some(91611), None),
/// Allows `c"foo"` literals.
(active, c_str_literals, "CURRENT_RUSTC_VERSION", Some(105723), None),
/// Treat `extern "C"` function as nounwind.
(active, c_unwind, "1.52.0", Some(74990), None),
/// Allows using C-variadics.

View File

@ -333,6 +333,7 @@ language_item_table! {
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
String, sym::String, string, Target::Struct, GenericRequirement::None;
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
}
pub enum GenericRequirement {

View File

@ -1300,6 +1300,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
opt_ty.unwrap_or_else(|| self.next_float_var())
}
ast::LitKind::Bool(_) => tcx.types.bool,
ast::LitKind::CStr(_, _) => tcx.mk_imm_ref(
tcx.lifetimes.re_static,
tcx.type_of(tcx.require_lang_item(hir::LangItem::CStr, Some(lit.span)))
.skip_binder(),
),
ast::LitKind::Err => tcx.ty_error_misc(),
}
}

View File

@ -9,11 +9,12 @@ use rustc_data_structures::OnDrop;
use rustc_errors::registry::Registry;
use rustc_errors::{ErrorGuaranteed, Handler};
use rustc_lint::LintStore;
use rustc_middle::ty;
use rustc_middle::{bug, ty};
use rustc_parse::maybe_new_parser_from_source_str;
use rustc_query_impl::QueryCtxt;
use rustc_query_system::query::print_query_stack;
use rustc_session::config::{self, CheckCfg, ErrorOutputType, Input, OutputFilenames};
use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames};
use rustc_session::config::{CheckCfg, ExpectedValues};
use rustc_session::lint;
use rustc_session::parse::{CrateConfig, ParseSess};
use rustc_session::Session;
@ -121,9 +122,9 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
rustc_span::create_default_session_if_not_set_then(move |_| {
let mut cfg = CheckCfg::default();
let mut check_cfg = CheckCfg::default();
'specs: for s in specs {
for s in specs {
let sess = ParseSess::with_silent_emitter(Some(format!(
"this error occurred on the command line: `--check-cfg={s}`"
)));
@ -137,40 +138,54 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
s
),
);
)
};
}
let expected_error = || {
error!(
"expected `names(name1, name2, ... nameN)` or \
`values(name, \"value1\", \"value2\", ... \"valueN\")`"
)
};
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
Ok(mut parser) => match parser.parse_meta_item() {
Ok(meta_item) if parser.token == token::Eof => {
if let Some(args) = meta_item.meta_item_list() {
if meta_item.has_name(sym::names) {
let names_valid =
cfg.names_valid.get_or_insert_with(|| FxHashSet::default());
check_cfg.exhaustive_names = true;
for arg in args {
if arg.is_word() && arg.ident().is_some() {
let ident = arg.ident().expect("multi-segment cfg key");
names_valid.insert(ident.name.to_string());
check_cfg
.expecteds
.entry(ident.name.to_string())
.or_insert(ExpectedValues::Any);
} else {
error!("`names()` arguments must be simple identifiers");
}
}
continue 'specs;
} else if meta_item.has_name(sym::values) {
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");
let ident_values = cfg
.values_valid
let expected_values = check_cfg
.expecteds
.entry(ident.name.to_string())
.or_insert_with(|| FxHashSet::default());
.or_insert_with(|| {
ExpectedValues::Some(FxHashSet::default())
});
let ExpectedValues::Some(expected_values) = expected_values else {
bug!("shoudn't be possible")
};
for val in values {
if let Some(LitKind::Str(s, _)) =
val.lit().map(|lit| &lit.kind)
{
ident_values.insert(s.to_string());
expected_values.insert(Some(s.to_string()));
} else {
error!(
"`values()` arguments must be string literals"
@ -178,35 +193,40 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
}
}
continue 'specs;
if values.is_empty() {
expected_values.insert(None);
}
} else {
error!(
"`values()` first argument must be a simple identifier"
);
}
} else if args.is_empty() {
cfg.well_known_values = true;
continue 'specs;
check_cfg.exhaustive_values = true;
} else {
expected_error();
}
} else {
expected_error();
}
} else {
expected_error();
}
}
Ok(..) => {}
Err(err) => err.cancel(),
Ok(..) => expected_error(),
Err(err) => {
err.cancel();
expected_error();
}
},
Err(errs) => drop(errs),
Err(errs) => {
drop(errs);
expected_error();
}
}
error!(
"expected `names(name1, name2, ... nameN)` or \
`values(name, \"value1\", \"value2\", ... \"valueN\")`"
);
}
if let Some(names_valid) = &mut cfg.names_valid {
names_valid.extend(cfg.values_valid.keys().cloned());
}
cfg
check_cfg
})
}

View File

@ -186,12 +186,16 @@ pub enum LiteralKind {
Str { terminated: bool },
/// "b"abc"", "b"abc"
ByteStr { terminated: bool },
/// `c"abc"`, `c"abc`
CStr { terminated: bool },
/// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a". `None` indicates
/// an invalid literal.
RawStr { n_hashes: Option<u8> },
/// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a". `None`
/// indicates an invalid literal.
RawByteStr { n_hashes: Option<u8> },
/// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` indicates an invalid literal.
RawCStr { n_hashes: Option<u8> },
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
@ -357,39 +361,18 @@ impl Cursor<'_> {
},
// Byte literal, byte string literal, raw byte string literal or identifier.
'b' => match (self.first(), self.second()) {
('\'', _) => {
self.bump();
let terminated = self.single_quoted_string();
let suffix_start = self.pos_within_token();
if terminated {
self.eat_literal_suffix();
}
let kind = Byte { terminated };
Literal { kind, suffix_start }
}
('"', _) => {
self.bump();
let terminated = self.double_quoted_string();
let suffix_start = self.pos_within_token();
if terminated {
self.eat_literal_suffix();
}
let kind = ByteStr { terminated };
Literal { kind, suffix_start }
}
('r', '"') | ('r', '#') => {
self.bump();
let res = self.raw_double_quoted_string(2);
let suffix_start = self.pos_within_token();
if res.is_ok() {
self.eat_literal_suffix();
}
let kind = RawByteStr { n_hashes: res.ok() };
Literal { kind, suffix_start }
}
_ => self.ident_or_unknown_prefix(),
},
'b' => self.c_or_byte_string(
|terminated| ByteStr { terminated },
|n_hashes| RawByteStr { n_hashes },
Some(|terminated| Byte { terminated }),
),
// c-string literal, raw c-string literal or identifier.
'c' => self.c_or_byte_string(
|terminated| CStr { terminated },
|n_hashes| RawCStr { n_hashes },
None,
),
// Identifier (this should be checked after other variant that can
// start as identifier).
@ -553,6 +536,47 @@ impl Cursor<'_> {
}
}
fn c_or_byte_string(
&mut self,
mk_kind: impl FnOnce(bool) -> LiteralKind,
mk_kind_raw: impl FnOnce(Option<u8>) -> LiteralKind,
single_quoted: Option<fn(bool) -> LiteralKind>,
) -> TokenKind {
match (self.first(), self.second(), single_quoted) {
('\'', _, Some(mk_kind)) => {
self.bump();
let terminated = self.single_quoted_string();
let suffix_start = self.pos_within_token();
if terminated {
self.eat_literal_suffix();
}
let kind = mk_kind(terminated);
Literal { kind, suffix_start }
}
('"', _, _) => {
self.bump();
let terminated = self.double_quoted_string();
let suffix_start = self.pos_within_token();
if terminated {
self.eat_literal_suffix();
}
let kind = mk_kind(terminated);
Literal { kind, suffix_start }
}
('r', '"', _) | ('r', '#', _) => {
self.bump();
let res = self.raw_double_quoted_string(2);
let suffix_start = self.pos_within_token();
if res.is_ok() {
self.eat_literal_suffix();
}
let kind = mk_kind_raw(res.ok());
Literal { kind, suffix_start }
}
_ => self.ident_or_unknown_prefix(),
}
}
fn number(&mut self, first_digit: char) -> LiteralKind {
debug_assert!('0' <= self.prev() && self.prev() <= '9');
let mut base = Base::Decimal;

View File

@ -86,10 +86,45 @@ where
let res = unescape_char_or_byte(&mut chars, mode == Mode::Byte);
callback(0..(src.len() - chars.as_str().len()), res);
}
Mode::Str | Mode::ByteStr => unescape_str_or_byte_str(src, mode == Mode::ByteStr, callback),
Mode::Str | Mode::ByteStr => unescape_str_common(src, mode, callback),
Mode::RawStr | Mode::RawByteStr => {
unescape_raw_str_or_raw_byte_str(src, mode == Mode::RawByteStr, callback)
}
Mode::CStr | Mode::RawCStr => unreachable!(),
}
}
/// A unit within CStr. Must not be a nul character.
pub enum CStrUnit {
Byte(u8),
Char(char),
}
impl From<u8> for CStrUnit {
fn from(value: u8) -> Self {
CStrUnit::Byte(value)
}
}
impl From<char> for CStrUnit {
fn from(value: char) -> Self {
CStrUnit::Char(value)
}
}
pub fn unescape_c_string<F>(src: &str, mode: Mode, callback: &mut F)
where
F: FnMut(Range<usize>, Result<CStrUnit, EscapeError>),
{
if mode == Mode::RawCStr {
unescape_raw_str_or_raw_byte_str(
src,
mode.characters_should_be_ascii(),
&mut |r, result| callback(r, result.map(CStrUnit::Char)),
);
} else {
unescape_str_common(src, mode, callback);
}
}
@ -114,34 +149,69 @@ pub enum Mode {
ByteStr,
RawStr,
RawByteStr,
CStr,
RawCStr,
}
impl Mode {
pub fn in_double_quotes(self) -> bool {
match self {
Mode::Str | Mode::ByteStr | Mode::RawStr | Mode::RawByteStr => true,
Mode::Str
| Mode::ByteStr
| Mode::RawStr
| Mode::RawByteStr
| Mode::CStr
| Mode::RawCStr => true,
Mode::Char | Mode::Byte => false,
}
}
pub fn is_byte(self) -> bool {
/// Non-byte literals should have `\xXX` escapes that are within the ASCII range.
pub fn ascii_escapes_should_be_ascii(self) -> bool {
match self {
Mode::Char | Mode::Str | Mode::RawStr => true,
Mode::Byte | Mode::ByteStr | Mode::RawByteStr | Mode::CStr | Mode::RawCStr => false,
}
}
/// Whether characters within the literal must be within the ASCII range
pub fn characters_should_be_ascii(self) -> bool {
match self {
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true,
Mode::Char | Mode::Str | Mode::RawStr => false,
Mode::Char | Mode::Str | Mode::RawStr | Mode::CStr | Mode::RawCStr => false,
}
}
/// Byte literals do not allow unicode escape.
pub fn is_unicode_escape_disallowed(self) -> bool {
match self {
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true,
Mode::Char | Mode::Str | Mode::RawStr | Mode::CStr | Mode::RawCStr => false,
}
}
pub fn prefix_noraw(self) -> &'static str {
match self {
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => "b",
Mode::CStr | Mode::RawCStr => "c",
Mode::Char | Mode::Str | Mode::RawStr => "",
}
}
}
fn scan_escape(chars: &mut Chars<'_>, is_byte: bool) -> Result<char, EscapeError> {
fn scan_escape<T: From<u8> + From<char>>(
chars: &mut Chars<'_>,
mode: Mode,
) -> Result<T, EscapeError> {
// Previous character was '\\', unescape what follows.
let res = match chars.next().ok_or(EscapeError::LoneSlash)? {
'"' => '"',
'n' => '\n',
'r' => '\r',
't' => '\t',
'\\' => '\\',
'\'' => '\'',
'0' => '\0',
'"' => b'"',
'n' => b'\n',
'r' => b'\r',
't' => b'\t',
'\\' => b'\\',
'\'' => b'\'',
'0' => b'\0',
'x' => {
// Parse hexadecimal character code.
@ -154,76 +224,78 @@ fn scan_escape(chars: &mut Chars<'_>, is_byte: bool) -> Result<char, EscapeError
let value = hi * 16 + lo;
// For a non-byte literal verify that it is within ASCII range.
if !is_byte && !is_ascii(value) {
if mode.ascii_escapes_should_be_ascii() && !is_ascii(value) {
return Err(EscapeError::OutOfRangeHexEscape);
}
let value = value as u8;
value as char
value as u8
}
'u' => {
// We've parsed '\u', now we have to parse '{..}'.
if chars.next() != Some('{') {
return Err(EscapeError::NoBraceInUnicodeEscape);
}
// First character must be a hexadecimal digit.
let mut n_digits = 1;
let mut value: u32 = match chars.next().ok_or(EscapeError::UnclosedUnicodeEscape)? {
'_' => return Err(EscapeError::LeadingUnderscoreUnicodeEscape),
'}' => return Err(EscapeError::EmptyUnicodeEscape),
c => c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?,
};
// First character is valid, now parse the rest of the number
// and closing brace.
loop {
match chars.next() {
None => return Err(EscapeError::UnclosedUnicodeEscape),
Some('_') => continue,
Some('}') => {
if n_digits > 6 {
return Err(EscapeError::OverlongUnicodeEscape);
}
// Incorrect syntax has higher priority for error reporting
// than unallowed value for a literal.
if is_byte {
return Err(EscapeError::UnicodeEscapeInByte);
}
break std::char::from_u32(value).ok_or_else(|| {
if value > 0x10FFFF {
EscapeError::OutOfRangeUnicodeEscape
} else {
EscapeError::LoneSurrogateUnicodeEscape
}
})?;
}
Some(c) => {
let digit: u32 =
c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?;
n_digits += 1;
if n_digits > 6 {
// Stop updating value since we're sure that it's incorrect already.
continue;
}
value = value * 16 + digit;
}
};
}
}
'u' => return scan_unicode(chars, mode.is_unicode_escape_disallowed()).map(Into::into),
_ => return Err(EscapeError::InvalidEscape),
};
Ok(res)
Ok(res.into())
}
fn scan_unicode(
chars: &mut Chars<'_>,
is_unicode_escape_disallowed: bool,
) -> Result<char, EscapeError> {
// We've parsed '\u', now we have to parse '{..}'.
if chars.next() != Some('{') {
return Err(EscapeError::NoBraceInUnicodeEscape);
}
// First character must be a hexadecimal digit.
let mut n_digits = 1;
let mut value: u32 = match chars.next().ok_or(EscapeError::UnclosedUnicodeEscape)? {
'_' => return Err(EscapeError::LeadingUnderscoreUnicodeEscape),
'}' => return Err(EscapeError::EmptyUnicodeEscape),
c => c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?,
};
// First character is valid, now parse the rest of the number
// and closing brace.
loop {
match chars.next() {
None => return Err(EscapeError::UnclosedUnicodeEscape),
Some('_') => continue,
Some('}') => {
if n_digits > 6 {
return Err(EscapeError::OverlongUnicodeEscape);
}
// Incorrect syntax has higher priority for error reporting
// than unallowed value for a literal.
if is_unicode_escape_disallowed {
return Err(EscapeError::UnicodeEscapeInByte);
}
break std::char::from_u32(value).ok_or_else(|| {
if value > 0x10FFFF {
EscapeError::OutOfRangeUnicodeEscape
} else {
EscapeError::LoneSurrogateUnicodeEscape
}
});
}
Some(c) => {
let digit: u32 = c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?;
n_digits += 1;
if n_digits > 6 {
// Stop updating value since we're sure that it's incorrect already.
continue;
}
value = value * 16 + digit;
}
};
}
}
#[inline]
fn ascii_check(c: char, is_byte: bool) -> Result<char, EscapeError> {
if is_byte && !c.is_ascii() {
fn ascii_check(c: char, characters_should_be_ascii: bool) -> Result<char, EscapeError> {
if characters_should_be_ascii && !c.is_ascii() {
// Byte literal can't be a non-ascii character.
Err(EscapeError::NonAsciiCharInByte)
} else {
@ -234,7 +306,7 @@ fn ascii_check(c: char, is_byte: bool) -> Result<char, EscapeError> {
fn unescape_char_or_byte(chars: &mut Chars<'_>, is_byte: bool) -> Result<char, EscapeError> {
let c = chars.next().ok_or(EscapeError::ZeroChars)?;
let res = match c {
'\\' => scan_escape(chars, is_byte),
'\\' => scan_escape(chars, if is_byte { Mode::Byte } else { Mode::Char }),
'\n' | '\t' | '\'' => Err(EscapeError::EscapeOnlyChar),
'\r' => Err(EscapeError::BareCarriageReturn),
_ => ascii_check(c, is_byte),
@ -247,9 +319,9 @@ fn unescape_char_or_byte(chars: &mut Chars<'_>, is_byte: bool) -> Result<char, E
/// Takes a contents of a string literal (without quotes) and produces a
/// sequence of escaped characters or errors.
fn unescape_str_or_byte_str<F>(src: &str, is_byte: bool, callback: &mut F)
fn unescape_str_common<F, T: From<u8> + From<char>>(src: &str, mode: Mode, callback: &mut F)
where
F: FnMut(Range<usize>, Result<char, EscapeError>),
F: FnMut(Range<usize>, Result<T, EscapeError>),
{
let mut chars = src.chars();
@ -266,47 +338,49 @@ where
// if unescaped '\' character is followed by '\n'.
// For details see [Rust language reference]
// (https://doc.rust-lang.org/reference/tokens.html#string-literals).
skip_ascii_whitespace(&mut chars, start, callback);
skip_ascii_whitespace(&mut chars, start, &mut |range, err| {
callback(range, Err(err))
});
continue;
}
_ => scan_escape(&mut chars, is_byte),
_ => scan_escape::<T>(&mut chars, mode),
}
}
'\n' => Ok('\n'),
'\t' => Ok('\t'),
'\n' => Ok(b'\n'.into()),
'\t' => Ok(b'\t'.into()),
'"' => Err(EscapeError::EscapeOnlyChar),
'\r' => Err(EscapeError::BareCarriageReturn),
_ => ascii_check(c, is_byte),
_ => ascii_check(c, mode.characters_should_be_ascii()).map(Into::into),
};
let end = src.len() - chars.as_str().len();
callback(start..end, res);
callback(start..end, res.map(Into::into));
}
}
fn skip_ascii_whitespace<F>(chars: &mut Chars<'_>, start: usize, callback: &mut F)
where
F: FnMut(Range<usize>, Result<char, EscapeError>),
{
let tail = chars.as_str();
let first_non_space = tail
.bytes()
.position(|b| b != b' ' && b != b'\t' && b != b'\n' && b != b'\r')
.unwrap_or(tail.len());
if tail[1..first_non_space].contains('\n') {
// The +1 accounts for the escaping slash.
let end = start + first_non_space + 1;
callback(start..end, Err(EscapeError::MultipleSkippedLinesWarning));
}
let tail = &tail[first_non_space..];
if let Some(c) = tail.chars().nth(0) {
if c.is_whitespace() {
// For error reporting, we would like the span to contain the character that was not
// skipped. The +1 is necessary to account for the leading \ that started the escape.
let end = start + first_non_space + c.len_utf8() + 1;
callback(start..end, Err(EscapeError::UnskippedWhitespaceWarning));
}
}
*chars = tail.chars();
fn skip_ascii_whitespace<F>(chars: &mut Chars<'_>, start: usize, callback: &mut F)
where
F: FnMut(Range<usize>, EscapeError),
{
let tail = chars.as_str();
let first_non_space = tail
.bytes()
.position(|b| b != b' ' && b != b'\t' && b != b'\n' && b != b'\r')
.unwrap_or(tail.len());
if tail[1..first_non_space].contains('\n') {
// The +1 accounts for the escaping slash.
let end = start + first_non_space + 1;
callback(start..end, EscapeError::MultipleSkippedLinesWarning);
}
let tail = &tail[first_non_space..];
if let Some(c) = tail.chars().nth(0) {
if c.is_whitespace() {
// For error reporting, we would like the span to contain the character that was not
// skipped. The +1 is necessary to account for the leading \ that started the escape.
let end = start + first_non_space + c.len_utf8() + 1;
callback(start..end, EscapeError::UnskippedWhitespaceWarning);
}
}
*chars = tail.chars();
}
/// Takes a contents of a string literal (without quotes) and produces a

View File

@ -63,6 +63,7 @@ use rustc_middle::ty::layout::{LayoutError, LayoutOf};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
use rustc_session::config::ExpectedValues;
use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
@ -3306,16 +3307,15 @@ impl EarlyLintPass for UnexpectedCfgs {
let cfg = &cx.sess().parse_sess.config;
let check_cfg = &cx.sess().parse_sess.check_config;
for &(name, value) in cfg {
if let Some(names_valid) = &check_cfg.names_valid && !names_valid.contains(&name){
cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName {
name,
});
}
if let Some(value) = value && let Some(values) = check_cfg.values_valid.get(&name) && !values.contains(&value) {
cx.emit_lint(
UNEXPECTED_CFGS,
BuiltinUnexpectedCliConfigValue { name, value },
);
match check_cfg.expecteds.get(&name) {
Some(ExpectedValues::Some(values)) if !values.contains(&value) => {
let value = value.unwrap_or(kw::Empty);
cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigValue { name, value });
}
None if check_cfg.exhaustive_names => {
cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName { name });
}
_ => { /* expected */ }
}
}
}

View File

@ -36,6 +36,7 @@ use rustc_middle::middle::stability;
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt};
use rustc_session::config::ExpectedValues;
use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
use rustc_session::Session;
@ -768,22 +769,52 @@ pub trait LintContext: Sized {
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((name, name_span), None) => {
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");
};
let possibilities: Vec<Symbol> = names_valid.iter().map(|s| *s).collect();
BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => {
let possibilities: Vec<Symbol> = sess.parse_sess.check_config.expecteds.keys().map(|s| *s).collect();
// Suggest the most probable if we found one
if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
db.span_suggestion(name_span, "did you mean", best_match, Applicability::MaybeIncorrect);
if let Some(ExpectedValues::Some(best_match_values)) =
sess.parse_sess.check_config.expecteds.get(&best_match) {
let mut possibilities = best_match_values.iter()
.flatten()
.map(Symbol::as_str)
.collect::<Vec<_>>();
possibilities.sort();
if let Some((value, value_span)) = value {
if best_match_values.contains(&Some(value)) {
db.span_suggestion(name_span, "there is a config with a similar name and value", best_match, Applicability::MaybeIncorrect);
} else if best_match_values.contains(&None) {
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and no value", best_match, Applicability::MaybeIncorrect);
} else if let Some(first_value) = possibilities.first() {
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", format!("{best_match} = \"{first_value}\""), Applicability::MaybeIncorrect);
} else {
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", best_match, Applicability::MaybeIncorrect);
};
} else {
db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
}
if !possibilities.is_empty() {
let possibilities = possibilities.join("`, `");
db.help(format!("expected values for `{best_match}` are: `{possibilities}`"));
}
} else {
db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
}
}
},
BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), Some((value, value_span))) => {
let Some(values) = &sess.parse_sess.check_config.values_valid.get(&name) else {
BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => {
let Some(ExpectedValues::Some(values)) = &sess.parse_sess.check_config.expecteds.get(&name) else {
bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values");
};
let possibilities: Vec<Symbol> = values.iter().map(|&s| s).collect();
let mut have_none_possibility = false;
let possibilities: Vec<Symbol> = values.iter()
.inspect(|a| have_none_possibility |= a.is_none())
.copied()
.flatten()
.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
@ -792,17 +823,24 @@ pub trait LintContext: Sized {
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}"));
let possibilities = possibilities.join("`, `");
let none = if have_none_possibility { "(none), " } else { "" };
db.note(format!("expected values for `{name}` are: {none}`{possibilities}`"));
}
// Suggest the most probable if we found one
if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) {
db.span_suggestion(value_span, "did you mean", format!("\"{best_match}\""), Applicability::MaybeIncorrect);
if let Some((value, value_span)) = value {
// Suggest the most probable if we found one
if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) {
db.span_suggestion(value_span, "there is a expected value with a similar name", format!("\"{best_match}\""), Applicability::MaybeIncorrect);
}
} else if let &[first_possibility] = &possibilities[..] {
db.span_suggestion(name_span.shrink_to_hi(), "specify a config value", format!(" = \"{first_possibility}\""), Applicability::MaybeIncorrect);
}
} else {
} else if have_none_possibility {
db.note(format!("no expected value for `{name}`"));
if name != sym::feature {
if let Some((_value, value_span)) = value {
db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect);
}
}

View File

@ -496,7 +496,8 @@ pub enum BuiltinLintDiagnostics {
BreakWithLabelAndLoop(Span),
NamedAsmLabel(String),
UnicodeTextFlow(Span, String),
UnexpectedCfg((Symbol, Span), Option<(Symbol, Span)>),
UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>),
UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>),
DeprecatedWhereclauseLocation(Span, String),
SingleUseLifetime {
/// Span of the parameter which declares this lifetime.

View File

@ -307,7 +307,7 @@ static size_t getLongestEntryLength(ArrayRef<KV> Table) {
return MaxLen;
}
extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) {
extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, const char* TargetCPU) {
const TargetMachine *Target = unwrap(TM);
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
const Triple::ArchType HostArch = Triple(sys::getDefaultTargetTriple()).getArch();
@ -323,9 +323,18 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) {
printf(" %-*s - Select the CPU of the current host (currently %.*s).\n",
MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data());
}
for (auto &CPU : CPUTable)
printf(" %-*s\n", MaxCPULen, CPU.Key);
printf("\n");
for (auto &CPU : CPUTable) {
// Compare cpu against current target to label the default
if (strcmp(CPU.Key, TargetCPU) == 0) {
printf(" %-*s - This is the default target CPU"
" for the current build target (currently %s).",
MaxCPULen, CPU.Key, Target->getTargetTriple().str().c_str());
}
else {
printf(" %-*s", MaxCPULen, CPU.Key);
}
printf("\n");
}
}
extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {

View File

@ -146,6 +146,12 @@ pub(crate) fn lit_to_mir_constant<'tcx>(
let id = tcx.allocate_bytes(data);
ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx))
}
(ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) =>
{
let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
let allocation = tcx.mk_const_alloc(allocation);
ConstValue::Slice { data: allocation, start: 0, end: data.len() }
}
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1)))
}

View File

@ -1,3 +1,5 @@
use std::ops::Range;
use crate::errors;
use crate::lexer::unicode_chars::UNICODE_ARRAY;
use crate::make_unclosed_delims_error;
@ -6,7 +8,7 @@ use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::util::unicode::contains_text_flow_control_chars;
use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey};
use rustc_lexer::unescape::{self, Mode};
use rustc_lexer::unescape::{self, EscapeError, Mode};
use rustc_lexer::Cursor;
use rustc_lexer::{Base, DocStyle, RawStrError};
use rustc_session::lint::builtin::{
@ -204,6 +206,9 @@ impl<'a> StringReader<'a> {
rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
let suffix_start = start + BytePos(suffix_start);
let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
if let token::LitKind::CStr | token::LitKind::CStrRaw(_) = kind {
self.sess.gated_spans.gate(sym::c_str_literals, self.mk_sp(start, self.pos));
}
let suffix = if suffix_start < self.pos {
let string = self.str_from(suffix_start);
if string == "_" {
@ -415,6 +420,16 @@ impl<'a> StringReader<'a> {
}
self.cook_quoted(token::ByteStr, Mode::ByteStr, start, end, 2, 1) // b" "
}
rustc_lexer::LiteralKind::CStr { terminated } => {
if !terminated {
self.sess.span_diagnostic.span_fatal_with_code(
self.mk_sp(start + BytePos(1), end),
"unterminated C string",
error_code!(E0767),
)
}
self.cook_c_string(token::CStr, Mode::CStr, start, end, 2, 1) // c" "
}
rustc_lexer::LiteralKind::RawStr { n_hashes } => {
if let Some(n_hashes) = n_hashes {
let n = u32::from(n_hashes);
@ -433,6 +448,15 @@ impl<'a> StringReader<'a> {
self.report_raw_str_error(start, 2);
}
}
rustc_lexer::LiteralKind::RawCStr { n_hashes } => {
if let Some(n_hashes) = n_hashes {
let n = u32::from(n_hashes);
let kind = token::CStrRaw(n_hashes);
self.cook_c_string(kind, Mode::RawCStr, start, end, 3 + n, 1 + n) // cr##" "##
} else {
self.report_raw_str_error(start, 2);
}
}
rustc_lexer::LiteralKind::Int { base, empty_int } => {
if empty_int {
let span = self.mk_sp(start, end);
@ -648,7 +672,7 @@ impl<'a> StringReader<'a> {
self.sess.emit_fatal(errors::TooManyHashes { span: self.mk_sp(start, self.pos), num });
}
fn cook_quoted(
fn cook_common(
&self,
kind: token::LitKind,
mode: Mode,
@ -656,12 +680,13 @@ impl<'a> StringReader<'a> {
end: BytePos,
prefix_len: u32,
postfix_len: u32,
unescape: fn(&str, Mode, &mut dyn FnMut(Range<usize>, Result<(), EscapeError>)),
) -> (token::LitKind, Symbol) {
let mut has_fatal_err = false;
let content_start = start + BytePos(prefix_len);
let content_end = end - BytePos(postfix_len);
let lit_content = self.str_from_to(content_start, content_end);
unescape::unescape_literal(lit_content, mode, &mut |range, result| {
unescape(lit_content, mode, &mut |range, result| {
// Here we only check for errors. The actual unescaping is done later.
if let Err(err) = result {
let span_with_quotes = self.mk_sp(start, end);
@ -692,6 +717,38 @@ impl<'a> StringReader<'a> {
(token::Err, self.symbol_from_to(start, end))
}
}
fn cook_quoted(
&self,
kind: token::LitKind,
mode: Mode,
start: BytePos,
end: BytePos,
prefix_len: u32,
postfix_len: u32,
) -> (token::LitKind, Symbol) {
self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
unescape::unescape_literal(src, mode, &mut |span, result| {
callback(span, result.map(drop))
})
})
}
fn cook_c_string(
&self,
kind: token::LitKind,
mode: Mode,
start: BytePos,
end: BytePos,
prefix_len: u32,
postfix_len: u32,
) -> (token::LitKind, Symbol) {
self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
unescape::unescape_c_string(src, mode, &mut |span, result| {
callback(span, result.map(drop))
})
})
}
}
pub fn nfc_normalize(string: &str) -> Symbol {

View File

@ -78,8 +78,7 @@ pub(crate) fn emit_unescape_error(
}
};
let sugg = sugg.unwrap_or_else(|| {
let is_byte = mode.is_byte();
let prefix = if is_byte { "b" } else { "" };
let prefix = mode.prefix_noraw();
let mut escaped = String::with_capacity(lit.len());
let mut chrs = lit.chars().peekable();
while let Some(first) = chrs.next() {
@ -97,7 +96,11 @@ pub(crate) fn emit_unescape_error(
};
}
let sugg = format!("{prefix}\"{escaped}\"");
MoreThanOneCharSugg::Quotes { span: span_with_quotes, is_byte, sugg }
MoreThanOneCharSugg::Quotes {
span: span_with_quotes,
is_byte: mode == Mode::Byte,
sugg,
}
});
handler.emit_err(UnescapeError::MoreThanOneChar {
span: span_with_quotes,
@ -112,7 +115,7 @@ pub(crate) fn emit_unescape_error(
char_span,
escaped_sugg: c.escape_default().to_string(),
escaped_msg: escaped_char(c),
byte: mode.is_byte(),
byte: mode == Mode::Byte,
});
}
EscapeError::BareCarriageReturn => {
@ -126,12 +129,15 @@ pub(crate) fn emit_unescape_error(
EscapeError::InvalidEscape => {
let (c, span) = last_char();
let label =
if mode.is_byte() { "unknown byte escape" } else { "unknown character escape" };
let label = if mode == Mode::Byte || mode == Mode::ByteStr {
"unknown byte escape"
} else {
"unknown character escape"
};
let ec = escaped_char(c);
let mut diag = handler.struct_span_err(span, format!("{}: `{}`", label, ec));
diag.span_label(span, label);
if c == '{' || c == '}' && !mode.is_byte() {
if c == '{' || c == '}' && matches!(mode, Mode::Str | Mode::RawStr) {
diag.help(
"if used in a formatting string, curly braces are escaped with `{{` and `}}`",
);
@ -141,7 +147,7 @@ pub(crate) fn emit_unescape_error(
version control settings",
);
} else {
if !mode.is_byte() {
if mode == Mode::Str || mode == Mode::Char {
diag.span_suggestion(
span_with_quotes,
"if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal",

View File

@ -1870,6 +1870,7 @@ impl<'a> Parser<'a> {
let recovered = self.recover_after_dot();
let token = recovered.as_ref().unwrap_or(&self.token);
let span = token.span;
token::Lit::from_token(token).map(|token_lit| {
self.bump();
(token_lit, span)

View File

@ -101,3 +101,5 @@ session_invalid_int_literal_width = invalid width `{$width}` for integer literal
.help = valid widths are 8, 16, 32, 64 and 128
session_optimization_fuel_exhausted = optimization-fuel-exhausted: {$msg}
session_nul_in_c_str = null characters in C string literals are not supported

View File

@ -1064,37 +1064,76 @@ pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig
/// The parsed `--check-cfg` options
pub struct CheckCfg<T = String> {
/// The set of all `names()`, if None no name checking is performed
pub names_valid: Option<FxHashSet<T>>,
/// Is well known names activated
pub exhaustive_names: bool,
/// Is well known values activated
pub well_known_values: bool,
/// The set of all `values()`
pub values_valid: FxHashMap<T, FxHashSet<T>>,
pub exhaustive_values: bool,
/// All the expected values for a config name
pub expecteds: FxHashMap<T, ExpectedValues<T>>,
}
impl<T> Default for CheckCfg<T> {
fn default() -> Self {
CheckCfg {
names_valid: Default::default(),
values_valid: Default::default(),
well_known_values: false,
exhaustive_names: false,
exhaustive_values: false,
expecteds: FxHashMap::default(),
}
}
}
impl<T> CheckCfg<T> {
fn map_data<O: Eq + Hash>(&self, f: impl Fn(&T) -> O) -> CheckCfg<O> {
fn map_data<O: Eq + Hash>(self, f: impl Fn(T) -> O) -> CheckCfg<O> {
CheckCfg {
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()))
exhaustive_names: self.exhaustive_names,
exhaustive_values: self.exhaustive_values,
expecteds: self
.expecteds
.into_iter()
.map(|(name, values)| {
(
f(name),
match values {
ExpectedValues::Some(values) => ExpectedValues::Some(
values.into_iter().map(|b| b.map(|b| f(b))).collect(),
),
ExpectedValues::Any => ExpectedValues::Any,
},
)
})
.collect(),
well_known_values: self.well_known_values,
}
}
}
pub enum ExpectedValues<T> {
Some(FxHashSet<Option<T>>),
Any,
}
impl<T: Eq + Hash> ExpectedValues<T> {
fn insert(&mut self, value: T) -> bool {
match self {
ExpectedValues::Some(expecteds) => expecteds.insert(Some(value)),
ExpectedValues::Any => false,
}
}
}
impl<T: Eq + Hash> Extend<T> for ExpectedValues<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
match self {
ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(Some)),
ExpectedValues::Any => {}
}
}
}
impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues<T> {
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
match self {
ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(|a| Some(*a))),
ExpectedValues::Any => {}
}
}
}
@ -1103,58 +1142,27 @@ impl<T> CheckCfg<T> {
/// `rustc_interface::interface::Config` accepts this in the compiler configuration,
/// but the symbol interner is not yet set up then, so we must convert it later.
pub fn to_crate_check_config(cfg: CheckCfg) -> CrateCheckConfig {
cfg.map_data(|s| Symbol::intern(s))
cfg.map_data(|s| Symbol::intern(&s))
}
impl CrateCheckConfig {
/// Fills a `CrateCheckConfig` with well-known configuration names.
fn fill_well_known_names(&mut self) {
// NOTE: This should be kept in sync with `default_configuration` and
// `fill_well_known_values`
const WELL_KNOWN_NAMES: &[Symbol] = &[
// rustc
sym::unix,
sym::windows,
sym::target_os,
sym::target_family,
sym::target_arch,
sym::target_endian,
sym::target_pointer_width,
sym::target_env,
sym::target_abi,
sym::target_vendor,
sym::target_thread_local,
sym::target_has_atomic_load_store,
sym::target_has_atomic,
sym::target_has_atomic_equal_alignment,
sym::target_feature,
sym::panic,
sym::sanitize,
sym::debug_assertions,
sym::proc_macro,
sym::test,
sym::feature,
// rustdoc
sym::doc,
sym::doctest,
// miri
sym::miri,
];
// We only insert well-known names if `names()` was activated
if let Some(names_valid) = &mut self.names_valid {
names_valid.extend(WELL_KNOWN_NAMES);
}
}
/// Fills a `CrateCheckConfig` with well-known configuration values.
fn fill_well_known_values(&mut self, current_target: &Target) {
if !self.well_known_values {
pub fn fill_well_known(&mut self, current_target: &Target) {
if !self.exhaustive_values && !self.exhaustive_names {
return;
}
// NOTE: This should be kept in sync with `default_configuration` and
// `fill_well_known_names`
let no_values = || {
let mut values = FxHashSet::default();
values.insert(None);
ExpectedValues::Some(values)
};
let empty_values = || {
let values = FxHashSet::default();
ExpectedValues::Some(values)
};
// NOTE: This should be kept in sync with `default_configuration`
let panic_values = &PanicStrategy::all();
@ -1174,6 +1182,9 @@ impl CrateCheckConfig {
// Unknown possible values:
// - `feature`
// - `target_feature`
for name in [sym::feature, sym::target_feature] {
self.expecteds.entry(name).or_insert(ExpectedValues::Any);
}
// No-values
for name in [
@ -1187,20 +1198,23 @@ impl CrateCheckConfig {
sym::debug_assertions,
sym::target_thread_local,
] {
self.values_valid.entry(name).or_default();
self.expecteds.entry(name).or_insert_with(no_values);
}
// Pre-defined values
self.values_valid.entry(sym::panic).or_default().extend(panic_values);
self.values_valid.entry(sym::sanitize).or_default().extend(sanitize_values);
self.values_valid.entry(sym::target_has_atomic).or_default().extend(atomic_values);
self.values_valid
.entry(sym::target_has_atomic_load_store)
.or_default()
self.expecteds.entry(sym::panic).or_insert_with(empty_values).extend(panic_values);
self.expecteds.entry(sym::sanitize).or_insert_with(empty_values).extend(sanitize_values);
self.expecteds
.entry(sym::target_has_atomic)
.or_insert_with(no_values)
.extend(atomic_values);
self.values_valid
self.expecteds
.entry(sym::target_has_atomic_load_store)
.or_insert_with(no_values)
.extend(atomic_values);
self.expecteds
.entry(sym::target_has_atomic_equal_alignment)
.or_default()
.or_insert_with(no_values)
.extend(atomic_values);
// Target specific values
@ -1218,47 +1232,50 @@ impl CrateCheckConfig {
// Initialize (if not already initialized)
for &e in VALUES {
self.values_valid.entry(e).or_default();
let entry = self.expecteds.entry(e);
if !self.exhaustive_values {
entry.or_insert(ExpectedValues::Any);
} else {
entry.or_insert_with(empty_values);
}
}
// Get all values map at once otherwise it would be costly.
// (8 values * 220 targets ~= 1760 times, at the time of writing this comment).
let [
values_target_os,
values_target_family,
values_target_arch,
values_target_endian,
values_target_env,
values_target_abi,
values_target_vendor,
values_target_pointer_width,
] = self
.values_valid
.get_many_mut(VALUES)
.expect("unable to get all the check-cfg values buckets");
if self.exhaustive_values {
// Get all values map at once otherwise it would be costly.
// (8 values * 220 targets ~= 1760 times, at the time of writing this comment).
let [
values_target_os,
values_target_family,
values_target_arch,
values_target_endian,
values_target_env,
values_target_abi,
values_target_vendor,
values_target_pointer_width,
] = self
.expecteds
.get_many_mut(VALUES)
.expect("unable to get all the check-cfg values buckets");
for target in TARGETS
.iter()
.map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
.chain(iter::once(current_target.clone()))
{
values_target_os.insert(Symbol::intern(&target.options.os));
values_target_family
.extend(target.options.families.iter().map(|family| Symbol::intern(family)));
values_target_arch.insert(Symbol::intern(&target.arch));
values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
values_target_env.insert(Symbol::intern(&target.options.env));
values_target_abi.insert(Symbol::intern(&target.options.abi));
values_target_vendor.insert(Symbol::intern(&target.options.vendor));
values_target_pointer_width.insert(sym::integer(target.pointer_width));
for target in TARGETS
.iter()
.map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
.chain(iter::once(current_target.clone()))
{
values_target_os.insert(Symbol::intern(&target.options.os));
values_target_family.extend(
target.options.families.iter().map(|family| Symbol::intern(family)),
);
values_target_arch.insert(Symbol::intern(&target.arch));
values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
values_target_env.insert(Symbol::intern(&target.options.env));
values_target_abi.insert(Symbol::intern(&target.options.abi));
values_target_vendor.insert(Symbol::intern(&target.options.vendor));
values_target_pointer_width.insert(sym::integer(target.pointer_width));
}
}
}
}
pub fn fill_well_known(&mut self, current_target: &Target) {
self.fill_well_known_names();
self.fill_well_known_values(current_target);
}
}
pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig {

View File

@ -6,7 +6,7 @@ use rustc_ast::token;
use rustc_ast::util::literal::LitError;
use rustc_errors::{error_code, DiagnosticMessage, EmissionGuarantee, IntoDiagnostic, MultiSpan};
use rustc_macros::Diagnostic;
use rustc_span::{Span, Symbol};
use rustc_span::{BytePos, Span, Symbol};
use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
#[derive(Diagnostic)]
@ -323,6 +323,13 @@ pub(crate) struct BinaryFloatLiteralNotSupported {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(session_nul_in_c_str)]
pub(crate) struct NulInCStr {
#[primary_span]
pub span: Span,
}
pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: Span) {
// Checks if `s` looks like i32 or u1234 etc.
fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
@ -401,6 +408,12 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span:
};
sess.emit_err(IntLiteralTooLarge { span, limit });
}
LitError::NulInCStr(range) => {
let lo = BytePos(span.lo().0 + range.start as u32 + 2);
let hi = BytePos(span.lo().0 + range.end as u32 + 2);
let span = span.with_lo(lo).with_hi(hi);
sess.emit_err(NulInCStr { span });
}
}
}

View File

@ -441,6 +441,7 @@ symbols! {
bridge,
bswap,
c_str,
c_str_literals,
c_unwind,
c_variadic,
c_void,

View File

@ -3079,8 +3079,8 @@ impl<'a, K, V, A> CursorMut<'a, K, V, A> {
unsafe { self.root.reborrow() }
.as_mut()?
.borrow_mut()
.first_leaf_edge()
.next_kv()
.last_leaf_edge()
.next_back_kv()
.ok()?
.into_kv_valmut()
}

View File

@ -8,6 +8,7 @@ use crate::testing::crash_test::{CrashTestDummy, Panic};
use crate::testing::ord_chaos::{Cyclic3, Governed, Governor};
use crate::testing::rng::DeterministicRng;
use crate::vec::Vec;
use core::assert_matches::assert_matches;
use std::cmp::Ordering;
use std::iter;
use std::mem;
@ -2448,3 +2449,21 @@ fn test_cursor_mut_insert_after_4() {
let mut cur = map.upper_bound_mut(Bound::Included(&2));
cur.insert_after(4, 'd');
}
#[test]
fn cursor_peek_prev_agrees_with_cursor_mut() {
let mut map = BTreeMap::from([(1, 1), (2, 2), (3, 3)]);
let cursor = map.lower_bound(Bound::Excluded(&3));
assert!(cursor.key().is_none());
let prev = cursor.peek_prev();
assert_matches!(prev, Some((&3, _)));
// Shadow names so the two parts of this test match.
let mut cursor = map.lower_bound_mut(Bound::Excluded(&3));
assert!(cursor.key().is_none());
let prev = cursor.peek_prev();
assert_matches!(prev, Some((&3, _)));
}

View File

@ -79,9 +79,9 @@ use crate::str;
///
/// [str]: prim@str "str"
#[derive(Hash)]
#[cfg_attr(not(test), rustc_diagnostic_item = "CStr")]
#[stable(feature = "core_c_str", since = "1.64.0")]
#[rustc_has_incoherent_inherent_impls]
#[cfg_attr(not(bootstrap), lang = "CStr")]
// FIXME:
// `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies
// on `CStr` being layout-compatible with `[u8]`.

View File

@ -337,6 +337,8 @@ pub enum LitKind {
StrRaw(u8),
ByteStr,
ByteStrRaw(u8),
CStr,
CStrRaw(u8),
Err,
}
@ -350,6 +352,8 @@ rpc_encode_decode!(
StrRaw(n),
ByteStr,
ByteStrRaw(n),
CStr,
CStrRaw(n),
Err,
}
);

View File

@ -40,11 +40,7 @@ impl Timespec {
}
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = other
.as_secs()
.try_into() // <- target type would be `libc::time_t`
.ok()
.and_then(|secs| self.t.tv_sec.checked_add(secs))?;
let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
@ -57,11 +53,7 @@ impl Timespec {
}
fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = other
.as_secs()
.try_into() // <- target type would be `libc::time_t`
.ok()
.and_then(|secs| self.t.tv_sec.checked_sub(secs))?;
let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
// Similar to above, nanos can't overflow.
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;

View File

@ -47,10 +47,10 @@ impl SystemTime {
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_add(other.as_secs().try_into().ok()?)?))
Some(SystemTime(self.0.checked_add_unsigned(other.as_secs())?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_sub(other.as_secs().try_into().ok()?)?))
Some(SystemTime(self.0.checked_sub_unsigned(other.as_secs())?))
}
}

View File

@ -113,11 +113,7 @@ impl Timespec {
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = other
.as_secs()
.try_into() // <- target type would be `i64`
.ok()
.and_then(|secs| self.tv_sec.checked_add(secs))?;
let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
@ -126,15 +122,11 @@ impl Timespec {
nsec -= NSEC_PER_SEC as u32;
secs = secs.checked_add(1)?;
}
Some(Timespec::new(secs, nsec as i64))
Some(Timespec::new(secs, nsec.into()))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = other
.as_secs()
.try_into() // <- target type would be `i64`
.ok()
.and_then(|secs| self.tv_sec.checked_sub(secs))?;
let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
// Similar to above, nanos can't overflow.
let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32;
@ -142,7 +134,7 @@ impl Timespec {
nsec += NSEC_PER_SEC as i32;
secs = secs.checked_sub(1)?;
}
Some(Timespec::new(secs, nsec as i64))
Some(Timespec::new(secs, nsec.into()))
}
#[allow(dead_code)]

View File

@ -1,4 +1,5 @@
use super::{Duration, Instant, SystemTime, UNIX_EPOCH};
use core::fmt::Debug;
#[cfg(not(target_arch = "wasm32"))]
use test::{black_box, Bencher};
@ -201,6 +202,32 @@ fn since_epoch() {
assert!(a < hundred_twenty_years);
}
#[test]
fn big_math() {
// Check that the same result occurs when adding/subtracting each duration one at a time as when
// adding/subtracting them all at once.
#[track_caller]
fn check<T: Eq + Copy + Debug>(start: Option<T>, op: impl Fn(&T, Duration) -> Option<T>) {
const DURATIONS: [Duration; 2] =
[Duration::from_secs(i64::MAX as _), Duration::from_secs(50)];
if let Some(start) = start {
assert_eq!(
op(&start, DURATIONS.into_iter().sum()),
DURATIONS.into_iter().try_fold(start, |t, d| op(&t, d))
)
}
}
check(SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(100)), SystemTime::checked_add);
check(SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(100)), SystemTime::checked_sub);
let instant = Instant::now();
check(instant.checked_sub(Duration::from_secs(100)), Instant::checked_add);
check(instant.checked_sub(Duration::from_secs(i64::MAX as _)), Instant::checked_add);
check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub);
check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub);
}
macro_rules! bench_instant_threaded {
($bench_name:ident, $thread_count:expr) => {
#[bench]

View File

@ -574,7 +574,8 @@ change in the future.
This instructs `rustc` to generate code specifically for a particular processor.
You can run `rustc --print target-cpus` to see the valid options to pass
here. Each target has a default base CPU. Special values include:
and the default target CPU for the current buid target.
Each target has a default base CPU. Special values include:
* `native` can be passed to use the processor of the host machine.
* `generic` refers to an LLVM target with minimal features but modern tuning.

View File

@ -811,7 +811,9 @@ impl<'src> Classifier<'src> {
| LiteralKind::Str { .. }
| LiteralKind::ByteStr { .. }
| LiteralKind::RawStr { .. }
| LiteralKind::RawByteStr { .. } => Class::String,
| LiteralKind::RawByteStr { .. }
| LiteralKind::CStr { .. }
| LiteralKind::RawCStr { .. } => Class::String,
// Number literals.
LiteralKind::Float { .. } | LiteralKind::Int { .. } => Class::Number,
},

View File

@ -284,6 +284,7 @@ impl<'a> NormalizedPat<'a> {
LitKind::Str(sym, _) => Self::LitStr(sym),
LitKind::ByteStr(ref bytes, _) => Self::LitBytes(bytes),
LitKind::Byte(val) => Self::LitInt(val.into()),
LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes),
LitKind::Char(val) => Self::LitInt(val.into()),
LitKind::Int(val, _) => Self::LitInt(val),
LitKind::Bool(val) => Self::LitBool(val),

View File

@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::visitors::is_expr_unsafe;
use clippy_utils::{get_parent_node, match_libc_symbol};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, UnsafeSource};
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, UnsafeSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) {
"as_bytes"
} else if is_type_diagnostic_item(cx, ty, sym::CStr) {
} else if is_type_lang_item(cx, ty, LangItem::CStr) {
"to_bytes"
} else {
return;

View File

@ -304,6 +304,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("ByteStr(ref {vec})");
chain!(self, "let [{:?}] = **{vec}", vec.value);
},
LitKind::CStr(ref vec, _) => {
bind!(self, vec);
kind!("CStr(ref {vec})");
chain!(self, "let [{:?}] = **{vec}", vec.value);
}
LitKind::Str(s, _) => {
bind!(self, s);
kind!("Str({s}, _)");

View File

@ -211,6 +211,7 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(u128::from(b)),
LitKind::ByteStr(ref s, _) => Constant::Binary(Lrc::clone(s)),
LitKind::CStr(ref s, _) => Constant::Binary(Lrc::clone(s)),
LitKind::Char(c) => Constant::Char(c),
LitKind::Int(n, _) => Constant::Int(n),
LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {

View File

@ -319,7 +319,8 @@ impl<'test> TestCx<'test> {
fn run_cfail_test(&self) {
let pm = self.pass_mode();
let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
let proc_res =
self.compile_test(WillExecute::No, self.should_emit_metadata(pm), Vec::new());
self.check_if_test_should_compile(&proc_res, pm);
self.check_no_compiler_crash(&proc_res, self.props.should_ice);
@ -347,7 +348,7 @@ impl<'test> TestCx<'test> {
fn run_rfail_test(&self) {
let pm = self.pass_mode();
let should_run = self.run_if_enabled();
let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm));
let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm), Vec::new());
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
@ -395,7 +396,7 @@ impl<'test> TestCx<'test> {
fn run_cpass_test(&self) {
let emit_metadata = self.should_emit_metadata(self.pass_mode());
let proc_res = self.compile_test(WillExecute::No, emit_metadata);
let proc_res = self.compile_test(WillExecute::No, emit_metadata, Vec::new());
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
@ -410,7 +411,7 @@ impl<'test> TestCx<'test> {
fn run_rpass_test(&self) {
let emit_metadata = self.should_emit_metadata(self.pass_mode());
let should_run = self.run_if_enabled();
let proc_res = self.compile_test(should_run, emit_metadata);
let proc_res = self.compile_test(should_run, emit_metadata, Vec::new());
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
@ -440,7 +441,7 @@ impl<'test> TestCx<'test> {
}
let should_run = self.run_if_enabled();
let mut proc_res = self.compile_test(should_run, Emit::None);
let mut proc_res = self.compile_test(should_run, Emit::None, Vec::new());
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
@ -686,7 +687,7 @@ impl<'test> TestCx<'test> {
// compile test file (it should have 'compile-flags:-g' in the header)
let should_run = self.run_if_enabled();
let compile_result = self.compile_test(should_run, Emit::None);
let compile_result = self.compile_test(should_run, Emit::None, Vec::new());
if !compile_result.status.success() {
self.fatal_proc_rec("compilation failed!", &compile_result);
}
@ -806,7 +807,7 @@ impl<'test> TestCx<'test> {
// compile test file (it should have 'compile-flags:-g' in the header)
let should_run = self.run_if_enabled();
let compiler_run_result = self.compile_test(should_run, Emit::None);
let compiler_run_result = self.compile_test(should_run, Emit::None, Vec::new());
if !compiler_run_result.status.success() {
self.fatal_proc_rec("compilation failed!", &compiler_run_result);
}
@ -1043,7 +1044,7 @@ impl<'test> TestCx<'test> {
fn run_debuginfo_lldb_test_no_opt(&self) {
// compile test file (it should have 'compile-flags:-g' in the header)
let should_run = self.run_if_enabled();
let compile_result = self.compile_test(should_run, Emit::None);
let compile_result = self.compile_test(should_run, Emit::None, Vec::new());
if !compile_result.status.success() {
self.fatal_proc_rec("compilation failed!", &compile_result);
}
@ -1482,8 +1483,8 @@ impl<'test> TestCx<'test> {
}
}
fn compile_test(&self, will_execute: WillExecute, emit: Emit) -> ProcRes {
self.compile_test_general(will_execute, emit, self.props.local_pass_mode())
fn compile_test(&self, will_execute: WillExecute, emit: Emit, passes: Vec<String>) -> ProcRes {
self.compile_test_general(will_execute, emit, self.props.local_pass_mode(), passes)
}
fn compile_test_general(
@ -1491,6 +1492,7 @@ impl<'test> TestCx<'test> {
will_execute: WillExecute,
emit: Emit,
local_pm: Option<PassMode>,
passes: Vec<String>,
) -> ProcRes {
// Only use `make_exe_name` when the test ends up being executed.
let output_file = match will_execute {
@ -1527,6 +1529,7 @@ impl<'test> TestCx<'test> {
emit,
allow_unused,
LinkToAux::Yes,
passes,
);
self.compose_and_run_compiler(rustc, None)
@ -1777,6 +1780,7 @@ impl<'test> TestCx<'test> {
Emit::None,
AllowUnused::No,
LinkToAux::No,
Vec::new(),
);
for key in &aux_props.unset_rustc_env {
@ -1908,6 +1912,7 @@ impl<'test> TestCx<'test> {
emit: Emit,
allow_unused: AllowUnused,
link_to_aux: LinkToAux,
passes: Vec<String>, // Vec of passes under mir-opt test to be dumped
) -> Command {
let is_aux = input_file.components().map(|c| c.as_os_str()).any(|c| c == "auxiliary");
let is_rustdoc = self.is_rustdoc() && !is_aux;
@ -2008,9 +2013,18 @@ impl<'test> TestCx<'test> {
rustc.arg("-Cstrip=debuginfo");
}
MirOpt => {
// We check passes under test to minimize the mir-opt test dump
// if files_for_miropt_test parses the passes, we dump only those passes
// otherwise we conservatively pass -Zdump-mir=all
let zdump_arg = if !passes.is_empty() {
format!("-Zdump-mir={}", passes.join(" | "))
} else {
"-Zdump-mir=all".to_string()
};
rustc.args(&[
"-Copt-level=1",
"-Zdump-mir=all",
&zdump_arg,
"-Zvalidate-mir",
"-Zdump-mir-exclude-pass-number",
"-Zmir-pretty-relative-line-numbers=yes",
@ -2333,6 +2347,7 @@ impl<'test> TestCx<'test> {
Emit::LlvmIr,
AllowUnused::No,
LinkToAux::Yes,
Vec::new(),
);
self.compose_and_run_compiler(rustc, None)
@ -2364,8 +2379,14 @@ impl<'test> TestCx<'test> {
None => self.fatal("missing 'assembly-output' header"),
}
let rustc =
self.make_compile_args(input_file, output_file, emit, AllowUnused::No, LinkToAux::Yes);
let rustc = self.make_compile_args(
input_file,
output_file,
emit,
AllowUnused::No,
LinkToAux::Yes,
Vec::new(),
);
(self.compose_and_run_compiler(rustc, None), output_path)
}
@ -2496,6 +2517,7 @@ impl<'test> TestCx<'test> {
Emit::None,
AllowUnused::Yes,
LinkToAux::Yes,
Vec::new(),
);
new_rustdoc.build_all_auxiliary(&mut rustc);
@ -2769,7 +2791,7 @@ impl<'test> TestCx<'test> {
fn run_codegen_units_test(&self) {
assert!(self.revision.is_none(), "revisions not relevant here");
let proc_res = self.compile_test(WillExecute::No, Emit::None);
let proc_res = self.compile_test(WillExecute::No, Emit::None, Vec::new());
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
@ -3310,14 +3332,15 @@ impl<'test> TestCx<'test> {
if let Some(FailMode::Build) = self.props.fail_mode {
// Make sure a build-fail test cannot fail due to failing analysis (e.g. typeck).
let pm = Some(PassMode::Check);
let proc_res = self.compile_test_general(WillExecute::No, Emit::Metadata, pm);
let proc_res =
self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new());
self.check_if_test_should_compile(&proc_res, pm);
}
let pm = self.pass_mode();
let should_run = self.should_run(pm);
let emit_metadata = self.should_emit_metadata(pm);
let proc_res = self.compile_test(should_run, emit_metadata);
let proc_res = self.compile_test(should_run, emit_metadata, Vec::new());
self.check_if_test_should_compile(&proc_res, pm);
// if the user specified a format in the ui test
@ -3479,6 +3502,7 @@ impl<'test> TestCx<'test> {
emit_metadata,
AllowUnused::No,
LinkToAux::Yes,
Vec::new(),
);
let res = self.compose_and_run_compiler(rustc, None);
if !res.status.success() {
@ -3497,14 +3521,14 @@ impl<'test> TestCx<'test> {
let pm = self.pass_mode();
let should_run = self.should_run(pm);
let emit_metadata = self.should_emit_metadata(pm);
let proc_res = self.compile_test(should_run, emit_metadata);
let passes = self.get_passes();
let proc_res = self.compile_test(should_run, emit_metadata, passes);
self.check_mir_dump();
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
}
self.check_mir_dump();
if let WillExecute::Yes = should_run {
let proc_res = self.exec_compiled_test();
@ -3514,6 +3538,26 @@ impl<'test> TestCx<'test> {
}
}
fn get_passes(&self) -> Vec<String> {
let files = miropt_test_tools::files_for_miropt_test(
&self.testpaths.file,
self.config.get_pointer_width(),
);
let mut out = Vec::new();
for miropt_test_tools::MiroptTestFiles {
from_file: _,
to_file: _,
expected_file: _,
passes,
} in files
{
out.extend(passes);
}
out
}
fn check_mir_dump(&self) {
let test_file_contents = fs::read_to_string(&self.testpaths.file).unwrap();
@ -3543,8 +3587,9 @@ impl<'test> TestCx<'test> {
&self.testpaths.file,
self.config.get_pointer_width(),
);
for miropt_test_tools::MiroptTestFiles { from_file, to_file, expected_file } in files {
for miropt_test_tools::MiroptTestFiles { from_file, to_file, expected_file, passes: _ } in
files
{
let dumped_string = if let Some(after) = to_file {
self.diff_mir_files(from_file.into(), after.into())
} else {

View File

@ -4,6 +4,8 @@ pub struct MiroptTestFiles {
pub expected_file: std::path::PathBuf,
pub from_file: String,
pub to_file: Option<String>,
/// Vec of passes under test to be dumped
pub passes: Vec<String>,
}
pub fn files_for_miropt_test(testfile: &std::path::Path, bit_width: u32) -> Vec<MiroptTestFiles> {
@ -28,9 +30,11 @@ pub fn files_for_miropt_test(testfile: &std::path::Path, bit_width: u32) -> Vec<
let mut expected_file;
let from_file;
let to_file;
let mut passes = Vec::new();
if test_name.ends_with(".diff") {
let trimmed = test_name.trim_end_matches(".diff");
passes.push(trimmed.split('.').last().unwrap().to_owned());
let test_against = format!("{}.after.mir", trimmed);
from_file = format!("{}.before.mir", trimmed);
expected_file = format!("{}{}.diff", trimmed, bit_width);
@ -38,7 +42,14 @@ pub fn files_for_miropt_test(testfile: &std::path::Path, bit_width: u32) -> Vec<
to_file = Some(test_against);
} else if let Some(first_pass) = test_names.next() {
let second_pass = test_names.next().unwrap();
if let Some((first_pass_name, _)) = first_pass.split_once('.') {
passes.push(first_pass_name.to_owned());
}
if let Some((second_pass_name, _)) = second_pass.split_once('.') {
passes.push(second_pass_name.to_owned());
}
assert!(test_names.next().is_none(), "three mir pass names specified for MIR diff");
expected_file =
format!("{}{}.{}-{}.diff", test_name, bit_width, first_pass, second_pass);
let second_file = format!("{}.{}.mir", test_name, second_pass);
@ -51,18 +62,24 @@ pub fn files_for_miropt_test(testfile: &std::path::Path, bit_width: u32) -> Vec<
.next()
.expect("test_name has an invalid extension");
let extension = cap.get(1).unwrap().as_str();
expected_file =
format!("{}{}{}", test_name.trim_end_matches(extension), bit_width, extension,);
from_file = test_name.to_string();
assert!(test_names.next().is_none(), "two mir pass names specified for MIR dump");
to_file = None;
// the pass name is the third to last string in the test name
// this gets pushed into passes
passes.push(
test_name.split('.').rev().nth(2).expect("invalid test format").to_string(),
);
};
if !expected_file.starts_with(&test_crate) {
expected_file = format!("{}.{}", test_crate, expected_file);
}
let expected_file = test_dir.join(expected_file);
out.push(MiroptTestFiles { expected_file, from_file, to_file });
out.push(MiroptTestFiles { expected_file, from_file, to_file, passes });
}
}

View File

@ -21,42 +21,42 @@ fn main() -> () {
}
alloc1 (static: FOO, size: 8, align: 4) {
alloc18 03 00 00 00 ....
alloc19 03 00 00 00 ....
}
alloc18 (size: 48, align: 4) {
0x00 00 00 00 00 __ __ __ __ alloc5 00 00 00 00 ........
0x10 00 00 00 00 __ __ __ __ alloc8 02 00 00 00 ........
0x20 01 00 00 00 2a 00 00 00 alloc13 03 00 00 00 ....*.......
alloc19 (size: 48, align: 4) {
0x00 00 00 00 00 __ __ __ __ alloc6 00 00 00 00 ........
0x10 00 00 00 00 __ __ __ __ alloc9 02 00 00 00 ........
0x20 01 00 00 00 2a 00 00 00 alloc14 03 00 00 00 ....*.......
}
alloc5 (size: 0, align: 4) {}
alloc6 (size: 0, align: 4) {}
alloc8 (size: 16, align: 4) {
alloc9 03 00 00 00 alloc10 03 00 00 00 ........
}
alloc9 (size: 3, align: 1) {
66 6f 6f foo
alloc9 (size: 16, align: 4) {
alloc10 03 00 00 00 alloc11 03 00 00 00 ........
}
alloc10 (size: 3, align: 1) {
66 6f 6f foo
}
alloc11 (size: 3, align: 1) {
62 61 72 bar
}
alloc13 (size: 24, align: 4) {
0x00 alloc14 03 00 00 00 alloc15 03 00 00 00 ........
0x10 alloc16 04 00 00 00 ....
}
alloc14 (size: 3, align: 1) {
6d 65 68 meh
alloc14 (size: 24, align: 4) {
0x00 alloc15 03 00 00 00 alloc16 03 00 00 00 ........
0x10 alloc17 04 00 00 00 ....
}
alloc15 (size: 3, align: 1) {
6d 65 68 meh
}
alloc16 (size: 3, align: 1) {
6d 6f 70 mop
}
alloc16 (size: 4, align: 1) {
alloc17 (size: 4, align: 1) {
6d c3 b6 70 m..p
}

View File

@ -21,46 +21,46 @@ fn main() -> () {
}
alloc1 (static: FOO, size: 16, align: 8) {
alloc18 03 00 00 00 00 00 00 00 ........
alloc19 03 00 00 00 00 00 00 00 ........
}
alloc18 (size: 72, align: 8) {
0x00 00 00 00 00 __ __ __ __ alloc5 ....
alloc19 (size: 72, align: 8) {
0x00 00 00 00 00 __ __ __ __ alloc6 ....
0x10 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ ............
0x20 alloc8 02 00 00 00 00 00 00 00 ........
0x30 01 00 00 00 2a 00 00 00 alloc13 ....*...
0x20 alloc9 02 00 00 00 00 00 00 00 ........
0x30 01 00 00 00 2a 00 00 00 alloc14 ....*...
0x40 03 00 00 00 00 00 00 00 ........
}
alloc5 (size: 0, align: 8) {}
alloc6 (size: 0, align: 8) {}
alloc8 (size: 32, align: 8) {
0x00 alloc9 03 00 00 00 00 00 00 00 ........
0x10 alloc10 03 00 00 00 00 00 00 00 ........
}
alloc9 (size: 3, align: 1) {
66 6f 6f foo
alloc9 (size: 32, align: 8) {
0x00 alloc10 03 00 00 00 00 00 00 00 ........
0x10 alloc11 03 00 00 00 00 00 00 00 ........
}
alloc10 (size: 3, align: 1) {
66 6f 6f foo
}
alloc11 (size: 3, align: 1) {
62 61 72 bar
}
alloc13 (size: 48, align: 8) {
0x00 alloc14 03 00 00 00 00 00 00 00 ........
0x10 alloc15 03 00 00 00 00 00 00 00 ........
0x20 alloc16 04 00 00 00 00 00 00 00 ........
}
alloc14 (size: 3, align: 1) {
6d 65 68 meh
alloc14 (size: 48, align: 8) {
0x00 alloc15 03 00 00 00 00 00 00 00 ........
0x10 alloc16 03 00 00 00 00 00 00 00 ........
0x20 alloc17 04 00 00 00 00 00 00 00 ........
}
alloc15 (size: 3, align: 1) {
6d 65 68 meh
}
alloc16 (size: 3, align: 1) {
6d 6f 70 mop
}
alloc16 (size: 4, align: 1) {
alloc17 (size: 4, align: 1) {
6d c3 b6 70 m..p
}

View File

@ -21,41 +21,41 @@ fn main() -> () {
}
alloc1 (static: FOO, size: 8, align: 4) {
alloc22 03 00 00 00 ....
alloc23 03 00 00 00 ....
}
alloc22 (size: 48, align: 4) {
0x00 00 00 00 00 __ __ __ __ alloc9 00 00 00 00 ........
0x10 00 00 00 00 __ __ __ __ alloc14 02 00 00 00 ........
0x20 01 00 00 00 2a 00 00 00 alloc20 03 00 00 00 ....*.......
alloc23 (size: 48, align: 4) {
0x00 00 00 00 00 __ __ __ __ alloc10 00 00 00 00 ........
0x10 00 00 00 00 __ __ __ __ alloc15 02 00 00 00 ........
0x20 01 00 00 00 2a 00 00 00 alloc21 03 00 00 00 ....*.......
}
alloc9 (size: 0, align: 4) {}
alloc10 (size: 0, align: 4) {}
alloc14 (size: 8, align: 4) {
alloc12 alloc13
}
alloc12 (size: 1, align: 1) {
05 .
alloc15 (size: 8, align: 4) {
alloc13 alloc14
}
alloc13 (size: 1, align: 1) {
05 .
}
alloc14 (size: 1, align: 1) {
06 .
}
alloc20 (size: 12, align: 4) {
a17+0x3 alloc18 a19+0x2
alloc21 (size: 12, align: 4) {
a18+0x3 alloc19 a20+0x2
}
alloc17 (size: 4, align: 1) {
alloc18 (size: 4, align: 1) {
2a 45 15 6f *E.o
}
alloc18 (size: 1, align: 1) {
alloc19 (size: 1, align: 1) {
2a *
}
alloc19 (size: 4, align: 1) {
alloc20 (size: 4, align: 1) {
2a 45 15 6f *E.o
}

View File

@ -21,44 +21,44 @@ fn main() -> () {
}
alloc1 (static: FOO, size: 16, align: 8) {
alloc22 03 00 00 00 00 00 00 00 ........
alloc23 03 00 00 00 00 00 00 00 ........
}
alloc22 (size: 72, align: 8) {
0x00 00 00 00 00 __ __ __ __ alloc9 ....
alloc23 (size: 72, align: 8) {
0x00 00 00 00 00 __ __ __ __ alloc10 ....
0x10 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ ............
0x20 alloc14 02 00 00 00 00 00 00 00 ........
0x30 01 00 00 00 2a 00 00 00 alloc20 ....*...
0x20 alloc15 02 00 00 00 00 00 00 00 ........
0x30 01 00 00 00 2a 00 00 00 alloc21 ....*...
0x40 03 00 00 00 00 00 00 00 ........
}
alloc9 (size: 0, align: 8) {}
alloc10 (size: 0, align: 8) {}
alloc14 (size: 16, align: 8) {
alloc12 alloc13
}
alloc12 (size: 1, align: 1) {
05 .
alloc15 (size: 16, align: 8) {
alloc13 alloc14
}
alloc13 (size: 1, align: 1) {
05 .
}
alloc14 (size: 1, align: 1) {
06 .
}
alloc20 (size: 24, align: 8) {
0x00 alloc17+0x3 alloc18
0x10 alloc19+0x2
alloc21 (size: 24, align: 8) {
0x00 alloc18+0x3 alloc19
0x10 alloc20+0x2
}
alloc17 (size: 4, align: 1) {
alloc18 (size: 4, align: 1) {
2a 45 15 6f *E.o
}
alloc18 (size: 1, align: 1) {
alloc19 (size: 1, align: 1) {
2a *
}
alloc19 (size: 4, align: 1) {
alloc20 (size: 4, align: 1) {
2a 45 15 6f *E.o
}

View File

@ -21,30 +21,30 @@ fn main() -> () {
}
alloc1 (static: FOO, size: 4, align: 4) {
alloc11
alloc12
}
alloc11 (size: 168, align: 1) {
alloc12 (size: 168, align: 1) {
0x00 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ................
0x10 ab ab ab ab ab ab ab ab ab ab ab ab alloc6 ............
0x10 ab ab ab ab ab ab ab ab ab ab ab ab alloc7 ............
0x20 01 ef cd ab 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x80 00 00 00 00 00 00 00 00 00 00 alloc8 00 00 ............
0x90 a9+0x63 00 00 00 00 00 00 00 00 00 00 00 00 ............
0x80 00 00 00 00 00 00 00 00 00 00 alloc9 00 00 ............
0x90 a10+0x63 00 00 00 00 00 00 00 00 00 00 00 00 ............
0xa0 00 00 00 00 00 00 00 00 ........
}
alloc6 (size: 4, align: 4) {
alloc7 (size: 4, align: 4) {
2a 00 00 00 *...
}
alloc8 (fn: main)
alloc9 (fn: main)
alloc9 (size: 100, align: 1) {
alloc10 (size: 100, align: 1) {
0x00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

View File

@ -21,12 +21,12 @@ fn main() -> () {
}
alloc1 (static: FOO, size: 8, align: 8) {
alloc11
alloc12
}
alloc11 (size: 180, align: 1) {
alloc12 (size: 180, align: 1) {
0x00 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ................
0x10 ab ab ab ab ab ab ab ab ab ab ab ab alloc6 ............
0x10 ab ab ab ab ab ab ab ab ab ab ab ab alloc7 ............
0x20 01 ef cd ab 00 00 00 00 00 00 00 00 ............
0x30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
@ -34,18 +34,18 @@ alloc11 (size: 180, align: 1) {
0x60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ..............
0x90 alloc8 00 00 alloc9+0x63 ..
0x90 alloc9 00 00 alloc10+0x63 ..
0xa0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0xb0 00 00 00 00 ....
}
alloc6 (size: 4, align: 4) {
alloc7 (size: 4, align: 4) {
2a 00 00 00 *...
}
alloc8 (fn: main)
alloc9 (fn: main)
alloc9 (size: 100, align: 1) {
alloc10 (size: 100, align: 1) {
0x00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

View File

@ -2,7 +2,7 @@ warning: unexpected `cfg` condition name
--> $DIR/check-cfg.rs:5:7
|
LL | #[cfg(uniz)]
| ^^^^ help: did you mean: `unix`
| ^^^^ help: there is a config with a similar name: `unix`
|
= note: `#[warn(unexpected_cfgs)]` on by default

View File

@ -4,7 +4,7 @@ warning: unexpected `cfg` condition value
LL | #[cfg(feature = "invalid")]
| ^^^^^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: test
= note: expected values for `feature` are: `test`
= note: `#[warn(unexpected_cfgs)]` on by default
warning: 1 warning emitted

View File

@ -4,7 +4,7 @@ warning: unexpected `cfg` condition value
LL | #[cfg(target(os = "linux", arch = "X"))]
| ^^^^^^^^^^
|
= note: expected values for `target_arch` are: aarch64, arm, avr, bpf, hexagon, loongarch64, m68k, mips, mips64, msp430, nvptx64, powerpc, powerpc64, riscv32, riscv64, s390x, sparc, sparc64, wasm32, wasm64, x86, x86_64
= note: expected values for `target_arch` are: `aarch64`, `arm`, `avr`, `bpf`, `hexagon`, `loongarch64`, `m68k`, `mips`, `mips64`, `msp430`, `nvptx64`, `powerpc`, `powerpc64`, `riscv32`, `riscv64`, `s390x`, `sparc`, `sparc64`, `wasm32`, `wasm64`, `x86`, `x86_64`
= note: `#[warn(unexpected_cfgs)]` on by default
warning: 1 warning emitted

View File

@ -0,0 +1,31 @@
// check-pass
// compile-flags: --check-cfg=names() --check-cfg=values(feature,"foo") --check-cfg=values(no_values) -Z unstable-options
#[cfg(featur)]
//~^ WARNING unexpected `cfg` condition name
fn feature() {}
#[cfg(featur = "foo")]
//~^ WARNING unexpected `cfg` condition name
fn feature() {}
#[cfg(featur = "fo")]
//~^ WARNING unexpected `cfg` condition name
fn feature() {}
#[cfg(feature = "foo")]
fn feature() {}
#[cfg(no_value)]
//~^ WARNING unexpected `cfg` condition name
fn no_values() {}
#[cfg(no_value = "foo")]
//~^ WARNING unexpected `cfg` condition name
fn no_values() {}
#[cfg(no_values = "bar")]
//~^ WARNING unexpected `cfg` condition value
fn no_values() {}
fn main() {}

View File

@ -0,0 +1,62 @@
warning: unexpected `cfg` condition name
--> $DIR/diagnotics.rs:4:7
|
LL | #[cfg(featur)]
| ^^^^^^ help: there is a config with a similar name: `feature`
|
= help: expected values for `feature` are: `foo`
= note: `#[warn(unexpected_cfgs)]` on by default
warning: unexpected `cfg` condition name
--> $DIR/diagnotics.rs:8:7
|
LL | #[cfg(featur = "foo")]
| ^^^^^^^^^^^^^^
|
= help: expected values for `feature` are: `foo`
help: there is a config with a similar name and value
|
LL | #[cfg(feature = "foo")]
| ~~~~~~~
warning: unexpected `cfg` condition name
--> $DIR/diagnotics.rs:12:7
|
LL | #[cfg(featur = "fo")]
| ^^^^^^^^^^^^^
|
= help: expected values for `feature` are: `foo`
help: there is a config with a similar name and different values
|
LL | #[cfg(feature = "foo")]
| ~~~~~~~~~~~~~~~
warning: unexpected `cfg` condition name
--> $DIR/diagnotics.rs:19:7
|
LL | #[cfg(no_value)]
| ^^^^^^^^ help: there is a config with a similar name: `no_values`
warning: unexpected `cfg` condition name
--> $DIR/diagnotics.rs:23:7
|
LL | #[cfg(no_value = "foo")]
| ^^^^^^^^^^^^^^^^
|
help: there is a config with a similar name and no value
|
LL | #[cfg(no_values)]
| ~~~~~~~~~
warning: unexpected `cfg` condition value
--> $DIR/diagnotics.rs:27:7
|
LL | #[cfg(no_values = "bar")]
| ^^^^^^^^^--------
| |
| help: remove the value
|
= note: no expected value for `no_values`
warning: 6 warnings emitted

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`
| ^^^^^^^ help: there is a config with a similar name: `windows`
|
= note: `#[warn(unexpected_cfgs)]` on by default

View File

@ -4,9 +4,9 @@ warning: unexpected `cfg` condition value
LL | #[cfg(feature = "sedre")]
| ^^^^^^^^^^-------
| |
| help: did you mean: `"serde"`
| help: there is a expected value with a similar name: `"serde"`
|
= note: expected values for `feature` are: full, serde
= note: expected values for `feature` are: `full`, `serde`
= note: `#[warn(unexpected_cfgs)]` on by default
warning: unexpected `cfg` condition value
@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value
LL | #[cfg(feature = "rand")]
| ^^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: full, serde
= note: expected values for `feature` are: `full`, `serde`
warning: unexpected condition value `rand` for condition name `feature`
|

View File

@ -12,6 +12,10 @@ fn do_windows_stuff() {}
//~^ WARNING unexpected `cfg` condition name
fn do_windows_stuff() {}
#[cfg(feature)]
//~^ WARNING unexpected `cfg` condition value
fn no_feature() {}
#[cfg(feature = "foo")]
fn use_foo() {}

View File

@ -2,28 +2,36 @@ warning: unexpected `cfg` condition name
--> $DIR/mix.rs:11:7
|
LL | #[cfg(widnows)]
| ^^^^^^^ help: did you mean: `windows`
| ^^^^^^^ help: there is a config with a similar name: `windows`
|
= note: `#[warn(unexpected_cfgs)]` on by default
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:18:7
--> $DIR/mix.rs:15:7
|
LL | #[cfg(feature = "bar")]
| ^^^^^^^^^^^^^^^
LL | #[cfg(feature)]
| ^^^^^^^- help: specify a config value: `= "foo"`
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:22:7
|
LL | #[cfg(feature = "bar")]
| ^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:26:7
|
LL | #[cfg(feature = "zebra")]
| ^^^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:26:12
--> $DIR/mix.rs:30:12
|
LL | #[cfg_attr(uu, test)]
| ^^
@ -37,146 +45,146 @@ warning: unexpected `unknown_name` as condition name
= help: was set with `--cfg` but isn't in the `--check-cfg` expected names
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:35:10
--> $DIR/mix.rs:39:10
|
LL | cfg!(widnows);
| ^^^^^^^ help: did you mean: `windows`
| ^^^^^^^ help: there is a config with a similar name: `windows`
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:38:10
--> $DIR/mix.rs:42:10
|
LL | cfg!(feature = "bar");
| ^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:40:10
--> $DIR/mix.rs:44:10
|
LL | cfg!(feature = "zebra");
| ^^^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:42:10
--> $DIR/mix.rs:46:10
|
LL | cfg!(xxx = "foo");
| ^^^^^^^^^^^
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:44:10
--> $DIR/mix.rs:48:10
|
LL | cfg!(xxx);
| ^^^
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:46:14
--> $DIR/mix.rs:50:14
|
LL | cfg!(any(xxx, windows));
| ^^^
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:48:14
--> $DIR/mix.rs:52:14
|
LL | cfg!(any(feature = "bad", windows));
| ^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:50:23
--> $DIR/mix.rs:54:23
|
LL | cfg!(any(windows, xxx));
| ^^^
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:52:20
--> $DIR/mix.rs:56:20
|
LL | cfg!(all(unix, xxx));
| ^^^
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:54:14
--> $DIR/mix.rs:58:14
|
LL | cfg!(all(aa, bb));
| ^^
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:54:18
--> $DIR/mix.rs:58:18
|
LL | cfg!(all(aa, bb));
| ^^
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:57:14
--> $DIR/mix.rs:61:14
|
LL | cfg!(any(aa, bb));
| ^^
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:57:18
--> $DIR/mix.rs:61:18
|
LL | cfg!(any(aa, bb));
| ^^
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:60:20
--> $DIR/mix.rs:64:20
|
LL | cfg!(any(unix, feature = "zebra"));
| ^^^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:62:14
--> $DIR/mix.rs:66:14
|
LL | cfg!(any(xxx, feature = "zebra"));
| ^^^
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:62:19
--> $DIR/mix.rs:66:19
|
LL | cfg!(any(xxx, feature = "zebra"));
| ^^^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:65:14
--> $DIR/mix.rs:69:14
|
LL | cfg!(any(xxx, unix, xxx));
| ^^^
warning: unexpected `cfg` condition name
--> $DIR/mix.rs:65:25
--> $DIR/mix.rs:69:25
|
LL | cfg!(any(xxx, unix, xxx));
| ^^^
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:68:14
--> $DIR/mix.rs:72:14
|
LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
| ^^^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:68:33
--> $DIR/mix.rs:72:33
|
LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
| ^^^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: unexpected `cfg` condition value
--> $DIR/mix.rs:68:52
--> $DIR/mix.rs:72:52
|
LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
| ^^^^^^^^^^^^^^^^^
|
= note: expected values for `feature` are: foo
= note: expected values for `feature` are: `foo`
warning: 27 warnings emitted
warning: 28 warnings emitted

View File

@ -2,7 +2,9 @@ warning: unexpected `cfg` condition value
--> $DIR/no-values.rs:6:7
|
LL | #[cfg(feature = "foo")]
| ^^^^^^^^^^^^^^^
| ^^^^^^^--------
| |
| help: remove the value
|
= note: no expected value for `feature`
= note: `#[warn(unexpected_cfgs)]` on by default

View File

@ -4,9 +4,9 @@ warning: unexpected `cfg` condition value
LL | #[cfg(target_os = "linuz")]
| ^^^^^^^^^^^^-------
| |
| help: did you mean: `"linux"`
| help: there is a expected value with a similar name: `"linux"`
|
= note: expected values for `target_os` are: aix, android, cuda, dragonfly, emscripten, ericos, espidf, freebsd, fuchsia, haiku, hermit, horizon, illumos, ios, l4re, linux, macos, netbsd, none, nto, openbsd, psp, redox, solaris, solid_asp3, tvos, uefi, unknown, vita, vxworks, wasi, watchos, windows, xous
= note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `ericos`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `tvos`, `uefi`, `unknown`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`
= note: `#[warn(unexpected_cfgs)]` on by default
warning: 1 warning emitted

View File

@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name
LL | #[cfg(target_oz = "linux")]
| ---------^^^^^^^^^^
| |
| help: did you mean: `target_os`
| help: there is a config with a similar name: `target_os`
|
= note: `#[warn(unexpected_cfgs)]` on by default
@ -14,13 +14,13 @@ warning: unexpected `cfg` condition name
LL | #[cfg(features = "foo")]
| --------^^^^^^^^
| |
| help: did you mean: `feature`
| help: there is a config with a similar name: `feature`
warning: unexpected `cfg` condition name
--> $DIR/well-known-names.rs:20:7
|
LL | #[cfg(uniw)]
| ^^^^ help: did you mean: `unix`
| ^^^^ help: there is a config with a similar name: `unix`
warning: 3 warnings emitted

View File

@ -4,9 +4,9 @@ warning: unexpected `cfg` condition value
LL | #[cfg(target_os = "linuz")]
| ^^^^^^^^^^^^-------
| |
| help: did you mean: `"linux"`
| help: there is a expected value with a similar name: `"linux"`
|
= note: expected values for `target_os` are: aix, android, cuda, dragonfly, emscripten, espidf, freebsd, fuchsia, haiku, hermit, horizon, illumos, ios, l4re, linux, macos, netbsd, none, nto, openbsd, psp, redox, solaris, solid_asp3, tvos, uefi, unknown, vita, vxworks, wasi, watchos, windows, xous
= note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `tvos`, `uefi`, `unknown`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`
= note: `#[warn(unexpected_cfgs)]` on by default
warning: unexpected `cfg` condition value
@ -15,9 +15,9 @@ warning: unexpected `cfg` condition value
LL | #[cfg(target_has_atomic = "0")]
| ^^^^^^^^^^^^^^^^^^^^---
| |
| help: did you mean: `"8"`
| help: there is a expected value with a similar name: `"8"`
|
= note: expected values for `target_has_atomic` are: 128, 16, 32, 64, 8, ptr
= note: expected values for `target_has_atomic` are: (none), `128`, `16`, `32`, `64`, `8`, `ptr`
warning: unexpected `cfg` condition value
--> $DIR/well-known-values.rs:21:7

View File

@ -0,0 +1,7 @@
// run-pass
#![feature(c_str_literals)]
fn main() {
assert_eq!(b"test\0", c"test".to_bytes_with_nul());
}

View File

@ -0,0 +1,13 @@
// gate-test-c_str_literals
macro_rules! m {
($t:tt) => {}
}
fn main() {
c"foo";
//~^ ERROR: `c".."` literals are experimental
m!(c"test");
//~^ ERROR: `c".."` literals are experimental
}

View File

@ -0,0 +1,21 @@
error[E0658]: `c".."` literals are experimental
--> $DIR/gate.rs:8:5
|
LL | c"foo";
| ^^^^^^
|
= note: see issue #105723 <https://github.com/rust-lang/rust/issues/105723> for more information
= help: add `#![feature(c_str_literals)]` to the crate attributes to enable
error[E0658]: `c".."` literals are experimental
--> $DIR/gate.rs:11:8
|
LL | m!(c"test");
| ^^^^^^^
|
= note: see issue #105723 <https://github.com/rust-lang/rust/issues/105723> for more information
= help: add `#![feature(c_str_literals)]` to the crate attributes to enable
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.

Binary file not shown.

View File

@ -0,0 +1,10 @@
// run-pass
#![feature(c_str_literals)]
fn main() {
assert_eq!(
c"\xEF\x80🦀\u{1F980}".to_bytes_with_nul(),
&[0xEF, 0x80, 0xF0, 0x9F, 0xA6, 0x80, 0xF0, 0x9F, 0xA6, 0x80, 0x00],
);
}