From 5def7534e4f86165fd5f984e542c17921dc9fd08 Mon Sep 17 00:00:00 2001 From: beetrees Date: Wed, 5 Oct 2022 22:27:17 +0100 Subject: [PATCH 01/21] Fix `checked_{add,sub}_duration` incorrectly returning `None` when `other` has more than `i64::MAX` seconds --- library/std/src/sys/hermit/time.rs | 12 ++---------- library/std/src/sys/solid/time.rs | 4 ++-- library/std/src/sys/unix/time.rs | 16 ++++------------ library/std/src/time/tests.rs | 27 +++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/library/std/src/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs index c17e6c8af6276..d6d2b1a302794 100644 --- a/library/std/src/sys/hermit/time.rs +++ b/library/std/src/sys/hermit/time.rs @@ -39,11 +39,7 @@ impl Timespec { } fn checked_add_duration(&self, other: &Duration) -> Option { - 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. @@ -56,11 +52,7 @@ impl Timespec { } fn checked_sub_duration(&self, other: &Duration) -> Option { - 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; diff --git a/library/std/src/sys/solid/time.rs b/library/std/src/sys/solid/time.rs index ce31cb45a69a1..f83f1644fe854 100644 --- a/library/std/src/sys/solid/time.rs +++ b/library/std/src/sys/solid/time.rs @@ -47,10 +47,10 @@ impl SystemTime { } pub fn checked_add_duration(&self, other: &Duration) -> Option { - 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 { - Some(SystemTime(self.0.checked_sub(other.as_secs().try_into().ok()?)?)) + Some(SystemTime(self.0.checked_sub_unsigned(other.as_secs())?)) } } diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index cca9c67670161..5cf70110812f9 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -102,11 +102,7 @@ impl Timespec { } pub fn checked_add_duration(&self, other: &Duration) -> Option { - 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. @@ -115,15 +111,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 { - 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; @@ -131,7 +123,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)] diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs index 6229556c85fee..4472a9625607a 100644 --- a/library/std/src/time/tests.rs +++ b/library/std/src/time/tests.rs @@ -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}; @@ -193,6 +194,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(start: Option, op: impl Fn(&T, Duration) -> Option) { + 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] From 8ff3903643b530c9029e8f2c6c6956fda8f21d77 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 5 Mar 2023 15:03:22 +0000 Subject: [PATCH 02/21] initial step towards implementing C string literals --- compiler/rustc_ast/src/ast.rs | 3 + compiler/rustc_ast/src/token.rs | 7 + compiler/rustc_ast/src/util/literal.rs | 55 ++++- compiler/rustc_ast_pretty/src/pprust/state.rs | 2 + compiler/rustc_builtin_macros/src/concat.rs | 4 + .../rustc_builtin_macros/src/concat_bytes.rs | 4 + .../rustc_expand/src/proc_macro_server.rs | 4 + compiler/rustc_hir/src/lang_items.rs | 1 + .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 5 + compiler/rustc_lexer/src/lib.rs | 30 +++ compiler/rustc_lexer/src/unescape.rs | 199 +++++++++++------- compiler/rustc_parse/src/lexer/mod.rs | 64 ++++++ library/core/src/ffi/c_str.rs | 1 + src/librustdoc/html/highlight.rs | 4 +- .../src/matches/match_same_arms.rs | 1 + .../clippy/clippy_lints/src/utils/author.rs | 5 + src/tools/clippy/clippy_utils/src/consts.rs | 1 + 17 files changed, 310 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ea04ba4f66e46..fb22e4640647d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1814,6 +1814,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"`). + CStr(Lrc<[u8]>, StrStyle), /// A byte char (`b'f'`). Byte(u8), /// A character literal (`'a'`). @@ -1868,6 +1870,7 @@ impl LitKind { // unsuffixed variants LitKind::Str(..) | LitKind::ByteStr(..) + | LitKind::CStr(..) | LitKind::Byte(..) | LitKind::Char(..) | LitKind::Int(_, LitIntType::Unsuffixed) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f947ae4d05732..42b843482a32b 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -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", } } diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 74b842ac96eac..8534011e18921 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -2,7 +2,10 @@ 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::{ascii, fmt, str}; @@ -158,6 +161,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(b'\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(b'\0'); + LitKind::CStr(buf.into(), StrStyle::Raw(n)) + } token::Err => LitKind::Err, }) } @@ -191,6 +240,8 @@ impl fmt::Display for LitKind { string = symbol )?; } + // TODO need to reescape + LitKind::CStr(..) => todo!(), LitKind::Int(n, ty) => { write!(f, "{n}")?; match ty { @@ -237,6 +288,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, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 849336c8669a1..535ac89e751d5 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -210,6 +210,8 @@ pub fn literal_to_string(lit: token::Lit) -> String { token::ByteStrRaw(n) => { format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol) } + // TODO + token::CStr | token::CStrRaw(_) => todo!(), token::Integer | token::Float | token::Bool | token::Err => symbol.to_string(), }; diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs index b92964d03e9f9..50e88ae2eeede 100644 --- a/compiler/rustc_builtin_macros/src/concat.rs +++ b/compiler/rustc_builtin_macros/src/concat.rs @@ -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; diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index ba639c0a9fe3c..ae674995e4294 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -18,6 +18,10 @@ 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(_, _)) => { + // TODO + cx.span_err(span, "cannot concatenate C string litearls"); + } Ok(ast::LitKind::Char(_)) => { let sugg = snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet }); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1e7d07bc22d52..04bdea273ebd5 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -61,6 +61,8 @@ impl FromInternal for LitKind { token::StrRaw(n) => LitKind::StrRaw(n), token::ByteStr => LitKind::ByteStr, token::ByteStrRaw(n) => LitKind::ByteStrRaw(n), + // TODO + token::CStr | token::CStrRaw(_) => todo!(), token::Err => LitKind::Err, token::Bool => unreachable!(), } @@ -436,6 +438,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 => {} } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index e1c030d3e198a..7ddafc9083a15 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -332,6 +332,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 { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index f42c825d9e8b1..374266638d160 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -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(), } } diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index b3f4b5cd5e5a0..95cb1f93e3906 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -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 }, /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a". `None` /// indicates an invalid literal. RawByteStr { n_hashes: Option }, + /// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` is invalid. + RawCStr { n_hashes: Option }, } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -391,6 +395,32 @@ impl Cursor<'_> { _ => self.ident_or_unknown_prefix(), }, + // TODO deduplicate this code + // c-string literal, raw c-string literal or identifier. + 'c' => match (self.first(), self.second()) { + ('"', _) => { + self.bump(); + let terminated = self.double_quoted_string(); + let suffix_start = self.pos_within_token(); + if terminated { + self.eat_literal_suffix(); + } + let kind = CStr { 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 = RawCStr { n_hashes: res.ok() }; + Literal { kind, suffix_start } + } + _ => self.ident_or_unknown_prefix(), + }, + // Identifier (this should be checked after other variant that can // start as identifier). c if is_id_start(c) => self.ident_or_unknown_prefix(), diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index bb4d91247b81d..4b707c9ec9619 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -90,6 +90,39 @@ where Mode::RawStr | Mode::RawByteStr => { unescape_raw_str_or_raw_byte_str(src, mode == Mode::RawByteStr, callback) } + Mode::CStr | Mode::RawCStr => unreachable!(), + } +} + +pub enum CStrUnit { + Byte(u8), + Char(char), +} + +impl From for CStrUnit { + fn from(value: u8) -> Self { + CStrUnit::Byte(value) + } +} + +impl From for CStrUnit { + fn from(value: char) -> Self { + CStrUnit::Char(value) + } +} + +pub fn unescape_c_string(src: &str, mode: Mode, callback: &mut F) +where + F: FnMut(Range, Result), +{ + 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,19 +147,26 @@ 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 { match self { - Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true, + Mode::Byte | Mode::ByteStr | Mode::RawByteStr | Mode::CStr | Mode::RawCStr => true, Mode::Char | Mode::Str | Mode::RawStr => false, } } @@ -163,62 +203,63 @@ fn scan_escape(chars: &mut Chars<'_>, is_byte: bool) -> Result { - // We've parsed '\u', now we have to parse '{..}'. + 'u' => scan_unicode(chars, is_byte)?, + _ => return Err(EscapeError::InvalidEscape), + }; + Ok(res) +} - if chars.next() != Some('{') { - return Err(EscapeError::NoBraceInUnicodeEscape); - } +fn scan_unicode(chars: &mut Chars<'_>, is_byte: bool) -> Result { + // We've parsed '\u', now we have to parse '{..}'. - // 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; + 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 } - }; + }); } - } - _ => return Err(EscapeError::InvalidEscape), - }; - Ok(res) + 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] @@ -266,7 +307,9 @@ 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), @@ -281,32 +324,32 @@ where let end = src.len() - chars.as_str().len(); callback(start..end, res); } +} - fn skip_ascii_whitespace(chars: &mut Chars<'_>, start: usize, callback: &mut F) - where - F: FnMut(Range, Result), - { - 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)); - } +fn skip_ascii_whitespace(chars: &mut Chars<'_>, start: usize, callback: &mut F) +where + F: FnMut(Range, 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(); } + *chars = tail.chars(); } /// Takes a contents of a string literal (without quotes) and produces a diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index a4a75fcb96995..b2050780309f8 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -415,6 +415,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 +443,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); @@ -692,6 +711,51 @@ impl<'a> StringReader<'a> { (token::Err, self.symbol_from_to(start, end)) } } + + fn cook_c_string( + &self, + kind: token::LitKind, + mode: Mode, + start: BytePos, + end: BytePos, + prefix_len: u32, + postfix_len: u32, + ) -> (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_c_string(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); + let (start, end) = (range.start as u32, range.end as u32); + let lo = content_start + BytePos(start); + let hi = lo + BytePos(end - start); + let span = self.mk_sp(lo, hi); + if err.is_fatal() { + has_fatal_err = true; + } + emit_unescape_error( + &self.sess.span_diagnostic, + lit_content, + span_with_quotes, + span, + mode, + range, + err, + ); + } + }); + + // We normally exclude the quotes for the symbol, but for errors we + // include it because it results in clearer error messages. + if !has_fatal_err { + (kind, Symbol::intern(lit_content)) + } else { + (token::Err, self.symbol_from_to(start, end)) + } + } } pub fn nfc_normalize(string: &str) -> Symbol { diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index bd2b2c36c4315..2ac679b6bc347 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -82,6 +82,7 @@ use crate::str; #[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` current implementation relies // on `CStr` being layout-compatible with `[u8]`. diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 946c85a205f5a..c94968b4817cb 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -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, }, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 158e6caa4de54..a48f4c77f857f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -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), diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 01927b6b5f10d..f75dff46624e4 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -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}, _)"); diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 99bfc4b5717c8..7c7ec6d334d9b 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -211,6 +211,7 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> 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 { From 76d1f93896fb642cd27cbe8ef481b66e974dbdf9 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 6 Mar 2023 07:10:23 +0000 Subject: [PATCH 03/21] update and add a few tests --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/util/literal.rs | 4 ++-- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_feature/src/active.rs | 2 ++ .../src/build/expr/as_constant.rs | 6 +++++ compiler/rustc_span/src/symbol.rs | 1 + .../rfcs/rfc-3348-c-string-literals/basic.rs | 7 ++++++ .../rfcs/rfc-3348-c-string-literals/gate.rs | 7 ++++++ .../rfc-3348-c-string-literals/gate.stderr | 21 ++++++++++++++++++ .../rfc-3348-c-string-literals/no-nuls.rs | Bin 0 -> 322 bytes .../rfc-3348-c-string-literals/no-nuls.stderr | Bin 0 -> 670 bytes 11 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/basic.rs create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/gate.rs create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/gate.stderr create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.rs create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fb22e4640647d..fb90d309fcde7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1814,7 +1814,7 @@ 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"`). + /// A C String (`c"foo"`). Guaranteed only have `\0` in the end. CStr(Lrc<[u8]>, StrStyle), /// A byte char (`b'f'`). Byte(u8), diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 8534011e18921..5fa0ea354550a 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -181,7 +181,7 @@ impl LitKind { } }); error?; - buf.push(b'\0'); + buf.push(0); LitKind::CStr(buf.into(), StrStyle::Cooked) } token::CStrRaw(n) => { @@ -204,7 +204,7 @@ impl LitKind { } }); error?; - buf.push(b'\0'); + buf.push(0); LitKind::CStr(buf.into(), StrStyle::Raw(n)) } token::Err => LitKind::Err, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 17bcd24ee39fd..9defc6603e837 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,3 +1,4 @@ +use ast::token; use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{attr, AssocConstraint, AssocConstraintKind, NodeId}; diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 6201e5b619b87..45f462a63ee8c 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -313,6 +313,8 @@ declare_features! ( (active, async_fn_in_trait, "1.66.0", Some(91611), None), /// Treat `extern "C"` function as nounwind. (active, c_unwind, "1.52.0", Some(74990), None), + /// Allows `c"foo"` literals. + (active, c_str_literals, "CURRENT_RUSTC_VERSION", Some(105723), None), /// Allows using C-variadics. (active, c_variadic, "1.34.0", Some(44930), None), /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used. diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index fbcfd43372433..59549435233c5 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -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))) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7969b848fd956..f83aa504b90e6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -441,6 +441,7 @@ symbols! { bridge, bswap, c_str, + c_str_literals, c_unwind, c_variadic, call, diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/basic.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/basic.rs new file mode 100644 index 0000000000000..e4b07ab8108e0 --- /dev/null +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/basic.rs @@ -0,0 +1,7 @@ +// run-pass + +#![feature(c_str_literals)] + +fn main() { + assert_eq!(b"test\0", c"test".to_bytes_with_nul()); +} diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/gate.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/gate.rs new file mode 100644 index 0000000000000..674b0c5e23351 --- /dev/null +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/gate.rs @@ -0,0 +1,7 @@ +fn main() { + c"foo"; + //~^ ERROR: `c".."` literals are experimental + + m!(c"test"); + //~^ ERROR: `c".."` literals are experimental +} diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/gate.stderr b/tests/ui/rfcs/rfc-3348-c-string-literals/gate.stderr new file mode 100644 index 0000000000000..bc0c537aada83 --- /dev/null +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/gate.stderr @@ -0,0 +1,21 @@ +error[E0658]: `c".."` literals are experimental + --> $DIR/gate.rs:8:5 + | +LL | c"foo"; + | ^^^^^^ + | + = note: see issue #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 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`. diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.rs new file mode 100644 index 0000000000000000000000000000000000000000..f6c86a1ba8762358537827376cbffc5c3f4508e9 GIT binary patch literal 322 zcmY#Zj802UEGaEY)kuynE-8x7$t+1NO3W$NjOF4=%Tvfr%*@l!RH)`s0D@$t7y~71 zFjHT@E>6KUD9As^N+GW_Cr2SUBe5tk8K_qwGf%-;0cccaUOE>{TWPg{K`mKY8OYKV QQ(-`sON*39w6B&60N$!x!TeXXdftt2!GI~Yrix-cM3w;1ma$Enh(Pc#!UVj(ggLC5kOomYT5DR{C8&R_#& z`K>OKf1}P$Lfcgd?nWL;VFqm$4^_YPMWO$ VFr1x))mY+2^4@4?5MIMD_6Dmm#F_vA literal 0 HcmV?d00001 From a49570fd20a16c0a41b1dfdaf121ef69f60acd7e Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 6 Mar 2023 07:42:04 +0000 Subject: [PATCH 04/21] fix TODO comments --- compiler/rustc_ast/src/util/literal.rs | 10 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 6 +- .../rustc_builtin_macros/src/concat_bytes.rs | 5 +- .../rustc_expand/src/proc_macro_server.rs | 6 +- compiler/rustc_feature/src/active.rs | 4 +- compiler/rustc_lexer/src/lib.rs | 108 +++++++++--------- library/proc_macro/src/bridge/mod.rs | 4 + .../rfcs/rfc-3348-c-string-literals/gate.rs | 6 + 8 files changed, 82 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 5fa0ea354550a..cd3b163e3ac97 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -240,8 +240,14 @@ impl fmt::Display for LitKind { string = symbol )?; } - // TODO need to reescape - LitKind::CStr(..) => todo!(), + 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 { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 535ac89e751d5..61b7863c686cf 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -210,8 +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) } - // TODO - token::CStr | token::CStrRaw(_) => todo!(), + 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(), }; diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index ae674995e4294..5ef35af0a059a 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -19,8 +19,9 @@ 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(_, _)) => { - // TODO - cx.span_err(span, "cannot concatenate C string litearls"); + // 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 = diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 04bdea273ebd5..891e84a2f3071 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -61,8 +61,8 @@ impl FromInternal for LitKind { token::StrRaw(n) => LitKind::StrRaw(n), token::ByteStr => LitKind::ByteStr, token::ByteStrRaw(n) => LitKind::ByteStrRaw(n), - // TODO - token::CStr | token::CStrRaw(_) => todo!(), + token::CStr => LitKind::CStr, + token::CStrRaw(n) => LitKind::CStrRaw(n), token::Err => LitKind::Err, token::Bool => unreachable!(), } @@ -80,6 +80,8 @@ impl ToInternal 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, } } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 45f462a63ee8c..4e5eebd285bbb 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -311,10 +311,10 @@ 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), - /// Treat `extern "C"` function as nounwind. - (active, c_unwind, "1.52.0", Some(74990), 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. (active, c_variadic, "1.34.0", Some(44930), None), /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used. diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 95cb1f93e3906..ce8c9ebe7ce77 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -361,65 +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 }), + ), - // TODO deduplicate this code // c-string literal, raw c-string literal or identifier. - 'c' => match (self.first(), self.second()) { - ('"', _) => { - self.bump(); - let terminated = self.double_quoted_string(); - let suffix_start = self.pos_within_token(); - if terminated { - self.eat_literal_suffix(); - } - let kind = CStr { 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 = RawCStr { n_hashes: res.ok() }; - Literal { kind, suffix_start } - } - _ => self.ident_or_unknown_prefix(), - }, + '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). @@ -583,6 +536,47 @@ impl Cursor<'_> { } } + fn c_or_byte_string( + &mut self, + mk_kind: impl FnOnce(bool) -> LiteralKind, + mk_kind_raw: impl FnOnce(Option) -> LiteralKind, + single_quoted: Option 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; diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 54b11c543f162..caecda1bc63fd 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -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, } ); diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/gate.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/gate.rs index 674b0c5e23351..b27da26ed23bb 100644 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/gate.rs +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/gate.rs @@ -1,3 +1,9 @@ +// gate-test-c_str_literals + +macro_rules! m { + ($t:tt) => {} +} + fn main() { c"foo"; //~^ ERROR: `c".."` literals are experimental From d5e7206ca674661a13d7bbe03284b81031e1ac33 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 6 Mar 2023 14:09:28 +0000 Subject: [PATCH 05/21] rm diag item, use lang item --- library/core/src/ffi/c_str.rs | 1 - src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 2ac679b6bc347..07b11814f965f 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -79,7 +79,6 @@ 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")] diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs index 03324c66e8efc..2f2e84fa35a12 100644 --- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs +++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs @@ -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; From 4c01d494b8233c930868be33cf4880b4267ede82 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 6 Mar 2023 14:14:55 +0000 Subject: [PATCH 06/21] refactor unescape --- compiler/rustc_lexer/src/unescape.rs | 91 +++++++++++++------ .../src/lexer/unescape_error_reporting.rs | 22 +++-- 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index 4b707c9ec9619..c9ad54d8d9806 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -86,7 +86,8 @@ 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) } @@ -94,6 +95,7 @@ where } } +/// A unit within CStr. Must not be a nul character. pub enum CStrUnit { Byte(u8), Char(char), @@ -164,24 +166,52 @@ impl Mode { } } - 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 | 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 | Mode::CStr | Mode::RawCStr => true, - Mode::Char | Mode::Str | Mode::RawStr => false, + 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 { +fn scan_escape + From>( + chars: &mut Chars<'_>, + mode: Mode, +) -> Result { // 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. @@ -194,22 +224,23 @@ fn scan_escape(chars: &mut Chars<'_>, is_byte: bool) -> Result scan_unicode(chars, is_byte)?, + '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_byte: bool) -> Result { +fn scan_unicode( + chars: &mut Chars<'_>, + is_unicode_escape_disallowed: bool, +) -> Result { // We've parsed '\u', now we have to parse '{..}'. if chars.next() != Some('{') { @@ -237,7 +268,7 @@ fn scan_unicode(chars: &mut Chars<'_>, is_byte: bool) -> Result, is_byte: bool) -> Result Result { - if is_byte && !c.is_ascii() { +fn ascii_check(c: char, characters_should_be_ascii: bool) -> Result { + if characters_should_be_ascii && !c.is_ascii() { // Byte literal can't be a non-ascii character. Err(EscapeError::NonAsciiCharInByte) } else { @@ -275,7 +306,7 @@ fn ascii_check(c: char, is_byte: bool) -> Result { fn unescape_char_or_byte(chars: &mut Chars<'_>, is_byte: bool) -> Result { 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), @@ -288,9 +319,9 @@ fn unescape_char_or_byte(chars: &mut Chars<'_>, is_byte: bool) -> Result(src: &str, is_byte: bool, callback: &mut F) +fn unescape_str_common + From>(src: &str, mode: Mode, callback: &mut F) where - F: FnMut(Range, Result), + F: FnMut(Range, Result), { let mut chars = src.chars(); @@ -312,17 +343,17 @@ where }); continue; } - _ => scan_escape(&mut chars, is_byte), + _ => scan_escape::(&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)); } } diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 0d12ec6081d83..2e4c798ab2259 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -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", From 78e3455d375feb5d100a43110f78b405a8ff05f1 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 6 Mar 2023 14:15:02 +0000 Subject: [PATCH 07/21] address comments --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_lexer/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fb90d309fcde7..8555ba3e7cee4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1814,7 +1814,7 @@ 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 only have `\0` in the end. + /// A C String (`c"foo"`). Guaranteed to only have `\0` at the end. CStr(Lrc<[u8]>, StrStyle), /// A byte char (`b'f'`). Byte(u8), diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index ce8c9ebe7ce77..c07dc19a0ac3a 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -194,7 +194,7 @@ pub enum LiteralKind { /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a". `None` /// indicates an invalid literal. RawByteStr { n_hashes: Option }, - /// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` is invalid. + /// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` indicates an invalid literal. RawCStr { n_hashes: Option }, } From bf3ca5979e47774802e95623c11e71fb303e5ff3 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 7 Mar 2023 05:09:19 +0000 Subject: [PATCH 08/21] try gating early, add non-ascii test --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 - compiler/rustc_parse/src/parser/expr.rs | 1 + tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs | 10 ++++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 9defc6603e837..17bcd24ee39fd 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,4 +1,3 @@ -use ast::token; use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{attr, AssocConstraint, AssocConstraintKind, NodeId}; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index bff9de5c652f9..c89b4ca8d6f0f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1922,6 +1922,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) diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs new file mode 100644 index 0000000000000..82e8e2090d7db --- /dev/null +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs @@ -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], + ); +} From abb181dfd9b9df22908ab08d7cfb46509295e2e6 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 10 Mar 2023 14:18:58 +0000 Subject: [PATCH 09/21] make it semantic error --- compiler/rustc_ast/src/util/literal.rs | 2 ++ compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_parse/src/lexer/mod.rs | 3 +++ compiler/rustc_session/messages.ftl | 2 ++ compiler/rustc_session/src/errors.rs | 15 ++++++++++++++- .../rfcs/rfc-3348-c-string-literals/no-nuls.rs | Bin 322 -> 570 bytes .../rfc-3348-c-string-literals/no-nuls.stderr | Bin 670 -> 674 bytes 7 files changed, 22 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index cd3b163e3ac97..15a54fe13d0b7 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -8,6 +8,7 @@ use rustc_lexer::unescape::{ }; 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, @@ -38,6 +39,7 @@ pub enum LitError { InvalidFloatSuffix, NonDecimalFloat(u32), IntTooLarge(u32), + NulInCStr(Range), } impl LitKind { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 17bcd24ee39fd..c4578ec4af1ef 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -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", diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index b2050780309f8..050f18986154a 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -204,6 +204,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 == "_" { diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index ff53f22d43f93..2420857e739ed 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -93,3 +93,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 diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index bd32adbbdbb54..22af74eb1d9e0 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -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)] @@ -307,6 +307,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 { @@ -385,6 +392,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 }); + } } } diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.rs index f6c86a1ba8762358537827376cbffc5c3f4508e9..e66519f294cd08f751f03b725967b5cd60dcee1d 100644 GIT binary patch literal 570 zcmbV}F>Avx5QRPKSDYOWQkQs6Xd#4jDiqw&PK^+9YLJy6ouS3JzdaYZZGy>C-z1*& z?w-C|#6_(oc209ud32R&P&;Y7*fUmJXk}x$fSv)BO3Ex*hvRbj{SLc4f6Z55J7Yk7 zNGBC}Jv0`!K)o7!V86bu&$3~jH=1WFKeKsZUGm?F17odJ?pV3bXdi=aPx$r2jHf(B zTp@}F0gq464{Jv67lo=1{CV^8wjoT$jeCafw@y%Qo3Q zPSCPEM;jBMN(!DFx@K1cWyzSFfW04Mhd1Y+KEI4HjfNE;5i#rWCql1B(8Y@nzCYRR fyP*I|{vI!G(MUg&)j`5rY|)T>DjOU+cR0qrx?#zt literal 670 zcmcJNI}5@v5XYVKDgG4KYQ#q+f{QLfC+AYAHE5wEa!Ew6es@F0SVV;0bT96AFG-|H zK)JR>eXXdftt2!GI~Yrix-cM3w;1ma$Enh(Pc#!UVj(ggLC5kOomYT5DR{C8&R_#& z`K>OKf1}P$Lfcgd?nWL;VFqm$4^_YPMWO$ VFr1x))mY+2^4@4?5MIMD_6Dmm#F_vA From 6d905a8cc14783bc577dc0534d2516d16ef3e43b Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 10 Mar 2023 14:29:26 +0000 Subject: [PATCH 10/21] fix tidy --- .../rfcs/rfc-3348-c-string-literals/no-nuls.rs | Bin 570 -> 565 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/no-nuls.rs index e66519f294cd08f751f03b725967b5cd60dcee1d..7bc6097f124aabfded4a9e9791611cfe4fbf9f78 100644 GIT binary patch delta 7 OcmdnRvXy0nDH8w-N&;U1 delta 13 ScmdnWvWsPdDHAIf2mk;YE&`SS From d30c6681751b10a14265e09e5f74f39d2a32e641 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 28 Apr 2023 15:28:52 +0000 Subject: [PATCH 11/21] make cook generic --- compiler/rustc_parse/src/lexer/mod.rs | 64 +++++++++++---------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 050f18986154a..9d0037b8a8083 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -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::{ @@ -670,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, @@ -678,12 +680,13 @@ impl<'a> StringReader<'a> { end: BytePos, prefix_len: u32, postfix_len: u32, + unescape: fn(&str, Mode, &mut dyn FnMut(Range, 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); @@ -715,7 +718,7 @@ impl<'a> StringReader<'a> { } } - fn cook_c_string( + fn cook_quoted( &self, kind: token::LitKind, mode: Mode, @@ -724,40 +727,27 @@ impl<'a> StringReader<'a> { prefix_len: u32, postfix_len: u32, ) -> (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_c_string(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); - let (start, end) = (range.start as u32, range.end as u32); - let lo = content_start + BytePos(start); - let hi = lo + BytePos(end - start); - let span = self.mk_sp(lo, hi); - if err.is_fatal() { - has_fatal_err = true; - } - emit_unescape_error( - &self.sess.span_diagnostic, - lit_content, - span_with_quotes, - span, - mode, - range, - err, - ); - } - }); + 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)) + }) + }) + } - // We normally exclude the quotes for the symbol, but for errors we - // include it because it results in clearer error messages. - if !has_fatal_err { - (kind, Symbol::intern(lit_content)) - } else { - (token::Err, self.symbol_from_to(start, end)) - } + 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)) + }) + }) } } From c19959f4c30da775220da20993b0b59d95d01d2f Mon Sep 17 00:00:00 2001 From: James Dietz Date: Tue, 18 Apr 2023 08:46:03 -0400 Subject: [PATCH 12/21] add passes to miroptfiles struct and passed to -zdump-mir args blessed new test --- src/tools/compiletest/src/runtest.rs | 87 ++++++++++++++----- src/tools/miropt-test-tools/src/lib.rs | 19 +++- ..._allocation.main.ConstProp.after.32bit.mir | 32 +++---- ..._allocation.main.ConstProp.after.64bit.mir | 36 ++++---- ...allocation2.main.ConstProp.after.32bit.mir | 30 +++---- ...allocation2.main.ConstProp.after.64bit.mir | 32 +++---- ...allocation3.main.ConstProp.after.32bit.mir | 16 ++-- ...allocation3.main.ConstProp.after.64bit.mir | 14 +-- 8 files changed, 164 insertions(+), 102 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index a4be7af886b45..841f5b4f6ee20 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -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) -> 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, + passes: Vec, ) -> 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, // 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 { + 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 { diff --git a/src/tools/miropt-test-tools/src/lib.rs b/src/tools/miropt-test-tools/src/lib.rs index cfba7d583b138..f86c3ce0afeaa 100644 --- a/src/tools/miropt-test-tools/src/lib.rs +++ b/src/tools/miropt-test-tools/src/lib.rs @@ -4,6 +4,8 @@ pub struct MiroptTestFiles { pub expected_file: std::path::PathBuf, pub from_file: String, pub to_file: Option, + /// Vec of passes under test to be dumped + pub passes: Vec, } pub fn files_for_miropt_test(testfile: &std::path::Path, bit_width: u32) -> Vec { @@ -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 }); } } diff --git a/tests/mir-opt/const_allocation.main.ConstProp.after.32bit.mir b/tests/mir-opt/const_allocation.main.ConstProp.after.32bit.mir index 9b69f79c28ee7..169e99deee755 100644 --- a/tests/mir-opt/const_allocation.main.ConstProp.after.32bit.mir +++ b/tests/mir-opt/const_allocation.main.ConstProp.after.32bit.mir @@ -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: 16, align: 4) { + ╾─alloc10─╼ 03 00 00 00 ╾─alloc11─╼ 03 00 00 00 │ ╾──╼....╾──╼.... } -alloc9 (size: 3, align: 1) { +alloc10 (size: 3, align: 1) { 66 6f 6f │ foo } -alloc10 (size: 3, align: 1) { +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: 24, align: 4) { + 0x00 │ ╾─alloc15─╼ 03 00 00 00 ╾─alloc16─╼ 03 00 00 00 │ ╾──╼....╾──╼.... + 0x10 │ ╾─alloc17─╼ 04 00 00 00 │ ╾──╼.... } -alloc14 (size: 3, align: 1) { +alloc15 (size: 3, align: 1) { 6d 65 68 │ meh } -alloc15 (size: 3, align: 1) { +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 } diff --git a/tests/mir-opt/const_allocation.main.ConstProp.after.64bit.mir b/tests/mir-opt/const_allocation.main.ConstProp.after.64bit.mir index d0f196e724581..db1f9648843ea 100644 --- a/tests/mir-opt/const_allocation.main.ConstProp.after.64bit.mir +++ b/tests/mir-opt/const_allocation.main.ConstProp.after.64bit.mir @@ -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: 32, align: 8) { + 0x00 │ ╾───────alloc10───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x10 │ ╾───────alloc11───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ } -alloc9 (size: 3, align: 1) { +alloc10 (size: 3, align: 1) { 66 6f 6f │ foo } -alloc10 (size: 3, align: 1) { +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: 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 │ ╾──────╼........ } -alloc14 (size: 3, align: 1) { +alloc15 (size: 3, align: 1) { 6d 65 68 │ meh } -alloc15 (size: 3, align: 1) { +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 } diff --git a/tests/mir-opt/const_allocation2.main.ConstProp.after.32bit.mir b/tests/mir-opt/const_allocation2.main.ConstProp.after.32bit.mir index aab005c52d6a0..999acb48afec3 100644 --- a/tests/mir-opt/const_allocation2.main.ConstProp.after.32bit.mir +++ b/tests/mir-opt/const_allocation2.main.ConstProp.after.32bit.mir @@ -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─╼ │ ╾──╼╾──╼ +alloc15 (size: 8, align: 4) { + ╾─alloc13─╼ ╾─alloc14─╼ │ ╾──╼╾──╼ } -alloc12 (size: 1, align: 1) { +alloc13 (size: 1, align: 1) { 05 │ . } -alloc13 (size: 1, align: 1) { +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 } diff --git a/tests/mir-opt/const_allocation2.main.ConstProp.after.64bit.mir b/tests/mir-opt/const_allocation2.main.ConstProp.after.64bit.mir index 0eff9474c2051..30311890eeef9 100644 --- a/tests/mir-opt/const_allocation2.main.ConstProp.after.64bit.mir +++ b/tests/mir-opt/const_allocation2.main.ConstProp.after.64bit.mir @@ -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───────╼ │ ╾──────╼╾──────╼ +alloc15 (size: 16, align: 8) { + ╾───────alloc13───────╼ ╾───────alloc14───────╼ │ ╾──────╼╾──────╼ } -alloc12 (size: 1, align: 1) { +alloc13 (size: 1, align: 1) { 05 │ . } -alloc13 (size: 1, align: 1) { +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 } diff --git a/tests/mir-opt/const_allocation3.main.ConstProp.after.32bit.mir b/tests/mir-opt/const_allocation3.main.ConstProp.after.32bit.mir index 55c6db5d0ce91..d592e59fafd79 100644 --- a/tests/mir-opt/const_allocation3.main.ConstProp.after.32bit.mir +++ b/tests/mir-opt/const_allocation3.main.ConstProp.after.32bit.mir @@ -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 │ ................ diff --git a/tests/mir-opt/const_allocation3.main.ConstProp.after.64bit.mir b/tests/mir-opt/const_allocation3.main.ConstProp.after.64bit.mir index 27492a7fd22c1..ca53b28be7cbf 100644 --- a/tests/mir-opt/const_allocation3.main.ConstProp.after.64bit.mir +++ b/tests/mir-opt/const_allocation3.main.ConstProp.after.64bit.mir @@ -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 │ ................ From ea17aa9141cde9f26ec09b423ca2efe8aa08cc33 Mon Sep 17 00:00:00 2001 From: James Dietz Date: Wed, 26 Apr 2023 21:11:14 -0400 Subject: [PATCH 13/21] `--print target-cpus` shows default target cpu, updated docs --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 8 +++++++- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 11 +++++++++-- src/doc/rustc/src/codegen-options/index.md | 3 ++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 46692fd5e8bcf..71fe8d36132f4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -329,7 +329,13 @@ 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 => { + println!( + "Default CPU for this target:\n {}", + handle_native(sess.target.cpu.as_ref()) + ); + unsafe { llvm::LLVMRustPrintTargetCPUs(tm, handle_native(sess.target.cpu.as_ref())) }; + } PrintRequest::TargetFeatures => print_target_features(sess, tm), _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 1acdc95ca8d2d..b65780342755c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -307,7 +307,7 @@ static size_t getLongestEntryLength(ArrayRef Table) { return MaxLen; } -extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) { +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, &Char[]) { const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const Triple::ArchType HostArch = Triple(sys::getDefaultTargetTriple()).getArch(); @@ -324,7 +324,14 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) { MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); } for (auto &CPU : CPUTable) - printf(" %-*s\n", MaxCPULen, CPU.Key); + + printf(" %-*s", MaxCPULen, CPU.Key); + if (CPU.Key == Target->getTargetTriple().getArch()) { + printf(" default target\n"); + } + else { + printf("\n"); + } printf("\n"); } diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index d7c6a884fc8fb..e2b859e705df0 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -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. From 9aa596a014d2582c1c51166953bd3fd85c71cca8 Mon Sep 17 00:00:00 2001 From: James Dietz Date: Fri, 28 Apr 2023 17:23:40 -0400 Subject: [PATCH 14/21] moved default CPU message inline --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 8 +++----- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 16 +++++++--------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c95148013eb74..53d97f3520106 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -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, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 71fe8d36132f4..1baef931ff9b7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -330,11 +330,9 @@ pub(crate) fn print(req: PrintRequest, sess: &Session) { let tm = create_informational_target_machine(sess); match req { PrintRequest::TargetCPUs => { - println!( - "Default CPU for this target:\n {}", - handle_native(sess.target.cpu.as_ref()) - ); - unsafe { llvm::LLVMRustPrintTargetCPUs(tm, handle_native(sess.target.cpu.as_ref())) }; + let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref())) + .expect("failed to convert to cstring"); + 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), diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index b65780342755c..03e76380c246c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -307,7 +307,7 @@ static size_t getLongestEntryLength(ArrayRef Table) { return MaxLen; } -extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, &Char[]) { +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,16 +323,14 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, &Char[]) { printf(" %-*s - Select the CPU of the current host (currently %.*s).\n", MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); } - for (auto &CPU : CPUTable) - + for (auto &CPU : CPUTable) { printf(" %-*s", MaxCPULen, CPU.Key); - if (CPU.Key == Target->getTargetTriple().getArch()) { - printf(" default target\n"); - } - else { - printf("\n"); + // Compare cpu against current target to label the default + if (strcmp(CPU.Key, TargetCPU) == 0) { + printf(" - this is the default target cpu for the current target"); } - printf("\n"); + printf("\n"); + } } extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) { From cb74cd524f2798a098943425c271d9eada8d901e Mon Sep 17 00:00:00 2001 From: James Dietz Date: Sun, 30 Apr 2023 09:47:29 -0400 Subject: [PATCH 15/21] change expect() to unwrap_or_else() and update msg --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 2 +- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 1baef931ff9b7..a5d4ca30fab11 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -331,7 +331,7 @@ pub(crate) fn print(req: PrintRequest, sess: &Session) { match req { PrintRequest::TargetCPUs => { let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref())) - .expect("failed to convert to cstring"); + .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), diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 03e76380c246c..e88a3cdf620d1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -324,10 +324,14 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, const char* Tar MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); } for (auto &CPU : CPUTable) { - printf(" %-*s", MaxCPULen, CPU.Key); // Compare cpu against current target to label the default if (strcmp(CPU.Key, TargetCPU) == 0) { - printf(" - this is the default target cpu for the current target"); + 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"); } From f239cd6a35776cbc91400a9e498d8b7487fe4975 Mon Sep 17 00:00:00 2001 From: James Dietz Date: Thu, 4 May 2023 20:54:17 -0400 Subject: [PATCH 16/21] added SAFETY comment --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index a5d4ca30fab11..2fbdab9f8ce03 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -330,6 +330,9 @@ pub(crate) fn print(req: PrintRequest, sess: &Session) { let tm = create_informational_target_machine(sess); match req { 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()) }; From 00cb59b53b46f15fa9faa06a523a10f7fb02dc40 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 4 May 2023 23:45:48 -0700 Subject: [PATCH 17/21] btree_map: `Cursor{,Mut}::peek_prev` must agree Our `Cursor::peek_prev` and `CursorMut::peek_prev` must agree on how to behave when they are called on the "null element". --- library/alloc/src/collections/btree/map.rs | 4 ++-- .../alloc/src/collections/btree/map/tests.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index efbbc1c2331cf..2daef82d6f1dd 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -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() } diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index da00d83bdbb57..7ecffe3eef2d5 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -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, _))); +} From ad6f4b73ebfc26859a3a6b70b3790ab55d285440 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 30 Apr 2023 11:51:36 +0200 Subject: [PATCH 18/21] Use explicit instead of implicit control-flow for check-cfg parsing --- compiler/rustc_interface/src/interface.rs | 63 ++++++++++++++--------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 8e9150ba8ad3d..de24a25f1336e 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -13,7 +13,8 @@ use rustc_middle::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) -> FxHashSet<(String, Option) -> 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,17 +138,24 @@ pub fn parse_check_cfg(specs: Vec) -> 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.names_valid.get_or_insert_with(|| FxHashSet::default()); for arg in args { if arg.is_word() && arg.ident().is_some() { let ident = arg.ident().expect("multi-segment cfg key"); @@ -156,15 +164,20 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { 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 + let ident_values = check_cfg .values_valid .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, _)) = @@ -177,36 +190,40 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { ); } } - - continue 'specs; } else { error!( "`values()` first argument must be a simple identifier" ); } } else if args.is_empty() { - cfg.well_known_values = true; - continue 'specs; + check_cfg.well_known_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()); + if let Some(names_valid) = &mut check_cfg.names_valid { + names_valid.extend(check_cfg.values_valid.keys().cloned()); } - cfg + check_cfg }) } From d327d5b16840346d838a29fdbac49a66be216a08 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 30 Apr 2023 14:43:59 +0200 Subject: [PATCH 19/21] Improve internal representation of check-cfg This is done to simplify to relationship between names() and values() but also make thing clearer (having an Any to represent that any values are allowed) but also to allow the (none) + values expected cases that wasn't possible before. --- compiler/rustc_attr/src/builtin.rs | 34 ++-- compiler/rustc_interface/src/interface.rs | 25 ++- compiler/rustc_lint/src/builtin.rs | 20 +- compiler/rustc_lint/src/context.rs | 14 +- compiler/rustc_session/src/config.rs | 233 ++++++++++++---------- 5 files changed, 173 insertions(+), 153 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index d2d3792345f1c..70426d8607520 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -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,8 +582,20 @@ 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::UnexpectedCfg( + (cfg.name, cfg.name_span), + cfg.value_span.map(|vs| (cfg.value.unwrap(), vs)), + ), + ); + } + None if sess.check_config.exhaustive_names => { sess.buffer_lint_with_diagnostic( UNEXPECTED_CFGS, cfg.span, @@ -591,22 +604,7 @@ pub fn cfg_matches( BuiltinLintDiagnostics::UnexpectedCfg((cfg.name, cfg.name_span), None), ); } - } - 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)) }) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index de24a25f1336e..9d9f4ee13f402 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -9,7 +9,7 @@ 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; @@ -154,12 +154,14 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { Ok(meta_item) if parser.token == token::Eof => { if let Some(args) = meta_item.meta_item_list() { if meta_item.has_name(sym::names) { - let names_valid = - check_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"); } @@ -168,8 +170,8 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { if let Some((name, values)) = args.split_first() { if name.is_word() && name.ident().is_some() { let ident = name.ident().expect("multi-segment cfg key"); - let ident_values = check_cfg - .values_valid + let expected_values = check_cfg + .expecteds .entry(ident.name.to_string()) .or_insert_with(|| { ExpectedValues::Some(FxHashSet::default()) @@ -183,20 +185,24 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { 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" ); } } + + if values.is_empty() { + expected_values.insert(None); + } } else { error!( "`values()` first argument must be a simple identifier" ); } } else if args.is_empty() { - check_cfg.well_known_values = true; + check_cfg.exhaustive_values = true; } else { expected_error(); } @@ -220,9 +226,6 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { } } - if let Some(names_valid) = &mut check_cfg.names_valid { - names_valid.extend(check_cfg.values_valid.keys().cloned()); - } check_cfg }) } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index aeb791901bd23..010526988506d 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -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 */ } } } } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d4898ffe883bc..f53a7bb0c19db 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -769,10 +769,7 @@ pub trait LintContext: Sized { db.note("see the asm section of Rust By Example 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 = names_valid.iter().map(|s| *s).collect(); + let possibilities: Vec = 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) { @@ -780,10 +777,15 @@ pub trait LintContext: Sized { } }, BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), Some((value, value_span))) => { - let Some(values) = &sess.parse_sess.check_config.values_valid.get(&name) else { + let Some(rustc_session::config::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 = values.iter().map(|&s| s).collect(); + let mut have_none_possibility = false; + let possibilities: Vec = values.iter() + .inspect(|a| have_none_possibility |= a.is_none()) + .copied() + .filter_map(std::convert::identity) + .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 diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 51418c01eedb4..e731fde6c3c6d 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1056,37 +1056,76 @@ pub fn to_crate_config(cfg: FxHashSet<(String, Option)>) -> CrateConfig /// The parsed `--check-cfg` options pub struct CheckCfg { - /// The set of all `names()`, if None no name checking is performed - pub names_valid: Option>, + /// 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>, + pub exhaustive_values: bool, + /// All the expected values for a config name + pub expecteds: FxHashMap>, } impl Default for CheckCfg { 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 CheckCfg { - fn map_data(&self, f: impl Fn(&T) -> O) -> CheckCfg { + fn map_data(self, f: impl Fn(T) -> O) -> CheckCfg { 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 { + Some(FxHashSet>), + Any, +} + +impl ExpectedValues { + fn insert(&mut self, value: T) -> bool { + match self { + ExpectedValues::Some(expecteds) => expecteds.insert(Some(value)), + ExpectedValues::Any => false, + } + } +} + +impl Extend for ExpectedValues { + fn extend>(&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 { + fn extend>(&mut self, iter: I) { + match self { + ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(|a| Some(*a))), + ExpectedValues::Any => {} } } } @@ -1095,58 +1134,27 @@ impl CheckCfg { /// `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(); @@ -1166,6 +1174,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 [ @@ -1179,20 +1190,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 + 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.expecteds .entry(sym::target_has_atomic_load_store) - .or_default() + .or_insert_with(no_values) .extend(atomic_values); - self.values_valid + self.expecteds .entry(sym::target_has_atomic_equal_alignment) - .or_default() + .or_insert_with(no_values) .extend(atomic_values); // Target specific values @@ -1210,47 +1224,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"); - - 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)); + 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)); + } } } } - - 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 { From a5f8dba4cd7add4fc51914a98967baf4008bd2f8 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 30 Apr 2023 15:52:44 +0200 Subject: [PATCH 20/21] Improve check-cfg diagnostics (part 1) --- compiler/rustc_attr/src/builtin.rs | 2 +- compiler/rustc_lint/src/context.rs | 16 ++++++------- tests/rustdoc-ui/check-cfg/check-cfg.stderr | 2 +- .../rustdoc-ui/doctest/check-cfg-test.stderr | 2 +- tests/ui/check-cfg/compact-values.stderr | 2 +- tests/ui/check-cfg/invalid-cfg-name.stderr | 2 +- tests/ui/check-cfg/invalid-cfg-value.stderr | 6 ++--- tests/ui/check-cfg/mix.stderr | 24 +++++++++---------- tests/ui/check-cfg/no-values.stderr | 4 +++- tests/ui/check-cfg/values-target-json.stderr | 4 ++-- tests/ui/check-cfg/well-known-names.stderr | 6 ++--- tests/ui/check-cfg/well-known-values.stderr | 8 +++---- 12 files changed, 40 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 70426d8607520..5d32080a9989c 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -591,7 +591,7 @@ pub fn cfg_matches( "unexpected `cfg` condition value", BuiltinLintDiagnostics::UnexpectedCfg( (cfg.name, cfg.name_span), - cfg.value_span.map(|vs| (cfg.value.unwrap(), vs)), + cfg.value.map(|v| (v, cfg.value_span.unwrap())), ), ); } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index f53a7bb0c19db..63c25d8f030ad 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -773,7 +773,7 @@ pub trait LintContext: Sized { // 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); + db.span_suggestion(name_span, "there is an config with a similar name", best_match, Applicability::MaybeIncorrect); } }, BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), Some((value, value_span))) => { @@ -794,19 +794,19 @@ pub trait LintContext: Sized { let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::>(); 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); + db.span_suggestion(value_span, "there is an expected value with a similar name", format!("\"{best_match}\""), Applicability::MaybeIncorrect); } - } else { + } else if have_none_possibility { db.note(format!("no expected value for `{name}`")); - if name != sym::feature { - db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect); - } + db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect); } }, BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(new_span, suggestion) => { diff --git a/tests/rustdoc-ui/check-cfg/check-cfg.stderr b/tests/rustdoc-ui/check-cfg/check-cfg.stderr index 1db8e1d91c249..03fb6f96fb5f9 100644 --- a/tests/rustdoc-ui/check-cfg/check-cfg.stderr +++ b/tests/rustdoc-ui/check-cfg/check-cfg.stderr @@ -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 diff --git a/tests/rustdoc-ui/doctest/check-cfg-test.stderr b/tests/rustdoc-ui/doctest/check-cfg-test.stderr index 9770be2f191f0..f84543c207254 100644 --- a/tests/rustdoc-ui/doctest/check-cfg-test.stderr +++ b/tests/rustdoc-ui/doctest/check-cfg-test.stderr @@ -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 diff --git a/tests/ui/check-cfg/compact-values.stderr b/tests/ui/check-cfg/compact-values.stderr index 5ca4d3b3de741..70a967c0e5f64 100644 --- a/tests/ui/check-cfg/compact-values.stderr +++ b/tests/ui/check-cfg/compact-values.stderr @@ -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 diff --git a/tests/ui/check-cfg/invalid-cfg-name.stderr b/tests/ui/check-cfg/invalid-cfg-name.stderr index 2bd1821c9422b..01ee4f209f1e6 100644 --- a/tests/ui/check-cfg/invalid-cfg-name.stderr +++ b/tests/ui/check-cfg/invalid-cfg-name.stderr @@ -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 an config with a similar name: `windows` | = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/invalid-cfg-value.stderr b/tests/ui/check-cfg/invalid-cfg-value.stderr index 83383ea61a4ab..0ef8a06dc92e2 100644 --- a/tests/ui/check-cfg/invalid-cfg-value.stderr +++ b/tests/ui/check-cfg/invalid-cfg-value.stderr @@ -4,9 +4,9 @@ warning: unexpected `cfg` condition value LL | #[cfg(feature = "sedre")] | ^^^^^^^^^^------- | | - | help: did you mean: `"serde"` + | help: there is an 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` | diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr index 9cf887ec7885c..e3738af13ff5f 100644 --- a/tests/ui/check-cfg/mix.stderr +++ b/tests/ui/check-cfg/mix.stderr @@ -2,7 +2,7 @@ warning: unexpected `cfg` condition name --> $DIR/mix.rs:11:7 | LL | #[cfg(widnows)] - | ^^^^^^^ help: did you mean: `windows` + | ^^^^^^^ help: there is an config with a similar name: `windows` | = note: `#[warn(unexpected_cfgs)]` on by default @@ -12,7 +12,7 @@ warning: unexpected `cfg` condition value 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:22:7 @@ -20,7 +20,7 @@ warning: unexpected `cfg` condition value 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 @@ -40,7 +40,7 @@ warning: unexpected `cfg` condition name --> $DIR/mix.rs:35:10 | LL | cfg!(widnows); - | ^^^^^^^ help: did you mean: `windows` + | ^^^^^^^ help: there is an config with a similar name: `windows` warning: unexpected `cfg` condition value --> $DIR/mix.rs:38:10 @@ -48,7 +48,7 @@ warning: unexpected `cfg` condition value 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 @@ -56,7 +56,7 @@ warning: unexpected `cfg` condition value 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 @@ -82,7 +82,7 @@ warning: unexpected `cfg` condition value 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 @@ -126,7 +126,7 @@ warning: unexpected `cfg` condition value 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 @@ -140,7 +140,7 @@ warning: unexpected `cfg` condition value 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 @@ -160,7 +160,7 @@ warning: unexpected `cfg` condition value 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 @@ -168,7 +168,7 @@ warning: unexpected `cfg` condition value 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 @@ -176,7 +176,7 @@ warning: unexpected `cfg` condition value 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 diff --git a/tests/ui/check-cfg/no-values.stderr b/tests/ui/check-cfg/no-values.stderr index 8c926d187fed4..ffa87dc58f203 100644 --- a/tests/ui/check-cfg/no-values.stderr +++ b/tests/ui/check-cfg/no-values.stderr @@ -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 diff --git a/tests/ui/check-cfg/values-target-json.stderr b/tests/ui/check-cfg/values-target-json.stderr index b58d2970773b8..8500fc0880503 100644 --- a/tests/ui/check-cfg/values-target-json.stderr +++ b/tests/ui/check-cfg/values-target-json.stderr @@ -4,9 +4,9 @@ warning: unexpected `cfg` condition value LL | #[cfg(target_os = "linuz")] | ^^^^^^^^^^^^------- | | - | help: did you mean: `"linux"` + | help: there is an 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 diff --git a/tests/ui/check-cfg/well-known-names.stderr b/tests/ui/check-cfg/well-known-names.stderr index bdbe4d29d30fe..987339a222a12 100644 --- a/tests/ui/check-cfg/well-known-names.stderr +++ b/tests/ui/check-cfg/well-known-names.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name LL | #[cfg(target_oz = "linux")] | ---------^^^^^^^^^^ | | - | help: did you mean: `target_os` + | help: there is an 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 an 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 an config with a similar name: `unix` warning: 3 warnings emitted diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 69d799783a94b..4bd8e6ae09f29 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -4,9 +4,9 @@ warning: unexpected `cfg` condition value LL | #[cfg(target_os = "linuz")] | ^^^^^^^^^^^^------- | | - | help: did you mean: `"linux"` + | help: there is an 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 an 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 From 53647845b917fac2a1565c72f2046f6bbad341d6 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 30 Apr 2023 19:09:15 +0200 Subject: [PATCH 21/21] Improve check-cfg diagnostics (part 2) --- compiler/rustc_attr/src/builtin.rs | 7 ++- compiler/rustc_lint/src/context.rs | 54 ++++++++++++++--- compiler/rustc_lint_defs/src/lib.rs | 3 +- tests/ui/check-cfg/diagnotics.rs | 31 ++++++++++ tests/ui/check-cfg/diagnotics.stderr | 62 ++++++++++++++++++++ tests/ui/check-cfg/invalid-cfg-name.stderr | 2 +- tests/ui/check-cfg/invalid-cfg-value.stderr | 2 +- tests/ui/check-cfg/mix.rs | 4 ++ tests/ui/check-cfg/mix.stderr | 62 +++++++++++--------- tests/ui/check-cfg/values-target-json.stderr | 2 +- tests/ui/check-cfg/well-known-names.stderr | 6 +- tests/ui/check-cfg/well-known-values.stderr | 4 +- 12 files changed, 192 insertions(+), 47 deletions(-) create mode 100644 tests/ui/check-cfg/diagnotics.rs create mode 100644 tests/ui/check-cfg/diagnotics.stderr diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 5d32080a9989c..2a3092d3c7b34 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -589,7 +589,7 @@ pub fn cfg_matches( cfg.span, lint_node_id, "unexpected `cfg` condition value", - BuiltinLintDiagnostics::UnexpectedCfg( + BuiltinLintDiagnostics::UnexpectedCfgValue( (cfg.name, cfg.name_span), cfg.value.map(|v| (v, cfg.value_span.unwrap())), ), @@ -601,7 +601,10 @@ pub fn cfg_matches( 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())), + ), ); } _ => { /* not unexpected */ } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 63c25d8f030ad..53d7cf74cde53 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -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,23 +769,51 @@ pub trait LintContext: Sized { db.help(help); db.note("see the asm section of Rust By Example for more information"); }, - BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), None) => { + BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => { let possibilities: Vec = 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, "there is an config with a similar name", 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::>(); + 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(rustc_session::config::ExpectedValues::Some(values)) = &sess.parse_sess.check_config.expecteds.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 mut have_none_possibility = false; let possibilities: Vec = values.iter() .inspect(|a| have_none_possibility |= a.is_none()) .copied() - .filter_map(std::convert::identity) + .flatten() .collect(); // Show the full list if all possible values for a given name, but don't do it @@ -800,13 +829,20 @@ pub trait LintContext: Sized { 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, "there is an expected value with a similar name", 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 if have_none_possibility { db.note(format!("no expected value for `{name}`")); - db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect); + if let Some((_value, value_span)) = value { + db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect); + } } }, BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(new_span, suggestion) => { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7ea472ed504a3..e27e322db8858 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -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. diff --git a/tests/ui/check-cfg/diagnotics.rs b/tests/ui/check-cfg/diagnotics.rs new file mode 100644 index 0000000000000..49e127d079a79 --- /dev/null +++ b/tests/ui/check-cfg/diagnotics.rs @@ -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() {} diff --git a/tests/ui/check-cfg/diagnotics.stderr b/tests/ui/check-cfg/diagnotics.stderr new file mode 100644 index 0000000000000..8b9fef09d09ec --- /dev/null +++ b/tests/ui/check-cfg/diagnotics.stderr @@ -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 + diff --git a/tests/ui/check-cfg/invalid-cfg-name.stderr b/tests/ui/check-cfg/invalid-cfg-name.stderr index 01ee4f209f1e6..ed09f8cb66d29 100644 --- a/tests/ui/check-cfg/invalid-cfg-name.stderr +++ b/tests/ui/check-cfg/invalid-cfg-name.stderr @@ -2,7 +2,7 @@ warning: unexpected `cfg` condition name --> $DIR/invalid-cfg-name.rs:7:7 | LL | #[cfg(widnows)] - | ^^^^^^^ help: there is an config with a similar name: `windows` + | ^^^^^^^ help: there is a config with a similar name: `windows` | = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/invalid-cfg-value.stderr b/tests/ui/check-cfg/invalid-cfg-value.stderr index 0ef8a06dc92e2..776d264a7adcb 100644 --- a/tests/ui/check-cfg/invalid-cfg-value.stderr +++ b/tests/ui/check-cfg/invalid-cfg-value.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition value LL | #[cfg(feature = "sedre")] | ^^^^^^^^^^------- | | - | help: there is an expected value with a similar name: `"serde"` + | help: there is a expected value with a similar name: `"serde"` | = note: expected values for `feature` are: `full`, `serde` = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/mix.rs b/tests/ui/check-cfg/mix.rs index 4e488fc03ec4b..9adf5c46e43fe 100644 --- a/tests/ui/check-cfg/mix.rs +++ b/tests/ui/check-cfg/mix.rs @@ -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() {} diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr index e3738af13ff5f..07c514aed5242 100644 --- a/tests/ui/check-cfg/mix.stderr +++ b/tests/ui/check-cfg/mix.stderr @@ -2,12 +2,20 @@ warning: unexpected `cfg` condition name --> $DIR/mix.rs:11:7 | LL | #[cfg(widnows)] - | ^^^^^^^ help: there is an config with a similar name: `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)] + | ^^^^^^^- help: specify a config value: `= "foo"` + | + = note: expected values for `feature` are: `foo` + +warning: unexpected `cfg` condition value + --> $DIR/mix.rs:22:7 | LL | #[cfg(feature = "bar")] | ^^^^^^^^^^^^^^^ @@ -15,7 +23,7 @@ LL | #[cfg(feature = "bar")] = note: expected values for `feature` are: `foo` warning: unexpected `cfg` condition value - --> $DIR/mix.rs:22:7 + --> $DIR/mix.rs:26:7 | LL | #[cfg(feature = "zebra")] | ^^^^^^^^^^^^^^^^^ @@ -23,7 +31,7 @@ LL | #[cfg(feature = "zebra")] = 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,13 +45,13 @@ 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: there is an config with a similar name: `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"); | ^^^^^^^^^^^^^^^ @@ -51,7 +59,7 @@ LL | cfg!(feature = "bar"); = 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"); | ^^^^^^^^^^^^^^^^^ @@ -59,25 +67,25 @@ LL | cfg!(feature = "zebra"); = 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)); | ^^^^^^^^^^^^^^^ @@ -85,43 +93,43 @@ LL | cfg!(any(feature = "bad", windows)); = 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")); | ^^^^^^^^^^^^^^^^^ @@ -129,13 +137,13 @@ LL | cfg!(any(unix, feature = "zebra")); = 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")); | ^^^^^^^^^^^^^^^^^ @@ -143,19 +151,19 @@ LL | cfg!(any(xxx, feature = "zebra")); = 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")); | ^^^^^^^^^^^^^^^^^ @@ -163,7 +171,7 @@ LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra")); = 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")); | ^^^^^^^^^^^^^^^^^ @@ -171,12 +179,12 @@ LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra")); = 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` -warning: 27 warnings emitted +warning: 28 warnings emitted diff --git a/tests/ui/check-cfg/values-target-json.stderr b/tests/ui/check-cfg/values-target-json.stderr index 8500fc0880503..eb81535e3edb0 100644 --- a/tests/ui/check-cfg/values-target-json.stderr +++ b/tests/ui/check-cfg/values-target-json.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition value LL | #[cfg(target_os = "linuz")] | ^^^^^^^^^^^^------- | | - | help: there is an expected value with a similar name: `"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: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/well-known-names.stderr b/tests/ui/check-cfg/well-known-names.stderr index 987339a222a12..34c5d6172d94d 100644 --- a/tests/ui/check-cfg/well-known-names.stderr +++ b/tests/ui/check-cfg/well-known-names.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name LL | #[cfg(target_oz = "linux")] | ---------^^^^^^^^^^ | | - | help: there is an config with a similar name: `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: there is an config with a similar name: `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: there is an config with a similar name: `unix` + | ^^^^ help: there is a config with a similar name: `unix` warning: 3 warnings emitted diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 4bd8e6ae09f29..2d18cb82e037a 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition value LL | #[cfg(target_os = "linuz")] | ^^^^^^^^^^^^------- | | - | help: there is an expected value with a similar name: `"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: `#[warn(unexpected_cfgs)]` on by default @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value LL | #[cfg(target_has_atomic = "0")] | ^^^^^^^^^^^^^^^^^^^^--- | | - | help: there is an expected value with a similar name: `"8"` + | help: there is a expected value with a similar name: `"8"` | = note: expected values for `target_has_atomic` are: (none), `128`, `16`, `32`, `64`, `8`, `ptr`