use super::cpp_token::CppStandard;
pub fn cplusplus_value(standard: CppStandard) -> i64 {
match standard {
CppStandard::Cxx98 | CppStandard::GnuXx98 => 199711,
CppStandard::Cxx03 | CppStandard::GnuXx03 => 199711,
CppStandard::Cxx11 | CppStandard::GnuXx11 => 201103,
CppStandard::Cxx14 | CppStandard::GnuXx14 => 201402,
CppStandard::Cxx17 | CppStandard::GnuXx17 => 201703,
CppStandard::Cxx20 | CppStandard::GnuXx20 => 202002,
CppStandard::Cxx23 | CppStandard::GnuXx23 => 202302,
}
}
pub fn cpp_predefined_macros(standard: CppStandard) -> Vec<(String, String)> {
let mut macros = Vec::new();
macros.push(("__cplusplus".into(), cplusplus_value(standard).to_string()));
macros.push(("__STDCPP_DEFAULT_NEW_ALIGNMENT__".into(), "16".into()));
macros.push(("__cpp_raw_strings".into(), "200710".into()));
macros.push(("__cpp_unicode_characters".into(), "200704".into()));
macros.push(("__cpp_user_defined_literals".into(), "200809".into()));
macros.push(("__cpp_lambdas".into(), "200907".into()));
macros.push((
"__cpp_constexpr".into(),
if standard >= CppStandard::Cxx14 {
"201304".into()
} else {
"200704".into()
},
));
macros.push((
"__cpp_range_based_for".into(),
if standard >= CppStandard::Cxx17 {
"201603".into()
} else {
"200907".into()
},
));
macros.push((
"__cpp_static_assert".into(),
if standard >= CppStandard::Cxx17 {
"201411".into()
} else {
"200410".into()
},
));
macros.push(("__cpp_decltype".into(), "200707".into()));
macros.push(("__cpp_attributes".into(), "200809".into()));
macros.push(("__cpp_rvalue_references".into(), "200610".into()));
macros.push(("__cpp_variadic_templates".into(), "200704".into()));
macros.push(("__cpp_initializer_lists".into(), "200806".into()));
macros.push(("__cpp_delegating_constructors".into(), "200604".into()));
macros.push(("__cpp_nsdmi".into(), "200809".into()));
macros.push((
"__cpp_inheriting_constructors".into(),
if standard >= CppStandard::Cxx17 {
"201511".into()
} else {
"200802".into()
},
));
macros.push(("__cpp_ref_qualifiers".into(), "200710".into()));
macros.push(("__cpp_alias_templates".into(), "200704".into()));
if standard >= CppStandard::Cxx14 {
macros.push(("__cpp_return_type_deduction".into(), "201304".into()));
macros.push(("__cpp_decltype_auto".into(), "201304".into()));
macros.push(("__cpp_generic_lambdas".into(), "201304".into()));
macros.push(("__cpp_variable_templates".into(), "201304".into()));
macros.push(("__cpp_binary_literals".into(), "201304".into()));
macros.push(("__cpp_digit_separators".into(), "201309".into()));
macros.push(("__cpp_init_captures".into(), "201304".into()));
macros.push(("__cpp_aggregate_nsdmi".into(), "201304".into()));
}
if standard >= CppStandard::Cxx17 {
macros.push(("__cpp_structured_bindings".into(), "201606".into()));
macros.push(("__cpp_deduction_guides".into(), "201703".into()));
macros.push(("__cpp_if_constexpr".into(), "201606".into()));
macros.push(("__cpp_inline_variables".into(), "201606".into()));
macros.push(("__cpp_fold_expressions".into(), "201603".into()));
macros.push(("__cpp_capture_star_this".into(), "201603".into()));
macros.push(("__cpp_namespace_attributes".into(), "201411".into()));
macros.push(("__cpp_nested_namespace_definitions".into(), "201411".into()));
macros.push(("__cpp_noexcept_function_type".into(), "201510".into()));
macros.push(("__cpp_template_auto".into(), "201606".into()));
macros.push(("__cpp_variadic_using".into(), "201611".into()));
macros.push(("__cpp_guaranteed_copy_elision".into(), "201606".into()));
macros.push(("__cpp_hex_float".into(), "201603".into()));
macros.push(("__cpp_constexpr_dynamic_alloc".into(), "201907".into()));
}
if standard >= CppStandard::Cxx20 {
macros.push(("__cpp_concepts".into(), "202002".into()));
macros.push(("__cpp_coroutines".into(), "201902".into()));
macros.push(("__cpp_modules".into(), "201907".into()));
macros.push(("__cpp_using_enum".into(), "201907".into()));
macros.push(("__cpp_constexpr_in_virtual".into(), "202002".into()));
macros.push(("__cpp_aggregate_paren_init".into(), "201902".into()));
macros.push(("__cpp_char8_t".into(), "201811".into()));
macros.push(("__cpp_conditional_explicit".into(), "201806".into()));
macros.push(("__cpp_consteval".into(), "201811".into()));
macros.push(("__cpp_constinit".into(), "201907".into()));
macros.push(("__cpp_designated_initializers".into(), "201707".into()));
macros.push(("__cpp_implicit_move".into(), "202002".into()));
macros.push(("__cpp_nontype_template_args".into(), "201911".into()));
macros.push((
"__cpp_nontype_template_parameter_auto".into(),
"201606".into(),
));
macros.push(("__cpp_range_based_for".into(), "201603".into()));
macros.push(("__cpp_three_way_comparison".into(), "201907".into()));
macros.push(("__cpp_lib_concepts".into(), "202002".into()));
macros.push(("__cpp_lib_coroutine".into(), "201902".into()));
}
if standard >= CppStandard::Cxx23 {
macros.push(("__cpp_explicit_this_parameter".into(), "202110".into()));
macros.push(("__cpp_if_consteval".into(), "202106".into()));
macros.push(("__cpp_multidimensional_subscript".into(), "202110".into()));
macros.push(("__cpp_static_call_operator".into(), "202207".into()));
macros.push(("__cpp_auto_cast".into(), "202110".into()));
macros.push(("__cpp_lib_expected".into(), "202202".into()));
}
macros
}
pub fn eval_has_include(header: &str, _standard: CppStandard) -> bool {
let known_headers = [
"<iostream>",
"<vector>",
"<string>",
"<map>",
"<set>",
"<algorithm>",
"<memory>",
"<functional>",
"<utility>",
"<type_traits>",
"<tuple>",
"<array>",
"<queue>",
"<stack>",
"<bitset>",
"<deque>",
"<list>",
"<forward_list>",
"<unordered_map>",
"<unordered_set>",
"<regex>",
"<thread>",
"<mutex>",
"<future>",
"<chrono>",
"<random>",
"<filesystem>",
"<optional>",
"<variant>",
"<any>",
"<string_view>",
"<span>",
"<concepts>",
"<coroutine>",
"<compare>",
"<format>",
"<ranges>",
"<source_location>",
"\"stdio.h\"",
"\"stdlib.h\"",
"\"string.h\"",
"\"math.h\"",
];
known_headers.iter().any(|&h| {
header == h
|| header.trim_matches(&['<', '>', '"'] as &[_])
== h.trim_matches(&['<', '>', '"'] as &[_])
})
}
pub fn eval_has_cpp_attribute(attr: &str, standard: CppStandard) -> u32 {
match attr {
"noreturn" if standard >= CppStandard::Cxx11 => 200809,
"carries_dependency" if standard >= CppStandard::Cxx11 => 200809,
"deprecated" if standard >= CppStandard::Cxx14 => 201309,
"fallthrough" if standard >= CppStandard::Cxx17 => 201603,
"nodiscard" if standard >= CppStandard::Cxx17 => 201603,
"maybe_unused" if standard >= CppStandard::Cxx17 => 201603,
"likely" if standard >= CppStandard::Cxx20 => 201803,
"unlikely" if standard >= CppStandard::Cxx20 => 201803,
"no_unique_address" if standard >= CppStandard::Cxx20 => 201803,
"assume" if standard >= CppStandard::Cxx23 => 202207,
_ => 0,
}
}
pub struct IncludeNextResolver {
include_paths: Vec<String>,
current_dir: String,
}
impl IncludeNextResolver {
pub fn new(include_paths: Vec<String>, current_dir: &str) -> Self {
Self {
include_paths,
current_dir: current_dir.to_string(),
}
}
pub fn resolve(&self, header: &str) -> Option<String> {
let mut found_current = false;
for path in &self.include_paths {
if path == &self.current_dir {
found_current = true;
continue;
}
if found_current {
let candidate = std::path::Path::new(path).join(header);
if candidate.exists() {
return Some(candidate.to_string_lossy().to_string());
}
}
}
None
}
}
pub fn process_warning_directive(message: &str) -> String {
format!("#warning: {}", message)
}
pub struct EmbedProcessor;
impl EmbedProcessor {
pub fn process_embed(filename: &str, limit: Option<usize>) -> Result<String, String> {
let data =
std::fs::read(filename).map_err(|e| format!("cannot embed '{}': {}", filename, e))?;
let limit = limit.unwrap_or(data.len());
let bytes: Vec<String> = data.iter().take(limit).map(|b| format!("{}", b)).collect();
Ok(bytes.join(", "))
}
pub fn embed_to_ir(filename: &str, limit: Option<usize>) -> Result<String, String> {
let data =
std::fs::read(filename).map_err(|e| format!("cannot embed '{}': {}", filename, e))?;
let limit = limit.unwrap_or(data.len());
let bytes: Vec<String> = data
.iter()
.take(limit)
.map(|b| format!("i8 {}", b))
.collect();
Ok(format!("[{} x i8] [{}]", bytes.len(), bytes.join(", ")))
}
}
pub struct HeaderUnitProcessor {
pub standard: CppStandard,
}
impl HeaderUnitProcessor {
pub fn new(standard: CppStandard) -> Self {
Self { standard }
}
pub fn is_importable_header(&self, header: &str) -> bool {
if self.standard < CppStandard::Cxx20 {
return false;
}
let importable = [
"<iostream>",
"<vector>",
"<string>",
"<algorithm>",
"<memory>",
"<map>",
"<set>",
"<utility>",
];
importable.iter().any(|&h| h == header)
}
pub fn bmi_filename(&self, header: &str) -> String {
let clean = header
.replace(&['<', '>', '"'] as &[_], "")
.replace('/', "_");
format!("{}.pcm", clean)
}
}
pub struct CppPreprocessorExpr;
impl CppPreprocessorExpr {
pub fn eval(expr: &str, macros: &std::collections::HashMap<String, String>) -> Option<i64> {
let mut substituted = expr.to_string();
for (name, value) in macros {
substituted = substituted.replace(name, value);
}
if substituted.contains("__has_include") {
return Some(1); }
if substituted.contains("__has_cpp_attribute") {
return Some(1); }
substituted.parse::<i64>().ok()
}
pub fn is_defined(name: &str, macros: &std::collections::HashMap<String, String>) -> bool {
macros.contains_key(name)
}
}
pub struct FeatureTestMacroDb;
impl FeatureTestMacroDb {
pub fn lookup(macro_name: &str, standard: CppStandard) -> Option<String> {
let macros = cpp_predefined_macros(standard);
macros
.iter()
.find(|(name, _)| name == macro_name)
.map(|(_, value)| value.clone())
}
pub fn list_all(standard: CppStandard) -> Vec<&'static str> {
let macros = cpp_predefined_macros(standard);
macros.iter().map(|(name, _)| name.as_str()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cplusplus_value() {
assert_eq!(cplusplus_value(CppStandard::Cxx98), 199711);
assert_eq!(cplusplus_value(CppStandard::Cxx11), 201103);
assert_eq!(cplusplus_value(CppStandard::Cxx14), 201402);
assert_eq!(cplusplus_value(CppStandard::Cxx17), 201703);
assert_eq!(cplusplus_value(CppStandard::Cxx20), 202002);
assert_eq!(cplusplus_value(CppStandard::Cxx23), 202302);
}
#[test]
fn test_cpp_predefined_macros() {
let macros = cpp_predefined_macros(CppStandard::Cxx17);
assert!(macros.iter().any(|(n, _)| n == "__cplusplus"));
assert!(macros.iter().any(|(n, _)| n == "__cpp_constexpr"));
assert!(macros.iter().any(|(n, _)| n == "__cpp_fold_expressions"));
}
#[test]
fn test_cpp_feature_macros_cxx14() {
let macros = cpp_predefined_macros(CppStandard::Cxx14);
assert!(macros.iter().any(|(n, _)| n == "__cpp_generic_lambdas"));
assert!(macros.iter().any(|(n, _)| n == "__cpp_binary_literals"));
}
#[test]
fn test_cpp_feature_macros_cxx20() {
let macros = cpp_predefined_macros(CppStandard::Cxx20);
assert!(macros.iter().any(|(n, _)| n == "__cpp_concepts"));
assert!(macros.iter().any(|(n, _)| n == "__cpp_coroutines"));
assert!(macros
.iter()
.any(|(n, _)| n == "__cpp_three_way_comparison"));
}
#[test]
fn test_eval_has_include() {
assert!(eval_has_include("<iostream>", CppStandard::Cxx17));
assert!(eval_has_include("<vector>", CppStandard::Cxx17));
assert!(!eval_has_include("<nonexistent>", CppStandard::Cxx17));
}
#[test]
fn test_eval_has_cpp_attribute() {
assert_eq!(
eval_has_cpp_attribute("noreturn", CppStandard::Cxx11),
200809
);
assert_eq!(
eval_has_cpp_attribute("deprecated", CppStandard::Cxx14),
201309
);
assert_eq!(
eval_has_cpp_attribute("nodiscard", CppStandard::Cxx17),
201603
);
assert_eq!(eval_has_cpp_attribute("likely", CppStandard::Cxx20), 201803);
assert_eq!(eval_has_cpp_attribute("assume", CppStandard::Cxx23), 202207);
assert_eq!(eval_has_cpp_attribute("noreturn", CppStandard::Cxx98), 0);
}
#[test]
fn test_include_next_resolver() {
let resolver = IncludeNextResolver::new(
vec!["/usr/include".into(), "/usr/local/include".into()],
"/usr/include",
);
assert_eq!(resolver.include_paths.len(), 2);
}
#[test]
fn test_warning_directive() {
let msg = process_warning_directive("deprecated header");
assert!(msg.contains("deprecated header"));
}
#[test]
fn test_header_unit_processor() {
let hp = HeaderUnitProcessor::new(CppStandard::Cxx20);
assert!(hp.is_importable_header("<iostream>"));
assert!(hp.is_importable_header("<vector>"));
assert!(!hp.is_importable_header("<nonexistent>"));
assert_eq!(hp.bmi_filename("<iostream>"), "iostream.pcm");
}
#[test]
fn test_feature_test_macro_db() {
let val = FeatureTestMacroDb::lookup("__cpp_constexpr", CppStandard::Cxx17);
assert!(val.is_some());
let val = FeatureTestMacroDb::lookup("__cpp_concepts", CppStandard::Cxx17);
assert!(val.is_none()); let val = FeatureTestMacroDb::lookup("__cpp_concepts", CppStandard::Cxx20);
assert!(val.is_some()); }
}