summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cexpr/tests
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/cexpr/tests')
-rw-r--r--third_party/rust/cexpr/tests/clang.rs339
-rw-r--r--third_party/rust/cexpr/tests/input/chars.h3
-rw-r--r--third_party/rust/cexpr/tests/input/fail.h9
-rw-r--r--third_party/rust/cexpr/tests/input/floats.h8
-rw-r--r--third_party/rust/cexpr/tests/input/int_signed.h3
-rw-r--r--third_party/rust/cexpr/tests/input/int_unsigned.h29
-rw-r--r--third_party/rust/cexpr/tests/input/strings.h17
-rw-r--r--third_party/rust/cexpr/tests/input/test_llvm_bug_9069.h4
8 files changed, 412 insertions, 0 deletions
diff --git a/third_party/rust/cexpr/tests/clang.rs b/third_party/rust/cexpr/tests/clang.rs
new file mode 100644
index 0000000000..b2484f0778
--- /dev/null
+++ b/third_party/rust/cexpr/tests/clang.rs
@@ -0,0 +1,339 @@
+// (C) Copyright 2016 Jethro G. Beekman
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+extern crate cexpr;
+extern crate clang_sys;
+
+use std::collections::HashMap;
+use std::io::Write;
+use std::str::{self, FromStr};
+use std::{char, ffi, mem, ptr, slice};
+
+use cexpr::assert_full_parse;
+use cexpr::expr::{fn_macro_declaration, EvalResult, IdentifierParser};
+use cexpr::literal::CChar;
+use cexpr::token::Token;
+use clang_sys::*;
+
+// main testing routine
+fn test_definition(
+ ident: Vec<u8>,
+ tokens: &[Token],
+ idents: &mut HashMap<Vec<u8>, EvalResult>,
+) -> bool {
+ fn bytes_to_int(value: &[u8]) -> Option<EvalResult> {
+ str::from_utf8(value)
+ .ok()
+ .map(|s| s.replace("n", "-"))
+ .map(|s| s.replace("_", ""))
+ .and_then(|v| i64::from_str(&v).ok())
+ .map(::std::num::Wrapping)
+ .map(Int)
+ }
+
+ use cexpr::expr::EvalResult::*;
+
+ let display_name = String::from_utf8_lossy(&ident).into_owned();
+
+ let functional;
+ let test = {
+ // Split name such as Str_test_string into (Str,test_string)
+ let pos = ident
+ .iter()
+ .position(|c| *c == b'_')
+ .expect(&format!("Invalid definition in testcase: {}", display_name));
+ let mut expected = &ident[..pos];
+ let mut value = &ident[(pos + 1)..];
+
+ functional = expected == b"Fn";
+
+ if functional {
+ let ident = value;
+ let pos = ident
+ .iter()
+ .position(|c| *c == b'_')
+ .expect(&format!("Invalid definition in testcase: {}", display_name));
+ expected = &ident[..pos];
+ value = &ident[(pos + 1)..];
+ }
+
+ if expected == b"Str" {
+ let mut splits = value.split(|c| *c == b'U');
+ let mut s = Vec::with_capacity(value.len());
+ s.extend_from_slice(splits.next().unwrap());
+ for split in splits {
+ let (chr, rest) = split.split_at(6);
+ let chr = u32::from_str_radix(str::from_utf8(chr).unwrap(), 16).unwrap();
+ write!(s, "{}", char::from_u32(chr).unwrap()).unwrap();
+ s.extend_from_slice(rest);
+ }
+ Some(Str(s))
+ } else if expected == b"Int" {
+ bytes_to_int(value)
+ } else if expected == b"Float" {
+ str::from_utf8(value)
+ .ok()
+ .map(|s| s.replace("n", "-").replace("p", "."))
+ .and_then(|v| f64::from_str(&v).ok())
+ .map(Float)
+ } else if expected == b"CharRaw" {
+ str::from_utf8(value)
+ .ok()
+ .and_then(|v| u64::from_str(v).ok())
+ .map(CChar::Raw)
+ .map(Char)
+ } else if expected == b"CharChar" {
+ str::from_utf8(value)
+ .ok()
+ .and_then(|v| u32::from_str(v).ok())
+ .and_then(char::from_u32)
+ .map(CChar::Char)
+ .map(Char)
+ } else {
+ Some(Invalid)
+ }
+ .expect(&format!("Invalid definition in testcase: {}", display_name))
+ };
+
+ let result = if functional {
+ let mut fnidents;
+ let expr_tokens;
+ match fn_macro_declaration(&tokens) {
+ Ok((rest, (_, args))) => {
+ fnidents = idents.clone();
+ expr_tokens = rest;
+ for arg in args {
+ let val = match test {
+ Int(_) => bytes_to_int(&arg),
+ Str(_) => Some(Str(arg.to_owned())),
+ _ => unimplemented!(),
+ }
+ .expect(&format!(
+ "Invalid argument in functional macro testcase: {}",
+ display_name
+ ));
+ fnidents.insert(arg.to_owned(), val);
+ }
+ }
+ e => {
+ println!(
+ "Failed test for {}, unable to parse functional macro declaration: {:?}",
+ display_name, e
+ );
+ return false;
+ }
+ }
+ assert_full_parse(IdentifierParser::new(&fnidents).expr(&expr_tokens))
+ } else {
+ IdentifierParser::new(idents)
+ .macro_definition(&tokens)
+ .map(|(i, (_, val))| (i, val))
+ };
+
+ match result {
+ Ok((_, val)) => {
+ if val == test {
+ if let Some(_) = idents.insert(ident, val) {
+ panic!("Duplicate definition for testcase: {}", display_name);
+ }
+ true
+ } else {
+ println!(
+ "Failed test for {}, expected {:?}, got {:?}",
+ display_name, test, val
+ );
+ false
+ }
+ }
+ e => {
+ if test == Invalid {
+ true
+ } else {
+ println!(
+ "Failed test for {}, expected {:?}, got {:?}",
+ display_name, test, e
+ );
+ false
+ }
+ }
+ }
+}
+
+// support code for the clang lexer
+unsafe fn clang_str_to_vec(s: CXString) -> Vec<u8> {
+ let vec = ffi::CStr::from_ptr(clang_getCString(s))
+ .to_bytes()
+ .to_owned();
+ clang_disposeString(s);
+ vec
+}
+
+#[allow(non_upper_case_globals)]
+unsafe fn token_clang_to_cexpr(tu: CXTranslationUnit, orig: &CXToken) -> Token {
+ Token {
+ kind: match clang_getTokenKind(*orig) {
+ CXToken_Comment => cexpr::token::Kind::Comment,
+ CXToken_Identifier => cexpr::token::Kind::Identifier,
+ CXToken_Keyword => cexpr::token::Kind::Keyword,
+ CXToken_Literal => cexpr::token::Kind::Literal,
+ CXToken_Punctuation => cexpr::token::Kind::Punctuation,
+ _ => panic!("invalid token kind: {:?}", *orig),
+ },
+ raw: clang_str_to_vec(clang_getTokenSpelling(tu, *orig)).into_boxed_slice(),
+ }
+}
+
+extern "C" fn visit_children_thunk<F>(
+ cur: CXCursor,
+ parent: CXCursor,
+ closure: CXClientData,
+) -> CXChildVisitResult
+where
+ F: FnMut(CXCursor, CXCursor) -> CXChildVisitResult,
+{
+ unsafe { (&mut *(closure as *mut F))(cur, parent) }
+}
+
+unsafe fn visit_children<F>(cursor: CXCursor, mut f: F)
+where
+ F: FnMut(CXCursor, CXCursor) -> CXChildVisitResult,
+{
+ clang_visitChildren(
+ cursor,
+ visit_children_thunk::<F> as _,
+ &mut f as *mut F as CXClientData,
+ );
+}
+
+unsafe fn location_in_scope(r: CXSourceRange) -> bool {
+ let start = clang_getRangeStart(r);
+ let mut file = ptr::null_mut();
+ clang_getSpellingLocation(
+ start,
+ &mut file,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ );
+ clang_Location_isFromMainFile(start) != 0
+ && clang_Location_isInSystemHeader(start) == 0
+ && file != ptr::null_mut()
+}
+
+/// tokenize_range_adjust can be used to work around LLVM bug 9069
+/// https://bugs.llvm.org//show_bug.cgi?id=9069
+fn file_visit_macros<F: FnMut(Vec<u8>, Vec<Token>)>(
+ file: &str,
+ tokenize_range_adjust: bool,
+ mut visitor: F,
+) {
+ unsafe {
+ let tu = {
+ let index = clang_createIndex(true as _, false as _);
+ let cfile = ffi::CString::new(file).unwrap();
+ let mut tu = mem::MaybeUninit::uninit();
+ assert!(
+ clang_parseTranslationUnit2(
+ index,
+ cfile.as_ptr(),
+ [b"-std=c11\0".as_ptr() as *const ::std::os::raw::c_char].as_ptr(),
+ 1,
+ ptr::null_mut(),
+ 0,
+ CXTranslationUnit_DetailedPreprocessingRecord,
+ &mut *tu.as_mut_ptr()
+ ) == CXError_Success,
+ "Failure reading test case {}",
+ file
+ );
+ tu.assume_init()
+ };
+ visit_children(clang_getTranslationUnitCursor(tu), |cur, _parent| {
+ if cur.kind == CXCursor_MacroDefinition {
+ let mut range = clang_getCursorExtent(cur);
+ if !location_in_scope(range) {
+ return CXChildVisit_Continue;
+ }
+ range.end_int_data -= if tokenize_range_adjust { 1 } else { 0 };
+ let mut token_ptr = ptr::null_mut();
+ let mut num = 0;
+ clang_tokenize(tu, range, &mut token_ptr, &mut num);
+ if token_ptr != ptr::null_mut() {
+ let tokens = slice::from_raw_parts(token_ptr, num as usize);
+ let tokens: Vec<_> = tokens
+ .iter()
+ .filter_map(|t| {
+ if clang_getTokenKind(*t) != CXToken_Comment {
+ Some(token_clang_to_cexpr(tu, t))
+ } else {
+ None
+ }
+ })
+ .collect();
+ clang_disposeTokens(tu, token_ptr, num);
+ visitor(clang_str_to_vec(clang_getCursorSpelling(cur)), tokens)
+ }
+ }
+ CXChildVisit_Continue
+ });
+ clang_disposeTranslationUnit(tu);
+ };
+}
+
+fn test_file(file: &str) -> bool {
+ let mut idents = HashMap::new();
+ let mut all_succeeded = true;
+ file_visit_macros(file, fix_bug_9069(), |ident, tokens| {
+ all_succeeded &= test_definition(ident, &tokens, &mut idents)
+ });
+ all_succeeded
+}
+
+fn fix_bug_9069() -> bool {
+ fn check_bug_9069() -> bool {
+ let mut token_sets = vec![];
+ file_visit_macros(
+ "tests/input/test_llvm_bug_9069.h",
+ false,
+ |ident, tokens| {
+ assert_eq!(&ident, b"A");
+ token_sets.push(tokens);
+ },
+ );
+ assert_eq!(token_sets.len(), 2);
+ token_sets[0] != token_sets[1]
+ }
+
+ use std::sync::atomic::{AtomicBool, Ordering};
+ use std::sync::Once;
+
+ static CHECK_FIX: Once = Once::new();
+ static FIX: AtomicBool = AtomicBool::new(false);
+
+ CHECK_FIX.call_once(|| FIX.store(check_bug_9069(), Ordering::SeqCst));
+
+ FIX.load(Ordering::SeqCst)
+}
+
+macro_rules! test_file {
+ ($f:ident) => {
+ #[test]
+ fn $f() {
+ assert!(
+ test_file(concat!("tests/input/", stringify!($f), ".h")),
+ "test_file"
+ )
+ }
+ };
+}
+
+test_file!(floats);
+test_file!(chars);
+test_file!(strings);
+test_file!(int_signed);
+test_file!(int_unsigned);
+test_file!(fail);
diff --git a/third_party/rust/cexpr/tests/input/chars.h b/third_party/rust/cexpr/tests/input/chars.h
new file mode 100644
index 0000000000..45351d3259
--- /dev/null
+++ b/third_party/rust/cexpr/tests/input/chars.h
@@ -0,0 +1,3 @@
+#define CharChar_65 'A'
+#define CharChar_127849 '\U0001f369' // 🍩
+#define CharRaw_255 U'\xff'
diff --git a/third_party/rust/cexpr/tests/input/fail.h b/third_party/rust/cexpr/tests/input/fail.h
new file mode 100644
index 0000000000..fd416bc7cb
--- /dev/null
+++ b/third_party/rust/cexpr/tests/input/fail.h
@@ -0,0 +1,9 @@
+#define FAIL_function_like(x) 3
+#define FAIL_empty
+#define FAIL_invalid_for_radix 0b2
+#define FAIL_shift_by_float 3<<1f
+#define FAIL_unknown_identifier UNKNOWN
+#define Int_0 0
+#define Str_str "str"
+#define FAIL_concat_integer "test" Str_str Int_0
+#define FAIL_too_large_int 18446744073709551616
diff --git a/third_party/rust/cexpr/tests/input/floats.h b/third_party/rust/cexpr/tests/input/floats.h
new file mode 100644
index 0000000000..61942cf41f
--- /dev/null
+++ b/third_party/rust/cexpr/tests/input/floats.h
@@ -0,0 +1,8 @@
+#define Float_0 0.
+#define Float_1 1f
+#define Float_p1 .1
+#define Float_2 2.0
+#define Float_1000 1e3
+#define Float_2000 2e+3
+#define Float_p001 1e-3
+#define Float_80 10.0*(1<<3)
diff --git a/third_party/rust/cexpr/tests/input/int_signed.h b/third_party/rust/cexpr/tests/input/int_signed.h
new file mode 100644
index 0000000000..65854a63e3
--- /dev/null
+++ b/third_party/rust/cexpr/tests/input/int_signed.h
@@ -0,0 +1,3 @@
+#define Int_n3 -(-(-3))
+#define Int_n5 -3-2
+#define Int_n9223372036854775808 -9223372036854775808
diff --git a/third_party/rust/cexpr/tests/input/int_unsigned.h b/third_party/rust/cexpr/tests/input/int_unsigned.h
new file mode 100644
index 0000000000..6663dda3d6
--- /dev/null
+++ b/third_party/rust/cexpr/tests/input/int_unsigned.h
@@ -0,0 +1,29 @@
+#define Int_456 456
+#define Int_0 0
+#define Int_1 0b1
+#define Int_2 0x2
+#define Int_3 3L
+#define Int_4 0X4
+#define Int_5 0B101
+#define Int_63 077
+#define Int_123 123
+#define Int_124 124u
+#define Int_125 125uL
+#define Int_126 126LuL
+#define Int_16 (((1)<<4ULL))/*comment*/
+#define Int_13 1|8^6&2<<1
+
+#define Int_47 32|15
+#define Int_38 (32|15)^9
+#define Int_6 ((32|15)^9)&7
+#define Int_12 (((32|15)^9)&7)<<1
+#define Int_17 ((((32|15)^9)&7)<<1)+5
+#define Int_15 (((((32|15)^9)&7)<<1)+5)-2
+#define Int_60 ((((((32|15)^9)&7)<<1)+5)-2)*4
+#define Int_30 (((((((32|15)^9)&7)<<1)+5)-2)*4)/2
+#define Int_39 32|15^9&7<<1+5-2*4/2
+
+#define Int_n1 18446744073709551615 /*2^64-1*/
+#define Int_n9223372036854775808 9223372036854775808
+
+#define Fn_Int_9(_3) _3*3
diff --git a/third_party/rust/cexpr/tests/input/strings.h b/third_party/rust/cexpr/tests/input/strings.h
new file mode 100644
index 0000000000..d01d409cbf
--- /dev/null
+++ b/third_party/rust/cexpr/tests/input/strings.h
@@ -0,0 +1,17 @@
+#define Str_ ""
+#define Str_str "str"
+#define Str_unicode u"unicode"
+#define Str_long L"long"
+#define Str_concat u"con" L"cat"
+#define Str_concat_parens ("concat" U"_parens")
+#define Str_concat_identifier (Str_concat L"_identifier")
+#define Str_hex_escape_all "\x68\x65\x78\x5f\x65\x73\x63\x61\x70\x65\x5f\x61\x6c\x6c"
+#define Str_hex_escape_hex "h\x65x_\x65s\x63\x61p\x65_h\x65x"
+#define Str_quote_U000022_escape "quote_\"_escape"
+#define Str_Fly_away_in_my_space_U01F680_You_no_need_put_U01F4B5_in_my_pocket \
+ u8"Fly_away_in_my_space_🚀_You_no_need_put_💵_in_my_pocket"
+#define Fn_Str_no_args() "no_args"
+#define Fn_Str_no_args_concat() "no_args_" Str_concat
+#define Fn_Str_prepend_arg(arg) "prepend_" arg
+#define Fn_Str_two_args(two, args) two "_" args
+#define Fn_Str_three_args(three, _, args) three _ args
diff --git a/third_party/rust/cexpr/tests/input/test_llvm_bug_9069.h b/third_party/rust/cexpr/tests/input/test_llvm_bug_9069.h
new file mode 100644
index 0000000000..a92374efee
--- /dev/null
+++ b/third_party/rust/cexpr/tests/input/test_llvm_bug_9069.h
@@ -0,0 +1,4 @@
+// The following two definitions should yield the same list of tokens.
+// If https://bugs.llvm.org//show_bug.cgi?id=9069 is not fixed, they don't.
+#define A 1
+#define A 1