summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cstr
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/rust/cstr
parentInitial commit. (diff)
downloadthunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz
thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cstr')
-rw-r--r--third_party/rust/cstr/.cargo-checksum.json1
-rw-r--r--third_party/rust/cstr/Cargo.toml41
-rw-r--r--third_party/rust/cstr/LICENSE25
-rw-r--r--third_party/rust/cstr/README.md30
-rw-r--r--third_party/rust/cstr/src/lib.rs56
-rw-r--r--third_party/rust/cstr/src/parse.rs225
-rw-r--r--third_party/rust/cstr/tests/clippy_lints.rs10
-rw-r--r--third_party/rust/cstr/tests/compile_fail/empty.rs5
-rw-r--r--third_party/rust/cstr/tests/compile_fail/empty.stderr7
-rw-r--r--third_party/rust/cstr/tests/compile_fail/interior-nul.rs5
-rw-r--r--third_party/rust/cstr/tests/compile_fail/interior-nul.stderr5
-rw-r--r--third_party/rust/cstr/tests/compile_fail/non-str.rs7
-rw-r--r--third_party/rust/cstr/tests/compile_fail/non-str.stderr17
-rw-r--r--third_party/rust/cstr/tests/compile_fail/trash-after.rs5
-rw-r--r--third_party/rust/cstr/tests/compile_fail/trash-after.stderr5
-rw-r--r--third_party/rust/cstr/tests/compile_test.rs6
-rw-r--r--third_party/rust/cstr/tests/pass/byte_str_lit.rs7
-rw-r--r--third_party/rust/cstr/tests/pass/const.rs10
-rw-r--r--third_party/rust/cstr/tests/pass/ident.rs9
-rw-r--r--third_party/rust/cstr/tests/pass/macro.rs21
-rw-r--r--third_party/rust/cstr/tests/pass/str_lit.rs8
21 files changed, 505 insertions, 0 deletions
diff --git a/third_party/rust/cstr/.cargo-checksum.json b/third_party/rust/cstr/.cargo-checksum.json
new file mode 100644
index 0000000000..9d9ca8aff4
--- /dev/null
+++ b/third_party/rust/cstr/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"9766da0dcd235f8d0d4ebdc925050558710adfd4495c123b1f4997666869d524","LICENSE":"5a9bf0e7661617253ca7c12313f51a96aa62dec0bcd15a59c533c88b8093d124","README.md":"8fdfa924e95d7a83f3c032dcc103cb411743c404e7e080b985c97b5db90eea24","src/lib.rs":"ad266f1d5c682943741344d84dba39c516c3b8b26b34a4ff2c858de9934cdfe5","src/parse.rs":"19214fac49af5852b93a37d43af6ee93e62a1e95e3a629f8d5da254925b7d294","tests/clippy_lints.rs":"4398124cd5bc3a7f295f6203d543fc7d99abfd945eb7418ccfa60535586d7e37","tests/compile_fail/empty.rs":"52dc3c0d4d6ee0bd6d89a34d1caf38d159830401f24ba30f5655f9de92697903","tests/compile_fail/empty.stderr":"dbcf3dab8a8638b833df9089d9bc9ff7494f39dbb91e94bdd769912678ccf7f8","tests/compile_fail/interior-nul.rs":"ecc09440020287377ca18e4b8308d1d516620a87612a5381bafc01fe48734d34","tests/compile_fail/interior-nul.stderr":"8bd003a7dfff248411403bdf666f8a0631307f468d589cf01e475b062db4b101","tests/compile_fail/non-str.rs":"e08be18a524a4482fb7f34cbc6e8448a878b41cf2c26dea99268aaabab6c3f3f","tests/compile_fail/non-str.stderr":"8dff245264d9c69dc151f742542a72400d7422f2a0f2b133a9f4d4fc96a4016a","tests/compile_fail/trash-after.rs":"7dff7a301c9087984c5acda183e34492f3d0f2ebec14b8dc0d2b11aab972a111","tests/compile_fail/trash-after.stderr":"487b5d6b687c52b80f9d9cba691a8654067a88f7d03d2d952d7e97d610ab70f3","tests/compile_test.rs":"13e3e0d22ec0dffa4d0be0c4db6381a03feff50cc25aa65c4950cc7e865d122d","tests/pass/byte_str_lit.rs":"9085e1f1e67dae193d33ff59c253cac23c9e23e9d8c7f92f0aba99097ade132e","tests/pass/const.rs":"777aeb93c3030349529a41ac62b3577b36badc4bada4ec46e45b5055d3676dbd","tests/pass/ident.rs":"5116ee71578d479d899345e039e5955b5dee442234dc504e1a9bfb9260cf8f15","tests/pass/macro.rs":"9596c936ed4d963fb40459ecd98b60610d3d90e41918f350ff45b6129b1aa0b7","tests/pass/str_lit.rs":"955fb887ebc01538bafe10fa810381eb53aebaafb8b36053e8712c081862fe7a"},"package":"8aa998c33a6d3271e3678950a22134cd7dd27cef86dee1b611b5b14207d1d90b"} \ No newline at end of file
diff --git a/third_party/rust/cstr/Cargo.toml b/third_party/rust/cstr/Cargo.toml
new file mode 100644
index 0000000000..712f393718
--- /dev/null
+++ b/third_party/rust/cstr/Cargo.toml
@@ -0,0 +1,41 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.64"
+name = "cstr"
+version = "0.2.11"
+authors = ["Xidorn Quan <me@upsuper.org>"]
+description = "Macro for building static CStr reference"
+readme = "README.md"
+keywords = [
+ "macro",
+ "cstr",
+]
+license = "MIT"
+repository = "https://github.com/upsuper/cstr"
+
+[lib]
+proc-macro = true
+
+[dependencies.proc-macro2]
+version = "1"
+
+[dependencies.quote]
+version = "1"
+
+[dev-dependencies.trybuild]
+version = "1.0.30"
+
+[badges.travis-ci]
+branch = "master"
+repository = "upsuper/cstr"
diff --git a/third_party/rust/cstr/LICENSE b/third_party/rust/cstr/LICENSE
new file mode 100644
index 0000000000..82ec98b7cc
--- /dev/null
+++ b/third_party/rust/cstr/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2018-2020 Xidorn Quan
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/third_party/rust/cstr/README.md b/third_party/rust/cstr/README.md
new file mode 100644
index 0000000000..7ee3ba2474
--- /dev/null
+++ b/third_party/rust/cstr/README.md
@@ -0,0 +1,30 @@
+# cstr
+
+[![CI](https://github.com/upsuper/cstr/workflows/CI/badge.svg)](https://github.com/upsuper/cstr/actions)
+[![Crates.io](https://img.shields.io/crates/v/cstr.svg)](https://crates.io/crates/cstr)
+[![Docs](https://docs.rs/cstr/badge.svg)](https://docs.rs/cstr)
+
+<!-- cargo-sync-readme start -->
+
+A macro for getting `&'static CStr` from literal or identifier.
+
+This macro checks whether the given literal is valid for `CStr`
+at compile time, and returns a static reference of `CStr`.
+
+This macro can be used to to initialize constants on Rust 1.64 and above.
+
+## Example
+
+```rust
+use cstr::cstr;
+use std::ffi::CStr;
+
+let test = cstr!(b"hello\xff");
+assert_eq!(test, CStr::from_bytes_with_nul(b"hello\xff\0").unwrap());
+let test = cstr!("hello");
+assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap());
+let test = cstr!(hello);
+assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap());
+```
+
+<!-- cargo-sync-readme end -->
diff --git a/third_party/rust/cstr/src/lib.rs b/third_party/rust/cstr/src/lib.rs
new file mode 100644
index 0000000000..c9e69d924c
--- /dev/null
+++ b/third_party/rust/cstr/src/lib.rs
@@ -0,0 +1,56 @@
+//! A macro for getting `&'static CStr` from literal or identifier.
+//!
+//! This macro checks whether the given literal is valid for `CStr`
+//! at compile time, and returns a static reference of `CStr`.
+//!
+//! This macro can be used to to initialize constants on Rust 1.64 and above.
+//!
+//! ## Example
+//!
+//! ```
+//! use cstr::cstr;
+//! use std::ffi::CStr;
+//!
+//! let test = cstr!(b"hello\xff");
+//! assert_eq!(test, CStr::from_bytes_with_nul(b"hello\xff\0").unwrap());
+//! let test = cstr!("hello");
+//! assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap());
+//! let test = cstr!(hello);
+//! assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap());
+//! ```
+
+// While this isn't necessary when using Cargo >= 1.42, omitting it actually requires path-less
+// `--extern proc_macro` to be passed to `rustc` when building this crate. Some tools may not do
+// this correctly. So it's added as a precaution.
+extern crate proc_macro;
+
+use crate::parse::parse_input;
+use proc_macro::TokenStream as RawTokenStream;
+use proc_macro2::{Literal, Span, TokenStream};
+use quote::{quote, quote_spanned};
+use std::ffi::CString;
+
+mod parse;
+
+struct Error(Span, &'static str);
+
+#[proc_macro]
+pub fn cstr(input: RawTokenStream) -> RawTokenStream {
+ let tokens = match build_byte_str(input.into()) {
+ Ok(s) => quote!(unsafe { ::core::ffi::CStr::from_bytes_with_nul_unchecked(#s) }),
+ Err(Error(span, msg)) => quote_spanned!(span => compile_error!(#msg)),
+ };
+ tokens.into()
+}
+
+fn build_byte_str(input: TokenStream) -> Result<Literal, Error> {
+ let (bytes, span) = parse_input(input)?;
+ match CString::new(bytes) {
+ Ok(s) => {
+ let mut lit = Literal::byte_string(s.as_bytes_with_nul());
+ lit.set_span(span);
+ Ok(lit)
+ }
+ Err(_) => Err(Error(span, "nul byte found in the literal")),
+ }
+}
diff --git a/third_party/rust/cstr/src/parse.rs b/third_party/rust/cstr/src/parse.rs
new file mode 100644
index 0000000000..aff419d654
--- /dev/null
+++ b/third_party/rust/cstr/src/parse.rs
@@ -0,0 +1,225 @@
+use crate::Error;
+use proc_macro2::{Delimiter, Ident, Literal, Span, TokenStream, TokenTree};
+use std::char;
+
+macro_rules! unexpected_content {
+ () => {
+ "expected one of: byte string literal, string literal, identifier"
+ };
+}
+
+pub(crate) fn parse_input(mut input: TokenStream) -> Result<(Vec<u8>, Span), Error> {
+ loop {
+ let mut tokens = input.into_iter();
+ let token = match tokens.next() {
+ Some(token) => token,
+ None => {
+ return Err(Error(
+ Span::call_site(),
+ concat!("unexpected end of input, ", unexpected_content!()),
+ ))
+ }
+ };
+ let span = token.span();
+ let result = match token {
+ // Unwrap any empty group which may be created from macro expansion.
+ TokenTree::Group(group) if group.delimiter() == Delimiter::None => Err(group),
+ TokenTree::Literal(literal) => match parse_literal(literal) {
+ Ok(result) => Ok(result),
+ Err(msg) => return Err(Error(span, msg)),
+ },
+ TokenTree::Ident(ident) => Ok(parse_ident(ident)),
+ _ => return Err(Error(span, unexpected_content!())),
+ };
+ if let Some(token) = tokens.next() {
+ return Err(Error(token.span(), "unexpected token"));
+ }
+ match result {
+ Ok(result) => return Ok((result, span)),
+ Err(group) => input = group.stream(),
+ }
+ }
+}
+
+fn parse_literal(literal: Literal) -> Result<Vec<u8>, &'static str> {
+ let s = literal.to_string();
+ let s = s.as_bytes();
+ match s[0] {
+ b'"' => Ok(parse_cooked_content(s)),
+ b'r' => Ok(parse_raw_content(&s[1..])),
+ b'b' => match s[1] {
+ b'"' => Ok(parse_cooked_content(&s[1..])),
+ b'r' => Ok(parse_raw_content(&s[2..])),
+ _ => Err(unexpected_content!()),
+ },
+ _ => Err(unexpected_content!()),
+ }
+}
+
+fn all_pounds(bytes: &[u8]) -> bool {
+ bytes.iter().all(|b| *b == b'#')
+}
+
+/// Parses raw string / bytes content after `r` prefix.
+fn parse_raw_content(s: &[u8]) -> Vec<u8> {
+ let q_start = s.iter().position(|b| *b == b'"').unwrap();
+ let q_end = s.iter().rposition(|b| *b == b'"').unwrap();
+ assert!(all_pounds(&s[0..q_start]));
+ assert!(all_pounds(&s[q_end + 1..q_end + q_start + 1]));
+ Vec::from(&s[q_start + 1..q_end])
+}
+
+/// Parses the cooked string / bytes content within quotes.
+fn parse_cooked_content(mut s: &[u8]) -> Vec<u8> {
+ s = &s[1..s.iter().rposition(|b| *b == b'"').unwrap()];
+ let mut result = Vec::new();
+ while !s.is_empty() {
+ match s[0] {
+ b'\\' => {}
+ b'\r' => {
+ assert_eq!(s[1], b'\n');
+ result.push(b'\n');
+ s = &s[2..];
+ continue;
+ }
+ b => {
+ result.push(b);
+ s = &s[1..];
+ continue;
+ }
+ }
+ let b = s[1];
+ s = &s[2..];
+ match b {
+ b'x' => {
+ let (b, rest) = backslash_x(s);
+ result.push(b);
+ s = rest;
+ }
+ b'u' => {
+ let (c, rest) = backslash_u(s);
+ result.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes());
+ s = rest;
+ }
+ b'n' => result.push(b'\n'),
+ b'r' => result.push(b'\r'),
+ b't' => result.push(b'\t'),
+ b'\\' => result.push(b'\\'),
+ b'0' => result.push(b'\0'),
+ b'\'' => result.push(b'\''),
+ b'"' => result.push(b'"'),
+ b'\r' | b'\n' => {
+ let next = s.iter().position(|b| {
+ let ch = char::from_u32(u32::from(*b)).unwrap();
+ !ch.is_whitespace()
+ });
+ match next {
+ Some(pos) => s = &s[pos..],
+ None => s = b"",
+ }
+ }
+ b => panic!("unexpected byte {:?} after \\", b),
+ }
+ }
+ result
+}
+
+fn backslash_x(s: &[u8]) -> (u8, &[u8]) {
+ let ch = hex_to_u8(s[0]) * 0x10 + hex_to_u8(s[1]);
+ (ch, &s[2..])
+}
+
+fn hex_to_u8(b: u8) -> u8 {
+ match b {
+ b'0'..=b'9' => b - b'0',
+ b'a'..=b'f' => b - b'a' + 10,
+ b'A'..=b'F' => b - b'A' + 10,
+ _ => unreachable!("unexpected non-hex character {:?} after \\x", b),
+ }
+}
+
+fn backslash_u(s: &[u8]) -> (char, &[u8]) {
+ assert_eq!(s[0], b'{');
+ let end = s[1..].iter().position(|b| *b == b'}').unwrap();
+ let mut ch = 0;
+ for b in &s[1..=end] {
+ ch *= 0x10;
+ ch += u32::from(hex_to_u8(*b));
+ }
+ (char::from_u32(ch).unwrap(), &s[end + 2..])
+}
+
+fn parse_ident(ident: Ident) -> Vec<u8> {
+ ident.to_string().into_bytes()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::str::FromStr;
+
+ // Tests below were modified from
+ // https://github.com/dtolnay/syn/blob/cd5fdc0f530f822446fccaf831669cd0cf4a0fc9/tests/test_lit.rs
+
+ fn lit(s: &str) -> Vec<u8> {
+ match TokenStream::from_str(s)
+ .unwrap()
+ .into_iter()
+ .next()
+ .unwrap()
+ {
+ TokenTree::Literal(lit) => parse_literal(lit).unwrap(),
+ _ => panic!(),
+ }
+ }
+
+ #[test]
+ fn strings() {
+ #[track_caller]
+ fn test_string(s: &str, value: &[u8]) {
+ assert_eq!(lit(s), value);
+ }
+
+ test_string("\"a\"", b"a");
+ test_string("\"\\n\"", b"\n");
+ test_string("\"\\r\"", b"\r");
+ test_string("\"\\t\"", b"\t");
+ test_string("\"🐕\"", b"\xf0\x9f\x90\x95"); // NOTE: This is an emoji
+ test_string("\"\\\"\"", b"\"");
+ test_string("\"'\"", b"'");
+ test_string("\"\"", b"");
+ test_string("\"\\u{1F415}\"", b"\xf0\x9f\x90\x95");
+ test_string(
+ "\"contains\nnewlines\\\nescaped newlines\"",
+ b"contains\nnewlinesescaped newlines",
+ );
+ test_string("r\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere");
+ test_string("\"...\"q", b"...");
+ test_string("r\"...\"q", b"...");
+ test_string("r##\"...\"##q", b"...");
+ }
+
+ #[test]
+ fn byte_strings() {
+ #[track_caller]
+ fn test_byte_string(s: &str, value: &[u8]) {
+ assert_eq!(lit(s), value);
+ }
+
+ test_byte_string("b\"a\"", b"a");
+ test_byte_string("b\"\\n\"", b"\n");
+ test_byte_string("b\"\\r\"", b"\r");
+ test_byte_string("b\"\\t\"", b"\t");
+ test_byte_string("b\"\\\"\"", b"\"");
+ test_byte_string("b\"'\"", b"'");
+ test_byte_string("b\"\"", b"");
+ test_byte_string(
+ "b\"contains\nnewlines\\\nescaped newlines\"",
+ b"contains\nnewlinesescaped newlines",
+ );
+ test_byte_string("br\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere");
+ test_byte_string("b\"...\"q", b"...");
+ test_byte_string("br\"...\"q", b"...");
+ test_byte_string("br##\"...\"##q", b"...");
+ }
+}
diff --git a/third_party/rust/cstr/tests/clippy_lints.rs b/third_party/rust/cstr/tests/clippy_lints.rs
new file mode 100644
index 0000000000..47d7f27e2b
--- /dev/null
+++ b/third_party/rust/cstr/tests/clippy_lints.rs
@@ -0,0 +1,10 @@
+use cstr::cstr;
+use std::ffi::CStr;
+
+#[test]
+#[deny(clippy::transmute_ptr_to_ref)]
+fn deny_transmute_ptr_to_ref() {
+ let s: &'static CStr = cstr!("foo\u{4e00}bar");
+ let expected = b"foo\xe4\xb8\x80bar\0";
+ assert_eq!(s, CStr::from_bytes_with_nul(expected).unwrap());
+}
diff --git a/third_party/rust/cstr/tests/compile_fail/empty.rs b/third_party/rust/cstr/tests/compile_fail/empty.rs
new file mode 100644
index 0000000000..0ac967273e
--- /dev/null
+++ b/third_party/rust/cstr/tests/compile_fail/empty.rs
@@ -0,0 +1,5 @@
+use cstr::cstr;
+
+fn main() {
+ let _foo = cstr!();
+}
diff --git a/third_party/rust/cstr/tests/compile_fail/empty.stderr b/third_party/rust/cstr/tests/compile_fail/empty.stderr
new file mode 100644
index 0000000000..68bda07fab
--- /dev/null
+++ b/third_party/rust/cstr/tests/compile_fail/empty.stderr
@@ -0,0 +1,7 @@
+error: unexpected end of input, expected one of: byte string literal, string literal, identifier
+ --> $DIR/empty.rs:4:16
+ |
+4 | let _foo = cstr!();
+ | ^^^^^^^
+ |
+ = note: this error originates in the macro `cstr` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/third_party/rust/cstr/tests/compile_fail/interior-nul.rs b/third_party/rust/cstr/tests/compile_fail/interior-nul.rs
new file mode 100644
index 0000000000..a6876aeb1d
--- /dev/null
+++ b/third_party/rust/cstr/tests/compile_fail/interior-nul.rs
@@ -0,0 +1,5 @@
+use cstr::cstr;
+
+fn main() {
+ let _foo = cstr!("foo\0bar");
+}
diff --git a/third_party/rust/cstr/tests/compile_fail/interior-nul.stderr b/third_party/rust/cstr/tests/compile_fail/interior-nul.stderr
new file mode 100644
index 0000000000..0c6ced1a45
--- /dev/null
+++ b/third_party/rust/cstr/tests/compile_fail/interior-nul.stderr
@@ -0,0 +1,5 @@
+error: nul byte found in the literal
+ --> $DIR/interior-nul.rs:4:22
+ |
+4 | let _foo = cstr!("foo\0bar");
+ | ^^^^^^^^^^
diff --git a/third_party/rust/cstr/tests/compile_fail/non-str.rs b/third_party/rust/cstr/tests/compile_fail/non-str.rs
new file mode 100644
index 0000000000..172b145590
--- /dev/null
+++ b/third_party/rust/cstr/tests/compile_fail/non-str.rs
@@ -0,0 +1,7 @@
+use cstr::cstr;
+
+fn main() {
+ let _foo = cstr!(1);
+ let _foo = cstr!(("a"));
+ let _foo = cstr!(&1);
+}
diff --git a/third_party/rust/cstr/tests/compile_fail/non-str.stderr b/third_party/rust/cstr/tests/compile_fail/non-str.stderr
new file mode 100644
index 0000000000..bbbf7e7169
--- /dev/null
+++ b/third_party/rust/cstr/tests/compile_fail/non-str.stderr
@@ -0,0 +1,17 @@
+error: expected one of: byte string literal, string literal, identifier
+ --> $DIR/non-str.rs:4:22
+ |
+4 | let _foo = cstr!(1);
+ | ^
+
+error: expected one of: byte string literal, string literal, identifier
+ --> $DIR/non-str.rs:5:22
+ |
+5 | let _foo = cstr!(("a"));
+ | ^^^^^
+
+error: expected one of: byte string literal, string literal, identifier
+ --> $DIR/non-str.rs:6:22
+ |
+6 | let _foo = cstr!(&1);
+ | ^
diff --git a/third_party/rust/cstr/tests/compile_fail/trash-after.rs b/third_party/rust/cstr/tests/compile_fail/trash-after.rs
new file mode 100644
index 0000000000..5e4f7c3f08
--- /dev/null
+++ b/third_party/rust/cstr/tests/compile_fail/trash-after.rs
@@ -0,0 +1,5 @@
+use cstr::cstr;
+
+fn main() {
+ let _foo = cstr!("foo" + "bar");
+}
diff --git a/third_party/rust/cstr/tests/compile_fail/trash-after.stderr b/third_party/rust/cstr/tests/compile_fail/trash-after.stderr
new file mode 100644
index 0000000000..d6bb1530af
--- /dev/null
+++ b/third_party/rust/cstr/tests/compile_fail/trash-after.stderr
@@ -0,0 +1,5 @@
+error: unexpected token
+ --> $DIR/trash-after.rs:4:28
+ |
+4 | let _foo = cstr!("foo" + "bar");
+ | ^
diff --git a/third_party/rust/cstr/tests/compile_test.rs b/third_party/rust/cstr/tests/compile_test.rs
new file mode 100644
index 0000000000..b3b9d9f6ce
--- /dev/null
+++ b/third_party/rust/cstr/tests/compile_test.rs
@@ -0,0 +1,6 @@
+#[test]
+fn compile_test() {
+ let t = trybuild::TestCases::new();
+ t.pass("tests/pass/*.rs");
+ t.compile_fail("tests/compile_fail/*.rs");
+}
diff --git a/third_party/rust/cstr/tests/pass/byte_str_lit.rs b/third_party/rust/cstr/tests/pass/byte_str_lit.rs
new file mode 100644
index 0000000000..fd87f176c2
--- /dev/null
+++ b/third_party/rust/cstr/tests/pass/byte_str_lit.rs
@@ -0,0 +1,7 @@
+use cstr::cstr;
+use std::ffi::CStr;
+
+fn main() {
+ let foo: &'static CStr = cstr!(b"foo\xffbar");
+ assert_eq!(foo, CStr::from_bytes_with_nul(b"foo\xffbar\0").unwrap());
+}
diff --git a/third_party/rust/cstr/tests/pass/const.rs b/third_party/rust/cstr/tests/pass/const.rs
new file mode 100644
index 0000000000..ae8647b954
--- /dev/null
+++ b/third_party/rust/cstr/tests/pass/const.rs
@@ -0,0 +1,10 @@
+use cstr::cstr;
+use std::ffi::CStr;
+
+const FOO: &CStr = cstr!(b"foo\xffbar");
+static BAR: &CStr = cstr!("bar");
+
+fn main() {
+ assert_eq!(FOO, CStr::from_bytes_with_nul(b"foo\xffbar\0").unwrap());
+ assert_eq!(BAR, CStr::from_bytes_with_nul(b"bar\0").unwrap());
+}
diff --git a/third_party/rust/cstr/tests/pass/ident.rs b/third_party/rust/cstr/tests/pass/ident.rs
new file mode 100644
index 0000000000..55e41c8621
--- /dev/null
+++ b/third_party/rust/cstr/tests/pass/ident.rs
@@ -0,0 +1,9 @@
+use cstr::cstr;
+use std::ffi::CStr;
+
+fn main() {
+ let foo: &'static CStr = cstr!(foobar);
+ assert_eq!(foo, CStr::from_bytes_with_nul(b"foobar\0").unwrap());
+ let foo: &'static CStr = cstr!(r#foobar);
+ assert_eq!(foo, CStr::from_bytes_with_nul(b"r#foobar\0").unwrap());
+}
diff --git a/third_party/rust/cstr/tests/pass/macro.rs b/third_party/rust/cstr/tests/pass/macro.rs
new file mode 100644
index 0000000000..e89deac648
--- /dev/null
+++ b/third_party/rust/cstr/tests/pass/macro.rs
@@ -0,0 +1,21 @@
+use cstr::cstr;
+use std::ffi::CStr;
+
+macro_rules! cstr_expr {
+ ($s:expr) => {
+ cstr!($s)
+ };
+}
+
+macro_rules! cstr_literal {
+ ($s:literal) => {
+ cstr!($s)
+ };
+}
+
+fn main() {
+ let foo: &'static CStr = cstr_expr!("foo");
+ assert_eq!(foo, CStr::from_bytes_with_nul(b"foo\0").unwrap());
+ let bar: &'static CStr = cstr_literal!("bar");
+ assert_eq!(bar, CStr::from_bytes_with_nul(b"bar\0").unwrap());
+}
diff --git a/third_party/rust/cstr/tests/pass/str_lit.rs b/third_party/rust/cstr/tests/pass/str_lit.rs
new file mode 100644
index 0000000000..d925859a9a
--- /dev/null
+++ b/third_party/rust/cstr/tests/pass/str_lit.rs
@@ -0,0 +1,8 @@
+use cstr::cstr;
+use std::ffi::CStr;
+
+fn main() {
+ let foo: &'static CStr = cstr!("foo\u{4e00}bar");
+ let expected = b"foo\xe4\xb8\x80bar\0";
+ assert_eq!(foo, CStr::from_bytes_with_nul(expected).unwrap());
+}