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