diff options
Diffstat (limited to 'third_party/rust/cexpr/tests')
-rw-r--r-- | third_party/rust/cexpr/tests/clang.rs | 339 | ||||
-rw-r--r-- | third_party/rust/cexpr/tests/input/chars.h | 3 | ||||
-rw-r--r-- | third_party/rust/cexpr/tests/input/fail.h | 9 | ||||
-rw-r--r-- | third_party/rust/cexpr/tests/input/floats.h | 8 | ||||
-rw-r--r-- | third_party/rust/cexpr/tests/input/int_signed.h | 3 | ||||
-rw-r--r-- | third_party/rust/cexpr/tests/input/int_unsigned.h | 29 | ||||
-rw-r--r-- | third_party/rust/cexpr/tests/input/strings.h | 17 | ||||
-rw-r--r-- | third_party/rust/cexpr/tests/input/test_llvm_bug_9069.h | 4 |
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 |