//! NAC 去以太坊化 Lint 检查器 //! //! 在 CNNL 编译器中检测并自动纠正以太坊化术语, //! 防止开发者误用 RPC、EVM、Solidity 等以太坊概念。 //! //! # 设计原则 //! - 输入 RPC → 错误,建议使用 NAC Lens //! - 输入 EVM → 错误,建议使用 NVM //! - 输入 Solidity → 错误,建议使用 Charter //! - 输入 JSON-RPC → 错误,建议使用 NAC Lens 协议 //! - 输入 eth_ / net_ / web3_ 方法前缀 → 错误,建议使用 nac_ 方法 use std::collections::HashMap; /// 以太坊化术语检测规则 #[derive(Debug, Clone)] pub struct EthTermRule { /// 错误的以太坊术语 pub eth_term: &'static str, /// 正确的 NAC 术语 pub nac_term: &'static str, /// 错误说明 pub message: &'static str, /// 是否大小写不敏感匹配 pub case_insensitive: bool, } /// NAC 去以太坊化 Lint 诊断 #[derive(Debug, Clone)] pub struct EthLintDiagnostic { /// 发现位置(字节偏移) pub span: std::ops::Range, /// 发现的以太坊术语 pub found: String, /// 建议的 NAC 术语 pub suggestion: String, /// 错误消息 pub message: String, /// 是否可自动修复 pub auto_fixable: bool, } impl std::fmt::Display for EthLintDiagnostic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "[NAC-LINT-E001] 去以太坊化错误:在 {}..{} 发现 '{}'\n\ → 错误:{}\n\ → 建议:将 '{}' 替换为 '{}'", self.span.start, self.span.end, self.found, self.message, self.found, self.suggestion ) } } /// NAC 去以太坊化 Lint 检查器 pub struct NacDeEthLint { rules: Vec, } impl NacDeEthLint { /// 创建标准 NAC 去以太坊化检查器 pub fn new() -> Self { let rules = vec![ // RPC 相关 EthTermRule { eth_term: "RPC", nac_term: "NAC Lens", message: "NAC 公链不使用 RPC 协议。NAC 原生网络协议为 NAC Lens(元协议文明网络栈)", case_insensitive: false, }, EthTermRule { eth_term: "rpc", nac_term: "nac_lens", message: "NAC 公链不使用 rpc。请使用 NAC Lens 协议(nac_lens)", case_insensitive: false, }, EthTermRule { eth_term: "NRPC", nac_term: "NAC Lens", message: "NRPC 已更名为 NAC Lens。请使用 NAC Lens 协议", case_insensitive: false, }, EthTermRule { eth_term: "JSON-RPC", nac_term: "NAC Lens", message: "NAC 公链不使用 JSON-RPC(以太坊协议)。请使用 NAC Lens 原生协议", case_insensitive: true, }, EthTermRule { eth_term: "jsonrpc", nac_term: "nac_lens", message: "NAC 公链不使用 jsonrpc 字段(以太坊 JSON-RPC 格式)。请使用 NAC Lens 请求格式", case_insensitive: false, }, // 虚拟机相关 EthTermRule { eth_term: "EVM", nac_term: "NVM", message: "NAC 公链不使用 EVM(以太坊虚拟机)。NAC 原生虚拟机为 NVM(NAC Virtual Machine)", case_insensitive: false, }, EthTermRule { eth_term: "evm", nac_term: "nvm", message: "NAC 公链不使用 evm。请使用 NVM(nvm)", case_insensitive: false, }, // 智能合约语言 EthTermRule { eth_term: "Solidity", nac_term: "Charter", message: "NAC 公链不使用 Solidity(以太坊合约语言)。NAC 原生智能合约语言为 Charter", case_insensitive: false, }, EthTermRule { eth_term: "solidity", nac_term: "charter", message: "NAC 公链不使用 solidity。请使用 Charter 合约语言", case_insensitive: false, }, // 共识机制 EthTermRule { eth_term: "PoS", nac_term: "CBPP", message: "NAC 公链不使用 PoS(权益证明)。NAC 原生共识为 CBPP(宪政区块生产协议)", case_insensitive: false, }, EthTermRule { eth_term: "PoW", nac_term: "CBPP", message: "NAC 公链不使用 PoW(工作量证明)。NAC 原生共识为 CBPP", case_insensitive: false, }, // 网络协议 EthTermRule { eth_term: "P2P", nac_term: "CSNP", message: "NAC 公链不使用传统 P2P 网络。NAC 原生网络协议为 CSNP(宪政感知神经网络协议)", case_insensitive: false, }, // 以太坊方法前缀 EthTermRule { eth_term: "eth_", nac_term: "nac_", message: "NAC 公链不使用 eth_ 方法前缀(以太坊 JSON-RPC)。请使用 nac_ 方法前缀", case_insensitive: false, }, EthTermRule { eth_term: "net_version", nac_term: "nac_chainId", message: "NAC 公链不使用 net_version(以太坊方法)。请使用 nac_chainId", case_insensitive: false, }, EthTermRule { eth_term: "web3_", nac_term: "nac_", message: "NAC 公链不使用 web3_ 方法前缀。请使用 nac_ 方法前缀", case_insensitive: false, }, // 代币标准 EthTermRule { eth_term: "ERC20", nac_term: "ACC-20", message: "NAC 公链不使用 ERC20(以太坊代币标准)。NAC 原生代币标准为 ACC-20", case_insensitive: false, }, EthTermRule { eth_term: "ERC-20", nac_term: "ACC-20", message: "NAC 公链不使用 ERC-20。NAC 原生代币标准为 ACC-20", case_insensitive: false, }, EthTermRule { eth_term: "ERC721", nac_term: "ACC-721", message: "NAC 公链不使用 ERC721(以太坊 NFT 标准)。NAC 原生 NFT 标准为 ACC-721", case_insensitive: false, }, // 地址类型 EthTermRule { eth_term: "address(20)", nac_term: "Address(32)", message: "NAC 地址为 32 字节,不是以太坊的 20 字节地址", case_insensitive: false, }, ]; Self { rules } } /// 对源代码进行去以太坊化检查 pub fn check(&self, source: &str) -> Vec { let mut diagnostics = Vec::new(); for rule in &self.rules { let term = rule.eth_term; let search = if rule.case_insensitive { source.to_lowercase() } else { source.to_string() }; let search_term = if rule.case_insensitive { term.to_lowercase() } else { term.to_string() }; let mut start = 0; while let Some(pos) = search[start..].find(&search_term) { let abs_pos = start + pos; let end_pos = abs_pos + term.len(); // 检查是否是完整词(避免误匹配子字符串) let before_ok = abs_pos == 0 || !source[..abs_pos] .chars() .last() .map(|c| c.is_alphanumeric() || c == '_') .unwrap_or(false); let after_ok = end_pos >= source.len() || !source[end_pos..] .chars() .next() .map(|c| c.is_alphanumeric() || c == '_') .unwrap_or(false) || term.ends_with('_'); // 前缀匹配(如 eth_) if before_ok && after_ok { diagnostics.push(EthLintDiagnostic { span: abs_pos..end_pos, found: source[abs_pos..end_pos].to_string(), suggestion: rule.nac_term.to_string(), message: rule.message.to_string(), auto_fixable: true, }); } start = abs_pos + 1; } } // 按位置排序 diagnostics.sort_by_key(|d| d.span.start); diagnostics } /// 自动修复:将所有以太坊化术语替换为 NAC 术语 pub fn auto_fix(&self, source: &str) -> (String, Vec) { let diagnostics = self.check(source); if diagnostics.is_empty() { return (source.to_string(), diagnostics); } // 构建替换映射(按长度降序,避免短词替换长词的子串) let mut replacement_map: HashMap<&str, &str> = HashMap::new(); for rule in &self.rules { replacement_map.insert(rule.eth_term, rule.nac_term); } let mut result = source.to_string(); // 从后往前替换,避免位置偏移 let mut sorted_diags = diagnostics.clone(); sorted_diags.sort_by_key(|d| std::cmp::Reverse(d.span.start)); for diag in &sorted_diags { if diag.auto_fixable { result.replace_range(diag.span.clone(), &diag.suggestion); } } (result, diagnostics) } /// 生成人类可读的错误报告 pub fn report(&self, source: &str, filename: &str) -> String { let diagnostics = self.check(source); if diagnostics.is_empty() { return format!("✅ [NAC-LINT] {} 通过去以太坊化检查,无以太坊化术语", filename); } let mut report = format!( "❌ [NAC-LINT] {} 发现 {} 处以太坊化术语,必须修正:\n\n", filename, diagnostics.len() ); for (i, diag) in diagnostics.iter().enumerate() { // 计算行号 let line_num = source[..diag.span.start].chars().filter(|&c| c == '\n').count() + 1; report.push_str(&format!( " [{}/{}] 第{}行:{}\n", i + 1, diagnostics.len(), line_num, diag )); report.push('\n'); } report.push_str("\n💡 运行 `cnnl-compiler --fix` 可自动修正所有可修复的问题"); report } } impl Default for NacDeEthLint { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_rpc_detection() { let lint = NacDeEthLint::new(); let source = "connect to RPC endpoint"; let diags = lint.check(source); assert!(!diags.is_empty(), "应检测到 RPC"); assert_eq!(diags[0].suggestion, "NAC Lens"); } #[test] fn test_evm_detection() { let lint = NacDeEthLint::new(); let source = "deploy to EVM"; let diags = lint.check(source); assert!(!diags.is_empty(), "应检测到 EVM"); assert_eq!(diags[0].suggestion, "NVM"); } #[test] fn test_solidity_detection() { let lint = NacDeEthLint::new(); let source = "write Solidity contract"; let diags = lint.check(source); assert!(!diags.is_empty(), "应检测到 Solidity"); assert_eq!(diags[0].suggestion, "Charter"); } #[test] fn test_eth_method_prefix() { let lint = NacDeEthLint::new(); let source = r#"call "eth_blockNumber""#; let diags = lint.check(source); assert!(!diags.is_empty(), "应检测到 eth_ 前缀"); assert_eq!(diags[0].suggestion, "nac_"); } #[test] fn test_auto_fix() { let lint = NacDeEthLint::new(); let source = "use RPC to connect to EVM"; let (fixed, diags) = lint.auto_fix(source); assert!(!diags.is_empty()); assert!(!fixed.contains("RPC") || fixed.contains("NAC Lens")); } #[test] fn test_clean_source() { let lint = NacDeEthLint::new(); let source = "use NAC Lens to connect to NVM via Charter"; let diags = lint.check(source); assert!(diags.is_empty(), "干净的 NAC 代码不应有警告"); } #[test] fn test_nrpc_detection() { let lint = NacDeEthLint::new(); let source = "NRPC protocol version"; let diags = lint.check(source); assert!(!diags.is_empty(), "应检测到 NRPC"); assert_eq!(diags[0].suggestion, "NAC Lens"); } #[test] fn test_erc20_detection() { let lint = NacDeEthLint::new(); let source = "implement ERC20 token"; let diags = lint.check(source); assert!(!diags.is_empty(), "应检测到 ERC20"); assert_eq!(diags[0].suggestion, "ACC-20"); } }