167c3a3e4Sopenharmony_ci// (C) Copyright 2016 Jethro G. Beekman 267c3a3e4Sopenharmony_ci// 367c3a3e4Sopenharmony_ci// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or 467c3a3e4Sopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license 567c3a3e4Sopenharmony_ci// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your 667c3a3e4Sopenharmony_ci// option. This file may not be copied, modified, or distributed 767c3a3e4Sopenharmony_ci// except according to those terms. 867c3a3e4Sopenharmony_ciextern crate cexpr; 967c3a3e4Sopenharmony_ciextern crate clang_sys; 1067c3a3e4Sopenharmony_ci 1167c3a3e4Sopenharmony_ciuse std::collections::HashMap; 1267c3a3e4Sopenharmony_ciuse std::io::Write; 1367c3a3e4Sopenharmony_ciuse std::str::{self, FromStr}; 1467c3a3e4Sopenharmony_ciuse std::{char, ffi, mem, ptr, slice}; 1567c3a3e4Sopenharmony_ci 1667c3a3e4Sopenharmony_ciuse cexpr::assert_full_parse; 1767c3a3e4Sopenharmony_ciuse cexpr::expr::{fn_macro_declaration, EvalResult, IdentifierParser}; 1867c3a3e4Sopenharmony_ciuse cexpr::literal::CChar; 1967c3a3e4Sopenharmony_ciuse cexpr::token::Token; 2067c3a3e4Sopenharmony_ciuse clang_sys::*; 2167c3a3e4Sopenharmony_ci 2267c3a3e4Sopenharmony_ci// main testing routine 2367c3a3e4Sopenharmony_cifn test_definition( 2467c3a3e4Sopenharmony_ci ident: Vec<u8>, 2567c3a3e4Sopenharmony_ci tokens: &[Token], 2667c3a3e4Sopenharmony_ci idents: &mut HashMap<Vec<u8>, EvalResult>, 2767c3a3e4Sopenharmony_ci) -> bool { 2867c3a3e4Sopenharmony_ci fn bytes_to_int(value: &[u8]) -> Option<EvalResult> { 2967c3a3e4Sopenharmony_ci str::from_utf8(value) 3067c3a3e4Sopenharmony_ci .ok() 3167c3a3e4Sopenharmony_ci .map(|s| s.replace("n", "-")) 3267c3a3e4Sopenharmony_ci .map(|s| s.replace("_", "")) 3367c3a3e4Sopenharmony_ci .and_then(|v| i64::from_str(&v).ok()) 3467c3a3e4Sopenharmony_ci .map(::std::num::Wrapping) 3567c3a3e4Sopenharmony_ci .map(Int) 3667c3a3e4Sopenharmony_ci } 3767c3a3e4Sopenharmony_ci 3867c3a3e4Sopenharmony_ci use cexpr::expr::EvalResult::*; 3967c3a3e4Sopenharmony_ci 4067c3a3e4Sopenharmony_ci let display_name = String::from_utf8_lossy(&ident).into_owned(); 4167c3a3e4Sopenharmony_ci 4267c3a3e4Sopenharmony_ci let functional; 4367c3a3e4Sopenharmony_ci let test = { 4467c3a3e4Sopenharmony_ci // Split name such as Str_test_string into (Str,test_string) 4567c3a3e4Sopenharmony_ci let pos = ident 4667c3a3e4Sopenharmony_ci .iter() 4767c3a3e4Sopenharmony_ci .position(|c| *c == b'_') 4867c3a3e4Sopenharmony_ci .expect(&format!("Invalid definition in testcase: {}", display_name)); 4967c3a3e4Sopenharmony_ci let mut expected = &ident[..pos]; 5067c3a3e4Sopenharmony_ci let mut value = &ident[(pos + 1)..]; 5167c3a3e4Sopenharmony_ci 5267c3a3e4Sopenharmony_ci functional = expected == b"Fn"; 5367c3a3e4Sopenharmony_ci 5467c3a3e4Sopenharmony_ci if functional { 5567c3a3e4Sopenharmony_ci let ident = value; 5667c3a3e4Sopenharmony_ci let pos = ident 5767c3a3e4Sopenharmony_ci .iter() 5867c3a3e4Sopenharmony_ci .position(|c| *c == b'_') 5967c3a3e4Sopenharmony_ci .expect(&format!("Invalid definition in testcase: {}", display_name)); 6067c3a3e4Sopenharmony_ci expected = &ident[..pos]; 6167c3a3e4Sopenharmony_ci value = &ident[(pos + 1)..]; 6267c3a3e4Sopenharmony_ci } 6367c3a3e4Sopenharmony_ci 6467c3a3e4Sopenharmony_ci if expected == b"Str" { 6567c3a3e4Sopenharmony_ci let mut splits = value.split(|c| *c == b'U'); 6667c3a3e4Sopenharmony_ci let mut s = Vec::with_capacity(value.len()); 6767c3a3e4Sopenharmony_ci s.extend_from_slice(splits.next().unwrap()); 6867c3a3e4Sopenharmony_ci for split in splits { 6967c3a3e4Sopenharmony_ci let (chr, rest) = split.split_at(6); 7067c3a3e4Sopenharmony_ci let chr = u32::from_str_radix(str::from_utf8(chr).unwrap(), 16).unwrap(); 7167c3a3e4Sopenharmony_ci write!(s, "{}", char::from_u32(chr).unwrap()).unwrap(); 7267c3a3e4Sopenharmony_ci s.extend_from_slice(rest); 7367c3a3e4Sopenharmony_ci } 7467c3a3e4Sopenharmony_ci Some(Str(s)) 7567c3a3e4Sopenharmony_ci } else if expected == b"Int" { 7667c3a3e4Sopenharmony_ci bytes_to_int(value) 7767c3a3e4Sopenharmony_ci } else if expected == b"Float" { 7867c3a3e4Sopenharmony_ci str::from_utf8(value) 7967c3a3e4Sopenharmony_ci .ok() 8067c3a3e4Sopenharmony_ci .map(|s| s.replace("n", "-").replace("p", ".")) 8167c3a3e4Sopenharmony_ci .and_then(|v| f64::from_str(&v).ok()) 8267c3a3e4Sopenharmony_ci .map(Float) 8367c3a3e4Sopenharmony_ci } else if expected == b"CharRaw" { 8467c3a3e4Sopenharmony_ci str::from_utf8(value) 8567c3a3e4Sopenharmony_ci .ok() 8667c3a3e4Sopenharmony_ci .and_then(|v| u64::from_str(v).ok()) 8767c3a3e4Sopenharmony_ci .map(CChar::Raw) 8867c3a3e4Sopenharmony_ci .map(Char) 8967c3a3e4Sopenharmony_ci } else if expected == b"CharChar" { 9067c3a3e4Sopenharmony_ci str::from_utf8(value) 9167c3a3e4Sopenharmony_ci .ok() 9267c3a3e4Sopenharmony_ci .and_then(|v| u32::from_str(v).ok()) 9367c3a3e4Sopenharmony_ci .and_then(char::from_u32) 9467c3a3e4Sopenharmony_ci .map(CChar::Char) 9567c3a3e4Sopenharmony_ci .map(Char) 9667c3a3e4Sopenharmony_ci } else { 9767c3a3e4Sopenharmony_ci Some(Invalid) 9867c3a3e4Sopenharmony_ci } 9967c3a3e4Sopenharmony_ci .expect(&format!("Invalid definition in testcase: {}", display_name)) 10067c3a3e4Sopenharmony_ci }; 10167c3a3e4Sopenharmony_ci 10267c3a3e4Sopenharmony_ci let result = if functional { 10367c3a3e4Sopenharmony_ci let mut fnidents; 10467c3a3e4Sopenharmony_ci let expr_tokens; 10567c3a3e4Sopenharmony_ci match fn_macro_declaration(&tokens) { 10667c3a3e4Sopenharmony_ci Ok((rest, (_, args))) => { 10767c3a3e4Sopenharmony_ci fnidents = idents.clone(); 10867c3a3e4Sopenharmony_ci expr_tokens = rest; 10967c3a3e4Sopenharmony_ci for arg in args { 11067c3a3e4Sopenharmony_ci let val = match test { 11167c3a3e4Sopenharmony_ci Int(_) => bytes_to_int(&arg), 11267c3a3e4Sopenharmony_ci Str(_) => Some(Str(arg.to_owned())), 11367c3a3e4Sopenharmony_ci _ => unimplemented!(), 11467c3a3e4Sopenharmony_ci } 11567c3a3e4Sopenharmony_ci .expect(&format!( 11667c3a3e4Sopenharmony_ci "Invalid argument in functional macro testcase: {}", 11767c3a3e4Sopenharmony_ci display_name 11867c3a3e4Sopenharmony_ci )); 11967c3a3e4Sopenharmony_ci fnidents.insert(arg.to_owned(), val); 12067c3a3e4Sopenharmony_ci } 12167c3a3e4Sopenharmony_ci } 12267c3a3e4Sopenharmony_ci e => { 12367c3a3e4Sopenharmony_ci println!( 12467c3a3e4Sopenharmony_ci "Failed test for {}, unable to parse functional macro declaration: {:?}", 12567c3a3e4Sopenharmony_ci display_name, e 12667c3a3e4Sopenharmony_ci ); 12767c3a3e4Sopenharmony_ci return false; 12867c3a3e4Sopenharmony_ci } 12967c3a3e4Sopenharmony_ci } 13067c3a3e4Sopenharmony_ci assert_full_parse(IdentifierParser::new(&fnidents).expr(&expr_tokens)) 13167c3a3e4Sopenharmony_ci } else { 13267c3a3e4Sopenharmony_ci IdentifierParser::new(idents) 13367c3a3e4Sopenharmony_ci .macro_definition(&tokens) 13467c3a3e4Sopenharmony_ci .map(|(i, (_, val))| (i, val)) 13567c3a3e4Sopenharmony_ci }; 13667c3a3e4Sopenharmony_ci 13767c3a3e4Sopenharmony_ci match result { 13867c3a3e4Sopenharmony_ci Ok((_, val)) => { 13967c3a3e4Sopenharmony_ci if val == test { 14067c3a3e4Sopenharmony_ci if let Some(_) = idents.insert(ident, val) { 14167c3a3e4Sopenharmony_ci panic!("Duplicate definition for testcase: {}", display_name); 14267c3a3e4Sopenharmony_ci } 14367c3a3e4Sopenharmony_ci true 14467c3a3e4Sopenharmony_ci } else { 14567c3a3e4Sopenharmony_ci println!( 14667c3a3e4Sopenharmony_ci "Failed test for {}, expected {:?}, got {:?}", 14767c3a3e4Sopenharmony_ci display_name, test, val 14867c3a3e4Sopenharmony_ci ); 14967c3a3e4Sopenharmony_ci false 15067c3a3e4Sopenharmony_ci } 15167c3a3e4Sopenharmony_ci } 15267c3a3e4Sopenharmony_ci e => { 15367c3a3e4Sopenharmony_ci if test == Invalid { 15467c3a3e4Sopenharmony_ci true 15567c3a3e4Sopenharmony_ci } else { 15667c3a3e4Sopenharmony_ci println!( 15767c3a3e4Sopenharmony_ci "Failed test for {}, expected {:?}, got {:?}", 15867c3a3e4Sopenharmony_ci display_name, test, e 15967c3a3e4Sopenharmony_ci ); 16067c3a3e4Sopenharmony_ci false 16167c3a3e4Sopenharmony_ci } 16267c3a3e4Sopenharmony_ci } 16367c3a3e4Sopenharmony_ci } 16467c3a3e4Sopenharmony_ci} 16567c3a3e4Sopenharmony_ci 16667c3a3e4Sopenharmony_ci// support code for the clang lexer 16767c3a3e4Sopenharmony_ciunsafe fn clang_str_to_vec(s: CXString) -> Vec<u8> { 16867c3a3e4Sopenharmony_ci let vec = ffi::CStr::from_ptr(clang_getCString(s)) 16967c3a3e4Sopenharmony_ci .to_bytes() 17067c3a3e4Sopenharmony_ci .to_owned(); 17167c3a3e4Sopenharmony_ci clang_disposeString(s); 17267c3a3e4Sopenharmony_ci vec 17367c3a3e4Sopenharmony_ci} 17467c3a3e4Sopenharmony_ci 17567c3a3e4Sopenharmony_ci#[allow(non_upper_case_globals)] 17667c3a3e4Sopenharmony_ciunsafe fn token_clang_to_cexpr(tu: CXTranslationUnit, orig: &CXToken) -> Token { 17767c3a3e4Sopenharmony_ci Token { 17867c3a3e4Sopenharmony_ci kind: match clang_getTokenKind(*orig) { 17967c3a3e4Sopenharmony_ci CXToken_Comment => cexpr::token::Kind::Comment, 18067c3a3e4Sopenharmony_ci CXToken_Identifier => cexpr::token::Kind::Identifier, 18167c3a3e4Sopenharmony_ci CXToken_Keyword => cexpr::token::Kind::Keyword, 18267c3a3e4Sopenharmony_ci CXToken_Literal => cexpr::token::Kind::Literal, 18367c3a3e4Sopenharmony_ci CXToken_Punctuation => cexpr::token::Kind::Punctuation, 18467c3a3e4Sopenharmony_ci _ => panic!("invalid token kind: {:?}", *orig), 18567c3a3e4Sopenharmony_ci }, 18667c3a3e4Sopenharmony_ci raw: clang_str_to_vec(clang_getTokenSpelling(tu, *orig)).into_boxed_slice(), 18767c3a3e4Sopenharmony_ci } 18867c3a3e4Sopenharmony_ci} 18967c3a3e4Sopenharmony_ci 19067c3a3e4Sopenharmony_ciextern "C" fn visit_children_thunk<F>( 19167c3a3e4Sopenharmony_ci cur: CXCursor, 19267c3a3e4Sopenharmony_ci parent: CXCursor, 19367c3a3e4Sopenharmony_ci closure: CXClientData, 19467c3a3e4Sopenharmony_ci) -> CXChildVisitResult 19567c3a3e4Sopenharmony_ciwhere 19667c3a3e4Sopenharmony_ci F: FnMut(CXCursor, CXCursor) -> CXChildVisitResult, 19767c3a3e4Sopenharmony_ci{ 19867c3a3e4Sopenharmony_ci unsafe { (&mut *(closure as *mut F))(cur, parent) } 19967c3a3e4Sopenharmony_ci} 20067c3a3e4Sopenharmony_ci 20167c3a3e4Sopenharmony_ciunsafe fn visit_children<F>(cursor: CXCursor, mut f: F) 20267c3a3e4Sopenharmony_ciwhere 20367c3a3e4Sopenharmony_ci F: FnMut(CXCursor, CXCursor) -> CXChildVisitResult, 20467c3a3e4Sopenharmony_ci{ 20567c3a3e4Sopenharmony_ci clang_visitChildren( 20667c3a3e4Sopenharmony_ci cursor, 20767c3a3e4Sopenharmony_ci visit_children_thunk::<F> as _, 20867c3a3e4Sopenharmony_ci &mut f as *mut F as CXClientData, 20967c3a3e4Sopenharmony_ci ); 21067c3a3e4Sopenharmony_ci} 21167c3a3e4Sopenharmony_ci 21267c3a3e4Sopenharmony_ciunsafe fn location_in_scope(r: CXSourceRange) -> bool { 21367c3a3e4Sopenharmony_ci let start = clang_getRangeStart(r); 21467c3a3e4Sopenharmony_ci let mut file = ptr::null_mut(); 21567c3a3e4Sopenharmony_ci clang_getSpellingLocation( 21667c3a3e4Sopenharmony_ci start, 21767c3a3e4Sopenharmony_ci &mut file, 21867c3a3e4Sopenharmony_ci ptr::null_mut(), 21967c3a3e4Sopenharmony_ci ptr::null_mut(), 22067c3a3e4Sopenharmony_ci ptr::null_mut(), 22167c3a3e4Sopenharmony_ci ); 22267c3a3e4Sopenharmony_ci clang_Location_isFromMainFile(start) != 0 22367c3a3e4Sopenharmony_ci && clang_Location_isInSystemHeader(start) == 0 22467c3a3e4Sopenharmony_ci && file != ptr::null_mut() 22567c3a3e4Sopenharmony_ci} 22667c3a3e4Sopenharmony_ci 22767c3a3e4Sopenharmony_ci/// tokenize_range_adjust can be used to work around LLVM bug 9069 22867c3a3e4Sopenharmony_ci/// https://bugs.llvm.org//show_bug.cgi?id=9069 22967c3a3e4Sopenharmony_cifn file_visit_macros<F: FnMut(Vec<u8>, Vec<Token>)>( 23067c3a3e4Sopenharmony_ci file: &str, 23167c3a3e4Sopenharmony_ci tokenize_range_adjust: bool, 23267c3a3e4Sopenharmony_ci mut visitor: F, 23367c3a3e4Sopenharmony_ci) { 23467c3a3e4Sopenharmony_ci unsafe { 23567c3a3e4Sopenharmony_ci let tu = { 23667c3a3e4Sopenharmony_ci let index = clang_createIndex(true as _, false as _); 23767c3a3e4Sopenharmony_ci let cfile = ffi::CString::new(file).unwrap(); 23867c3a3e4Sopenharmony_ci let mut tu = mem::MaybeUninit::uninit(); 23967c3a3e4Sopenharmony_ci assert!( 24067c3a3e4Sopenharmony_ci clang_parseTranslationUnit2( 24167c3a3e4Sopenharmony_ci index, 24267c3a3e4Sopenharmony_ci cfile.as_ptr(), 24367c3a3e4Sopenharmony_ci [b"-std=c11\0".as_ptr() as *const ::std::os::raw::c_char].as_ptr(), 24467c3a3e4Sopenharmony_ci 1, 24567c3a3e4Sopenharmony_ci ptr::null_mut(), 24667c3a3e4Sopenharmony_ci 0, 24767c3a3e4Sopenharmony_ci CXTranslationUnit_DetailedPreprocessingRecord, 24867c3a3e4Sopenharmony_ci &mut *tu.as_mut_ptr() 24967c3a3e4Sopenharmony_ci ) == CXError_Success, 25067c3a3e4Sopenharmony_ci "Failure reading test case {}", 25167c3a3e4Sopenharmony_ci file 25267c3a3e4Sopenharmony_ci ); 25367c3a3e4Sopenharmony_ci tu.assume_init() 25467c3a3e4Sopenharmony_ci }; 25567c3a3e4Sopenharmony_ci visit_children(clang_getTranslationUnitCursor(tu), |cur, _parent| { 25667c3a3e4Sopenharmony_ci if cur.kind == CXCursor_MacroDefinition { 25767c3a3e4Sopenharmony_ci let mut range = clang_getCursorExtent(cur); 25867c3a3e4Sopenharmony_ci if !location_in_scope(range) { 25967c3a3e4Sopenharmony_ci return CXChildVisit_Continue; 26067c3a3e4Sopenharmony_ci } 26167c3a3e4Sopenharmony_ci range.end_int_data -= if tokenize_range_adjust { 1 } else { 0 }; 26267c3a3e4Sopenharmony_ci let mut token_ptr = ptr::null_mut(); 26367c3a3e4Sopenharmony_ci let mut num = 0; 26467c3a3e4Sopenharmony_ci clang_tokenize(tu, range, &mut token_ptr, &mut num); 26567c3a3e4Sopenharmony_ci if token_ptr != ptr::null_mut() { 26667c3a3e4Sopenharmony_ci let tokens = slice::from_raw_parts(token_ptr, num as usize); 26767c3a3e4Sopenharmony_ci let tokens: Vec<_> = tokens 26867c3a3e4Sopenharmony_ci .iter() 26967c3a3e4Sopenharmony_ci .filter_map(|t| { 27067c3a3e4Sopenharmony_ci if clang_getTokenKind(*t) != CXToken_Comment { 27167c3a3e4Sopenharmony_ci Some(token_clang_to_cexpr(tu, t)) 27267c3a3e4Sopenharmony_ci } else { 27367c3a3e4Sopenharmony_ci None 27467c3a3e4Sopenharmony_ci } 27567c3a3e4Sopenharmony_ci }) 27667c3a3e4Sopenharmony_ci .collect(); 27767c3a3e4Sopenharmony_ci clang_disposeTokens(tu, token_ptr, num); 27867c3a3e4Sopenharmony_ci visitor(clang_str_to_vec(clang_getCursorSpelling(cur)), tokens) 27967c3a3e4Sopenharmony_ci } 28067c3a3e4Sopenharmony_ci } 28167c3a3e4Sopenharmony_ci CXChildVisit_Continue 28267c3a3e4Sopenharmony_ci }); 28367c3a3e4Sopenharmony_ci clang_disposeTranslationUnit(tu); 28467c3a3e4Sopenharmony_ci }; 28567c3a3e4Sopenharmony_ci} 28667c3a3e4Sopenharmony_ci 28767c3a3e4Sopenharmony_cifn test_file(file: &str) -> bool { 28867c3a3e4Sopenharmony_ci let mut idents = HashMap::new(); 28967c3a3e4Sopenharmony_ci let mut all_succeeded = true; 29067c3a3e4Sopenharmony_ci file_visit_macros(file, fix_bug_9069(), |ident, tokens| { 29167c3a3e4Sopenharmony_ci all_succeeded &= test_definition(ident, &tokens, &mut idents) 29267c3a3e4Sopenharmony_ci }); 29367c3a3e4Sopenharmony_ci all_succeeded 29467c3a3e4Sopenharmony_ci} 29567c3a3e4Sopenharmony_ci 29667c3a3e4Sopenharmony_cifn fix_bug_9069() -> bool { 29767c3a3e4Sopenharmony_ci fn check_bug_9069() -> bool { 29867c3a3e4Sopenharmony_ci let mut token_sets = vec![]; 29967c3a3e4Sopenharmony_ci file_visit_macros( 30067c3a3e4Sopenharmony_ci "tests/input/test_llvm_bug_9069.h", 30167c3a3e4Sopenharmony_ci false, 30267c3a3e4Sopenharmony_ci |ident, tokens| { 30367c3a3e4Sopenharmony_ci assert_eq!(&ident, b"A"); 30467c3a3e4Sopenharmony_ci token_sets.push(tokens); 30567c3a3e4Sopenharmony_ci }, 30667c3a3e4Sopenharmony_ci ); 30767c3a3e4Sopenharmony_ci assert_eq!(token_sets.len(), 2); 30867c3a3e4Sopenharmony_ci token_sets[0] != token_sets[1] 30967c3a3e4Sopenharmony_ci } 31067c3a3e4Sopenharmony_ci 31167c3a3e4Sopenharmony_ci use std::sync::atomic::{AtomicBool, Ordering}; 31267c3a3e4Sopenharmony_ci use std::sync::Once; 31367c3a3e4Sopenharmony_ci 31467c3a3e4Sopenharmony_ci static CHECK_FIX: Once = Once::new(); 31567c3a3e4Sopenharmony_ci static FIX: AtomicBool = AtomicBool::new(false); 31667c3a3e4Sopenharmony_ci 31767c3a3e4Sopenharmony_ci CHECK_FIX.call_once(|| FIX.store(check_bug_9069(), Ordering::SeqCst)); 31867c3a3e4Sopenharmony_ci 31967c3a3e4Sopenharmony_ci FIX.load(Ordering::SeqCst) 32067c3a3e4Sopenharmony_ci} 32167c3a3e4Sopenharmony_ci 32267c3a3e4Sopenharmony_cimacro_rules! test_file { 32367c3a3e4Sopenharmony_ci ($f:ident) => { 32467c3a3e4Sopenharmony_ci #[test] 32567c3a3e4Sopenharmony_ci fn $f() { 32667c3a3e4Sopenharmony_ci assert!( 32767c3a3e4Sopenharmony_ci test_file(concat!("tests/input/", stringify!($f), ".h")), 32867c3a3e4Sopenharmony_ci "test_file" 32967c3a3e4Sopenharmony_ci ) 33067c3a3e4Sopenharmony_ci } 33167c3a3e4Sopenharmony_ci }; 33267c3a3e4Sopenharmony_ci} 33367c3a3e4Sopenharmony_ci 33467c3a3e4Sopenharmony_citest_file!(floats); 33567c3a3e4Sopenharmony_citest_file!(chars); 33667c3a3e4Sopenharmony_citest_file!(strings); 33767c3a3e4Sopenharmony_citest_file!(int_signed); 33867c3a3e4Sopenharmony_citest_file!(int_unsigned); 33967c3a3e4Sopenharmony_citest_file!(fail); 340