#[allow(unused_macros)]
macro_rules! get_language {
(tree_sitter_typescript) => {
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()
};
(tree_sitter_tsx) => {
tree_sitter_typescript::LANGUAGE_TSX.into()
};
(tree_sitter_php) => {
tree_sitter_php::LANGUAGE_PHP.into()
};
($name:ident) => {
$name::LANGUAGE.into()
};
}
macro_rules! implement_metric_trait {
(Abc, $($code:ident),+) => (
implement_metric_trait!(@code_taking Abc, $($code),+);
);
(Cognitive, $($code:ident),+) => (
$(
impl Cognitive for $code {
fn compute<'a>(
_node: &Node<'a>,
_code: &'a [u8],
_stats: &mut Stats,
_nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {}
}
)+
);
(Halstead, $($code:ident),+) => (
$(
impl Halstead for $code {
fn compute<'a>(_node: &Node<'a>, _code: &'a [u8], _halstead_maps: &mut HalsteadMaps<'a>) {}
}
)+
);
(@code_taking $trait:ident, $($code:ident),+) => (
$(
impl $trait for $code {
fn compute<'a>(_node: &Node<'a>, _code: &'a [u8], _stats: &mut Stats) {}
}
)+
);
(Exit, $($code:ident),+) => (
implement_metric_trait!(@code_taking Exit, $($code),+);
);
(Cyclomatic, $($code:ident),+) => (
implement_metric_trait!(@code_taking Cyclomatic, $($code),+);
);
(Npa, $($code:ident),+) => (
implement_metric_trait!(@code_taking Npa, $($code),+);
);
(Npm, $($code:ident),+) => (
implement_metric_trait!(@code_taking Npm, $($code),+);
);
(Loc, $($code:ident),+) => (
$(
impl Loc for $code {
fn compute(_node: &Node, _stats: &mut Stats, _is_func_space: bool, _is_unit: bool) {}
}
)+
);
(Wmc, $($code:ident),+) => (
$(
impl Wmc for $code {
fn compute(_space_kind: SpaceKind, _cyclomatic: &cyclomatic::Stats, _stats: &mut Stats) {}
}
)+
);
([$trait:ident], $($code:ident),+) => (
$(
impl $trait for $code {}
)+
);
($trait:ident, $($code:ident),+) => (
$(
impl $trait for $code {
fn compute(_node: &Node, _stats: &mut Stats) {}
}
)+
)
}
macro_rules! mk_lang {
( $( ($feature:literal, $camel:ident, $name:ident, $display: expr, $description:expr, $version:literal) ),* ) => {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum LANG {
$(
#[doc = $description]
$camel,
)*
}
impl LANG {
pub fn into_enum_iter() -> impl Iterator<Item=LANG> {
use LANG::*;
[$( $camel, )*].into_iter()
}
pub fn name(&self) -> &'static str {
match self {
$(
LANG::$camel => $display,
)*
}
}
#[must_use]
pub fn grammar_version(&self) -> &'static str {
match self {
$(
LANG::$camel => $version,
)*
}
}
#[must_use]
pub fn is_enabled(&self) -> bool {
match self {
$(
#[cfg(feature = $feature)]
LANG::$camel => true,
#[cfg(not(feature = $feature))]
LANG::$camel => false,
)*
}
}
pub(crate) fn get_ts_language(&self) -> Result<Language, crate::MetricsError> {
match self {
$(
#[cfg(feature = $feature)]
LANG::$camel => Ok(get_language!($name)),
#[cfg(not(feature = $feature))]
LANG::$camel => Err(crate::MetricsError::LanguageDisabled(*self)),
)*
}
}
pub fn tree_sitter_language(&self) -> Result<::tree_sitter::Language, crate::MetricsError> {
self.get_ts_language()
}
}
impl ::std::fmt::Display for LANG {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
f.write_str(self.name())
}
}
impl ::std::str::FromStr for LANG {
type Err = $crate::macros::ParseLangError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
LANG::into_enum_iter()
.find(|lang| lang.name() == s)
.ok_or_else(|| $crate::macros::ParseLangError::new(s))
}
}
};
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseLangError(String);
impl ParseLangError {
pub(crate) fn new(input: &str) -> Self {
Self(input.to_owned())
}
#[must_use]
pub fn input(&self) -> &str {
&self.0
}
}
impl ::std::fmt::Display for ParseLangError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "unknown language: {}", self.0)
}
}
impl ::std::error::Error for ParseLangError {}
macro_rules! mk_action {
( $( ($feature:literal, $camel:ident, $parser:ident) ),* ) => {
pub(crate) enum AstInner {
$(
#[cfg(feature = $feature)]
$camel($parser),
)*
}
impl AstInner {
pub(crate) fn run_metrics(
&self,
name: Option<String>,
options: MetricsOptions,
) -> Result<FuncSpace, MetricsError> {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => metrics_inner(parser, name, options),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => {
let _ = (name, options);
match *self {}
},
}
}
pub(crate) fn run_ops(
&self,
name: Option<String>,
) -> Result<Ops, MetricsError> {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => ops_inner(parser, name),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => {
let _ = name;
match *self {}
},
}
}
pub(crate) fn run_strip_comments(&self) -> Option<Vec<u8>> {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => crate::comment_rm::rm_comments(parser),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => match *self {},
}
}
pub(crate) fn run_functions(&self) -> Vec<crate::FunctionSpan> {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => crate::function::function(parser),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => match *self {},
}
}
pub(crate) fn run_dump(&self, cfg: crate::AstCfg) -> crate::AstResponse {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => crate::ast::dump_inner(parser, cfg),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => {
let _ = cfg;
match *self {}
},
}
}
pub(crate) fn run_count(&self, filters: &[String]) -> (usize, usize) {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => crate::count::count(parser, filters),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => {
let _ = filters;
match *self {}
},
}
}
pub(crate) fn run_find(
&self,
filters: &[String],
) -> Result<Vec<crate::Node<'_>>, MetricsError> {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => crate::find::find(parser, filters),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => {
let _ = filters;
match *self {}
},
}
}
pub(crate) fn run_suppressions(&self) -> Vec<crate::SuppressionMarker> {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => crate::suppression::suppression_markers(parser),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => match *self {},
}
}
pub(crate) fn root_node(&self) -> crate::Node<'_> {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => parser.root(),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => match *self {},
}
}
pub(crate) fn language(&self) -> LANG {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(_) => LANG::$camel,
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => match *self {},
}
}
pub(crate) fn code_bytes(&self) -> &[u8] {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => parser.code(),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => match *self {},
}
}
pub(crate) fn ts_tree(&self) -> &::tree_sitter::Tree {
match self {
$(
#[cfg(feature = $feature)]
AstInner::$camel(parser) => parser.ts_tree(),
)*
#[cfg(not(any( $( feature = $feature ),* )))]
_ => match *self {},
}
}
}
pub(crate) fn ast_parse_dispatch(
lang: LANG,
source: Vec<u8>,
preproc_path: Option<&Path>,
preproc: Option<Arc<PreprocResults>>,
) -> Result<AstInner, MetricsError> {
let preproc_path = preproc_path.unwrap_or(Path::new(""));
match lang {
$(
#[cfg(feature = $feature)]
LANG::$camel => Ok(AstInner::$camel($parser::new(source, preproc_path, preproc))),
#[cfg(not(feature = $feature))]
LANG::$camel => {
let _ = (source, preproc_path, preproc);
Err(MetricsError::LanguageDisabled(lang))
},
)*
}
}
pub(crate) fn ast_from_tree_dispatch(
lang: LANG,
tree: ::tree_sitter::Tree,
source: Vec<u8>,
) -> Result<AstInner, MetricsError> {
match lang {
$(
#[cfg(feature = $feature)]
LANG::$camel => Ok(AstInner::$camel($parser::from_tree(tree, source))),
#[cfg(not(feature = $feature))]
LANG::$camel => {
let _ = (tree, source);
Err(MetricsError::LanguageDisabled(lang))
},
)*
}
}
};
}
macro_rules! mk_extensions {
( $( ($camel:ident, [ $( $ext:ident ),* ]) ),* ) => {
pub fn get_from_ext(ext: &str) -> Option<LANG>{
match ext {
$(
$(
stringify!($ext) => Some(LANG::$camel),
)*
)*
_ => None,
}
}
impl LANG {
#[must_use]
pub fn extensions(&self) -> &'static [&'static str] {
match self {
$(
LANG::$camel => &[ $( stringify!($ext), )* ],
)*
}
}
}
};
}
macro_rules! mk_emacs_mode {
( $( ($camel:ident, [ $( $emacs_mode:expr ),* ]) ),* ) => {
pub fn get_from_emacs_mode(mode: &str) -> Option<LANG>{
match mode {
$(
$(
$emacs_mode => Some(LANG::$camel),
)*
)*
_ => None,
}
}
};
}
macro_rules! mk_code {
( $( ($camel:ident, $code:ident, $parser:ident, $name:ident, $docname:expr) ),* ) => {
$(
#[doc = concat!("Per-language code type tag for ", $docname, "; carries no data.")]
pub(crate) struct $code { _guard: (), }
impl LanguageInfo for $code {
type BaseLang = $camel;
fn lang() -> LANG {
LANG::$camel
}
}
#[doc = "The `"]
#[doc = $docname]
#[doc = "` language parser."]
pub(crate) type $parser = Parser<$code>;
)*
};
}
macro_rules! mk_langs {
( $( ($feature:literal, $camel:ident, $description: expr, $display: expr, $code:ident, $parser:ident, $name:ident, [ $( $ext:ident ),* ], [ $( $emacs_mode:expr ),* ], $version:literal) ),* ) => {
mk_lang!($( ($feature, $camel, $name, $display, $description, $version) ),*);
mk_action!($( ($feature, $camel, $parser) ),*);
mk_extensions!($( ($camel, [ $( $ext ),* ]) ),*);
mk_emacs_mode!($( ($camel, [ $( $emacs_mode ),* ]) ),*);
mk_code!($( ($camel, $code, $parser, $name, stringify!($camel)) ),*);
};
}
mod kind_sets;
pub(crate) use implement_metric_trait;
pub(crate) use kind_sets::{
cpp_bool_terminal_kinds, csharp_bool_terminal_kinds, csharp_invocation_expr_kinds,
csharp_paren_expr_kinds, csharp_prefix_unary_expr_kinds, csharp_var_decl_kinds,
csharp_var_declarator_kinds, elixir_bool_terminal_kinds, go_bool_terminal_kinds,
groovy_bool_terminal_kinds, irules_bool_terminal_kinds, java_bool_terminal_kinds,
javascript_bool_terminal_kinds, kotlin_bool_terminal_kinds, lua_bool_terminal_kinds,
mozjs_bool_terminal_kinds, perl_bool_terminal_kinds, php_bool_terminal_kinds,
python_bool_terminal_kinds, ruby_bool_terminal_kinds, rust_bool_terminal_kinds,
tcl_bool_terminal_kinds, tsx_bool_terminal_kinds, typescript_bool_terminal_kinds,
};
pub(crate) use {
get_language, mk_action, mk_code, mk_emacs_mode, mk_extensions, mk_lang, mk_langs,
};