use std::sync::OnceLock;
use aho_corasick::AhoCorasick;
use regex::bytes::Regex;
use crate::*;
static AHO_CORASICK: OnceLock<AhoCorasick> = OnceLock::new();
static RE: OnceLock<Regex> = OnceLock::new();
macro_rules! check_if_func {
($parser: ident, $node: ident) => {
$node.count_specific_ancestors::<$parser>(
|node| {
matches!(
node.kind_id().into(),
VariableDeclarator | AssignmentExpression | LabeledStatement | Pair
)
},
|node| {
matches!(
node.kind_id().into(),
StatementBlock | ReturnStatement | NewExpression | Arguments
)
},
) > 0
|| $node.is_child(Identifier as u16)
};
}
macro_rules! check_if_arrow_func {
($parser: ident, $node: ident) => {
$node.count_specific_ancestors::<$parser>(
|node| {
matches!(
node.kind_id().into(),
VariableDeclarator | AssignmentExpression | LabeledStatement
)
},
|node| {
matches!(
node.kind_id().into(),
StatementBlock | ReturnStatement | NewExpression | CallExpression
)
},
) > 0
|| $node.has_sibling(PropertyIdentifier as u16)
};
}
macro_rules! is_js_func {
($parser: ident, $node: ident) => {
match $node.kind_id().into() {
FunctionDeclaration | MethodDefinition => true,
FunctionExpression => check_if_func!($parser, $node),
ArrowFunction => check_if_arrow_func!($parser, $node),
_ => false,
}
};
}
macro_rules! is_js_closure {
($parser: ident, $node: ident) => {
match $node.kind_id().into() {
GeneratorFunction | GeneratorFunctionDeclaration => true,
FunctionExpression => !check_if_func!($parser, $node),
ArrowFunction => !check_if_arrow_func!($parser, $node),
_ => false,
}
};
}
macro_rules! is_js_func_and_closure_checker {
($parser: ident, $language: ident) => {
#[inline(always)]
fn is_func(node: &Node) -> bool {
use $language::*;
is_js_func!($parser, node)
}
#[inline(always)]
fn is_closure(node: &Node) -> bool {
use $language::*;
is_js_closure!($parser, node)
}
};
}
#[inline(always)]
fn get_aho_corasick_match(code: &[u8]) -> bool {
AHO_CORASICK
.get_or_init(|| AhoCorasick::new(vec![b"<div rustbindgen"]).unwrap())
.is_match(code)
}
pub trait Checker {
fn is_comment(_: &Node) -> bool;
fn is_useful_comment(_: &Node, _: &[u8]) -> bool;
fn is_func_space(_: &Node) -> bool;
fn is_func(_: &Node) -> bool;
fn is_closure(_: &Node) -> bool;
fn is_call(_: &Node) -> bool;
fn is_non_arg(_: &Node) -> bool;
fn is_string(_: &Node) -> bool;
fn is_else_if(_: &Node) -> bool;
fn is_primitive(_id: u16) -> bool;
fn is_error(node: &Node) -> bool {
node.has_error()
}
}
impl Checker for PreprocCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Preproc::Comment
}
fn is_useful_comment(_: &Node, _: &[u8]) -> bool {
false
}
fn is_func_space(_: &Node) -> bool {
false
}
fn is_func(_: &Node) -> bool {
false
}
fn is_closure(_: &Node) -> bool {
false
}
fn is_call(_: &Node) -> bool {
false
}
fn is_non_arg(_: &Node) -> bool {
false
}
fn is_string(node: &Node) -> bool {
node.kind_id() == Preproc::StringLiteral || node.kind_id() == Preproc::RawStringLiteral
}
fn is_else_if(_: &Node) -> bool {
false
}
fn is_primitive(_id: u16) -> bool {
false
}
}
impl Checker for CcommentCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Ccomment::Comment
}
fn is_useful_comment(node: &Node, code: &[u8]) -> bool {
get_aho_corasick_match(&code[node.start_byte()..node.end_byte()])
}
fn is_func_space(_: &Node) -> bool {
false
}
fn is_func(_: &Node) -> bool {
false
}
fn is_closure(_: &Node) -> bool {
false
}
fn is_call(_: &Node) -> bool {
false
}
fn is_non_arg(_: &Node) -> bool {
false
}
fn is_string(node: &Node) -> bool {
node.kind_id() == Ccomment::StringLiteral || node.kind_id() == Ccomment::RawStringLiteral
}
fn is_else_if(_: &Node) -> bool {
false
}
fn is_primitive(_id: u16) -> bool {
false
}
}
impl Checker for CppCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Cpp::Comment
}
fn is_useful_comment(node: &Node, code: &[u8]) -> bool {
get_aho_corasick_match(&code[node.start_byte()..node.end_byte()])
}
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Cpp::TranslationUnit
| Cpp::FunctionDefinition
| Cpp::FunctionDefinition2
| Cpp::FunctionDefinition3
| Cpp::StructSpecifier
| Cpp::ClassSpecifier
| Cpp::NamespaceDefinition
)
}
fn is_func(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Cpp::FunctionDefinition
| Cpp::FunctionDefinition2
| Cpp::FunctionDefinition3
| Cpp::FunctionDefinition4
)
}
fn is_closure(node: &Node) -> bool {
node.kind_id() == Cpp::LambdaExpression
}
fn is_call(node: &Node) -> bool {
node.kind_id() == Cpp::CallExpression
}
fn is_non_arg(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Cpp::LPAREN | Cpp::LPAREN2 | Cpp::COMMA | Cpp::RPAREN
)
}
fn is_string(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Cpp::StringLiteral | Cpp::ConcatenatedString | Cpp::RawStringLiteral
)
}
fn is_else_if(node: &Node) -> bool {
if node.kind_id() != Cpp::IfStatement {
return false;
}
if let Some(parent) = node.parent() {
return parent.kind_id() == Cpp::ElseClause;
}
false
}
#[inline(always)]
fn is_primitive(id: u16) -> bool {
id == Cpp::PrimitiveType
}
}
impl Checker for PythonCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Python::Comment
}
fn is_useful_comment(node: &Node, code: &[u8]) -> bool {
node.start_row() <= 1
&& RE
.get_or_init(|| {
Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)").unwrap()
})
.is_match(&code[node.start_byte()..node.end_byte()])
}
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Python::Module | Python::FunctionDefinition | Python::ClassDefinition
)
}
fn is_func(node: &Node) -> bool {
node.kind_id() == Python::FunctionDefinition
}
fn is_closure(node: &Node) -> bool {
node.kind_id() == Python::Lambda
}
fn is_call(node: &Node) -> bool {
node.kind_id() == Python::Call
}
fn is_non_arg(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Python::LPAREN | Python::COMMA | Python::RPAREN
)
}
fn is_string(node: &Node) -> bool {
node.kind_id() == Python::String || node.kind_id() == Python::ConcatenatedString
}
fn is_else_if(_: &Node) -> bool {
false
}
fn is_primitive(_id: u16) -> bool {
false
}
}
impl Checker for JavaCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Java::LineComment || node.kind_id() == Java::BlockComment
}
fn is_useful_comment(_: &Node, _: &[u8]) -> bool {
false
}
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Java::Program | Java::ClassDeclaration | Java::InterfaceDeclaration
)
}
fn is_func(node: &Node) -> bool {
node.kind_id() == Java::MethodDeclaration || node.kind_id() == Java::ConstructorDeclaration
}
fn is_closure(node: &Node) -> bool {
node.kind_id() == Java::LambdaExpression
}
fn is_call(node: &Node) -> bool {
node.kind_id() == Java::MethodInvocation
}
fn is_non_arg(_: &Node) -> bool {
false
}
fn is_string(node: &Node) -> bool {
node.kind_id() == Java::StringLiteral
}
fn is_else_if(_: &Node) -> bool {
false
}
fn is_primitive(_id: u16) -> bool {
false
}
}
impl Checker for MozjsCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Mozjs::Comment
}
fn is_useful_comment(_: &Node, _: &[u8]) -> bool {
false
}
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Mozjs::Program
| Mozjs::FunctionExpression
| Mozjs::Class
| Mozjs::GeneratorFunction
| Mozjs::FunctionDeclaration
| Mozjs::MethodDefinition
| Mozjs::GeneratorFunctionDeclaration
| Mozjs::ClassDeclaration
| Mozjs::ArrowFunction
)
}
is_js_func_and_closure_checker!(MozjsParser, Mozjs);
fn is_call(node: &Node) -> bool {
node.kind_id() == Mozjs::CallExpression
}
fn is_non_arg(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Mozjs::LPAREN | Mozjs::COMMA | Mozjs::RPAREN
)
}
fn is_string(node: &Node) -> bool {
node.kind_id() == Mozjs::String || node.kind_id() == Mozjs::TemplateString
}
#[inline(always)]
fn is_else_if(node: &Node) -> bool {
if node.kind_id() != Mozjs::IfStatement {
return false;
}
if let Some(parent) = node.parent() {
return parent.kind_id() == Mozjs::ElseClause;
}
false
}
fn is_primitive(_id: u16) -> bool {
false
}
}
impl Checker for JavascriptCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Javascript::Comment
}
fn is_useful_comment(_: &Node, _: &[u8]) -> bool {
false
}
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Javascript::Program
| Javascript::FunctionExpression
| Javascript::Class
| Javascript::GeneratorFunction
| Javascript::FunctionDeclaration
| Javascript::MethodDefinition
| Javascript::GeneratorFunctionDeclaration
| Javascript::ClassDeclaration
| Javascript::ArrowFunction
)
}
is_js_func_and_closure_checker!(JavascriptParser, Javascript);
fn is_call(node: &Node) -> bool {
node.kind_id() == Javascript::CallExpression
}
fn is_non_arg(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Javascript::LPAREN | Javascript::COMMA | Javascript::RPAREN
)
}
fn is_string(node: &Node) -> bool {
node.kind_id() == Javascript::String || node.kind_id() == Javascript::TemplateString
}
#[inline(always)]
fn is_else_if(node: &Node) -> bool {
if node.kind_id() != Javascript::IfStatement {
return false;
}
if let Some(parent) = node.parent() {
return node.kind_id() == Javascript::IfStatement
&& parent.kind_id() == Javascript::IfStatement;
}
false
}
fn is_primitive(_id: u16) -> bool {
false
}
}
impl Checker for TypescriptCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Typescript::Comment
}
fn is_useful_comment(_: &Node, _: &[u8]) -> bool {
false
}
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Typescript::Program
| Typescript::FunctionExpression
| Typescript::Class
| Typescript::GeneratorFunction
| Typescript::FunctionDeclaration
| Typescript::MethodDefinition
| Typescript::GeneratorFunctionDeclaration
| Typescript::ClassDeclaration
| Typescript::InterfaceDeclaration
| Typescript::ArrowFunction
)
}
is_js_func_and_closure_checker!(TypescriptParser, Typescript);
fn is_call(node: &Node) -> bool {
node.kind_id() == Typescript::CallExpression
}
fn is_non_arg(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Typescript::LPAREN | Typescript::COMMA | Typescript::RPAREN
)
}
fn is_string(node: &Node) -> bool {
node.kind_id() == Typescript::String || node.kind_id() == Typescript::TemplateString
}
#[inline(always)]
fn is_else_if(node: &Node) -> bool {
if node.kind_id() != Typescript::IfStatement {
return false;
}
if let Some(parent) = node.parent() {
return parent.kind_id() == Typescript::ElseClause;
}
false
}
#[inline(always)]
fn is_primitive(id: u16) -> bool {
id == Typescript::PredefinedType
}
}
impl Checker for TsxCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Tsx::Comment
}
fn is_useful_comment(_: &Node, _: &[u8]) -> bool {
false
}
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Tsx::Program
| Tsx::FunctionExpression
| Tsx::Class
| Tsx::GeneratorFunction
| Tsx::FunctionDeclaration
| Tsx::MethodDefinition
| Tsx::GeneratorFunctionDeclaration
| Tsx::ClassDeclaration
| Tsx::InterfaceDeclaration
| Tsx::ArrowFunction
)
}
is_js_func_and_closure_checker!(TsxParser, Tsx);
fn is_call(node: &Node) -> bool {
node.kind_id() == Tsx::CallExpression
}
fn is_non_arg(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Tsx::LPAREN | Tsx::COMMA | Tsx::RPAREN
)
}
fn is_string(node: &Node) -> bool {
node.kind_id() == Tsx::String || node.kind_id() == Tsx::TemplateString
}
fn is_else_if(node: &Node) -> bool {
if node.kind_id() != Tsx::IfStatement {
return false;
}
if let Some(parent) = node.parent() {
return node.kind_id() == Tsx::IfStatement && parent.kind_id() == Tsx::IfStatement;
}
false
}
#[inline(always)]
fn is_primitive(id: u16) -> bool {
id == Tsx::PredefinedType
}
}
impl Checker for RustCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Rust::LineComment || node.kind_id() == Rust::BlockComment
}
fn is_useful_comment(node: &Node, code: &[u8]) -> bool {
if let Some(parent) = node.parent()
&& parent.kind_id() == Rust::TokenTree
{
return true;
}
let code = &code[node.start_byte()..node.end_byte()];
code.starts_with(b"/// cbindgen:")
}
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Rust::SourceFile
| Rust::FunctionItem
| Rust::ImplItem
| Rust::TraitItem
| Rust::ClosureExpression
)
}
fn is_func(node: &Node) -> bool {
node.kind_id() == Rust::FunctionItem
}
fn is_closure(node: &Node) -> bool {
node.kind_id() == Rust::ClosureExpression
}
fn is_call(node: &Node) -> bool {
node.kind_id() == Rust::CallExpression
}
fn is_non_arg(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Rust::LPAREN | Rust::COMMA | Rust::RPAREN | Rust::PIPE | Rust::AttributeItem
)
}
fn is_string(node: &Node) -> bool {
node.kind_id() == Rust::StringLiteral || node.kind_id() == Rust::RawStringLiteral
}
#[inline(always)]
fn is_else_if(node: &Node) -> bool {
if node.kind_id() != Rust::IfExpression {
return false;
}
if let Some(parent) = node.parent() {
return parent.kind_id() == Rust::ElseClause;
}
false
}
#[inline(always)]
fn is_primitive(id: u16) -> bool {
id == Rust::PrimitiveType
}
}
impl Checker for KotlinCode {
fn is_comment(_: &Node) -> bool {
false
}
fn is_useful_comment(_: &Node, _: &[u8]) -> bool {
false
}
fn is_func_space(_: &Node) -> bool {
false
}
fn is_func(_: &Node) -> bool {
false
}
fn is_closure(_: &Node) -> bool {
false
}
fn is_call(_: &Node) -> bool {
false
}
fn is_non_arg(_: &Node) -> bool {
false
}
fn is_string(_: &Node) -> bool {
false
}
fn is_else_if(_: &Node) -> bool {
false
}
fn is_primitive(_id: u16) -> bool {
false
}
}